Skip to content

Commit

Permalink
Implement Enums for Role and State constants in controlTypes (#12510)
Browse files Browse the repository at this point in the history
In order to improve the NVDA API and internal typing, Enums are created
for ROLE_* and STATE_* constants.

Changes:
- retain backwards compatibility until 2022.1 in controlTypes
- improve type information for controlTypes processing
- create a clickableRoles set in role.py for processAndLabelStates
- update docstrings to reflect new types
- lint controlTypes due to the mass changes
- deprecate helper functions for processAndLabelStates
- standardise members of the State enum from 0X[0-9]+ to 0x[0-9]+
- implement a displayStringEnumMixin
- deprecate roleLabels, stateLabels, negativeStateLabels
- update coding standards for enum usage and deprecations

Co-authored-by: Leonard de Ruijter <leonardder@users.noreply.github.com>
  • Loading branch information
seanbudd and LeonarddeR committed Jun 30, 2021
1 parent 50bfc8b commit 8353f5f
Show file tree
Hide file tree
Showing 10 changed files with 980 additions and 550 deletions.
16 changes: 16 additions & 0 deletions devDocs/codingStandards.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ for more information.
- E.G. `BrailleHandler`.
* Constants should be all upper case, separating words with underscores;
- E.G. `LANGS_WITH_CONJUNCT_CHARS`.
- Avoid unnecesary shared prefixes in constants. Instead, use an enum for related constants.
* Event handlers are prefixed with "event_", subsequent words in camel case.
Note, `object` and `action` are separated by underscores.
- E.G.: `event_action` or `event_object_action`.
Expand All @@ -59,6 +60,13 @@ for more information.
- TBD. Ideally follows a similar style the others, and communicates if the filtering happens
before or after some action.
- It would also be nice to have a naming scheme that differentiates it from the others.
* Enums should be formatted using the expected mix of above eg:
```python
class ExampleGroupOfData(Enum):
CONSTANT_VALUE_MEMBER = auto()
@property
def _formatMember(self): pass
```

### Translatable Strings
* All strings that could be presented to the user should be marked as translatable using the `_()`
Expand Down Expand Up @@ -86,3 +94,11 @@ self.copySettingsButton = wx.Button(
)
)
```

### Imports
* Unused imports should be removed where possible.
- Anything imported into a (sub)module can also be imported from that submodule.
- As a result, removing unused imports may break compatibility, and should be done in compatibility breaking releases (see `deprecations.md`).
* Unused imports will give a lint warning. These can be handled the following ways:
- If these imports are inteded to be imported from other modules, they can be done included in a definition for `__all__`. This will override and define the symbols imported when performing a star import, eg `from module import *`.
- Otherwise, with a comment like `# noqa: <explanation>`.
21 changes: 21 additions & 0 deletions devDocs/deprecations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The NVDA API must maintain compatibility with add-ons throughout yearly development cycles.
The first release of a year, i.e. `20XX.1`, is when the NVDA API can introduce breaking changes.

In order to improve the NVDA API, changes that will break future compatibility can be implemented, as long they retain backwards compatibility until the `20XX.1` release.

This can be done by using a version check to automate deprecation. For example, if you wish to replace usages of `foo` with `bar`. When we begin work on `NEXT_YEAR`, `foo` will no longer be part of the NVDA API and all internal usages must be removed prior.
```python
from buildVersion import version_year
if version_year < NEXT_YEAR:
foo = bar
```

To ensure a module retains the same symbol names being importable, check across versions what is imported using the NVDA python console.
```python
import controlTypes
dir(controlTypes)
```

Changes different to moving or renaming symbols need to be considered carefully with a different approach.

Any API breaking changes such as deprecations marked for removal should be commented with the year of intended removal, and notes on how to implement the API change as an add-on developer and NVDA developer.
248 changes: 248 additions & 0 deletions source/controlTypes/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
# A part of NonVisual Desktop Access (NVDA)
# This file is covered by the GNU General Public License.
# See the file COPYING for more details.
# Copyright (C) 2007-2021 NV Access Limited, Babbage B.V.

from buildVersion import version_year

from .isCurrent import IsCurrent
from .outputReason import OutputReason
from .processAndLabelStates import processAndLabelStates
from .role import Role, silentRolesOnFocus, silentValuesForRoles
from .state import State, STATES_SORTED


# To maintain backwards compatibility, all symbols are exported from the package until 2022.1
if version_year >= 2022:
# Override (and limit) the symbols exported by the controlTypes package
# These are the symbols available when `from controlTypes import *` is used.
__all__ = [
"IsCurrent",
"OutputReason",
"processAndLabelStates",
"Role",
"silentRolesOnFocus",
"silentValuesForRoles",
"State",
"STATES_SORTED",
]


# Added to maintain backwards compatibility, marked for deprecation to be removed in 2022.1
# usages to be avoided or replaced by .processAndLabelStates.[_processNegativeStates|_processPositiveStates]
if version_year < 2022:
from .processAndLabelStates import _processNegativeStates, _processPositiveStates
processNegativeStates = _processNegativeStates
processPositiveStates = _processPositiveStates


# Added to maintain backwards compatibility, marked for deprecation to be removed in 2022.1
# usages to be replaced by Role.*.displayString and State.*.displayString
if version_year < 2022:
from .role import _roleLabels
from .state import _stateLabels, _negativeStateLabels
roleLabels = _roleLabels
stateLabels = _stateLabels
negativeStateLabels = _negativeStateLabels


# Added to maintain backwards compatibility, marked for deprecation to be removed in 2022.1
if version_year < 2022:
ROLE_UNKNOWN = Role.UNKNOWN
ROLE_WINDOW = Role.WINDOW
ROLE_TITLEBAR = Role.TITLEBAR
ROLE_PANE = Role.PANE
ROLE_DIALOG = Role.DIALOG
ROLE_CHECKBOX = Role.CHECKBOX
ROLE_RADIOBUTTON = Role.RADIOBUTTON
ROLE_STATICTEXT = Role.STATICTEXT
ROLE_EDITABLETEXT = Role.EDITABLETEXT
ROLE_BUTTON = Role.BUTTON
ROLE_MENUBAR = Role.MENUBAR
ROLE_MENUITEM = Role.MENUITEM
ROLE_POPUPMENU = Role.POPUPMENU
ROLE_COMBOBOX = Role.COMBOBOX
ROLE_LIST = Role.LIST
ROLE_LISTITEM = Role.LISTITEM
ROLE_GRAPHIC = Role.GRAPHIC
ROLE_HELPBALLOON = Role.HELPBALLOON
ROLE_TOOLTIP = Role.TOOLTIP
ROLE_LINK = Role.LINK
ROLE_TREEVIEW = Role.TREEVIEW
ROLE_TREEVIEWITEM = Role.TREEVIEWITEM
ROLE_TAB = Role.TAB
ROLE_TABCONTROL = Role.TABCONTROL
ROLE_SLIDER = Role.SLIDER
ROLE_PROGRESSBAR = Role.PROGRESSBAR
ROLE_SCROLLBAR = Role.SCROLLBAR
ROLE_STATUSBAR = Role.STATUSBAR
ROLE_TABLE = Role.TABLE
ROLE_TABLECELL = Role.TABLECELL
ROLE_TABLECOLUMN = Role.TABLECOLUMN
ROLE_TABLEROW = Role.TABLEROW
ROLE_TABLECOLUMNHEADER = Role.TABLECOLUMNHEADER
ROLE_TABLEROWHEADER = Role.TABLEROWHEADER
ROLE_FRAME = Role.FRAME
ROLE_TOOLBAR = Role.TOOLBAR
ROLE_DROPDOWNBUTTON = Role.DROPDOWNBUTTON
ROLE_CLOCK = Role.CLOCK
ROLE_SEPARATOR = Role.SEPARATOR
ROLE_FORM = Role.FORM
ROLE_HEADING = Role.HEADING
ROLE_HEADING1 = Role.HEADING1
ROLE_HEADING2 = Role.HEADING2
ROLE_HEADING3 = Role.HEADING3
ROLE_HEADING4 = Role.HEADING4
ROLE_HEADING5 = Role.HEADING5
ROLE_HEADING6 = Role.HEADING6
ROLE_PARAGRAPH = Role.PARAGRAPH
ROLE_BLOCKQUOTE = Role.BLOCKQUOTE
ROLE_TABLEHEADER = Role.TABLEHEADER
ROLE_TABLEBODY = Role.TABLEBODY
ROLE_TABLEFOOTER = Role.TABLEFOOTER
ROLE_DOCUMENT = Role.DOCUMENT
ROLE_ANIMATION = Role.ANIMATION
ROLE_APPLICATION = Role.APPLICATION
ROLE_BOX = Role.BOX
ROLE_GROUPING = Role.GROUPING
ROLE_PROPERTYPAGE = Role.PROPERTYPAGE
ROLE_CANVAS = Role.CANVAS
ROLE_CAPTION = Role.CAPTION
ROLE_CHECKMENUITEM = Role.CHECKMENUITEM
ROLE_DATEEDITOR = Role.DATEEDITOR
ROLE_ICON = Role.ICON
ROLE_DIRECTORYPANE = Role.DIRECTORYPANE
ROLE_EMBEDDEDOBJECT = Role.EMBEDDEDOBJECT
ROLE_ENDNOTE = Role.ENDNOTE
ROLE_FOOTER = Role.FOOTER
ROLE_FOOTNOTE = Role.FOOTNOTE
ROLE_GLASSPANE = Role.GLASSPANE
ROLE_HEADER = Role.HEADER
ROLE_IMAGEMAP = Role.IMAGEMAP
ROLE_INPUTWINDOW = Role.INPUTWINDOW
ROLE_LABEL = Role.LABEL
ROLE_NOTE = Role.NOTE
ROLE_PAGE = Role.PAGE
ROLE_RADIOMENUITEM = Role.RADIOMENUITEM
ROLE_LAYEREDPANE = Role.LAYEREDPANE
ROLE_REDUNDANTOBJECT = Role.REDUNDANTOBJECT
ROLE_ROOTPANE = Role.ROOTPANE
ROLE_EDITBAR = Role.EDITBAR
ROLE_TERMINAL = Role.TERMINAL
ROLE_RICHEDIT = Role.RICHEDIT
ROLE_RULER = Role.RULER
ROLE_SCROLLPANE = Role.SCROLLPANE
ROLE_SECTION = Role.SECTION
ROLE_SHAPE = Role.SHAPE
ROLE_SPLITPANE = Role.SPLITPANE
ROLE_VIEWPORT = Role.VIEWPORT
ROLE_TEAROFFMENU = Role.TEAROFFMENU
ROLE_TEXTFRAME = Role.TEXTFRAME
ROLE_TOGGLEBUTTON = Role.TOGGLEBUTTON
ROLE_BORDER = Role.BORDER
ROLE_CARET = Role.CARET
ROLE_CHARACTER = Role.CHARACTER
ROLE_CHART = Role.CHART
ROLE_CURSOR = Role.CURSOR
ROLE_DIAGRAM = Role.DIAGRAM
ROLE_DIAL = Role.DIAL
ROLE_DROPLIST = Role.DROPLIST
ROLE_SPLITBUTTON = Role.SPLITBUTTON
ROLE_MENUBUTTON = Role.MENUBUTTON
ROLE_DROPDOWNBUTTONGRID = Role.DROPDOWNBUTTONGRID
ROLE_MATH = Role.MATH
ROLE_GRIP = Role.GRIP
ROLE_HOTKEYFIELD = Role.HOTKEYFIELD
ROLE_INDICATOR = Role.INDICATOR
ROLE_SPINBUTTON = Role.SPINBUTTON
ROLE_SOUND = Role.SOUND
ROLE_WHITESPACE = Role.WHITESPACE
ROLE_TREEVIEWBUTTON = Role.TREEVIEWBUTTON
ROLE_IPADDRESS = Role.IPADDRESS
ROLE_DESKTOPICON = Role.DESKTOPICON
ROLE_INTERNALFRAME = Role.INTERNALFRAME
ROLE_DESKTOPPANE = Role.DESKTOPPANE
ROLE_OPTIONPANE = Role.OPTIONPANE
ROLE_COLORCHOOSER = Role.COLORCHOOSER
ROLE_FILECHOOSER = Role.FILECHOOSER
ROLE_FILLER = Role.FILLER
ROLE_MENU = Role.MENU
ROLE_PANEL = Role.PANEL
ROLE_PASSWORDEDIT = Role.PASSWORDEDIT
ROLE_FONTCHOOSER = Role.FONTCHOOSER
ROLE_LINE = Role.LINE
ROLE_FONTNAME = Role.FONTNAME
ROLE_FONTSIZE = Role.FONTSIZE
ROLE_BOLD = Role.BOLD
ROLE_ITALIC = Role.ITALIC
ROLE_UNDERLINE = Role.UNDERLINE
ROLE_FGCOLOR = Role.FGCOLOR
ROLE_BGCOLOR = Role.BGCOLOR
ROLE_SUPERSCRIPT = Role.SUPERSCRIPT
ROLE_SUBSCRIPT = Role.SUBSCRIPT
ROLE_STYLE = Role.STYLE
ROLE_INDENT = Role.INDENT
ROLE_ALIGNMENT = Role.ALIGNMENT
ROLE_ALERT = Role.ALERT
ROLE_DATAGRID = Role.DATAGRID
ROLE_DATAITEM = Role.DATAITEM
ROLE_HEADERITEM = Role.HEADERITEM
ROLE_THUMB = Role.THUMB
ROLE_CALENDAR = Role.CALENDAR
ROLE_VIDEO = Role.VIDEO
ROLE_AUDIO = Role.AUDIO
ROLE_CHARTELEMENT = Role.CHARTELEMENT
ROLE_DELETED_CONTENT = Role.DELETED_CONTENT
ROLE_INSERTED_CONTENT = Role.INSERTED_CONTENT
ROLE_LANDMARK = Role.LANDMARK
ROLE_ARTICLE = Role.ARTICLE
ROLE_REGION = Role.REGION
ROLE_FIGURE = Role.FIGURE
ROLE_MARKED_CONTENT = Role.MARKED_CONTENT


# Added to maintain backwards compatibility, marked for deprecation to be removed in 2022.1
if version_year < 2022:
STATE_UNAVAILABLE = State.UNAVAILABLE
STATE_FOCUSED = State.FOCUSED
STATE_SELECTED = State.SELECTED
STATE_BUSY = State.BUSY
STATE_PRESSED = State.PRESSED
STATE_CHECKED = State.CHECKED
STATE_HALFCHECKED = State.HALFCHECKED
STATE_READONLY = State.READONLY
STATE_EXPANDED = State.EXPANDED
STATE_COLLAPSED = State.COLLAPSED
STATE_INVISIBLE = State.INVISIBLE
STATE_VISITED = State.VISITED
STATE_LINKED = State.LINKED
STATE_HASPOPUP = State.HASPOPUP
STATE_PROTECTED = State.PROTECTED
STATE_REQUIRED = State.REQUIRED
STATE_DEFUNCT = State.DEFUNCT
STATE_INVALID_ENTRY = State.INVALID_ENTRY
STATE_MODAL = State.MODAL
STATE_AUTOCOMPLETE = State.AUTOCOMPLETE
STATE_MULTILINE = State.MULTILINE
STATE_ICONIFIED = State.ICONIFIED
STATE_OFFSCREEN = State.OFFSCREEN
STATE_SELECTABLE = State.SELECTABLE
STATE_FOCUSABLE = State.FOCUSABLE
STATE_CLICKABLE = State.CLICKABLE
STATE_EDITABLE = State.EDITABLE
STATE_CHECKABLE = State.CHECKABLE
STATE_DRAGGABLE = State.DRAGGABLE
STATE_DRAGGING = State.DRAGGING
STATE_DROPTARGET = State.DROPTARGET
STATE_SORTED = State.SORTED
STATE_SORTED_ASCENDING = State.SORTED_ASCENDING
STATE_SORTED_DESCENDING = State.SORTED_DESCENDING
STATE_HASLONGDESC = State.HASLONGDESC
STATE_PINNED = State.PINNED
STATE_HASFORMULA = State.HASFORMULA
STATE_HASCOMMENT = State.HASCOMMENT
STATE_OBSCURED = State.OBSCURED
STATE_CROPPED = State.CROPPED
STATE_OVERFLOWING = State.OVERFLOWING
STATE_UNLOCKED = State.UNLOCKED
STATE_HAS_ARIA_DETAILS = State.HAS_ARIA_DETAILS
21 changes: 9 additions & 12 deletions source/controlTypes/isCurrent.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@
# See the file COPYING for more details.
# Copyright (C) 2007-2021 NV Access Limited, Babbage B.V.

from enum import Enum
from typing import Dict

from logHandler import log
from utils.displayString import DisplayStringStrEnum


class IsCurrent(Enum):
class IsCurrent(DisplayStringStrEnum):
"""Values to use within NVDA to denote 'current' values.
These describe if an item is the current item within a particular kind of selection.
EG aria-current
Expand All @@ -22,23 +21,21 @@ class IsCurrent(Enum):
DATE = "date"
TIME = "time"

@property
def _displayStringLabels(self):
return _isCurrentLabels

@property
def displayString(self):
"""
@return: The translated UI display string that should be used for this value of the IsCurrent enum
"""
try:
return _isCurrentLabels[self]
return super().displayString
except KeyError:
log.debugWarning(f"No translation mapping for: {self}")
# there is a value for 'current' but NVDA hasn't learned about it yet,
# at least describe in the general sense that this item is 'current'
return _isCurrentLabels[IsCurrent.YES]
return self.YES.displayString


#: Text to use for 'current' values. These describe if an item is the current item
#: within a particular kind of selection. EG aria-current
_isCurrentLabels: Dict[Enum, str] = {
_isCurrentLabels: Dict[IsCurrent, str] = {
IsCurrent.NO: "", # There is nothing extra to say for items that are not current.
# Translators: Presented when an item is marked as current in a collection of items
IsCurrent.YES: _("current"),
Expand Down

0 comments on commit 8353f5f

Please sign in to comment.