qt.py 4.19 KB
#NVDAObjects/IAccessible/qt.py
#A part of NonVisual Desktop Access (NVDA)
#Copyright (C) 2006-2009 NVDA Contributors <http://www.nvda-project.org/>
#This file is covered by the GNU General Public License.
#See the file COPYING for more details.

from comtypes import COMError
import controlTypes
from NVDAObjects.IAccessible import IAccessible
import eventHandler
from scriptHandler import isScriptWaiting

class Client(IAccessible):

	def _get__containedWidget(self):
		widget = self.firstChild
		if not widget:
			return None

		wnext = widget.next
		if not wnext:
			# There is only one child, so this is probably a widget container.
			return widget

		try:
			if wnext.firstChild.role == controlTypes.ROLE_SCROLLBAR:
				# There is only one child plus a scrollbar, so this is probably a widget container.
				return widget
		except AttributeError:
			pass

		# This is not a widget container.
		return None

	def event_gainFocus(self):
		if eventHandler.isPendingEvents("gainFocus"):
			return

		widget = self._containedWidget
		if widget:
			# This is a widget container.
			# Redirect the focus to the contained widget, since QT doesn't do it properly.
			self.event_focusEntered()
			eventHandler.executeEvent("gainFocus", widget)
			return

		return super(Client, self).event_gainFocus()

class Container(IAccessible):

	def _get_activeChild(self):
		# QT doesn't do accFocus properly, so find the active child ourselves.
		child = self.firstChild
		while child:
			states = child.states
			if controlTypes.STATE_FOCUSED in states or controlTypes.STATE_SELECTED in states:
				return child
			child = child.next
		return None

	def _get_shouldAllowIAccessibleFocusEvent(self):
		# QT doesn't fire focus on the active child as it should, so we will bounce the focus to it.
		# However, as the container does not have the focused state in QT5, we must still ensure we can get the event if we are going to bounce it
		res=super(Container,self).shouldAllowIAccessibleFocusEvent
		if not res:
			res=bool(self.activeChild)
		return res

	def event_gainFocus(self):
		if eventHandler.isPendingEvents("gainFocus"):
			return

		child = self.activeChild
		if child:
			# QT doesn't fire focus on the active child as it should, so redirect the focus.
			self.event_focusEntered()
			eventHandler.executeEvent("gainFocus", child)
			return

		return super(Container, self).event_gainFocus()


class TableRow(Container):

	value=None
	description=None

	def _get_activeChild(self):
		# QT doesn't do accFocus properly, so find the active child ourselves.
		child = self.firstChild
		while child:
			states = child.states
			if controlTypes.STATE_FOCUSED in states:
				return child
			child = child.next
		return None


class TableCell(IAccessible):

	value=None

	def script_nextColumn(self,gesture):
		gesture.send()
		if not isScriptWaiting():
			next=self.next
			if next and controlTypes.STATE_FOCUSED in next.states:
				eventHandler.executeEvent("gainFocus", next)

	def script_previousColumn(self,gesture):
		gesture.send()
		if not isScriptWaiting():
			previous=self.previous
			if previous and controlTypes.STATE_FOCUSED in previous.states:
				eventHandler.executeEvent("gainFocus", previous)

	__gestures = {
		"kb:tab": "nextColumn",
		"kb:rightArrow": "nextColumn",
		"kb:shift+tab": "previousColumn",
		"kb:leftArrow": "previousColumn",
	}


class TreeViewItem(IAccessible):

	value = None

	hasEncodedAccDescription=True

class Menu(IAccessible):
	# QT incorrectly fires a focus event on the parent menu immediately after (correctly) firing focus on the menu item.
	# This is probably QT task 241161, which was apparently fixed in QT 4.5.1.
	shouldAllowIAccessibleFocusEvent = False

class LayeredPane(IAccessible):
	# QT < 4.6 uses ROLE_SYSTEM_IPADDRESS for layered pane.
	# See QT task 258413.
	role = controlTypes.ROLE_LAYEREDPANE

class Application(IAccessible):
	# QT sets the path of the application in the description, which is irrelevant to the user.
	description = None

	def _get_states(self):
		states = super(Application, self)._get_states()
		# The application should not have the focused state.
		# Otherwise, checks for the focused state will always hit the application and assume the focus is valid.
		states.discard(controlTypes.STATE_FOCUSED)
		return states