[gnome-shell] status/keyboard: Factor out an InputSourceManager class



commit effe6fab3a97d60f14a585e8083b332f58893c25
Author: Rui Matos <tiagomatos gmail com>
Date:   Thu May 29 18:42:46 2014 +0200

    status/keyboard: Factor out an InputSourceManager class
    
    https://bugzilla.gnome.org/show_bug.cgi?id=736435

 js/ui/status/keyboard.js |  376 ++++++++++++++++++++++++++--------------------
 1 files changed, 210 insertions(+), 166 deletions(-)
---
diff --git a/js/ui/status/keyboard.js b/js/ui/status/keyboard.js
index 990021b..56752fd 100644
--- a/js/ui/status/keyboard.js
+++ b/js/ui/status/keyboard.js
@@ -147,25 +147,10 @@ const InputSourceSwitcher = new Lang.Class({
     }
 });
 
-const InputSourceIndicator = new Lang.Class({
-    Name: 'InputSourceIndicator',
-    Extends: PanelMenu.Button,
+const InputSourceManager = new Lang.Class({
+    Name: 'InputSourceManager',
 
     _init: function() {
-        this.parent(0.0, _("Keyboard"));
-
-        this._container = new Shell.GenericContainer();
-        this._container.connect('get-preferred-width', Lang.bind(this, this._containerGetPreferredWidth));
-        this._container.connect('get-preferred-height', Lang.bind(this, this._containerGetPreferredHeight));
-        this._container.connect('allocate', Lang.bind(this, this._containerAllocate));
-
-        this._hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
-        this._hbox.add_child(this._container);
-        this._hbox.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM));
-
-        this.actor.add_child(this._hbox);
-        this.actor.add_style_class_name('panel-status-button');
-
         // All valid input sources currently in the gsettings
         // KEY_INPUT_SOURCES list indexed by their index there
         this._inputSources = {};
@@ -197,29 +182,16 @@ const InputSourceIndicator = new Lang.Class({
 
         this._xkbInfo = KeyboardManager.getXkbInfo();
 
-        this._propSeparator = new PopupMenu.PopupSeparatorMenuItem();
-        this.menu.addMenuItem(this._propSeparator);
-        this._propSection = new PopupMenu.PopupMenuSection();
-        this.menu.addMenuItem(this._propSection);
-        this._propSection.actor.hide();
-
         this._ibusReady = false;
         this._ibusManager = IBusManager.getIBusManager();
         this._ibusManager.connect('ready', Lang.bind(this, this._ibusReadyCallback));
         this._ibusManager.connect('properties-registered', Lang.bind(this, this._ibusPropertiesRegistered));
         this._ibusManager.connect('property-updated', Lang.bind(this, this._ibusPropertyUpdated));
-        this._inputSourcesChanged();
 
         this._keyboardManager = KeyboardManager.getKeyboardManager();
 
         global.display.connect('modifiers-accelerator-activated', Lang.bind(this, this._modifiersSwitcher));
 
-        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
-        this._showLayoutItem = this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, 
this._showLayout));
-
-        Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
-        this._sessionUpdated();
-
         this._sourcesPerWindow = false;
         this._focusWindowNotifyId = 0;
         this._overviewShowingId = 0;
@@ -228,12 +200,8 @@ const InputSourceIndicator = new Lang.Class({
         this._sourcesPerWindowChanged();
     },
 
-    _sessionUpdated: function() {
-        // re-using "allowSettings" for the keyboard layout is a bit shady,
-        // but at least for now it is used as "allow popping up windows
-        // from shell menus"; we can always add a separate sessionMode
-        // option if need arises.
-        this._showLayoutItem.actor.visible = Main.sessionMode.allowSettings;
+    reload: function() {
+        this._inputSourcesChanged();
     },
 
     _ibusReadyCallback: function(im, ready) {
@@ -288,7 +256,6 @@ const InputSourceIndicator = new Lang.Class({
     },
 
     _currentInputSourceChanged: function() {
-        let nVisibleSources = Object.keys(this._inputSources).length;
         let newSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
         let newSource = this._inputSources[newSourceIndex];
 
@@ -300,25 +267,11 @@ const InputSourceIndicator = new Lang.Class({
             oldSource.indicatorLabel.hide();
         }
 
-        if (!newSource || (nVisibleSources < 2 && !newSource.properties)) {
-            // This source index might be invalid if we weren't able
-            // to build a menu item for it, so we hide ourselves since
-            // we can't fix it here. *shrug*
-
-            // We also hide if we have only one visible source unless
-            // it's an IBus source with properties.
-            this.menu.close();
-            this.actor.hide();
-            return;
-        }
-
-        this.actor.show();
+        this.emit('current-source-changed');
 
         newSource.menuItem.setOrnament(PopupMenu.Ornament.DOT);
         newSource.indicatorLabel.show();
 
-        this._buildPropSection(newSource.properties);
-
         for (let i = 1; i < this._mruSources.length; ++i)
             if (this._mruSources[i] == newSource) {
                 let currentSource = this._mruSources.splice(i, 1);
@@ -384,20 +337,17 @@ const InputSourceIndicator = new Lang.Class({
                 this._ibusSources[is.id] = is;
         }
 
-        let menuIndex = 0;
         for (let i in this._inputSources) {
             let is = this._inputSources[i];
             if (inputSourcesByShortName[is.shortName].length > 1) {
                 let sub = inputSourcesByShortName[is.shortName].indexOf(is) + 1;
                 is.shortName += String.fromCharCode(0x2080 + sub);
             }
-
-            this.menu.addMenuItem(is.menuItem, menuIndex++);
-
             is.indicatorLabel.hide();
-            this._container.add_actor(is.indicatorLabel);
         }
 
+        this.emit('sources-changed');
+
         let sourcesList = [];
         for (let i in this._inputSources)
             sourcesList.push(this._inputSources[i]);
@@ -416,33 +366,6 @@ const InputSourceIndicator = new Lang.Class({
         this._currentInputSourceChanged();
     },
 
-    _showLayout: function() {
-        Main.overview.hide();
-
-        let source = this._currentSource;
-        let xkbLayout = '';
-        let xkbVariant = '';
-
-        if (source.type == INPUT_SOURCE_TYPE_XKB) {
-            [, , , xkbLayout, xkbVariant] = this._xkbInfo.get_layout_info(source.id);
-        } else if (source.type == INPUT_SOURCE_TYPE_IBUS) {
-            let engineDesc = this._ibusManager.getEngineDesc(source.id);
-            if (engineDesc) {
-                xkbLayout = engineDesc.get_layout();
-                xkbVariant = '';
-            }
-        }
-
-        if (!xkbLayout || xkbLayout.length == 0)
-            return;
-
-        let description = xkbLayout;
-        if (xkbVariant.length > 0)
-            description = description + '\t' + xkbVariant;
-
-        Util.spawn(['gkbd-keyboard-display', '-l', description]);
-    },
-
     _makeEngineShortName: function(engineDesc) {
         let symbol = engineDesc.get_symbol();
         if (symbol && symbol[0])
@@ -493,6 +416,175 @@ const InputSourceIndicator = new Lang.Class({
         return false;
     },
 
+    _getNewInputSource: function(current) {
+        for (let i in this._inputSources) {
+            let is = this._inputSources[i];
+            if (is.type == current.type &&
+                is.id == current.id)
+                return is;
+        }
+        return this._currentSource;
+    },
+
+    _getCurrentWindow: function() {
+        if (Main.overview.visible)
+            return Main.overview;
+        else
+            return global.display.focus_window;
+    },
+
+    _setPerWindowInputSource: function() {
+        let window = this._getCurrentWindow();
+        if (!window)
+            return;
+
+        if (!window._inputSources) {
+            window._inputSources = this._inputSources;
+            window._currentSource = this._currentSource;
+        } else if (window._inputSources == this._inputSources) {
+            window._currentSource.activate();
+        } else {
+            window._inputSources = this._inputSources;
+            window._currentSource = this._getNewInputSource(window._currentSource);
+            window._currentSource.activate();
+        }
+    },
+
+    _sourcesPerWindowChanged: function() {
+        this._sourcesPerWindow = this._settings.get_boolean('per-window');
+
+        if (this._sourcesPerWindow && this._focusWindowNotifyId == 0) {
+            this._focusWindowNotifyId = global.display.connect('notify::focus-window',
+                                                               Lang.bind(this, 
this._setPerWindowInputSource));
+            this._overviewShowingId = Main.overview.connect('showing',
+                                                            Lang.bind(this, this._setPerWindowInputSource));
+            this._overviewHiddenId = Main.overview.connect('hidden',
+                                                           Lang.bind(this, this._setPerWindowInputSource));
+        } else if (!this._sourcesPerWindow && this._focusWindowNotifyId != 0) {
+            global.display.disconnect(this._focusWindowNotifyId);
+            this._focusWindowNotifyId = 0;
+            Main.overview.disconnect(this._overviewShowingId);
+            this._overviewShowingId = 0;
+            Main.overview.disconnect(this._overviewHiddenId);
+            this._overviewHiddenId = 0;
+
+            let windows = global.get_window_actors().map(function(w) {
+                return w.meta_window;
+            });
+            for (let i = 0; i < windows.length; ++i) {
+                delete windows[i]._inputSources;
+                delete windows[i]._currentSource;
+            }
+            delete Main.overview._inputSources;
+            delete Main.overview._currentSource;
+        }
+    },
+
+    _changePerWindowSource: function() {
+        if (!this._sourcesPerWindow)
+            return;
+
+        let window = this._getCurrentWindow();
+        if (!window)
+            return;
+
+        window._inputSources = this._inputSources;
+        window._currentSource = this._currentSource;
+    },
+
+    get currentSource() {
+        return this._currentSource;
+    },
+
+    get inputSources() {
+        return this._inputSources;
+    },
+});
+Signals.addSignalMethods(InputSourceManager.prototype);
+
+let _inputSourceManager = null;
+
+function getInputSourceManager() {
+    if (_inputSourceManager == null)
+        _inputSourceManager = new InputSourceManager();
+    return _inputSourceManager;
+}
+
+const InputSourceIndicator = new Lang.Class({
+    Name: 'InputSourceIndicator',
+    Extends: PanelMenu.Button,
+
+    _init: function() {
+        this.parent(0.0, _("Keyboard"));
+
+        this._container = new Shell.GenericContainer();
+        this._container.connect('get-preferred-width', Lang.bind(this, this._containerGetPreferredWidth));
+        this._container.connect('get-preferred-height', Lang.bind(this, this._containerGetPreferredHeight));
+        this._container.connect('allocate', Lang.bind(this, this._containerAllocate));
+
+        this._hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
+        this._hbox.add_child(this._container);
+        this._hbox.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM));
+
+        this.actor.add_child(this._hbox);
+        this.actor.add_style_class_name('panel-status-button');
+
+        this._propSeparator = new PopupMenu.PopupSeparatorMenuItem();
+        this.menu.addMenuItem(this._propSeparator);
+        this._propSection = new PopupMenu.PopupMenuSection();
+        this.menu.addMenuItem(this._propSection);
+        this._propSection.actor.hide();
+
+        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+        this._showLayoutItem = this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, 
this._showLayout));
+
+        Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
+        this._sessionUpdated();
+
+        this._inputSourceManager = getInputSourceManager();
+        this._inputSourceManager.connect('sources-changed', Lang.bind(this, this._sourcesChanged));
+        this._inputSourceManager.connect('current-source-changed', Lang.bind(this, 
this._currentSourceChanged));
+        this._inputSourceManager.reload();
+    },
+
+    _sessionUpdated: function() {
+        // re-using "allowSettings" for the keyboard layout is a bit shady,
+        // but at least for now it is used as "allow popping up windows
+        // from shell menus"; we can always add a separate sessionMode
+        // option if need arises.
+        this._showLayoutItem.actor.visible = Main.sessionMode.allowSettings;
+    },
+
+    _sourcesChanged: function() {
+        let menuIndex = 0;
+        for (let i in this._inputSourceManager.inputSources) {
+            let is = this._inputSourceManager.inputSources[i];
+            this.menu.addMenuItem(is.menuItem, menuIndex++);
+            this._container.add_actor(is.indicatorLabel);
+        }
+    },
+
+    _currentSourceChanged: function() {
+        let nVisibleSources = Object.keys(this._inputSourceManager.inputSources).length;
+        let newSource = this._inputSourceManager.currentSource;
+
+        if (!newSource || (nVisibleSources < 2 && !newSource.properties)) {
+            // This source index might be invalid if we weren't able
+            // to build a menu item for it, so we hide ourselves since
+            // we can't fix it here. *shrug*
+
+            // We also hide if we have only one visible source unless
+            // it's an IBus source with properties.
+            this.menu.close();
+            this.actor.hide();
+            return;
+        }
+
+        this.actor.show();
+
+        this._buildPropSection(newSource.properties);
+    },
+
     _buildPropSection: function(properties) {
         this._propSeparator.actor.hide();
         this._propSection.actor.hide();
@@ -510,6 +602,7 @@ const InputSourceIndicator = new Lang.Class({
         if (!props)
             return;
 
+        let ibusManager = IBusManager.getIBusManager();
         let radioGroup = [];
         let p;
         for (let i = 0; (p = props.get(i)) != null; ++i) {
@@ -552,13 +645,13 @@ const InputSourceIndicator = new Lang.Class({
                         if (group[i] == item) {
                             item.setOrnament(PopupMenu.Ornament.DOT);
                             item.prop.set_state(IBus.PropState.CHECKED);
-                            this._ibusManager.activateProperty(item.prop.get_key(),
-                                                               IBus.PropState.CHECKED);
+                            ibusManager.activateProperty(item.prop.get_key(),
+                                                         IBus.PropState.CHECKED);
                         } else {
                             group[i].setOrnament(PopupMenu.Ornament.NONE);
                             group[i].prop.set_state(IBus.PropState.UNCHECKED);
-                            this._ibusManager.activateProperty(group[i].prop.get_key(),
-                                                               IBus.PropState.UNCHECKED);
+                            ibusManager.activateProperty(group[i].prop.get_key(),
+                                                         IBus.PropState.UNCHECKED);
                         }
                     }
                 }));
@@ -570,12 +663,12 @@ const InputSourceIndicator = new Lang.Class({
                 item.connect('toggled', Lang.bind(this, function() {
                     if (item.state) {
                         item.prop.set_state(IBus.PropState.CHECKED);
-                        this._ibusManager.activateProperty(item.prop.get_key(),
-                                                           IBus.PropState.CHECKED);
+                        ibusManager.activateProperty(item.prop.get_key(),
+                                                     IBus.PropState.CHECKED);
                     } else {
                         item.prop.set_state(IBus.PropState.UNCHECKED);
-                        this._ibusManager.activateProperty(item.prop.get_key(),
-                                                           IBus.PropState.UNCHECKED);
+                        ibusManager.activateProperty(item.prop.get_key(),
+                                                     IBus.PropState.UNCHECKED);
                     }
                 }));
                 break;
@@ -584,8 +677,8 @@ const InputSourceIndicator = new Lang.Class({
                 item = new PopupMenu.PopupMenuItem(prop.get_label().get_text());
                 item.prop = prop;
                 item.connect('activate', Lang.bind(this, function() {
-                    this._ibusManager.activateProperty(item.prop.get_key(),
-                                                       item.prop.get_state());
+                    ibusManager.activateProperty(item.prop.get_key(),
+                                                 item.prop.get_state());
                 }));
                 break;
 
@@ -603,80 +696,31 @@ const InputSourceIndicator = new Lang.Class({
         }
     },
 
-    _getNewInputSource: function(current) {
-        for (let i in this._inputSources) {
-            let is = this._inputSources[i];
-            if (is.type == current.type &&
-                is.id == current.id)
-                return is;
-        }
-        return this._currentSource;
-    },
-
-    _getCurrentWindow: function() {
-        if (Main.overview.visible)
-            return Main.overview;
-        else
-            return global.display.focus_window;
-    },
-
-    _setPerWindowInputSource: function() {
-        let window = this._getCurrentWindow();
-        if (!window)
-            return;
-
-        if (!window._inputSources) {
-            window._inputSources = this._inputSources;
-            window._currentSource = this._currentSource;
-        } else if (window._inputSources == this._inputSources) {
-            window._currentSource.activate();
-        } else {
-            window._inputSources = this._inputSources;
-            window._currentSource = this._getNewInputSource(window._currentSource);
-            window._currentSource.activate();
-        }
-    },
-
-    _sourcesPerWindowChanged: function() {
-        this._sourcesPerWindow = this._settings.get_boolean('per-window');
+    _showLayout: function() {
+        Main.overview.hide();
 
-        if (this._sourcesPerWindow && this._focusWindowNotifyId == 0) {
-            this._focusWindowNotifyId = global.display.connect('notify::focus-window',
-                                                               Lang.bind(this, 
this._setPerWindowInputSource));
-            this._overviewShowingId = Main.overview.connect('showing',
-                                                            Lang.bind(this, this._setPerWindowInputSource));
-            this._overviewHiddenId = Main.overview.connect('hidden',
-                                                           Lang.bind(this, this._setPerWindowInputSource));
-        } else if (!this._sourcesPerWindow && this._focusWindowNotifyId != 0) {
-            global.display.disconnect(this._focusWindowNotifyId);
-            this._focusWindowNotifyId = 0;
-            Main.overview.disconnect(this._overviewShowingId);
-            this._overviewShowingId = 0;
-            Main.overview.disconnect(this._overviewHiddenId);
-            this._overviewHiddenId = 0;
+        let source = this._inputSourceManager.currentSource;
+        let xkbLayout = '';
+        let xkbVariant = '';
 
-            let windows = global.get_window_actors().map(function(w) {
-                return w.meta_window;
-            });
-            for (let i = 0; i < windows.length; ++i) {
-                delete windows[i]._inputSources;
-                delete windows[i]._currentSource;
+        if (source.type == INPUT_SOURCE_TYPE_XKB) {
+            [, , , xkbLayout, xkbVariant] = KeyboardManager.getXkbInfo().get_layout_info(source.id);
+        } else if (source.type == INPUT_SOURCE_TYPE_IBUS) {
+            let engineDesc = IBusManager.getIBusManager().getEngineDesc(source.id);
+            if (engineDesc) {
+                xkbLayout = engineDesc.get_layout();
+                xkbVariant = '';
             }
-            delete Main.overview._inputSources;
-            delete Main.overview._currentSource;
         }
-    },
 
-    _changePerWindowSource: function() {
-        if (!this._sourcesPerWindow)
+        if (!xkbLayout || xkbLayout.length == 0)
             return;
 
-        let window = this._getCurrentWindow();
-        if (!window)
-            return;
+        let description = xkbLayout;
+        if (xkbVariant.length > 0)
+            description = description + '\t' + xkbVariant;
 
-        window._inputSources = this._inputSources;
-        window._currentSource = this._currentSource;
+        Util.spawn(['gkbd-keyboard-display', '-l', description]);
     },
 
     _containerGetPreferredWidth: function(container, for_height, alloc) {
@@ -685,8 +729,8 @@ const InputSourceIndicator = new Lang.Class({
         // for those we don't actually display.
         let max_min_width = 0, max_natural_width = 0;
 
-        for (let i in this._inputSources) {
-            let is = this._inputSources[i];
+        for (let i in this._inputSourceManager.inputSources) {
+            let is = this._inputSourceManager.inputSources[i];
             let [min_width, natural_width] = is.indicatorLabel.get_preferred_width(for_height);
             max_min_width = Math.max(max_min_width, min_width);
             max_natural_width = Math.max(max_natural_width, natural_width);
@@ -699,8 +743,8 @@ const InputSourceIndicator = new Lang.Class({
     _containerGetPreferredHeight: function(container, for_width, alloc) {
         let max_min_height = 0, max_natural_height = 0;
 
-        for (let i in this._inputSources) {
-            let is = this._inputSources[i];
+        for (let i in this._inputSourceManager.inputSources) {
+            let is = this._inputSourceManager.inputSources[i];
             let [min_height, natural_height] = is.indicatorLabel.get_preferred_height(for_width);
             max_min_height = Math.max(max_min_height, min_height);
             max_natural_height = Math.max(max_natural_height, natural_height);
@@ -717,8 +761,8 @@ const InputSourceIndicator = new Lang.Class({
         box.y2 -= box.y1;
         box.y1 = 0;
 
-        for (let i in this._inputSources) {
-            let is = this._inputSources[i];
+        for (let i in this._inputSourceManager.inputSources) {
+            let is = this._inputSourceManager.inputSources[i];
             is.indicatorLabel.allocate_align_fill(box, 0.5, 0.5, false, false, flags);
         }
     }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]