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