diff --git a/news.d/feature/1308.ui.md b/news.d/feature/1308.ui.md
new file mode 100644
index 000000000..ccf152084
--- /dev/null
+++ b/news.d/feature/1308.ui.md
@@ -0,0 +1,6 @@
+Improve accessibility:
+- Disable tag-key navigation in tables, so focusing a table does not lock global tab-key navigation to it.
+- Remove some container widget, tweak focus rules to avoid extra unnecessary steps when using tab-key navigation (like selecting the dictionaries widget outer frame).
+- In form layouts, link each widget to its label (like each option in the configuration dialog).
+- Set the accessible name / description of focusable widgets.
+- Use lists for the dictionaries widget, suggestions widget, and the paper tape.
diff --git a/news.d/feature/1332.ui.md b/news.d/feature/1332.ui.md
new file mode 100644
index 000000000..ccf152084
--- /dev/null
+++ b/news.d/feature/1332.ui.md
@@ -0,0 +1,6 @@
+Improve accessibility:
+- Disable tag-key navigation in tables, so focusing a table does not lock global tab-key navigation to it.
+- Remove some container widget, tweak focus rules to avoid extra unnecessary steps when using tab-key navigation (like selecting the dictionaries widget outer frame).
+- In form layouts, link each widget to its label (like each option in the configuration dialog).
+- Set the accessible name / description of focusable widgets.
+- Use lists for the dictionaries widget, suggestions widget, and the paper tape.
diff --git a/plover/gui_qt/add_translation_widget.ui b/plover/gui_qt/add_translation_widget.ui
index 9c8937e75..98cab39aa 100644
--- a/plover/gui_qt/add_translation_widget.ui
+++ b/plover/gui_qt/add_translation_widget.ui
@@ -31,6 +31,16 @@
-
+
-
+
+
+ Dictionary:
+
+
+ dictionary
+
+
+
-
@@ -42,6 +52,9 @@
Strokes:
+
+ strokes
+
-
@@ -52,6 +65,9 @@
0
+
+ Strokes
+
-
@@ -65,6 +81,9 @@
Translation:
+
+ translation
+
-
@@ -75,17 +94,19 @@
0
-
-
- -
-
-
- Dictionary:
+
+ Translation
-
+
+ Dictionary
+
+
+ Select the target dictionary for the new translation.
+
false
@@ -94,51 +115,58 @@
-
-
+
0
0
+
+ Existing mappings (strokes)
+
QFrame::Box
-
-
-
-
- true
-
Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse
+
+
+
-
-
+
0
0
+
+ Existing mappings (translations)
+
QFrame::Box
-
-
-
-
- true
-
Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse
+
+
+
+
+ strokes
+ translation
+ strokes_info
+ translation_info
+ dictionary
+
diff --git a/plover/gui_qt/config_file_widget.ui b/plover/gui_qt/config_file_widget.ui
index 75d5f6029..602f64c7d 100644
--- a/plover/gui_qt/config_file_widget.ui
+++ b/plover/gui_qt/config_file_widget.ui
@@ -21,7 +21,14 @@
-
-
+
+
+ Log file path.
+
+
+ Path to the log file.
+
+
-
@@ -31,6 +38,12 @@
0
+
+ Browse.
+
+
+ Open a file picker to select the log file.
+
Browse
diff --git a/plover/gui_qt/config_serial_widget.ui b/plover/gui_qt/config_serial_widget.ui
index aaa1e4e15..3419b3565 100644
--- a/plover/gui_qt/config_serial_widget.ui
+++ b/plover/gui_qt/config_serial_widget.ui
@@ -13,287 +13,311 @@
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
- QFrame::StyledPanel
+
+ Serial
+
+
+
-
+
+
+
+ 0
+ 0
+
-
- QFrame::Sunken
+
+ Connection
-
-
-
-
+
+
-
+
-
+
0
0
-
- Connection
+
+ Port
+
+
+ port
-
-
-
-
-
-
- 0
- 0
-
-
-
- Port
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- true
-
-
- QComboBox::InsertAtBottom
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 16777215
- 16777215
-
-
-
- Scan
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Baudrate
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
-
- -
-
-
- Data format
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Port
+
+
+ Serial port device name.
+
+
+ true
+
+
+ QComboBox::InsertAtBottom
-
-
-
-
-
-
- 0
- 0
-
-
-
- Data bits
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Stop bits
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Parity
-
-
-
- -
-
-
-
- -
-
-
-
-
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 16777215
+ 16777215
+
+
+
+ Scan for available serial ports.
+
+
+ Scan
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Baudrate
+
+
+ baudrate
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Baudrate
+
+
+
+
+
+
+ -
+
+
+ Data format
+
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Data bits
+
+
+ bytesize
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Data bits
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Stop bits
+
+
+ stopbits
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Stop bits
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Parity
+
+
+ parity
+
+
+
+ -
+
+
+ Parity
+
+
+
+
+
+
+ -
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+ Timeout
+
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Duration
+
+
+ timeout
+
+
+
+ -
+
+
+ true
+
0
0
-
- Timeout
+
+ Duration
+
+
+ Timeout duration in seconds.
+
+
+ 0.100000000000000
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Use timeout
+
+
+
+
+
+
+ -
+
+
+ Flow control
+
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Xon/Xoff
-
-
-
-
-
-
-
-
- true
-
-
-
- 0
- 0
-
-
-
- 0.100000000000000
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- seconds
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Use timeout
-
-
-
-
-
-
-
- Flow control
+
+
+
+ 0
+ 0
+
+
+
+ RTS/CTS
-
-
-
-
-
-
- 0
- 0
-
-
-
- Xon/Xoff
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- RTS/CTS
-
-
-
-
-
-
-
+
+
+
diff --git a/plover/gui_qt/config_window.py b/plover/gui_qt/config_window.py
index cc71e056e..f9905ea4d 100644
--- a/plover/gui_qt/config_window.py
+++ b/plover/gui_qt/config_window.py
@@ -16,6 +16,7 @@
QFileDialog,
QFormLayout,
QFrame,
+ QGroupBox,
QLabel,
QScrollArea,
QSpinBox,
@@ -89,7 +90,7 @@ def on_activated(self, index):
self.valueChanged.emit(self.itemData(index))
-class FileOption(QWidget, Ui_FileWidget):
+class FileOption(QGroupBox, Ui_FileWidget):
valueChanged = pyqtSignal(str)
@@ -118,7 +119,36 @@ def on_path_edited(self):
self.valueChanged.emit(expand_path(self.path.text()))
-class KeymapOption(QTableWidget):
+class TableOption(QTableWidget):
+
+ def __init__(self):
+ super().__init__()
+ self.horizontalHeader().setStretchLastSection(True)
+ self.setSelectionMode(self.SingleSelection)
+ self.setTabKeyNavigation(False)
+ self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
+ self.verticalHeader().hide()
+ self.currentItemChanged.connect(self._on_current_item_changed)
+
+ def _on_current_item_changed(self, current, previous):
+ # Ensure current item is visible.
+ parent = self.parent()
+ while parent is not None:
+ if isinstance(parent, QScrollArea):
+ row = current.row()
+ pos = self.pos()
+ x = pos.x()
+ y = (
+ + pos.y()
+ + self.rowViewportPosition(row)
+ + self.rowHeight(row)
+ )
+ parent.ensureVisible(x, y)
+ return
+ parent = parent.parent()
+
+
+class KeymapOption(TableOption):
valueChanged = pyqtSignal(QVariant)
@@ -147,9 +177,6 @@ def __init__(self):
# i18n: Widget: “KeymapOption”.
_('Action'),
))
- self.horizontalHeader().setStretchLastSection(True)
- self.verticalHeader().hide()
- self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.cellChanged.connect(self._on_cell_changed)
def setValue(self, value):
@@ -188,7 +215,7 @@ def _on_cell_changed(self, row, column):
self.valueChanged.emit(self._value)
-class MultipleChoicesOption(QTableWidget):
+class MultipleChoicesOption(TableOption):
valueChanged = pyqtSignal(QVariant)
@@ -213,9 +240,6 @@ def __init__(self, choices=None, labels=None):
}
self.setColumnCount(2)
self.setHorizontalHeaderLabels(labels)
- self.horizontalHeader().setStretchLastSection(True)
- self.verticalHeader().hide()
- self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.cellChanged.connect(self._on_cell_changed)
def setValue(self, value):
@@ -276,6 +300,7 @@ def __init__(self, display_name, option_name, widget_class,
self.dependents = dependents
self.layout = None
self.widget = None
+ self.label = None
class ConfigWindow(QDialog, Ui_ConfigWindow, WindowState):
@@ -387,33 +412,35 @@ def __init__(self, engine):
self._supported_options.update(option.option_name for option in option_list)
self._update_config()
# Create and fill tabs.
- options = {}
+ option_by_name = {}
for section, option_list in mappings:
layout = QFormLayout()
for option in option_list:
- widget = self._create_option_widget(option)
- options[option.option_name] = option
- option.tab_index = self.tabs.count()
+ option_by_name[option.option_name] = option
option.layout = layout
- option.widget = widget
- label = QLabel(option.display_name)
- label.setToolTip(option.help_text)
- layout.addRow(label, widget)
+ option.widget = self._create_option_widget(option)
+ option.label = QLabel(option.display_name)
+ option.label.setToolTip(option.help_text)
+ option.label.setBuddy(option.widget)
+ layout.addRow(option.label, option.widget)
frame = QFrame()
frame.setLayout(layout)
+ frame.setAccessibleName(section)
+ frame.setFocusProxy(option_list[0].widget)
scroll_area = QScrollArea()
scroll_area.setWidgetResizable(True)
scroll_area.setWidget(frame)
+ scroll_area.setFocusProxy(frame)
self.tabs.addTab(scroll_area, section)
# Update dependents.
- for option in options.values():
+ for option in option_by_name.values():
option.dependents = [
- (options[option_name], update_fn)
+ (option_by_name[option_name], update_fn)
for option_name, update_fn in option.dependents
]
- buttons = self.findChild(QWidget, 'buttons')
- buttons.button(QDialogButtonBox.Ok).clicked.connect(self.on_apply)
- buttons.button(QDialogButtonBox.Apply).clicked.connect(self.on_apply)
+ self.buttons.button(QDialogButtonBox.Ok).clicked.connect(self.on_apply)
+ self.buttons.button(QDialogButtonBox.Apply).clicked.connect(self.on_apply)
+ self.tabs.currentWidget().setFocus()
self.restore_state()
self.finished.connect(self.save_state)
@@ -457,6 +484,8 @@ def _update_keymap(self, system_name=None, machine_type=None):
def _create_option_widget(self, option):
widget = option.widget_class()
widget.setToolTip(option.help_text)
+ widget.setAccessibleName(option.display_name)
+ widget.setAccessibleDescription(option.help_text)
widget.valueChanged.connect(partial(self.on_option_changed, option))
widget.setValue(self._config[option.option_name])
return widget
@@ -476,6 +505,7 @@ def on_option_changed(self, option, value):
self._config.maps[1][dependent.option_name] = update_fn(value)
widget = self._create_option_widget(dependent)
dependent.layout.replaceWidget(dependent.widget, widget)
+ dependent.label.setBuddy(widget)
dependent.widget.deleteLater()
dependent.widget = widget
diff --git a/plover/gui_qt/dictionaries_widget.py b/plover/gui_qt/dictionaries_widget.py
index 003f71eb0..79f75265e 100644
--- a/plover/gui_qt/dictionaries_widget.py
+++ b/plover/gui_qt/dictionaries_widget.py
@@ -97,7 +97,8 @@ def is_loaded(self):
return self.state not in {'loading', 'error'}
SUPPORTED_ROLES = {
- Qt.CheckStateRole, Qt.DecorationRole, Qt.DisplayRole, Qt.ToolTipRole
+ Qt.AccessibleTextRole, Qt.CheckStateRole,
+ Qt.DecorationRole, Qt.DisplayRole, Qt.ToolTipRole
}
FLAGS = Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsUserCheckable
@@ -366,6 +367,25 @@ def data(self, index, role):
return d.short_path
if role == Qt.CheckStateRole:
return Qt.Checked if d.enabled else Qt.Unchecked
+ if role == Qt.AccessibleTextRole:
+ accessible_text = [d.short_path]
+ if not d.enabled:
+ # i18n: Widget: “DictionariesWidget”, accessible text.
+ accessible_text.append(_('disabled'))
+ if d is self._favorite:
+ # i18n: Widget: “DictionariesWidget”, accessible text.
+ accessible_text.append(_('favorite'))
+ elif d.state == 'error':
+ # i18n: Widget: “DictionariesWidget”, accessible text.
+ accessible_text.append(_('errored: {exception}.').format(
+ exception=str(d.loaded.exception)))
+ elif d.state == 'loading':
+ # i18n: Widget: “DictionariesWidget”, accessible text.
+ accessible_text.append(_('loading'))
+ elif d.state == 'readonly':
+ # i18n: Widget: “DictionariesWidget”, accessible text.
+ accessible_text.append(_('read-only'))
+ return ', '.join(accessible_text)
if role == Qt.DecorationRole:
return self._icons.get('favorite' if d is self._favorite else d.state)
if role == Qt.ToolTipRole:
diff --git a/plover/gui_qt/dictionaries_widget.ui b/plover/gui_qt/dictionaries_widget.ui
index 7e820aba3..bf3606364 100644
--- a/plover/gui_qt/dictionaries_widget.ui
+++ b/plover/gui_qt/dictionaries_widget.ui
@@ -37,9 +37,15 @@
-
+
+ Dictionaries
+
QFrame::Box
+
+ false
+
true
diff --git a/plover/gui_qt/dictionary_editor.ui b/plover/gui_qt/dictionary_editor.ui
index 4402b7806..0f2ae08a7 100644
--- a/plover/gui_qt/dictionary_editor.ui
+++ b/plover/gui_qt/dictionary_editor.ui
@@ -22,6 +22,9 @@
-
+
+ Filter
+
Filter
@@ -37,10 +40,17 @@
By strokes:
+
+ strokes_filter
+
-
-
+
+
+ Strokes filter
+
+
-
@@ -50,6 +60,9 @@
0
+
+ Apply filter
+
Apply
@@ -60,13 +73,26 @@
By translation:
+
+ translation_filter
+
-
-
+
+
+ Translation filter
+
+
-
+
+ Clear filter
+
+
+
+
Clear
@@ -77,9 +103,15 @@
-
+
+ Mappings
+
QFrame::Box
+
+ false
+
true
@@ -163,6 +195,13 @@
+
+ table
+ strokes_filter
+ translation_filter
+ pushButton
+ pushButton_2
+
diff --git a/plover/gui_qt/lookup_dialog.ui b/plover/gui_qt/lookup_dialog.ui
index 6e755070b..61b7713f7 100644
--- a/plover/gui_qt/lookup_dialog.ui
+++ b/plover/gui_qt/lookup_dialog.ui
@@ -41,6 +41,12 @@
0
+
+ Pattern
+
+
+ Translation pattern to lookup.
+
-
@@ -51,6 +57,9 @@
0
+
+ Results
+
diff --git a/plover/gui_qt/machine_options.py b/plover/gui_qt/machine_options.py
index 394b801d5..6a361a6c4 100644
--- a/plover/gui_qt/machine_options.py
+++ b/plover/gui_qt/machine_options.py
@@ -1,7 +1,7 @@
from copy import copy
from PyQt5.QtCore import QVariant, pyqtSignal
-from PyQt5.QtWidgets import QWidget
+from PyQt5.QtWidgets import QGroupBox
from serial import Serial
from serial.tools.list_ports import comports
@@ -12,7 +12,7 @@
from plover.gui_qt.config_serial_widget_ui import Ui_SerialWidget
-class SerialOption(QWidget, Ui_SerialWidget):
+class SerialOption(QGroupBox, Ui_SerialWidget):
valueChanged = pyqtSignal(QVariant)
@@ -96,7 +96,7 @@ def on_rtscts_changed(self, value):
self._update('rtscts', value)
-class KeyboardOption(QWidget, Ui_KeyboardWidget):
+class KeyboardOption(QGroupBox, Ui_KeyboardWidget):
valueChanged = pyqtSignal(QVariant)
diff --git a/plover/gui_qt/main_window.ui b/plover/gui_qt/main_window.ui
index 03c674f9b..b4ead2ec4 100644
--- a/plover/gui_qt/main_window.ui
+++ b/plover/gui_qt/main_window.ui
@@ -54,6 +54,12 @@
-
+
+ State
+
+
+ Connection state for the current machine.
+
@@ -70,6 +76,12 @@
Disconnect and reconnect the machine.
+
+ Reconnect
+
+
+ Disconnect and reconnect the machine.
+
@@ -87,6 +99,12 @@
0
+
+ Type
+
+
+ Change the current machine type.
+
diff --git a/plover/gui_qt/paper_tape.py b/plover/gui_qt/paper_tape.py
index c0dd301ec..a2edf2d5d 100644
--- a/plover/gui_qt/paper_tape.py
+++ b/plover/gui_qt/paper_tape.py
@@ -1,7 +1,10 @@
-
import time
-from PyQt5.QtCore import Qt
+from PyQt5.QtCore import (
+ QAbstractListModel,
+ QModelIndex,
+ Qt,
+)
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import (
QFileDialog,
@@ -18,32 +21,107 @@
from plover.gui_qt.tool import Tool
+STYLE_PAPER, STYLE_RAW = (
+ # i18n: Paper tape style.
+ _('Paper'),
+ # i18n: Paper tape style.
+ _('Raw'),
+)
+TAPE_STYLES = (STYLE_PAPER, STYLE_RAW)
+
+
+class TapeModel(QAbstractListModel):
+
+ def __init__(self):
+ super().__init__()
+ self._stroke_list = []
+ self._style = None
+ self._all_keys = None
+ self._numbers = None
+
+ def rowCount(self, parent):
+ return 0 if parent.isValid() else len(self._stroke_list)
+
+ @property
+ def style(self):
+ return self._style
+
+ @style.setter
+ def style(self, style):
+ assert style in TAPE_STYLES
+ self.layoutAboutToBeChanged.emit()
+ self._style = style
+ self.layoutChanged.emit()
+
+ def _paper_format(self, stroke):
+ text = self._all_keys_filler * 1
+ keys = stroke.steno_keys[:]
+ if any(key in self._numbers for key in keys):
+ keys.append('#')
+ for key in keys:
+ index = system.KEY_ORDER[key]
+ text[index] = self._all_keys[index]
+ return ''.join(text)
+
+ @staticmethod
+ def _raw_format(stroke):
+ return stroke.rtfcre
+
+ def headerData(self, section, orientation, role):
+ if (section != 0 or orientation != Qt.Horizontal or
+ role != Qt.DisplayRole or self._style != STYLE_PAPER):
+ return None
+ return self._all_keys
+
+ def data(self, index, role):
+ if not index.isValid():
+ return None
+ stroke = self._stroke_list[index.row()]
+ if role == Qt.DisplayRole:
+ if self._style == STYLE_PAPER:
+ return self._paper_format(stroke)
+ if self._style == STYLE_RAW:
+ return self._raw_format(stroke)
+ if role == Qt.AccessibleTextRole:
+ return stroke.rtfcre
+ return None
+
+ def reset(self):
+ self.modelAboutToBeReset.emit()
+ self._all_keys = ''.join(key.strip('-') for key in system.KEYS)
+ self._all_keys_filler = [
+ ' ' * wcwidth(k)
+ for k in self._all_keys
+ ]
+ self._numbers = set(system.NUMBERS.values())
+ self._stroke_list.clear()
+ self.modelReset.emit()
+ return self._all_keys
+
+ def append(self, stroke):
+ row = len(self._stroke_list)
+ self.beginInsertRows(QModelIndex(), row, row)
+ self._stroke_list.append(stroke)
+ self.endInsertRows()
+
+
class PaperTape(Tool, Ui_PaperTape):
- ''' Paper tape display of strokes. '''
+ # i18n: Widget: “PaperTape”, tooltip.
+ __doc__ = _('Paper tape display of strokes.')
TITLE = _('Paper Tape')
ICON = ':/tape.svg'
ROLE = 'paper_tape'
SHORTCUT = 'Ctrl+T'
- STYLE_PAPER, STYLE_RAW = (
- # i18n: Paper tape style.
- _('Paper'),
- # i18n: Paper tape style.
- _('Raw'),
- )
- STYLES = (STYLE_PAPER, STYLE_RAW)
-
def __init__(self, engine):
super().__init__(engine)
self.setupUi(self)
- self._strokes = []
- self._all_keys = None
- self._all_keys_filler = None
- self._formatter = None
- self._history_size = 2000000
- self.styles.addItems(self.STYLES)
+ self._model = TapeModel()
+ self.header.setContentsMargins(4, 0, 0, 0)
+ self.styles.addItems(TAPE_STYLES)
+ self.tape.setModel(self._model)
# Toolbar.
self.layout().addWidget(ToolBar(
self.action_ToggleOnTop,
@@ -63,8 +141,9 @@ def __init__(self, engine):
def _restore_state(self, settings):
style = settings.value('style', None, int)
if style is not None:
- self.styles.setCurrentText(self.STYLES[style])
- self.on_style_changed(self.STYLES[style])
+ style = TAPE_STYLES[style]
+ self.styles.setCurrentText(style)
+ self.on_style_changed(style)
font_string = settings.value('font')
if font_string is not None:
font = QFont()
@@ -77,67 +156,42 @@ def _restore_state(self, settings):
self.on_toggle_ontop(ontop)
def _save_state(self, settings):
- settings.setValue('style', self.STYLES.index(self._style))
- settings.setValue('font', self.header.font().toString())
+ settings.setValue('style', TAPE_STYLES.index(self._style))
+ settings.setValue('font', self.tape.font().toString())
ontop = bool(self.windowFlags() & Qt.WindowStaysOnTopHint)
settings.setValue('ontop', ontop)
def on_config_changed(self, config):
if 'system_name' in config:
- self._strokes = []
- self._all_keys = ''.join(key.strip('-') for key in system.KEYS)
- self._all_keys_filler = [
- ' ' * wcwidth(k)
- for k in self._all_keys
- ]
- self._numbers = set(system.NUMBERS.values())
- self.header.setText(self._all_keys)
- self.on_style_changed(self._style)
+ self._model.reset()
+
+ @property
+ def _scroll_at_end(self):
+ scrollbar = self.tape.verticalScrollBar()
+ return scrollbar.value() == scrollbar.maximum()
@property
def _style(self):
return self.styles.currentText()
- def _paper_format(self, stroke):
- text = self._all_keys_filler * 1
- keys = stroke.steno_keys[:]
- if any(key in self._numbers for key in keys):
- keys.append('#')
- for key in keys:
- index = system.KEY_ORDER[key]
- text[index] = self._all_keys[index]
- return ''.join(text)
-
- def _raw_format(self, stroke):
- return stroke.rtfcre
-
- def _show_stroke(self, stroke):
- text = self._formatter(stroke)
- self.tape.appendPlainText(text)
-
def on_stroke(self, stroke):
- assert len(self._strokes) <= self._history_size
- if len(self._strokes) == self._history_size:
- self._strokes.pop(0)
- self._strokes.append(stroke)
- self._show_stroke(stroke)
+ scroll_at_end = self._scroll_at_end
+ self._model.append(stroke)
+ if scroll_at_end:
+ self.tape.scrollToBottom()
self.action_Clear.setEnabled(True)
self.action_Save.setEnabled(True)
def on_style_changed(self, style):
- assert style in self.STYLES
- if style == self.STYLE_PAPER:
- self.header.show()
- self._formatter = self._paper_format
- elif style == self.STYLE_RAW:
- self.header.hide()
- self._formatter = self._raw_format
- self.tape.clear()
- for stroke in self._strokes:
- self._show_stroke(stroke)
+ assert style in TAPE_STYLES
+ scroll_at_end = self._scroll_at_end
+ self._model.style = style
+ self.header.setVisible(style == STYLE_PAPER)
+ if scroll_at_end:
+ self.tape.scrollToBottom()
def on_select_font(self):
- font, ok = QFontDialog.getFont(self.header.font(), self, '',
+ font, ok = QFontDialog.getFont(self.tape.font(), self, '',
QFontDialog.MonospacedFonts)
if ok:
self.header.setFont(font)
@@ -164,7 +218,7 @@ def on_clear(self):
self._strokes = []
self.action_Clear.setEnabled(False)
self.action_Save.setEnabled(False)
- self.tape.clear()
+ self._model.reset()
def on_save(self):
filename_suggestion = 'steno-notes-%s.txt' % time.strftime('%Y-%m-%d-%H-%M')
@@ -176,4 +230,6 @@ def on_save(self):
if not filename:
return
with open(filename, 'w') as fp:
- fp.write(self.tape.toPlainText())
+ for row in range(self.tape.count()):
+ item = self.tape.item(row)
+ print(item.data(Qt.DisplayRole), file=fp)
diff --git a/plover/gui_qt/paper_tape.ui b/plover/gui_qt/paper_tape.ui
index 785d8d883..43b9f9fc5 100644
--- a/plover/gui_qt/paper_tape.ui
+++ b/plover/gui_qt/paper_tape.ui
@@ -27,7 +27,10 @@
-
- Mode
+ Mode:
+
+
+ styles
@@ -39,6 +42,12 @@
0
+
+ Mode
+
+
+ Select paper tape display mode.
+
@@ -57,17 +66,23 @@
-
-
+
+
+ Tape
+
QFrame::Panel
-
+
false
-
- QPlainTextEdit::NoWrap
+
+ QAbstractItemView::SingleSelection
+
+
+ QAbstractItemView::SelectRows
-
+
true
diff --git a/plover/gui_qt/suggestions_dialog.ui b/plover/gui_qt/suggestions_dialog.ui
index 128a3a7d8..fb144083f 100644
--- a/plover/gui_qt/suggestions_dialog.ui
+++ b/plover/gui_qt/suggestions_dialog.ui
@@ -23,7 +23,11 @@
-
-
+
+
+ Suggestions
+
+
diff --git a/plover/gui_qt/suggestions_widget.py b/plover/gui_qt/suggestions_widget.py
index 957f733cb..91b06502b 100644
--- a/plover/gui_qt/suggestions_widget.py
+++ b/plover/gui_qt/suggestions_widget.py
@@ -1,91 +1,162 @@
+from PyQt5.QtCore import (
+ QAbstractListModel,
+ QModelIndex,
+ Qt,
+)
from PyQt5.QtGui import (
QFont,
- QTextCursor,
QTextCharFormat,
+ QTextCursor,
+ QTextDocument,
+)
+from PyQt5.QtWidgets import (
+ QListView,
+ QStyle,
+ QStyledItemDelegate,
)
-from PyQt5.QtWidgets import QWidget
from plover import _
from plover.translation import escape_translation
-from plover.gui_qt.suggestions_widget_ui import Ui_SuggestionsWidget
-
-class SuggestionsWidget(QWidget, Ui_SuggestionsWidget):
+# i18n: Widget: “SuggestionsWidget”.
+NO_SUGGESTIONS_STRING = _('no suggestions')
+MAX_SUGGESTIONS_COUNT = 10
- STYLE_TRANSLATION, STYLE_STROKES = range(2)
- # Anatomy of the text document:
- # - "root":
- # - 0+ "suggestions" blocks
- # - 1+ "translation" blocks
- # - 1-10 "strokes" blocks
+class SuggestionsDelegate(QStyledItemDelegate):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.setupUi(self)
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+ self._doc = QTextDocument()
self._translation_char_format = QTextCharFormat()
self._strokes_char_format = QTextCharFormat()
self._strokes_char_format.font().setStyleHint(QFont.Monospace)
+ @property
+ def text_font(self):
+ return self._translation_char_format.font()
+
+ @text_font.setter
+ def text_font(self, font):
+ self._translation_char_format.setFont(font)
+
+ @property
+ def strokes_font(self):
+ return self._strokes_char_format.font()
+
+ @strokes_font.setter
+ def strokes_font(self, font):
+ self._strokes_char_format.setFont(font)
+
+ def _format_suggestion(self, index):
+ suggestion = index.data(Qt.DisplayRole)
+ self._doc.clear()
+ cursor = QTextCursor(self._doc)
+ cursor.setCharFormat(self._translation_char_format)
+ cursor.insertText(escape_translation(suggestion.text) + ':')
+ if not suggestion.steno_list:
+ cursor.insertText(' ' + NO_SUGGESTIONS_STRING)
+ return
+ for strokes_list in suggestion.steno_list[:MAX_SUGGESTIONS_COUNT]:
+ cursor.insertBlock()
+ cursor.setCharFormat(self._strokes_char_format)
+ cursor.insertText(' ' + '/'.join(strokes_list))
+
+ def paint(self, painter, option, index):
+ painter.save()
+ if option.state & QStyle.State_Selected:
+ painter.fillRect(option.rect, option.palette.highlight())
+ text_color = option.palette.highlightedText()
+ else:
+ text_color = option.palette.text()
+ self._translation_char_format.setForeground(text_color)
+ self._strokes_char_format.setForeground(text_color)
+ painter.translate(option.rect.topLeft())
+ self._format_suggestion(index)
+ self._doc.drawContents(painter)
+ painter.restore()
+
+ def sizeHint(self, option, index):
+ self._format_suggestion(index)
+ return self._doc.size().toSize()
+
+
+class SuggestionsModel(QAbstractListModel):
+
+ def __init__(self):
+ super().__init__()
+ self._suggestion_list = []
+
+ def rowCount(self, parent):
+ return 0 if parent.isValid() else len(self._suggestion_list)
+
+ def data(self, index, role):
+ if not index.isValid():
+ return None
+ suggestion = self._suggestion_list[index.row()]
+ if role == Qt.DisplayRole:
+ return suggestion
+ if role == Qt.AccessibleTextRole:
+ translation = escape_translation(suggestion.text)
+ if suggestion.steno_list:
+ steno = ', '.join('/'.join(strokes_list) for strokes_list in
+ suggestion.steno_list[:MAX_SUGGESTIONS_COUNT])
+ else:
+ steno = NO_SUGGESTIONS_STRING
+ return translation + ': ' + steno
+ return None
+
+ def clear(self):
+ self.modelAboutToBeReset.emit()
+ self._suggestion_list.clear()
+ self.modelReset.emit()
+
+ def extend(self, suggestion_list):
+ row = len(self._suggestion_list)
+ self.beginInsertRows(QModelIndex(), row, row + len(suggestion_list))
+ self._suggestion_list.extend(suggestion_list)
+ self.endInsertRows()
+
+
+class SuggestionsWidget(QListView):
+
+ def __init__(self, parent=None):
+ super().__init__(parent=parent)
+ self.setResizeMode(self.Adjust)
+ self._model = SuggestionsModel()
+ self._delegate = SuggestionsDelegate(self)
+ self.setModel(self._model)
+ self.setItemDelegate(self._delegate)
+
def append(self, suggestion_list):
- scrollbar = self.suggestions.verticalScrollBar()
+ scrollbar = self.verticalScrollBar()
scroll_at_end = scrollbar.value() == scrollbar.maximum()
- cursor = self.suggestions.textCursor()
- cursor.movePosition(QTextCursor.End)
- for suggestion in suggestion_list:
- cursor.insertBlock()
- cursor.setCharFormat(self._translation_char_format)
- cursor.block().setUserState(self.STYLE_TRANSLATION)
- cursor.insertText(escape_translation(suggestion.text) + ':')
- if not suggestion.steno_list:
- # i18n: Widget: “SuggestionsWidget”.
- cursor.insertText(' ' + _('no suggestions'))
- continue
- for strokes_list in suggestion.steno_list[:10]:
- cursor.insertBlock()
- cursor.setCharFormat(self._strokes_char_format)
- cursor.block().setUserState(self.STYLE_STROKES)
- cursor.insertText(' ' + '/'.join(strokes_list))
- cursor.insertText('\n')
- # Keep current position when not at the end of the document.
+ self._model.extend(suggestion_list)
if scroll_at_end:
- scrollbar.setValue(scrollbar.maximum())
+ self.scrollToBottom()
def clear(self):
- self.suggestions.clear()
+ self._model.clear()
def _reformat(self):
- document = self.suggestions.document()
- cursor = self.suggestions.textCursor()
- block = document.begin()
- style_format = {
- self.STYLE_TRANSLATION: self._translation_char_format,
- self.STYLE_STROKES: self._strokes_char_format,
- }
- while block != document.end():
- style = block.userState()
- fmt = style_format.get(style)
- if fmt is not None:
- cursor.setPosition(block.position())
- cursor.select(QTextCursor.BlockUnderCursor)
- cursor.setCharFormat(fmt)
- block = block.next()
+ self._model.layoutAboutToBeChanged.emit()
+ self._model.layoutChanged.emit()
@property
def text_font(self):
- return self._translation_char_format.font()
+ return self._delegate.text_font
@text_font.setter
def text_font(self, font):
- self._translation_char_format.setFont(font)
+ self._delegate.text_font = font
self._reformat()
@property
def strokes_font(self):
- return self._strokes_char_format.font()
+ return self._delegate.strokes_font
@strokes_font.setter
def strokes_font(self, font):
- self._strokes_char_format.setFont(font)
+ self._delegate.strokes_font = font
self._reformat()
diff --git a/plover/gui_qt/suggestions_widget.ui b/plover/gui_qt/suggestions_widget.ui
deleted file mode 100644
index 2d74000c8..000000000
--- a/plover/gui_qt/suggestions_widget.ui
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
- SuggestionsWidget
-
-
-
- 0
- 0
- 400
- 300
-
-
-
-
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
- -
-
-
- QFrame::Box
-
-
- false
-
-
- true
-
-
-
-
-
-
-
-
diff --git a/plover/messages/plover.pot b/plover/messages/plover.pot
index dcbb6e94a..56a3727c1 100644
--- a/plover/messages/plover.pot
+++ b/plover/messages/plover.pot
@@ -6,9 +6,9 @@
#, fuzzy
msgid ""
msgstr ""
-"Project-Id-Version: plover 4.0.0.dev8\n"
+"Project-Id-Version: plover 4.0.0.dev9\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2021-04-16 05:40+0200\n"
+"POT-Creation-Date: 2021-05-23 20:19+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -44,69 +44,125 @@ msgid ""
"hackers, hobbyists, accessibility mavens, and all-around speed demons."
msgstr ""
-#. Widget: “AboutDialog”, windowtitle.
+#. Widget: “AboutDialog”, window title.
#: plover/gui_qt/about_dialog_ui.py:38
msgid "Plover: About"
msgstr ""
#. Widget: “AddTranslationDialog”, tooltip.
#. Widget: “AddTranslationWidget”, tooltip.
-#: plover/gui_qt/add_translation_dialog.py:8
-#: plover/gui_qt/add_translation_widget.py:22
+#: plover/gui_qt/add_translation_dialog.py:10
+#: plover/gui_qt/add_translation_widget.py:23
msgid "Add a new translation to the dictionary."
msgstr ""
-#: plover/gui_qt/add_translation_dialog.py:10
+#: plover/gui_qt/add_translation_dialog.py:12
msgid "Add Translation"
msgstr ""
#. Widget: “AddTranslationWidget”.
-#: plover/gui_qt/add_translation_widget.py:199
+#: plover/gui_qt/add_translation_widget.py:200
msgid "{dictionary} (disabled)"
msgstr ""
#. Widget: “AddTranslationWidget”.
-#: plover/gui_qt/add_translation_widget.py:258
+#: plover/gui_qt/add_translation_widget.py:259
msgid "{strokes} maps to "
msgstr ""
#. Widget: “AddTranslationWidget”.
-#: plover/gui_qt/add_translation_widget.py:269
+#: plover/gui_qt/add_translation_widget.py:270
msgid "Overwritten entries:"
msgstr ""
#. Widget: “AddTranslationWidget”.
-#: plover/gui_qt/add_translation_widget.py:274
+#: plover/gui_qt/add_translation_widget.py:275
msgid "{strokes} is not mapped in any dictionary"
msgstr ""
#. Widget: “AddTranslationWidget”.
-#: plover/gui_qt/add_translation_widget.py:290
+#: plover/gui_qt/add_translation_widget.py:291
msgid "{translation} is mapped to: {strokes}"
msgstr ""
#. Widget: “AddTranslationWidget”.
-#: plover/gui_qt/add_translation_widget.py:293
+#: plover/gui_qt/add_translation_widget.py:294
msgid "{translation} is not in the dictionary"
msgstr ""
#. Widget: “AddTranslationWidget”, text.
-#: plover/gui_qt/add_translation_widget_ui.py:101
+#: plover/gui_qt/add_translation_widget_ui.py:106
+msgid "Dictionary:"
+msgstr ""
+
+#. Widget: “AddTranslationWidget”, text.
+#: plover/gui_qt/add_translation_widget_ui.py:108
msgid "Strokes:"
msgstr ""
+#. Widget: “AddTranslationWidget”, accessible name.
+#. Widget: “DictionaryEditor”.
+#: plover/gui_qt/add_translation_widget_ui.py:110
+#: plover/gui_qt/dictionary_editor.py:164
+msgid "Strokes"
+msgstr ""
+
#. Widget: “AddTranslationWidget”, text.
-#: plover/gui_qt/add_translation_widget_ui.py:103
+#: plover/gui_qt/add_translation_widget_ui.py:112
msgid "Translation:"
msgstr ""
-#. Widget: “AddTranslationWidget”, text.
-#: plover/gui_qt/add_translation_widget_ui.py:105
-msgid "Dictionary:"
+#. Widget: “AddTranslationWidget”, accessible name.
+#. Widget: “DictionaryEditor”.
+#: plover/gui_qt/add_translation_widget_ui.py:114
+#: plover/gui_qt/dictionary_editor.py:167
+msgid "Translation"
msgstr ""
-#. Widget: “FileWidget”, text.
+#. Widget: “AddTranslationWidget”, accessible name.
+#. Widget: “DictionaryEditor”.
+#: plover/gui_qt/add_translation_widget_ui.py:116
+#: plover/gui_qt/dictionary_editor.py:170
+msgid "Dictionary"
+msgstr ""
+
+#. Widget: “AddTranslationWidget”, accessible description.
+#: plover/gui_qt/add_translation_widget_ui.py:118
+msgid "Select the target dictionary for the new translation."
+msgstr ""
+
+#. Widget: “AddTranslationWidget”, accessible name.
+#: plover/gui_qt/add_translation_widget_ui.py:120
+msgid "Existing mappings (strokes)"
+msgstr ""
+
+#. Widget: “AddTranslationWidget”, accessible name.
+#: plover/gui_qt/add_translation_widget_ui.py:122
+msgid "Existing mappings (translations)"
+msgstr ""
+
+#. Widget: “FileWidget”, accessible name.
#: plover/gui_qt/config_file_widget_ui.py:45
+msgid "Log file path."
+msgstr ""
+
+#. Widget: “FileWidget”, accessible description.
+#: plover/gui_qt/config_file_widget_ui.py:47
+msgid "Path to the log file."
+msgstr ""
+
+#. Widget: “FileWidget”, accessible name.
+#: plover/gui_qt/config_file_widget_ui.py:49
+msgid "Browse."
+msgstr ""
+
+#. Widget: “FileWidget”, accessible description.
+#: plover/gui_qt/config_file_widget_ui.py:51
+msgid "Open a file picker to select the log file."
+msgstr ""
+
+#. Widget: “FileWidget”, text.
+#: plover/gui_qt/config_file_widget_ui.py:53
msgid "Browse"
msgstr ""
@@ -115,155 +171,187 @@ msgstr ""
msgid "Arpeggiate"
msgstr ""
+#. Widget: “SerialWidget”, accessible name.
+#: plover/gui_qt/config_serial_widget_ui.py:205
+msgid "Serial"
+msgstr ""
+
#. Widget: “SerialWidget”, title.
-#: plover/gui_qt/config_serial_widget_ui.py:209
+#: plover/gui_qt/config_serial_widget_ui.py:207
msgid "Connection"
msgstr ""
#. Widget: “SerialWidget”, text.
+#. Widget: “SerialWidget”, accessible name.
+#: plover/gui_qt/config_serial_widget_ui.py:209
#: plover/gui_qt/config_serial_widget_ui.py:211
msgid "Port"
msgstr ""
-#. Widget: “SerialWidget”, text.
+#. Widget: “SerialWidget”, accessible description.
#: plover/gui_qt/config_serial_widget_ui.py:213
+msgid "Serial port device name."
+msgstr ""
+
+#. Widget: “SerialWidget”, accessible description.
+#: plover/gui_qt/config_serial_widget_ui.py:215
+msgid "Scan for available serial ports."
+msgstr ""
+
+#. Widget: “SerialWidget”, text.
+#: plover/gui_qt/config_serial_widget_ui.py:217
msgid "Scan"
msgstr ""
#. Widget: “SerialWidget”, text.
-#: plover/gui_qt/config_serial_widget_ui.py:215
+#. Widget: “SerialWidget”, accessible name.
+#: plover/gui_qt/config_serial_widget_ui.py:219
+#: plover/gui_qt/config_serial_widget_ui.py:221
msgid "Baudrate"
msgstr ""
#. Widget: “SerialWidget”, title.
-#: plover/gui_qt/config_serial_widget_ui.py:217
+#: plover/gui_qt/config_serial_widget_ui.py:223
msgid "Data format"
msgstr ""
#. Widget: “SerialWidget”, text.
-#: plover/gui_qt/config_serial_widget_ui.py:219
+#. Widget: “SerialWidget”, accessible name.
+#: plover/gui_qt/config_serial_widget_ui.py:225
+#: plover/gui_qt/config_serial_widget_ui.py:227
msgid "Data bits"
msgstr ""
#. Widget: “SerialWidget”, text.
-#: plover/gui_qt/config_serial_widget_ui.py:221
+#. Widget: “SerialWidget”, accessible name.
+#: plover/gui_qt/config_serial_widget_ui.py:229
+#: plover/gui_qt/config_serial_widget_ui.py:231
msgid "Stop bits"
msgstr ""
#. Widget: “SerialWidget”, text.
-#: plover/gui_qt/config_serial_widget_ui.py:223
+#. Widget: “SerialWidget”, accessible name.
+#: plover/gui_qt/config_serial_widget_ui.py:233
+#: plover/gui_qt/config_serial_widget_ui.py:235
msgid "Parity"
msgstr ""
#. Widget: “SerialWidget”, title.
-#: plover/gui_qt/config_serial_widget_ui.py:225
+#: plover/gui_qt/config_serial_widget_ui.py:237
msgid "Timeout"
msgstr ""
#. Widget: “SerialWidget”, text.
-#: plover/gui_qt/config_serial_widget_ui.py:227
-msgid "seconds"
+#. Widget: “SerialWidget”, accessible name.
+#: plover/gui_qt/config_serial_widget_ui.py:239
+#: plover/gui_qt/config_serial_widget_ui.py:241
+msgid "Duration"
+msgstr ""
+
+#. Widget: “SerialWidget”, accessible description.
+#: plover/gui_qt/config_serial_widget_ui.py:243
+msgid "Timeout duration in seconds."
msgstr ""
#. Widget: “SerialWidget”, text.
-#: plover/gui_qt/config_serial_widget_ui.py:229
+#: plover/gui_qt/config_serial_widget_ui.py:245
msgid "Use timeout"
msgstr ""
#. Widget: “SerialWidget”, title.
-#: plover/gui_qt/config_serial_widget_ui.py:231
+#: plover/gui_qt/config_serial_widget_ui.py:247
msgid "Flow control"
msgstr ""
#. Widget: “SerialWidget”, text.
-#: plover/gui_qt/config_serial_widget_ui.py:233
+#: plover/gui_qt/config_serial_widget_ui.py:249
msgid "Xon/Xoff"
msgstr ""
#. Widget: “SerialWidget”, text.
-#: plover/gui_qt/config_serial_widget_ui.py:235
+#: plover/gui_qt/config_serial_widget_ui.py:251
msgid "RTS/CTS"
msgstr ""
#. Widget: “NopeOption” (empty config option message,
#. e.g. the machine option when selecting the Treal machine).
-#: plover/gui_qt/config_window.py:45
+#: plover/gui_qt/config_window.py:47
msgid "Nothing to see here!"
msgstr ""
#. Widget: “KeymapOption”.
-#: plover/gui_qt/config_window.py:145
+#: plover/gui_qt/config_window.py:176
msgid "Key"
msgstr ""
#. Widget: “KeymapOption”.
-#: plover/gui_qt/config_window.py:147
+#: plover/gui_qt/config_window.py:178
msgid "Action"
msgstr ""
#. Widget: “MultipleChoicesOption”.
-#: plover/gui_qt/config_window.py:196
+#: plover/gui_qt/config_window.py:224
msgid "Choice"
msgstr ""
#. Widget: “MultipleChoicesOption”.
-#: plover/gui_qt/config_window.py:198
+#: plover/gui_qt/config_window.py:226
msgid "Selected"
msgstr ""
#. Widget: “ConfigWindow”.
-#: plover/gui_qt/config_window.py:294
+#: plover/gui_qt/config_window.py:320
msgid "Interface"
msgstr ""
-#: plover/gui_qt/config_window.py:295
+#: plover/gui_qt/config_window.py:321
msgid "Start minimized:"
msgstr ""
-#: plover/gui_qt/config_window.py:296
+#: plover/gui_qt/config_window.py:322
msgid "Minimize the main window to systray on startup."
msgstr ""
-#: plover/gui_qt/config_window.py:297
+#: plover/gui_qt/config_window.py:323
msgid "Show paper tape:"
msgstr ""
-#: plover/gui_qt/config_window.py:298
+#: plover/gui_qt/config_window.py:324
msgid "Open the paper tape on startup."
msgstr ""
-#: plover/gui_qt/config_window.py:299
+#: plover/gui_qt/config_window.py:325
msgid "Show suggestions:"
msgstr ""
-#: plover/gui_qt/config_window.py:300
+#: plover/gui_qt/config_window.py:326
msgid "Open the suggestions dialog on startup."
msgstr ""
-#: plover/gui_qt/config_window.py:301
+#: plover/gui_qt/config_window.py:327
msgid "Add translation dialog opacity:"
msgstr ""
-#: plover/gui_qt/config_window.py:303
+#: plover/gui_qt/config_window.py:329
msgid ""
"Set the translation dialog opacity:\n"
"- 0 makes the dialog invisible\n"
"- 100 is fully opaque"
msgstr ""
-#: plover/gui_qt/config_window.py:306
+#: plover/gui_qt/config_window.py:332
msgid "Dictionaries display order:"
msgstr ""
-#: plover/gui_qt/config_window.py:308
+#: plover/gui_qt/config_window.py:334
msgid "top-down"
msgstr ""
-#: plover/gui_qt/config_window.py:308
+#: plover/gui_qt/config_window.py:334
msgid "bottom-up"
msgstr ""
-#: plover/gui_qt/config_window.py:309
+#: plover/gui_qt/config_window.py:335
msgid ""
"Set the display order for dictionaries:\n"
"- top-down: match the search order; highest priority first\n"
@@ -271,114 +359,114 @@ msgid ""
msgstr ""
#. Widget: “ConfigWindow”.
-#: plover/gui_qt/config_window.py:314
+#: plover/gui_qt/config_window.py:340
msgid "Logging"
msgstr ""
-#: plover/gui_qt/config_window.py:315
+#: plover/gui_qt/config_window.py:341
msgid "Log file:"
msgstr ""
-#: plover/gui_qt/config_window.py:317
+#: plover/gui_qt/config_window.py:343
msgid "Select a log file"
msgstr ""
-#: plover/gui_qt/config_window.py:318
+#: plover/gui_qt/config_window.py:344
msgid "Log files (*.log)"
msgstr ""
-#: plover/gui_qt/config_window.py:319
+#: plover/gui_qt/config_window.py:345
msgid "File to use for logging strokes/translations."
msgstr ""
-#: plover/gui_qt/config_window.py:320
+#: plover/gui_qt/config_window.py:346
msgid "Log strokes:"
msgstr ""
-#: plover/gui_qt/config_window.py:321
+#: plover/gui_qt/config_window.py:347
msgid "Save strokes to the logfile."
msgstr ""
-#: plover/gui_qt/config_window.py:322
+#: plover/gui_qt/config_window.py:348
msgid "Log translations:"
msgstr ""
-#: plover/gui_qt/config_window.py:323
+#: plover/gui_qt/config_window.py:349
msgid "Save translations to the logfile."
msgstr ""
#. Widget: “ConfigWindow”.
#. Widget: “MainWindow”, title.
-#: plover/gui_qt/config_window.py:326 plover/gui_qt/main_window_ui.py:170
+#: plover/gui_qt/config_window.py:352 plover/gui_qt/main_window_ui.py:164
msgid "Machine"
msgstr ""
-#: plover/gui_qt/config_window.py:327
+#: plover/gui_qt/config_window.py:353
msgid "Machine:"
msgstr ""
-#: plover/gui_qt/config_window.py:332
+#: plover/gui_qt/config_window.py:358
msgid "Options:"
msgstr ""
-#: plover/gui_qt/config_window.py:333
+#: plover/gui_qt/config_window.py:359
msgid "Keymap:"
msgstr ""
#. Widget: “ConfigWindow”.
#. Widget: “MainWindow”, title.
-#: plover/gui_qt/config_window.py:336 plover/gui_qt/main_window_ui.py:174
+#: plover/gui_qt/config_window.py:362 plover/gui_qt/main_window_ui.py:180
msgid "Output"
msgstr ""
-#: plover/gui_qt/config_window.py:337
+#: plover/gui_qt/config_window.py:363
msgid "Enable at start:"
msgstr ""
-#: plover/gui_qt/config_window.py:338
+#: plover/gui_qt/config_window.py:364
msgid "Enable output on startup."
msgstr ""
-#: plover/gui_qt/config_window.py:339
+#: plover/gui_qt/config_window.py:365
msgid "Start attached:"
msgstr ""
-#: plover/gui_qt/config_window.py:340
+#: plover/gui_qt/config_window.py:366
msgid ""
"Disable preceding space on first output.\n"
"\n"
"This option is only applicable when spaces are placed before."
msgstr ""
-#: plover/gui_qt/config_window.py:343
+#: plover/gui_qt/config_window.py:369
msgid "Start capitalized:"
msgstr ""
-#: plover/gui_qt/config_window.py:344
+#: plover/gui_qt/config_window.py:370
msgid "Capitalize the first word."
msgstr ""
-#: plover/gui_qt/config_window.py:345
+#: plover/gui_qt/config_window.py:371
msgid "Space placement:"
msgstr ""
-#: plover/gui_qt/config_window.py:347
+#: plover/gui_qt/config_window.py:373
msgid "Before Output"
msgstr ""
-#: plover/gui_qt/config_window.py:348
+#: plover/gui_qt/config_window.py:374
msgid "After Output"
msgstr ""
-#: plover/gui_qt/config_window.py:350
+#: plover/gui_qt/config_window.py:376
msgid "Set automatic space placement: before or after each word."
msgstr ""
-#: plover/gui_qt/config_window.py:351
+#: plover/gui_qt/config_window.py:377
msgid "Undo levels:"
msgstr ""
-#: plover/gui_qt/config_window.py:355
+#: plover/gui_qt/config_window.py:381
msgid ""
"Set how many preceding strokes can be undone.\n"
"\n"
@@ -387,137 +475,159 @@ msgid ""
msgstr ""
#. Widget: “ConfigWindow”.
-#: plover/gui_qt/config_window.py:361
+#: plover/gui_qt/config_window.py:387
msgid "Plugins"
msgstr ""
-#: plover/gui_qt/config_window.py:362
+#: plover/gui_qt/config_window.py:388
msgid "Extension:"
msgstr ""
-#: plover/gui_qt/config_window.py:366
+#: plover/gui_qt/config_window.py:392
msgid "Name"
msgstr ""
#. Widget: “MainWindow”, text.
-#: plover/gui_qt/config_window.py:366 plover/gui_qt/main_window_ui.py:176
+#: plover/gui_qt/config_window.py:392 plover/gui_qt/main_window_ui.py:182
msgid "Enabled"
msgstr ""
-#: plover/gui_qt/config_window.py:367
+#: plover/gui_qt/config_window.py:393
msgid "Configure enabled plugin extensions."
msgstr ""
#. Widget: “ConfigWindow”.
-#: plover/gui_qt/config_window.py:370
+#: plover/gui_qt/config_window.py:396
msgid "System"
msgstr ""
-#: plover/gui_qt/config_window.py:371
+#: plover/gui_qt/config_window.py:397
msgid "System:"
msgstr ""
-#. Widget: “ConfigWindow”, windowtitle.
+#. Widget: “ConfigWindow”, window title.
#: plover/gui_qt/config_window_ui.py:43
msgid "Plover: Configuration"
msgstr ""
#. Widget: “DictionariesWidget”, file picker.
-#: plover/gui_qt/dictionaries_widget.py:43
+#: plover/gui_qt/dictionaries_widget.py:40
msgid "Dictionaries ({extensions})"
msgstr ""
#. Widget: “DictionariesWidget”, file picker.
-#: plover/gui_qt/dictionaries_widget.py:46
+#: plover/gui_qt/dictionaries_widget.py:43
msgid "{format} dictionaries ({extensions})"
msgstr ""
-#. Widget: “DictionariesWidget”, “add” menu.
-#: plover/gui_qt/dictionaries_widget.py:106
-msgid "Open dictionaries"
+#. Widget: “DictionariesWidget”, accessible text.
+#: plover/gui_qt/dictionaries_widget.py:374
+msgid "disabled"
msgstr ""
-#. Widget: “DictionariesWidget”, “add” menu.
-#. Widget: “DictionariesWidget”, “new” file picker.
-#: plover/gui_qt/dictionaries_widget.py:110
-#: plover/gui_qt/dictionaries_widget.py:483
-msgid "New dictionary"
+#. Widget: “DictionariesWidget”, accessible text.
+#: plover/gui_qt/dictionaries_widget.py:377
+msgid "favorite"
msgstr ""
-#. Widget: “DictionariesWidget”, “save” menu.
-#: plover/gui_qt/dictionaries_widget.py:117
-msgid "Create a copy of each dictionary"
+#. Widget: “DictionariesWidget”, accessible text.
+#: plover/gui_qt/dictionaries_widget.py:380
+msgid "errored: {exception}."
msgstr ""
-#. Widget: “DictionariesWidget”, “save” menu.
-#: plover/gui_qt/dictionaries_widget.py:121
-msgid "Merge dictionaries into a new one"
+#. Widget: “DictionariesWidget”, accessible text.
+#: plover/gui_qt/dictionaries_widget.py:384
+msgid "loading"
+msgstr ""
+
+#. Widget: “DictionariesWidget”, accessible text.
+#: plover/gui_qt/dictionaries_widget.py:387
+msgid "read-only"
msgstr ""
#. Widget: “DictionariesWidget”, tooltip.
-#: plover/gui_qt/dictionaries_widget.py:196
+#: plover/gui_qt/dictionaries_widget.py:393
+msgid "Full path: {path}."
+msgstr ""
+
+#. Widget: “DictionariesWidget”, tool tip.
+#: plover/gui_qt/dictionaries_widget.py:396
+msgid "This dictionary is marked as the favorite."
+msgstr ""
+
+#. Widget: “DictionariesWidget”, tool tip.
+#: plover/gui_qt/dictionaries_widget.py:399
msgid "This dictionary is being loaded."
msgstr ""
-#. Widget: “DictionariesWidget”, tooltip.
-#: plover/gui_qt/dictionaries_widget.py:205
-msgid "This dictionary is read-only."
+#. Widget: “DictionariesWidget”, tool tip.
+#: plover/gui_qt/dictionaries_widget.py:402
+msgid "Loading this dictionary failed: {exception}."
msgstr ""
-#. Widget: “DictionariesWidget”, tooltip.
-#: plover/gui_qt/dictionaries_widget.py:209
-msgid "This dictionary is marked as a favorite."
+#. Widget: “DictionariesWidget”, tool tip.
+#: plover/gui_qt/dictionaries_widget.py:406
+msgid "This dictionary is read-only."
msgstr ""
#. Widget: “DictionariesWidget”, “save as copy” file picker.
-#: plover/gui_qt/dictionaries_widget.py:399
+#: plover/gui_qt/dictionaries_widget.py:586
msgid "Save a copy of {name} as..."
msgstr ""
#. Widget: “DictionariesWidget”, “save as copy” file picker.
-#: plover/gui_qt/dictionaries_widget.py:401
+#: plover/gui_qt/dictionaries_widget.py:588
msgid "{name} - Copy"
msgstr ""
#. Widget: “DictionariesWidget”, “save as merge” file picker.
-#: plover/gui_qt/dictionaries_widget.py:422
+#: plover/gui_qt/dictionaries_widget.py:609
msgid "Merge {names} as..."
msgstr ""
#. Widget: “DictionariesWidget”, “add” file picker.
-#. Widget: “DictionariesWidget”, tooltip.
-#: plover/gui_qt/dictionaries_widget.py:466
-#: plover/gui_qt/dictionaries_widget_ui.py:130
+#. Widget: “DictionariesWidget”, tool tip.
+#: plover/gui_qt/dictionaries_widget.py:636
+#: plover/gui_qt/dictionaries_widget_ui.py:139
msgid "Add dictionaries"
msgstr ""
+#. Widget: “DictionariesWidget”, “new” file picker.
#. Widget: “DictionariesWidget”, text.
-#: plover/gui_qt/dictionaries_widget_ui.py:102
+#: plover/gui_qt/dictionaries_widget.py:647
+#: plover/gui_qt/dictionaries_widget_ui.py:163
+msgid "New dictionary"
+msgstr ""
+
+#. Widget: “DictionariesWidget”, title.
+#. Widget: “DictionariesWidget”, accessible name.
+#: plover/gui_qt/dictionaries_widget_ui.py:109
+#: plover/gui_qt/dictionaries_widget_ui.py:111
msgid "Dictionaries"
msgstr ""
#. Widget: “DictionariesWidget”, text.
-#: plover/gui_qt/dictionaries_widget_ui.py:104
+#: plover/gui_qt/dictionaries_widget_ui.py:113
msgid "&Edit dictionaries"
msgstr ""
-#. Widget: “DictionariesWidget”, tooltip.
-#: plover/gui_qt/dictionaries_widget_ui.py:106
+#. Widget: “DictionariesWidget”, tool tip.
+#: plover/gui_qt/dictionaries_widget_ui.py:115
msgid "Edit selected dictionaries"
msgstr ""
#. Widget: “DictionariesWidget”, shortcut.
-#: plover/gui_qt/dictionaries_widget_ui.py:108
+#: plover/gui_qt/dictionaries_widget_ui.py:117
msgid "Ctrl+E"
msgstr ""
#. Widget: “DictionariesWidget”, text.
-#: plover/gui_qt/dictionaries_widget_ui.py:110
+#: plover/gui_qt/dictionaries_widget_ui.py:119
msgid "&Save dictionaries as..."
msgstr ""
-#. Widget: “DictionariesWidget”, tooltip.
-#: plover/gui_qt/dictionaries_widget_ui.py:112
+#. Widget: “DictionariesWidget”, tool tip.
+#: plover/gui_qt/dictionaries_widget_ui.py:121
msgid ""
"Save the selected dictionaries: create a new copy of each dictionary, or "
"merge them into a new dictionary."
@@ -525,176 +635,218 @@ msgstr ""
#. Widget: “DictionariesWidget”, shortcut.
#. Widget: “PaperTape”, shortcut.
-#: plover/gui_qt/dictionaries_widget_ui.py:114
-#: plover/gui_qt/paper_tape_ui.py:99
+#: plover/gui_qt/dictionaries_widget_ui.py:123
+#: plover/gui_qt/paper_tape_ui.py:107
msgid "Ctrl+S"
msgstr ""
#. Widget: “DictionariesWidget”, text.
-#: plover/gui_qt/dictionaries_widget_ui.py:116
+#: plover/gui_qt/dictionaries_widget_ui.py:125
msgid "&Remove dictionaries"
msgstr ""
-#. Widget: “DictionariesWidget”, tooltip.
-#: plover/gui_qt/dictionaries_widget_ui.py:118
+#. Widget: “DictionariesWidget”, tool tip.
+#: plover/gui_qt/dictionaries_widget_ui.py:127
msgid "Remove selected dictionaries"
msgstr ""
#. Widget: “DictionariesWidget”, shortcut.
#. Widget: “DictionaryEditor”, shortcut.
-#: plover/gui_qt/dictionaries_widget_ui.py:120
-#: plover/gui_qt/dictionary_editor_ui.py:116
+#: plover/gui_qt/dictionaries_widget_ui.py:129
+#: plover/gui_qt/dictionary_editor_ui.py:136
msgid "Del"
msgstr ""
#. Widget: “DictionariesWidget”, text.
#. Widget: “DictionaryEditor”, text.
-#: plover/gui_qt/dictionaries_widget_ui.py:122
-#: plover/gui_qt/dictionary_editor_ui.py:118
+#: plover/gui_qt/dictionaries_widget_ui.py:131
+#: plover/gui_qt/dictionary_editor_ui.py:138
msgid "&Undo"
msgstr ""
-#. Widget: “DictionariesWidget”, tooltip.
-#: plover/gui_qt/dictionaries_widget_ui.py:124
+#. Widget: “DictionariesWidget”, tool tip.
+#: plover/gui_qt/dictionaries_widget_ui.py:133
msgid "Undo last add/delete/reorder operation."
msgstr ""
#. Widget: “DictionariesWidget”, shortcut.
#. Widget: “DictionaryEditor”, shortcut.
-#: plover/gui_qt/dictionaries_widget_ui.py:126
-#: plover/gui_qt/dictionary_editor_ui.py:122
+#: plover/gui_qt/dictionaries_widget_ui.py:135
+#: plover/gui_qt/dictionary_editor_ui.py:142
msgid "Ctrl+Z"
msgstr ""
#. Widget: “DictionariesWidget”, text.
-#: plover/gui_qt/dictionaries_widget_ui.py:128
+#: plover/gui_qt/dictionaries_widget_ui.py:137
msgid "&Add dictionaries"
msgstr ""
#. Widget: “DictionariesWidget”, shortcut.
-#: plover/gui_qt/dictionaries_widget_ui.py:132
+#: plover/gui_qt/dictionaries_widget_ui.py:141
msgid "Ctrl+O"
msgstr ""
#. Widget: “DictionariesWidget”, text.
-#: plover/gui_qt/dictionaries_widget_ui.py:134
+#: plover/gui_qt/dictionaries_widget_ui.py:143
msgid "Add &translation"
msgstr ""
-#. Widget: “DictionariesWidget”, tooltip.
-#. Widget: “DictionaryEditor”, tooltip.
-#: plover/gui_qt/dictionaries_widget_ui.py:136
-#: plover/gui_qt/dictionary_editor_ui.py:126
+#. Widget: “DictionariesWidget”, tool tip.
+#. Widget: “DictionaryEditor”, tool tip.
+#: plover/gui_qt/dictionaries_widget_ui.py:145
+#: plover/gui_qt/dictionary_editor_ui.py:146
msgid "Add a new translation"
msgstr ""
#. Widget: “DictionariesWidget”, shortcut.
#. Widget: “DictionaryEditor”, shortcut.
-#: plover/gui_qt/dictionaries_widget_ui.py:138
-#: plover/gui_qt/dictionary_editor_ui.py:128
+#: plover/gui_qt/dictionaries_widget_ui.py:147
+#: plover/gui_qt/dictionary_editor_ui.py:148
msgid "Ctrl+N"
msgstr ""
#. Widget: “DictionariesWidget”, text.
-#: plover/gui_qt/dictionaries_widget_ui.py:140
+#: plover/gui_qt/dictionaries_widget_ui.py:149
msgid "Move dictionaries &up"
msgstr ""
-#. Widget: “DictionariesWidget”, tooltip.
-#: plover/gui_qt/dictionaries_widget_ui.py:142
+#. Widget: “DictionariesWidget”, tool tip.
+#: plover/gui_qt/dictionaries_widget_ui.py:151
msgid "Move selected dictionaries up."
msgstr ""
#. Widget: “DictionariesWidget”, text.
-#: plover/gui_qt/dictionaries_widget_ui.py:144
+#: plover/gui_qt/dictionaries_widget_ui.py:153
msgid "Move dictionaries &down"
msgstr ""
-#. Widget: “DictionariesWidget”, tooltip.
-#: plover/gui_qt/dictionaries_widget_ui.py:146
+#. Widget: “DictionariesWidget”, tool tip.
+#: plover/gui_qt/dictionaries_widget_ui.py:155
msgid "Move selected dictionaries down."
msgstr ""
-#. Widget: “DictionaryEditor”.
-#: plover/gui_qt/dictionary_editor.py:163
-msgid "Strokes"
+#. Widget: “DictionariesWidget”, text.
+#: plover/gui_qt/dictionaries_widget_ui.py:157
+msgid "Create a copy of each dictionary"
msgstr ""
-#. Widget: “DictionaryEditor”.
-#: plover/gui_qt/dictionary_editor.py:166
-msgid "Translation"
+#. Widget: “DictionariesWidget”, text.
+#: plover/gui_qt/dictionaries_widget_ui.py:159
+msgid "Merge dictionaries into a new one"
msgstr ""
-#. Widget: “DictionaryEditor”.
-#: plover/gui_qt/dictionary_editor.py:169
-msgid "Dictionary"
+#. Widget: “DictionariesWidget”, text.
+#: plover/gui_qt/dictionaries_widget_ui.py:161
+msgid "Open dictionaries"
msgstr ""
-#. Widget: “DictionaryEditor”, windowtitle.
-#: plover/gui_qt/dictionary_editor_ui.py:100
+#. Widget: “DictionaryEditor”, window title.
+#: plover/gui_qt/dictionary_editor_ui.py:108
msgid "Plover: Dictionary Editor"
msgstr ""
+#. Widget: “DictionaryEditor”, accessible name.
#. Widget: “DictionaryEditor”, title.
-#: plover/gui_qt/dictionary_editor_ui.py:102
+#: plover/gui_qt/dictionary_editor_ui.py:110
+#: plover/gui_qt/dictionary_editor_ui.py:112
msgid "Filter"
msgstr ""
#. Widget: “DictionaryEditor”, text.
-#: plover/gui_qt/dictionary_editor_ui.py:104
+#: plover/gui_qt/dictionary_editor_ui.py:114
msgid "By strokes:"
msgstr ""
+#. Widget: “DictionaryEditor”, accessible name.
+#: plover/gui_qt/dictionary_editor_ui.py:116
+msgid "Strokes filter"
+msgstr ""
+
+#. Widget: “DictionaryEditor”, accessible name.
+#: plover/gui_qt/dictionary_editor_ui.py:118
+msgid "Apply filter"
+msgstr ""
+
#. Widget: “DictionaryEditor”, text.
-#: plover/gui_qt/dictionary_editor_ui.py:106
+#: plover/gui_qt/dictionary_editor_ui.py:120
msgid "Apply"
msgstr ""
#. Widget: “DictionaryEditor”, text.
-#: plover/gui_qt/dictionary_editor_ui.py:108
+#: plover/gui_qt/dictionary_editor_ui.py:122
msgid "By translation:"
msgstr ""
+#. Widget: “DictionaryEditor”, accessible name.
+#: plover/gui_qt/dictionary_editor_ui.py:124
+msgid "Translation filter"
+msgstr ""
+
+#. Widget: “DictionaryEditor”, accessible name.
+#: plover/gui_qt/dictionary_editor_ui.py:126
+msgid "Clear filter"
+msgstr ""
+
#. Widget: “DictionaryEditor”, text.
-#: plover/gui_qt/dictionary_editor_ui.py:110
+#: plover/gui_qt/dictionary_editor_ui.py:128
msgid "Clear"
msgstr ""
+#. Widget: “DictionaryEditor”, accessible name.
+#: plover/gui_qt/dictionary_editor_ui.py:130
+msgid "Mappings"
+msgstr ""
+
#. Widget: “DictionaryEditor”, text.
-#: plover/gui_qt/dictionary_editor_ui.py:112
+#: plover/gui_qt/dictionary_editor_ui.py:132
msgid "&Delete"
msgstr ""
-#. Widget: “DictionaryEditor”, tooltip.
-#: plover/gui_qt/dictionary_editor_ui.py:114
+#. Widget: “DictionaryEditor”, tool tip.
+#: plover/gui_qt/dictionary_editor_ui.py:134
msgid "Delete selected entries."
msgstr ""
-#. Widget: “DictionaryEditor”, tooltip.
-#: plover/gui_qt/dictionary_editor_ui.py:120
+#. Widget: “DictionaryEditor”, tool tip.
+#: plover/gui_qt/dictionary_editor_ui.py:140
msgid "Undo last add/delete/edit operation."
msgstr ""
#. Widget: “DictionaryEditor”, text.
-#: plover/gui_qt/dictionary_editor_ui.py:124
+#: plover/gui_qt/dictionary_editor_ui.py:144
msgid "&New translation"
msgstr ""
#. Widget: “LookupDialog”, tooltip.
-#: plover/gui_qt/lookup_dialog.py:13
+#: plover/gui_qt/lookup_dialog.py:14
msgid "Search the dictionary for translations."
msgstr ""
-#: plover/gui_qt/lookup_dialog.py:15
+#: plover/gui_qt/lookup_dialog.py:16
msgid "Lookup"
msgstr ""
-#. Widget: “LookupDialog”, windowtitle.
+#. Widget: “LookupDialog”, window title.
#: plover/gui_qt/lookup_dialog_ui.py:56
msgid "Plover: Dictionary Lookup"
msgstr ""
-#: plover/gui_qt/machine_options.py:104
+#. Widget: “LookupDialog”, accessible name.
+#: plover/gui_qt/lookup_dialog_ui.py:58
+msgid "Pattern"
+msgstr ""
+
+#. Widget: “LookupDialog”, accessible description.
+#: plover/gui_qt/lookup_dialog_ui.py:60
+msgid "Translation pattern to lookup."
+msgstr ""
+
+#. Widget: “LookupDialog”, accessible name.
+#: plover/gui_qt/lookup_dialog_ui.py:62
+msgid "Results"
+msgstr ""
+
+#: plover/gui_qt/machine_options.py:106
msgid ""
"Arpeggiate allows using non-NKRO keyboards.\n"
"\n"
@@ -702,255 +854,304 @@ msgid ""
"space bar is pressed to send the stroke."
msgstr ""
-#. Widget: “MainWindow”, tooltip.
-#: plover/gui_qt/main_window_ui.py:172 plover/gui_qt/main_window_ui.py:212
+#. Widget: “MainWindow”, accessible name.
+#: plover/gui_qt/main_window_ui.py:166
+msgid "State"
+msgstr ""
+
+#. Widget: “MainWindow”, accessible description.
+#: plover/gui_qt/main_window_ui.py:168
+msgid "Connection state for the current machine."
+msgstr ""
+
+#. Widget: “MainWindow”, tool tip.
+#. Widget: “MainWindow”, accessible description.
+#: plover/gui_qt/main_window_ui.py:170 plover/gui_qt/main_window_ui.py:174
+#: plover/gui_qt/main_window_ui.py:218
msgid "Disconnect and reconnect the machine."
msgstr ""
-#. Widget: “MainWindow”, text.
+#. Widget: “MainWindow”, accessible name.
+#: plover/gui_qt/main_window_ui.py:172
+msgid "Reconnect"
+msgstr ""
+
+#. Widget: “MainWindow”, accessible name.
+#: plover/gui_qt/main_window_ui.py:176
+msgid "Type"
+msgstr ""
+
+#. Widget: “MainWindow”, accessible description.
#: plover/gui_qt/main_window_ui.py:178
+msgid "Change the current machine type."
+msgstr ""
+
+#. Widget: “MainWindow”, text.
+#: plover/gui_qt/main_window_ui.py:184
msgid "Disabled"
msgstr ""
#. Widget: “MainWindow”, title.
-#: plover/gui_qt/main_window_ui.py:180
+#: plover/gui_qt/main_window_ui.py:186
msgid "&File"
msgstr ""
#. Widget: “MainWindow”, title.
-#: plover/gui_qt/main_window_ui.py:182
+#: plover/gui_qt/main_window_ui.py:188
msgid "&Tools"
msgstr ""
#. Widget: “MainWindow”, title.
-#: plover/gui_qt/main_window_ui.py:184
+#: plover/gui_qt/main_window_ui.py:190
msgid "&Help"
msgstr ""
#. Widget: “MainWindow”, title.
-#: plover/gui_qt/main_window_ui.py:186
+#: plover/gui_qt/main_window_ui.py:192
msgid "&Edit"
msgstr ""
-#. Widget: “MainWindow”, windowtitle.
-#: plover/gui_qt/main_window_ui.py:188
+#. Widget: “MainWindow”, window title.
+#: plover/gui_qt/main_window_ui.py:194
msgid "Plover: Toolbar"
msgstr ""
#. Widget: “MainWindow”, text.
-#: plover/gui_qt/main_window_ui.py:190
+#: plover/gui_qt/main_window_ui.py:196
msgid "&Quit Plover"
msgstr ""
-#. Widget: “MainWindow”, tooltip.
-#: plover/gui_qt/main_window_ui.py:192
+#. Widget: “MainWindow”, tool tip.
+#: plover/gui_qt/main_window_ui.py:198
msgid "Quit the application."
msgstr ""
#. Widget: “MainWindow”, shortcut.
-#: plover/gui_qt/main_window_ui.py:194
+#: plover/gui_qt/main_window_ui.py:200
msgid "Ctrl+Q"
msgstr ""
#. Widget: “MainWindow”, text.
-#: plover/gui_qt/main_window_ui.py:196
+#: plover/gui_qt/main_window_ui.py:202
msgid "&Configure"
msgstr ""
-#. Widget: “MainWindow”, tooltip.
-#: plover/gui_qt/main_window_ui.py:198
+#. Widget: “MainWindow”, tool tip.
+#: plover/gui_qt/main_window_ui.py:204
msgid "Open the configuration dialog."
msgstr ""
#. Widget: “MainWindow”, shortcut.
-#: plover/gui_qt/main_window_ui.py:200
+#: plover/gui_qt/main_window_ui.py:206
msgid "Ctrl+,"
msgstr ""
#. Widget: “MainWindow”, text.
-#: plover/gui_qt/main_window_ui.py:202
+#: plover/gui_qt/main_window_ui.py:208
msgid "Open config &folder"
msgstr ""
-#. Widget: “MainWindow”, tooltip.
-#: plover/gui_qt/main_window_ui.py:204
+#. Widget: “MainWindow”, tool tip.
+#: plover/gui_qt/main_window_ui.py:210
msgid "Open the configuration folder."
msgstr ""
#. Widget: “MainWindow”, text.
-#: plover/gui_qt/main_window_ui.py:206
+#: plover/gui_qt/main_window_ui.py:212
msgid "&About"
msgstr ""
-#. Widget: “MainWindow”, tooltip.
-#: plover/gui_qt/main_window_ui.py:208
+#. Widget: “MainWindow”, tool tip.
+#: plover/gui_qt/main_window_ui.py:214
msgid "Open the about dialog."
msgstr ""
#. Widget: “MainWindow”, text.
-#: plover/gui_qt/main_window_ui.py:210
+#: plover/gui_qt/main_window_ui.py:216
msgid "&Reconnect machine"
msgstr ""
#. Widget: “MainWindow”, shortcut.
-#: plover/gui_qt/main_window_ui.py:214
+#: plover/gui_qt/main_window_ui.py:220
msgid "Ctrl+R"
msgstr ""
#. Widget: “MainWindow”, text.
-#: plover/gui_qt/main_window_ui.py:216
+#: plover/gui_qt/main_window_ui.py:222
msgid "&Show"
msgstr ""
-#. Widget: “MainWindow”, tooltip.
-#: plover/gui_qt/main_window_ui.py:218
+#. Widget: “MainWindow”, tool tip.
+#: plover/gui_qt/main_window_ui.py:224
msgid "Show the main window."
msgstr ""
#. Widget: “MainWindow”, text.
-#: plover/gui_qt/main_window_ui.py:220
+#: plover/gui_qt/main_window_ui.py:226
msgid "Toggle &output"
msgstr ""
-#. Widget: “MainWindow”, tooltip.
-#: plover/gui_qt/main_window_ui.py:222
+#. Widget: “MainWindow”, tool tip.
+#: plover/gui_qt/main_window_ui.py:228
msgid "Toggle the output."
msgstr ""
#. Widget: “MainWindow”, shortcut.
-#: plover/gui_qt/main_window_ui.py:224
+#: plover/gui_qt/main_window_ui.py:230
msgid "Ctrl+."
msgstr ""
-#: plover/gui_qt/paper_tape.py:25
-msgid "Paper Tape"
-msgstr ""
-
#. Paper tape style.
-#: plover/gui_qt/paper_tape.py:32
+#: plover/gui_qt/paper_tape.py:26
msgid "Paper"
msgstr ""
#. Paper tape style.
-#: plover/gui_qt/paper_tape.py:34
+#: plover/gui_qt/paper_tape.py:28
msgid "Raw"
msgstr ""
-#: plover/gui_qt/paper_tape.py:158
+#. Widget: “PaperTape”, tooltip.
+#: plover/gui_qt/paper_tape.py:111
+msgid "Paper tape display of strokes."
+msgstr ""
+
+#: plover/gui_qt/paper_tape.py:113
+msgid "Paper Tape"
+msgstr ""
+
+#: plover/gui_qt/paper_tape.py:212
msgid "Do you want to clear the paper tape?"
msgstr ""
-#: plover/gui_qt/paper_tape.py:172
+#: plover/gui_qt/paper_tape.py:226
msgid "Save Paper Tape"
msgstr ""
#. Paper tape, "save" file picker.
-#: plover/gui_qt/paper_tape.py:174
+#: plover/gui_qt/paper_tape.py:228
msgid "Text files (*.txt)"
msgstr ""
#. Widget: “PaperTape”, text.
-#: plover/gui_qt/paper_tape_ui.py:87
+#: plover/gui_qt/paper_tape_ui.py:89
+msgid "Mode:"
+msgstr ""
+
+#. Widget: “PaperTape”, accessible name.
+#: plover/gui_qt/paper_tape_ui.py:91
msgid "Mode"
msgstr ""
+#. Widget: “PaperTape”, accessible description.
+#: plover/gui_qt/paper_tape_ui.py:93
+msgid "Select paper tape display mode."
+msgstr ""
+
+#. Widget: “PaperTape”, accessible name.
+#: plover/gui_qt/paper_tape_ui.py:95
+msgid "Tape"
+msgstr ""
+
#. Widget: “PaperTape”, text.
#. Widget: “SuggestionsDialog”, text.
-#: plover/gui_qt/paper_tape_ui.py:89 plover/gui_qt/suggestions_dialog_ui.py:53
+#: plover/gui_qt/paper_tape_ui.py:97 plover/gui_qt/suggestions_dialog_ui.py:55
msgid "&Clear"
msgstr ""
-#. Widget: “PaperTape”, tooltip.
-#: plover/gui_qt/paper_tape_ui.py:91
+#. Widget: “PaperTape”, tool tip.
+#: plover/gui_qt/paper_tape_ui.py:99
msgid "Clear paper tape."
msgstr ""
#. Widget: “PaperTape”, shortcut.
#. Widget: “SuggestionsDialog”, shortcut.
-#: plover/gui_qt/paper_tape_ui.py:93 plover/gui_qt/suggestions_dialog_ui.py:57
+#: plover/gui_qt/paper_tape_ui.py:101 plover/gui_qt/suggestions_dialog_ui.py:59
msgid "Ctrl+L"
msgstr ""
#. Widget: “PaperTape”, text.
-#: plover/gui_qt/paper_tape_ui.py:95
+#: plover/gui_qt/paper_tape_ui.py:103
msgid "&Save"
msgstr ""
-#. Widget: “PaperTape”, tooltip.
-#: plover/gui_qt/paper_tape_ui.py:97
+#. Widget: “PaperTape”, tool tip.
+#: plover/gui_qt/paper_tape_ui.py:105
msgid "Save paper tape to file."
msgstr ""
#. Widget: “PaperTape”, text.
#. Widget: “SuggestionsDialog”, text.
-#: plover/gui_qt/paper_tape_ui.py:101 plover/gui_qt/suggestions_dialog_ui.py:59
+#: plover/gui_qt/paper_tape_ui.py:109 plover/gui_qt/suggestions_dialog_ui.py:61
msgid "&Toggle \"always on top\""
msgstr ""
-#. Widget: “PaperTape”, tooltip.
-#. Widget: “SuggestionsDialog”, tooltip.
-#: plover/gui_qt/paper_tape_ui.py:103 plover/gui_qt/suggestions_dialog_ui.py:61
+#. Widget: “PaperTape”, tool tip.
+#. Widget: “SuggestionsDialog”, tool tip.
+#: plover/gui_qt/paper_tape_ui.py:111 plover/gui_qt/suggestions_dialog_ui.py:63
msgid "Toggle \"always on top\"."
msgstr ""
#. Widget: “PaperTape”, shortcut.
#. Widget: “SuggestionsDialog”, shortcut.
-#: plover/gui_qt/paper_tape_ui.py:105 plover/gui_qt/suggestions_dialog_ui.py:63
+#: plover/gui_qt/paper_tape_ui.py:113 plover/gui_qt/suggestions_dialog_ui.py:65
msgid "Ctrl+T"
msgstr ""
#. Widget: “PaperTape”, text.
#. Widget: “SuggestionsDialog”, text.
-#: plover/gui_qt/paper_tape_ui.py:107 plover/gui_qt/suggestions_dialog_ui.py:65
+#: plover/gui_qt/paper_tape_ui.py:115 plover/gui_qt/suggestions_dialog_ui.py:67
msgid "Select &font"
msgstr ""
-#. Widget: “PaperTape”, tooltip.
-#. Widget: “SuggestionsDialog”, tooltip.
-#: plover/gui_qt/paper_tape_ui.py:109 plover/gui_qt/suggestions_dialog_ui.py:67
+#. Widget: “PaperTape”, tool tip.
+#. Widget: “SuggestionsDialog”, tool tip.
+#: plover/gui_qt/paper_tape_ui.py:117 plover/gui_qt/suggestions_dialog_ui.py:69
msgid "Open font selection dialog."
msgstr ""
#. Widget: “SuggestionsDialog”, tooltip.
-#: plover/gui_qt/suggestions_dialog.py:26
+#: plover/gui_qt/suggestions_dialog.py:27
msgid "Suggest possible strokes for the last written words."
msgstr ""
-#: plover/gui_qt/suggestions_dialog.py:28
+#. Widget: “SuggestionsDialog”, accessible name.
+#: plover/gui_qt/suggestions_dialog.py:29
+#: plover/gui_qt/suggestions_dialog_ui.py:53
msgid "Suggestions"
msgstr ""
#. Widget: “SuggestionsDialog”, “font” menu.
-#: plover/gui_qt/suggestions_dialog.py:57
+#: plover/gui_qt/suggestions_dialog.py:58
msgid "&Text"
msgstr ""
#. Widget: “SuggestionsDialog”, “font” menu.
-#: plover/gui_qt/suggestions_dialog.py:59
+#: plover/gui_qt/suggestions_dialog.py:60
msgid "&Strokes"
msgstr ""
-#. Widget: “SuggestionsDialog”, tooltip.
-#: plover/gui_qt/suggestions_dialog_ui.py:55
+#. Widget: “SuggestionsDialog”, tool tip.
+#: plover/gui_qt/suggestions_dialog_ui.py:57
msgid "Clear the history."
msgstr ""
#. Widget: “SuggestionsWidget”.
-#: plover/gui_qt/suggestions_widget.py:42
+#: plover/gui_qt/suggestions_widget.py:23
msgid "no suggestions"
msgstr ""
-#: plover/gui_qt/trayicon.py:116
+#: plover/gui_qt/trayicon.py:115
msgid "{machine} is {state}"
msgstr ""
#. Tray icon tooltip.
-#: plover/gui_qt/trayicon.py:122
+#: plover/gui_qt/trayicon.py:121
msgid "output is enabled"
msgstr ""
#. Tray icon tooltip.
-#: plover/gui_qt/trayicon.py:125
+#: plover/gui_qt/trayicon.py:124
msgid "output is disabled"
msgstr ""
diff --git a/test/gui_qt/test_dictionaries_widget.py b/test/gui_qt/test_dictionaries_widget.py
index 6ed342b64..6368a6801 100644
--- a/test/gui_qt/test_dictionaries_widget.py
+++ b/test/gui_qt/test_dictionaries_widget.py
@@ -40,7 +40,8 @@
Qt.Unchecked: False,
}
-MODEL_ROLES = sorted([Qt.CheckStateRole, Qt.DecorationRole, Qt.DisplayRole, Qt.ToolTipRole])
+MODEL_ROLES = sorted([Qt.AccessibleTextRole, Qt.CheckStateRole,
+ Qt.DecorationRole, Qt.DisplayRole, Qt.ToolTipRole])
def parse_state(state_str):
@@ -207,6 +208,52 @@ def model_test(monkeypatch, request):
return test
+def test_model_accessible_text_1(model_test):
+ '''
+ ☑ 🗘 read-only.ro
+ ☑ 🗘 user.json
+ ☑ 🗘 invalid.bad
+ ☑ 🗘 commands.json
+ ☐ 🗘 asset:plover:assets/main.json
+ '''
+ for n, expected in enumerate((
+ 'read-only.ro, loading',
+ 'user.json, loading',
+ 'invalid.bad, loading',
+ 'commands.json, loading',
+ 'asset:plover:assets/main.json, disabled, loading',
+ )):
+ assert model_test.model.index(n).data(Qt.AccessibleTextRole) == expected
+
+def test_model_accessible_text_2(model_test):
+ '''
+ ☑ 🛇 read-only.ro
+ ☑ ★ user.json
+ ☑ ⎕ commands.json
+ ☐ 🛇 asset:plover:assets/main.json
+ '''
+ for n, expected in enumerate((
+ 'read-only.ro, read-only',
+ 'user.json, favorite',
+ 'commands.json',
+ 'asset:plover:assets/main.json, disabled, read-only',
+ )):
+ assert model_test.model.index(n).data(Qt.AccessibleTextRole) == expected
+
+def test_model_accessible_text_3(model_test):
+ '''
+ ☑ ! invalid.bad
+ '''
+ expected = 'invalid.bad, errored: %s.' % INVALID_EXCEPTION
+ assert model_test.model.index(0).data(Qt.AccessibleTextRole) == expected
+
+def test_model_accessible_text_4(model_test):
+ '''
+ ☐ ! invalid.bad
+ '''
+ expected = 'invalid.bad, disabled, errored: %s.' % INVALID_EXCEPTION
+ assert model_test.model.index(0).data(Qt.AccessibleTextRole) == expected
+
def test_model_add_existing(model_test):
'''
☑ ★ user.json