Skip to content

Commit 7afc3f6

Browse files
committed
added an utility to manage NVDA's config easier for this add-on
1 parent 5df7b50 commit 7afc3f6

File tree

3 files changed

+145
-47
lines changed

3 files changed

+145
-47
lines changed

addon/globalPlugins/beepKeyboard.py renamed to addon/globalPlugins/beepKeyboard/__init__.py

Lines changed: 50 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,34 @@
11
# -*- coding: UTF-8 -*-
22
# Beep keyboard: This add-on beeps with some keyboard events.
3-
# Copyright (C) 2019 David CM
3+
# Copyright (C) 2019 - 2022 David CM
44
# Author: David CM <[email protected]>
55
# Released under GPL 2
66
#globalPlugins/beepKeyboard.py
77

88
import api, codecs, config, globalPluginHandler, gui, keyboardHandler, tones, ui, winreg, winUser, wx, addonHandler
9+
910
addonHandler.initTranslation()
1011

11-
# config schema
12-
confspec = {
13-
"beepUpperWithCapsLock": "boolean(default=True)",
14-
"beepCharacterWithShift": "boolean(default=False)",
15-
"beepToggleKeyChanges": "boolean(default=False)",
16-
"announceToggleStatus": "boolean(default=True)",
17-
"disableBeepingOnPasswordFields": "boolean(default=True)",
18-
"ignoredCharactersForShift": "string(default='\\x1b\\t\\b\\r ')",
19-
"beepForCharacters": "string(default='')",
20-
"shiftedCharactersTone": "int_list(default=list(6000,10,25))",
21-
"customCharactersTone": "int_list(default=list(6000,10,25))",
22-
"capsLockUpperTone": "int_list(default=list(3000,40,50))",
23-
"toggleOffTone": "int_list(default=list(500,40,50))",
24-
"toggleOnTone": "int_list(default=list(2000, 40, 50))"
25-
}
26-
config.conf.spec["beepKeyboard"] = confspec
12+
13+
from ._configHelper import *
14+
class AppConfig(BaseConfig):
15+
def __init__(self):
16+
super().__init__('beepKeyboard')
17+
18+
beepUpperWithCapsLock = OptConfig('boolean(default=True)')
19+
beepCharacterWithShift = OptConfig('boolean(default=False)')
20+
beepToggleKeyChanges = OptConfig('boolean(default=False)')
21+
announceToggleStatus = OptConfig('boolean(default=True)')
22+
disableBeepingOnPasswordFields = OptConfig('boolean(default=True)')
23+
ignoredCharactersForShift = OptConfig("string(default='\\x1b\\t\\b\\r ')")
24+
beepForCharacters = OptConfig("string(default='')")
25+
shiftedCharactersTone = OptConfig('int_list(default=list(6000,10,25))')
26+
customCharactersTone = OptConfig('int_list(default=list(6000,10,25))')
27+
capsLockUpperTone = OptConfig('int_list(default=list(3000,40,50))')
28+
toggleOffTone = OptConfig('int_list(default=list(500,40,50))')
29+
toggleOnTone = OptConfig('int_list(default=list(2000, 40, 50))')
30+
AF = registerConfig(AppConfig)
31+
2732

2833
# Constants.
2934
REG_TOGGLE_KEYS = r"Control Panel\Accessibility\ToggleKeys"
@@ -35,7 +40,7 @@
3540

3641
def beep(l):
3742
""" it receives a list with three arguments to beep: [pitch, length, volume]"""
38-
if not (config.conf['beepKeyboard']['disableBeepingOnPasswordFields'] and api.getFocusObject().isProtected):
43+
if not (AF.disableBeepingOnPasswordFields and api.getFocusObject().isProtected):
3944
tones.beep(*l, right=l[-1])
4045

4146
#saves the original _reportToggleKey function
@@ -46,9 +51,9 @@ def _reportToggleKey(self):
4651
global ignoreToggleKeys
4752
if not ignoreToggleKeys:
4853
if winUser.getKeyState(self.vkCode) & 1:
49-
beep(config.conf['beepKeyboard']['toggleOnTone'])
50-
else: beep(config.conf['beepKeyboard']['toggleOffTone'])
51-
if ignoreToggleKeys or config.conf['beepKeyboard']['announceToggleStatus'] or (config.conf['beepKeyboard']['disableBeepingOnPasswordFields'] and api.getFocusObject().isProtected):
54+
beep(AF.toggleOnTone)
55+
else: beep(AF.toggleOffTone)
56+
if ignoreToggleKeys or AF.announceToggleStatus or (AF.disableBeepingOnPasswordFields and api.getFocusObject().isProtected):
5257
origReportToggleKey(self)
5358

5459
class BeepKeyboardSettingsPanel(gui.SettingsPanel):
@@ -59,19 +64,19 @@ def makeSettings(self, settingsSizer):
5964
sHelper = gui.guiHelper.BoxSizerHelper(self, sizer=settingsSizer)
6065
# Translators: label for a checkbox option in the settings panel.
6166
self.beepUpperWithCapsLock = sHelper.addItem(wx.CheckBox(self, label=_("Beep for uppercases when &caps lock is on")))
62-
self.beepUpperWithCapsLock.SetValue(config.conf['beepKeyboard']['beepUpperWithCapsLock'])
67+
self.beepUpperWithCapsLock.SetValue(AF.beepUpperWithCapsLock)
6368
# Translators: label for a checkbox option in the settings panel.
6469
self.beepCharacterWithShift = sHelper.addItem(wx.CheckBox(self, label=_("Beep for typed characters when &shift is pressed")))
65-
self.beepCharacterWithShift.SetValue(config.conf['beepKeyboard']['beepCharacterWithShift'])
70+
self.beepCharacterWithShift.SetValue(AF.beepCharacterWithShift)
6671
# Translators: label for a checkbox option in the settings panel.
6772
self.beepToggleKeyChanges = sHelper.addItem(wx.CheckBox(self, label=_("Beep for &toggle keys changes")))
68-
self.beepToggleKeyChanges.SetValue(config.conf['beepKeyboard']['beepToggleKeyChanges'])
73+
self.beepToggleKeyChanges.SetValue(AF.beepToggleKeyChanges)
6974
# Translators: label for a checkbox option in the settings panel.
7075
self.announceToggleStatus = sHelper.addItem(wx.CheckBox(self, label=_("&Announce toggle keys changes (if Beep for toggle keys changes is disabled NVDA will have the original behavior)")))
71-
self.announceToggleStatus.SetValue(config.conf['beepKeyboard']['announceToggleStatus'])
76+
self.announceToggleStatus.SetValue(AF.announceToggleStatus)
7277
# Translators: label for a checkbox option in the settings panel.
7378
self.disableBeepingOnPasswordFields = sHelper.addItem(wx.CheckBox(self, label=_("&disable beeping on password fields")))
74-
self.disableBeepingOnPasswordFields.SetValue(config.conf['beepKeyboard']['disableBeepingOnPasswordFields'])
79+
self.disableBeepingOnPasswordFields.SetValue(AF.disableBeepingOnPasswordFields)
7580

7681
# Translators: label for a button to open advanced settings dialog in the settings panel.
7782
advancedButton = sHelper.addItem (wx.Button (self, label = _("&Open advanced options")))
@@ -82,11 +87,11 @@ def onAdvanced(self, evt):
8287
advanced.ShowModal()
8388

8489
def onSave(self):
85-
config.conf['beepKeyboard']['beepUpperWithCapsLock'] = self.beepUpperWithCapsLock.GetValue()
86-
config.conf['beepKeyboard']['beepCharacterWithShift'] = self.beepCharacterWithShift.GetValue()
87-
config.conf['beepKeyboard']['beepToggleKeyChanges'] = self.beepToggleKeyChanges.GetValue()
88-
config.conf['beepKeyboard']['announceToggleStatus'] = self.announceToggleStatus.GetValue()
89-
config.conf['beepKeyboard']['disableBeepingOnPasswordFields'] = self.disableBeepingOnPasswordFields.GetValue()
90+
AF.beepUpperWithCapsLock = self.beepUpperWithCapsLock.GetValue()
91+
AF.beepCharacterWithShift = self.beepCharacterWithShift.GetValue()
92+
AF.beepToggleKeyChanges = self.beepToggleKeyChanges.GetValue()
93+
AF.announceToggleStatus = self.announceToggleStatus.GetValue()
94+
AF.disableBeepingOnPasswordFields = self.disableBeepingOnPasswordFields.GetValue()
9095
config.post_configProfileSwitch.notify()
9196

9297
class AdvancedBeepKeyboardSettingsDialog(gui.SettingsDialog):
@@ -95,14 +100,13 @@ class AdvancedBeepKeyboardSettingsDialog(gui.SettingsDialog):
95100

96101
def makeSettings(self, settingsSizer):
97102
sHelper = gui.guiHelper.BoxSizerHelper(self, sizer=settingsSizer)
98-
co = config.conf['beepKeyboard']
99103
# Translators: label for an edit text control option in the advanced settings dialog.
100104
self.ignoredCharactersForShift = sHelper.addLabeledControl(_("&Ignored characters with shift pressed"), wx.TextCtrl)
101-
self.ignoredCharactersForShift.SetValue(co['ignoredCharactersForShift'])
105+
self.ignoredCharactersForShift.SetValue(AF.ignoredCharactersForShift)
102106
# Translators: label for an edit text control option in the advanced settings dialog.
103107
self.beepForCharacters = sHelper.addLabeledControl(_("Beep &always for the following characters"), wx.TextCtrl)
104-
self.beepForCharacters.SetValue(co['beepForCharacters'])
105-
self.tonesParameters = [co['shiftedCharactersTone'], co['customCharactersTone'], co['capsLockUpperTone'], co['toggleOffTone'], co['toggleOnTone']]
108+
self.beepForCharacters.SetValue(AF.beepForCharacters)
109+
self.tonesParameters = [AF.shiftedCharactersTone, AF.customCharactersTone, AF.capsLockUpperTone, AF.toggleOffTone, AF.toggleOnTone]
106110
# Translators: label for a combo box control in the advanced settings dialog.
107111
self.toneOptionsList = sHelper.addLabeledControl(_("&Select tone to configure"), wx.Choice, choices=[
108112
# Translators: label for an option of a combo box control.
@@ -165,9 +169,9 @@ def onToneOptionChange(self, evt):
165169

166170
def onOk(self, evt):
167171
self.updateCurrentToneValues()
168-
config.conf['beepKeyboard']['ignoredCharactersForShift'] = self.ignoredCharactersForShift.GetValue()
169-
config.conf['beepKeyboard']['beepForCharacters'] = self.beepForCharacters.GetValue()
170-
config.conf['beepKeyboard']['shiftedCharactersTone'], config.conf['beepKeyboard']['customCharactersTone'], config.conf['beepKeyboard']['capsLockUpperTone'], config.conf['beepKeyboard']['toggleOffTone'], config.conf['beepKeyboard']['toggleOnTone'] = self.tonesParameters
172+
AF.ignoredCharactersForShift = self.ignoredCharactersForShift.GetValue()
173+
AF.beepForCharacters = self.beepForCharacters.GetValue()
174+
AF.shiftedCharactersTone, AF.customCharactersTone, AF.capsLockUpperTone, AF.toggleOffTone, AF.toggleOnTone = self.tonesParameters
171175
config.post_configProfileSwitch.notify()
172176
super(AdvancedBeepKeyboardSettingsDialog, self).onOk(evt)
173177

@@ -192,11 +196,11 @@ def checkEaseAccessToggleKeys(self):
192196

193197
def event_typedCharacter(self, obj, nextHandler, ch):
194198
nextHandler()
195-
if config.conf['beepKeyboard']['beepUpperWithCapsLock'] and winUser.getKeyState(winUser.VK_CAPITAL)&1 and ch.isupper():
196-
beep(config.conf['beepKeyboard']['capsLockUpperTone'])
197-
elif config.conf['beepKeyboard']['beepCharacterWithShift'] and not winUser.getKeyState(winUser.VK_CONTROL) &32768 and winUser.getKeyState(winUser.VK_SHIFT) &32768 and ch not in self.ignoredCharactersForShift and not (config.conf["keyboard"]["beepForLowercaseWithCapslock"] and ch.islower() and winUser.getKeyState(winUser.VK_CAPITAL)&1):
198-
beep(config.conf['beepKeyboard']['shiftedCharactersTone'])
199-
elif ch in self.beepForCharacters: beep(config.conf['beepKeyboard']['customCharactersTone'])
199+
if AF.beepUpperWithCapsLock and winUser.getKeyState(winUser.VK_CAPITAL)&1 and ch.isupper():
200+
beep(AF.capsLockUpperTone)
201+
elif AF.beepCharacterWithShift and not winUser.getKeyState(winUser.VK_CONTROL) &32768 and winUser.getKeyState(winUser.VK_SHIFT) &32768 and ch not in self.ignoredCharactersForShift and not (config.conf["keyboard"]["beepForLowercaseWithCapslock"] and ch.islower() and winUser.getKeyState(winUser.VK_CAPITAL)&1):
202+
beep(AF.shiftedCharactersTone)
203+
elif ch in self.beepForCharacters: beep(AF.customCharactersTone)
200204

201205
def setExternalReportToggleStatus(self, flag):
202206
global ignoreToggleKeys
@@ -208,9 +212,9 @@ def setExternalReportToggleStatus(self, flag):
208212
keyboardHandler.KeyboardInputGesture._reportToggleKey = origReportToggleKey
209213

210214
def handleConfigProfileSwitch(self):
211-
self.setExternalReportToggleStatus(config.conf['beepKeyboard']['beepToggleKeyChanges'])
212-
self.ignoredCharactersForShift = codecs.decode(config.conf['beepKeyboard']['ignoredCharactersForShift'], 'unicode_escape')
213-
self.beepForCharacters = codecs.decode(config.conf['beepKeyboard']['beepForCharacters'], 'unicode_escape')
215+
self.setExternalReportToggleStatus(AF.beepToggleKeyChanges)
216+
self.ignoredCharactersForShift = codecs.decode(AF.ignoredCharactersForShift, 'unicode_escape')
217+
self.beepForCharacters = codecs.decode(AF.beepForCharacters, 'unicode_escape')
214218

215219
def terminate(self):
216220
super(GlobalPlugin, self).terminate()
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# NVDA configHelper.
2+
# Copyright (C) 2022 David CM
3+
4+
import config
5+
6+
def getConfigValue(path, optName):
7+
""" this function helps to accessing config values.
8+
params
9+
@path: the path to the option.
10+
@optName: the option name
11+
"""
12+
ops = config.conf[path[0]]
13+
for k in path[1:]:
14+
ops = ops[k]
15+
return ops[optName]
16+
17+
18+
def setConfigValue(path, optName, value):
19+
""" this function helps to accessing and set config values.
20+
params
21+
@path: the path to the option.
22+
@optName: the option name
23+
@value: the value to set.
24+
"""
25+
ops = config.conf[path[0]]
26+
for k in path[1:]:
27+
ops = ops[k]
28+
ops[optName] = value
29+
30+
31+
def registerConfig(clsSpec):
32+
AF = clsSpec()
33+
config.conf.spec[AF.path[0]] = AF.createSpec()
34+
AF.returnValue = True
35+
return AF
36+
37+
38+
class OptConfig:
39+
""" just a helper descriptor to create the main class to accesing config values.
40+
the option name will be taken from the declared variable. if you need to set another name, set it in the first param.
41+
"""
42+
def __init__(self, a, b = None):
43+
"""
44+
params:
45+
@a: usually the spec description. But if b is not none, a will be the name of the option.
46+
@b: the config description when is not None.
47+
"""
48+
if b:
49+
self.name = a
50+
self.desc = b
51+
else:
52+
self.desc = a
53+
self.name = None
54+
55+
def __set_name__(self, owner, name):
56+
if not self.name:
57+
self.name = name
58+
owner._confOpts.append(name)
59+
60+
def __get__(self, obj, type=None):
61+
if obj.returnValue:
62+
return getConfigValue(obj.path, self.name)
63+
return self.name, self.desc
64+
65+
def __set__(self, obj, value):
66+
setConfigValue(obj.path, self.name, value)
67+
68+
69+
class BaseConfig:
70+
""" this class will help to get and set config values.
71+
the idea behind this is to generalize the config path and config names.
72+
sometimes, a mistake in the dict to access the values can produce an undetectable bug.
73+
if returnValue attribute is set to False, this will return the option name instead of the value.
74+
by default this value is False, to help to create the configuration spec first.
75+
Set it to true after creating this spec.
76+
"""
77+
78+
def __init__(self, path):
79+
self.returnValue = False
80+
if isinstance(path, list):
81+
self.path = path
82+
else:
83+
self.path = [path]
84+
85+
def createSpec(self):
86+
""" this method creates a config spec with the provided attributes in the class
87+
"""
88+
s = {}
89+
for k in self.__class__._confOpts:
90+
k = self.__getattribute__(k)
91+
s[k[0]] = k[1]
92+
return s
93+
# an array of the available options.
94+
_confOpts = []

buildVars.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
# Translators: Long description to be shown for this add-on on add-on information from add-ons manager
2020
"addon_description" : _("""This add-on beeps with some keyboard events."""),
2121
# version
22-
"addon_version" : ,
22+
"addon_version" : "1.8",
2323
# Author(s)
2424
"addon_author" : u"David CM <[email protected]>",
2525
# URL for the add-on documentation support

0 commit comments

Comments
 (0)