Screen Reader Issue - Graphics and Text

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
User avatar
JoeWinograd
Posts: 2177
Joined: 10 Feb 2014, 20:00
Location: U.S. Central Time Zone

Screen Reader Issue - Graphics and Text

10 Jun 2021, 12:19

Hi Folks,

I'm working on a program where some of the target users are visually impaired (in some cases, blind). Because of this, the program offers a choice of colors, fonts, and font sizes. An issue with AHK's standard checkboxes and radio buttons is that their size is constant. To get around this, I'm using code developed by Hellbent that can create checkboxes of specified sizes...it's a Class that he calls AutoSizeCheckbox...and it is great stuff!

The original code that he developed creates a button that is a pure graphic...no text. This is an issue for visually impaired/blind users who use a screen reader program (like JAWS and NVDA) to speak text aloud. So, Hellbent enhanced his code to place a text control under the graphic so that a screen reader will find text and be able to speak it.

Visually, it appears to work fine, but it exhibits this really strange behavior: When hovering on the first button in the dialog, the NVDA screen reader speaks the title bar; when hovering on the second button, NVDA speaks the first button; when hovering on the third, it speaks the second; etc.

To troubleshoot this, Hellbent wrote this script (I modified it slightly):

Code: Select all

;*******************************
;#Include your Gdip Library here
;*******************************
#SingleInstance, Force
SetBatchLines, -1

GDIP_Startup()

global ButtonNames := ["Screen","Reader","Problem","Speaks","Previous","Button"]
, Colors := ["FF0000","00FF00","FFFFFF","FF00FF","FFFF00","00FFFF"]
, Handles := []

Gui, 1:+AlwaysOnTop -DPIScale
Gui, 1:Color, 22262A
Gui, 1:Font, cWhite

Loop, % ButtonNames.Length() {
  if(A_Index=1)
    Options := "c" Colors[A_Index] " xm ym w300 h30  +Border hwndButtonHandle gTestTrigger"
  else if( A_Index = 4 || A_Index = 7 )
    Options := "c" Colors[A_Index] " xm y+10 w300 h30  +Border hwndButtonHandle gTestTrigger"
  else
    Options := "c" Colors[A_Index] " x+10 yp w300 h30  +Border hwndButtonHandle gTestTrigger"

  Gui, Add, Text, % Options , % ButtonNames[A_Index]
  Handles[A_Index] := ButtonHandle
}

Gui, 1:Add, Button, xm y+20 -Theme gDrawGraphics, Draw Checkbox Graphics on Text Controls
Gui, 1:Margin, , 20
Gui, 1:Show,,This is the title bar

return
GuiClose:
GuiContextMenu:
*ESC::ExitApp

DrawGraphics:
  Loop, % Handles.Length()  {
    GuiControl, 1: +0xE , % Handles[A_Index]
    HB_BITMAP_MAKER()
  }
  return

TestTrigger:
  SoundBeep, 600
  return

HB_BITMAP_MAKER(){
  ;Bitmap Created Using: HB Bitmap Maker
  Static Index := 1
  pBitmap := Gdip_CreateBitmap( 300 , 30 ) , G := Gdip_GraphicsFromImage( pBitmap ) , Gdip_SetSmoothingMode( G , 2 )
  Brush := Gdip_BrushCreateSolid( "0xFF22262A" ) , Gdip_FillRectangle( G , Brush , -1 , -1 , 302 , 52 ) , Gdip_DeleteBrush( Brush )
  Brush := Gdip_BrushCreateSolid( "0xFF" Colors[Index] ) , Gdip_FillRoundedRectangle( G , Brush , 1 , 1 , 298 , 28 , 5 ) , Gdip_DeleteBrush( Brush )
  Brush := Gdip_CreateLineBrushFromRect( 1 , 1 , 299 , 28 , "0xFFF0F0F0" , "0xFF000000" , 1 , 1 ) , Pen := Gdip_CreatePenFromBrush( Brush , 1 ) , Gdip_DeleteBrush( Brush ) , Gdip_DrawRoundedRectangle( G , Pen , 1 , 1 , 298 , 28 , 5 ) , Gdip_DeletePen( Pen )
  Brush := Gdip_BrushCreateSolid( "0xFFF0F0F0" ) , Gdip_FillRoundedRectangle( G , Brush , 3 , 3 , 24 , 24 , 5 ) , Gdip_DeleteBrush( Brush )
  Pen := Gdip_CreatePen( "0xaa000000" , 1 ) , Gdip_DrawRoundedRectangle( G , Pen , 3 , 3 , 24 , 24 , 5 ) , Gdip_DeletePen( Pen )
  Brush := Gdip_BrushCreateSolid( "0xFF00ff00" ) , Gdip_FillEllipse( G , Brush , 4 , 4 , 22 , 22 ) , Gdip_DeleteBrush( Brush )
  Brush := Gdip_CreateLineBrushFromRect( 6 , 7 , 17 , 17 , "0x4400ff00" , "0x99000000" , 1 , 1 ) , Gdip_FillEllipse( G , Brush , 4 , 4 , 22 , 22 ) , Gdip_DeleteBrush( Brush )
  Brush := Gdip_CreateLineBrushFromRect( 4 , 4 , 22 , 21 , "0xFF008080" , "0xFF000000" , 1 , 1 ) , Pen := Gdip_CreatePenFromBrush( Brush , 1 ) , Gdip_DeleteBrush( Brush ) , Gdip_DrawEllipse( G , Pen , 4 , 4 , 22 , 22 ) , Gdip_DeletePen( Pen )
  Brush := Gdip_CreateLineBrush( 45 , 3 , 190 , 1 , "0xFFF0F0F0" , "0xFF000000" , 1 ) , Pen := Gdip_CreatePenFromBrush( Brush , 1 ) , Gdip_DeleteBrush( Brush ) , Gdip_DrawLines( G , Pen , "35,4|290,4|290,26|35,26|" ) , Gdip_DeletePen( Pen )
  Brush := Gdip_BrushCreateSolid( "0xFFaaaaaa" ) , Gdip_TextToGraphics( G , ButtonNames[Index] , "s18  vCenter Bold c" Brush " x33 y3" , "Arial" , 260 , 28 ) , Gdip_DeleteBrush( Brush )
  Brush := Gdip_BrushCreateSolid( "0xFF0C020F" ) , Gdip_TextToGraphics( G , ButtonNames[Index] , "s18  vCenter Bold c" Brush " x32 y2" , "Arial" , 260 , 28 ) , Gdip_DeleteBrush( Brush )
  Gdip_DeleteGraphics( G )
  hBitmap := Gdip_CreateHBITMAPFromBitmap(pBitmap)
  Gdip_DisposeImage( pBitmap )
  SetImage( Handles[Index] , hBitmap )
  DeleteObject( hBitmap )
  Index++
}
The script first displays only the text, looking like this:

screen reader problem text only.png
screen reader problem text only.png (10.39 KiB) Viewed 1139 times

When you fire up NVDA on that dialog, it works perfectly, speaking the correct button when you hover on it.

After clicking the button at the bottom of the dialog, it looks like this:

screen reader problem text and graphics.png
screen reader problem text and graphics.png (22.8 KiB) Viewed 1139 times

Now when you fire up NVDA on the dialog, it has the speaks-previous-control problem. Anyone have thoughts on where the problem might be? AHK bug? NVDA bug? Windows API bug? Any ideas for a work-around?

I don't have JAWS, but if anyone has it, or any other screen reader, I'd really appreciate it if you run it on the test script above and let me know if it also exhibits the speaks-previous-control behavior.

Thanks very much, Joe
User avatar
Hellbent
Posts: 2102
Joined: 23 Sep 2017, 13:34

Re: Screen Reader Issue - Graphics and Text

11 Jun 2021, 07:51

I did a little test with the script from above and even without drawing the graphics the problem remains.

Code: Select all

;*******************************
;#Include your Gdip Library here
;*******************************

#SingleInstance, Force
SetBatchLines, -1

GDIP_Startup()

global ButtonNames := ["Screen","Reader","Problem","Speaks","Previous","Button"]
, Colors := ["FF0000","00FF00","FFFFFF","FF00FF","FFFF00","00FFFF"]
, Handles := []

Gui, 1:+AlwaysOnTop -DPIScale
Gui, 1:Color, 22262A
Gui, 1:Font, cWhite

Loop, % ButtonNames.Length() {
  if(A_Index=1)
    Options := "c" Colors[A_Index] " xm ym w300 h30  +Border hwndButtonHandle gTestTrigger"
  else if( A_Index = 4 || A_Index = 7 )
    Options := "c" Colors[A_Index] " xm y+10 w300 h30  +Border hwndButtonHandle gTestTrigger"
  else
    Options := "c" Colors[A_Index] " x+10 yp w300 h30  +Border hwndButtonHandle gTestTrigger"

  Gui, Add, Text, % Options , % ButtonNames[A_Index]
  Handles[A_Index] := ButtonHandle
}

Gui, 1:Add, Button, xm y+20 -Theme gDrawGraphics, Draw Checkbox Graphics on Text Controls
Gui, 1:Margin, , 20
Gui, 1:Show,,This is the title bar

return
GuiClose:
GuiContextMenu:
*ESC::ExitApp

DrawGraphics:
  Loop, % Handles.Length() 
    GuiControl, 1: +0xE , % Handles[A_Index]
  return

TestTrigger:
  SoundBeep, 600
  return
The problem seems to be with adding the option 0xE so I did some looking around but I didn't really find much of anything.
This could be in part to the fact that not everyone types it out the same way, sometimes it has a few leading "0"s
0x0E 0x00E 0x00000000E etc which made searching for it perhaps more difficult than it should be.

I was only able to find these two snippets which provide little or incorrect info.
Note we have set the 0xE style for it to accept an hBitmap
Already knew and understood that.
0x000E/_WM_GETTEXTLENGTH/ "Text window to obtain a length (excluding end character)",
That looks like something completely unrelated to making a control accept a hBitmap.

I do have a few ideas floating around in my head that might work, the main one would be to use BitBlt but that would bring up other issues, and that is assuming that it would even work.
User avatar
Hellbent
Posts: 2102
Joined: 23 Sep 2017, 13:34

Re: Screen Reader Issue - Graphics and Text

11 Jun 2021, 09:29

This workaround seems to fix the issue with the screen reader NVDA, but as I mentioned before it introduces new issues.

Temp.gif
Temp.gif (200.04 KiB) Viewed 1092 times

Code: Select all

;*******************************
;#Include your Gdip Library here
;*******************************
#SingleInstance, Force
SetBatchLines, -1

GDIP_Startup()

global ButtonNames := ["Screen","Reader","Problem","Speaks","Previous","Button"]
, Colors := ["FF0000","00FF00","FFFFFF","FF00FF","FFFF00","00FFFF"]
, Handles := []

Gui, 1:+AlwaysOnTop -DPIScale hwndhwnd
Gui, 1:Color, 22262A
Gui, 1:Margin, 10, 10
y:= 10
Loop, % ButtonNames.Length() {
	Options := "c" Colors[A_Index] " x10 y" y " w300 h30  +Border hwndButtonHandle gTestTrigger"
	Gui, 1: Add, Text, % Options , % ButtonNames[A_Index]
	Handles[A_Index] := ButtonHandle
	y+=40
}

Gui, 1:Add, Button, xm y+20 -Theme gDrawGraphics, Draw Checkbox Graphics on Text Controls
Gui, 1:Margin, , 20
Gui, 1:Show,,This is the title bar

hdc := GetDc(hwnd)
hdcG := Gdip_GraphicsFromHdc(hdc)

return
GuiClose:
GuiContextMenu:
*ESC::ExitApp

DrawGraphics:
	y:= 10
  Loop, % Handles.Length()	{
    Gdip_DrawImage( hdcG , pBitmap := HB_BITMAP_MAKER() , 10, y, 300, 30 )
	Gdip_DisposeImage(pBitmap)
	y+=40
  }
  return

TestTrigger:
  SoundBeep, 600
  return

HB_BITMAP_MAKER(){
	;Bitmap Created Using: HB Bitmap Maker
	Static Index := 1
	pBitmap := Gdip_CreateBitmap( 300 , 30 ) , G := Gdip_GraphicsFromImage( pBitmap ) , Gdip_SetSmoothingMode( G , 2 )
	Brush := Gdip_BrushCreateSolid( "0xFF22262A" ) , Gdip_FillRectangle( G , Brush , -1 , -1 , 302 , 52 ) , Gdip_DeleteBrush( Brush )
	Brush := Gdip_BrushCreateSolid( "0xFF" Colors[Index] ) , Gdip_FillRoundedRectangle( G , Brush , 1 , 1 , 298 , 28 , 5 ) , Gdip_DeleteBrush( Brush )
	Brush := Gdip_CreateLineBrushFromRect( 1 , 1 , 299 , 28 , "0xFFF0F0F0" , "0xFF000000" , 1 , 1 ) , Pen := Gdip_CreatePenFromBrush( Brush , 1 ) , Gdip_DeleteBrush( Brush ) , Gdip_DrawRoundedRectangle( G , Pen , 1 , 1 , 298 , 28 , 5 ) , Gdip_DeletePen( Pen )
	Brush := Gdip_BrushCreateSolid( "0xFFF0F0F0" ) , Gdip_FillRoundedRectangle( G , Brush , 3 , 3 , 24 , 24 , 5 ) , Gdip_DeleteBrush( Brush )
	Pen := Gdip_CreatePen( "0xaa000000" , 1 ) , Gdip_DrawRoundedRectangle( G , Pen , 3 , 3 , 24 , 24 , 5 ) , Gdip_DeletePen( Pen )
	Brush := Gdip_BrushCreateSolid( "0xFF00ff00" ) , Gdip_FillEllipse( G , Brush , 4 , 4 , 22 , 22 ) , Gdip_DeleteBrush( Brush )
	Brush := Gdip_CreateLineBrushFromRect( 6 , 7 , 17 , 17 , "0x4400ff00" , "0x99000000" , 1 , 1 ) , Gdip_FillEllipse( G , Brush , 4 , 4 , 22 , 22 ) , Gdip_DeleteBrush( Brush )
	Brush := Gdip_CreateLineBrushFromRect( 4 , 4 , 22 , 21 , "0xFF008080" , "0xFF000000" , 1 , 1 ) , Pen := Gdip_CreatePenFromBrush( Brush , 1 ) , Gdip_DeleteBrush( Brush ) , Gdip_DrawEllipse( G , Pen , 4 , 4 , 22 , 22 ) , Gdip_DeletePen( Pen )
	Brush := Gdip_CreateLineBrush( 45 , 3 , 190 , 1 , "0xFFF0F0F0" , "0xFF000000" , 1 ) , Pen := Gdip_CreatePenFromBrush( Brush , 1 ) , Gdip_DeleteBrush( Brush ) , Gdip_DrawLines( G , Pen , "35,4|290,4|290,26|35,26|" ) , Gdip_DeletePen( Pen )
	Brush := Gdip_BrushCreateSolid( "0xFFaaaaaa" ) , Gdip_TextToGraphics( G , ButtonNames[Index] , "s18  vCenter Bold c" Brush " x33 y3" , "Arial" , 260 , 28 ) , Gdip_DeleteBrush( Brush )
	Brush := Gdip_BrushCreateSolid( "0xFF0C020F" ) , Gdip_TextToGraphics( G , ButtonNames[Index] , "s18  vCenter Bold c" Brush " x32 y2" , "Arial" , 260 , 28 ) , Gdip_DeleteBrush( Brush )
	Gdip_DeleteGraphics( G )
	Index++
	return pBitmap
}

Does anyone have a good way of dealing with the graphics getting cleared every time the gui moves out of the screen or is minimized?

I'm still hopeful that there is a solution to the original method of drawing a hBimap to a text control but I don't even know how to search for 0xE so it seems unlikely (and that doesn't even address why it causes the screen reader to mess up...).

Anyway if you have a good method of correcting the off screen / minimize problem I would love to hear it.
User avatar
JoeWinograd
Posts: 2177
Joined: 10 Feb 2014, 20:00
Location: U.S. Central Time Zone

Re: Screen Reader Issue - Graphics and Text

12 Jun 2021, 20:32

Hellbent wrote:the graphics getting cleared every time the gui moves out of the screen or is minimized
Hi HB,
Fascinating stuff! Experimentation here confirms your results that it happens only when the window is minimized then un-minimized, or moved beyond the physical monitor(s)...top, bottom, left, or right. To be clear, you can move it across multiple physical monitors without a problem, i.e., the problem occurs only when the window is moved to where a monitor does not exist.

So, I'm thinking that a work-around would be to detect when the window is (1) minimized then un-minimized, or (2) moved and its new X/Y position goes beyond the monitor(s) in any direction (top, bottom, left, or right), then is moved back to a location fully on a monitor. In either case, seems to me that a solution is to redraw the graphics (maybe just a Gosub,DrawGraphics). Possible?

Btw, although it would be nice to have a solution for both cases, I'd be fine with making this a -MinimizeBox window, so I'd be happy with a work-around for only case (2). Regards, Joe
User avatar
Hellbent
Posts: 2102
Joined: 23 Sep 2017, 13:34

Re: Screen Reader Issue - Graphics and Text

14 Jun 2021, 10:07

JoeWinograd wrote:
12 Jun 2021, 20:32
So, I'm thinking that a work-around would be to detect when the window is (1) minimized then un-minimized, or (2) moved and its new X/Y position goes beyond the monitor(s) in any direction (top, bottom, left, or right), then is moved back to a location fully on a monitor. In either case, seems to me that a solution is to redraw the graphics (maybe just a Gosub,DrawGraphics). Possible?

Btw, although it would be nice to have a solution for both cases, I'd be fine with making this a -MinimizeBox window, so I'd be happy with a work-around for only case (2). Regards, Joe
Hi Joe. I have dealt with this problem before so it won't be an issue to have the graphics redrawn when the window moves out of the screen or when the window is minimized.
The only issue with it is that while you are moving the window it wouldn't be practical to constantly redraw the graphics over and over again, so that means that they would only get redrawn once the window is released.

Temp.gif
Temp.gif (213.18 KiB) Viewed 1047 times
I still have a few other ideas on how to deal with this and the original problem but if you can live with what is seen in the gif we at least have a Plan B ready.
User avatar
JoeWinograd
Posts: 2177
Joined: 10 Feb 2014, 20:00
Location: U.S. Central Time Zone

Re: Screen Reader Issue - Graphics and Text

14 Jun 2021, 10:43

Hellbent wrote:I still have a few other ideas on how to deal with this and the original problem
Great! Looking forward to trying whatever you develop.
Hellbent wrote:if you can live with what is seen in the gif we at least have a Plan B ready
I can definitely live with Plan B!

Thanks much, Joe
User avatar
Hellbent
Posts: 2102
Joined: 23 Sep 2017, 13:34

Re: Screen Reader Issue - Graphics and Text

15 Jun 2021, 21:33

JoeWinograd wrote:
14 Jun 2021, 10:43
Hellbent wrote:I still have a few other ideas on how to deal with this and the original problem
Great! Looking forward to trying whatever you develop.
Not using 0xE seems to do the trick.

Please confirm on your end.

Code: Select all

;***************************************************************************************************
#Include <My Altered Gdip Lib>   ;Replace with your path
;***************************************************************************************************

#SingleInstance, Force
SetBatchLines, -1

Gdip_Startup()

Gui, 1:+AlwaysOnTop -DPIScale
Gui, 1:Color, 22262a

pBitmap := HB_BITMAP_MAKER("Item 1")
hBitmap := Gdip_CreateHBITMAPFromBitmap(pBitmap)
Gdip_DisposeImage( pBitmap )
Gui, 1:Add, Text, xm ym w90 h24 BackgroundTrans , Item 1
Gui, 1:Add, Picture, xp yp wp hp hwndhwnd1 gTestLabel, % "HBITMAP:" hBitmap
DeleteObject( hBitmap )

Loop, 10	{
	pBitmap := HB_BITMAP_MAKER("Item " A_Index+1)
	hBitmap := Gdip_CreateHBITMAPFromBitmap(pBitmap)
	Gdip_DisposeImage( pBitmap )
	Gui, 1:Add, Text, xm y+10 w90 h24 BackgroundTrans , % "Item " A_Index+1
	Gui, 1:Add, Picture, % "xp yp wp hp gTestLabel hwndhwnd" A_Index+1 , % "HBITMAP:" hBitmap
	DeleteObject( hBitmap )
}
Gui , 1:Show,,

return
GuiClose:
GuiContextMenu:
*ESC::ExitApp

TestLabel:
	tog := !tog
	Loop, 11	{
		(tog)?(pBitmap := Bit2( "Item " A_Index )):(pBitmap := HB_BITMAP_MAKER( "Item " A_Index  ))
		hBitmap := Gdip_CreateHBITMAPFromBitmap(pBitmap)
		GuiControl,,% hwnd%A_Index%, % "HBITMAP:" hBitmap
		DeleteObject( hBitmap )
		Gdip_DisposeImage( pBitmap )
	}
	return

HB_BITMAP_MAKER(Text){
	;Bitmap Created Using: HB Bitmap Maker
	pBitmap := Gdip_CreateBitmap( 90 , 24 ) , G := Gdip_GraphicsFromImage( pBitmap ) , Gdip_SetSmoothingMode( G , 2 )
	Brush := Gdip_BrushCreateSolid( "0xFF22262a" ) , Gdip_FillRectangle( G , Brush , -1 , -1 , 143 , 28 ) , Gdip_DeleteBrush( Brush )
	Brush := Gdip_BrushCreateSolid( "0xFF000808" ) , Gdip_FillRoundedRectangle( G , Brush , 3 , 4 , 32 , 16 , 5 ) , Gdip_DeleteBrush( Brush )
	Brush := Gdip_BrushCreateSolid( "0xFF002222" ) , Gdip_FillRoundedRectangle( G , Brush , 4 , 5 , 30 , 14 , 5 ) , Gdip_DeleteBrush( Brush )
	Brush := Gdip_BrushCreateSolid( "0xFF004444" ) , Gdip_FillRoundedRectangle( G , Brush , 4 , 8 , 30 , 11 , 5 ) , Gdip_DeleteBrush( Brush )
	Brush := Gdip_BrushCreateSolid( "0xFF000808" ) , Gdip_FillEllipse( G , Brush , 1 , 0 , 22 , 22 ) , Gdip_DeleteBrush( Brush )
	Brush := Gdip_CreateLineBrushFromRect( 5 , 0 , 24 , 20 , "0xff004444" , "0xff02060a" , 1 , 1 ) , Gdip_FillEllipse( G , Brush , 2 , 1 , 20 , 20 ) , Gdip_DeleteBrush( Brush )
	Brush := Gdip_CreateLineBrushFromRect( 1 , 1 , 135 , 21 , "0xFF009999" , "0xFF004444" , 1 , 1 ) , Gdip_TextToGraphics( G , text , "s12 Center vCenter Bold NoWrap c" Brush " x17 y1" , "Segoe ui" , 90 , 24 ) , Gdip_DeleteBrush( Brush )
	Gdip_DeleteGraphics( G )
	return pBitmap
}
Bit2(text){
	;Bitmap Created Using: HB Bitmap Maker
	pBitmap := Gdip_CreateBitmap( 90 , 24 ) , G := Gdip_GraphicsFromImage( pBitmap ) , Gdip_SetSmoothingMode( G , 2 )
	Brush := Gdip_BrushCreateSolid( "0xFF22262a" ) , Gdip_FillRectangle( G , Brush , -1 , -1 , 143 , 28 ) , Gdip_DeleteBrush( Brush )
	Brush := Gdip_BrushCreateSolid( "0xFF000808" ) , Gdip_FillRoundedRectangle( G , Brush , 3 , 4 , 32 , 16 , 5 ) , Gdip_DeleteBrush( Brush )
	Brush := Gdip_BrushCreateSolid( "0xFF121212" ) , Gdip_FillRoundedRectangle( G , Brush , 4 , 5 , 30 , 14 , 5 ) , Gdip_DeleteBrush( Brush )
	Brush := Gdip_BrushCreateSolid( "0xFF1D1D1D" ) , Gdip_FillRoundedRectangle( G , Brush , 4 , 8 , 30 , 11 , 5 ) , Gdip_DeleteBrush( Brush )
	Brush := Gdip_BrushCreateSolid( "0xFF000808" ) , Gdip_FillEllipse( G , Brush , 17 , 0 , 22 , 22 ) , Gdip_DeleteBrush( Brush )
	Brush := Gdip_CreateLineBrushFromRect( 5 , 0 , 24 , 20 , "0xff004444" , "0xff02060a" , 1 , 1 ) , Gdip_FillEllipse( G , Brush , 18 , 1 , 20 , 20 ) , Gdip_DeleteBrush( Brush )
	Brush := Gdip_CreateLineBrushFromRect( 1 , 1 , 135 , 21 , "0xFF009999" , "0xFF004444" , 1 , 1 ) , Gdip_TextToGraphics( G , text , "s12 Center vCenter Bold NoWrap c" Brush " x17 y1" , "Segoe ui" , 90 , 24 ) , Gdip_DeleteBrush( Brush )
	Gdip_DeleteGraphics( G )
	return pBitmap
}
User avatar
JoeWinograd
Posts: 2177
Joined: 10 Feb 2014, 20:00
Location: U.S. Central Time Zone

Re: Screen Reader Issue - Graphics and Text

15 Jun 2021, 22:25

Hellbent wrote:Please confirm on your end.
Very nice! Confirmed that the graphic remains after minimizing/restoring the window or after moving it off the edge of all physical monitors and then back on. But I'm unclear on how to incorporate that into the script in this post:

https://www.autohotkey.com/boards/viewtopic.php?f=76&t=91515&p=405128#p404581

That's the one (with your animated GIF) where the graphic disappears after minimizing/restoring or moving it off the edge of all physical monitors. But I don't understand "Not using 0xE" to fix it, since that script doesn't even use 0XE. Regards, Joe
User avatar
Hellbent
Posts: 2102
Joined: 23 Sep 2017, 13:34

Re: Screen Reader Issue - Graphics and Text

15 Jun 2021, 22:31

JoeWinograd wrote:
15 Jun 2021, 22:25
Hellbent wrote:Please confirm on your end.
Very nice! Confirmed that the graphic remains after minimizing/restoring the window or after moving it off the edge of all physical monitors and then back on. But I'm unclear on how to incorporate that into the script in this post:
My bad. We have a bit of a misunderstanding. This is a fix to the original screen reader problem. There is no need to redraw when moved / minimized with this.

When I test this with the screen reader it says what it should say and not read the last line.
User avatar
Hellbent
Posts: 2102
Joined: 23 Sep 2017, 13:34

Re: Screen Reader Issue - Graphics and Text

15 Jun 2021, 22:34

This will be easy to add to the class. It will be just as it was with the text and pic controls but will use GuiControl,,, rather than SetImage()
User avatar
JoeWinograd
Posts: 2177
Joined: 10 Feb 2014, 20:00
Location: U.S. Central Time Zone

Re: Screen Reader Issue - Graphics and Text

15 Jun 2021, 22:51

Hellbent wrote:This is a fix to the original screen reader problem. There is no need to redraw when moved / minimized with this.
OK, so the script in my original post has 0xE in one line (in DrawGraphics):

Code: Select all

GuiControl, 1: +0xE , % Handles[A_Index]
Are you saying that the fix is to change that line not to use 0xE? What I'm not understanding is how the script in your most recent post relates to the script in my initial post.
Hellbent wrote:When I test this with the screen reader it says what it should say and not read the last line.
Great to hear!
Hellbent wrote:will use GuiControl,,, rather than SetImage()
OK, please let me know exactly what needs to be changed in the class.

Thanks much, Joe
User avatar
Hellbent
Posts: 2102
Joined: 23 Sep 2017, 13:34

Re: Screen Reader Issue - Graphics and Text

15 Jun 2021, 23:11

JoeWinograd wrote:
15 Jun 2021, 22:51
What I'm not understanding is how the script in your most recent post relates to the script in my initial post.
Do you remember that altered class I sent you that had the tiny text on top of the checkbox? That is what we will do again (with the text on the bottom) and rather than using SetImage() in the _DrawControl( obj ) method, we will just use that GuiControl,,% pichwnd, % "HBITMAP:" hBitmap line to replace it ( using the correct hwnd and window name ).

Hellbent wrote:
When I test this with the screen reader it says what it should say and not read the last line.
Great to hear!
Please confirm on your end.
Hellbent wrote:
will use GuiControl,,, rather than SetImage()
OK, please let me know exactly what needs to be changed in the class.

Code: Select all

_AddControl(obj){
		local hwnd
		Gui, % obj.Window ":Add", Picture, % "x" obj.x " y" obj.y " w" obj.w " h" obj.h " hwndhwnd 0xE gAutoSizeCheckbox._CreateBitmap"
		AutoSizeCheckbox.Handles[hwnd] := AutoSizeCheckbox.Index
		obj.Hwnd := hwnd
	}
just needs to be replaced with something like this (needs editing)

Code: Select all

Gui, 1:Add, Text, xm y+10 w90 h24 BackgroundTrans , % "Item " A_Index+1
Gui, 1:Add, Picture, % "xp yp wp hp gTestLabel hwndhwnd" A_Index+1 , 
And then in _DrawControl( obj )

we just need to change setImage() with GuiControl,,,% "HBITMAP:" hBitmap

I can get that done in the morning.
It needs to be done for the Radio Class and Checkbox Class. Easy stuff though.
User avatar
JoeWinograd
Posts: 2177
Joined: 10 Feb 2014, 20:00
Location: U.S. Central Time Zone

Re: Screen Reader Issue - Graphics and Text

15 Jun 2021, 23:21

Hellbent wrote:Do you remember that altered class I sent you that had the tiny text on top of the checkbox?
Ah, that one!
Hellbent wrote:rather than using SetImage() ... we will just use that GuiControl ...
Got it!
Hellbent wrote:I can get that done in the morning.
Much appreciated!
User avatar
Hellbent
Posts: 2102
Joined: 23 Sep 2017, 13:34

Re: Screen Reader Issue - Graphics and Text

16 Jun 2021, 13:49

Hi Joe.

I have the radio class with the fix for the screen reader issue added in.
I have decided to let you adjust the checkbox class since you have already done editing to it.

You can find the changes that you need to make in _AddControl( obj ){ and _DrawControl( obj ){

As for the Radio class.
I added in the extra param to __New() like we had talked about. You can use either param to pass any and all the info that you want to pass.
I made a little demo script that should give you a better sense of what you can do. What I didn't show is that you can pass the same key/value in both objects. So for example you could pass {MainColor: } in both and it will use the second one you passed (the use of that may not be entirely clear at first but it is very useful).

Here is the radio class (still early draft):

Code: Select all

;						<><><><><><><><><><><><><><><><>	Custom Auto Size Radio Class	<><><><><><><><><><><><><><><><>
;**************************************************************************************************************************************************************************
;**************************************************************************************************************************************************************************
;**************************************************************************************************************************************************************************
;By: Hellbent
;Version: 0.2
;Date: June 16th, 2021
;Purpose: Create Custom Gui Radio Controls
;Creates custom radio controls that auto size based on the font type and font size that you use with them and the text that you feed them.
;Adapted to work with screen readers.
class AutoSizeRadio	{
	static Index := 0 , Handles := [] , Controls := [] , Groups := []
	__New( Input1 := "" , Input2 := "" ){
		AutoSizeRadio.Controls[ ++AutoSizeRadio.Index ] := {}
		AutoSizeRadio._SetDefaults( AutoSizeRadio.Controls[AutoSizeRadio.Index] )
		AutoSizeRadio._UpdateDefaults( Input1 , Input2 , AutoSizeRadio.Controls[AutoSizeRadio.Index] )
		AutoSizeRadio._GetTextSize( AutoSizeRadio.Controls[AutoSizeRadio.Index] )
		AutoSizeRadio._AddControl( AutoSizeRadio.Controls[AutoSizeRadio.Index] )
		AutoSizeRadio._DrawControl( AutoSizeRadio.Controls[AutoSizeRadio.Index] )
		return AutoSizeRadio.Controls[AutoSizeRadio.Index]
	}
	_SetDefaults(obj){
		obj.X := 10
		obj.Y := 10
		obj.W := 10
		obj.H := 10
		obj.MinW := 50
		obj.MinH := 20
		obj.Window := 1
		obj.State := 0
		obj.Value := 1
		obj.BackgroundColor := "0xFFFFFF00"
		obj.MainColor := "0xFFDCAE26"
		obj.Text := "Radio"
		obj.Font := "Arial"
		obj.FontSize := "16"
		obj.FontOptions := " vCenter Bold "
		obj.CheckColor := "0xFF00FF00"
		obj.ControlNumber := AutoSizeRadio.Index
		obj.Group := 1
		( (temp := AutoSizeRadio._GetGray( SubStr( obj.MainColor , 3 ) )) < 128 ) ? ( obj.FontColorTop := "0xFFFFFFFF" , obj.FontColorBottom := "0xFF000000" ) 
		: ( obj.FontColorTop := "0xFF000000" , obj.FontColorBottom := "0xFFFFFFFF" )
	}
	_UpdateDefaults( input1 := "" , Input2 := "" , obj := "" ){
		for k , v in input1
			obj[k] := v
		for k , v in input2
			obj[k] := v
		( temp := AutoSizeRadio._GetGray( SubStr( obj.MainColor , 3 ) ) < 128 ) ? ( obj.FontColorTop := "0xFFFFFFFF" , obj.FontColorBottom := "0xFF000000" ) 
		: ( obj.FontColorTop := "0xFF000000" , obj.FontColorBottom := "0xFFFFFFFF" )
		if(!isObject(AutoSizeRadio.Groups[obj.Group]))
			AutoSizeRadio.Groups[obj.Group] := [ ]
		AutoSizeRadio.Groups[obj.Group].Push(obj.ControlNumber)
	}
	_AddControl( obj ){
		local hwnd
		Gui, % obj.Window ":Add", Text, % "x" obj.x " y" obj.y " w" obj.w " h" obj.h " BackgroundTrans ", % obj.Text
		Gui, % obj.Window ":Add", Picture, % "xp yp wp hp hwndhwnd gAutoSizeRadio._TriggerCheck"
		AutoSizeRadio.Handles[hwnd] := AutoSizeRadio.Index
		obj.Hwnd := hwnd
	}
	_GetTextSize(obj){
		local pBitmap, G, Brush, temparr
		pBitmap := Gdip_CreateBitmap( 10,10), G := Gdip_GraphicsFromImage( pBitmap ), Gdip_SetSmoothingMode( G , 2 )
		Brush := Gdip_BrushCreateSolid( "0xFF000000")
		temparr := StrSplit( Gdip_TextToGraphics( G , obj.Text, " s" obj.Fontsize " c" Brush " " obj.FontOptions " Left NoWrap x" 0 " y" 0 , obj.Font , 10000, 10000  ),"|","|"  )
		Gdip_DeleteBrush( Brush ), Gdip_DeleteGraphics( G ), Gdip_DisposeImage( pBitmap )
		obj.StringWidth := temparr[3]
		obj.StringHeight := temparr[4]
		obj.W := obj.StringWidth + obj.StringHeight + 6 + obj.StringHeight + 6
		obj.H := obj.StringHeight + 6 + 2
		(obj.W<Obj.MinW)?(obj.W := Obj.MinW)
		(obj.H<Obj.MinH)?(obj.H := Obj.MinH)
	}
	_TriggerCheck(){
		local cIndex , gIndex , cNum , sNum
		MouseGetPos,,,,ctrl,2
		cIndex := AutoSizeRadio.Handles[ctrl]
		gIndex := AutoSizeRadio.Controls[cIndex].Group
		cNum := AutoSizeRadio.Controls[cIndex].ControlNumber
		Loop, % AutoSizeRadio.Groups[gIndex].Length()	
			AutoSizeRadio.Controls[ AutoSizeRadio.Groups[gIndex,A_Index] ].State := 0
		Loop, % AutoSizeRadio.Groups[gIndex].Length()	
			if(AutoSizeRadio.Groups[gIndex,A_Index]=cNum)
				snum := A_Index
		Loop, % AutoSizeRadio.Groups[gIndex].Length()	
			AutoSizeRadio.Controls[AutoSizeRadio.Groups[gIndex,A_Index]].Value := snum
		AutoSizeRadio.Controls[cIndex].State := 1
		AutoSizeRadio._CreateBitmap(gIndex)
	}
	_CreateBitmap( group ){
		Loop, % AutoSizeRadio.Groups[group].Length()	
			AutoSizeRadio._DrawControl( AutoSizeRadio.Controls[ AutoSizeRadio.Groups[ group , A_Index ] ] )
	}
	_GetGray( OUTPUTCOLOR ){
		StringTrimLeft,OUTPUTCOLOR,OUTPUTCOLOR,2
		StringLeft , r ,  OUTPUTCOLOR , 2
		StringTrimLeft,OUTPUTCOLOR,OUTPUTCOLOR,2
		StringLeft , g ,  OUTPUTCOLOR , 2
		StringTrimLeft,OUTPUTCOLOR,OUTPUTCOLOR,2
		StringLeft , b ,  OUTPUTCOLOR , 2
		r := "0x" r , g := "0x" g , b := "0x" b
		REDSLIDERVALUE := r+0 , GreenSLIDERVALUE := g+0 , BlueSLIDERVALUE := b+0
		GreyScaleSLIDERVALUE := Round((REDSLIDERVALUE+GreenSLIDERVALUE+BlueSLIDERVALUE)/3)
		return GreyScaleSLIDERVALUE
	}
	_DrawControl( obj ){
		local width := obj.StringWidth + obj.StringHeight + 6 + obj.StringHeight + 6 , Height := obj.StringHeight + 6 , a , b , c 
		(Width<Obj.MinW)?(Width := obj.MinW)
		(Height<obj.MinH)?(Height := obj.MinH)
		pBitmap := Gdip_CreateBitmap( width , Height + 2 ) , G := Gdip_GraphicsFromImage( pBitmap ) , Gdip_SetSmoothingMode( G , 2 )
		Brush := Gdip_BrushCreateSolid( obj.BackgroundColor ) , Gdip_FillRectangle( G , Brush , -1 , -1 , width + 2 , Height + 4 ) , Gdip_DeleteBrush( Brush )
		Brush := Gdip_BrushCreateSolid( obj.MainColor ) , Gdip_FillRoundedRectangle( G , Brush , 1 , 1 , width-2 , Height , 5 ) , Gdip_DeleteBrush( Brush )
		;***********************************************
		;Remove / comment this to remove gradient
		Brush := Gdip_CreateLineBrushFromRect( 1 , 3 , Width - 6 , Height , "0x33F0F0F0" , "0x99000000" , 1 , 1 ) , Gdip_FillRoundedRectangle( G , Brush , 1 , 1 , Width - 2 , Height , 5 ) , Gdip_DeleteBrush( Brush )
		;***********************************************
		Brush := Gdip_CreateLineBrushFromRect( 1 , 1 , Width - 1 , Height , "0xFFF0F0F0" , "0xFF000000" , 1 , 1 ) , Pen := Gdip_CreatePenFromBrush( Brush , 1 ) , Gdip_DeleteBrush( Brush ) , Gdip_DrawRoundedRectangle( G , Pen , 1 , 1 , Width - 2 , Height , 5 ) , Gdip_DeletePen( Pen )
		Brush := Gdip_BrushCreateSolid( "0xFFF0F0F0" ) , Gdip_FillRoundedRectangle( G , Brush , 3 , 3 , Height-4 , Height-4 , 5 ) , Gdip_DeleteBrush( Brush )
		Pen := Gdip_CreatePen( "0xaa000000" , 1 ) , Gdip_DrawRoundedRectangle( G , Pen , 3 , 3 , Height-4 , Height-4 , 5 ) , Gdip_DeletePen( Pen )
		if(obj.State){
			Brush := Gdip_BrushCreateSolid( (obj.State)?(obj.Checkcolor):("0xFFc2c6ca") ) , Gdip_FillEllipse( G , Brush , 4 , 4 , Height-6 , Height-6 ) , Gdip_DeleteBrush( Brush )
			Brush := Gdip_CreateLineBrushFromRect( 6 , 7 , Height-6 , Height-6 , (obj.State)?( "0x44" SubStr(obj.Checkcolor,4)):("0x44a2a6aa") , (obj.State)?("0x99000000"):("0x33000000") , 1 , 1 ) , Gdip_FillEllipse( G , Brush , 4 , 4 , Height-6 , Height-6 ) , Gdip_DeleteBrush( Brush )
			Brush := Gdip_CreateLineBrushFromRect( 4 , 4 , Height-6 , Height-6 , "0xFF008080" , "0xFF000000" , 1 , 1 ) , Pen := Gdip_CreatePenFromBrush( Brush , 1 ) , Gdip_DeleteBrush( Brush ) , Gdip_DrawEllipse( G , Pen , 4 , 4 , Height-6 , Height-6 ) , Gdip_DeletePen( Pen )
		}
		a := Height + 7,b := Height - 3,c := Width - 4
		list = %a% , 4 | %c% , 4 | %c% , %b% | %a% , %b% | 
		Brush := Gdip_CreateLineBrush( 30 , 3 , width /2  , 1 , (!obj.State)?("0xFFF0F0F0"):(obj.Checkcolor) , (!obj.State)?("0xFF000000"):("0xFFffffff") , 1 ) , Pen := Gdip_CreatePenFromBrush( Brush , 1 ) , Gdip_DeleteBrush( Brush ) , Gdip_DrawLines( G , Pen , List ) , Gdip_DeletePen( Pen )
		Brush := Gdip_BrushCreateSolid( obj.FontColorBottom ) , Gdip_TextToGraphics( G , obj.Text , "s" obj.FontSize " " obj.FontOptions " c" Brush " x" Height+5 " y3" , obj.Font , obj.StringWidth , Height ) , Gdip_DeleteBrush( Brush )
		Brush := Gdip_BrushCreateSolid( obj.FontColorTop ) , Gdip_TextToGraphics( G , obj.Text , "s" obj.FontSize " " obj.FontOptions " c" Brush " x" Height+4 " y2" , obj.Font , obj.StringWidth , Height ) , Gdip_DeleteBrush( Brush )
		Gdip_DeleteGraphics( G )
		hBitmap := Gdip_CreateHBITMAPFromBitmap(pBitmap)
		Gdip_DisposeImage( pBitmap )
		GuiControl, % obj.Window ":" , % obj.Hwnd, % "HBitmap:" hBitmap
		DeleteObject( hBitmap )
	}
	DeleteControl( input ){
		;Might improve this later
		;~ GuiControl, % input.Window ":Move" , % input.Hwnd, x-100 y-100 w0 h0
		AutoSizeRadio.Controls[ input.ControlNumber ] := ""
	}
}
;**************************************************************************************************************************************************************************
.
And here is a demo of it's use. (This uses the method we spoke of before of having the radio settings placed elsewhere in the script)

Code: Select all

;***************************************************************************************************
;#Include ;GDIP
;#Include ;AutoSizeRadio Class
;***************************************************************************************************
#SingleInstance, Force
SetBatchLines, -1
Gdip_Startup()

Gui, 1:+AlwaysOnTop -DPIScale
Gui, 1:Color, 22262a
Gui, 1:Margin, 10, 10

Group1 := SetupRadioGroup1()
Group1[1] := New AutoSizeRadio( { X: "m" , Y: "m" 	, Window: "1" , BackgroundColor: "0xFF22262A" } , Group1[1] )
Loop, 3	
	Group1[A_Index+1] := New AutoSizeRadio( { X: "m" , Y: "+5" 	, Window: "1" , BackgroundColor: "0xFF22262A" } , Group1[A_Index+1] )

OptionList := SetupRadioGroup2()
Loop, % OptionList.Length()
	OptionList[ A_Index ] := New AutoSizeRadio( OptionList[ A_Index ] )

OtherRadios := SetupRadioGroup3()
Loop, % OtherRadios.Length()
	OtherRadios[ A_Index ] := New AutoSizeRadio( { CheckColor: "0xFF00ff00" } , OtherRadios[ A_Index ] )

Gui, 1:Show,, AutoSizeRadio

return
GuiClose:
*ESC::ExitApp

Numpad1::
	Gui, 1:+OwnDialogs
	
	; Using the "STATE" key
	; getting the state of the first radio in the first group
	msgbox, % "State of radio 1:    ( "   Group1[1].State " )"
	
	;Using the "VALUE" key
	;Getting the selected radio from the first group. ( You can use any radio in the group to get this value )
	msgbox, % "The selected radio in grounp 1 is:   ( " Group1[2].Value " )"
	
	return

Numpad2::
	;How to delete a radio when you are destorying a gui.
	;Will improve to do other things later.
	AutoSizeRadio.DeleteControl( Group1[1] )
	Group1[1] := ""
	return

	
;**************************************************************************************************************************************************************************
SetupRadioGroup1(){
	local obj := []
	
	obj[1] := { Group: 		1
			,	State: 		0
			,	Value: 		0
			,	Text:		"Option 1"
			,	FontSize:	12
			,	Font:		"Segoe UI"
			,	MinW:		150
			,	MinH:		30
			,	MainColor:	"0xFFFFBF00"
			,	CheckColor:	"0xFFff0000"	}
			
	obj[2] :=	{ Group: 1, State: 0, Value: 0, Text: "Option 2", FontSize: 12 , Font: "Segoe UI", MinW: 150, MinH: 30, MainColor: "0xFF0000FF", CheckColor: "0xFFff0000" }	
	obj[3] :=	{ Group: 1, State: 0, Value: 0, Text: "Option 3", FontSize: 12 , Font: "Segoe UI", MinW: 150, MinH: 30, MainColor: "0xFF32E64F", CheckColor: "0xFFff0000" }	
	obj[4] :=	{ Group: 1, State: 0, Value: 0, Text: "Option 4", FontSize: 12 , Font: "Segoe UI", MinW: 150, MinH: 30, MainColor: "0xFF9A04DF", CheckColor: "0xFFff0000" }	
	return obj
}
;**************************************************************************************************************************************************************************
SetupRadioGroup2(){
	local obj := [] , fs := 24 , Font := "Comic Sans MS" , GroupName := 2 , MinimumHeight := 60
	obj[1] :=	{ Window: 1, X: "+10", Y: "m", BackgroundColor: "0xFF22262A", Group: GroupName, State: 1, Value: 1, Text: "Item 1", FontSize: fs , Font: Font , MinW: 150, MinH: MinimumHeight, MainColor: "0xFFFDD42D", CheckColor: "0xFF11FFFF" }	
	obj[2] :=	{ Window: 1, X: "p", Y: "+5", BackgroundColor: "0xFF22262A", Group: GroupName, State: 0, Value: 1, Text: "Item 2", FontSize: fs , Font: Font , MinW: 150, MinH: MinimumHeight, MainColor: "0xFFC80530", CheckColor: "0xFF11FFFF" }	
	obj[3] :=	{ Window: 1, X: "p", Y: "+5", BackgroundColor: "0xFF22262A", Group: GroupName, State: 0, Value: 1, Text: "Item 3", FontSize: fs , Font: Font , MinW: 150, MinH: MinimumHeight, MainColor: "0xFF8C24AD", CheckColor: "0xFF11FFFF" }	
	obj[4] :=	{ Window: 1, X: "p", Y: "+5", BackgroundColor: "0xFF22262A", Group: GroupName, State: 0, Value: 1, Text: "Item 4", FontSize: fs , Font: Font , MinW: 150, MinH: MinimumHeight, MainColor: "0xFFF5CE19", CheckColor: "0xFF11FFFF" }	
	obj[5] :=	{ Window: 1, X: "p", Y: "+5", BackgroundColor: "0xFF22262A", Group: GroupName, State: 0, Value: 1, Text: "Item 5", FontSize: fs , Font: Font , MinW: 150, MinH: MinimumHeight, MainColor: "0xFFF4035D", CheckColor: "0xFF11FFFF" }	
	obj[6] :=	{ Window: 1, X: "p", Y: "+5", BackgroundColor: "0xFF22262A", Group: GroupName, State: 0, Value: 1, Text: "Item 6", FontSize: fs , Font: Font , MinW: 150, MinH: MinimumHeight, MainColor: "0xFF1EFD4C", CheckColor: "0xFF11FFFF" }	
	return obj
}
;**************************************************************************************************************************************************************************
SetupRadioGroup3(){
	local obj := [] , fs := 64 , Font := "Courier New" , GroupName := 3 
	obj[1] :=	{ Window: 1, X: "+10", Y: "m", BackgroundColor: "0xFF22262A", Group: GroupName, State: 0, Value: 2, Text: "Blah Blah 1", FontSize: fs , Font: Font , MinW: 600, MinH: 30, MainColor: "0xFFffffff" }	
	obj[2] :=	{ Window: 1, X: "p", Y: "+5", BackgroundColor: "0xFF22262A", Group: GroupName, State: 1, Value: 2, Text: "Some other text", FontSize: fs , Font: Font , MinW: 150, MinH: 30, MainColor: "0xFF888888" }	
	obj[3] :=	{ Window: 1, X: "p", Y: "+5", BackgroundColor: "0xFF22262A", Group: GroupName, State: 0, Value: 2, Text: "Blah Blah 3", FontSize: fs , Font: Font , MinW: 150, MinH: 30, MainColor: "0xFF000000" }	
	return obj
}

F.Y.I. You can just paste the class into the demo script. There is no need to save it to another file.


I have already tested this with the screen reader and it works flawlessly on my end.

If I forgot anything or if you have any questions let me know.


*EDIT*
Added a way to delete a radio for when you are destroying a gui. (See Numpad2 in the demo)
User avatar
JoeWinograd
Posts: 2177
Joined: 10 Feb 2014, 20:00
Location: U.S. Central Time Zone

Re: Screen Reader Issue - Graphics and Text

16 Jun 2021, 17:53

Hi HB,

Tested AutoSizeRadio class here with your demo script...EVERYTHING WORKS! Specifically, (1) the NVDA screen reader speaks all the button names correctly, (2) moving the dialog off-screen does not obliterate the graphics, (3) the Numpad1 hotkey correctly reports the state of the first radio in the first group and what the selected radio is in the first group, (4) the Numpad2 hotkey deletes the first radio in the first group.

All-in-all...PERFECT! Confirmed your "flawlessly" working performance!

I'll start to incorporate this into my program and will let you know if I run into any issues. Can't thank you enough for your ongoing support! Regards, Joe
User avatar
Hellbent
Posts: 2102
Joined: 23 Sep 2017, 13:34

Re: Screen Reader Issue - Graphics and Text

16 Jun 2021, 18:28

There was one thing that I did notice that I think may be a result of using "HBitmap:" for the picture controls, and that is that the line ellipse that goes around the check circle seems to be getting drawn with gaps in it. I think that the code that draws the Ellipse can just be replaced with having a filled Ellipse inside another filled Ellipse. I'll test later or you can try your hand at doing it.

If option 2:

Draw (Gdip_FillEllipse) the border color Ellipse the same size as it is now and then draw the Check Ellipse to be (relative) x+1 y+1 w-2 h-2
User avatar
JoeWinograd
Posts: 2177
Joined: 10 Feb 2014, 20:00
Location: U.S. Central Time Zone

Re: Screen Reader Issue - Graphics and Text

16 Jun 2021, 20:25

Hi HB,

I didn't notice the gaps and I think it looks fine as is. That said, I'll be happy to try any other code that you develop...yes, I'm going for option 1. :)

Thanks, Joe
User avatar
Hellbent
Posts: 2102
Joined: 23 Sep 2017, 13:34

Re: Screen Reader Issue - Graphics and Text

18 Jun 2021, 15:19

@JoeWinograd

I have made a quick pass at the DeleteControl class method. It should now have full functionality to remove single radios and full groups.

Here is the new method(). Just replace the old one with the new one.

Code: Select all

DeleteControl( input , HideControls := 0 ){
		if(HideControls){
			if(IsObject(input)){
				GuiControl, % input.Window ":Move" , % input.Hwnd, x-100 y-100 w0 h0
				GuiControl, % input.Window ":Move" , % input.TextHwnd, x-100 y-100 w0 h0
			}else{
				Loop, % AutoSizeRadio.Groups[input].Length(){
					GuiControl, % AutoSizeRadio.Controls[ AutoSizeRadio.Groups[input,A_Index] ].Window ":Move" , % AutoSizeRadio.Controls[ AutoSizeRadio.Groups[input,A_Index] ].Hwnd, x-100 y-100 w0 h0
					GuiControl, % AutoSizeRadio.Controls[ AutoSizeRadio.Groups[input,A_Index] ].Window ":Move" , % AutoSizeRadio.Controls[ AutoSizeRadio.Groups[input,A_Index] ].TextHwnd, x-100 y-100 w0 h0
				}
			}
		}
		if(IsObject( input ) ){
			Loop, % AutoSizeRadio.Groups[ input.Group ].Length()
				if( AutoSizeRadio.Groups[ input.Group , A_Index ] = input.ControlNumber )
					AutoSizeRadio.Groups[ input.Group , A_Index ] := ""
			AutoSizeRadio.Controls[ input.ControlNumber ] := ""	
		}else{
			Loop, % AutoSizeRadio.Groups[ input ].Length()
				AutoSizeRadio.Controls[ AutoSizeRadio.Groups[ input , A_Index ] ] := ""
			AutoSizeRadio.Groups[ input ] := ""
		}
	}
Limited testing shows it to work as intended but if you encounter a problem let me know.

Here is a updated demo script that should give you a good sense of how to use it.

Code: Select all

;***************************************************************************************************
#Include <My Altered Gdip Lib>   ;Replace with your path
;***************************************************************************************************
#SingleInstance, Force
SetBatchLines, -1
Gdip_Startup()

gosub, MakeNewGui
return
GuiClose:
*ESC::ExitApp

Numpad1::
	Gui, 1:+OwnDialogs
	
	; Using the "STATE" key
	; getting the state of the first radio in the first group
	msgbox, % "State of radio 1:    ( "   Group1[1].State " )"
	
	;Using the "VALUE" key
	;Getting the selected radio from the first group. ( You can use any radio in the group to get this value )
	msgbox, % "The selected radio in grounp 1 is:   ( " Group1[2].Value " )"
	
	return


;Delete a group ( "Group1" ) and remove from gui. (Only need to remove from gui if you are not destroying the gui)
Numpad2::
	AutoSizeRadio.DeleteControl( 1 , 1 )  ; ( group number , hide controls )
	Group1 := ""  
	return

;Deleting the 4th radio in the 2nd group and hiding the control.
Numpad3::
	AutoSizeRadio.DeleteControl( OptionList[4] , 1 )
	OptionList[4] := ""
	return

;Another way to delete  a whole group. This time without hiding the controls
Numpad4::
	AutoSizeRadio.DeleteControl( OtherRadios[1].Group )
	OtherRadios := ""
	return


;Destroying and making a new gui.
Numpad5::
MakeNewGui:
	;Deleting the radio groups before making a new gui
	if(IsObject(Group1)){
		AutoSizeRadio.DeleteControl( 1 ) ;Radio Group 1
		Group1 := ""
	}
	if(IsObject(OptionList)){
		AutoSizeRadio.DeleteControl( 2 ) ;Radio Group 2
		OptionList := ""
	}
	if(IsObject(OtherRadios)){
		AutoSizeRadio.DeleteControl( 3 ) ;Radio Group 3
		OtherRadios := ""
	}
	
	Gui, 1:New,+AlwaysOnTop -DPIScale 
	Gui, 1:Color, 22262a
	Gui, 1:Margin, 10, 10

	Group1 := SetupRadioGroup1()
	Group1[1] := New AutoSizeRadio( { X: "m" , Y: "m" 	, Window: "1" , BackgroundColor: "0xFF22262A" } , Group1[1] )
	Loop, 3	
		Group1[A_Index+1] := New AutoSizeRadio( { X: "m" , Y: "+5" 	, Window: "1" , BackgroundColor: "0xFF22262A" } , Group1[A_Index+1] )

	OptionList := SetupRadioGroup2()
	Loop, % OptionList.Length()
		OptionList[ A_Index ] := New AutoSizeRadio( OptionList[ A_Index ] )

	OtherRadios := SetupRadioGroup3()
	Loop, % OtherRadios.Length()
		OtherRadios[ A_Index ] := New AutoSizeRadio( { CheckColor: "0xFF00ff00" } , OtherRadios[ A_Index ] )

	Gui, 1:Show,, AutoSizeRadio
	return


;**************************************************************************************************************************************************************************
SetupRadioGroup1(){
	local obj := []
	
	obj[1] := { Group: 		1
			,	State: 		0
			,	Value: 		0
			,	Text:		"Option 1"
			,	FontSize:	12
			,	Font:		"Segoe UI"
			,	MinW:		150
			,	MinH:		30
			,	MainColor:	"0xFFFFBF00"
			,	CheckColor:	"0xFFff0000"	}
			
	obj[2] :=	{ Group: 1, State: 0, Value: 0, Text: "Option 2", FontSize: 12 , Font: "Segoe UI", MinW: 150, MinH: 30, MainColor: "0xFF0000FF", CheckColor: "0xFF00ff00" }	
	obj[3] :=	{ Group: 1, State: 0, Value: 0, Text: "Option 3", FontSize: 12 , Font: "Segoe UI", MinW: 150, MinH: 30, MainColor: "0xFF32E64F", CheckColor: "0xFF00ff00" }	
	obj[4] :=	{ Group: 1, State: 0, Value: 0, Text: "Option 4", FontSize: 12 , Font: "Segoe UI", MinW: 150, MinH: 30, MainColor: "0xFF9A04DF", CheckColor: "0xFF00ff00" }	
	return obj
}
;**************************************************************************************************************************************************************************
SetupRadioGroup2(){
	local obj := [] , fs := 24 , Font := "Comic Sans MS" , GroupName := 2 , MinimumHeight := 60
	obj[1] :=	{ Window: 1, X: "+10", Y: "m", BackgroundColor: "0xFF22262A", Group: GroupName, State: 1, Value: 1, Text: "Item 1", FontSize: fs , Font: Font , MinW: 150, MinH: MinimumHeight, MainColor: "0xFFFDD42D", CheckColor: "0xFF11FFFF" }	
	obj[2] :=	{ Window: 1, X: "p", Y: "+5", BackgroundColor: "0xFF22262A", Group: GroupName, State: 0, Value: 1, Text: "Item 2", FontSize: fs , Font: Font , MinW: 150, MinH: MinimumHeight, MainColor: "0xFFC80530", CheckColor: "0xFF11FFFF" }	
	obj[3] :=	{ Window: 1, X: "p", Y: "+5", BackgroundColor: "0xFF22262A", Group: GroupName, State: 0, Value: 1, Text: "Item 3", FontSize: fs , Font: Font , MinW: 150, MinH: MinimumHeight, MainColor: "0xFF8C24AD", CheckColor: "0xFF11FFFF" }	
	obj[4] :=	{ Window: 1, X: "p", Y: "+5", BackgroundColor: "0xFF22262A", Group: GroupName, State: 0, Value: 1, Text: "Item 4", FontSize: fs , Font: Font , MinW: 150, MinH: MinimumHeight, MainColor: "0xFFF5CE19", CheckColor: "0xFF11FFFF" }	
	obj[5] :=	{ Window: 1, X: "p", Y: "+5", BackgroundColor: "0xFF22262A", Group: GroupName, State: 0, Value: 1, Text: "Item 5", FontSize: fs , Font: Font , MinW: 150, MinH: MinimumHeight, MainColor: "0xFFF4035D", CheckColor: "0xFF11FFFF" }	
	obj[6] :=	{ Window: 1, X: "p", Y: "+5", BackgroundColor: "0xFF22262A", Group: GroupName, State: 0, Value: 1, Text: "Item 6", FontSize: fs , Font: Font , MinW: 150, MinH: MinimumHeight, MainColor: "0xFF1EFD4C", CheckColor: "0xFF11FFFF" }	
	return obj
}
;**************************************************************************************************************************************************************************
SetupRadioGroup3(){
	local obj := [] , fs := 64 , Font := "Courier New" , GroupName := 3 
	obj[1] :=	{ Window: 1, X: "+10", Y: "m", BackgroundColor: "0xFF22262A", Group: GroupName, State: 0, Value: 2, Text: "Blah Blah 1", FontSize: fs , Font: Font , MinW: 600, MinH: 30, MainColor: "0xFFffffff" }	
	obj[2] :=	{ Window: 1, X: "p", Y: "+5", BackgroundColor: "0xFF22262A", Group: GroupName, State: 1, Value: 2, Text: "Some other text", FontSize: fs , Font: Font , MinW: 150, MinH: 30, MainColor: "0xFF888888" }	
	obj[3] :=	{ Window: 1, X: "p", Y: "+5", BackgroundColor: "0xFF22262A", Group: GroupName, State: 0, Value: 2, Text: "Blah Blah 3", FontSize: fs , Font: Font , MinW: 150, MinH: 30, MainColor: "0xFF000000" }	
	return obj
}
Temp (1).gif
Temp (1).gif (651.77 KiB) Viewed 921 times


I haven't done anything with the graphics yet, but will replace the ellipse as mentioned before on my next pass.
I will also optimize the drawing method so that only the two radios that are changing get redrawn (that will reduce any flickering you may see when using large groups).
User avatar
JoeWinograd
Posts: 2177
Joined: 10 Feb 2014, 20:00
Location: U.S. Central Time Zone

Re: Screen Reader Issue - Graphics and Text

18 Jun 2021, 18:54

Hellbent wrote:Just replace the old one with the new one.
Will do...thanks for that!
Hellbent wrote:Here is a updated demo script that should give you a good sense of how to use it.
Thanks!
Hellbent wrote:will replace the ellipse as mentioned before on my next pass
Sounds good, but no rush on this.
Hellbent wrote:will also optimize the drawing method so that only the two radios that are changing get redrawn
That will be an excellent improvement! I don't like when GUIs flicker.

Thanks for your ongoing enhancements/improvements...very much appreciated! Regards, Joe

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Google [Bot], marypoppins_1, Rohwedder, Spawnova and 149 guests