[polari] mainWindow: Make entryArea a per-room widget



commit 76d403778a246a3621577a3a2b84c802b73df867
Author: Florian Müllner <fmuellner gnome org>
Date:   Mon Dec 9 10:36:25 2013 +0100

    mainWindow: Make entryArea a per-room widget
    
    Currently when the main input entry contains text when switching
    rooms, the text is taken over to the new room, which is unexpected.
    Rather than keeping track of uncommitted input and saving/restoring
    it on room switches, split out the entryArea code and use one for
    each room as we already do for the chat log.

 data/resources/main-window.ui |   37 +--------
 src/Makefile.am               |    1 +
 src/entryArea.js              |  181 +++++++++++++++++++++++++++++++++++++++++
 src/mainWindow.js             |  139 ++++----------------------------
 4 files changed, 199 insertions(+), 159 deletions(-)
---
diff --git a/data/resources/main-window.ui b/data/resources/main-window.ui
index 4068172..a073cd3 100644
--- a/data/resources/main-window.ui
+++ b/data/resources/main-window.ui
@@ -390,43 +390,8 @@
                   <class name="polari-input-area"/>
                 </style>
                 <child>
-                  <object class="GtkBox" id="box4">
+                  <object class="GtkStack" id="input_area_stack">
                     <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="margin_left">6</property>
-                    <property name="margin_right">6</property>
-                    <property name="margin_top">6</property>
-                    <property name="margin_bottom">6</property>
-                    <style>
-                      <class name="linked"/>
-                    </style>
-                    <child>
-                      <object class="GtkEntry" id="nick_entry">
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <style>
-                          <class name="dim-label"/>
-                        </style>
-                      </object>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">True</property>
-                        <property name="position">0</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <object class="GtkEntry" id="message_entry">
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="hexpand">True</property>
-                        <property name="activates_default">True</property>
-                      </object>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">True</property>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
                   </object>
                 </child>
               </object>
diff --git a/src/Makefile.am b/src/Makefile.am
index da04646..9b7f594 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -33,6 +33,7 @@ dist_js_DATA = \
        chatroomManager.js \
        chatView.js \
        connections.js \
+       entryArea.js \
        ircParser.js \
        joinDialog.js \
        main.js \
diff --git a/src/entryArea.js b/src/entryArea.js
new file mode 100644
index 0000000..d160d26
--- /dev/null
+++ b/src/entryArea.js
@@ -0,0 +1,181 @@
+const Gdk = imports.gi.Gdk;
+const Gtk = imports.gi.Gtk;
+
+const ChatroomManager = imports.chatroomManager;
+const ChatView = imports.chatView;
+const IrcParser = imports.ircParser;
+const Lang = imports.lang;
+const Mainloop = imports.mainloop;
+const TabCompletion = imports.tabCompletion;
+const Tp = imports.gi.TelepathyGLib;
+
+const MAX_NICK_UPDATE_TIME = 5; /* s */
+
+
+const EntryArea = new Lang.Class({
+    Name: 'EntryArea',
+
+    _init: function(room) {
+        this._createWidget();
+
+        this._ircParser = new IrcParser.IrcParser();
+
+        this._room = room;
+
+        this._roomManager = new ChatroomManager.getDefault();
+        this._activeRoomChangedId =
+            this._roomManager.connect('active-changed',
+                                      Lang.bind(this, this._activeRoomChanged));
+
+        if (!room)
+            return;
+
+        this._completion = new TabCompletion.TabCompletion(this._entry);
+        this._membersChangedId =
+            this._room.connect('members-changed',
+                               Lang.bind(this, this._updateCompletions));
+        this._nicknameChangedId =
+            this._room.channel.connection.connect('notify::self-contact',
+                                                  Lang.bind(this,
+                                                            this._updateNick));
+        this._updateCompletions();
+        this._updateNick();
+    },
+
+    _createWidget: function() {
+        this.widget = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL,
+                                    sensitive: false,
+                                    margin: 6 });
+        this.widget.get_style_context().add_class('linked');
+
+        this.widget.connect('destroy', Lang.bind(this, this._onDestroy));
+
+        this._nickEntry = new Gtk.Entry();
+        this._nickEntry.width_chars = ChatView.MAX_NICK_CHARS
+        this._nickEntry.get_style_context().add_class('dim-label');
+        this.widget.add(this._nickEntry);
+
+        this._nickEntry.connect('activate', Lang.bind(this,
+            function() {
+               if (this._nickEntry.text)
+                   this._setNick(this._nickEntry.text);
+               this._entry.grab_focus();
+            }));
+        this._nickEntry.connect('focus-out-event', Lang.bind(this,
+             function() {
+               this._nickEntry.text = '';
+               return false;
+            }));
+        this._nickEntry.connect_after('key-press-event', Lang.bind(this,
+            function(w, event) {
+                let [, keyval] = event.get_keyval();
+                if (keyval == Gdk.KEY_Escape) {
+                    this._entry.grab_focus();
+                    return true;
+                }
+                return false;
+            }));
+
+        this._entry = new Gtk.Entry({ hexpand: true,
+                                      activates_default: true });
+        this.widget.add(this._entry);
+
+        this._entry.connect('activate', Lang.bind(this,
+            function() {
+                this._ircParser.process(this._entry.text);
+                this._entry.text = '';
+            }));
+        this._entry.connect('notify::is-focus', Lang.bind(this,
+            function() {
+                if (!this.widget.sensitive)
+                    return;
+                // HACK: force focus to the entry unless it was
+                //       moved by keynav or moved to another entry
+                if (this.widget.get_toplevel().get_focus() instanceof Gtk.Entry)
+                    return;
+                let device = Gtk.get_current_event_device();
+                if (!device || device.get_source() == Gdk.InputSource.KEYBOARD)
+                    return;
+                this._entry.grab_focus();
+            }));
+
+
+        this.widget.show_all();
+    },
+
+    _updateCompletions: function() {
+        let nicks = [];
+
+        if (this._room &&
+            this._room.channel.has_interface(Tp.IFACE_CHANNEL_INTERFACE_GROUP)) {
+            let members = this._room.channel.group_dup_members_contacts();
+            nicks = members.map(function(member) { return member.alias; });
+        }
+        this._completion.setCompletions(nicks);
+    },
+
+    _activeRoomChanged: function(manager, room) {
+        this.widget.sensitive = this._room && this._room == room;
+
+        if (!this.widget.sensitive)
+            return;
+
+        Mainloop.idle_add(Lang.bind(this,
+            function() {
+                this._entry.grab_focus();
+                return false;
+            }));
+    },
+
+    _setNick: function(nick) {
+        this._nickEntry.width_chars = Math.max(nick.length, ChatView.MAX_NICK_CHARS)
+        this._nickEntry.placeholder_text = nick;
+
+        let account = this._room.channel.connection.get_account();
+        account.set_nickname_async(nick, Lang.bind(this,
+            function(a, res) {
+                try {
+                    a.set_nickname_finish(res);
+                } catch(e) {
+                    logError(e, "Failed to change nick");
+
+                    this._updateNick();
+                    return;
+                }
+
+                // TpAccount:nickname is a local property which doesn't
+                // necessarily match the externally visible nick; telepathy
+                // doesn't consider failing to sync the two an error, so
+                // we give the server MAX_NICK_UPDATE_TIME seconds until
+                // we assume failure and revert back to the server nick
+                //
+                // (set_aliases() would do what we want, but it's not
+                // introspected)
+                Mainloop.timeout_add_seconds(MAX_NICK_UPDATE_TIME,
+                    Lang.bind(this, function() {
+                        this._updateNick();
+                        return false;
+                    }));
+            }));
+    },
+
+    _updateNick: function() {
+        let nick = this._room ? this._room.channel.connection.self_contact.alias
+                              : '';
+
+        this._nickEntry.width_chars = Math.max(nick.length, ChatView.MAX_NICK_CHARS)
+        this._nickEntry.placeholder_text = nick;
+    },
+
+    _onDestroy: function() {
+        this._roomManager.disconnect(this._activeRoomChangedId);
+        this._activeRoomChangedId = 0;
+
+        if (this._membersChangedId)
+            this._room.disconnect(this._membersChangedId);
+        this._membersChangedId = 0;
+        if (this._nicknameChangedId)
+            this._room.channel.connection.disconnect(this._nicknameChangedId);
+        this._nicknameChangedId = 0;
+    }
+});
diff --git a/src/mainWindow.js b/src/mainWindow.js
index ed9808f..cd51687 100644
--- a/src/mainWindow.js
+++ b/src/mainWindow.js
@@ -8,17 +8,15 @@ const AccountsMonitor = imports.accountsMonitor;
 const AppNotifications = imports.appNotifications;
 const ChatroomManager = imports.chatroomManager;
 const ChatView = imports.chatView;
-const IrcParser = imports.ircParser;
+const EntryArea = imports.entryArea;
 const JoinDialog = imports.joinDialog;
 const Lang = imports.lang;
 const Mainloop = imports.mainloop;
 const MessageDialog = imports.messageDialog;
 const RoomList = imports.roomList;
-const TabCompletion = imports.tabCompletion;
 const UserList = imports.userList;
 const Utils = imports.utils;
 
-const MAX_NICK_UPDATE_TIME = 5; /* s */
 const CONFIGURE_TIMEOUT = 100; /* ms */
 
 
@@ -37,8 +35,6 @@ const MainWindow = new Lang.Class({
         overlay.add_overlay(app.notificationQueue.widget);
         overlay.add_overlay(app.commandOutputQueue.widget);
 
-        this._ircParser = new IrcParser.IrcParser();
-
         this._accountsMonitor = new AccountsMonitor.getDefault();
         this._accountsMonitor.connect('account-status-changed',
                                       Lang.bind(this, this._onAccountChanged));
@@ -54,13 +50,13 @@ const MainWindow = new Lang.Class({
                                   Lang.bind(this, this._activeRoomChanged));
 
         this._rooms = {};
+        this._entries = {};
 
         this._room = null;
         this._settings = new Gio.Settings({ schema: 'org.gnome.polari' });
 
         this._displayNameChangedId = 0;
         this._topicChangedId = 0;
-        this._nicknameChangedId = 0;
         this._configureId = 0;
 
         this._titlebarRight = builder.get_object('titlebar_right');
@@ -73,12 +69,10 @@ const MainWindow = new Lang.Class({
         this._showUserListButton = builder.get_object('show_user_list_button');
         this._revealer = builder.get_object('room_list_revealer');
         this._chatStack = builder.get_object('chat_stack');
-        this._inputArea = builder.get_object('main_input_area');
-        this._nickEntry = builder.get_object('nick_entry');
-        this._entry = builder.get_object('message_entry');
+        this._inputStack = builder.get_object('input_area_stack');
 
-        this._nickEntry.width_chars = ChatView.MAX_NICK_CHARS
-        this._completion = new TabCompletion.TabCompletion(this._entry);
+        let placeholderEntry = new EntryArea.EntryArea(null);
+        this._inputStack.add_named(placeholderEntry.widget, 'placeholder');
 
         let scroll = builder.get_object('room_list_scrollview');
         this._roomList = new RoomList.RoomList();
@@ -104,43 +98,6 @@ const MainWindow = new Lang.Class({
 
         this._userListAction = app.lookup_action('user-list');
 
-        this._entry.connect('activate', Lang.bind(this,
-            function() {
-                this._ircParser.process(this._entry.text);
-                this._entry.text = '';
-            }));
-        this._entry.connect('notify::is-focus', Lang.bind(this,
-            function() {
-                // HACK: force focus to the entry unless it was
-                //       moved by keynav or moved to another entry
-                if (this.window.get_focus() instanceof Gtk.Entry)
-                    return;
-                let device = Gtk.get_current_event_device();
-                if (!device || device.get_source() == Gdk.InputSource.KEYBOARD)
-                    return;
-                this._entry.grab_focus();
-            }));
-
-        this._nickEntry.connect('activate', Lang.bind(this,
-            function() {
-               if (this._nickEntry.text)
-                   this._setNick(this._nickEntry.text);
-               this._entry.grab_focus();
-            }));
-        this._nickEntry.connect('focus-out-event', Lang.bind(this,
-             function() {
-               this._nickEntry.text = '';
-               return false;
-            }));
-        this._nickEntry.connect_after('key-press-event', Lang.bind(this,
-            function(w, event) {
-                let [, keyval] = event.get_keyval();
-                if (keyval == Gdk.KEY_Escape) {
-                    this._entry.grab_focus();
-                    return true;
-                }
-                return false;
-            }));
         this.window.connect_after('key-press-event', Lang.bind(this,
             function(w, event) {
                 let [, keyval] = event.get_keyval();
@@ -172,8 +129,6 @@ const MainWindow = new Lang.Class({
         if (this._settings.get_boolean('window-maximized'))
             this.window.maximize();
 
-        this._updateSensitivity();
-
         this.window.show_all();
     },
 
@@ -271,31 +226,36 @@ const MainWindow = new Lang.Class({
         this._rooms[room.id] = chatView;
 
         this._chatStack.add_named(chatView.widget, room.id);
+
+        let entryArea = new EntryArea.EntryArea(room);
+        this._entries[room.id] = entryArea;
+
+        this._inputStack.add_named(entryArea.widget, room.id);
     },
 
     _roomRemoved: function(roomManager, room) {
         this._rooms[room.id].widget.destroy();
         delete this._rooms[room.id];
+
+        this._entries[room.id].widget.destroy();
+        delete this._entries[room.id];
     },
 
     _activeRoomChanged: function(manager, room) {
         if (this._room) {
             this._room.disconnect(this._displayNameChangedId);
             this._room.disconnect(this._topicChangedId);
-            this._room.disconnect(this._membersChangedId);
-            this._room.channel.connection.disconnect(this._nicknameChangedId);
         }
         this._displayNameChangedId = 0;
         this._topicChangedId = 0;
-        this._nicknameChangedId = 0;
 
         this._room = room;
         this._revealer.reveal_child = room != null;
 
         this._updateTitlebar();
-        this._updateNick();
-        this._updateSensitivity();
-        this._updateCompletions();
+
+        this._inputStack.set_visible_child_name(this._room ? this._room.id
+                                                           : 'placeholder');
 
         if (!this._room)
             return; // finished
@@ -306,49 +266,10 @@ const MainWindow = new Lang.Class({
         this._topicChangedId =
             this._room.connect('notify::topic',
                                Lang.bind(this, this._updateTitlebar));
-        this._membersChangedId =
-            this._room.connect('members-changed',
-                               Lang.bind(this, this._updateCompletions));
-        this._nicknameChangedId =
-            this._room.channel.connection.connect('notify::self-contact',
-                                                  Lang.bind(this,
-                                                            this._updateNick));
 
         this._chatStack.set_visible_child_name(this._room.id);
     },
 
-    _setNick: function(nick) {
-        this._nickEntry.width_chars = Math.max(nick.length, ChatView.MAX_NICK_CHARS)
-        this._nickEntry.placeholder_text = nick;
-
-        let account = this._room.channel.connection.get_account();
-        account.set_nickname_async(nick, Lang.bind(this,
-            function(a, res) {
-                try {
-                    a.set_nickname_finish(res);
-                } catch(e) {
-                    logError(e, "Failed to change nick");
-
-                    this._updateNick();
-                    return;
-                }
-
-                // TpAccount:nickname is a local property which doesn't
-                // necessarily match the externally visible nick; telepathy
-                // doesn't consider failing to sync the two an error, so
-                // we give the server MAX_NICK_UPDATE_TIME seconds until
-                // we assume failure and revert back to the server nick
-                //
-                // (set_aliases() would do what we want, but it's not
-                // introspected)
-                Mainloop.timeout_add_seconds(MAX_NICK_UPDATE_TIME,
-                    Lang.bind(this, function() {
-                        this._updateNick();
-                        return false;
-                    }));
-            }));
-    },
-
     showJoinRoomDialog: function() {
         let dialog = new JoinDialog.JoinDialog();
         dialog.widget.transient_for = this.window;
@@ -369,17 +290,6 @@ const MainWindow = new Lang.Class({
             });
     },
 
-    _updateCompletions: function() {
-        let nicks = [];
-
-        if (this._room &&
-            this._room.channel.has_interface(Tp.IFACE_CHANNEL_INTERFACE_GROUP)) {
-            let members = this._room.channel.group_dup_members_contacts();
-            nicks = members.map(function(member) { return member.alias; });
-        }
-        this._completion.setCompletions(nicks);
-    },
-
     _updateTitlebar: function() {
         let subtitle = '';
         if (this._room && this._room.topic) {
@@ -399,22 +309,5 @@ const MainWindow = new Lang.Class({
         this._subtitleLabel.visible = subtitle.length > 0;
 
         this._titleLabel.label = this._room ? this._room.display_name : null;
-    },
-
-    _updateNick: function() {
-        let nick = this._room ? this._room.channel.connection.self_contact.alias
-                              : '';
-
-        this._nickEntry.width_chars = Math.max(nick.length, ChatView.MAX_NICK_CHARS)
-        this._nickEntry.placeholder_text = nick;
-    },
-
-    _updateSensitivity: function() {
-        this._inputArea.sensitive = this._room != null;
-
-        if (!this._inputArea.sensitive)
-            return;
-
-        this._entry.grab_focus();
     }
 });


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