Page 1 of 1
How can the HyperLinkGadget get the keyboard focus?
Posted: Tue Jul 02, 2024 8:30 pm
by Little John
Hi all,
when running the code below, clicking at the HyperLinkGadget with the mouse works as expected, but that gadget doesn't get the keyboard focus – when pressing [Tab], it is skipped (tested with PB 6.11 LTS (x64) on Windows 11).
How must the code be changed, so that the HyperLinkGadget can receive the keyboard focus?
Code: Select all
; Windows only
EnableExplicit
Macro SetUIState (_hwnd_)
SendMessage_(_hwnd_, #WM_UPDATEUISTATE, $30002, 0)
EndMacro
#winMain = 0
Enumeration
#gadCheck
#gadLink
#gadButton
EndEnumeration
Define.i event
If OpenWindow(#winMain, 400, 100, 200, 150, "Demo") = 0
MessageRequester("Fatal error", "Can't open main window.")
End
EndIf
CheckBoxGadget(#gadCheck, 20, 10, 80, 30, "CheckBox")
HyperLinkGadget(#gadLink, 20, 50, 80, 30, "Hyperlink", RGB(0,0,255), #PB_HyperLink_Underline)
ButtonGadget(#gadButton , 20, 90, 80, 30, "Button")
SetActiveGadget(#gadCheck)
SetUIState(WindowID(#winMain)) ; initially show focus rectangle
Repeat
event = WaitWindowEvent()
Select event
Case #PB_Event_Gadget
Select EventGadget()
Case #gadCheck
Debug "CheckBox"
Case #gadLink
Debug "Hyperlink"
Case #gadButton
Debug "Button"
EndSelect
EndSelect
Until event = #PB_Event_CloseWindow
Re: How can the HyperLinkGadget get the keyboard focus?
Posted: Wed Jul 03, 2024 8:06 pm
by Little John
My hope was/is, that one of our WinAPI experts has a solution for this.
When I first encountered this behaviour of the HyperLinkGadget, I thought that it might be intended. But in the meantime I think that it's a bug in PureBasic, especially since Quin pointed out that it causes a serious problem for screenreaders.
Re: How can the HyperLinkGadget get the keyboard focus?
Posted: Wed Jul 03, 2024 9:39 pm
by Justin
This does it, windows only:
Code: Select all
; Windows only
EnableExplicit
Macro SetUIState (_hwnd_)
SendMessage_(_hwnd_, #WM_UPDATEUISTATE, $30002, 0)
EndMacro
#winMain = 0
Enumeration
#gadCheck
#gadLink
#gadButton
EndEnumeration
Global.i g_hl_oldProc
Procedure addWindowStyle(hwnd.i, style.l)
Protected.l oldStyle
oldStyle = GetWindowLong_(hwnd, #GWL_STYLE)
SetWindowLong_(hwnd, #GWL_STYLE, oldStyle | style)
EndProcedure
Procedure drawFocus(hwnd.i)
Protected.RECT rc
Protected.i hdc
hdc = GetDC_(hwnd)
GetClientRect_(hwnd, @rc)
DrawFocusRect_(hdc, @rc)
ReleaseDC_(hwnd, hdc)
EndProcedure
Procedure.i hl_proc(hwnd.i, msg.l, wparam.i, lparam.i)
Select msg
Case #WM_KEYUP
If wparam = #VK_SPACE
PostEvent(#PB_Event_Gadget, #PB_Any, GetProp_(hwnd, "PB_ID"))
EndIf
Case #WM_LBUTTONDOWN
SetFocus_(hwnd)
Case #WM_SETFOCUS
drawFocus(hwnd)
ProcedureReturn 0
Case #WM_PAINT
CallWindowProc_(g_hl_oldProc, hwnd, msg, wparam, lparam)
If GetFocus_() = hwnd
drawFocus(hwnd)
EndIf
ProcedureReturn 0
Case #WM_KILLFOCUS
InvalidateRect_(hwnd, #Null, #True)
ProcedureReturn 0
Case #WM_NCDESTROY
SetWindowLongPtr_(hwnd, #GWLP_WNDPROC, g_hl_oldProc)
EndSelect
ProcedureReturn CallWindowProc_(g_hl_oldProc, hwnd, msg, wparam, lparam)
EndProcedure
Define.i event
If OpenWindow(#winMain, 400, 100, 200, 150, "Demo") = 0
MessageRequester("Fatal error", "Can't open main window.")
End
EndIf
CheckBoxGadget(#gadCheck, 20, 10, 80, 30, "CheckBox")
HyperLinkGadget(#gadLink, 20, 50, 80, 30, "Hyperlink", RGB(0,0,255), #PB_HyperLink_Underline)
ButtonGadget(#gadButton , 20, 90, 80, 30, "Button")
addWindowStyle(GadgetID(#gadLink), #WS_TABSTOP)
g_hl_oldProc = SetWindowLongPtr_(GadgetID(#gadLink), #GWLP_WNDPROC, @hl_proc())
SetActiveGadget(#gadCheck)
SetUIState(WindowID(#winMain)) ; initially show focus rectangle
Repeat
event = WaitWindowEvent()
Select event
Case #PB_Event_Gadget
Select EventGadget()
Case #gadCheck
Debug "CheckBox"
Case #gadLink
Debug "Hyperlink"
Case #gadButton
Debug "Button"
EndSelect
EndSelect
Until event = #PB_Event_CloseWindow
Re: How can the HyperLinkGadget get the keyboard focus?
Posted: Wed Jul 03, 2024 10:01 pm
by Little John
Justin wrote:
This does it, windows only:
That's impressive, thank you!
However, when the button has the focus and I press [Space], then the HyperLinkGadget disappears.
Re: How can the HyperLinkGadget get the keyboard focus?
Posted: Wed Jul 03, 2024 10:52 pm
by RASHAD
Simplify things
Code: Select all
; Windows only
Global result
Macro SetUIState (_hwnd_)
SendMessage_(_hwnd_, #WM_UPDATEUISTATE, $30002, 0)
EndMacro
#winMain = 0
Enumeration
#gadCheck
#gadLink
#gadButton
EndEnumeration
Define.i event
If OpenWindow(#winMain, 400, 100, 200, 150, "Demo") = 0
MessageRequester("Fatal error", "Can't open main window.")
End
EndIf
CheckBoxGadget(#gadCheck, 20, 10, 80, 30, "CheckBox")
HyperLinkGadget(#gadLink, 20, 50, 80, 30, "Hyperlink", RGB(0,0,255), #PB_HyperLink_Underline)
SetWindowLongPtr_(GadgetID(#gadLink), #GWL_STYLE, GetWindowLongPtr_(GadgetID(#gadLink),#GWL_STYLE)|#WS_TABSTOP)
ButtonGadget(#gadButton , 20, 90, 80, 30, "Button")
SetActiveGadget(#gadCheck)
SetUIState(WindowID(#winMain)) ; initially show focus rectangle
Repeat
event = WaitWindowEvent()
Select event
Case #WM_KEYDOWN
keyp = EventwParam()
If keyp = 9
If GetActiveGadget() = 1 And result = 0
hdc = GetDC_(GadgetID(1))
GetClientRect_(GadgetID(1), r.RECT)
result = DrawFocusRect_(hdc, r.RECT)
ReleaseDC_(GadgetID(1), hdc)
EndIf
If GetActiveGadget() <> 1 And result = 1
result = 0
hdc = GetDC_(GadgetID(1))
GetClientRect_(GadgetID(1), r.RECT)
DrawFocusRect_(hdc, r.RECT)
ReleaseDC_(GadgetID(1), hdc)
EndIf
EndIf
If (keyp = 32 Or keyp = 13) And GetActiveGadget() = 1
Debug "Hyperlink"
EndIf
Case #PB_Event_Gadget
Select EventGadget()
Case #gadCheck
Debug "CheckBox"
Case #gadLink
Debug "Hyperlink"
Case #gadButton
Debug "Button"
EndSelect
EndSelect
Until event = #PB_Event_CloseWindow
Re: How can the HyperLinkGadget get the keyboard focus?
Posted: Thu Jul 04, 2024 6:25 am
by Justin
Little John wrote: Wed Jul 03, 2024 10:01 pm
Justin wrote:
This does it, windows only:
That's impressive, thank you!
However, when the button has the focus and I press [Space], then the HyperLinkGadget disappears.
That's because i did put FreeGadget(#gadLink) after the button click for testing, i forgot to remove it, it's fixed now.
Re: How can the HyperLinkGadget get the keyboard focus?
Posted: Thu Jul 04, 2024 7:00 am
by Quin

both solutions (including corrections) seem to work on my machine with NVDA. Although I haven't extensively tested the button behavior yet like LittleJohn.
Re: How can the HyperLinkGadget get the keyboard focus?
Posted: Thu Jul 04, 2024 8:07 pm
by Little John
@RASHAD:
Thank you!
Justin wrote:
That's because i did put FreeGadget(#gadLink) after the button click for testing, i forgot to remove it, it's fixed now.
Fix confirmed.
By the way, in your procedure
addWindowStyle(), it should be GetWindowLong
Ptr_() and SetWindowLong
Ptr_().

Thanks again!
Re: How can the HyperLinkGadget get the keyboard focus?
Posted: Sat Jul 06, 2024 8:50 am
by Mesa
If transparency is not important, you can use a canvasgadget as a container like that(multiplatform):
Code: Select all
; EnableExplicit
#winMain = 0
Enumeration
#gadCheck
#gadLink
#gadButton
EndEnumeration
Define.i event
If OpenWindow(#winMain, 400, 100, 200, 150, "Demo") = 0
MessageRequester("Fatal error", "Can't open main window.")
End
EndIf
SetWindowColor(#winMain,#White)
CheckBoxGadget(#gadCheck, 20, 10, 80, 30, "CheckBox")
cv=CanvasGadget(#PB_Any,20,50,80,30,#PB_Canvas_DrawFocus|#PB_Canvas_Container|#PB_Canvas_Keyboard )
HyperLinkGadget(#gadLink, 5, 5, 70, 20, "Hyperlink", RGB(0,0,255), #PB_HyperLink_Underline)
CloseGadgetList()
ButtonGadget(#gadButton , 20, 90, 80, 30, "Button")
SetGadgetColor(#gadLink,#PB_Gadget_BackColor, #White)
SetGadgetColor(#gadLink,#PB_Gadget_FrontColor, #Blue)
SetActiveGadget(cv)
Repeat
event = WaitWindowEvent()
Select event
Case #PB_Event_Gadget
Select EventGadget()
Case #gadCheck
Debug "CheckBox"
Case cv
Select EventType()
Case #PB_EventType_Focus
Case #PB_EventType_KeyUp
If GetGadgetAttribute(cv, #PB_Canvas_Key)=#PB_Shortcut_Space
Debug "Hyperlink cv"
EndIf
EndSelect
Case #gadLink
Debug "Hyperlink"
Case #gadButton
Debug "Button"
EndSelect
EndSelect
Until event = #PB_Event_CloseWindow
Mesa.
Re: How can the HyperLinkGadget get the keyboard focus?
Posted: Sat Jul 06, 2024 9:53 am
by Quin
Mesa wrote: Sat Jul 06, 2024 8:50 am
If transparency is not important, you can use a canvasgadget as a container like that(multiplatform):
Code: Select all
; EnableExplicit
#winMain = 0
Enumeration
#gadCheck
#gadLink
#gadButton
EndEnumeration
Define.i event
If OpenWindow(#winMain, 400, 100, 200, 150, "Demo") = 0
MessageRequester("Fatal error", "Can't open main window.")
End
EndIf
SetWindowColor(#winMain,#White)
CheckBoxGadget(#gadCheck, 20, 10, 80, 30, "CheckBox")
cv=CanvasGadget(#PB_Any,20,50,80,30,#PB_Canvas_DrawFocus|#PB_Canvas_Container|#PB_Canvas_Keyboard )
HyperLinkGadget(#gadLink, 5, 5, 70, 20, "Hyperlink", RGB(0,0,255), #PB_HyperLink_Underline)
CloseGadgetList()
ButtonGadget(#gadButton , 20, 90, 80, 30, "Button")
SetGadgetColor(#gadLink,#PB_Gadget_BackColor, #White)
SetGadgetColor(#gadLink,#PB_Gadget_FrontColor, #Blue)
SetActiveGadget(cv)
Repeat
event = WaitWindowEvent()
Select event
Case #PB_Event_Gadget
Select EventGadget()
Case #gadCheck
Debug "CheckBox"
Case cv
Select EventType()
Case #PB_EventType_Focus
Case #PB_EventType_KeyUp
If GetGadgetAttribute(cv, #PB_Canvas_Key)=#PB_Shortcut_Space
Debug "Hyperlink cv"
EndIf
EndSelect
Case #gadLink
Debug "Hyperlink"
Case #gadButton
Debug "Button"
EndSelect
EndSelect
Until event = #PB_Event_CloseWindow
Mesa.
Maybe part of the overarching feature request should be to be able to call SetGadgetText() on a CanvasGadget...
It doesn't work if I put this near your color setting lines, like ButtonImageGadget().
Code: Select all
SetGadgetText(#gadLink, "hyperlink")
Re: How can the HyperLinkGadget get the keyboard focus?
Posted: Sat Jul 06, 2024 3:00 pm
by Little John
Mesa wrote: Sat Jul 06, 2024 8:50 am
If transparency is not important, you can use a canvasgadget as a container like that(multiplatform):
Very cool. Thank you, Mesa!
The CanvasGadget seems to be a Jack of all trades.

Re: How can the HyperLinkGadget get the keyboard focus?
Posted: Sat Jul 06, 2024 3:09 pm
by Little John
Quin wrote: Sat Jul 06, 2024 9:53 am
Maybe part of the overarching feature request should be to be able to call SetGadgetText() on a CanvasGadget...
It doesn't work if I put this near your color setting lines, like ButtonImageGadget().
Code: Select all
SetGadgetText(#gadLink, "hyperlink")
This works here with Mesa's code on Windows 11, using the built-in screenreader:
Code: Select all
[...]
cv = CanvasGadget(#PB_Any, 20, 50, 80, 24, #PB_Canvas_DrawFocus|#PB_Canvas_Container|#PB_Canvas_Keyboard)
HyperLinkGadget(#gadLink, 2, 2, 70, 18, "Hyperlink", RGB(0,0,255), #PB_HyperLink_Underline)
CloseGadgetList()
SetGadgetText(cv, "HyperLink")
[...]
Re: How can the HyperLinkGadget get the keyboard focus?
Posted: Sat Jul 06, 2024 6:17 pm
by Justin
If transparency is not important, you can use a canvasgadget as a container like that(multiplatform):
It won't work on linux as is, you will need to handle tab key presses by yourself. The canvas gadget is outside the focus ring as i mentioned here:
https://www.purebasic.fr/english/viewtopic.php?t=84388
Actually i think doing it by yourself is a good thing and should not be removed, but having an option to do it by the os would be nice too.
Re: How can the HyperLinkGadget get the keyboard focus?
Posted: Sat Jul 06, 2024 11:01 pm
by Quin
Justin wrote: Sat Jul 06, 2024 6:17 pm
Actually i think doing it by yourself is a good thing and should not be removed, but having an option to do it by the os would be nice too.
I completely agree, this is how all of PB's accessibility/keyboard focus infrastructure should be IMO
Re: How can the HyperLinkGadget get the keyboard focus?
Posted: Mon Jul 08, 2024 1:36 pm
by Mesa
Justin wrote: Sat Jul 06, 2024 6:17 pm
by Justin » Sat Jul 06, 2024 6:17 pm
If transparency is not important, you can use a canvasgadget as a container like that(multiplatform):
It won't work on linux as is, you will need to handle tab key presses by yourself. The canvas gadget is outside the focus ring as i mentioned
Does this module managing the tab key work under Linux?
https://www.purebasic.fr/english/viewto ... 54#p598854
M.