Skip to content

Commit cc9c4af

Browse files
committed
Redesign and refactor
1 parent 997b117 commit cc9c4af

9 files changed

Lines changed: 336 additions & 56 deletions

File tree

addon/globalPlugins/devBox/__init__.py

Lines changed: 34 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -2,90 +2,68 @@
22

33
import addonHandler
44
import api
5-
import characterProcessing
5+
import config
66
import globalCommands
77
import globalPluginHandler
8-
import languageHandler
98
import nvwave
109
import scriptHandler
1110
import speech
1211
import textInfos
1312

14-
SOUNDS_DIR = Path(__file__).parent / "media"
13+
from . import interface
14+
from .features.diff_sounds import DiffSoundsFeature
15+
from .features.space_folding import SpaceFoldingFeature
16+
17+
config.conf.spec["devBox"] = {
18+
"features": {
19+
"diffSounds": "boolean(default=false)",
20+
"spaceFolding": "boolean(default=false)",
21+
},
22+
}
23+
ADDON_ROOT = Path(addonHandler.getCodeAddon().installPath).resolve()
24+
SOUNDS_DIR = ADDON_ROOT / "media"
1525
DIFF_SOUNDS = {
1626
"-": "diffLineDeleted.wav",
1727
"+": "diffLineInserted.wav",
1828
}
1929
DIFF_SOUNDS = {k: str(SOUNDS_DIR / v) for k, v in DIFF_SOUNDS.items()}
2030

21-
SPACE = characterProcessing.processSpeechSymbol(languageHandler.getLanguage(), " ")
22-
end_utterance_command = speech.commands.EndUtteranceCommand()
2331

2432
# We need this to get translations fromNVDA standard catalog.
2533
gettext = _
26-
addonHandler.initTranslation()
2734

2835

29-
def get_spelling_speech_decorator(func):
30-
def wrapper(*args, **kwargs):
31-
original_commands = list(func(*args, **kwargs))
32-
new_commands = []
33-
spaces_buffer = []
34-
35-
def flush_spaces_buffer():
36-
spaces_amount = len(spaces_buffer) // 2
37-
if spaces_amount < 2:
38-
new_commands.extend(spaces_buffer)
39-
else:
40-
new_commands.append(f"{spaces_amount} {SPACE}")
41-
new_commands.append(end_utterance_command)
42-
spaces_buffer.clear()
43-
44-
cursor = 0
45-
while cursor < len(original_commands):
46-
command = original_commands[cursor]
47-
if isinstance(command, str) and command == SPACE:
48-
spaces_buffer.extend(original_commands[cursor : cursor + 2])
49-
cursor += 2 # Space and end utterance command
50-
continue
51-
flush_spaces_buffer()
52-
new_commands.append(command)
53-
cursor += 1
54-
flush_spaces_buffer()
55-
return new_commands
56-
57-
return wrapper
36+
addonHandler.initTranslation()
5837

5938

6039
class GlobalPlugin(globalPluginHandler.GlobalPlugin):
6140
scriptCategory = "Dev Box"
6241

6342
def __init__(self, *args, **kwargs):
6443
super().__init__(*args, **kwargs)
65-
self._speech__getSpellingSpeechWithoutCharMode = (
66-
speech._getSpellingSpeechWithoutCharMode
67-
)
68-
speech._getSpellingSpeechWithoutCharMode = get_spelling_speech_decorator(
69-
speech._getSpellingSpeechWithoutCharMode
70-
)
71-
self._speech_speech__getSpellingSpeechWithoutCharMode = (
72-
speech.speech._getSpellingSpeechWithoutCharMode
73-
)
74-
speech.speech._getSpellingSpeechWithoutCharMode = get_spelling_speech_decorator(
75-
speech.speech._getSpellingSpeechWithoutCharMode
76-
)
44+
self.config = config.conf["devBox"]
45+
self._diff_sounds_enabled = False
46+
self.features = [SpaceFoldingFeature(self), DiffSoundsFeature(self)]
47+
self.sync_features()
48+
interface.add_settings(self.on_config_change)
49+
config.post_configProfileSwitch.register(self.on_profile_switch)
50+
51+
def on_config_change(self):
52+
self.sync_features()
53+
54+
def on_profile_switch(self):
55+
self.sync_features()
56+
57+
def sync_features(self):
58+
[feature.sync() for feature in self.features]
7759

7860
def terminate(self):
79-
speech._getSpellingSpeechWithoutCharMode = (
80-
self._speech__getSpellingSpeechWithoutCharMode
81-
)
82-
speech.speech._getSpellingSpeechWithoutCharMode = (
83-
self._speech_speech__getSpellingSpeechWithoutCharMode
84-
)
61+
[feature.terminate() for feature in self.features]
62+
interface.remove_settings()
63+
config.post_configProfileSwitch.unregister(self.on_profile_switch)
8564

8665
@scriptHandler.script(
8766
description=gettext(
88-
# Translators: Input help mode message for move review cursor to previous line command.
8967
"Moves the review cursor to the previous line of the current navigator object and speaks it",
9068
),
9169
resumeSayAllMode=speech.sayAll.CURSOR.REVIEW,
@@ -99,7 +77,6 @@ def script_review_previous_line(self, gesture):
9977

10078
@scriptHandler.script(
10179
description=gettext(
102-
# Translators: Input help mode message for read current line under review cursor command.
10380
"Reports the line of the current navigator object where the review cursor is situated. "
10481
"If this key is pressed twice, the current line will be spelled. "
10582
"Pressing three times will spell the line using character descriptions.",
@@ -116,7 +93,6 @@ def script_review_currentLine(self, gesture):
11693

11794
@scriptHandler.script(
11895
description=gettext(
119-
# Translators: Input help mode message for move review cursor to next line command.
12096
"Moves the review cursor to the next line of the current navigator object and speaks it",
12197
),
12298
resumeSayAllMode=speech.sayAll.CURSOR.REVIEW,
@@ -129,6 +105,8 @@ def script_review_nextLine(self, gesture):
129105
return result
130106

131107
def report_diff_line_status(self):
108+
if not self._diff_sounds_enabled:
109+
return
132110
info = api.getReviewPosition().copy()
133111
info.expand(textInfos.UNIT_LINE)
134112
text = info.text

addon/globalPlugins/devBox/features/__init__.py

Whitespace-only changes.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from abc import ABC, abstractmethod
2+
3+
import config
4+
from globalPluginHandler import GlobalPlugin
5+
6+
7+
class BaseFeature(ABC):
8+
def __init__(self, global_plugin, config_key):
9+
self.config_key = config_key
10+
self.global_plugin: GlobalPlugin = global_plugin
11+
self.is_enabled = False
12+
13+
@property
14+
def features_config(self):
15+
return config.conf["devBox"]["features"]
16+
17+
@property
18+
def should_be_enabled(self):
19+
return bool(self.features_config[self.config_key])
20+
21+
def sync(self):
22+
if self.should_be_enabled:
23+
if not self.is_enabled:
24+
self._enable()
25+
else:
26+
if self.is_enabled:
27+
self._disable()
28+
29+
def terminate(self):
30+
if self.is_enabled:
31+
self._disable()
32+
33+
def _enable(self):
34+
self.enable()
35+
self.is_enabled = True
36+
37+
def _disable(self):
38+
self.disable()
39+
self.is_enabled = False
40+
41+
@abstractmethod
42+
def enable(self):
43+
pass
44+
45+
@abstractmethod
46+
def disable(self):
47+
pass
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from .base import BaseFeature
2+
3+
4+
class DiffSoundsFeature(BaseFeature):
5+
def __init__(self, global_plugin):
6+
super().__init__(global_plugin, "diffSounds")
7+
8+
def enable(self):
9+
self.global_plugin._diff_sounds_enabled = True
10+
11+
def disable(self):
12+
self.global_plugin._diff_sounds_enabled = False
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import characterProcessing
2+
import languageHandler
3+
import speech
4+
5+
from .base import BaseFeature
6+
7+
SPACE = characterProcessing.processSpeechSymbol(languageHandler.getLanguage(), " ")
8+
end_utterance_command = speech.commands.EndUtteranceCommand()
9+
10+
11+
def get_spelling_speech_decorator(func):
12+
def wrapper(*args, **kwargs):
13+
original_commands = list(func(*args, **kwargs))
14+
new_commands = []
15+
spaces_buffer = []
16+
17+
def flush_spaces_buffer():
18+
spaces_amount = len(spaces_buffer) // 2
19+
if spaces_amount < 2:
20+
new_commands.extend(spaces_buffer)
21+
else:
22+
new_commands.append(f"{spaces_amount} {SPACE}")
23+
new_commands.append(end_utterance_command)
24+
spaces_buffer.clear()
25+
26+
cursor = 0
27+
while cursor < len(original_commands):
28+
command = original_commands[cursor]
29+
if isinstance(command, str) and command == SPACE:
30+
spaces_buffer.extend(original_commands[cursor : cursor + 2])
31+
cursor += 2 # Space and end utterance command
32+
continue
33+
flush_spaces_buffer()
34+
new_commands.append(command)
35+
cursor += 1
36+
flush_spaces_buffer()
37+
return new_commands
38+
39+
return wrapper
40+
41+
42+
class SpaceFoldingFeature(BaseFeature):
43+
def __init__(self, global_plugin):
44+
super().__init__(global_plugin, "spaceFolding")
45+
46+
def enable(self):
47+
self._speech__getSpellingSpeechWithoutCharMode = (
48+
speech._getSpellingSpeechWithoutCharMode
49+
)
50+
speech._getSpellingSpeechWithoutCharMode = get_spelling_speech_decorator(
51+
speech._getSpellingSpeechWithoutCharMode
52+
)
53+
self._speech_speech__getSpellingSpeechWithoutCharMode = (
54+
speech.speech._getSpellingSpeechWithoutCharMode
55+
)
56+
speech.speech._getSpellingSpeechWithoutCharMode = get_spelling_speech_decorator(
57+
speech.speech._getSpellingSpeechWithoutCharMode
58+
)
59+
60+
def disable(self):
61+
speech._getSpellingSpeechWithoutCharMode = (
62+
self._speech__getSpellingSpeechWithoutCharMode
63+
)
64+
speech.speech._getSpellingSpeechWithoutCharMode = (
65+
self._speech_speech__getSpellingSpeechWithoutCharMode
66+
)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import addonHandler
2+
import config
3+
import gui
4+
from gui.nvdaControls import CustomCheckListBox
5+
6+
from .interface_helpers import (
7+
CheckListBoxToDictConverter,
8+
ConfigBoundSettingsPanel,
9+
bind_with_config,
10+
)
11+
12+
addonHandler.initTranslation()
13+
14+
15+
class DevBoxSettingsPanel(ConfigBoundSettingsPanel):
16+
title = addonHandler.getCodeAddon().manifest["summary"]
17+
18+
def makeSettings(self, settings_sizer):
19+
self.config = config.conf["devBox"]
20+
sizer = gui.guiHelper.BoxSizerHelper(self, sizer=settings_sizer)
21+
# bind_with_config will try to check checkboxes, so let's add elements first
22+
features_list = sizer.addLabeledControl(
23+
_("Features"),
24+
CustomCheckListBox,
25+
)
26+
features_list.Append(_("Space folding"), "spaceFolding")
27+
features_list.Append(_("Diff sounds"), "diffSounds")
28+
self.feature_list = bind_with_config(
29+
features_list, "features", CheckListBoxToDictConverter
30+
)
31+
self.feature_list.SetSelection(0)
32+
33+
34+
def add_settings(on_save_callback):
35+
DevBoxSettingsPanel.on_save_callback = on_save_callback
36+
gui.settingsDialogs.NVDASettingsDialog.categoryClasses.append(DevBoxSettingsPanel)
37+
38+
39+
def remove_settings():
40+
gui.settingsDialogs.NVDASettingsDialog.categoryClasses.remove(DevBoxSettingsPanel)

0 commit comments

Comments
 (0)