From 75c05a76279a016a753667a8b70e45646f3e7584 Mon Sep 17 00:00:00 2001 From: Samuel Wilton Date: Sat, 22 Nov 2025 16:17:31 -0500 Subject: [PATCH 1/2] xlet-settings: Handle adding and removing applets while open --- .../bin/JsonSettingsWidgets.py | 12 +- .../cinnamon-settings/xlet-settings.py | 235 +++++++++++++----- 2 files changed, 176 insertions(+), 71 deletions(-) diff --git a/files/usr/share/cinnamon/cinnamon-settings/bin/JsonSettingsWidgets.py b/files/usr/share/cinnamon/cinnamon-settings/bin/JsonSettingsWidgets.py index afcc4a7d5a..fc86d21eb4 100644 --- a/files/usr/share/cinnamon/cinnamon-settings/bin/JsonSettingsWidgets.py +++ b/files/usr/share/cinnamon/cinnamon-settings/bin/JsonSettingsWidgets.py @@ -155,7 +155,8 @@ def check_settings(self, *args): self.timeout_id = 0 old_settings = self.settings self.settings = self.get_settings() - + if self.settings is None: + return for key in self.bindings: new_value = self.settings[key]["value"] if new_value != old_settings[key]["value"]: @@ -170,9 +171,12 @@ def check_settings(self, *args): return GLib.SOURCE_REMOVE def get_settings(self): - file = open(self.filepath) - raw_data = file.read() - file.close() + try: + file = open(self.filepath) + raw_data = file.read() + file.close() + except FileNotFoundError: + return try: settings = json.loads(raw_data, object_pairs_hook=collections.OrderedDict) except: diff --git a/files/usr/share/cinnamon/cinnamon-settings/xlet-settings.py b/files/usr/share/cinnamon/cinnamon-settings/xlet-settings.py index 4ea84404c8..0bccb48a6c 100755 --- a/files/usr/share/cinnamon/cinnamon-settings/xlet-settings.py +++ b/files/usr/share/cinnamon/cinnamon-settings/xlet-settings.py @@ -95,13 +95,21 @@ def __init__(self, args): self.type = args.type self.uuid = args.uuid self.tab = 0 + self.instance_info = [] self.instance_id = str(args.id) if args.tab is not None: self.tab = int(args.tab) self.selected_instance = None self.gsettings = Gio.Settings.new("org.cinnamon") + self.monitors = {} + self.g_directories = [] self.custom_modules = {} + if self.type == "applet": changed_key = "enabled-applets" + elif self.type == "desklet": changed_key = "enabled-desklets" + else: changed_key = None + if changed_key: + self.gsettings.connect("changed::" + changed_key, lambda *args: self.on_enabled_xlets_changed(changed_key, *args)) self.load_xlet_data() self.build_window() @@ -128,7 +136,7 @@ def _on_proxy_ready (self, obj, result, data=None): proxy = None if proxy: - proxy.highlightXlet('(ssb)', self.uuid, self.selected_instance["id"], True) + self.highlight_xlet(self.selected_instance, True) def load_xlet_data (self): self.xlet_dir = f"/usr/share/cinnamon/{self.type}s/{self.uuid}" @@ -243,13 +251,17 @@ def check_sizing(widget, data=None): self.next_button.connect("clicked", self.next_instance) def load_instances(self): - self.instance_info = [] path = Path(os.path.join(settings_dir, self.uuid)) old_path = Path(f"{home}/.cinnamon/configs/{self.uuid}") - instances = 0 + for p in path, old_path: + if not p.exists(): + continue + self.g_directories.append(Gio.File.new_for_path(str(p))) + new_items = os.listdir(path) if path.exists() else [] old_items = os.listdir(old_path) if old_path.exists() else [] dir_items = sorted(new_items + old_items) + try: multi_instance = int(self.xlet_meta["max-instances"]) != 1 except (KeyError, ValueError): @@ -281,58 +293,75 @@ def load_instances(self): if not instance_exists: continue - settings = JSONSettingsHandler( - os.path.join(path if item in new_items else old_path, item), self.uuid, instance_id, self.notify_dbus - ) - instance_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) - self.instance_stack.add_named(instance_box, instance_id) + config_path = os.path.join(path if item in new_items else old_path, item) + self.create_settings_page(config_path) + + if not self.instance_info: + print(f"No instances were found for {self.uuid}. Exiting...") + sys.exit() + + self.next_button.set_no_show_all(True) + self.prev_button.set_no_show_all(True) + self.show_prev_next_buttons() if self.has_multiple_instances() else self.hide_prev_next_buttons() + + def create_settings_page(self, config_path): + instance_id = os.path.basename(config_path)[:-5] + if self.instance_stack.get_child_by_name(instance_id) is not None: + return + settings = JSONSettingsHandler(config_path, self.uuid, instance_id, self.notify_dbus) + settings.instance_id = instance_id + instance_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + self.instance_stack.add_named(instance_box, instance_id) + info = {"settings": settings, "id": instance_id} + self.instance_info.append(info) + settings_map = settings.get_settings() + first_key = next(iter(settings_map.values())) - info = {"settings": settings, "id": instance_id} - self.instance_info.append(info) + try: + for setting in settings_map: + if setting == "__md5__": + continue + for key in settings_map[setting]: + if key in ("description", "tooltip", "units"): + try: + settings_map[setting][key] = translate(self.uuid, settings_map[setting][key]) + except (KeyError, ValueError): + traceback.print_exc() + elif key in "options": + new_opt_data = collections.OrderedDict() + opt_data = settings_map[setting][key] + for option in opt_data: + if opt_data[option] == "custom": + continue + new_opt_data[translate(self.uuid, option)] = opt_data[option] + settings_map[setting][key] = new_opt_data + elif key in "columns": + columns_data = settings_map[setting][key] + for column in columns_data: + column["title"] = translate(self.uuid, column["title"]) + finally: + # if a layout is not explicitly defined, generate the settings + # widgets based on the order they occur + if first_key["type"] == "layout": + self.build_with_layout(settings_map, info, instance_box, first_key) + else: + self.build_from_order(settings_map, info, instance_box, first_key) - settings_map = settings.get_settings() - first_key = next(iter(settings_map.values())) + if self.selected_instance is None: + self.selected_instance = info + if "stack" in info: + self.stack_switcher.set_stack(info["stack"]) - try: - for setting in settings_map: - if setting == "__md5__": - continue - for key in settings_map[setting]: - if key in ("description", "tooltip", "units"): - try: - settings_map[setting][key] = translate(self.uuid, settings_map[setting][key]) - except (KeyError, ValueError): - traceback.print_exc() - elif key in "options": - new_opt_data = collections.OrderedDict() - opt_data = settings_map[setting][key] - for option in opt_data: - if opt_data[option] == "custom": - continue - new_opt_data[translate(self.uuid, option)] = opt_data[option] - settings_map[setting][key] = new_opt_data - elif key in "columns": - columns_data = settings_map[setting][key] - for column in columns_data: - column["title"] = translate(self.uuid, column["title"]) - finally: - # if a layout is not explicitly defined, generate the settings - # widgets based on the order they occur - if first_key["type"] == "layout": - self.build_with_layout(settings_map, info, instance_box, first_key) - else: - self.build_from_order(settings_map, info, instance_box, first_key) - - if self.selected_instance is None: - self.selected_instance = info - if "stack" in info: - self.stack_switcher.set_stack(info["stack"]) + def has_multiple_instances(self): + return len(self.instance_info) > 1 - instances += 1 + def hide_prev_next_buttons(self): + self.prev_button.hide() + self.next_button.hide() - if instances < 2: - self.prev_button.set_no_show_all(True) - self.next_button.set_no_show_all(True) + def show_prev_next_buttons(self): + self.prev_button.show() + self.next_button.show() def build_with_layout(self, settings_map, info, box, first_key): layout = first_key @@ -462,26 +491,99 @@ def set_instance(self, info): else: info["stack"].set_visible_child(children[0]) if proxy: - proxy.highlightXlet('(ssb)', self.uuid, self.selected_instance["id"], False) - proxy.highlightXlet('(ssb)', self.uuid, info["id"], True) + old_info = self.selected_instance + new_info = info + self.highlight_xlet(old_info, False) + self.highlight_xlet(new_info, True) self.selected_instance = info + def highlight_xlet(self, info, highlighted): + try: + proxy.highlightXlet('(ssb)', self.uuid, info["id"], highlighted) + except: + return + def previous_instance(self, *args): - self.instance_stack.set_transition_type(Gtk.StackTransitionType.OVER_RIGHT) - index = self.instance_info.index(self.selected_instance) - self.set_instance(self.instance_info[index-1]) + self.get_next_instance(False) def next_instance(self, *args): - self.instance_stack.set_transition_type(Gtk.StackTransitionType.OVER_LEFT) - index = self.instance_info.index(self.selected_instance) - if index == len(self.instance_info) - 1: - index = 0 - else: - index +=1 - self.set_instance(self.instance_info[index]) - - # def unpack_args(self, args): - # args = {} + self.get_next_instance() + + def get_next_instance(self, positive_direction = True): + transition = Gtk.StackTransitionType.OVER_LEFT if positive_direction else Gtk.StackTransitionType.OVER_RIGHT + self.instance_stack.set_transition_type(transition) + step = 1 if positive_direction else -1 + instances_length = len(self.instance_info) + start = self.instance_info.index(self.selected_instance) + nextIndex = (start + step) % instances_length + self.set_instance(self.instance_info[nextIndex]) + + def on_enabled_xlets_changed(self, key, *args): + """ + Args: + key ("enabled-applets"|"enabled-desklets") + """ + current_ids = {info["id"] for info in self.instance_info} + new_ids = set() + for definition in self.gsettings.get_strv(key): + definition = definition.split(":") + uuid, instance_id = (definition[-2], definition[-1]) if key == "enabled-applets"\ + else (definition[0], definition[1]) + if uuid == self.uuid: + new_ids.add(instance_id) + + if len(new_ids) == 0: + self.quit() + return + + added_ids = new_ids - current_ids + + removed_indices = [] + selected_removed_index = -1 + for i, info in enumerate(self.instance_info): + if info["id"] in new_ids: + continue + removed_indices.append(i) + if info == self.selected_instance: selected_removed_index = i + + for id in added_ids: + for dir in self.g_directories: + file = dir.get_child(id + ".json") + if file.query_exists(None): + self.create_new_settings_page(file.get_path()) + break + # Config files have not been added yet, need to monitor directories + monitor = dir.monitor_directory(Gio.FileMonitorFlags.NONE, None) + monitor.connect("changed", self.on_config_file_added) + self.monitors.setdefault(id, []).append(monitor) + + if (selected_removed_index != -1): + self.get_next_instance() + + for index in sorted(removed_indices, reverse=True): + self.monitors.get(self.instance_info[index]["id"], []).clear() + self.instance_stack.remove(self.instance_stack.get_child_by_name(self.instance_info[index]["id"])) + self.instance_info.pop(index) + + if not self.has_multiple_instances(): self.hide_prev_next_buttons() + + def on_config_file_added(self, *args): + file, event_type = args[1], args[-1] + instance = file.get_basename()[:-5] + if event_type != Gio.FileMonitorEvent.CHANGES_DONE_HINT: + return + if instance not in self.monitors: + return + for monitor in self.monitors[instance]: monitor.cancel() + del self.monitors[instance] + self.create_new_settings_page(file.get_path()) + + + def create_new_settings_page(self, path): + self.create_settings_page(path) + self.window.show_all() + if self.has_multiple_instances(): self.show_prev_next_buttons() + self.highlight_xlet(self.selected_instance, True) def backup(self, *args): dialog = Gtk.FileChooserDialog(_("Select or enter file to export to"), @@ -533,8 +635,7 @@ def reload_xlet(self, *args): def quit(self, *args): if proxy: - proxy.highlightXlet('(ssb)', self.uuid, self.selected_instance["id"], False) - + self.highlight_xlet(self.selected_instance, False) self.window.destroy() Gtk.main_quit() From cb6308ce7146277f956399faa715e8192fc4677b Mon Sep 17 00:00:00 2001 From: Samuel Wilton Date: Sun, 7 Dec 2025 10:02:36 -0500 Subject: [PATCH 2/2] Keybinding handling updates --- .../cinnamon-settings/bin/KeybindingTable.py | 275 +++++++++++------- .../cinnamon-settings/bin/SettingsWidgets.py | 2 +- .../cinnamon-settings/xlet-settings.py | 29 +- 3 files changed, 184 insertions(+), 122 deletions(-) diff --git a/files/usr/share/cinnamon/cinnamon-settings/bin/KeybindingTable.py b/files/usr/share/cinnamon/cinnamon-settings/bin/KeybindingTable.py index b7400df97c..3ec6572cdc 100644 --- a/files/usr/share/cinnamon/cinnamon-settings/bin/KeybindingTable.py +++ b/files/usr/share/cinnamon/cinnamon-settings/bin/KeybindingTable.py @@ -355,11 +355,17 @@ def load_gsettings(self, *args): def load_json_settings(self, *args): del self.entries[:] - self.entries = self.getConfigSettings() + settings = self.getConfigSettings() + if settings is None: + return + self.entries = settings self.json_timeout_id = 0 self.emit_changed() def getConfigSettings(self): + if not Path(self.schema).exists(): + self.settings_monitor.cancel() + return with open(self.schema, encoding="utf-8") as config_file: config = json.load(config_file) keybinds = config[self.key]["value"].split("::") @@ -496,6 +502,11 @@ def __init__(self): self._collision_check_done = False self._collision_table = {} + self._config_monitors = { + OLD_SETTINGS_DIR: {}, + SETTINGS_DIR: {} + } + self._load_static_store() self._load_custom_store() self._load_spice_store() @@ -597,7 +608,6 @@ def _load_category(cat): def _on_enabled_spices_changed(self, settings, key, data=None): self._load_spice_store() - self.emit("spices-changed") def _load_spice_store(self): try: @@ -611,7 +621,11 @@ def _load_spice_store(self): settings.connect("changed::enabled-extensions", self._on_enabled_spices_changed) enabled_extensions = set() - enabled_spices = set() + enabled_spices = { + "applets": {}, + "desklets": {}, + "extensions": {} + } for applets in settings.get_strv("enabled-applets"): p, pos, order, applet_uuid, *extra = applets.split(":") @@ -619,123 +633,166 @@ def _load_spice_store(self): instance_id = extra[0] else: instance_id = None - enabled_spices.add((applet_uuid, 'applets', instance_id)) - + enabled_spices["applets"].setdefault(applet_uuid, set()).add(instance_id) for desklets in settings.get_strv("enabled-desklets"): desklet_uuid, instance_id, x, y = desklets.split(":") - enabled_spices.add((desklet_uuid, 'desklets', instance_id)) + enabled_spices["desklets"].setdefault(desklet_uuid, set()).add(instance_id) for extension in settings.get_strv("enabled-extensions"): enabled_extensions.add(extension) - enabled_spices.add((extension, 'extensions', None)) - - keyboard_spices = sorted(enabled_spices) + enabled_spices["extensions"].setdefault(extension, set()).add(None) spice_keybinds = {} spice_properties = {} - for uuid, _type, instance_id in keyboard_spices: - for settings_dir in (OLD_SETTINGS_DIR, SETTINGS_DIR): - config_path = Path.joinpath(settings_dir, uuid) - if Path.exists(config_path): - configs = [x for x in os.listdir(config_path) if x.endswith(".json")] - # If we encounted numbered and non-numbered, config files, filter out the uuid-named one - if not all(x.split(".json")[0].isdigit() for x in configs) and any(x.split(".json")[0].isdigit() for x in configs): - for index, value in enumerate(configs): - if not value.split(".json")[0].isdigit(): - configs.pop(index) - for config in configs: - config_json = Path.joinpath(config_path, config) - _id = config.split(".json")[0] - key_name = f"{uuid}_{_id}" if _id.isdigit() else uuid - with open(config_json, encoding="utf-8") as config_file: - _config = json.load(config_file) - - for key, val in _config.items(): - if isinstance(val, dict) and val.get("type") == "keybinding": - spice_properties.setdefault(key_name, {}) - spice_properties[key_name]["highlight"] = uuid not in enabled_extensions - spice_properties[key_name]["path"] = str(config_json) - spice_properties[key_name]["type"] = _type - spice_properties[key_name]["uuid"] = uuid - spice_properties[key_name]["instance_id"] = instance_id - spice_properties[key_name]["config_id"] = _id - spice_keybinds.setdefault(key_name, {}) - spice_keybinds[key_name].setdefault(key, {}) - spice_keybinds[key_name][key] = {val.get("description"): val.get("value").split("::")} - - self._spice_categories = {} - self._spice_store = [] - - new_categories = [] - new_keybindings = [] - - for spice, bindings in spice_keybinds.items(): - uuid, *_id = spice.split("_") - - spice_props = spice_properties[spice] - _type = spice_props["type"] - local_metadata_path = Path.home() / '.local/share/cinnamon' / _type / uuid / 'metadata.json' - if local_metadata_path.exists(): - gettext.bindtextdomain(uuid, str(Path.home() / '.local/share/locale')) - gettext.textdomain(uuid) - with open(local_metadata_path, encoding="utf-8") as metadata: - json_data = json.load(metadata) - category_label = _(json_data["name"]) - else: - system_metadata_path = Path("/usr/share/cinnamon") / _type / uuid / "metadata.json" - if system_metadata_path.exists(): - with open(system_metadata_path, encoding="utf-8") as metadata: + def set_properties(_type, uuid, config_path, configs): + for config in configs: + config_json = Path.joinpath(config_path, config) + _id = config.split(".json")[0] + key_name = f"{uuid}_{_id}" if _id.isdigit() else uuid + with open(config_json, encoding="utf-8") as config_file: + _config = json.load(config_file) + + for key, val in _config.items(): + if isinstance(val, dict) and val.get("type") == "keybinding": + spice_properties.setdefault(key_name, {}) + spice_properties[key_name]["highlight"] = uuid not in enabled_extensions + spice_properties[key_name]["path"] = str(config_json) + spice_properties[key_name]["type"] = _type + spice_properties[key_name]["uuid"] = uuid + spice_properties[key_name]["instance_id"] = _id + spice_properties[key_name]["config_id"] = _id + spice_keybinds.setdefault(key_name, {}) + spice_keybinds[key_name].setdefault(key, {}) + spice_keybinds[key_name][key] = {val.get("description"): val.get("value").split("::")} + + + def on_dir_changed(monitor, file, other_file, event_type, _type, uuid, config_path, get_configs): + if event_type != Gio.FileMonitorEvent.CHANGES_DONE_HINT: + return + monitor.cancel() + del self._config_monitors[config_path.parent][uuid] + set_properties(_type, uuid, config_path, get_configs(config_path)) + resume() + + + def has_monitors(): + for value in self._config_monitors.values(): + if len(value): + return True + return False + + + def resume(): + if has_monitors(): + return + finish_load() + + + for _type, value in enabled_spices.items(): + for uuid, instance_ids in value.items(): + for settings_dir in (OLD_SETTINGS_DIR, SETTINGS_DIR): + config_path = Path.joinpath(settings_dir, uuid) + if not Path.exists(config_path): + continue + + def get_configs(config_path): + configs = [x for x in os.listdir(config_path) if x.endswith(".json")] + # If we encounted numbered and non-numbered, config files, filter out the uuid-named one + if not all(x.split(".json")[0].isdigit() for x in configs) and any(x.split(".json")[0].isdigit() for x in configs): + for index, value in enumerate(configs): + if not value.split(".json")[0].isdigit(): + configs.pop(index) + return configs + + configs = get_configs(config_path) + # Must wait until config file is created/deleted + if len(configs) != len(instance_ids): + g_config_dir = Gio.File.new_for_path(str(config_path)) + monitor = g_config_dir.monitor_directory(Gio.FileMonitorFlags.NONE, None) + monitor.connect("changed", on_dir_changed, _type, uuid, config_path, get_configs) + self._config_monitors[settings_dir][uuid] = monitor + continue + + set_properties(_type, uuid, config_path, configs) + + def finish_load(): + self._spice_categories = {} + self._spice_store = [] + + new_categories = [] + new_keybindings = [] + + for spice, bindings in spice_keybinds.items(): + uuid, *_id = spice.split("_") + + spice_props = spice_properties[spice] + _type = spice_props["type"] + local_metadata_path = Path.home() / '.local/share/cinnamon' / _type / uuid / 'metadata.json' + if local_metadata_path.exists(): + gettext.bindtextdomain(uuid, str(Path.home() / '.local/share/locale')) + gettext.textdomain(uuid) + with open(local_metadata_path, encoding="utf-8") as metadata: json_data = json.load(metadata) category_label = _(json_data["name"]) - if not _id: - cat_label = category_label if category_label else uuid - new_categories.append([cat_label, uuid, "spices", None, spice_props]) - instance_num = 1 - elif len(new_categories) == 0 or uuid != new_categories[-1][2]: - cat_label = category_label if category_label else uuid - new_categories.append([cat_label, uuid, "spices", None, {}]) - instance_num = 1 - label = _("Instance") + f" {instance_num}" - new_categories.append([label, f"{uuid}_{instance_num}", uuid, None, spice_props]) - instance_num = 2 - elif len(new_categories) > 0 and uuid == new_categories[-1][2]: - label = _("Instance") + f" {instance_num}" - new_categories.append([label, f"{uuid}_{instance_num}", uuid, None, spice_props]) - instance_num += 1 - - dbus_info = {} - dbus_info["highlight"] = spice_props["highlight"] - dbus_info["uuid"] = spice_props["uuid"] - dbus_info["instance_id"] = spice_props["instance_id"] - dbus_info["config_id"] = spice_props["config_id"] - for binding_key, binding_values in bindings.items(): - if "@cinnamon.org" in uuid: - binding_label = _(list(binding_values.keys())[0]) else: - home = os.path.expanduser("~") - gettext.bindtextdomain(uuid, f"{home}/.local/share/locale") - gettext.textdomain(uuid) - binding_label = gettext.gettext(list(binding_values.keys())[0]) - binding_schema = spice_properties[spice]["path"] - binding_category = f"{uuid}_{instance_num - 1}" if _id else uuid - new_keybindings.append([binding_label, binding_schema, binding_key, binding_category, dbus_info]) - self._spice_categories[binding_category] = category_label - - cat_lookup = {} - - for cat in new_categories: - cat_lookup[cat[1]] = cat[0] - self._spice_store.append(KeyBindingCategory(cat[0], cat[1], cat[2], cat[3], cat[4])) - - for binding in new_keybindings: - self._spice_categories.setdefault(binding[3], cat_lookup[binding[3]]) - for category in self._spice_store: - if category.int_name == binding[3]: - kb = KeyBinding(binding[0], binding[1], binding[2], binding[3], self.settings_by_schema_id, binding[4]) - kb.connect("changed", self._on_kb_changed) - - self._add_to_collision_table(kb) - category.add(kb) + system_metadata_path = Path("/usr/share/cinnamon") / _type / uuid / "metadata.json" + if system_metadata_path.exists(): + with open(system_metadata_path, encoding="utf-8") as metadata: + json_data = json.load(metadata) + category_label = _(json_data["name"]) + if not _id: + cat_label = category_label if category_label else uuid + new_categories.append([cat_label, uuid, "spices", None, spice_props]) + instance_num = 1 + elif len(new_categories) == 0 or uuid != new_categories[-1][2]: + cat_label = category_label if category_label else uuid + new_categories.append([cat_label, uuid, "spices", None, {}]) + instance_num = 1 + label = _("Instance") + f" {instance_num}" + new_categories.append([label, f"{uuid}_{instance_num}", uuid, None, spice_props]) + instance_num = 2 + elif len(new_categories) > 0 and uuid == new_categories[-1][2]: + label = _("Instance") + f" {instance_num}" + new_categories.append([label, f"{uuid}_{instance_num}", uuid, None, spice_props]) + instance_num += 1 + + dbus_info = {} + dbus_info["highlight"] = spice_props["highlight"] + dbus_info["uuid"] = spice_props["uuid"] + dbus_info["instance_id"] = spice_props["instance_id"] + dbus_info["config_id"] = spice_props["config_id"] + for binding_key, binding_values in bindings.items(): + if "@cinnamon.org" in uuid: + binding_label = _(list(binding_values.keys())[0]) + else: + home = os.path.expanduser("~") + gettext.bindtextdomain(uuid, f"{home}/.local/share/locale") + gettext.textdomain(uuid) + binding_label = gettext.gettext(list(binding_values.keys())[0]) + binding_schema = spice_properties[spice]["path"] + binding_category = f"{uuid}_{instance_num - 1}" if _id else uuid + new_keybindings.append([binding_label, binding_schema, binding_key, binding_category, dbus_info]) + self._spice_categories[binding_category] = category_label + + cat_lookup = {} + + for cat in new_categories: + cat_lookup[cat[1]] = cat[0] + self._spice_store.append(KeyBindingCategory(cat[0], cat[1], cat[2], cat[3], cat[4])) + + for binding in new_keybindings: + self._spice_categories.setdefault(binding[3], cat_lookup[binding[3]]) + for category in self._spice_store: + if category.int_name == binding[3]: + kb = KeyBinding(binding[0], binding[1], binding[2], binding[3], self.settings_by_schema_id, binding[4]) + kb.connect("changed", self._on_kb_changed) + + self._add_to_collision_table(kb) + category.add(kb) + self.emit("spices-changed") + + resume() + def _load_custom_store(self): settings = self._get_settings_for_schema(CUSTOM_KEYS_PARENT_SCHEMA) diff --git a/files/usr/share/cinnamon/cinnamon-settings/bin/SettingsWidgets.py b/files/usr/share/cinnamon/cinnamon-settings/bin/SettingsWidgets.py index d444ea3063..9b5273a8e2 100755 --- a/files/usr/share/cinnamon/cinnamon-settings/bin/SettingsWidgets.py +++ b/files/usr/share/cinnamon/cinnamon-settings/bin/SettingsWidgets.py @@ -13,7 +13,7 @@ from KeybindingWidgets import ButtonKeybinding import util -import KeybindingTable +from bin import KeybindingTable settings_objects = {} diff --git a/files/usr/share/cinnamon/cinnamon-settings/xlet-settings.py b/files/usr/share/cinnamon/cinnamon-settings/xlet-settings.py index 0bccb48a6c..fbc00587af 100755 --- a/files/usr/share/cinnamon/cinnamon-settings/xlet-settings.py +++ b/files/usr/share/cinnamon/cinnamon-settings/xlet-settings.py @@ -17,6 +17,7 @@ import importlib.util import traceback from pathlib import Path +from bin import KeybindingTable from JsonSettingsWidgets import * from ExtensionCore import find_extension_subdir @@ -105,11 +106,15 @@ def __init__(self, args): self.monitors = {} self.g_directories = [] self.custom_modules = {} - if self.type == "applet": changed_key = "enabled-applets" - elif self.type == "desklet": changed_key = "enabled-desklets" - else: changed_key = None + if self.type == "applet": + changed_key = "enabled-applets" + elif self.type == "desklet": + changed_key = "enabled-desklets" + else: + changed_key = None if changed_key: - self.gsettings.connect("changed::" + changed_key, lambda *args: self.on_enabled_xlets_changed(changed_key, *args)) + kb_table = KeybindingTable.get_default() + kb_table.connect('spices-changed', lambda *args: self.on_enabled_xlets_changed(*args, changed_key)) self.load_xlet_data() self.build_window() @@ -518,11 +523,8 @@ def get_next_instance(self, positive_direction = True): nextIndex = (start + step) % instances_length self.set_instance(self.instance_info[nextIndex]) - def on_enabled_xlets_changed(self, key, *args): - """ - Args: - key ("enabled-applets"|"enabled-desklets") - """ + def on_enabled_xlets_changed(self, *args): + key = args[-1] # "enabled-applets" | "enabled-desklets" current_ids = {info["id"] for info in self.instance_info} new_ids = set() for definition in self.gsettings.get_strv(key): @@ -544,7 +546,8 @@ def on_enabled_xlets_changed(self, key, *args): if info["id"] in new_ids: continue removed_indices.append(i) - if info == self.selected_instance: selected_removed_index = i + if info == self.selected_instance: + selected_removed_index = i for id in added_ids: for dir in self.g_directories: @@ -565,7 +568,8 @@ def on_enabled_xlets_changed(self, key, *args): self.instance_stack.remove(self.instance_stack.get_child_by_name(self.instance_info[index]["id"])) self.instance_info.pop(index) - if not self.has_multiple_instances(): self.hide_prev_next_buttons() + if not self.has_multiple_instances(): + self.hide_prev_next_buttons() def on_config_file_added(self, *args): file, event_type = args[1], args[-1] @@ -574,7 +578,8 @@ def on_config_file_added(self, *args): return if instance not in self.monitors: return - for monitor in self.monitors[instance]: monitor.cancel() + for monitor in self.monitors[instance]: + monitor.cancel() del self.monitors[instance] self.create_new_settings_page(file.get_path())