Skip to content

Always report object states in same order, regardless whether positive or negative#7279

Merged
michaelDCurran merged 13 commits into
nvaccess:masterfrom
BabbageCom:i7076
Jan 1, 2018
Merged

Always report object states in same order, regardless whether positive or negative#7279
michaelDCurran merged 13 commits into
nvaccess:masterfrom
BabbageCom:i7076

Conversation

@LeonarddeR
Copy link
Copy Markdown
Collaborator

Link to issue number:

fixes #7076

Summary of the issue:

When reporting object states to the user in speech or braille, the positive object states are processed before the negative states. This creates the following problem as reported by @dkager:

Can be reproduced in many lists with checkable items in Windows 10, e.g. the disk clean-up utility.
Unchecked items are brailled as  Foo sel ( ) chk , checked ones as  Foo (x) sel chk .

Description of how this pull request fixes the issue:

  1. For both speech and braille, de positive and negative states sets are merged and numerically sorted. When looping through this merged, sorted list of states, we check whether the state is positive or negative and the appropriate label will be spoken or brailled.
  2. The negative state for controlTypes.STATE_DROPTARGET (Done dragging) is moved from speech.py to controlTypes.py

Testing performed:

In disk clean up, the selected state is now always spoken before the checked state, regardless whether positive or negative

Known issues with pull request:

  1. As I noted in Inconsistent reporting of checkable list items #7076 (comment), This changes the order of presentation people are accustomed to in some situations. E.g. invalid entry required will change to required invalid entry. Do we care about this, or do we want to have a certain order of priority as @jcsteh suggested in Inconsistent reporting of checkable list items #7076 (comment)?
  2. We probably want to test whether the "done dragging" case still works correctly.

…ame order, regardless whether they are positive or negative. Fixes #7076
Copy link
Copy Markdown
Contributor

@dkager dkager left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Quite a bit of code duplicaion, but I guess the states for braille and speech are not guaranteed to be identical?

Comment thread source/braille.py Outdated
elif state in negativeStates:
# Translators: Indicates that a particular state on an object is negated.
# Separate braille abbreviations have now been defined for commonly negated states (e.g. ( ) for not checked),
# and, if no braille abbreviations are provided, we fall back to the defined state labels in ControlTypes,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Slightly more spaces than necessary. :)

Leonard de Ruijter added 2 commits June 22, 2017 18:09
Comment thread source/speech.py
negativeStates=propertyValues['negativeStates']
else:
negativeStates=None
if negativeStates is not None or (reason != controlTypes.REASON_CHANGE and states is not None):
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jcsteh: Could you clarify this line? Was there a particular reason why if negativeStates is not None wasn't enough?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you clarify this line? Was there a particular reason why if negativeStates is not None wasn't enough?

The negativeStates argument is only provided for REASON_CHANGE. It specifies states that have just changed from positive to negative. However, for other reasons (e.g. REASON_FOCUS), we still want to report certain states if they aren't present; e.g. "not checked".

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. In the new code, it seems this check is no longer necessary as I provide empty sets as default

Comment thread source/controlTypes.py Outdated
# Return all negative states which should be spoken, excluding the positive states.
return speakNegatives - states

def processAndLabelStates(role, states, reason, positiveStates, negativeStates, positiveStateLabelDict={}, negativeStateLabelDict={}):
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm happy to provide a Doc-string for this, but processPositiveStates and processNegativeStates don't have a doc string yet. May be the initial author of these functions can provide some explanation (e.g. about why we both have states and positiveStates parameters for processPositiveStates)? We can add doc strings for these in this pr as well.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why we both have states and positiveStates parameters for processPositiveStates

states contains the current states for the object. For REASON_CHANGE, positiveStates contains only those states that just got added. In other words, positiveStates contains only the states we want to possibly be reported at this time, but we might need the "unfiltered" states for the object, so we provide states as well.

@LeonarddeR
Copy link
Copy Markdown
Collaborator Author

@jcsteh: Apart from doc strings, I think this is ready for review.

@LeonarddeR
Copy link
Copy Markdown
Collaborator Author

This now contains doc strings as well. @dkager: Would you like to review them? Your grammar is better than mine. :)

Copy link
Copy Markdown
Contributor

@dkager dkager left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty good.

Comment thread source/braille.py Outdated
textList.extend(positiveStateLabels.get(state, controlTypes.stateLabels[state]) for state in positiveStates)
negativeStates = controlTypes.processNegativeStates(role, states, controlTypes.REASON_FOCUS, None)
textList.extend(negativeStateLabels.get(state, controlTypes.negativeStateLabels.get(state, _("not %s") % controlTypes.stateLabels[state])) for state in negativeStates)
textList.extend(controlTypes.processAndLabelStates(role, states, controlTypes.REASON_FOCUS, states,None,positiveStateLabels,negativeStateLabels))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent use of space after comma. Maybe pull this function out into two lines to improve readability?

Comment thread source/controlTypes.py
# Translators: This is presented when a checkbox is not checked.
STATE_CHECKED:_("not checked"),
# Translators: This is presented when drag and drop is finished.
# This is only reported for objects which support accessible drag and drop.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think "that support" is the correct form here.

Comment thread source/controlTypes.py Outdated

def processPositiveStates(role, states, reason, positiveStates):
"""Processes the states for an object and returns the positive states to output for a specified reason.
For example, if C{STATE_CHECKED} is among the returned states, it means that the processed object is checked.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trivial I guess, but "in the returned states" is a bit closer to what you'd actually write in a program to check this.

Comment thread source/controlTypes.py Outdated
For example, if C{STATE_CHECKED} is among the returned states, it means that the processed object is checked.
@param role: The role of the object to process states for (e.g. C{ROLE_CHECKBOX}.
@type role: int
@param states: Usually the raw states for an object to process.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This implies there are exceptions. What are they?

Comment thread source/controlTypes.py Outdated
@type role: int
@param states: Usually the raw states for an object to process.
@type states: set
@param reason: The reason to process the states for (e.g. C{REASON_FOCUS}.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think "for" should be removed.

Comment thread source/controlTypes.py Outdated

def processNegativeStates(role, states, reason, negativeStates):
"""Processes the states for an object and returns the negative states to output for a specified reason.
For example, if C{STATE_CHECKED} is among the returned states, it means that the processed object is not checked.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same stuff as above.

Comment thread source/controlTypes.py Outdated
# Return all negative states which should be spoken, excluding the positive states.
return speakNegatives - states

def processAndLabelStates(role, states, reason, positiveStates, negativeStates, positiveStateLabelDict={}, negativeStateLabelDict={}):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In another PR @jcsteh cautioned against using empty dicts as default function parameters. I wonder if that applies here.

Comment thread source/controlTypes.py Outdated
"""Processes the states for an object and returns the appropriate state labels for both positive and negative states.
@param role: The role of the object to process states for (e.g. C{ROLE_CHECKBOX}.
@type role: int
@param states: Usually the raw states for an object to process.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same stuff as above.

Comment thread source/controlTypes.py Outdated
if state in positiveStates:
mergedStateLabels.append(positiveStateLabelDict.get(state, stateLabels[state]))
elif state in negativeStates:
# Translators: Indicates that a particular state on an object is negated.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"of an object"

@LeonarddeR
Copy link
Copy Markdown
Collaborator Author

@dkager: Could you review my most recent commits?

@dkager
Copy link
Copy Markdown
Contributor

dkager commented Dec 3, 2017

As far as small changes bringing big joy go, this is still one of my favorites.

michaelDCurran added a commit that referenced this pull request Dec 12, 2017
@michaelDCurran michaelDCurran merged commit 3bd729e into nvaccess:master Jan 1, 2018
@nvaccessAuto nvaccessAuto added this to the 2018.1 milestone Jan 1, 2018
@LeonarddeR LeonarddeR added the BabbageWork Pull requests filed on behalf of Babbage B.V. label Oct 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

BabbageWork Pull requests filed on behalf of Babbage B.V.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Inconsistent reporting of checkable list items

5 participants