diff --git a/lib/solaar/ui/__init__.py b/lib/solaar/ui/__init__.py index bfb3805f..df7f0c00 100644 --- a/lib/solaar/ui/__init__.py +++ b/lib/solaar/ui/__init__.py @@ -1,4 +1,5 @@ ## Copyright (C) 2012-2013 Daniel Pavel +## Copyright (C) 2014-2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/ ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -39,18 +40,8 @@ from gi.repository import Gtk # NOQA: E402 logger = logging.getLogger(__name__) -# -# -# - assert Gtk.get_major_version() > 2, "Solaar requires Gtk 3 python bindings" -GLib.threads_init() - -# -# -# - def _startup(app, startup_hook, use_tray, show_window): if logger.isEnabledFor(logging.DEBUG): @@ -113,11 +104,6 @@ def run_loop(startup_hook, shutdown_hook, use_tray, show_window): application.run() -# -# -# - - def _status_changed(device, alert, reason, refresh=False): assert device is not None if logger.isEnabledFor(logging.DEBUG): diff --git a/lib/solaar/ui/action.py b/lib/solaar/ui/action.py index 0db9a605..4e4c777d 100644 --- a/lib/solaar/ui/action.py +++ b/lib/solaar/ui/action.py @@ -1,4 +1,5 @@ ## Copyright (C) 2012-2013 Daniel Pavel +## Copyright (C) 2014-2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/ ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -22,16 +23,24 @@ from solaar.i18n import _ from . import pair_window from .common import error_dialog -# import logging -# logger = logging.getLogger(__name__) -# -# -# +def make_image_menu_item(label, icon_name, function, *args): + box = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 6) + label = Gtk.Label(label=label) + icon = Gtk.Image.new_from_icon_name(icon_name, Gtk.IconSize.LARGE_TOOLBAR) if icon_name is not None else Gtk.Image() + box.add(icon) + box.add(label) + menu_item = Gtk.MenuItem() + menu_item.add(box) + menu_item.show_all() + menu_item.connect("activate", function, *args) + menu_item.label = label + menu_item.icon = icon + return menu_item def make(name, label, function, stock_id=None, *args): - action = Gtk.Action(name, label, label, None) + action = Gtk.Action(name=name, label=label, tooltip=label, stock_id=None) action.set_icon_name(name) if stock_id is not None: action.set_stock_id(stock_id) @@ -41,7 +50,7 @@ def make(name, label, function, stock_id=None, *args): def make_toggle(name, label, function, stock_id=None, *args): - action = Gtk.ToggleAction(name, label, label, None) + action = Gtk.ToggleAction(name=name, label=label, tooltip=label, stock_id=None) action.set_icon_name(name) if stock_id is not None: action.set_stock_id(stock_id) @@ -49,19 +58,6 @@ def make_toggle(name, label, function, stock_id=None, *args): return action -# -# -# - -# def _toggle_notifications(action): -# if action.get_active(): -# notify.init('Solaar') -# else: -# notify.uninit() -# action.set_sensitive(notify.available) -# toggle_notifications = make_toggle('notifications', 'Notifications', _toggle_notifications) - - def pair(window, receiver): assert receiver assert receiver.kind is None @@ -80,7 +76,11 @@ def unpair(window, device): assert device.kind is not None qdialog = Gtk.MessageDialog( - window, 0, Gtk.MessageType.QUESTION, Gtk.ButtonsType.NONE, _("Unpair") + " " + device.name + " ?" + transient_for=window, + flags=0, + message_type=Gtk.MessageType.QUESTION, + buttons=Gtk.ButtonsType.NONE, + text=_("Unpair") + " " + device.name + " ?", ) qdialog.set_icon_name("remove") qdialog.add_button(_("Cancel"), Gtk.ResponseType.CANCEL) @@ -95,5 +95,4 @@ def unpair(window, device): try: del receiver[device_number] except Exception: - # logger.exception("unpairing %s", device) error_dialog("unpair", device) diff --git a/lib/solaar/ui/config_panel.py b/lib/solaar/ui/config_panel.py index 87311040..701f4534 100644 --- a/lib/solaar/ui/config_panel.py +++ b/lib/solaar/ui/config_panel.py @@ -1,4 +1,5 @@ ## Copyright (C) 2012-2013 Daniel Pavel +## Copyright (C) 2014-2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/ ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -37,10 +38,6 @@ from gi.repository import Gtk # NOQA: E402 logger = logging.getLogger(__name__) -# -# -# - def _read_async(setting, force_read, sbox, device_is_online, sensitive): def _do_read(s, force, sb, online, sensitive): @@ -72,11 +69,6 @@ def _write_async(setting, value, sbox, sensitive=True, key=None): _ui_async(_do_write, setting, value, sbox, key) -# -# -# - - class ComboBoxText(Gtk.ComboBoxText): def get_value(self): return int(self.get_active_id()) @@ -303,8 +295,7 @@ class MultipleControl(Gtk.ListBox, Control): self.set_no_show_all(True) self._showing = True self.setup(sbox.setting) # set up the data and boxes for the sub-controls - btn = Gtk.Button(button_label) - btn.set_alignment(1.0, 0.5) + btn = Gtk.Button(label=button_label) btn.connect("clicked", self.toggle_display) self._button = btn hbox = Gtk.HBox(homogeneous=False, spacing=6) @@ -349,15 +340,14 @@ class MultipleToggleControl(MultipleControl): l1, l2 = setting._labels.get(k, (None, None)) lbl_text = l1 if l1 else lbl_text lbl_tooltip = l2 if l2 else lbl_tooltip - lbl = Gtk.Label(lbl_text) + lbl = Gtk.Label(label=lbl_text) h.set_tooltip_text(lbl_tooltip or " ") control = Gtk.Switch() control._setting_key = int(k) control.connect("notify::active", self.toggle_notify) h.pack_start(lbl, False, False, 0) h.pack_end(control, False, False, 0) - lbl.set_alignment(0.0, 0.5) - lbl.set_margin_left(30) + lbl.set_margin_start(30) self.add(h) self._label_control_pairs.append((lbl, control)) @@ -397,7 +387,7 @@ class MultipleRangeControl(MultipleControl): l1, l2 = setting._labels.get(int(item), (None, None)) lbl_text = l1 if l1 else lbl_text lbl_tooltip = l2 if l2 else lbl_tooltip - item_lbl = Gtk.Label(lbl_text) + item_lbl = Gtk.Label(label=lbl_text) self.add(item_lbl) self.set_tooltip_text(lbl_tooltip or " ") item_lb = Gtk.ListBox() @@ -411,11 +401,10 @@ class MultipleRangeControl(MultipleControl): l1, l2 = setting._labels_sub.get(str(sub_item), (None, None)) lbl_text = l1 if l1 else lbl_text lbl_tooltip = l2 if l2 else lbl_tooltip - sub_item_lbl = Gtk.Label(lbl_text) + sub_item_lbl = Gtk.Label(label=lbl_text) h.set_tooltip_text(lbl_tooltip or " ") h.pack_start(sub_item_lbl, False, False, 0) - sub_item_lbl.set_margin_left(30) - sub_item_lbl.set_alignment(0.0, 0.5) + sub_item_lbl.set_margin_start(30) if sub_item.widget == "Scale": control = Gtk.Scale.new_with_range(Gtk.Orientation.HORIZONTAL, sub_item.minimum, sub_item.maximum, 1) control.set_round_digits(0) @@ -484,7 +473,7 @@ class PackedRangeControl(MultipleRangeControl): self._items = [] for item in range(validator.count): h = Gtk.HBox(homogeneous=False, spacing=0) - lbl = Gtk.Label(str(validator.keys[item])) + lbl = Gtk.Label(label=str(validator.keys[item])) control = Gtk.Scale.new_with_range(Gtk.Orientation.HORIZONTAL, validator.min_value, validator.max_value, 1) control.set_round_digits(0) control.set_digits(0) @@ -493,8 +482,7 @@ class PackedRangeControl(MultipleRangeControl): h.pack_end(control, True, True, 0) h._setting_item = validator.keys[item] h.control = control - lbl.set_alignment(0.0, 0.5) - lbl.set_margin_left(30) + lbl.set_margin_start(30) self.add(h) self._items.append(h) @@ -539,7 +527,7 @@ class HeteroKeyControl(Gtk.HBox, Control): self._items = {} for item in sbox.setting.possible_fields: if item["label"]: - item_lblbox = Gtk.Label(item["label"]) + item_lblbox = Gtk.Label(label=item["label"]) self.pack_start(item_lblbox, False, False, 0) item_lblbox.set_visible(False) else: @@ -651,8 +639,7 @@ def _create_sbox(s, device): sbox.kind = s.kind if s.description: sbox.set_tooltip_text(s.description) - lbl = Gtk.Label(s.label) - lbl.set_alignment(0.0, 0.5) + lbl = Gtk.Label(label=s.label) label = Gtk.EventBox() label.add(lbl) spinner = Gtk.Spinner() @@ -723,10 +710,6 @@ def _disable_listbox_highlight_bg(lb): child.override_background_color(Gtk.StateFlags.PRELIGHT, colour) -# -# -# - # config panel _box = None _items = {} diff --git a/lib/solaar/ui/diversion_rules.py b/lib/solaar/ui/diversion_rules.py index ceb998bc..8836acdc 100644 --- a/lib/solaar/ui/diversion_rules.py +++ b/lib/solaar/ui/diversion_rules.py @@ -1147,7 +1147,7 @@ class SetValueControl(Gtk.HBox): 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(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) @@ -1303,7 +1303,7 @@ class _DeviceUI: self.label = Gtk.Label(valign=Gtk.Align.CENTER, hexpand=True) self.label.set_text(self.label_text) self.widgets[self.label] = (0, 0, 5, 1) - lbl = Gtk.Label(_("Device"), halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER, hexpand=True) + lbl = Gtk.Label(label=_("Device"), halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER, hexpand=True) self.widgets[lbl] = (0, 1, 1, 1) self.device_field = SmartComboBox( [], @@ -1405,7 +1405,7 @@ class _SettingWithValueUI: self.widgets[self.label] = (0, 0, 5, 1) m = 20 - lbl = Gtk.Label(_("Device"), halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER, hexpand=True, margin_top=m) + lbl = Gtk.Label(label=_("Device"), halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER, hexpand=True, margin_top=m) self.widgets[lbl] = (0, 1, 1, 1) self.device_field = SmartComboBox( [], @@ -1423,7 +1423,7 @@ class _SettingWithValueUI: self.device_field.connect("changed", self._on_update) self.widgets[self.device_field] = (1, 1, 1, 1) - lbl = Gtk.Label(_("Setting"), halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER, hexpand=True, vexpand=False) + lbl = Gtk.Label(label=_("Setting"), halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER, hexpand=True, vexpand=False) self.widgets[lbl] = (0, 2, 1, 1) self.setting_field = SmartComboBox([(s[0].name, s[0].label) for s in self.ALL_SETTINGS.values()]) self.setting_field.set_valign(Gtk.Align.CENTER) @@ -1431,7 +1431,9 @@ class _SettingWithValueUI: self.setting_field.connect("changed", self._on_update) self.widgets[self.setting_field] = (1, 2, 1, 1) - self.value_lbl = Gtk.Label(_("Value"), halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER, hexpand=True, vexpand=False) + self.value_lbl = Gtk.Label( + label=_("Value"), halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER, hexpand=True, vexpand=False + ) self.widgets[self.value_lbl] = (2, 2, 1, 1) self.value_field = SetValueControl(self._on_update, accept_toggle=self.ACCEPT_TOGGLE) self.value_field.set_valign(Gtk.Align.CENTER) @@ -1439,7 +1441,7 @@ class _SettingWithValueUI: self.widgets[self.value_field] = (3, 2, 1, 1) self.key_lbl = Gtk.Label( - _("Item"), halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER, hexpand=True, vexpand=False, margin_top=m + label=_("Item"), halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER, hexpand=True, vexpand=False, margin_top=m ) self.key_lbl.hide() self.widgets[self.key_lbl] = (2, 1, 1, 1) diff --git a/lib/solaar/ui/icons.py b/lib/solaar/ui/icons.py index f6b847e0..1341b48a 100644 --- a/lib/solaar/ui/icons.py +++ b/lib/solaar/ui/icons.py @@ -1,4 +1,5 @@ ## Copyright (C) 2012-2013 Daniel Pavel +## Copyright (C) 2014-2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/ ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -22,14 +23,7 @@ import solaar.gtk as gtk logger = logging.getLogger(__name__) -# -# -# - -_LARGE_SIZE = 64 -Gtk.IconSize.LARGE = Gtk.icon_size_register("large", _LARGE_SIZE, _LARGE_SIZE) -# Gtk.IconSize.XLARGE = Gtk.icon_size_register('x-large', _LARGE_SIZE * 2, _LARGE_SIZE * 2) - +LARGE_SIZE = Gtk.IconSize.DIALOG # was 64 TRAY_INIT = "solaar-init" TRAY_OKAY = "solaar" TRAY_ATTENTION = "solaar-attention" @@ -58,11 +52,6 @@ def _init_icon_paths(): gtk.battery_icons_style = "solaar" -# -# -# - - def battery(level=None, charging=False): icon_name = _battery_icon_name(level, charging) if not _default_theme.has_icon(icon_name): @@ -93,85 +82,55 @@ def _battery_icon_name(level, charging): ) -# -# -# - - def lux(level=None): if level is None or level < 0: return "light_unknown" return "solaar-light_%03d" % (20 * ((level + 50) // 100)) -# -# -# - _ICON_SETS = {} def device_icon_set(name="_", kind=None): icon_set = _ICON_SETS.get(name) if icon_set is None: - icon_set = Gtk.IconSet.new() - _ICON_SETS[name] = icon_set - - # names of possible icons, in reverse order of likelihood - # the theme will hopefully pick up the most appropriate - names = ["preferences-desktop-peripherals"] + # names of possible icons, in reverse desirability + icon_set = ["preferences-desktop-peripherals"] if kind: if str(kind) == "numpad": - names += ("input-keyboard", "input-dialpad") + icon_set += ("input-keyboard", "input-dialpad") elif str(kind) == "touchpad": - names += ("input-mouse", "input-tablet") + icon_set += ("input-mouse", "input-tablet") elif str(kind) == "trackball": - names += ("input-mouse",) + icon_set += ("input-mouse",) elif str(kind) == "headset": - names += ("audio-headphones", "audio-headset") - names += ("input-" + str(kind),) - # names += (name.replace(' ', '-'),) - - source = Gtk.IconSource.new() - for n in names: - source.set_icon_name(n) - icon_set.add_source(source) - icon_set.names = names - + icon_set += ("audio-headphones", "audio-headset") + icon_set += ("input-" + str(kind),) + # icon_set += (name.replace(' ', '-'),) + _ICON_SETS[name] = icon_set return icon_set -def device_icon_file(name, kind=None, size=_LARGE_SIZE): - _init_icon_paths() - - icon_set = device_icon_set(name, kind) - assert icon_set - for n in reversed(icon_set.names): - if _default_theme.has_icon(n): - return _default_theme.lookup_icon(n, size, 0).get_filename() +def device_icon_file(name, kind=None, size=LARGE_SIZE): + icon_name = device_icon_name(name, kind) + return _default_theme.lookup_icon(icon_name, size, 0).get_filename() if icon_name is not None else None def device_icon_name(name, kind=None): _init_icon_paths() - icon_set = device_icon_set(name, kind) assert icon_set - for n in reversed(icon_set.names): + for n in reversed(icon_set): if _default_theme.has_icon(n): return n -def icon_file(name, size=_LARGE_SIZE): +def icon_file(name, size=LARGE_SIZE): _init_icon_paths() - # has_icon() somehow returned False while lookup_icon returns non-None. - # I guess it happens because share/solaar/icons/ has no hicolor and - # resolution subdirs + # I guess it happens because share/solaar/icons/ has no hicolor and resolution subdirs theme_icon = _default_theme.lookup_icon(name, size, 0) if theme_icon: file_name = theme_icon.get_filename() - # if logger.isEnabledFor(logging.DEBUG): - # logger.debug("icon %s(%d) => %s", name, size, file_name) return file_name - logger.warning("icon %s(%d) not found in current theme", name, size) diff --git a/lib/solaar/ui/pair_window.py b/lib/solaar/ui/pair_window.py index 4236da67..5899be32 100644 --- a/lib/solaar/ui/pair_window.py +++ b/lib/solaar/ui/pair_window.py @@ -37,27 +37,27 @@ address = kind = authentication = name = passcode = None def _create_page(assistant, kind, header=None, icon_name=None, text=None): - p = Gtk.VBox(False, 8) + p = Gtk.VBox(homogeneous=False, spacing=8) assistant.append_page(p) assistant.set_page_type(p, kind) if header: - item = Gtk.HBox(False, 16) + item = Gtk.HBox(homogeneous=False, spacing=16) p.pack_start(item, False, True, 0) - label = Gtk.Label(header) - label.set_alignment(0, 0) + label = Gtk.Label(label=header) + # deprecated - not needed label.set_alignment(0, 0) label.set_line_wrap(True) item.pack_start(label, True, True, 0) if icon_name: icon = Gtk.Image.new_from_icon_name(icon_name, Gtk.IconSize.DIALOG) - icon.set_alignment(1, 0) + # deprecated - not needed icon.set_alignment(1, 0) item.pack_start(icon, False, False, 0) if text: - label = Gtk.Label(text) - label.set_alignment(0, 0) + label = Gtk.Label(label=text) + # deprecated - not needed label.set_alignment(0, 0) label.set_line_wrap(True) p.pack_start(label, False, False, 0) @@ -211,23 +211,23 @@ def _pairing_succeeded(assistant, receiver, device): page = _create_page(assistant, Gtk.AssistantPageType.SUMMARY) - header = Gtk.Label(_("Found a new device:")) - header.set_alignment(0.5, 0) + header = Gtk.Label(label=_("Found a new device:")) + # deprecated - not needed header.set_alignment(0.5, 0) page.pack_start(header, False, False, 0) device_icon = Gtk.Image() - icon_set = _icons.device_icon_set(device.name, device.kind) - device_icon.set_from_icon_set(icon_set, Gtk.IconSize.LARGE) - device_icon.set_alignment(0.5, 1) + icon_name = _icons.device_icon_name(device.name, device.kind) + device_icon.set_from_icon_name(icon_name, _icons.LARGE_SIZE) + # deprecated - not needed device_icon.set_alignment(0.5, 1) page.pack_start(device_icon, True, True, 0) device_label = Gtk.Label() device_label.set_markup(f"{device.name}") - device_label.set_alignment(0.5, 0) + # deprecated - not needed device_label.set_alignment(0.5, 0) page.pack_start(device_label, True, True, 0) - hbox = Gtk.HBox(False, 8) - hbox.pack_start(Gtk.Label(" "), False, False, 0) + hbox = Gtk.HBox(homogeneous=False, spacing=8) + hbox.pack_start(Gtk.Label(label=" "), False, False, 0) hbox.set_property("expand", False) hbox.set_property("halign", Gtk.Align.CENTER) page.pack_start(hbox, False, False, 0) @@ -236,7 +236,7 @@ def _pairing_succeeded(assistant, receiver, device): if assistant.is_drawable(): if device.link_encrypted is False: hbox.pack_start(Gtk.Image.new_from_icon_name("security-low", Gtk.IconSize.MENU), False, False, 0) - hbox.pack_start(Gtk.Label(_("The wireless link is not encrypted") + "!"), False, False, 0) + hbox.pack_start(Gtk.Label(label=_("The wireless link is not encrypted") + "!"), False, False, 0) hbox.show_all() else: return True diff --git a/lib/solaar/ui/rule_actions.py b/lib/solaar/ui/rule_actions.py index 0259559e..e4cc8380 100644 --- a/lib/solaar/ui/rule_actions.py +++ b/lib/solaar/ui/rule_actions.py @@ -45,12 +45,12 @@ class KeyPressUI(ActionUI): self.widgets = {} self.fields = [] self.label = Gtk.Label( - _("Simulate a chorded key click or depress or release.\nOn Wayland requires write access to /dev/uinput."), + label=_("Simulate a chorded key click or depress or release.\nOn Wayland requires write access to /dev/uinput."), halign=Gtk.Align.CENTER, ) self.widgets[self.label] = (0, 0, 5, 1) self.del_btns = [] - self.add_btn = Gtk.Button(_("Add key"), halign=Gtk.Align.CENTER, valign=Gtk.Align.END, hexpand=True) + self.add_btn = Gtk.Button(label=_("Add key"), halign=Gtk.Align.CENTER, valign=Gtk.Align.END, hexpand=True) self.add_btn.connect("clicked", self._clicked_add) self.widgets[self.add_btn] = (1, 1, 1, 1) self.action_clicked_radio = Gtk.RadioButton.new_with_label_from_widget(None, _("Click")) @@ -71,7 +71,7 @@ class KeyPressUI(ActionUI): return field_entry def _create_del_btn(self): - btn = Gtk.Button(_("Delete"), halign=Gtk.Align.CENTER, valign=Gtk.Align.START, hexpand=True) + btn = Gtk.Button(label=_("Delete"), halign=Gtk.Align.CENTER, valign=Gtk.Align.START, hexpand=True) self.del_btns.append(btn) self.widgets[btn] = (len(self.del_btns) - 1, 2, 1, 1) btn.connect("clicked", self._clicked_del, len(self.del_btns) - 1) @@ -144,7 +144,7 @@ class MouseScrollUI(ActionUI): def create_widgets(self): self.widgets = {} self.label = Gtk.Label( - _("Simulate a mouse scroll.\nOn Wayland requires write access to /dev/uinput."), halign=Gtk.Align.CENTER + label=_("Simulate a mouse scroll.\nOn Wayland requires write access to /dev/uinput."), halign=Gtk.Align.CENTER ) self.widgets[self.label] = (0, 0, 4, 1) self.label_x = Gtk.Label(label="x", halign=Gtk.Align.END, valign=Gtk.Align.END, hexpand=True) @@ -200,7 +200,7 @@ class MouseClickUI(ActionUI): def create_widgets(self): self.widgets = {} self.label = Gtk.Label( - _("Simulate a mouse click.\nOn Wayland requires write access to /dev/uinput."), halign=Gtk.Align.CENTER + label=_("Simulate a mouse click.\nOn Wayland requires write access to /dev/uinput."), halign=Gtk.Align.CENTER ) self.widgets[self.label] = (0, 0, 4, 1) self.label_b = Gtk.Label(label=_("Button"), halign=Gtk.Align.END, valign=Gtk.Align.CENTER, hexpand=True) @@ -253,10 +253,10 @@ class ExecuteUI(ActionUI): def create_widgets(self): self.widgets = {} - self.label = Gtk.Label(_("Execute a command with arguments."), halign=Gtk.Align.CENTER) + self.label = Gtk.Label(label=_("Execute a command with arguments."), halign=Gtk.Align.CENTER) self.widgets[self.label] = (0, 0, 5, 1) self.fields = [] - self.add_btn = Gtk.Button(_("Add argument"), halign=Gtk.Align.CENTER, valign=Gtk.Align.END, hexpand=True) + self.add_btn = Gtk.Button(label=_("Add argument"), halign=Gtk.Align.CENTER, valign=Gtk.Align.END, hexpand=True) self.del_btns = [] self.add_btn.connect("clicked", self._clicked_add) self.widgets[self.add_btn] = (1, 1, 1, 1) @@ -270,7 +270,7 @@ class ExecuteUI(ActionUI): return field_entry def _create_del_btn(self): - btn = Gtk.Button(_("Delete"), halign=Gtk.Align.CENTER, valign=Gtk.Align.START, hexpand=True) + btn = Gtk.Button(label=_("Delete"), halign=Gtk.Align.CENTER, valign=Gtk.Align.START, hexpand=True) btn.set_size_request(150, 0) self.del_btns.append(btn) self.widgets[btn] = (len(self.del_btns) - 1, 2, 1, 1) diff --git a/lib/solaar/ui/rule_conditions.py b/lib/solaar/ui/rule_conditions.py index c41c2775..b505691d 100644 --- a/lib/solaar/ui/rule_conditions.py +++ b/lib/solaar/ui/rule_conditions.py @@ -197,7 +197,7 @@ class ModifiersUI(ConditionUI): self.switches = {} for i, m in enumerate(_DIV.MODIFIERS): switch = Gtk.Switch(halign=Gtk.Align.CENTER, valign=Gtk.Align.START, hexpand=True) - label = Gtk.Label(m, halign=Gtk.Align.CENTER, valign=Gtk.Align.END, hexpand=True) + label = Gtk.Label(label=m, halign=Gtk.Align.CENTER, valign=Gtk.Align.END, hexpand=True) self.widgets[label] = (i, 1, 1, 1) self.widgets[switch] = (i, 2, 1, 1) self.labels[m] = label @@ -323,9 +323,9 @@ class TestUI(ConditionUI): self.label = Gtk.Label(valign=Gtk.Align.CENTER, hexpand=True) self.label.set_text(_("Test condition on notification triggering rule processing.")) self.widgets[self.label] = (0, 0, 4, 1) - lbl = Gtk.Label(_("Test"), halign=Gtk.Align.CENTER, valign=Gtk.Align.END, hexpand=False, vexpand=False) + lbl = Gtk.Label(label=_("Test"), halign=Gtk.Align.CENTER, valign=Gtk.Align.END, hexpand=False, vexpand=False) self.widgets[lbl] = (0, 1, 1, 1) - lbl = Gtk.Label(_("Parameter"), halign=Gtk.Align.CENTER, valign=Gtk.Align.END, hexpand=False, vexpand=False) + lbl = Gtk.Label(label=_("Parameter"), halign=Gtk.Align.CENTER, valign=Gtk.Align.END, hexpand=False, vexpand=False) self.widgets[lbl] = (2, 1, 1, 1) self.test = Gtk.ComboBoxText.new_with_entry() @@ -437,7 +437,7 @@ class TestBytesUI(ConditionUI): self.mode_field.pack_start(mode_renderer, True) self.mode_field.add_attribute(mode_renderer, "text", 1) self.widgets[self.mode_field] = (mode_col, 2, 1, 1) - mode_label = Gtk.Label(_("type"), margin_top=20) + mode_label = Gtk.Label(label=_("type"), margin_top=20) self.widgets[mode_label] = (mode_col, 1, 1, 1) for mode_id, mode in TestBytesUI._modes.items(): self.mode_field.get_model().append([mode_id, mode.label]) @@ -447,7 +447,7 @@ class TestBytesUI(ConditionUI): field.set_value(0) field.set_size_request(150, 0) field.connect("value-changed", self._on_update) - label = Gtk.Label(element.label, margin_top=20) + label = Gtk.Label(label=element.label, margin_top=20) self.fields[element.id] = field self.field_labels[element.id] = label self.widgets[label] = (col, 1, 1, 1) @@ -525,12 +525,12 @@ class MouseGestureUI(ConditionUI): self.widgets = {} self.fields = [] self.label = Gtk.Label( - _("Mouse gesture with optional initiating button followed by zero or more mouse movements."), + label=_("Mouse gesture with optional initiating button followed by zero or more mouse movements."), halign=Gtk.Align.CENTER, ) self.widgets[self.label] = (0, 0, 5, 1) self.del_btns = [] - self.add_btn = Gtk.Button(_("Add movement"), halign=Gtk.Align.CENTER, valign=Gtk.Align.END, hexpand=True) + self.add_btn = Gtk.Button(label=_("Add movement"), halign=Gtk.Align.CENTER, valign=Gtk.Align.END, hexpand=True) self.add_btn.connect("clicked", self._clicked_add) self.widgets[self.add_btn] = (1, 1, 1, 1) @@ -545,7 +545,7 @@ class MouseGestureUI(ConditionUI): return field def _create_del_btn(self): - btn = Gtk.Button(_("Delete"), halign=Gtk.Align.CENTER, valign=Gtk.Align.START, hexpand=True) + btn = Gtk.Button(label=_("Delete"), halign=Gtk.Align.CENTER, valign=Gtk.Align.START, hexpand=True) self.del_btns.append(btn) self.widgets[btn] = (len(self.del_btns) - 1, 2, 1, 1) btn.connect("clicked", self._clicked_del, len(self.del_btns) - 1) diff --git a/lib/solaar/ui/tray.py b/lib/solaar/ui/tray.py index c9b47d04..292189fa 100644 --- a/lib/solaar/ui/tray.py +++ b/lib/solaar/ui/tray.py @@ -1,4 +1,5 @@ ## Copyright (C) 2012-2013 Daniel Pavel +## Copyright (C) 2014-2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/ ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -32,39 +33,29 @@ from solaar.i18n import _ from . import icons as _icons from .about import show_window as _show_about_window -from .action import make as _make +from .action import make_image_menu_item from .window import popup as _window_popup from .window import toggle as _window_toggle logger = logging.getLogger(__name__) -# -# constants -# - _TRAY_ICON_SIZE = 48 _MENU_ICON_SIZE = Gtk.IconSize.LARGE_TOOLBAR -# -# -# - def _create_menu(quit_handler): - menu = Gtk.Menu() - # per-device menu entries will be generated as-needed + menu = Gtk.Menu() no_receiver = Gtk.MenuItem.new_with_label(_("No supported device found")) no_receiver.set_sensitive(False) menu.append(no_receiver) menu.append(Gtk.SeparatorMenuItem.new()) - menu.append(_make("help-about", _("About %s") % NAME, _show_about_window, stock_id="help-about").create_menu_item()) - menu.append(_make("application-exit", _("Quit %s") % NAME, quit_handler, stock_id="application-exit").create_menu_item()) + menu.append(make_image_menu_item(_("About %s") % NAME, "help-about", _show_about_window)) + menu.append(make_image_menu_item(_("Quit %s") % NAME, "application-exit", quit_handler)) menu.show_all() - return menu @@ -85,17 +76,13 @@ def _scroll(tray_icon, event, direction=None): if sum(map(lambda i: i[1] is not None, _devices_info)) < 2: # don't bother even trying to scroll if less than two devices return - # scroll events come way too fast (at least 5-6 at once) - # so take a little break between them + # scroll events come way too fast (at least 5-6 at once) so take a little break between them global _last_scroll now = now or _timestamp() if now - _last_scroll < 0.33: # seconds return _last_scroll = now - # if logger.isEnabledFor(logging.DEBUG): - # logger.debug("scroll direction %s", direction) - global _picked_device candidate = None @@ -212,12 +199,6 @@ try: # icon_file = _icons.icon_file(icon_name, _TRAY_ICON_SIZE) _icon.set_icon_full(_icon_file(tray_icon_name), description) - def _update_menu_icon(image_widget, icon_name): - image_widget.set_from_icon_name(icon_name, _MENU_ICON_SIZE) - # icon_file = _icons.icon_file(icon_name, _MENU_ICON_SIZE) - # image_widget.set_from_file(icon_file) - # image_widget.set_pixel_size(_TRAY_ICON_SIZE) - def attention(reason=None): if _icon.get_status() != AppIndicator3.IndicatorStatus.ATTENTION: # _icon.set_attention_icon_full(_icon_file(_icons.TRAY_ATTENTION), reason or '') # works poorly for XFCe 16 @@ -260,9 +241,6 @@ except ImportError: tray_icon_name = _icons.TRAY_OKAY if _devices_info else _icons.TRAY_ATTENTION _icon.set_from_icon_name(tray_icon_name) - def _update_menu_icon(image_widget, icon_name): - image_widget.set_from_icon_name(icon_name, _MENU_ICON_SIZE) - _icon_before_attention = None def _blink(count): @@ -284,11 +262,6 @@ except ImportError: GLib.idle_add(_blink, 9) -# -# -# - - def _generate_tooltip_lines(): if not _devices_info: yield f"{NAME}: " + _("no receiver") @@ -341,11 +314,6 @@ def _pick_device_with_lowest_battery(): return picked -# -# -# - - def _add_device(device): assert device @@ -368,11 +336,8 @@ def _add_device(device): new_device_info = (receiver_path, device.number, device.name, device) _devices_info.insert(index, new_device_info) - label_prefix = " " - new_menu_item = Gtk.ImageMenuItem.new_with_label((label_prefix if device.number else "") + device.name) - new_menu_item.set_image(Gtk.Image()) - new_menu_item.show_all() - new_menu_item.connect("activate", _window_popup, receiver_path, device.number) + label = (" " if device.number else "") + device.name + new_menu_item = make_image_menu_item(label, None, _window_popup, receiver_path, device.number) _menu.insert(new_menu_item, index) return index @@ -393,17 +358,11 @@ def _remove_device(index): def _add_receiver(receiver): index = len(_devices_info) - new_receiver_info = (receiver.path, None, receiver.name, None) _devices_info.insert(index, new_receiver_info) - - new_menu_item = Gtk.ImageMenuItem.new_with_label(receiver.name) - icon_set = _icons.device_icon_set(receiver.name) - new_menu_item.set_image(Gtk.Image().new_from_icon_name(icon_set.names[0], _MENU_ICON_SIZE)) - new_menu_item.show_all() - new_menu_item.connect("activate", _window_popup, receiver.path) + icon_name = _icons.device_icon_name(receiver.name, receiver.kind) + new_menu_item = make_image_menu_item(receiver.name, icon_name, _window_popup, receiver.path) _menu.insert(new_menu_item, index) - return 0 @@ -422,24 +381,17 @@ def _update_menu_item(index, device): if device is None: logger.warning("updating an inactive device %s, assuming disconnected", device) return None - menu_items = _menu.get_children() menu_item = menu_items[index] - level = device.battery_info.level if device.battery_info is not None else None charging = device.battery_info.charging() if device.battery_info is not None else None icon_name = _icons.battery(level, charging) - - menu_item.set_label((" " if 0 < device.number <= 6 else "") + device.name + ": " + device.status_string()) - image_widget = menu_item.get_image() + menu_item.label.set_label((" " if 0 < device.number <= 6 else "") + device.name + ": " + device.status_string()) + image_widget = menu_item.icon image_widget.set_sensitive(bool(device.online)) - _update_menu_icon(image_widget, icon_name) + image_widget.set_from_icon_name(icon_name, _MENU_ICON_SIZE) -# -# -# - # for which device to show the battery info in systray, if more than one # it's actually an entry in _devices_info _picked_device = None diff --git a/lib/solaar/ui/window.py b/lib/solaar/ui/window.py index cae04b3b..d6c4ecae 100644 --- a/lib/solaar/ui/window.py +++ b/lib/solaar/ui/window.py @@ -1,4 +1,5 @@ ## Copyright (C) 2012-2013 Daniel Pavel +## Copyright (C) 2014-2024 Solaar Contributors https://pwr-solaar.github.io/Solaar/ ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -43,10 +44,6 @@ from gi.repository import Gtk # NOQA: E402 logger = logging.getLogger(__name__) -# -# constants -# - _SMALL_BUTTON_ICON_SIZE = Gtk.IconSize.MENU _NORMAL_BUTTON_ICON_SIZE = Gtk.IconSize.BUTTON _TREE_ICON_SIZE = Gtk.IconSize.BUTTON @@ -65,10 +62,6 @@ _TREE_SEPATATOR = (None, 0, False, None, None, None, None, None) assert len(_TREE_SEPATATOR) == len(_COLUMN_TYPES) assert len(_COLUMN_TYPES) == len(_COLUMN) -# -# create UI layout -# - def _new_button(label, icon_name=None, icon_size=_NORMAL_BUTTON_ICON_SIZE, tooltip=None, toggle=False, clicked=None): b = Gtk.ToggleButton() if toggle else Gtk.Button() @@ -76,7 +69,7 @@ def _new_button(label, icon_name=None, icon_size=_NORMAL_BUTTON_ICON_SIZE, toolt if icon_name: c.pack_start(Gtk.Image.new_from_icon_name(icon_name, icon_size), True, True, 0) if label: - c.pack_start(Gtk.Label(label), True, True, 0) + c.pack_start(Gtk.Label(label=label), True, True, 0) b.add(c) if clicked is not None: b.connect("clicked", clicked) @@ -92,15 +85,14 @@ def _create_receiver_panel(): p = Gtk.Box.new(Gtk.Orientation.VERTICAL, 4) p._count = Gtk.Label() - p._count.set_padding(24, 0) - p._count.set_alignment(0, 0.5) + p._count.set_margin_top(24) p.pack_start(p._count, True, True, 0) - p._scanning = Gtk.Label(_("Scanning") + "...") + p._scanning = Gtk.Label(label=_("Scanning") + "...") p._spinner = Gtk.Spinner() bp = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 8) - bp.pack_start(Gtk.Label(" "), True, True, 0) + bp.pack_start(Gtk.Label(label=" "), True, True, 0) bp.pack_start(p._scanning, False, False, 0) bp.pack_end(p._spinner, False, False, 0) p.pack_end(bp, False, False, 0) @@ -115,8 +107,7 @@ def _create_device_panel(): b = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 8) b.set_size_request(10, 28) - b._label = Gtk.Label(label_text) - b._label.set_alignment(0, 0.5) + b._label = Gtk.Label(label=label_text) b._label.set_size_request(170, 10) b.pack_start(b._label, False, False, 0) @@ -124,8 +115,7 @@ def _create_device_panel(): b.pack_start(b._icon, False, False, 0) b._text = Gtk.Label() - b._text.set_alignment(0, 0.5) - b.pack_start(b._text, True, True, 0) + b.pack_start(b._text, False, False, 0) return b @@ -154,8 +144,8 @@ def _create_details_panel(): p.set_state_flags(Gtk.StateFlags.ACTIVE, True) p._text = Gtk.Label() - p._text.set_padding(6, 4) - p._text.set_alignment(0, 0) + p._text.set_margin_start(6) + p._text.set_margin_end(4) p._text.set_selectable(True) p.add(p._text) @@ -163,7 +153,7 @@ def _create_details_panel(): def _create_buttons_box(): - bb = Gtk.ButtonBox(Gtk.Orientation.HORIZONTAL) + bb = Gtk.HButtonBox() bb.set_layout(Gtk.ButtonBoxStyle.END) bb._details = _new_button( @@ -213,8 +203,7 @@ def _create_empty_panel(): def _create_info_panel(): p = Gtk.Box.new(Gtk.Orientation.VERTICAL, 4) - p._title = Gtk.Label(" ") - p._title.set_alignment(0, 0.5) + p._title = Gtk.Label(label=" ") p._icon = Gtk.Image() b1 = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 4) @@ -313,7 +302,7 @@ def _create_window_layout(): panel.pack_start(_info, True, True, 0) panel.pack_start(_empty, True, True, 0) - bottom_buttons_box = Gtk.ButtonBox(Gtk.Orientation.HORIZONTAL) + bottom_buttons_box = Gtk.HButtonBox() bottom_buttons_box.set_layout(Gtk.ButtonBoxStyle.START) bottom_buttons_box.set_spacing(20) quit_button = _new_button(_("Quit %s") % NAME, "application-exit", _SMALL_BUTTON_ICON_SIZE, clicked=destroy) @@ -367,11 +356,6 @@ def _create(delete_action): return window -# -# window updates -# - - def _find_selected_device(): selection = _tree.get_selection() model, item = selection.get_selected() @@ -481,11 +465,6 @@ def _device_row(receiver_path, device_number, device=None): return item or None -# -# -# - - def select(receiver_path, device_number=None): assert _window assert receiver_path is not None @@ -524,11 +503,6 @@ def toggle(trigger=None): _window.present() -# -# -# - - def _update_details(button): assert button visible = button.get_active() @@ -808,7 +782,6 @@ def _update_info_panel(device, full=False): _update_details(_info._buttons._details) -# # window layout: # +--------------------------------+ # | tree | receiver | empty |