Add explorer key actions and improve auto-voice handling#1294
Closed
Add explorer key actions and improve auto-voice handling#1294
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
This PR adds a number of explorer keyboard actions from v3 that were left out in the rewrite for v4. These include:
Zto get the full speech for a collapsed subexpression.Vto mark a position for later retrieval.Pto cycle through the marked positions.Uto remove all marks and go to the initial location where the explorer was started.Shift+Arrowfor moving in a table0-9+0-9to move to a specific table cell by index.Hometo go to the top level of the expression.Plus the new
sto start or stop auto-voicing.There are also changes to improve the collapse symbol for roots (since the surd character in mathjax-newcm is not centered by default). This requires a new version of the font that includes a variant that is centered; I have made the modifications, but haven't pushed a new version of the fonts yet.
I updated the help dialog to include the new keys, and changed how the modal dialog is handled: the old version used focus-out to close the dialog, but some screen readers end up giving focus out events when reading the links, so that would close the dialog prematurely. So now it uses a transparent background element to trap clicks outside the dialog box and closes on that.
The handling of auto-voicing has been improved, not only by the addition of the
skey, but also by allowing any keypress or mouse down to stop the auto-voicing. That means the control key doesn't need to be mapped explicitly, since any keypress cancels the speech.Finally, I remove the
blankspeech that I had included earlier, and now any node with no speech is skipped by the explorer (thedata-speech-nodeattribute is removed, so it is never found by the explorer).Details
The changes to
collapse.tsare to set themathvariantto the tex variant font so that the surd will be properly placed. It requires a new version of the font, which isn't public yet.The
explorer.tschanges are just additional CSS to handle the help dialog better (I put the keys into akbdelement with the styling that you had in the documentation pages, and I changed theullist ot not have bullets, which were being read).The main changes are in
KeyExplorer.ts:The changes to the dialog text is mostly addition of the new keyboard commands.
The key mappings are changed to handle the shift key for the arrows, to add the new commands, and to remove the upper-case versions of the keys, which are handled now in the key-press event handler. The map value is now an array consisting of a keymapping and a boolean, rather than having lots of
explorer.active ? explorer.fn() : booleanconstructs. Theexplorer.activetest is now done in the keydown event handler when there ius a boolean in the array.The
marks,currentMark, andlastMarkare used by theV,P, andUkeys to handle the position marks.The
pendindIndexarray is used for the cell index processing. When the number keynis pressed, this array is set to[0, n]. It works this way: when a key is pressed, the array is shifted to the left, making it[n]if the previous key was a number key, and[]if not. So if a number key is followed by a nother number key,pendingIndexwill be[n]for the previous number and the new key will be the second number, and we can use these to get the indices for the cell to jumo to. If a number key is not followed by another number, then the[n]will be shifted off the array when the next key is pressed, so pressing a number key will only have a value inpendingIndexwhen it immediately follows a number key.The
Keydownevent handler shifts thependingIndexarray, as described above, and cancels any auto-voicing in progress. The other change is to handle upper-case letters by converting them to lower-case so we don't have to have duplicate entries in the key map.The
Mousedownevents clear the pending index array and cancels any auto-voicing in progress.The
controlKey()method is removed, as it is now redundant.The
homeKey()action is added.The arrow key functions are modified to check if the shift is pressed, and if so, they call
moveToNeighborCell()with index offsets, otherwise do the usual move.The new
moveToNeighborCell()function finds the cell (if any) containing the current node, and gets the indices of the cell within its table. Then it get the cell at the correct offset from that position and selects that.Next come the position marking functions. The
addMark()method pushes the current position onto the mark list and keeps the index into the list ascurrentMark, unless the current position is the same as the current mark, in which case, the usual text for that position is read again. That allows the "Position marked" text to be replaced by the usual speech at that point.The
prevMark()function checks ifcurrentMarkis unset (-1), and if so, setscurrentMarkto the last mark in the list, and if there is none, it reads either the initial position when we started exploring this expression (lastMark) or the top level of the expression. If there is a mark to be read, we jump to that mark and movecurrentMarkto be the next one in the list, so that if we hitPmultiple times in a row, we cycle through the positions. BecausesetCurrent()clearscurrentMark, we save it before setting the new position.The
clearMarks()function does what it says, clears the marks and then goes to the initial position by callingprevMark().the
autoVoice()method toggles the auto-voicing option either in the menu if there is one, or in the document options if not, then updates (to start the voicing).The
numberKey()function handles jumping to a specific cell by index. Here, if we aren't in a table, we quit with a honk. Then we change 0 to 10, if needed. Then, if there is no pending index (the "else" clause), this is the first index, so we save it in thependingIndexas[0, n], as described above (it will shift into[n]when the next number is pressed). We speak a phrase to indicate that we have the row number and are waiting for the column number. Otherwise, if there is a pending index, that means we have a previous index and are ready to jump. We look up the table for where we are, and the get the cell with the proper index. We speak the column index (to complete the "jump to row n and column" phrase, clear the index array, and finally select the new cell after a slight delay to allow the column number to be read.You may not like the extra "jump to..." speech, but I think there needs to be feedback for the numbers being pressed so the user knows they are being processed. It could be that they just say the number and not the whole phrase. Perhaps another menu option needs to be made for that?
The
details()function implements speaking the full text of a collapsed expression. Since that test is not available in the attributes of the tree, we have to work a little bit to get this text.First, we check if this is a collapsed node, and if not, we jsut speek the current node again. Otherwise, we look through the internal MathML for the item with the current node's semantic ID. We use that node to create a new MathML string from the subtree at that node, and add
<math>around it, if needed, removing any previousdata-semanticelements. Finally, we call the (new)speechFor()method of the MathItem (described below) to get the top-level speech and Braille strings for the MathML, and speak them when they arrive.The
help()function is modified to include the new transparent background (used to trap clicks outside the dialog), and to use a common dynamicclose()function for the event handlers that close the dialog.The
setCurrent()function has additions to track thelastMarkposition and to clear thecurrentMarkso thatPwill start cycling from the taop of the mark list if we do anything other thanP.The
nodeId()andparentId()functions are just service functions to make gettingdata-semanticids easier. ThegetNode()function returns the HTML node with the given semantic ID, while thechildArray()method returns an array of child IDs for the given HTML node.The
tableCell()function looks through the parents of a node for the first one with roletable(the cell that contains the node, if any), whilecellTable()returns the HTML node for the table that contains the given cell node.The
cellPosition()function returns the (row,column) indices of a cell within a table, whilecellAt()returns the HTML node for the cell at a given (row,column) position within the table, if any.The changes to
region.tsare for cancelling the auto-voicing. In particular, we want to make sure that the synchronous highlighting is removed if the speech is interrupted. (It had been being left with a permanent red background.) We add acancelVoice()function that can be called from the KeyExplorer.In
semantic-enrich.ts, we add atoEnriched()function that can be called to enrich an arbitrary MathML string. This is used by theZexplorer key to get the full speech for a collapsed node.Similarly, in
speech.ts, we add aspeechFor()function that returns the speech and Braille strings for an arbitrary mathML string. Again, this is for theZexplorer key. ThespeechFor()method uses a newSpeechFor() method in theGeneratorPool.tsfile, which in turn calls a newspeechFor()function inWebWorker.ts`, that returns a promise for the when worker prodiuces the speech structure.Finally,
WebWorker.tsincludes the changes to not mark nodes with blank speech or blank braille.