Improve screen reader, keyboard, and High Contrast accessibility (#2518)

* Improve screen reader and keyboard accessibility

* Fix settings page repaint regression

---------

Co-authored-by: jbgrimsby <314385300+jbgrimsby@users.noreply.github.com>
This commit is contained in:
jbgrimsby
2026-09-15 13:56:16 +02:00
committed by GitHub
co-authored by jbgrimsby
parent da73edf4ea
commit 1384d4eb04
10 changed files with 405 additions and 17 deletions
+81 -11
View File
@@ -1110,6 +1110,7 @@ class CSettingsDlg: public CResizeableDlg<CSettingsDlg>
public:
CSettingsDlg( void );
void Init( CSetting *pSettings, ICustomSettings *pCustom, int tab, const wchar_t* appId );
bool NavigateTab( bool bPrevious );
BEGIN_MSG_MAP( CSettingsDlg )
MESSAGE_HANDLER( WM_INITDIALOG, OnInitDialog )
@@ -1186,7 +1187,7 @@ private:
const wchar_t* m_AppId;
void AddTabs( int name, const CSetting *pSelect=NULL );
void SetCurTab( int index, bool bReset, const CSetting *pSelect=NULL );
void SetCurTab( int index, bool bReset, const CSetting *pSelect=NULL, bool bFocusPanel=true );
bool IsTabValid( void );
void StorePlacement( void );
@@ -1312,6 +1313,7 @@ LRESULT CSettingsDlg::OnInitDialog( UINT uMsg, WPARAM wParam, LPARAM lParam, BOO
icon=(HICON)LoadImage(GetModuleHandle(L"shell32.dll"),MAKEINTRESOURCE(323),IMAGE_ICON,GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON),LR_DEFAULTCOLOR);
SendDlgItemMessage(IDC_ICONSEARCH,STM_SETICON,(WPARAM)icon);
SendDlgItemMessage(IDC_EDITSEARCH,EM_SETCUEBANNER,FALSE,(LPARAM)(const wchar_t*)LoadStringEx(IDS_SEARCH_PROMPT));
SetControlAccessibleName(GetDlgItem(IDC_EDITSEARCH),LoadStringEx(IDS_SEARCH_PROMPT));
SetWindowSubclass(GetDlgItem(IDC_EDITSEARCH),SubclassSearchBoxProc,'CLSH',0);
CWindow tooltip;
@@ -1364,6 +1366,7 @@ LRESULT CSettingsDlg::OnInitDialog( UINT uMsg, WPARAM wParam, LPARAM lParam, BOO
SIZE maxSize={0,0};
m_Tabs=GetDlgItem(IDC_TABSETTINGS);
m_Tabs.SetWindowText(LoadStringEx(IDS_SETTINGS_PAGES));
m_Panel=NULL;
int idx=0;
for (const CSetting *pSetting=m_pSettings;pSetting->name;pSetting++)
@@ -1418,7 +1421,8 @@ LRESULT CSettingsDlg::OnInitDialog( UINT uMsg, WPARAM wParam, LPARAM lParam, BOO
ShowWindow(SW_MAXIMIZE);
}
return TRUE;
GetDlgItem(IDC_BUTTONBACKUP).SetFocus();
return FALSE;
}
LRESULT CSettingsDlg::OnDestroy( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
@@ -1477,6 +1481,14 @@ void CSettingsDlg::AddTabs( int name, const CSetting *pSelect )
if (pSetting->nameID==name)
idx=i;
}
int tabCount=TabCtrl_GetItemCount(m_Tabs);
LONG_PTR tabStyle=m_Tabs.GetWindowLongPtr(GWL_STYLE);
if (tabCount<=1)
tabStyle=(tabStyle&~WS_TABSTOP)|TCS_FOCUSNEVER;
else
tabStyle=(tabStyle|WS_TABSTOP)&~TCS_FOCUSNEVER;
m_Tabs.SetWindowLongPtr(GWL_STYLE,tabStyle);
m_Tabs.SetWindowPos(NULL,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_FRAMECHANGED);
m_Index=-1;
TabCtrl_SetCurSel(m_Tabs,idx);
m_Tabs.InvalidateRect(NULL);
@@ -1499,20 +1511,23 @@ LRESULT CSettingsDlg::OnKeyDown( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL&
if (wParam==VK_TAB && GetKeyState(VK_CONTROL)<0)
{
int sel=TabCtrl_GetCurSel(m_Tabs);
int count=TabCtrl_GetItemCount(m_Tabs);
if (GetKeyState(VK_SHIFT)<0)
{
if (sel>0)
if (count>0)
{
TabCtrl_SetCurSel(m_Tabs,sel-1);
SetCurTab(sel-1,false);
int next=(sel+count-1)%count;
TabCtrl_SetCurSel(m_Tabs,next);
SetCurTab(next,false);
}
}
else
{
if (sel<TabCtrl_GetItemCount(m_Tabs)-1)
if (count>0)
{
TabCtrl_SetCurSel(m_Tabs,sel+1);
SetCurTab(sel+1,false);
int next=(sel+1)%count;
TabCtrl_SetCurSel(m_Tabs,next);
SetCurTab(next,false);
}
}
}
@@ -1740,6 +1755,7 @@ LRESULT CSettingsDlg::OnCheckAll( WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL
{
m_bBasic=bBasic;
AddTabs(-1);
::SetFocus(hWndCtl);
}
return 0;
}
@@ -1758,7 +1774,7 @@ LRESULT CSettingsDlg::OnSearchChange( WORD wNotifyCode, WORD wID, HWND hWndCtl,
return 0;
}
void CSettingsDlg::SetCurTab( int index, bool bReset, const CSetting *pSelect )
void CSettingsDlg::SetCurTab( int index, bool bReset, const CSetting *pSelect, bool bFocusPanel )
{
if (m_Index==index && !bReset) return;
m_Index=index;
@@ -1778,12 +1794,59 @@ void CSettingsDlg::SetCurTab( int index, bool bReset, const CSetting *pSelect )
::MapWindowPoints(NULL,m_hWnd,(POINT*)&rc,2);
TabCtrl_AdjustRect(m_Tabs,FALSE,&rc);
HWND hwnd=pPanel->Activate(pGroup,rc,bReset);
CString pageTitle=LoadStringEx(pGroup->nameID);
::SetWindowText(hwnd,pageTitle);
if (hwnd!=m_Panel)
{
if (m_Panel) ::ShowWindow(m_Panel,SW_HIDE);
m_Panel=hwnd;
::SetFocus(m_Panel);
}
if (bFocusPanel) ::SetFocus(m_Panel);
}
bool CSettingsDlg::NavigateTab( bool bPrevious )
{
HWND focus=::GetFocus();
HWND firstPanel=m_Panel?::GetNextDlgTabItem(m_Panel,NULL,FALSE):NULL;
HWND lastPanel=m_Panel?::GetNextDlgTabItem(m_Panel,NULL,TRUE):NULL;
HWND targets[]={
GetDlgItem(IDC_BUTTONBACKUP),
GetDlgItem(IDC_CHECKALL),
GetDlgItem(IDC_EDITSEARCH),
GetDlgItem(IDC_TABSETTINGS),
firstPanel,
GetDlgItem(IDOK),
GetDlgItem(IDCANCEL),
GetDlgItem(IDC_LINKHELP),
GetDlgItem(IDC_LINKWEB),
};
int current=-1;
if (m_Panel && (focus==m_Panel || ::IsChild(m_Panel,focus)))
{
HWND next=::GetNextDlgTabItem(m_Panel,focus,bPrevious);
HWND wrapped=bPrevious?lastPanel:firstPanel;
if (next && next!=wrapped)
return false; // Let IsDialogMessage move within the active page.
current=4;
}
else
{
for (int i=0;i<_countof(targets);i++)
if (focus==targets[i]) { current=i; break; }
}
if (current<0) return false;
for (int step=1;step<=_countof(targets);step++)
{
int index=(current+(bPrevious?-step:step)+_countof(targets)*2)%_countof(targets);
HWND target=(index==4 && bPrevious)?lastPanel:targets[index];
if (!target || !::IsWindowVisible(target) || !::IsWindowEnabled(target)) continue;
if (index==3 && !(::GetWindowLongPtr(target,GWL_STYLE)&WS_TABSTOP)) continue;
::SetFocus(target);
return true;
}
return false;
}
LRESULT CSettingsDlg::OnSelChanging( int idCtrl, LPNMHDR pnmh, BOOL& bHandled )
@@ -1793,7 +1856,9 @@ LRESULT CSettingsDlg::OnSelChanging( int idCtrl, LPNMHDR pnmh, BOOL& bHandled )
LRESULT CSettingsDlg::OnSelChange( int idCtrl, LPNMHDR pnmh, BOOL& bHandled )
{
SetCurTab(TabCtrl_GetCurSel(m_Tabs),false);
// Mouse clicks and Left/Right navigation keep focus on the tab strip.
// Ctrl+Tab is handled separately and moves focus into the new page.
SetCurTab(TabCtrl_GetCurSel(m_Tabs),false,NULL,false);
return 0;
}
@@ -1926,6 +1991,11 @@ bool IsSettingsMessage( MSG *msg )
g_SettingsDlg.SendMessage(WM_KEYDOWN,VK_TAB,msg->lParam);
return true;
}
if (msg->message==WM_KEYDOWN && msg->wParam==VK_TAB)
{
if (g_SettingsDlg.NavigateTab(GetKeyState(VK_SHIFT)<0))
return true;
}
if (msg->message==WM_KEYDOWN && msg->wParam==VK_RETURN && GetKeyState(VK_CONTROL)<0)
{
g_SettingsDlg.SendMessage(WM_COMMAND,IDOK,0);