[jawsscripts] Re: Implementing first-letter navigation

  • From: Theodore Cooke <theodorecooke@xxxxxxxxx>
  • To: jawsscripts@xxxxxxxxxxxxx
  • Date: Tue, 24 Nov 2020 13:01:03 -0500

To add to what Udo said, once you find a way of capturing the
alphanumeric characters, you can use UIA tree view walkers to find the
next element that starts with that given letter. If you want to
implement it so it works for multiple letters, such as pressing t then
h goes to the next item starting with a th, then you can use the
IsSameScript function to determine if the letters have been pressed in
rapid succession.

On 11/9/20, Udo Egner-Walter <udo.egner-walter@xxxxxx> wrote:

Hi Sean,

you asked about the hook and I will insert a step by step instruction. But
meanwhile I thought about your problem and now think it could be done
without using a hook.

You could use the TrapKeys function of JAWS. This "traps" all the keys not
assigned to scripts. Since you are using only one letter keystrokes this
should be fine for you. I've tried this out myself on Explorer and inserted
the code I used too (at the end of the mail).

The difference between the TrapKey function and the Hook is that you
trapping keys not attached to scripts with TrapKey and on the other hand
have full control over scripts with the hook function.

First here are the steps for using a hook:

1. Make a script to start the hook

Script StartHook ()
AddHook (HK_SCRIPT, "KeyboardHookFunction")
EndScript

2. Make a script to stop the hook

Script StopHook ()
RemoveHook (HK_SCRIPT, "KeyboardHookFunction")
EndScript

3. Make a function that is called every time a script was executed

After starting the hook the function we made as the second parameter
("KeyboardHookFunction") is called every time we start a script. In this
function we can decide if we want a script to run or to stop. If we return
true the script is allowed to run and if we return false the script is
stopped.

int Function KeyboardHookFunction (string ScriptName)
If ScriptName == "StopHook" then
SayString ("Stop script now")
Return True
EndIf

; All other Script are not allowed
Return False
EndFunction

4. Trap the keys

To trap the keys not assigned to scripts we start trapping in the start hook
script and stop trapping in the stop hook script: So we add the lines to the
script:


Script StartHook ()
AddHook (HK_SCRIPT, "KeyboardHookFunction")
; trap the keys
TrapKeys (True, True)
EndScript

Script StopHook ()
RemoveHook (HK_SCRIPT, "KeyboardHookFunction")
; Don't trap the keys any longer
TrapKeys (False)
EndScript

5. React on key pressing

To react to keys being pressed we should remember if the hook function is
running. Therefore we save this in a global variable:

globals
int giHookActive

Script StartHook ()
AddHook (HK_SCRIPT, "KeyboardHookFunction")
TrapKeys (True, True)
; remember the hook is active
giHookActive = True
EndScript

Script StopHook ()
RemoveHook (HK_SCRIPT, "KeyboardHookFunction")
TrapKeys (False)
; remember the hook is no longer active
giHookActive = False
EndScript

6. No we can use a key press event of JAWS to react on keys being pressed:

Void Function KeyPressedEvent (int nKey, string strKeyName, int
nIsBrailleKey, int nIsScriptKey)

If giHookActive then
; speak the key being pressed
SayString (strKeyName)
EndIf

; react on pressing Escape (only if
; the hook is active)
if strKeyName == "Escape" && giHookActive then
PerformScript StopHook ()
EndIf

; pass the key pressed event to other functions
KeyPressedEvent (nKey,strKeyName,nIsBrailleKey,nIsScriptKey)
EndFunction

These are the steps if you want to use a keyboard hook.

Now a little example of doing one-letter navigation (here Explorer in
Windows). If there are many tree items the script will be very slow. But it
should give you a hint how to do this:

<Code start>

globals
int giTrapKeys

; Script to activate trapping of keys
Script TrapMyKeys ()
; trap keys not assigned to a script
TrapKeys (True)
; say the trap mode is on
; (you can delete the line after testing the script
; if you want)
SayString ("trapping keys")
; remember in a global variable that
; trapping keys is running
giTrapKeys = True
EndScript

; script to deactivate trapping of keys
Script DontTrapMyKeys ()
; Don't trap keys any longer
TrapKeys (False)
; say that trapping keys is ending
SayString ("trapping keys is off")
; and remember this in a global variable
giTrapKeys  = False
EndScript

; function is called every time a key is pressed
Void Function ProcessKeyPressed (int nKey, string strKeyName, int
nIsBrailleKey, int nIsScriptKey)
var
int iCharValue

; If we are trapping keys
If giTrapKeys then
            ; check if the string is 1 char of length
            If StringLength (strKeyName) == 1 then
              ; Get ASCII-value of this char
               iCharValue = GetCharacterValue (strKeyName)
                        ; If the ASCII value is between A and Z
                        If (iCharValue > 64) && (iCharValue < 91)
                        ; run search function (with char as parameter)
                        DoCharSearch (strKeyName)
                        EndIf ; If (iCharValue > 64) && (iCharValue < 91)
            EndIf ; If StringLength (strKeyName) == 1 then
EndIf
; pas event to other functions
ProcessKeyPressed(nKey,strKeyName,nIsBrailleKey,nIsScriptKey)
EndFunction

; search function
void Function DoCharSearch (string sChar)
var
object oUIA = CreateObjectEx ("FreedomSci.UIA", 0,
"UIAScriptAPI.x.manifest"),
object oFocusElement = oUIA.GetFocusedElement().BuildUpdatedCache(),
object oElement = oUIA.GetElementFromHandle(GetAppMainWindow (GetFocus())),
object oElements,
object oCondition,
object oEntry,
int iBegin = False,
int i

; search for tree items
oCondition = oUIA.CreateIntPropertyCondition (UIA_ControlTypePropertyId,
UIA_TreeItemControlTypeId)
; search the whole app
oElements = oElement.FindAll (TreeScope_Descendants, oCondition)

; Loop over all tree items
; (For each loop only runs the first time so later we use
; for loop)
ForEach oEntry in oElements
; Since we are search at the current position
; we will begin if "iBegin" is set to true
            if iBegin then
                        ; If the first char of the name is our searched char

                        If StringLeft (oEntry.Name, 1) == sChar then
                        ; focus it
                        oEntry.SetFocus ()
                        SayString ("found")
                        ; and leave the funciton
                        Return
                        EndIf
            EndIf ; If iBegin then

; here we check if the item of the loop is
; the item being focused if we started the script
If oUIA.CompareElements (oFocusElement, oEntry) then
; If yes then set iBegin to true
iBegin = True
EndIf ; If oUIA.CompareElements (oFocusElement, oEntry) then
EndForEach

; if the function still runs here we didn't find something
; beneath the focused item so we start the
; search at the beginning
SayString ("Nothing found yet")
; This time we use a for loop because
; ForEach loop only runs one time in a script
for i=0 to oElements.Count-1
                        ; if the first char of the name is our search char
                        If StringLeft (oElements(i).Name, 1) == sChar then
                        ; focus it
                        oElements(i).SetFocus ()
                        SayString ("gefunden")
                        ; and leave the function
                        Return
                        EndIf
EndFor
; if the function is still running to this point
; we have found nothing
SayString ("Nothing found in the whole tree")
EndFunction

; Here we use a FocusChangedEvenht to
; automatically start the script and stop it

void function FocusChangedEventEx (
            handle hwndFocus, int nObject, int nChild,
            handle hwndPrevFocus, int nPrevObject, int nPrevChild,
            int nChangeDepth)
var
object oUIA = CreateObjectEx ("FreedomSci.UIA", 0,
"UIAScriptAPI.x.manifest"),
object oFocusElement = oUIA.GetFocusedElement().BuildUpdatedCache()

; if the control type of the focused element is a tree item
If oFocusElement.ControlType == UIA_TreeItemControlTypeId then
; we can start the trapping of keys
PerformScript TrapMyKeys ()
else
; in the other case stop trapping of keys
PerformScript DontTrapMyKeys ()
EndIf
; Pass event to other functions
FocusChangedEventEx (hwndFocus, nObject, nChild, hwndPrevFocus, nPrevObject,
nPrevChild, nChangeDepth)
EndFunction

; And very important
; Stop trapping of keys if you leave your app

void Function AutoFinishEvent ()
PerformScript DontTrapMyKeys ()
EndFunction

<Code End>

Hope this helps. And I hope the mails clients didn't mix the lines so the
compiler is angry ðŸ˜‰
Udo



Am 08.11.2020 um 15:24 schrieb Sean Farrow
<sean.farrow@xxxxxxxxxxxxxxxx>:

Hi,
The tree view is a custom control, using the .Net framework and more
specifically WinForms. It kind of supports MSAA, so can be traversed.

A hook sounds like the best bet, but it would be good to find out more
about how you'd do it.
Thanks,
Sean.
-----Original Message-----
From: jawsscripts-bounce@xxxxxxxxxxxxx <jawsscripts-bounce@xxxxxxxxxxxxx>
On Behalf Of Udo Egner-Walter
Sent: 08 November 2020 08:36
To: jawsscripts@xxxxxxxxxxxxx
Subject: [jawsscripts] Re: Implementing first-letter navigation

Hi Sean,

there are some ways to do this and all of them have some advantages and
disadvantages. Here are some thoughts about this:

First of all the question is this a SysTreeView32 or another control? Does
the treeview support UIA?

In case of first-letter navigation there are some ways:

1. Using one of the JAWS keyboard events or keyboard event helpers, for
example KeyPressedEvent (). So you can react on the pressing of a specific
key. One disadvantage is here that you can't stop sending the key to the
app. If you app uses a key being pressed the app will execute the command
assigned to the key. For single keystrokes this rather unusual but you
should keep this in mind.

2. You can install a keyboard hook. If you do this you can trap all keys
and decide in your script which one should pass and which one not. One
disadvantage here is you might not be able to stop the keyboard hook and
the keys and scripts being trapped and you can't work no longer till you
release the keyboard hook.

3. You can implement some kind of virtual keys. If you do this your
scripts control the keys till you are pressing a key to end this virtual
mode. I know two ways of doing this:

a) Using the layered keys in combination with a * at the end of the key.
So the keys are still active till you press a key without * in the layered
keys section.

b) Load another key section from an ini-file where you can assign single
keys to you scripts.

This is only an overview and the possibilities I know. if you want more
details please let me know and I try to explain this in details and with
examples.

But implementing the first-letter navigation makes only sense if you can
focus your tree nodes by script. Therefore my questions at the beginning
of the mail.

Hope this helps
Udo


Am 07.11.2020 um 17:01 schrieb Sean Farrow
<sean.farrow@xxxxxxxxxxxxxxxx>:

Hi all,
I've got a tree view that does not natively implement first-letter
navigation, what is the best way of doing this effectively.

Any help appreciated.

Thanks,
Sean.


__________

View the list's information and change your settings at
http://www.freelists.org/list/jawsscripts


__________

View the list's information and change your settings at
http://www.freelists.org/list/jawsscripts

__________�

View the list's information and change your settings at
http://www.freelists.org/list/jawsscripts


__________�

View the list's information and change your settings at
http://www.freelists.org/list/jawsscripts


__________�

View the list's information and change your settings at 
http://www.freelists.org/list/jawsscripts

Other related posts: