ui: add support to multiple range in Set action
This commit is contained in:
parent
5c96d2d307
commit
0bce293017
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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]))
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue