ui: better startup behavior for LED effect settings
This commit is contained in:
parent
73d091c86f
commit
7c441cc652
|
@ -180,6 +180,8 @@ Some mice store one or more profiles, which control aspects of the behavior of t
|
||||||
|
|
||||||
Profiles can control the rate at which the mouse reports movement, the resolution of the the movement reports, what the mouse buttons do, and its LED effects. Solaar can dump the entire set of profiles into a YAML file can load an entire set of profiles from a file. Users can edit the file to effect changes to the profiles. Solaar has a setting that switches between profiles or disables all profiles. When switching between profiles or using a button to change resolution Solaar keeps track of the changes in the settings for these features.
|
Profiles can control the rate at which the mouse reports movement, the resolution of the the movement reports, what the mouse buttons do, and its LED effects. Solaar can dump the entire set of profiles into a YAML file can load an entire set of profiles from a file. Users can edit the file to effect changes to the profiles. Solaar has a setting that switches between profiles or disables all profiles. When switching between profiles or using a button to change resolution Solaar keeps track of the changes in the settings for these features.
|
||||||
|
|
||||||
|
When profiles are active changes cannot be made to the Report Rate setting. Changes can be made to the Sensitivity setting and to LED settings. To keep the profile values make these setting ignored.
|
||||||
|
|
||||||
A profile file has some bookkeeping information, including profile version and the name of the device, and a sequence of profiles.
|
A profile file has some bookkeeping information, including profile version and the name of the device, and a sequence of profiles.
|
||||||
|
|
||||||
Each profile has the following fields:
|
Each profile has the following fields:
|
||||||
|
|
|
@ -1172,8 +1172,8 @@ LEDParamSize = {
|
||||||
LEDParam.form: 1
|
LEDParam.form: 1
|
||||||
}
|
}
|
||||||
LEDEffects = {
|
LEDEffects = {
|
||||||
0x0: [_NamedInt(0x0, _('Disable')), {}],
|
0x0: [_NamedInt(0x0, _('Disabled')), {}],
|
||||||
0x1: [_NamedInt(0x1, _('Fixed')), {
|
0x1: [_NamedInt(0x1, _('Static')), {
|
||||||
LEDParam.color: 0,
|
LEDParam.color: 0,
|
||||||
LEDParam.ramp: 3
|
LEDParam.ramp: 3
|
||||||
}],
|
}],
|
||||||
|
|
|
@ -286,7 +286,7 @@ class Setting:
|
||||||
reply = self._rw.read(self._device)
|
reply = self._rw.read(self._device)
|
||||||
if reply:
|
if reply:
|
||||||
self._value = self._validator.validate_read(reply)
|
self._value = self._validator.validate_read(reply)
|
||||||
if self._device.persister and self.name not in self._device.persister:
|
if self._value is not None and self._device.persister and self.name not in self._device.persister:
|
||||||
# Don't update the persister if it already has a value,
|
# Don't update the persister if it already has a value,
|
||||||
# otherwise the first read might overwrite the value we wanted.
|
# otherwise the first read might overwrite the value we wanted.
|
||||||
self._device.persister[self.name] = self._value if self.persist else None
|
self._device.persister[self.name] = self._value if self.persist else None
|
||||||
|
@ -1216,15 +1216,17 @@ class HeteroValidator(Validator):
|
||||||
def build(cls, setting_class, device, **kwargs):
|
def build(cls, setting_class, device, **kwargs):
|
||||||
return cls(**kwargs)
|
return cls(**kwargs)
|
||||||
|
|
||||||
def __init__(self, data_class=None, options=None):
|
def __init__(self, data_class=None, options=None, readable=True):
|
||||||
assert data_class is not None and options is not None
|
assert data_class is not None and options is not None
|
||||||
self.data_class = data_class
|
self.data_class = data_class
|
||||||
self.options = options
|
self.options = options
|
||||||
|
self.readable = readable
|
||||||
self.needs_current_value = False
|
self.needs_current_value = False
|
||||||
|
|
||||||
def validate_read(self, reply_bytes):
|
def validate_read(self, reply_bytes):
|
||||||
reply_value = self.data_class.from_bytes(reply_bytes, options=self.options)
|
if self.readable:
|
||||||
return reply_value
|
reply_value = self.data_class.from_bytes(reply_bytes, options=self.options)
|
||||||
|
return reply_value
|
||||||
|
|
||||||
def prepare_write(self, new_value, current_value=None):
|
def prepare_write(self, new_value, current_value=None):
|
||||||
new_value.options = self.options
|
new_value.options = self.options
|
||||||
|
|
|
@ -1450,7 +1450,7 @@ class LEDZoneSetting(_Setting):
|
||||||
feature = _F.COLOR_LED_EFFECTS
|
feature = _F.COLOR_LED_EFFECTS
|
||||||
color_field = {'name': _LEDP.color, 'kind': _KIND.choice, 'label': None, 'choices': colors}
|
color_field = {'name': _LEDP.color, 'kind': _KIND.choice, 'label': None, 'choices': colors}
|
||||||
speed_field = {'name': _LEDP.speed, 'kind': _KIND.range, 'label': _('Speed'), 'min': 0, 'max': 255}
|
speed_field = {'name': _LEDP.speed, 'kind': _KIND.range, 'label': _('Speed'), 'min': 0, 'max': 255}
|
||||||
period_field = {'name': _LEDP.period, 'kind': _KIND.range, 'label': _('Period'), 'min': 0, 'max': 5000}
|
period_field = {'name': _LEDP.period, 'kind': _KIND.range, 'label': _('Period'), 'min': 100, 'max': 5000}
|
||||||
intensity_field = {'name': _LEDP.intensity, 'kind': _KIND.range, 'label': _('Intensity'), 'min': 0, 'max': 100}
|
intensity_field = {'name': _LEDP.intensity, 'kind': _KIND.range, 'label': _('Intensity'), 'min': 0, 'max': 100}
|
||||||
ramp_field = {'name': _LEDP.ramp, 'kind': _KIND.choice, 'label': _('Ramp'), 'choices': _hidpp20.LEDRampChoices}
|
ramp_field = {'name': _LEDP.ramp, 'kind': _KIND.choice, 'label': _('Ramp'), 'choices': _hidpp20.LEDRampChoices}
|
||||||
# form_field = { 'name': _LEDP.form, 'kind': _KIND.choice, 'label': _('Form'), 'choices': _hidpp20.LEDFormChoices }
|
# form_field = { 'name': _LEDP.form, 'kind': _KIND.choice, 'label': _('Form'), 'choices': _hidpp20.LEDFormChoices }
|
||||||
|
@ -1458,12 +1458,12 @@ class LEDZoneSetting(_Setting):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def build(cls, device):
|
def build(cls, device):
|
||||||
zone_infos = _hidpp20.LEDEffectsInfo(device).zones
|
infos = _hidpp20.LEDEffectsInfo(device)
|
||||||
settings = []
|
settings = []
|
||||||
for zone in zone_infos:
|
for zone in infos.zones:
|
||||||
prefix = zone.index.to_bytes(1)
|
prefix = zone.index.to_bytes(1)
|
||||||
rw = _FeatureRW(_F.COLOR_LED_EFFECTS, read_fnid=0xE0, write_fnid=0x30, prefix=prefix)
|
rw = _FeatureRW(_F.COLOR_LED_EFFECTS, read_fnid=0xE0, write_fnid=0x30, prefix=prefix)
|
||||||
validator = _HeteroV(data_class=_hidpp20.LEDEffectIndexed, options=zone.effects)
|
validator = _HeteroV(data_class=_hidpp20.LEDEffectIndexed, options=zone.effects, readable=infos.readable)
|
||||||
setting = cls(device, rw, validator)
|
setting = cls(device, rw, validator)
|
||||||
setting.name = cls.name + str(int(zone.location))
|
setting.name = cls.name + str(int(zone.location))
|
||||||
setting.label = _('LEDs') + ' ' + str(_hidpp20.LEDZoneLocations[zone.location])
|
setting.label = _('LEDs') + ' ' + str(_hidpp20.LEDZoneLocations[zone.location])
|
||||||
|
|
|
@ -42,7 +42,7 @@ def _read_async(setting, force_read, sbox, device_is_online, sensitive):
|
||||||
|
|
||||||
def _do_read(s, force, sb, online, sensitive):
|
def _do_read(s, force, sb, online, sensitive):
|
||||||
v = s.read(not force)
|
v = s.read(not force)
|
||||||
GLib.idle_add(_update_setting_item, sb, v, online, sensitive, priority=99)
|
GLib.idle_add(_update_setting_item, sb, v, online, sensitive, True, priority=99)
|
||||||
|
|
||||||
_ui_async(_do_read, setting, force_read, sbox, device_is_online, sensitive)
|
_ui_async(_do_read, setting, force_read, sbox, device_is_online, sensitive)
|
||||||
|
|
||||||
|
@ -124,7 +124,8 @@ class ToggleControl(Gtk.Switch, Control):
|
||||||
self.connect('notify::active', self.changed)
|
self.connect('notify::active', self.changed)
|
||||||
|
|
||||||
def set_value(self, value):
|
def set_value(self, value):
|
||||||
self.set_state(value)
|
if value is not None:
|
||||||
|
self.set_state(value)
|
||||||
|
|
||||||
def get_value(self):
|
def get_value(self):
|
||||||
return self.get_state()
|
return self.get_state()
|
||||||
|
@ -179,7 +180,8 @@ class ChoiceControlLittle(Gtk.ComboBoxText, Control):
|
||||||
return int(self.get_active_id()) if self.get_active_id() is not None else None
|
return int(self.get_active_id()) if self.get_active_id() is not None else None
|
||||||
|
|
||||||
def set_value(self, value):
|
def set_value(self, value):
|
||||||
self.set_active_id(str(int(value)))
|
if value is not None:
|
||||||
|
self.set_active_id(str(int(value)))
|
||||||
|
|
||||||
def get_choice(self):
|
def get_choice(self):
|
||||||
id = self.get_value()
|
id = self.get_value()
|
||||||
|
@ -217,7 +219,8 @@ class ChoiceControlBig(Gtk.Entry, Control):
|
||||||
return int(choice) if choice is not None else None
|
return int(choice) if choice is not None else None
|
||||||
|
|
||||||
def set_value(self, value):
|
def set_value(self, value):
|
||||||
self.set_text(str(next((x for x in self.choices if x == value), None)))
|
if value is not None:
|
||||||
|
self.set_text(str(next((x for x in self.choices if x == value), None)))
|
||||||
|
|
||||||
def get_choice(self):
|
def get_choice(self):
|
||||||
key = self.get_text()
|
key = self.get_text()
|
||||||
|
@ -264,6 +267,8 @@ class MapChoiceControl(Gtk.HBox, Control):
|
||||||
return self.valueBox.get_value()
|
return self.valueBox.get_value()
|
||||||
|
|
||||||
def set_value(self, value):
|
def set_value(self, value):
|
||||||
|
if value is None:
|
||||||
|
return
|
||||||
self.valueBox.set_sensitive(self.get_sensitive())
|
self.valueBox.set_sensitive(self.get_sensitive())
|
||||||
key = int(self.keyBox.get_active_id())
|
key = int(self.keyBox.get_active_id())
|
||||||
if value.get(key) is not None:
|
if value.get(key) is not None:
|
||||||
|
@ -370,6 +375,8 @@ class MultipleToggleControl(MultipleControl):
|
||||||
_write_async(self.sbox.setting, new_state, self.sbox, key=int(key))
|
_write_async(self.sbox.setting, new_state, self.sbox, key=int(key))
|
||||||
|
|
||||||
def set_value(self, value):
|
def set_value(self, value):
|
||||||
|
if value is None:
|
||||||
|
return
|
||||||
active = 0
|
active = 0
|
||||||
total = len(self._label_control_pairs)
|
total = len(self._label_control_pairs)
|
||||||
to_join = []
|
to_join = []
|
||||||
|
@ -452,6 +459,8 @@ class MultipleRangeControl(MultipleControl):
|
||||||
_write_async(self.sbox.setting, self.sbox.setting._value[int(item)], self.sbox, key=int(item))
|
_write_async(self.sbox.setting, self.sbox.setting._value[int(item)], self.sbox, key=int(item))
|
||||||
|
|
||||||
def set_value(self, value):
|
def set_value(self, value):
|
||||||
|
if value is None:
|
||||||
|
return
|
||||||
b = ''
|
b = ''
|
||||||
n = 0
|
n = 0
|
||||||
for ch in self._items:
|
for ch in self._items:
|
||||||
|
@ -512,6 +521,8 @@ class PackedRangeControl(MultipleRangeControl):
|
||||||
_write_async(self.sbox.setting, self.sbox.setting._value[int(item)], self.sbox, key=int(item))
|
_write_async(self.sbox.setting, self.sbox.setting._value[int(item)], self.sbox, key=int(item))
|
||||||
|
|
||||||
def set_value(self, value):
|
def set_value(self, value):
|
||||||
|
if value is None:
|
||||||
|
return
|
||||||
b = ''
|
b = ''
|
||||||
n = len(self._items)
|
n = len(self._items)
|
||||||
for h in self._items:
|
for h in self._items:
|
||||||
|
@ -527,7 +538,7 @@ class PackedRangeControl(MultipleRangeControl):
|
||||||
self._button.set_tooltip_text(b)
|
self._button.set_tooltip_text(b)
|
||||||
|
|
||||||
|
|
||||||
# control an ID key that determines what else to show
|
# control with an ID key that determines what else to show
|
||||||
class HeteroKeyControl(Gtk.HBox, Control):
|
class HeteroKeyControl(Gtk.HBox, Control):
|
||||||
|
|
||||||
def __init__(self, sbox, delegate=None):
|
def __init__(self, sbox, delegate=None):
|
||||||
|
@ -538,6 +549,7 @@ class HeteroKeyControl(Gtk.HBox, Control):
|
||||||
if item['label']:
|
if item['label']:
|
||||||
item_lblbox = Gtk.Label(item['label'])
|
item_lblbox = Gtk.Label(item['label'])
|
||||||
self.pack_start(item_lblbox, False, False, 0)
|
self.pack_start(item_lblbox, False, False, 0)
|
||||||
|
item_lblbox.set_visible(False)
|
||||||
else:
|
else:
|
||||||
item_lblbox = None
|
item_lblbox = None
|
||||||
if item['kind'] == _SETTING_KIND.choice:
|
if item['kind'] == _SETTING_KIND.choice:
|
||||||
|
@ -555,6 +567,7 @@ class HeteroKeyControl(Gtk.HBox, Control):
|
||||||
item_box.set_increments(1, 5)
|
item_box.set_increments(1, 5)
|
||||||
item_box.connect('value-changed', self.changed)
|
item_box.connect('value-changed', self.changed)
|
||||||
self.pack_start(item_box, True, True, 0)
|
self.pack_start(item_box, True, True, 0)
|
||||||
|
item_box.set_visible(False)
|
||||||
self._items[str(item['name'])] = (item_lblbox, item_box)
|
self._items[str(item['name'])] = (item_lblbox, item_box)
|
||||||
|
|
||||||
def get_value(self):
|
def get_value(self):
|
||||||
|
@ -566,11 +579,12 @@ class HeteroKeyControl(Gtk.HBox, Control):
|
||||||
|
|
||||||
def set_value(self, value):
|
def set_value(self, value):
|
||||||
self.set_sensitive(False)
|
self.set_sensitive(False)
|
||||||
for k, v in value.__dict__.items():
|
if value is not None:
|
||||||
if k in self._items:
|
for k, v in value.__dict__.items():
|
||||||
(lblbox, box) = self._items[k]
|
if k in self._items:
|
||||||
box.set_value(v)
|
(lblbox, box) = self._items[k]
|
||||||
self.setup_visibles(value.ID)
|
box.set_value(v)
|
||||||
|
self.setup_visibles(value.ID if value is not None else 0)
|
||||||
|
|
||||||
def setup_visibles(self, ID):
|
def setup_visibles(self, ID):
|
||||||
fields = self.sbox.setting.fields_map[ID][1] if ID in self.sbox.setting.fields_map else {}
|
fields = self.sbox.setting.fields_map[ID][1] if ID in self.sbox.setting.fields_map else {}
|
||||||
|
@ -694,10 +708,10 @@ def _create_sbox(s, device):
|
||||||
return sbox
|
return sbox
|
||||||
|
|
||||||
|
|
||||||
def _update_setting_item(sbox, value, is_online=True, sensitive=True):
|
def _update_setting_item(sbox, value, is_online=True, sensitive=True, nullOK=False):
|
||||||
# sbox._spinner.set_visible(False) # don't repack item box
|
# sbox._spinner.set_visible(False) # don't repack item box
|
||||||
sbox._spinner.stop()
|
sbox._spinner.stop()
|
||||||
if value is None:
|
if value is None and not nullOK:
|
||||||
sbox._control.set_sensitive(False)
|
sbox._control.set_sensitive(False)
|
||||||
_change_icon(False, sbox._change_icon)
|
_change_icon(False, sbox._change_icon)
|
||||||
sbox._failed.set_visible(is_online)
|
sbox._failed.set_visible(is_online)
|
||||||
|
|
Loading…
Reference in New Issue