From 6022c37325a43f0e45ba0f40eab3f9f530962a98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius?= Date: Mon, 17 Jan 2022 17:07:37 -0300 Subject: [PATCH] ui: add device selector to Set rule editor --- lib/logitech_receiver/diversion.py | 4 +- lib/solaar/ui/__init__.py | 3 +- lib/solaar/ui/diversion_rules.py | 126 ++++++++++++++++++++--------- lib/solaar/ui/window.py | 4 +- 4 files changed, 97 insertions(+), 40 deletions(-) diff --git a/lib/logitech_receiver/diversion.py b/lib/logitech_receiver/diversion.py index 2469fb7f..e7ce71ae 100644 --- a/lib/logitech_receiver/diversion.py +++ b/lib/logitech_receiver/diversion.py @@ -27,7 +27,6 @@ from math import sqrt as _sqrt import _thread import psutil -import solaar.ui.window as _window from solaar.ui.config_panel import change_setting as _change_setting from yaml import add_representer as _yaml_add_representer @@ -701,6 +700,9 @@ class Set(Action): return 'Set: ' + ' '.join([str(a) for a in self.args]) def evaluate(self, feature, notification, device, status, last_result): + import solaar.ui.window as _window + # importing here to avoid circular imports + if len(self.args) < 3: return None if _log.isEnabledFor(_INFO): diff --git a/lib/solaar/ui/__init__.py b/lib/solaar/ui/__init__.py index 23b0d840..76e09a7d 100644 --- a/lib/solaar/ui/__init__.py +++ b/lib/solaar/ui/__init__.py @@ -96,7 +96,7 @@ def ui_async(function, *args, **kwargs): # # -from . import notify, tray, window # isort:skip # noqa: E402 +from . import diversion_rules, notify, tray, window # isort:skip # noqa: E402 def _startup(app, startup_hook, use_tray, show_window): @@ -177,6 +177,7 @@ def _status_changed(device, alert, reason, refresh=False): need_popup = alert & ALERT.SHOW_WINDOW window.update(device, need_popup, refresh) + diversion_rules.update_devices() if alert & (ALERT.NOTIFICATION | ALERT.ATTENTION): notify.show(device, reason) diff --git a/lib/solaar/ui/diversion_rules.py b/lib/solaar/ui/diversion_rules.py index 62a5779e..39ae0277 100644 --- a/lib/solaar/ui/diversion_rules.py +++ b/lib/solaar/ui/diversion_rules.py @@ -671,6 +671,10 @@ class DiversionDialog: menu_copy.show() return menu_copy + def update_devices(self): + for rc in self.ui.values(): + rc.update_devices() + ## Not currently used # @@ -763,6 +767,9 @@ class RuleComponentUI: for c in self.panel.get_children(): self.panel.remove(c) + def update_devices(self): + pass + class UnsupportedRuleComponentUI(RuleComponentUI): @@ -1597,6 +1604,30 @@ def _all_settings(): return settings +def _all_devices(): + devices = [] + + def dev_in_row(_store, _treepath, row): + device = _dev_model.get_value(row, 7) + if device and device.kind: + devices.append(device) + + _dev_model.foreach(dev_in_row) + return devices + + +def _device_display_name(device): + id = device.serial or device.unitId + name = device.codename + return name if not id else name + ' (' + id + ')' + + +def _find_device(devices, search): + if not search: + return None + return next((d for d in devices if search in [d.unitId, d.serial, _device_display_name(d)]), None) + + class SetUI(ActionUI): CLASS = _DIV.Set @@ -1606,21 +1637,16 @@ class SetUI(ActionUI): MULTIPLE = [_SKIND.multiple_toggle, _SKIND.map_choice, _SKIND.multiple_range] def create_widgets(self): + self.devices = [] + self.widgets = {} - self._old_device_values = {} - - lbl = Gtk.Label(_('Same device'), halign=Gtk.Align.CENTER, valign=Gtk.Align.END, hexpand=True, vexpand=True) - self.widgets[lbl] = (0, 0, 1, 1) - self.same_device_chk = Gtk.Switch() - self.same_device_chk.connect('state-set', self._changed_same_device) - self.widgets[self.same_device_chk] = (0, 1, 1, 1) - - lbl = Gtk.Label(_('Serial or Unit ID'), halign=Gtk.Align.CENTER, valign=Gtk.Align.END, hexpand=True, vexpand=True) + lbl = Gtk.Label(_('Device'), halign=Gtk.Align.CENTER, valign=Gtk.Align.END, hexpand=True, vexpand=True) self.widgets[lbl] = (1, 0, 1, 1) self.device_field = Gtk.ComboBoxText.new_with_entry() self.device_field.get_child().set_text('') self.device_field.set_valign(Gtk.Align.START) + self.device_field.set_size_request(300, 0) self.device_field.connect('changed', self._on_update) self.widgets[self.device_field] = (1, 1, 1, 1) @@ -1687,17 +1713,6 @@ class SetUI(ActionUI): keys = None return setting, val_class, kind, keys - def _changed_same_device(self, *args): - same = self.same_device_chk.get_active() - if same: - self._old_device_values[self.component] = self.device_field.get_child().get_text() - self.device_field.get_child().set_text(_('[originating device]')) - self.device_field.set_sensitive(False) - else: - self.device_field.get_child().set_text(self._old_device_values.get(self.component, '')) - self.device_field.set_sensitive(True) - self.device_field.grab_focus() - def _changed_setting(self, *args): setting_name = self.setting_field.get_active_id() or None setting, val_class, kind, keys = self._setting_attributes(setting_name) @@ -1719,11 +1734,34 @@ class SetUI(ActionUI): CompletionEntry.add_completion_to_entry(self.key_field.get_child(), map(str, keys)) for k in sorted(keys, key=str): self.key_field.append(str(int(k)), str(k)) - self._update_visibility() + + def update_devices(self): + device_value = self.collect_value()[0] + self.devices = _all_devices() + if not self.component: + return + self.device_field.remove_all() + self.device_field.append('', _('Originating device')) + acceptable_values = [] + for device in self.devices: + display_name = _device_display_name(device) + acceptable_values += [display_name, device.unitId, device.serial] + self.device_field.append(device.serial or device.unitId, display_name) + CompletionEntry.add_completion_to_entry(self.device_field.get_child(), filter(lambda v: v, acceptable_values)) + device = _find_device(self.devices, device_value) + with self.ignore_changes(): + if device or not device_value: + self.device_field.set_active_id((device.serial or device.unitId) if device else '') + else: + self.device_field.get_child().set_text(device_value or '') def _update_visibility(self): + if not self.component: + return a = iter(self.component.args) - next(a, None) # device - currently not checked + device_str = next(a, None) + icon = 'dialog-warning' if device_str and not _find_device(self.devices, device_str) else '' + self.device_field.get_child().set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, icon) setting_name = next(a, '') setting, val_class, kind, keys = self._setting_attributes(setting_name) multiple = kind in self.MULTIPLE @@ -1742,16 +1780,16 @@ class SetUI(ActionUI): def show(self, component, editable): super().show(component, editable) + self.update_devices() a = iter(component.args) with self.ignore_changes(): - device = next(a, None) - same = device is None - self._old_device_values[self.component] = device or '' - self.device_field.get_child().set_text(device or '') - if self.same_device_chk.get_active() != same: - self.same_device_chk.set_active(same) + device_str = next(a, None) + same = not device_str + device = _find_device(self.devices, device_str) + if device or same: + self.device_field.set_active_id((device.serial or device.unitId) if device else '') else: - self._changed_same_device() + self.device_field.get_child().set_text(device_str or '') setting_name = next(a, '') setting, _v, kind, keys = self._setting_attributes(setting_name) self.setting_field.set_active_id(setting.name if setting else '') @@ -1766,8 +1804,12 @@ class SetUI(ActionUI): self._update_visibility() def collect_value(self): - same = self.same_device_chk.get_active() - device = None if same else self.device_field.get_active_id() or self.device_field.get_active_text().strip() + device_str = self.device_field.get_active_id() + if device_str is None: + device_str = self.device_field.get_active_text().strip() + same = device_str in ['', _('Originating device')] + device = None if same else _find_device(self.devices, device_str) + device_value = (device.serial or device.unitId) if device else None if same else device_str setting_name = self.setting_field.get_active_id() or None setting, val_class, kind, keys = self._setting_attributes(setting_name) key_value = [] @@ -1776,16 +1818,18 @@ class SetUI(ActionUI): key = _from_named_ints(key, keys) key_value.append(keys[key] if keys else key) key_value.append(self.value_field.get_value()) - return [device, setting_name, *key_value] + return [device_value, setting_name, *key_value] @classmethod def right_label(cls, component): a = iter(component.args) - device = next(a, None) - disp = [_('[originating device]') if device is None else device] + device_str = next(a, None) + device = None if not device_str else _find_device(_all_devices(), device_str) + device_disp = _('Originating device' + ) if not device_str else _device_display_name(device) if device else shlex_quote(device_str) setting_name = next(a, None) setting, val_class, kind, keys = cls._setting_attributes(setting_name) - disp.append(setting.label if setting else setting_name) + disp = [setting.label if setting else setting_name] if kind in cls.MULTIPLE: key = next(a, None) disp.append(_from_named_ints(key, keys) if keys else key) @@ -1795,7 +1839,7 @@ class SetUI(ActionUI): if all_values: value = all_values[value] disp.append(value) - return ' '.join(map(lambda s: shlex_quote(str(s)), [*disp, *a])) + return device_disp + ': ' + ' '.join(map(lambda s: shlex_quote(str(s)), [*disp, *a])) COMPONENT_UI = { @@ -1820,9 +1864,17 @@ COMPONENT_UI = { } -def show_window(trigger=None): +def update_devices(): + global _diversion_dialog + if _diversion_dialog: + _diversion_dialog.update_devices() + + +def show_window(model): GObject.type_register(RuleComponentWrapper) global _diversion_dialog + global _dev_model + _dev_model = model if _diversion_dialog is None: _diversion_dialog = DiversionDialog() _diversion_dialog.window.present() diff --git a/lib/solaar/ui/window.py b/lib/solaar/ui/window.py index c03f65cb..048a2c1d 100644 --- a/lib/solaar/ui/window.py +++ b/lib/solaar/ui/window.py @@ -323,7 +323,9 @@ def _create_window_layout(): bottom_buttons_box.add(quit_button) about_button = _new_button(_('About %s') % NAME, 'help-about', _SMALL_BUTTON_ICON_SIZE, clicked=_show_about_window) bottom_buttons_box.add(about_button) - diversion_button = _new_button(_('Rule Editor'), '', _SMALL_BUTTON_ICON_SIZE, clicked=_show_diversion_window) + diversion_button = _new_button( + _('Rule Editor'), '', _SMALL_BUTTON_ICON_SIZE, clicked=lambda *_trigger: _show_diversion_window(_model) + ) bottom_buttons_box.add(diversion_button) bottom_buttons_box.set_child_secondary(diversion_button, True)