Skip to content

Commit

Permalink
Ensure settings are scrolled to using tab navigation (#12300)
Browse files Browse the repository at this point in the history
Navigating through settings using tabbing does not visually scroll to the focused control.

wxPython ScrolledPanels calculate the position to scroll to based on the relative position to a focus elements parent, rather than the relative position to the ScrolledPanel itself.

When fixing right-to-left issues in #12181, another layer of nesting was introduced for controls in our settings panels, which caused the controls to no longer get scrolled to.

A patched version of ScrolledPanel is created, which calculates relative position to the ScrolledPanel
  • Loading branch information
seanbudd authored Apr 21, 2021
1 parent 0703066 commit bc8eb11
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 10 deletions.
52 changes: 44 additions & 8 deletions source/gui/nvdaControls.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
# -*- coding: UTF-8 -*-
#A part of NonVisual Desktop Access (NVDA)
#Copyright (C) 2016-2018 NV Access Limited, Derek Riemer
#This file is covered by the GNU General Public License.
#See the file COPYING for more details.
# A part of NonVisual Desktop Access (NVDA)
# Copyright (C) 2016-2021 NV Access Limited, Derek Riemer
# This file is covered by the GNU General Public License.
# See the file COPYING for more details.

from ctypes.wintypes import BOOL
from typing import Any, Tuple, Optional
import wx
from comtypes import GUID
from wx.lib import scrolledpanel
from wx.lib.mixins import listctrl as listmix
from .dpiScalingHelper import DpiScalingHelperMixin
from . import guiHelper
import oleacc
import winUser
import winsound

from collections.abc import Callable


class AutoWidthColumnListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin):
"""
A list control that allows you to specify a column to resize to take up the remaining width of a wx.ListCtrl.
Expand Down Expand Up @@ -355,3 +354,40 @@ def onSliderChar(self, evt):
evt.Skip()
return
self.SetValue(newValue)


class TabbableScrolledPanel(scrolledpanel.ScrolledPanel):
"""
This class was created to ensure a ScrolledPanel scrolls to nested children of the panel when navigating
with tabs (#12224). A PR to wxPython implementing this fix can be tracked on
https://github.com/wxWidgets/Phoenix/pull/1950
"""
def GetChildRectRelativeToSelf(self, child: wx.Window) -> wx.Rect:
"""
window.GetRect returns the size of a window, and its position relative to its parent.
When calculating ScrollChildIntoView, the position relative to its parent is not relevant unless the
parent is the ScrolledPanel itself. Instead, calculate the position relative to scrolledPanel
"""
childRectRelativeToScreen = child.GetScreenRect()
scrolledPanelScreenPosition = self.GetScreenPosition()
return wx.Rect(
childRectRelativeToScreen.x - scrolledPanelScreenPosition.x,
childRectRelativeToScreen.y - scrolledPanelScreenPosition.y,
childRectRelativeToScreen.width,
childRectRelativeToScreen.height
)

def ScrollChildIntoView(self, child: wx.Window) -> None:
"""
Overrides child.GetRect with `GetChildRectRelativeToSelf` before calling
`super().ScrollChildIntoView`. `super().ScrollChildIntoView` incorrectly uses child.GetRect to
navigate scrolling, which is relative to the parent, where it should instead be relative to this
ScrolledPanel.
"""
oldChildGetRectFunction = child.GetRect
child.GetRect = lambda: self.GetChildRectRelativeToSelf(child)
try:
super().ScrollChildIntoView(child)
finally:
# ensure child.GetRect is reset properly even if super().ScrollChildIntoView throws an exception
child.GetRect = oldChildGetRectFunction
3 changes: 1 addition & 2 deletions source/gui/settingsDialogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import typing
import wx
from vision.providerBase import VisionEnhancementProviderSettings
from wx.lib import scrolledpanel
from wx.lib.expando import ExpandoTextCtrl
import wx.lib.newevent
import winUser
Expand Down Expand Up @@ -483,7 +482,7 @@ def makeSettings(self, settingsSizer):
# The provided column header is just a placeholder, as it is hidden due to the wx.LC_NO_HEADER style flag.
self.catListCtrl.InsertColumn(0,categoriesLabelText)

self.container = scrolledpanel.ScrolledPanel(
self.container = nvdaControls.TabbableScrolledPanel(
parent = self,
style = wx.TAB_TRAVERSAL | wx.BORDER_THEME,
size=containerDim
Expand Down

0 comments on commit bc8eb11

Please sign in to comment.