diff --git a/meson.build b/meson.build index a32c369..7acc275 100644 --- a/meson.build +++ b/meson.build @@ -47,7 +47,9 @@ shared_module( meson.project_name(), gresource, 'src/Indicator.vala', + 'src/InputSource.vala', 'src/LayoutButton.vala', + 'src/LayoutManager.vala', 'src/PopoverWidget.vala', config_file, dependencies: [ diff --git a/src/Indicator.vala b/src/Indicator.vala index ee3e7de..5b8707b 100644 --- a/src/Indicator.vala +++ b/src/Indicator.vala @@ -98,7 +98,13 @@ public class Keyboard.Indicator : Wingpanel.Indicator { update_visibility (); }); - popover_widget.updated (); + update_visibility (); + + var layout_manager = LayoutManager.get_default (); + + layout_manager.input_sources.items_changed.connect (() => { + layouts_revealer.reveal_child = layout_manager.input_sources.n_items > 0 || settings.get_boolean ("always-show-layout"); + }); } return indicator_box; @@ -106,7 +112,6 @@ public class Keyboard.Indicator : Wingpanel.Indicator { private void update_visibility () { layouts_icon.label = popover_widget.current_language_code[0:2]; - layouts_revealer.reveal_child = popover_widget.has_multiple_layouts () || settings.get_boolean ("always-show-layout"); numlock_revealer.reveal_child = keymap.get_num_lock_state () && settings.get_boolean ("numlock"); capslock_revealer.reveal_child = keymap.get_caps_lock_state () && settings.get_boolean ("capslock"); diff --git a/src/InputSource.vala b/src/InputSource.vala new file mode 100644 index 0000000..1985dcc --- /dev/null +++ b/src/InputSource.vala @@ -0,0 +1,84 @@ +/* + * Copyright 2017-2025 elementary, Inc. (https://elementary.io) + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +/** + * Respresents a keyboard-InputSource according to "org.gnome.desktop.input-sources sources". + * This means that the enum parameter @layout_type equals the first string in the + * tupel of strings, and the @name parameter equals the second string. + */ +public class Keyboard.InputSource : Object { + /** + * Type of a keyboard-InputSource as described in the description of + * "org.gnome.desktop.input-sources sources". + */ + public enum LayoutType { + IBUS, + XKB + } + + public LayoutType layout_type { get; construct; } + + // Name of input source as stored in settings e.g. "gb" (xkb) or "xkb:gb:extd:eng" (ibus) or "mozc-jp" (ibus) + // These names are used both in org/gnome/desktop/input-sources and desktop/ibus/general/preload-engines + public string name { get; construct; } + + private InputSource (LayoutType layout_type, string name) { + Object ( + layout_type: layout_type, + name: name + ); + } + + public static InputSource? new_from_variant (Variant? variant) { + if (!variant.is_of_type (new VariantType ("(ss)"))) { + critical ("Ignoring attempt to create InputSource from invalid VariantType"); + return null; + } + + unowned string type; + unowned string name; + variant.get ("(&s&s)", out type, out name); + + if (name == "") { + critical ("Attempt to create invalid InputSource name %s", name); + return null; + } + + if (type == "xkb") { + return new InputSource (LayoutType.XKB, name); + } else if (type == "ibus") { + return new InputSource (LayoutType.IBUS, name); + } + + return null; + } + + public bool equal (InputSource other) { + return layout_type == other.layout_type && name == other.name; + } + + /** + * GSettings saves values in the form of GLib.Variant and this + * function creates a Variant representing this object. + */ + public GLib.Variant to_variant () requires (name != "") { + string type_name = ""; + switch (layout_type) { + case LayoutType.IBUS: + type_name = "ibus"; + break; + case LayoutType.XKB: + type_name = "xkb"; + break; + default: + assert_not_reached (); + } + GLib.Variant first = new GLib.Variant.string (type_name); + GLib.Variant second = new GLib.Variant.string (name); + GLib.Variant result = new GLib.Variant.tuple ({first, second}); + + return result; + } +} diff --git a/src/LayoutManager.vala b/src/LayoutManager.vala new file mode 100644 index 0000000..411666a --- /dev/null +++ b/src/LayoutManager.vala @@ -0,0 +1,41 @@ +/* + * Copyright 2025 elementary, Inc. (https://elementary.io) + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +public class Keyboard.LayoutManager : Object { + public GLib.ListStore input_sources { get; private set; } + + private static GLib.Once layout_manager; + public static LayoutManager get_default () { + return layout_manager.once (() => { + return new LayoutManager (); + }); + } + + private GLib.Settings settings; + + private LayoutManager () {} + + construct { + input_sources = new GLib.ListStore (typeof (InputSource)); + + settings = new Settings ("org.gnome.desktop.input-sources"); + settings.changed["sources"].connect (update_sources); + } + + + private void update_sources () { + input_sources.remove_all (); + + var sources = settings.get_value ("sources"); + for (size_t i = 0; i < sources.n_children (); i++) { + var input_source = InputSource.new_from_variant (sources.get_child_value (i)); + + uint pos = -1; + if (!input_sources.find (input_source, out pos)) { + input_sources.append (input_source); + } + } + } +} diff --git a/src/PopoverWidget.vala b/src/PopoverWidget.vala index 3a46e4b..9353e20 100644 --- a/src/PopoverWidget.vala +++ b/src/PopoverWidget.vala @@ -122,10 +122,6 @@ public class Keyboard.Widgets.PopoverWidget : Gtk.Box { settings = new GLib.Settings ("org.gnome.desktop.input-sources"); - settings.changed["sources"].connect (() => { - populate_layouts (); - }); - settings.changed["current"].connect_after (() => { set_active_layout_from_settings (); // Gala will set the keymap if required updated (); @@ -154,6 +150,9 @@ public class Keyboard.Widgets.PopoverWidget : Gtk.Box { show_all (); + var layout_manager = LayoutManager.get_default (); + layout_manager.input_sources.items_changed.connect (populate_layouts); + populate_layouts (); }