Skip to content

Commit

Permalink
Rules offline edition (#42)
Browse files Browse the repository at this point in the history
 - RulesManager: Disable group by position when results aren't available
 - GUI: RulesManager: Add current group by option to dialog title
 - GUI: RulesManager: `ctrl(+shift)+tab` cycles through group by options
 - GUI: RulesManager: `F6` switches from the list to the summary or comments back and forth
 - GUI: RulesManager: `enter` on the "Group By" radios sets focus to the tree
 - GUI: RulesManager: `*` and `/` on the numpad while on the tree expands/collapses it all
 - GUI: RulesManager: Fix layout
 - GUI: RuleEditor: Fix checking for conflicting name when renaming a rule
 - GUI: RuleEditor: Fix layout
 - Remove `showManager`, `showCreator` and `showEditor` from `webModuleHandler` and `ruleHandler`
 - webModuleHandler: Remove `getEditableScratchpadWebModule` and `getEditableUserConfigWebModule` from public API
 - WebModule: Add `getWritableLayer` to public API
 - WebModuleDataLayer: Remove obsolete `rulesOnly` attribute
 - Bump API version to 0.6
  • Loading branch information
JulienCochuyt committed Sep 13, 2024
1 parent 424a5e1 commit 49ba6c5
Show file tree
Hide file tree
Showing 16 changed files with 635 additions and 642 deletions.
14 changes: 6 additions & 8 deletions addon/globalPlugins/webAccess/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,10 +211,6 @@ def script_showWebAccessGui(self, gesture): # @UnusedVariable

def showWebAccessGui(self):
obj = api.getFocusObject()
if obj is None or obj.appModule is None:
# Translators: Error message when attempting to show the Web Access GUI.
ui.message(_("The current object does not support Web Access."))
return
if not canHaveWebAccessSupport(obj):
# Translators: Error message when attempting to show the Web Access GUI.
ui.message(_("You must be in a web browser to use Web Access."))
Expand All @@ -223,15 +219,17 @@ def showWebAccessGui(self):
# Translators: Error message when attempting to show the Web Access GUI.
ui.message(_("You must be on the web page to use Web Access."))
return

from .gui import menu
context = {}
context["webAccess"] = self
context["focusObject"] = obj
context = {
"webAccess": self,
"focusObject": obj,
}
webModule = obj.webAccess.webModule
if webModule is not None:
context["webModule"] = webModule
context["pageTitle"] = webModule.pageTitle
mgr = webModule.ruleManager
context["result"] = mgr.getResultAtCaret(focus=obj)
menu.show(context)

@script(
Expand Down
38 changes: 29 additions & 9 deletions addon/globalPlugins/webAccess/gui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
import wx
import wx.lib.mixins.listctrl as listmix

from gui import guiHelper, nvdaControls, _isDebug
import gui
from gui import guiHelper, nvdaControls
from gui.dpiScalingHelper import DpiScalingHelperMixinWithoutInit
from gui.settingsDialogs import (
MultiCategorySettingsDialog,
Expand Down Expand Up @@ -210,6 +211,7 @@ def __str__(self):


class ScalingMixin(DpiScalingHelperMixinWithoutInit):

def scale(self, *args):
sizes = tuple((
self.scaleSize(arg) if arg > 0 else arg
Expand Down Expand Up @@ -239,6 +241,13 @@ def _buildGui(self):
self.SetSizer(self.mainSizer)


# TODO: Consider migrating to NVDA's SettingsDialog once we hit 2023.2 as minimum version
class ContextualDialog(ScalingMixin, wx.Dialog):

def initData(self, context):
self.context = context


class ContextualSettingsPanel(FillableSettingsPanel, metaclass=guiHelper.SIPABCMeta):
"""ABC for the different editor panels.
Expand Down Expand Up @@ -291,7 +300,9 @@ class FillableMultiCategorySettingsDialog(MultiCategorySettingsDialog, ScalingMi
See `FillableSettingsPanel`
"""


onCategoryChange = guarded(MultiCategorySettingsDialog.onCategoryChange)

def _getCategoryPanel(self, catId):
# Changes to the original implementation:
# - Add `proportion=1`
Expand Down Expand Up @@ -324,7 +335,7 @@ def _getCategoryPanel(self, catId):
panel.SetAccessible(SettingsPanelAccessible(panel))

return panel

@guarded
def _enterActivatesOk_ctrlSActivatesApply(self, evt):
if evt.KeyCode in (wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER):
Expand Down Expand Up @@ -396,6 +407,7 @@ def focusContainerControl(self, index: int):
class ContextualMultiCategorySettingsDialog(
KbNavMultiCategorySettingsDialog,
configuredSettingsDialogType(hasApplyButton=False),
ContextualDialog,
):

def __new__(cls, *args, **kwargs):
Expand Down Expand Up @@ -449,20 +461,17 @@ def _getCategoryPanel(self, catId):

# Changed from NVDA's MultiCategorySettingsDialog: Use ValidationError instead of ValueError,
# in order to not misinterpret a real unintentional ValueError.
# Hence, ContextualSettingsPanel.isValid can either return False or willingly raise a ValidationError
# with the same outcome of cancelling the save operation and the destruction of the dialog.
# Additionnaly, this implementation selects the category for the invalid panel.
def _validateAllPanels(self):
"""Check if all panels are valid, and can be saved
@note: raises ValidationError if a panel is not valid. See c{SettingsPanel.isValid}
"""
for panel in self.catIdToInstanceMap.values():
if panel.isValid() is False:
if not panel.isValid():
self.selectPanel(panel)
raise ValidationError("Validation for %s blocked saving settings" % panel.__class__.__name__)



class TreeContextualPanel(ContextualSettingsPanel):

CATEGORY_PARAMS_CONTEXT_KEY = "TreeContextualPanel.categoryParams"
Expand Down Expand Up @@ -776,12 +785,23 @@ def updateTreeParams(self, tree, treeNode, treeParent=None):
prm.treeParent = treeParent


def showContextualDialog(cls, context, parent, *args, **kwargs):
def showContextualDialog(
cls: type(ContextualDialog),
context: Mapping[str, Any],
parent: wx.Window,
*args,
**kwargs
):
"""
Show a `ContextualDialog`
If a `parent` is specified, the dialog is shown modal and this function
returns its return code.
"""
if parent is not None:
with cls(parent, *args, **kwargs) as dlg:
dlg.initData(context)
return dlg.ShowModal()
import gui
gui.mainFrame.prePopup()
try:
dlg = cls(gui.mainFrame, *args, **kwargs)
Expand Down
20 changes: 10 additions & 10 deletions addon/globalPlugins/webAccess/gui/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,13 @@
# See the file COPYING.txt at the root of this distribution for more details.


__version__ = "2024.08.24"
__version__ = "2024.08.25"
__authors__ = (
"Shirley Noel <[email protected]>",
"Julien Cochuyt <[email protected]>",
"André-Abush Clause <[email protected]>",
"Sendhil Randon <[email protected]>",
"Gatien Bouyssou <[email protected]>",
)


Expand Down Expand Up @@ -187,13 +190,12 @@ def onAutoActionChoice(self, evt):

@guarded
def onAddGesture(self, evt):
context = self.context
context = self.context.copy()
context["data"]["gestures"] = self.gesturesMap
if gestureBinding.show(context=context, parent=self) == wx.ID_OK:
id = context["data"].pop("gestureBinding")["gestureIdentifier"]
if gestureBinding.show(context, self):
id = context["data"]["gestureBinding"]["gestureIdentifier"]
self.onGestureChange(Change.CREATION, id)
del context["data"]["gestures"]


@guarded
def onDeleteGesture(self, evt):
index = self.gesturesListBox.Selection
Expand All @@ -203,15 +205,13 @@ def onDeleteGesture(self, evt):

@guarded
def onEditGesture(self, evt):
context = self.context
context = self.context.copy()
gestures = context["data"]["gestures"] = self.gesturesMap
id = self.getSelectedGesture()
context["data"]["gestureBinding"] = {"gestureIdentifier": id, "action": gestures[id]}
if gestureBinding.show(context=context, parent=self) == wx.ID_OK:
if gestureBinding.show(context=context, parent=self):
id = context["data"]["gestureBinding"]["gestureIdentifier"]
self.onGestureChange(Change.UPDATE, id)
del context["data"]["gestureBinding"]
del context["data"]["gestures"]

def onGestureChange(self, change: Change, id: str):
if change is Change.DELETION:
Expand Down
17 changes: 9 additions & 8 deletions addon/globalPlugins/webAccess/gui/criteriaEditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,14 +229,14 @@ def getSummary(context, data, indent="", condensed=False) -> str:
def testCriteria(context):
ruleData = copy.deepcopy(context["data"]["rule"])
ruleData["name"] = "__tmp__"
ruleData.pop("new", None)
# Other rule types might not support the "multiple" property we are forcing for the test
ruleData["type"] = ruleTypes.MARKER
critData = context["data"]["criteria"].copy()
critData.pop("new", None)
critData.pop("criteriaIndex", None)
ruleData["criteria"] = [critData]
ruleData.setdefault("properties", {})['multiple'] = True
critData.setdefault("properties", {}).pop("multiple", True)
critData.setdefault("properties", {}).pop("multiple", None)
mgr = context["webModule"].ruleManager
from ..ruleHandler import Rule
rule = Rule(mgr, ruleData)
Expand Down Expand Up @@ -324,17 +324,18 @@ def makeSettings(self, settingsSizer):

def initData(self, context):
super().initData(context)
data = self.getData()
new = data.get("new", False)
self.sequenceOrderChoice.Clear()
nbCriteria = len(context["data"]["rule"]["criteria"]) + (1 if new else 0)
if nbCriteria == 1:
nbAlternatives = len(context["data"]["rule"]["criteria"])
if context.get("new"):
nbAlternatives += 1
data = self.getData()
if nbAlternatives == 1:
for item in self.hideable:
item.Show(False)
else:
for index in range(nbCriteria):
for index in range(nbAlternatives):
self.sequenceOrderChoice.Append(str(index + 1))
index = data.get("criteriaIndex", nbCriteria + 1)
index = data.get("criteriaIndex", nbAlternatives + 1)
self.sequenceOrderChoice.SetSelection(index)
self.criteriaName.Value = data.get("name", "")
self.commentText.Value = data.get("comment", "")
Expand Down
21 changes: 14 additions & 7 deletions addon/globalPlugins/webAccess/gui/elementDescription.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,13 @@
# Get ready for Python 3


__version__ = "2024.07.19"
__author__ = "Frédéric Brugnot <[email protected]>"
__version__ = "2024.08.25"
__authors__ = (
"Frédéric Brugnot <[email protected]>",
"Julien Cochuyt <[email protected]>",
"André-Abush Clause <[email protected]>",
"Gatien Bouyssou <[email protected]>",
)
__license__ = "GPL"


Expand Down Expand Up @@ -99,11 +104,13 @@ def formatAttributes(dic):
def getNodeDescription():
import api
from ..overlay import WebAccessObject
focus = api.getFocusObject()
if not (
isinstance(focus, WebAccessObject)
and focus.webAccess.nodeManager
):
for focus in (api.getFocusObject(), gui.mainFrame.prevFocus):
if (
isinstance(focus, WebAccessObject)
and focus.webAccess.nodeManager
):
break
else:
return _("No NodeManager")
ruleManager = focus.webAccess.ruleManager
results = ruleManager.getResults() if ruleManager else []
Expand Down
13 changes: 10 additions & 3 deletions addon/globalPlugins/webAccess/gui/gestureBinding.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,15 @@
# See the file COPYING.txt at the root of this distribution for more details.


__version__ = "2024.08.19"
__author__ = "Shirley Noel <[email protected]>"
__version__ = "2024.08.25"
__authors__ = (
"Shirley Noel <[email protected]>",
"Frédéric Brugnot <[email protected]>",
"Julien Cochuyt <[email protected]>",
"André-Abush Clause <[email protected]>",
"Sendhil Randon <[email protected]>",
"Gatien Bouyssou <[email protected]>",
)


from collections.abc import Mapping
Expand Down Expand Up @@ -211,4 +218,4 @@ def _captureFunc(self, gesture):


def show(context: Mapping[str, Any], parent: wx.Window):
return showContextualDialog(GestureBindingDialog, context, parent)
return showContextualDialog(GestureBindingDialog, context, parent) == wx.ID_OK
37 changes: 23 additions & 14 deletions addon/globalPlugins/webAccess/gui/menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,12 @@
"""Web Access GUI."""


__version__ = "2024.08.02"
__author__ = "Julien Cochuyt <[email protected]>"
__version__ = "2024.08.25"
__authors__ = (
"Julien Cochuyt <[email protected]>",
"André-Abush Clause <[email protected]>",
"Gatien Bouyssou <[email protected]>",
)


import wx
Expand All @@ -33,7 +37,6 @@

from ... import webAccess
from .. import ruleHandler
from .. import webModuleHandler
from ..utils import guarded
from . import webModulesManager

Expand All @@ -53,17 +56,15 @@ def __init__(self, context):
self.context = context

if webAccess.webAccessEnabled:
webModule = context["webModule"] if "webModule" in context else None
webModule = context.get("webModule")

if webModule is not None:
if webModule:
item = self.Append(
wx.ID_ANY,
# Translators: Web Access menu item label.
_("&New rule...")
)
self.Bind(wx.EVT_MENU, self.onRuleCreate, item)

if webModule is not None:
item = self.Append(
wx.ID_ANY,
# Translators: Web Access menu item label.
Expand All @@ -72,13 +73,14 @@ def __init__(self, context):
self.Bind(wx.EVT_MENU, self.onRulesManager, item)
self.AppendSeparator()

if webModule is None:
if not webModule:
item = self.Append(
wx.ID_ANY,
# Translators: Web Access menu item label.
_("&New web module..."))
self.Bind(wx.EVT_MENU, self.onWebModuleCreate, item)
else:

if webModule:
item = self.Append(
wx.ID_ANY,
# Translators: Web Access menu item label.
Expand Down Expand Up @@ -110,23 +112,30 @@ def show(self):

@guarded
def onRuleCreate(self, evt):
ruleHandler.showCreator(self.context)
self.context["new"] = True
from .rule.editor import show
show(self.context, gui.mainFrame)

@guarded
def onRulesManager(self, evt):
ruleHandler.showManager(self.context)
from .rule.manager import show
show(self.context, gui.mainFrame)

@guarded
def onWebModuleCreate(self, evt):
webModuleHandler.showCreator(self.context)
self.context["new"] = True
from .webModuleEditor import show
show(self.context, gui.mainFrame)

@guarded
def onWebModuleEdit(self, evt):
webModuleHandler.showEditor(self.context)
from .webModuleEditor import show
show(self.context)

@guarded
def onWebModulesManager(self, evt):
webModuleHandler.showManager(self.context)
from .webModulesManager import show
show(self.context)

@guarded
def onWebAccessToggle(self, evt):
Expand Down
Loading

0 comments on commit 49ba6c5

Please sign in to comment.