logitech_receiver: add UnsortedNamedInts

This commit is contained in:
Vinícius 2022-01-17 13:46:54 -03:00 committed by Peter F. Patel-Schneider
parent b36b070feb
commit 29ff5e5924
3 changed files with 55 additions and 25 deletions

View File

@ -99,7 +99,7 @@ class NamedInts:
if the value already exists in the set (int or string), ValueError will be
raised.
"""
__slots__ = ('__dict__', '_values', '_indexed', '_fallback')
__slots__ = ('__dict__', '_values', '_indexed', '_fallback', '_is_sorted')
def __init__(self, **kwargs):
def _readable_name(n):
@ -110,7 +110,9 @@ class NamedInts:
# print (repr(kwargs))
values = {k: NamedInt(v, _readable_name(k)) for (k, v) in kwargs.items()}
self.__dict__ = values
self._values = sorted(list(values.values()))
self._is_sorted = False
self._values = list(values.values())
self._sort_values()
self._indexed = {int(v): v for v in self._values}
# assert len(values) == len(self._indexed)
# "(%d) %r\n=> (%d) %r" % (len(values), values, len(self._indexed), self._indexed)
@ -137,14 +139,20 @@ class NamedInts:
if unknown_bits:
yield 'unknown:%06X' % unknown_bits
def _sort_values(self):
self._values = sorted(self._values)
self._is_sorted = True
def __getitem__(self, index):
if isinstance(index, int):
if index in self._indexed:
return self._indexed[int(index)]
if self._fallback and isinstance(index, int):
if self._fallback:
value = NamedInt(index, self._fallback(index))
self._indexed[index] = value
self._values = sorted(self._values + [value])
self._values.append(value)
self._is_sorted = False
self._sort_values()
return value
elif is_string(index):
@ -153,21 +161,24 @@ class NamedInts:
return (next((x for x in self._values if str(x) == index), None))
elif isinstance(index, slice):
values = self._values if self._is_sorted else sorted(self._values)
if index.start is None and index.stop is None:
return self._values[:]
return values[:]
v_start = int(self._values[0]) if index.start is None else int(index.start)
v_stop = (self._values[-1] + 1) if index.stop is None else int(index.stop)
v_start = int(values[0]) if index.start is None else int(index.start)
v_stop = (values[-1] + 1) if index.stop is None else int(index.stop)
if v_start > v_stop or v_start > self._values[-1] or v_stop <= self._values[0]:
if v_start > v_stop or v_start > values[-1] or v_stop <= values[0]:
return []
if v_start <= self._values[0] and v_stop > self._values[-1]:
return self._values[:]
if v_start <= values[0] and v_stop > values[-1]:
return values[:]
start_index = 0
stop_index = len(self._values)
for i, value in enumerate(self._values):
stop_index = len(values)
for i, value in enumerate(values):
if value < v_start:
start_index = i + 1
elif index.stop is None:
@ -176,7 +187,7 @@ class NamedInts:
stop_index = i
break
return self._values[start_index:stop_index]
return values[start_index:stop_index]
def __setitem__(self, index, name):
assert isinstance(index, int), type(index)
@ -193,7 +204,9 @@ class NamedInts:
if int(value) in self._indexed:
raise ValueError('%d (%s) already known' % (int(value), value))
self._values = sorted(self._values + [value])
self._values.append(value)
self._is_sorted = False
self._sort_values()
self.__dict__[str(value)] = value
self._indexed[int(value)] = value
@ -216,6 +229,15 @@ class NamedInts:
return NamedInts(**self.__dict__, **other.__dict__)
class UnsortedNamedInts(NamedInts):
def _sort_values(self):
pass
def __or__(self, other):
c = UnsortedNamedInts if isinstance(other, UnsortedNamedInts) else NamedInts
return c(**self.__dict__, **other.__dict__)
def strhex(x):
assert x is not None
"""Produce a hex-string representation of a sequence of bytes."""

View File

@ -31,6 +31,7 @@ from .common import FirmwareInfo as _FirmwareInfo
from .common import KwException as _KwException
from .common import NamedInt as _NamedInt
from .common import NamedInts as _NamedInts
from .common import UnsortedNamedInts as _UnsortedNamedInts
from .common import bytes2int as _bytes2int
from .common import pack as _pack
from .common import unpack as _unpack
@ -438,18 +439,18 @@ class ReprogrammableKeyV4(ReprogrammableKey):
return _NamedInt(self._mapped_to, task)
@property
def remappable_to(self) -> List[_NamedInt]:
def remappable_to(self) -> _NamedInts:
self._device.keys._ensure_all_keys_queried()
ret = []
ret = _UnsortedNamedInts()
if self.group_mask != []: # only keys with a non-zero gmask are remappable
ret = [self.default_task] # it should always be possible to map the key to itself
ret[self.default_task] = self.default_task # it should always be possible to map the key to itself
for g in self.group_mask:
g = special_keys.CID_GROUP[str(g)]
for tgt_cid in self._device.keys.group_cids[g]:
tgt_task = str(special_keys.TASK[self._device.keys.cid_to_tid[tgt_cid]])
tgt_task = _NamedInt(tgt_cid, tgt_task)
if tgt_task != self.default_task: # don't put itself in twice
ret.append(tgt_task)
ret[tgt_task] = tgt_task
return ret
@property

View File

@ -23,7 +23,7 @@ from shlex import quote as shlex_quote
from gi.repository import Gdk, GObject, Gtk
from logitech_receiver import diversion as _DIV
from logitech_receiver.common import NamedInt, NamedInts
from logitech_receiver.common import NamedInt, UnsortedNamedInts
from logitech_receiver.diversion import XK_KEYS as _XK_KEYS
from logitech_receiver.diversion import Key as _Key
from logitech_receiver.diversion import buttons as _buttons
@ -1655,10 +1655,16 @@ class SetUI(ActionUI):
Only one label per number is kept.
"""
if isinstance(setting, type) and issubclass(setting, _Setting):
return (getattr(setting, 'choices_universe', None)
or NamedInts()) | (getattr(setting, 'choices_extra', None) or NamedInts())
choices = UnsortedNamedInts()
universe = getattr(setting, 'choices_universe', None)
if universe:
choices |= universe
extra = getattr(setting, 'choices_extra', None)
if extra:
choices |= extra
return choices
settings = cls.ALL_SETTINGS.get(setting, [])
choices = NamedInts()
choices = UnsortedNamedInts()
for s in settings:
choices |= cls._all_choices(s)
return choices
@ -1670,10 +1676,11 @@ class SetUI(ActionUI):
val_class = setting.validator_class if setting else None
kind = val_class.kind if val_class else None
if kind in cls.MULTIPLE:
keys = NamedInts()
keys = UnsortedNamedInts()
for s in settings:
keys |= getattr(s, 'choices_universe' if kind == _SKIND.multiple_toggle else 'keys_universe',
None) or NamedInts()
universe = getattr(s, 'choices_universe' if kind == _SKIND.multiple_toggle else 'keys_universe', None)
if universe:
keys |= universe
# only one key per number is used
else:
keys = None