ui: add support to multiple range in Set action

This commit is contained in:
Vinícius 2022-01-20 20:05:29 -03:00 committed by Peter F. Patel-Schneider
parent 5c96d2d307
commit 0bce293017
2 changed files with 87 additions and 37 deletions

View File

@ -1057,6 +1057,7 @@ class MultipleRangeValidator(Validator):
assert isinstance(sub_items, dict) assert isinstance(sub_items, dict)
# sub_items: items -> class with .minimum, .maximum, .length (in bytes), .id (a string) and .widget (e.g. 'Scale') # sub_items: items -> class with .minimum, .maximum, .length (in bytes), .id (a string) and .widget (e.g. 'Scale')
self.items = items self.items = items
self.keys = _NamedInts(**{str(item): int(item) for item in items})
self._item_from_id = {int(k): k for k in items} self._item_from_id = {int(k): k for k in items}
self.sub_items = sub_items self.sub_items = sub_items

View File

@ -28,7 +28,7 @@ from typing import Dict
from gi.repository import Gdk, GObject, Gtk from gi.repository import Gdk, GObject, Gtk
from logitech_receiver import diversion as _DIV from logitech_receiver import diversion as _DIV
from logitech_receiver.common import NamedInt, UnsortedNamedInts from logitech_receiver.common import NamedInt, NamedInts, UnsortedNamedInts
from logitech_receiver.diversion import XK_KEYS as _XK_KEYS from logitech_receiver.diversion import XK_KEYS as _XK_KEYS
from logitech_receiver.diversion import Key as _Key from logitech_receiver.diversion import Key as _Key
from logitech_receiver.diversion import buttons as _buttons from logitech_receiver.diversion import buttons as _buttons
@ -1743,40 +1743,57 @@ class SetValueControl(Gtk.HBox):
self.toggle_widget.append(*v) self.toggle_widget.append(*v)
self.toggle_widget.connect('changed', self._changed) self.toggle_widget.connect('changed', self._changed)
self.range_widget = Gtk.SpinButton.new_with_range(0, 0xFFFF, 1) self.range_widget = Gtk.SpinButton.new_with_range(0, 0xFFFF, 1)
self.range_widget.connect('changed', self._changed) self.range_widget.connect('value-changed', self._changed)
self.choice_widget = SmartComboBox([], completion=True, has_entry=True) self.choice_widget = SmartComboBox([], completion=True, has_entry=True)
self.choice_widget.connect('changed', self._changed) self.choice_widget.connect('changed', self._changed)
self.sub_key_widget = SmartComboBox([])
self.sub_key_widget.connect('changed', self._changed)
self.unsupported_label = Gtk.Label(_('Unsupported setting')) self.unsupported_label = Gtk.Label(_('Unsupported setting'))
self.pack_start(self.sub_key_widget, False, False, 0)
self.sub_key_widget.set_hexpand(False)
self.sub_key_widget.set_size_request(120, 0)
self.sub_key_widget.hide()
for w in [self.toggle_widget, self.range_widget, self.choice_widget, self.unsupported_label]: for w in [self.toggle_widget, self.range_widget, self.choice_widget, self.unsupported_label]:
self.pack_end(w, True, True, 0) self.pack_end(w, True, True, 0)
w.hide() w.hide()
self.unsupp_value = None self.unsupp_value = None
self.get_value = lambda: None self.current_kind = None
self.set_value = lambda value: None self.sub_key_range_items = None
def _changed(self, widget, *args): def _changed(self, widget, *args):
if widget.get_visible(): if widget.get_visible():
value = self.get_value() value = self.get_value()
if widget == self.choice_widget: if self.current_kind == 'choice':
value = widget.get_value() value = widget.get_value()
icon = 'dialog-warning' if widget._allowed_values and (value not in widget._allowed_values) else '' icon = 'dialog-warning' if widget._allowed_values and (value not in widget._allowed_values) else ''
widget.get_child().set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, icon) widget.get_child().set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, icon)
elif self.current_kind == 'range_with_key' and widget == self.sub_key_widget:
key = self.sub_key_widget.get_value()
selected_item = next((item for item in self.sub_key_range_items
if key == item.id), None) if self.sub_key_range_items else None
(minimum, maximum) = (selected_item.minimum, selected_item.maximum) if selected_item else (0, 0xFFFF)
self.range_widget.set_range(minimum, maximum)
self.on_change(value) self.on_change(value)
def _hide_all(self): def _hide_all(self):
for w in self.get_children(): for w in self.get_children():
w.hide() w.hide()
def make_toggle(self): def get_value(self):
self._hide_all() if self.current_kind == 'toggle':
def g():
value = self.toggle_widget.get_active_id() value = self.toggle_widget.get_active_id()
return True if value == 't' else False if value == 'f' else value return True if value == 't' else False if value == 'f' else value
if self.current_kind == 'range':
return int(self.range_widget.get_value())
if self.current_kind == 'range_with_key':
return {self.sub_key_widget.get_value(): int(self.range_widget.get_value())}
if self.current_kind == 'choice':
return self.choice_widget.get_value()
return self.unsupp_value
self.get_value = g def set_value(self, value):
if self.current_kind == 'toggle':
def s(value): value = str(value).lower()
if value in ('true', 'yes', 'on', 't', 'y'): if value in ('true', 'yes', 'on', 't', 'y'):
self.toggle_widget.set_active_id('t') self.toggle_widget.set_active_id('t')
elif value in ('false', 'no', 'off', 'f', 'n'): elif value in ('false', 'no', 'off', 'f', 'n'):
@ -1785,50 +1802,74 @@ class SetValueControl(Gtk.HBox):
self.toggle_widget.set_active_id('~') self.toggle_widget.set_active_id('~')
else: else:
self.toggle_widget.set_active_id(None) self.toggle_widget.set_active_id(None)
elif self.current_kind == 'range':
self.set_value = lambda value: s(str(value).lower()) minimum, maximum = self.range_widget.get_range()
self.toggle_widget.show()
def make_range(self, minimum, maximum):
self._hide_all()
self.range_widget.set_range(minimum, maximum)
self.get_value = lambda: int(self.range_widget.get_value())
def s(value):
try: try:
v = round(float(value)) v = round(float(value))
except (ValueError, TypeError): except (ValueError, TypeError):
v = minimum v = minimum
self.range_widget.set_value(max(minimum, min(maximum, v))) self.range_widget.set_value(max(minimum, min(maximum, v)))
elif self.current_kind == 'range_with_key':
if not (isinstance(value, dict) and len(value) == 1):
value = {None: None}
key = next(iter(value.keys()))
selected_item = next((item for item in self.sub_key_range_items
if key == item.id), None) if self.sub_key_range_items else None
(minimum, maximum) = (selected_item.minimum, selected_item.maximum) if selected_item else (0, 0xFFFF)
try:
v = round(float(next(iter(value.values()))))
except (ValueError, TypeError):
v = minimum
self.sub_key_widget.set_value(key or '')
self.range_widget.set_value(max(minimum, min(maximum, v)))
elif self.current_kind == 'choice':
self.choice_widget.set_value(value)
else:
self.unsupp_value = value
if value is None or value == '': # reset all
self.range_widget.set_range(0x0000, 0xFFFF)
self.range_widget.set_value(0)
self.toggle_widget.set_active_id('')
self.sub_key_widget.set_value('')
self.choice_widget.set_value('')
self.set_value = s def make_toggle(self):
self.current_kind = 'toggle'
self._hide_all()
self.toggle_widget.show()
def make_range(self, minimum, maximum):
self.current_kind = 'range'
self._hide_all()
self.range_widget.set_range(minimum, maximum)
self.range_widget.show()
def make_range_with_key(self, items):
self.current_kind = 'range_with_key'
self._hide_all()
self.sub_key_range_items = items or None
self.sub_key_widget.set_all_values(map(lambda item: item.id, items) if items else [])
self.sub_key_widget.show()
self.range_widget.show() self.range_widget.show()
def make_choice(self, values): def make_choice(self, values):
self.current_kind = 'choice'
self._hide_all() self._hide_all()
sort_key = int if all(str(v).isdigit() for v in values) else str sort_key = int if all(str(v).isdigit() for v in values) else str
self.choice_widget.set_all_values(sorted(values, key=sort_key)) self.choice_widget.set_all_values(sorted(values, key=sort_key))
self.choice_widget._allowed_values = values self.choice_widget._allowed_values = values
self.get_value = self.choice_widget.get_value
self.set_value = self.choice_widget.set_value
self.choice_widget.show() self.choice_widget.show()
def make_unsupported(self): def make_unsupported(self):
self.current_kind = None
self._hide_all() self._hide_all()
self.get_value = lambda: self.unsupp_value
def s(value): # preserve unsupported values
self.unsupp_value = value
self.set_value = s
self.unsupported_label.show() self.unsupported_label.show()
def _all_settings(): def _all_settings():
settings = {} settings = {}
for s in sorted(_SETTINGS, key=lambda setting: setting.label): for s in sorted(_SETTINGS, key=lambda setting: setting.label):
if s.validator_class.kind == _SKIND.multiple_range: # not supported yet
continue
if s.name not in settings: if s.name not in settings:
settings[s.name] = [s] settings[s.name] = [s]
else: else:
@ -1931,7 +1972,7 @@ class SetUI(ActionUI):
if kind in cls.MULTIPLE: if kind in cls.MULTIPLE:
keys = UnsortedNamedInts() keys = UnsortedNamedInts()
for s in settings: for s in settings:
universe = getattr(s, 'choices_universe' if kind == _SKIND.multiple_toggle else 'keys_universe', None) universe = getattr(s, 'keys_universe' if kind == _SKIND.map_choice else 'choices_universe', None)
if universe: if universe:
keys |= universe keys |= universe
# only one key per number is used # only one key per number is used
@ -2004,6 +2045,8 @@ class SetUI(ActionUI):
elif device_setting.kind == _SKIND.map_choice: elif device_setting.kind == _SKIND.map_choice:
choices = val.choices or None choices = val.choices or None
supported_keys = choices.keys() if choices else None supported_keys = choices.keys() if choices else None
elif device_setting.kind == _SKIND.multiple_range:
supported_keys = val.keys
self.key_field.show_only(supported_keys) self.key_field.show_only(supported_keys)
self._update_validation() self._update_validation()
@ -2026,8 +2069,10 @@ class SetUI(ActionUI):
supported_values = choices.get(key, None) or None supported_values = choices.get(key, None) or None
self.value_field.choice_widget.show_only(supported_values) self.value_field.choice_widget.show_only(supported_values)
self._update_validation() self._update_validation()
elif kind in (_SKIND.range, ): # _SKIND.multiple_range not supported elif kind == _SKIND.range:
self.value_field.make_range(val_class.min_value, val_class.max_value) self.value_field.make_range(val_class.min_value, val_class.max_value)
elif kind == _SKIND.multiple_range:
self.value_field.make_range_with_key(getattr(setting, 'sub_items_universe', {}).get(key, {}) if setting else {})
else: else:
self.value_field.make_unsupported() self.value_field.make_unsupported()
@ -2107,9 +2152,13 @@ class SetUI(ActionUI):
value = next(a, None) value = next(a, None)
if setting and (kind in (_SKIND.choice, _SKIND.map_choice)): if setting and (kind in (_SKIND.choice, _SKIND.map_choice)):
all_values = cls._all_choices(setting_name) all_values = cls._all_choices(setting_name)
if all_values: if isinstance(all_values, NamedInts):
value = all_values[value] value = all_values[value]
disp.append(value) if isinstance(value, dict) and len(value) == 1:
k, v = next(iter(value.items()))
disp.append(f'{k}={v}')
else:
disp.append(value)
return device_disp + ': ' + ' '.join(map(lambda s: shlex_quote(str(s)), [*disp, *a])) return device_disp + ': ' + ' '.join(map(lambda s: shlex_quote(str(s)), [*disp, *a]))