[polari] Port to GTK4



commit 3b4e03a2ebc2cca7ef57b4c41f85f64cda65395b
Author: Florian Müllner <fmuellner gnome org>
Date:   Thu Sep 24 17:23:30 2020 +0200

    Port to GTK4
    
    It's finally time to turn the switch!
    
    After all the previous preparations, the changeset size is fairly
    manageable; still, git's --ignore-all-space option is advised ;-)
    
    Part-of: <https://gitlab.gnome.org/GNOME/polari/-/merge_requests/232>

 data/resources/connection-details.ui    |  59 ++++---
 data/resources/connection-properties.ui |   6 +-
 data/resources/entry-area.ui            | 201 +++++++++++-----------
 data/resources/help-overlay.ui          |   1 -
 data/resources/initial-setup-window.ui  | 241 ++++++++++++++-------------
 data/resources/join-room-dialog.ui      | 285 +++++++++++++++++---------------
 data/resources/main-window.ui           |  71 ++++----
 data/resources/nick-popover.ui          |   5 +-
 data/resources/room-list-header.ui      | 132 +++++++--------
 data/resources/room-list-row.ui         |  76 +++++----
 data/resources/user-details.ui          |  66 ++++----
 src/appNotifications.js                 |   2 +-
 src/application.js                      |   4 +-
 src/chatView.js                         |  98 +++++------
 src/entryArea.js                        |  14 +-
 src/initialSetup.js                     |   1 -
 src/joinDialog.js                       |   2 +-
 src/main.js                             |   2 +-
 src/mainWindow.js                       |  45 +++--
 src/roomList.js                         |  14 +-
 src/roomStack.js                        |   4 +-
 src/tabCompletion.js                    |   8 +-
 22 files changed, 672 insertions(+), 665 deletions(-)
---
diff --git a/data/resources/connection-details.ui b/data/resources/connection-details.ui
index 2daeb001..7b69c67c 100644
--- a/data/resources/connection-details.ui
+++ b/data/resources/connection-details.ui
@@ -4,7 +4,7 @@
     <property name="visible">True</property>
     <property name="row-spacing">6</property>
     <property name="column-spacing">12</property>
-        <child>
+    <child>
       <object class="GtkLabel" id="server_label">
         <property name="visible" bind-source="Gjs_ConnectionDetails"
                   bind-property="has-service"
@@ -33,11 +33,11 @@
         <property name="label" translatable="yes">Net_work Name</property>
         <property name="use-underline">True</property>
         <property name="mnemonic-widget">nameEntry</property>
+        <layout>
+          <property name="column">0</property>
+          <property name="row">1</property>
+        </layout>
       </object>
-      <packing>
-        <property name="left-attach">0</property>
-        <property name="top-attach">1</property>
-      </packing>
     </child>
     <child>
       <object class="GtkEntry" id="nameEntry">
@@ -47,11 +47,11 @@
         <property name="hexpand">True</property>
         <property name="activates-default">True</property>
         <property name="placeholder-text" translatable="yes">optional</property>
+        <layout>
+          <property name="column">1</property>
+          <property name="row">1</property>
+        </layout>
       </object>
-      <packing>
-        <property name="left-attach">1</property>
-        <property name="top-attach">1</property>
-      </packing>
     </child>
     <child>
       <object class="GtkCheckButton" id="sslCheckbox">
@@ -61,11 +61,11 @@
         <property name="label" translatable="yes">Use secure c_onnection</property>
         <property name="margin-bottom">24</property>
         <property name="use-underline">True</property>
+        <layout>
+          <property name="column">1</property>
+          <property name="row">2</property>
+        </layout>
       </object>
-      <packing>
-        <property name="left-attach">1</property>
-        <property name="top-attach">2</property>
-      </packing>
     </child>
     <child>
       <object class="GtkLabel" id="nickname_label">
@@ -74,23 +74,22 @@
         <property name="label" translatable="yes">_Nickname</property>
         <property name="use-underline">True</property>
         <property name="mnemonic-widget">nickEntry</property>
+        <layout>
+          <property name="column">0</property>
+          <property name="row">3</property>
+        </layout>
       </object>
-      <packing>
-        <property name="left-attach">0</property>
-        <property name="top-attach">3</property>
-      </packing>
     </child>
     <child>
       <object class="GtkEntry" id="nickEntry">
         <property name="visible">True</property>
         <property name="hexpand">True</property>
-        <property name="can-default">True</property>
         <property name="activates-default">True</property>
+        <layout>
+          <property name="column">1</property>
+          <property name="row">3</property>
+        </layout>
       </object>
-      <packing>
-        <property name="left-attach">1</property>
-        <property name="top-attach">3</property>
-      </packing>
     </child>
     <child>
       <object class="GtkLabel" id="realname_label">
@@ -99,11 +98,11 @@
         <property name="label" translatable="yes">_Real Name</property>
         <property name="use-underline">True</property>
         <property name="mnemonic-widget">realnameEntry</property>
+        <layout>
+          <property name="column">0</property>
+          <property name="row">4</property>
+        </layout>
       </object>
-      <packing>
-        <property name="left-attach">0</property>
-        <property name="top-attach">4</property>
-      </packing>
     </child>
     <child>
       <object class="GtkEntry" id="realnameEntry">
@@ -112,11 +111,11 @@
         <property name="activates-default">True</property>
         <property name="width-chars">30</property>
         <property name="placeholder-text" translatable="yes">optional</property>
+        <layout>
+          <property name="column">1</property>
+          <property name="row">4</property>
+        </layout>
       </object>
-      <packing>
-        <property name="left-attach">1</property>
-        <property name="top-attach">4</property>
-      </packing>
     </child>
   </template>
 </interface>
diff --git a/data/resources/connection-properties.ui b/data/resources/connection-properties.ui
index 1fdae4e7..d8db205e 100644
--- a/data/resources/connection-properties.ui
+++ b/data/resources/connection-properties.ui
@@ -16,16 +16,14 @@
       <object class="GtkButton" id="applyButton">
         <property name="label" translatable="yes">_Apply</property>
         <property name="visible">True</property>
-        <property name="can-default">True</property>
         <property name="receives-default">True</property>
         <property name="use-underline">True</property>
         <property name="sensitive" bind-source="details"
                   bind-property="can-confirm" bind-flags="sync-create"/>
       </object>
     </child>
-    <child internal-child="vbox">
+    <child internal-child="content_area">
       <object class="GtkBox">
-        <property name="can-focus">False</property>
         <property name="orientation">vertical</property>
         <property name="spacing">24</property>
         <child>
@@ -47,7 +45,7 @@
               <object class="GtkImage" id="image1">
                 <property name="visible">True</property>
                 <property name="icon-name">dialog-error-symbolic</property>
-                <property name="icon-size">5</property>
+                <property name="icon-size">large</property>
                 <property name="margin-start">24</property>
                 <property name="margin-end">12</property>
                 <property name="margin-top">12</property>
diff --git a/data/resources/entry-area.ui b/data/resources/entry-area.ui
index d985e584..27a33f9d 100644
--- a/data/resources/entry-area.ui
+++ b/data/resources/entry-area.ui
@@ -9,126 +9,127 @@
       <class name="view"/>
     </style>
     <child>
-      <object class="GtkBox">
-        <property name="visible">True</property>
-        <property name="spacing">6</property>
-        <property name="margin-start">6</property>
-        <property name="margin-end">14</property>
-        <property name="margin-top">6</property>
-        <property name="margin-bottom">6</property>
-        <child>
-          <object class="GtkToggleButton" id="nickButton">
+      <object class="GtkStackPage">
+        <property name="name">default</property>
+        <property name="child">
+          <object class="GtkBox">
             <property name="visible">True</property>
-            <property name="receives-default">True</property>
-            <property name="focus-on-click">False</property>
-            <style>
-              <class name="flat"/>
-              <class name="polari-nick-button"/>
-            </style>
+            <property name="spacing">6</property>
+            <property name="margin-start">6</property>
+            <property name="margin-end">14</property>
+            <property name="margin-top">6</property>
+            <property name="margin-bottom">6</property>
             <child>
-              <object class="GtkLabel" id="nickLabel">
+              <object class="GtkToggleButton" id="nickButton">
                 <property name="visible">True</property>
-                <property name="xalign">0</property>
-                <attributes>
-                  <attribute name="weight" value="PANGO_WEIGHT_BOLD"/>
-                </attributes>
+                <property name="receives-default">True</property>
+                <property name="focus-on-click">False</property>
+                <style>
+                  <class name="flat"/>
+                  <class name="polari-nick-button"/>
+                </style>
+                <child>
+                  <object class="GtkLabel" id="nickLabel">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <attributes>
+                      <attribute name="weight" value="PANGO_WEIGHT_BOLD"/>
+                    </attributes>
+                  </object>
+                </child>
+                <accessibility>
+                  <property name="label" translatable="yes">Change nickname</property>
+                </accessibility>
               </object>
             </child>
-            <child internal-child="accessible">
-              <object class="AtkObject">
-                <property name="AtkObject::accessible-name"
-                          translatable="yes">Change nickname</property>
+            <child>
+              <object class="Gjs_ChatEntry" id="chatEntry">
+                <property name="visible">True</property>
+                <property name="hexpand">True</property>
+                <property name="show-emoji-icon">True</property>
               </object>
             </child>
           </object>
-        </child>
-        <child>
-          <object class="Gjs_ChatEntry" id="chatEntry">
-            <property name="visible">True</property>
-            <property name="hexpand">True</property>
-            <property name="show-emoji-icon">True</property>
-          </object>
-        </child>
+        </property>
       </object>
-      <packing>
-        <property name="name">default</property>
-      </packing>
     </child>
     <child>
-      <object class="GtkBox" id="pasteBox">
-        <property name="visible">True</property>
-        <property name="spacing">6</property>
-        <style>
-          <class name="background"/>
-          <class name="polari-paste-confirmation"/>
-        </style>
-        <child>
-          <object class="GtkSpinner" id="uploadSpinner">
-            <property name="visible">True</property>
-          </object>
-        </child>
-        <child>
-          <object class="GtkLabel" id="uploadLabel">
-            <property name="visible" bind-source="confirmLabel"
-                      bind-property="visible"
-                      bind-flags="sync-create|invert-boolean" />
-            <property name="halign">start</property>
-            <property name="hexpand">True</property>
-            <property name="ellipsize">end</property>
-            <property name="xalign">0</property>
-          </object>
-        </child>
-        <child>
-          <object class="GtkLabel" id="confirmLabel">
-            <property name="halign">start</property>
-            <property name="hexpand">True</property>
-            <property name="ellipsize">end</property>
-            <property name="xalign">0</property>
-            <property name="visible">False</property>
-          </object>
-        </child>
-        <child>
-          <object class="GtkRevealer">
+      <object class="GtkStackPage">
+        <property name="name">paste-confirmation</property>
+        <property name="child">
+          <object class="GtkBox" id="pasteBox">
             <property name="visible">True</property>
-            <property name="transition-type">slide-left</property>
-            <property name="reveal-child" bind-source="confirmLabel"
-                      bind-property="visible" bind-flags="sync-create" />
+            <property name="spacing">6</property>
+            <style>
+              <class name="background"/>
+              <class name="polari-paste-confirmation"/>
+            </style>
             <child>
-              <object class="GtkBox">
+              <object class="GtkSpinner" id="uploadSpinner">
                 <property name="visible">True</property>
-                <property name="spacing">6</property>
-                <property name="margin-end">14</property>
-                <child>
-                  <object class="GtkButton" id="cancelButton">
-                    <property name="label" translatable="yes">_Cancel</property>
-                    <property name="visible">True</property>
-                    <property name="receives-default">True</property>
-                    <property name="use-underline">True</property>
-                    <property name="sensitive" bind-source="confirmLabel"
-                              bind-property="visible" bind-flags="sync-create" />
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkButton" id="pasteButton">
-                    <property name="label" translatable="yes">_Paste</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkLabel" id="uploadLabel">
+                <property name="visible" bind-source="confirmLabel"
+                          bind-property="visible"
+                          bind-flags="sync-create|invert-boolean"/>
+                <property name="halign">start</property>
+                <property name="hexpand">True</property>
+                <property name="ellipsize">end</property>
+                <property name="xalign">0</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkLabel" id="confirmLabel">
+                <property name="halign">start</property>
+                <property name="hexpand">True</property>
+                <property name="ellipsize">end</property>
+                <property name="xalign">0</property>
+                <property name="visible">False</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkRevealer">
+                <property name="visible">True</property>
+                <property name="transition-type">slide-left</property>
+                <property name="reveal-child" bind-source="confirmLabel"
+                          bind-property="visible" bind-flags="sync-create"/>
+                <property name="child">
+                  <object class="GtkBox">
                     <property name="visible">True</property>
-                    <property name="receives-default">True</property>
-                    <property name="use-underline">True</property>
-                    <property name="sensitive" bind-source="confirmLabel"
-                              bind-property="visible" bind-flags="sync-create" />
-                    <style>
-                      <class name="suggested-action"/>
-                    </style>
+                    <property name="spacing">6</property>
+                    <property name="margin-end">14</property>
+                    <child>
+                      <object class="GtkButton" id="cancelButton">
+                        <property name="label" translatable="yes">_Cancel</property>
+                        <property name="visible">True</property>
+                        <property name="receives-default">True</property>
+                        <property name="use-underline">True</property>
+                        <property name="sensitive" bind-source="confirmLabel"
+                                  bind-property="visible" bind-flags="sync-create" />
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkButton" id="pasteButton">
+                        <property name="label" translatable="yes">_Paste</property>
+                        <property name="visible">True</property>
+                        <property name="receives-default">True</property>
+                        <property name="use-underline">True</property>
+                        <property name="sensitive" bind-source="confirmLabel"
+                                  bind-property="visible" bind-flags="sync-create"/>
+                        <style>
+                          <class name="suggested-action"/>
+                        </style>
+                      </object>
+                    </child>
                   </object>
-                </child>
+                </property>
               </object>
             </child>
           </object>
-        </child>
+        </property>
       </object>
-      <packing>
-        <property name="name">paste-confirmation</property>
-      </packing>
     </child>
   </template>
 </interface>
diff --git a/data/resources/help-overlay.ui b/data/resources/help-overlay.ui
index cbac847c..198233bd 100644
--- a/data/resources/help-overlay.ui
+++ b/data/resources/help-overlay.ui
@@ -8,7 +8,6 @@
         <!-- The longer shortcut titles make GTK+ want to layout the groups
              vertically, but we really really want a horizontal layout -->
         <property name="max-height">10</property>
-        <!--property name="section-name">shortcuts</property-->
         <child>
           <object class="GtkShortcutsGroup">
             <property name="visible">True</property>
diff --git a/data/resources/initial-setup-window.ui b/data/resources/initial-setup-window.ui
index cba8302f..28de42b6 100644
--- a/data/resources/initial-setup-window.ui
+++ b/data/resources/initial-setup-window.ui
@@ -4,10 +4,11 @@
     <property name="icon-name">org.gnome.Polari</property>
     <property name="default-width">800</property>
     <property name="default-height">600</property>
+    <property name="default-widget">nextButton</property>
+    <property name="title" translatable="yes">Polari Setup</property>
     <child type="titlebar">
       <object class="GtkHeaderBar">
         <property name="visible">True</property>
-        <property name="title" translatable="yes">Polari Setup</property>
         <child>
           <object class="GtkButton" id="prevButton">
             <property name="visible">True</property>
@@ -15,16 +16,12 @@
             <property name="valign">center</property>
           </object>
         </child>
-        <child>
+        <child type="end">
           <object class="GtkButton" id="nextButton">
             <property name="visible">True</property>
             <property name="use-underline">True</property>
-            <property name="can-default">True</property>
             <property name="valign">center</property>
           </object>
-          <packing>
-            <property name="pack-type">end</property>
-          </packing>
         </child>
       </object>
     </child>
@@ -34,158 +31,164 @@
         <property name="transition-type">slide-left-right</property>
         <property name="vexpand">True</property>
         <child>
-          <object class="GtkGrid">
-            <property name="visible">True</property>
-            <property name="halign">center</property>
-            <property name="valign">center</property>
-            <property name="row_spacing">6</property>
-            <property name="column_spacing">18</property>
-            <child>
-              <object class="GtkImage">
-                <property name="visible">True</property>
-                <property name="pixel_size">64</property>
-                <property name="icon_name">network-offline-symbolic</property>
-                <style>
-                  <class name="dim-label"/>
-                </style>
-              </object>
-              <packing>
-                <property name="height">2</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkLabel">
-                <property name="visible">True</property>
-                <property name="halign">start</property>
-                <property name="label" translatable="yes">Not connected</property>
-                <style>
-                  <class name="polari-setup-title"/>
-                </style>
-              </object>
-              <packing>
-                <property name="left_attach">1</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkLabel">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="label" translatable="yes">Please connect to the internet to continue the 
setup.</property>
-                <property name="wrap">True</property>
-                <property name="max_width_chars">30</property>
-                <property name="xalign">0</property>
-                <style>
-                  <class name="dim-label"/>
-                </style>
-              </object>
-              <packing>
-                <property name="left_attach">1</property>
-                <property name="top_attach">1</property>
-              </packing>
-            </child>
-          </object>
-          <packing>
+          <object class="GtkStackPage">
             <property name="name">offline-hint</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkBox">
-            <property name="orientation">vertical</property>
-            <property name="visible">True</property>
-            <property name="vexpand">True</property>
-            <property name="spacing">24</property>
-            <style>
-              <class name="polari-setup-page"/>
-            </style>
-            <child>
-              <object class="GtkBox">
+            <property name="child">
+              <object class="GtkGrid">
                 <property name="visible">True</property>
-                <property name="orientation">vertical</property>
-                <property name="spacing">24</property>
                 <property name="halign">center</property>
+                <property name="valign">center</property>
+                <property name="row_spacing">6</property>
+                <property name="column_spacing">18</property>
+                <child>
+                  <object class="GtkImage">
+                    <property name="visible">True</property>
+                    <property name="pixel_size">64</property>
+                    <property name="icon_name">network-offline-symbolic</property>
+                    <style>
+                      <class name="dim-label"/>
+                    </style>
+                    <layout>
+                      <property name="row-span">2</property>
+                    </layout>
+                  </object>
+                </child>
                 <child>
                   <object class="GtkLabel">
                     <property name="visible">True</property>
-                    <property name="label" translatable="yes">Welcome to Polari</property>
+                    <property name="halign">start</property>
+                    <property name="label" translatable="yes">Not connected</property>
                     <style>
                       <class name="polari-setup-title"/>
                     </style>
+                    <layout>
+                      <property name="column">1</property>
+                    </layout>
                   </object>
                 </child>
                 <child>
                   <object class="GtkLabel">
                     <property name="visible">True</property>
-                    <property name="max-width-chars">42</property>
+                    <property name="can-focus">False</property>
+                    <property name="label" translatable="yes">Please connect to the internet to continue the 
setup.</property>
                     <property name="wrap">True</property>
-                    <property name="justify">center</property>
-                    <property name="label" translatable="yes">Polari is an easy way to chat using IRC. 
Select a network to get started.</property>
+                    <property name="max_width_chars">30</property>
+                    <property name="xalign">0</property>
                     <style>
                       <class name="dim-label"/>
                     </style>
+                    <layout>
+                      <property name="column">1</property>
+                      <property name="row">1</property>
+                    </layout>
                   </object>
                 </child>
               </object>
-            </child>
-            <child>
-              <object class="Gjs_ConnectionsList" id="connectionsList">
+            </property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkStackPage">
+            <property name="name">connections</property>
+            <property name="child">
+              <object class="GtkBox">
+                <property name="orientation">vertical</property>
                 <property name="visible">True</property>
-                <property name="vscrollbar-policy">never</property>
-                <property name="shadow-type">in</property>
-                <property name="favorites-only">True</property>
+                <property name="vexpand">True</property>
+                <property name="spacing">24</property>
+                <style>
+                  <class name="polari-setup-page"/>
+                </style>
+                <child>
+                  <object class="GtkBox">
+                    <property name="visible">True</property>
+                    <property name="orientation">vertical</property>
+                    <property name="spacing">24</property>
+                    <property name="halign">center</property>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Welcome to Polari</property>
+                        <style>
+                          <class name="polari-setup-title"/>
+                        </style>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="visible">True</property>
+                        <property name="max-width-chars">42</property>
+                        <property name="wrap">True</property>
+                        <property name="justify">center</property>
+                        <property name="label" translatable="yes">Polari is an easy way to chat using IRC. 
Select a network to get started.</property>
+                        <style>
+                          <class name="dim-label"/>
+                        </style>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child>
+                  <object class="Gjs_ConnectionsList" id="connectionsList">
+                    <property name="visible">True</property>
+                    <property name="vscrollbar-policy">never</property>
+                    <property name="has-frame">True</property>
+                    <property name="favorites-only">True</property>
+                  </object>
+                </child>
               </object>
-            </child>
+            </property>
           </object>
-          <packing>
-            <property name="name">connections</property>
-          </packing>
         </child>
         <child>
-          <object class="GtkBox">
-            <property name="orientation">vertical</property>
-            <property name="visible">True</property>
-            <property name="spacing">24</property>
-            <style>
-              <class name="polari-setup-page"/>
-            </style>
-            <child>
+          <object class="GtkStackPage">
+            <property name="name">rooms</property>
+            <property name="child">
               <object class="GtkBox">
-                <property name="visible">True</property>
                 <property name="orientation">vertical</property>
-                <property name="halign">center</property>
+                <property name="visible">True</property>
                 <property name="spacing">24</property>
+                <style>
+                  <class name="polari-setup-page"/>
+                </style>
                 <child>
-                  <object class="GtkLabel">
+                  <object class="GtkBox">
                     <property name="visible">True</property>
-                    <property name="label" translatable="yes">Welcome to Polari</property>
-                    <style>
-                      <class name="polari-setup-title"/>
-                    </style>
+                    <property name="orientation">vertical</property>
+                    <property name="halign">center</property>
+                    <property name="spacing">24</property>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Welcome to Polari</property>
+                        <style>
+                          <class name="polari-setup-title"/>
+                        </style>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="visible">True</property>
+                        <property name="max-width-chars">60</property>
+                        <property name="wrap">True</property>
+                        <property name="justify">center</property>
+                        <property name="label" translatable="yes">Select rooms you want to connect to. You 
can add more networks and rooms later, by clicking the + button.</property>
+                        <style>
+                          <class name="dim-label"/>
+                        </style>
+                      </object>
+                    </child>
                   </object>
                 </child>
                 <child>
-                  <object class="GtkLabel">
+                  <object class="Gjs_ServerRoomList" id="serverRoomList">
                     <property name="visible">True</property>
-                    <property name="max-width-chars">60</property>
-                    <property name="wrap">True</property>
-                    <property name="justify">center</property>
-                    <property name="label" translatable="yes">Select rooms you want to connect to. You can 
add more networks and rooms later, by clicking the + button.</property>
-                    <style>
-                      <class name="dim-label"/>
-                    </style>
+                    <property name="vexpand">True</property>
                   </object>
                 </child>
-                </object>
-            </child>
-            <child>
-              <object class="Gjs_ServerRoomList" id="serverRoomList">
-                <property name="visible">True</property>
-                <property name="vexpand">True</property>
               </object>
-            </child>
+            </property>
           </object>
-          <packing>
-            <property name="name">rooms</property>
-          </packing>
         </child>
       </object>
     </child>
diff --git a/data/resources/join-room-dialog.ui b/data/resources/join-room-dialog.ui
index c1b583c0..d22daf47 100644
--- a/data/resources/join-room-dialog.ui
+++ b/data/resources/join-room-dialog.ui
@@ -6,6 +6,7 @@
     <property name="destroy-with-parent">True</property>
     <property name="default-width">500</property>
     <property name="default-height">500</property>
+    <property name="default-widget">joinButton</property>
     <child type="titlebar">
       <object class="GtkHeaderBar">
         <property name="visible">True</property>
@@ -13,7 +14,17 @@
           <object class="GtkButton" id="backButton">
             <property name="visible">True</property>
             <property name="focus-on-click">False</property>
-            <accelerator key="Left" modifiers="GDK_MOD1_MASK" signal="clicked"/>
+            <child>
+              <object class='GtkShortcutController'>
+                <property name='scope'>managed</property>
+                <child>
+                  <object class='GtkShortcut'>
+                    <property name='trigger'>&lt;Alt&gt;Left</property>
+                    <property name='action'>signal(clicked)</property>
+                  </object>
+                </child>
+              </object>
+            </child>
             <child>
               <object class="GtkImage">
                 <property name="visible">True</property>
@@ -37,13 +48,11 @@
       <object class="GtkButton" id="joinButton">
         <property name="label" translatable="yes">_Join</property>
         <property name="visible">True</property>
-        <property name="can-default">True</property>
-        <property name="has-default">True</property>
         <property name="receives-default">True</property>
         <property name="use-underline">True</property>
       </object>
     </child>
-    <child internal-child="vbox">
+    <child internal-child="content_area">
       <object class="GtkBox">
         <property name="orientation">vertical</property>
         <property name="spacing">2</property>
@@ -52,166 +61,184 @@
             <property name="visible">True</property>
             <property name="transition-type">slide-left-right</property>
             <child>
-              <object class="GtkGrid">
-                <property name="visible">True</property>
-                <property name="margin-start">30</property>
-                <property name="margin-end">30</property>
-                <property name="margin-top">30</property>
-                <property name="margin-bottom">24</property>
-                <property name="row-spacing">18</property>
-                <property name="column-spacing">12</property>
-                <child>
-                  <object class="GtkLabel">
-                    <property name="visible">True</property>
-                    <property name="halign">end</property>
-                    <property name="label" translatable="yes">C_onnection</property>
-                    <property name="use-underline">True</property>
-                    <property name="mnemonic-widget">connectionCombo</property>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkComboBoxText" id="connectionCombo">
-                    <property name="visible">True</property>
-                    <property name="hexpand">True</property>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkButton" id="connectionButton">
+              <object class="GtkStackPage">
+                <property name="name">main</property>
+                <property name="child">
+                  <object class="GtkGrid">
                     <property name="visible">True</property>
-                    <property name="receives-default">True</property>
-                    <property name="focus-on-click">False</property>
-                    <accelerator key="n" modifiers="GDK_CONTROL_MASK" signal="clicked"/>
+                    <property name="margin-start">30</property>
+                    <property name="margin-end">30</property>
+                    <property name="margin-top">30</property>
+                    <property name="margin-bottom">24</property>
+                    <property name="row-spacing">18</property>
+                    <property name="column-spacing">12</property>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="visible">True</property>
+                        <property name="halign">end</property>
+                        <property name="label" translatable="yes">C_onnection</property>
+                        <property name="use-underline">True</property>
+                        <property name="mnemonic-widget">connectionCombo</property>
+                      </object>
+                    </child>
                     <child>
-                      <object class="GtkBox">
+                      <object class="GtkComboBoxText" id="connectionCombo">
                         <property name="visible">True</property>
-                        <property name="spacing">6</property>
+                        <property name="hexpand">True</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkButton" id="connectionButton">
+                        <property name="visible">True</property>
+                        <property name="receives-default">True</property>
+                        <property name="focus-on-click">False</property>
                         <child>
-                          <object class="GtkImage">
-                            <property name="visible">True</property>
-                            <property name="icon-name">list-add-symbolic</property>
+                          <object class='GtkShortcutController'>
+                            <property name='scope'>managed</property>
+                            <child>
+                              <object class='GtkShortcut'>
+                                <property name='trigger'>&lt;Control&gt;n</property>
+                                <property name='action'>signal(clicked)</property>
+                              </object>
+                            </child>
                           </object>
                         </child>
                         <child>
-                          <object class="GtkLabel">
+                          <object class="GtkBox">
                             <property name="visible">True</property>
-                            <property name="label" translatable="yes">_Add Network</property>
-                            <property name="use-underline">True</property>
-                            <property name="mnemonic-widget">connectionButton</property>
+                            <property name="spacing">6</property>
+                            <child>
+                              <object class="GtkImage">
+                                <property name="visible">True</property>
+                                <property name="icon-name">list-add-symbolic</property>
+                              </object>
+                            </child>
+                            <child>
+                              <object class="GtkLabel">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">_Add Network</property>
+                                <property name="use-underline">True</property>
+                                <property name="mnemonic-widget">connectionButton</property>
+                              </object>
+                            </child>
                           </object>
                         </child>
                       </object>
                     </child>
+                    <child>
+                      <object class="Gjs_ServerRoomList" id="serverRoomList">
+                        <property name="visible">True</property>
+                        <layout>
+                          <property name="column-span">3</property>
+                          <property name="column">0</property>
+                          <property name="row">1</property>
+                        </layout>
+                      </object>
+                    </child>
                   </object>
-                </child>
-                <child>
-                  <object class="Gjs_ServerRoomList" id="serverRoomList">
-                    <property name="visible">True</property>
-                  </object>
-                  <packing>
-                    <property name="width">3</property>
-                    <property name="left-attach">0</property>
-                    <property name="top-attach">1</property>
-                  </packing>
-                </child>
+                </property>
               </object>
-              <packing>
-                <property name="name">main</property>
-              </packing>
             </child>
             <child>
-              <object class="GtkBox">
-                <property name="visible">True</property>
-                <property name="margin-start">30</property>
-                <property name="margin-end">30</property>
-                <property name="margin-top">30</property>
-                <property name="margin-bottom">24</property>
-                <property name="orientation">vertical</property>
-                <property name="spacing">18</property>
-                <child>
-                  <object class="GtkStack" id="connectionStack">
+              <object class="GtkStackPage">
+                <property name="name">connection</property>
+                <property name="child">
+                  <object class="GtkBox">
                     <property name="visible">True</property>
-                    <property name="vexpand">True</property>
-                    <property name="transition-type">crossfade</property>
+                    <property name="margin-start">30</property>
+                    <property name="margin-end">30</property>
+                    <property name="margin-top">30</property>
+                    <property name="margin-bottom">24</property>
+                    <property name="orientation">vertical</property>
+                    <property name="spacing">18</property>
                     <child>
-                      <object class="GtkBox">
+                      <object class="GtkStack" id="connectionStack">
                         <property name="visible">True</property>
-                        <property name="orientation">vertical</property>
-                        <style>
-                          <class name="linked"/>
-                          <class name="frame"/>
-                        </style>
+                        <property name="vexpand">True</property>
+                        <property name="transition-type">crossfade</property>
                         <child>
-                          <object class="GtkBox">
-                            <property name="visible">True</property>
-                            <style>
-                              <class name="polari-listbox-filterbar"/>
-                            </style>
-                            <child>
-                              <object class="GtkSearchEntry" id="filterEntry">
+                          <object class="GtkStackPage">
+                            <property name="name">predefined</property>
+                            <property name="child">
+                              <object class="GtkBox">
                                 <property name="visible">True</property>
-                                <property name="hexpand">True</property>
-                                <property name="margin-start">60</property>
-                                <property name="margin-end">60</property>
-                                <property name="margin-top">6</property>
-                                <property name="margin-bottom">6</property>
+                                <property name="orientation">vertical</property>
+                                <style>
+                                  <class name="linked"/>
+                                  <class name="frame"/>
+                                </style>
+                                <child>
+                                  <object class="GtkBox">
+                                    <property name="visible">True</property>
+                                    <style>
+                                      <class name="polari-listbox-filterbar"/>
+                                    </style>
+                                    <child>
+                                      <object class="GtkSearchEntry" id="filterEntry">
+                                        <property name="visible">True</property>
+                                        <property name="hexpand">True</property>
+                                        <property name="margin-start">60</property>
+                                        <property name="margin-end">60</property>
+                                        <property name="margin-top">6</property>
+                                        <property name="margin-bottom">6</property>
+                                      </object>
+                                    </child>
+                                  </object>
+                                </child>
+                                <child>
+                                  <object class="Gjs_ConnectionsList" id="connectionsList">
+                                    <property name="visible">True</property>
+                                    <property name="vexpand">True</property>
+                                  </object>
+                                </child>
                               </object>
-                            </child>
+                            </property>
                           </object>
                         </child>
                         <child>
-                          <object class="Gjs_ConnectionsList" id="connectionsList">
-                            <property name="visible">True</property>
-                            <property name="vexpand">True</property>
+                          <object class="GtkStackPage">
+                            <property name="name">custom</property>
+                            <property name="child">
+                              <object class="GtkBox">
+                                <property name="visible">True</property>
+                                <property name="orientation">vertical</property>
+                                <property name="spacing">24</property>
+                                <child>
+                                  <object class="Gjs_ConnectionDetails" id="details">
+                                    <property name="visible">True</property>
+                                  </object>
+                                </child>
+                                <child>
+                                  <object class="GtkButton" id="addButton">
+                                    <property name="label" translatable="yes">_Add</property>
+                                    <property name="visible">True</property>
+                                    <property name="halign">end</property>
+                                    <property name="receives-default">True</property>
+                                    <property name="use-underline">True</property>
+                                    <property name="sensitive" bind-source="details"
+                                              bind-property="can-confirm"
+                                              bind-flags="sync-create"/>
+                                  </object>
+                                </child>
+                              </object>
+                            </property>
                           </object>
                         </child>
                       </object>
-                      <packing>
-                        <property name="name">predefined</property>
-                      </packing>
                     </child>
                     <child>
-                      <object class="GtkBox">
+                      <object class="GtkToggleButton" id="customToggle">
+                        <property name="label" translatable="yes">_Custom Network</property>
                         <property name="visible">True</property>
-                        <property name="orientation">vertical</property>
-                        <property name="spacing">24</property>
-                        <child>
-                          <object class="Gjs_ConnectionDetails" id="details">
-                            <property name="visible">True</property>
-                          </object>
-                        </child>
-                        <child>
-                          <object class="GtkButton" id="addButton">
-                            <property name="label" translatable="yes">_Add</property>
-                            <property name="visible">True</property>
-                            <property name="halign">end</property>
-                            <property name="can-default">True</property>
-                            <property name="receives-default">True</property>
-                            <property name="use-underline">True</property>
-                            <property name="sensitive" bind-source="details"
-                                      bind-property="can-confirm" bind-flags="sync-create"/>
-                          </object>
-                        </child>
+                        <property name="receives-default">True</property>
+                        <property name="use-underline">True</property>
+                        <property name="focus-on-click">False</property>
+                        <property name="halign">start</property>
                       </object>
-                      <packing>
-                        <property name="name">custom</property>
-                      </packing>
                     </child>
                   </object>
-                </child>
-                <child>
-                  <object class="GtkToggleButton" id="customToggle">
-                    <property name="label" translatable="yes">_Custom Network</property>
-                    <property name="visible">True</property>
-                    <property name="receives-default">True</property>
-                    <property name="use-underline">True</property>
-                    <property name="focus-on-click">False</property>
-                    <property name="halign">start</property>
-                  </object>
-                </child>
+                </property>
               </object>
-              <packing>
-                <property name="name">connection</property>
-              </packing>
             </child>
           </object>
         </child>
diff --git a/data/resources/main-window.ui b/data/resources/main-window.ui
index f00e0ae9..0f603f10 100644
--- a/data/resources/main-window.ui
+++ b/data/resources/main-window.ui
@@ -3,7 +3,6 @@
   <object class="Gjs_UserListPopover" id="userListPopover">
     <property name="position">bottom</property>
     <property name="width-request">250</property>
-    <property name="relative-to">showUserListButton</property>
     <style>
       <class name="polari-user-list"/>
     </style>
@@ -44,11 +43,10 @@
           <object class="GtkHeaderBar" id="titlebarLeft">
             <property name="visible">True</property>
             <property name="hexpand">False</property>
-            <property name="show-close-button">True</property>
             <child type="title">
               <object class="GtkLabel"/>
             </child>
-            <child>
+            <child type="start">
               <object class="GtkButton" id="joinButton">
                 <property name="visible">True</property>
                 <property name="halign">end</property>
@@ -65,38 +63,30 @@
                     <property name="icon-size">1</property>
                   </object>
                 </child>
-                <child internal-child="accessible">
-                  <object class="AtkObject">
-                    <property name="AtkObject::accessible-name"
-                              translatable="yes">Add rooms and networks</property>
-                  </object>
-                </child>
+                <accessibility>
+                  <property name="label" translatable="yes">Add rooms and networks</property>
+                </accessibility>
               </object>
-              <packing>
-                <property name="pack-type">start</property>
-              </packing>
             </child>
-            <child>
+            <child type="end">
               <object class="GtkMenuButton">
                 <property name="visible">True</property>
                 <property name="halign">end</property>
                 <property name="valign">center</property>
                 <property name="menu-model">hamburgerMenu</property>
-                <accelerator key="F10" signal="activate"/>
-                <style>
-                  <class name="image-button"/>
-                </style>
+                <property name="icon-name">open-menu-symbolic</property>
                 <child>
-                  <object class="GtkImage">
-                    <property name="visible">True</property>
-                    <property name="icon-name">open-menu-symbolic</property>
-                    <property name="icon-size">1</property>
+                  <object class='GtkShortcutController'>
+                    <property name='scope'>managed</property>
+                    <child>
+                      <object class='GtkShortcut'>
+                        <property name='trigger'>F10</property>
+                        <property name='action'>activate</property>
+                      </object>
+                    </child>
                   </object>
                 </child>
               </object>
-              <packing>
-                <property name="pack-type">end</property>
-              </packing>
             </child>
           </object>
         </child>
@@ -110,7 +100,6 @@
           <object class="GtkHeaderBar" id="titlebarRight">
             <property name="visible">True</property>
             <property name="hexpand">True</property>
-            <property name="show-close-button">True</property>
             <!-- Use a custom title widget to enable markup for subtitles
                  (for URLs in channel topics); other than that, we want
                  the default GtkHeaderBar behavior, e.g. the subtitle may
@@ -191,7 +180,7 @@
                 </child>
               </object>
             </child>
-            <child>
+            <child type="end">
               <object class="GtkToggleButton" id="showUserListButton">
                 <property name="visible">True</property>
                 <property name="focus-on-click">False</property>
@@ -202,9 +191,6 @@
                   <class name="text-button"/>
                 </style>
               </object>
-              <packing>
-                <property name="pack-type">end</property>
-              </packing>
             </child>
           </object>
         </child>
@@ -218,7 +204,7 @@
           <object class="GtkInfoBar" id="offlineInfoBar">
             <property name="visible">True</property>
             <property name="revealed">False</property>
-            <child internal-child="content_area">
+            <child>
               <object class="GtkBox">
                 <property name="visible">True</property>
                 <child>
@@ -238,10 +224,10 @@
                 </child>
               </object>
             </child>
+            <layout>
+              <property name="column-span">2</property>
+            </layout>
           </object>
-          <packing>
-            <property name="width">2</property>
-          </packing>
         </child>
         <child>
           <object class="GtkRevealer" id="roomListRevealer">
@@ -284,23 +270,24 @@
             <child>
               <object class="Gjs_RoomStack" id="roomStack">
                 <property name="visible">True</property>
-                <property name="homogeneous">True</property>
+                <property name="hhomogeneous">True</property>
+                <property name="vhomogeneous">True</property>
                 <property name="transition-type">crossfade</property>
               </object>
             </child>
+            <layout>
+              <property name="column">1</property>
+              <property name="row">1</property>
+            </layout>
           </object>
-          <packing>
-            <property name="left-attach">1</property>
-            <property name="top-attach">1</property>
-          </packing>
         </child>
       </object>
     </child>
   </template>
   <object class="GtkSizeGroup">
-      <widgets>
-        <widget name="titlebarLeft"/>
-        <widget name="roomSidebar"/>
-      </widgets>
+    <widgets>
+      <widget name="titlebarLeft"/>
+      <widget name="roomSidebar"/>
+    </widgets>
   </object>
 </interface>
diff --git a/data/resources/nick-popover.ui b/data/resources/nick-popover.ui
index d8355fd6..fc53b466 100644
--- a/data/resources/nick-popover.ui
+++ b/data/resources/nick-popover.ui
@@ -2,7 +2,7 @@
 <interface>
   <template class="Gjs_NickPopover" parent="GtkPopover">
     <property name="position">top</property>
-    <child>
+    <property name="child">
       <object class="GtkBox">
         <property name="visible">True</property>
         <property name="margin-start">6</property>
@@ -30,7 +30,6 @@
             <property name="label" translatable="yes">_Change</property>
             <property name="visible">True</property>
             <property name="receives-default">True</property>
-            <property name="can-default">True</property>
             <property name="halign">end</property>
             <property name="hexpand">True</property>
             <property name="use-underline">True</property>
@@ -40,6 +39,6 @@
           </object>
         </child>
       </object>
-    </child>
+    </property>
   </template>
 </interface>
diff --git a/data/resources/room-list-header.ui b/data/resources/room-list-header.ui
index b6bc86a2..115de83c 100644
--- a/data/resources/room-list-header.ui
+++ b/data/resources/room-list-header.ui
@@ -1,98 +1,100 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <template class="Gjs_RoomListHeader" parent="GtkEventBox">
+  <template class="Gjs_RoomListHeader" parent="GtkGrid">
     <property name="popover">connectionPopover</property>
+    <property name="column-spacing">6</property>
+    <property name="row-spacing">2</property>
+    <property name="margin-top">1</property>
     <property name="margin-bottom">4</property>
-    <property name="can-focus">True</property>
+    <property name="focusable">True</property>
     <style>
+      <class name="room-list-header"/>
       <class name="activatable"/>
+      <class name="dim-label"/>
     </style>
     <child>
-      <object class="GtkGrid">
-        <property name="column-spacing">6</property>
-        <property name="row-spacing">2</property>
-        <property name="margin-top">1</property>
+      <object class="GtkLabel" id="label">
+        <property name="xalign">0</property>
+        <property name="hexpand">True</property>
+        <property name="max-width-chars">15</property>
+        <property name="ellipsize">end</property>
+        <property name="visible">True</property>
+      </object>
+    </child>
+    <child>
+      <object class="GtkStack" id="iconStack">
+        <property name="transition-type">crossfade</property>
+        <property name="valign">center</property>
         <property name="visible">True</property>
-        <style>
-          <class name="room-list-header"/>
-          <class name="dim-label"/>
-        </style>
-        <child>
-          <object class="GtkLabel" id="label">
-            <property name="xalign">0</property>
-            <property name="hexpand">True</property>
-            <property name="max-width-chars">15</property>
-            <property name="ellipsize">end</property>
-            <property name="visible">True</property>
-          </object>
-        </child>
         <child>
-          <object class="GtkStack" id="iconStack">
-            <property name="transition-type">crossfade</property>
-            <property name="valign">center</property>
-            <property name="visible">True</property>
-            <child>
+          <object class="GtkStackPage">
+            <property name="name">disconnected</property>
+            <property name="child">
               <object class="GtkImage">
                 <property name="icon-name">network-offline-symbolic</property>
                 <property name="visible">True</property>
               </object>
-              <packing>
-                <property name="name">disconnected</property>
-              </packing>
-            </child>
-            <child>
+            </property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkStackPage">
+            <property name="name">error</property>
+            <property name="child">
               <object class="GtkImage">
                 <property name="icon_name">dialog-error-symbolic</property>
                 <property name="visible">True</property>
               </object>
-              <packing>
-                <property name="name">error</property>
-              </packing>
-            </child>
-            <child>
+            </property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkStackPage">
+            <property name="name">connecting</property>
+            <property name="child">
               <object class="GtkSpinner" id="spinner">
                 <property name="visible">True</property>
               </object>
-              <packing>
-                <property name="name">connecting</property>
-              </packing>
-            </child>
-            <child>
+            </property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkStackPage">
+            <property name="name">auth</property>
+            <property name="child">
               <object class="GtkImage">
                 <property name="icon-name">dialog-password-symbolic</property>
                 <property name="visible">True</property>
               </object>
-              <packing>
-                <property name="name">auth</property>
-              </packing>
-            </child>
-            <child>
+            </property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkStackPage">
+            <property name="name">default</property>
+            <property name="child">
               <object class="GtkImage">
                 <property name="icon-name">pan-down-symbolic</property>
                 <property name="visible">True</property>
               </object>
-              <packing>
-                <property name="name">default</property>
-              </packing>
-            </child>
-          </object>
-        </child>
-        <child>
-          <object class="GtkSeparator">
-            <property name="visible">True</property>
+            </property>
           </object>
-          <packing>
-            <property name="left-attach">0</property>
-            <property name="top-attach">1</property>
-            <property name="width">2</property>
-          </packing>
         </child>
       </object>
     </child>
+    <child>
+      <object class="GtkSeparator">
+        <property name="visible">True</property>
+        <layout>
+          <property name="column">0</property>
+          <property name="row">1</property>
+          <property name="column-span">2</property>
+        </layout>
+      </object>
+    </child>
   </template>
   <object class="GtkPopover" id="connectionPopover">
-    <property name="position">bottom</property>
-    <child>
+    <property name="child">
       <object class="GtkBox">
         <property name="orientation">vertical</property>
         <property name="margin-start">12</property>
@@ -124,9 +126,8 @@
         <child>
           <object class="GtkEntry" id="popoverPassword">
             <property name="visible" bind-source="popoverTitle"
-                      bind-property="visible" bind-flags="invert-boolean"/>
+                      bind-property="visible" bind-flags="invert-boolean|sync-create"/>
             <property name="visibility">False</property>
-            <property name="can-default">True</property>
             <property name="activates-default">True</property>
             <property name="margin-top">6</property>
             <property name="margin-start">6</property>
@@ -142,40 +143,35 @@
         </child>
         <child>
           <object class="GtkModelButton" id="popoverConnect">
-            <property name="xalign">0</property>
             <property name="visible">True</property>
             <property name="text" translatable="yes">Connect</property>
           </object>
         </child>
         <child>
           <object class="GtkModelButton" id="popoverReconnect">
-            <property name="xalign">0</property>
             <property name="visible">True</property>
             <property name="text" translatable="yes">Reconnect</property>
           </object>
         </child>
         <child>
           <object class="GtkModelButton" id="popoverDisconnect">
-            <property name="xalign">0</property>
             <property name="visible">True</property>
             <property name="text" translatable="yes">Disconnect</property>
           </object>
         </child>
         <child>
           <object class="GtkModelButton" id="popoverRemove">
-            <property name="xalign">0</property>
             <property name="visible">True</property>
             <property name="text" translatable="yes">Remove</property>
           </object>
         </child>
         <child>
           <object class="GtkModelButton" id="popoverProperties">
-            <property name="xalign">0</property>
             <property name="visible">True</property>
             <property name="text" translatable="yes">Properties</property>
           </object>
         </child>
       </object>
-    </child>
+    </property>
   </object>
 </interface>
diff --git a/data/resources/room-list-row.ui b/data/resources/room-list-row.ui
index 6d4a48e9..f0ff07db 100644
--- a/data/resources/room-list-row.ui
+++ b/data/resources/room-list-row.ui
@@ -5,37 +5,37 @@
     <property name="margin-bottom">4</property>
     <property name="focus-on-click">False</property>
     <child>
-      <object class="GtkEventBox" id="eventBox">
+      <object class="GtkBox" id="box">
         <property name="visible">True</property>
+        <property name="margin-start">8</property>
+        <property name="margin-end">8</property>
+        <property name="margin-top">2</property>
+        <property name="margin-bottom">2</property>
+        <property name="spacing">6</property>
         <child>
-          <object class="GtkBox">
+          <object class="GtkImage" id="icon">
+            <property name="visible">False</property>
+            <property name="valign">baseline</property>
+            <property name="icon-size">1</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkLabel" id="roomLabel">
             <property name="visible">True</property>
-            <property name="margin-start">8</property>
-            <property name="margin-end">8</property>
-            <property name="margin-top">2</property>
-            <property name="margin-bottom">2</property>
-            <property name="spacing">6</property>
-            <child>
-              <object class="GtkImage" id="icon">
-                <property name="visible">False</property>
-                <property name="valign">baseline</property>
-                <property name="icon-size">1</property>
-              </object>
-            </child>
-            <child>
-              <object class="GtkLabel" id="roomLabel">
-                <property name="visible">True</property>
-                <property name="halign">start</property>
-                <property name="valign">baseline</property>
-                <property name="hexpand">True</property>
-                <property name="ellipsize">end</property>
-              </object>
-            </child>
+            <property name="halign">start</property>
+            <property name="valign">baseline</property>
+            <property name="hexpand">True</property>
+            <property name="ellipsize">end</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkStack" id="eventStack">
+            <property name="visible">True</property>
+            <property name="transition-type">crossfade</property>
             <child>
-              <object class="GtkStack" id="eventStack">
-                <property name="visible">True</property>
-                <property name="transition-type">crossfade</property>
-                <child>
+              <object class="GtkStackPage">
+                <property name="name">connecting</property>
+                <property name="child">
                   <object class="GtkImage">
                     <property name="visible">True</property>
                     <property name="valign">baseline</property>
@@ -45,16 +45,17 @@
                       <class name="polari-room-loading"/>
                     </style>
                   </object>
-                  <packing>
-                    <property name="name">connecting</property>
-                  </packing>
-'               </child>
-                <child>
+                </property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkStackPage">
+                <property name="name">messages</property>
+                <property name="child">
                   <object class="GtkAspectFrame">
                     <property name="visible">True</property>
-                    <property name="shadow-type">none</property>
                     <property name="obey-child">False</property>
-                    <child>
+                    <property name="child">
                       <object class="GtkLabel" id="counter">
                         <property name="visible">True</property>
                         <property name="width-chars">2</property>
@@ -62,12 +63,9 @@
                           <class name="pending-messages-count"/>
                         </style>
                       </object>
-                    </child>
+                    </property>
                   </object>
-                  <packing>
-                    <property name="name">messages</property>
-                  </packing>
-                </child>
+                </property>
               </object>
             </child>
           </object>
diff --git a/data/resources/user-details.ui b/data/resources/user-details.ui
index d367af53..0829e0f0 100644
--- a/data/resources/user-details.ui
+++ b/data/resources/user-details.ui
@@ -47,12 +47,12 @@
             <style>
               <class name="dim-label" />
             </style>
+            <layout>
+              <property name="column-span">1</property>
+              <property name="row">0</property>
+              <property name="column">0</property>
+            </layout>
           </object>
-          <packing>
-            <property name="width">1</property>
-            <property name="top-attach">0</property>
-            <property name="left-attach">0</property>
-          </packing>
         </child>
         <child>
           <object class="GtkLabel">
@@ -65,11 +65,11 @@
             <style>
               <class name="dim-label" />
             </style>
+            <layout>
+              <property name="row">0</property>
+              <property name="column">0</property>
+            </layout>
           </object>
-          <packing>
-            <property name="top-attach">0</property>
-            <property name="left-attach">0</property>
-          </packing>
         </child>
         <child>
           <object class="GtkLabel" id="lastLabel">
@@ -83,11 +83,11 @@
             <style>
               <class name="dim-label" />
             </style>
+            <layout>
+              <property name="row">0</property>
+              <property name="column">1</property>
+            </layout>
           </object>
-          <packing>
-            <property name="top-attach">0</property>
-            <property name="left-attach">1</property>
-          </packing>
         </child>
         <child>
           <object class="GtkImage">
@@ -97,12 +97,12 @@
             <style>
               <class name="dim-label" />
             </style>
+            <layout>
+              <property name="column-span">1</property>
+              <property name="row">1</property>
+              <property name="column">0</property>
+            </layout>
           </object>
-          <packing>
-            <property name="width">1</property>
-            <property name="top-attach">1</property>
-            <property name="left-attach">0</property>
-          </packing>
         </child>
         <child>
           <object class="GtkLabel" id="fullnameLabel">
@@ -116,12 +116,12 @@
             <style>
               <class name="dim-label" />
             </style>
+            <layout>
+              <property name="column-span">2</property>
+              <property name="row">1</property>
+              <property name="column">0</property>
+            </layout>
           </object>
-          <packing>
-            <property name="width">2</property>
-            <property name="top-attach">1</property>
-            <property name="left-attach">0</property>
-          </packing>
         </child>
         <child>
           <object class="GtkImage">
@@ -134,12 +134,12 @@
             <style>
               <class name="dim-label" />
             </style>
+            <layout>
+              <property name="column-span">1</property>
+              <property name="row">2</property>
+              <property name="column">0</property>
+            </layout>
           </object>
-          <packing>
-            <property name="width">1</property>
-            <property name="top-attach">2</property>
-            <property name="left-attach">0</property>
-          </packing>
         </child>
         <child>
           <object class="GtkLabel" id="notificationLabel">
@@ -153,12 +153,12 @@
             <style>
               <class name="dim-label" />
             </style>
+            <layout>
+              <property name="column-span">2</property>
+              <property name="row">2</property>
+              <property name="column">0</property>
+            </layout>
           </object>
-          <packing>
-            <property name="width">2</property>
-            <property name="top-attach">2</property>
-            <property name="left-attach">0</property>
-          </packing>
         </child>
       </object>
     </child>
diff --git a/src/appNotifications.js b/src/appNotifications.js
index 5640ec67..a88fb5cc 100644
--- a/src/appNotifications.js
+++ b/src/appNotifications.js
@@ -232,7 +232,7 @@ export const MessageInfoBar = GObject.registerClass({
         super._init(Object.assign(defaultParams, params));
 
         let box = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL });
-        this.get_content_area().append(box);
+        this.add_child(box);
 
         this._titleLabel = new Gtk.Label({
             halign: Gtk.Align.START,
diff --git a/src/application.js b/src/application.js
index 2bcd5d9d..2d208289 100644
--- a/src/application.js
+++ b/src/application.js
@@ -1,8 +1,8 @@
-import Gdk from 'gi://Gdk?version=3.0';
+import Gdk from 'gi://Gdk?version=4.0';
 import Gio from 'gi://Gio';
 import GLib from 'gi://GLib';
 import GObject from 'gi://GObject';
-import Gtk from 'gi://Gtk?version=3.0';
+import Gtk from 'gi://Gtk?version=4.0';
 import Polari from 'gi://Polari';
 import Tp from 'gi://TelepathyGLib';
 
diff --git a/src/chatView.js b/src/chatView.js
index fd5bc2ee..93afa726 100644
--- a/src/chatView.js
+++ b/src/chatView.js
@@ -2,9 +2,9 @@ import Gdk from 'gi://Gdk';
 import Gio from 'gi://Gio';
 import GLib from 'gi://GLib';
 import GObject from 'gi://GObject';
+import Graphene from 'gi://Graphene';
 import Gtk from 'gi://Gtk';
 import Pango from 'gi://Pango';
-import PangoCairo from 'gi://PangoCairo';
 import Polari from 'gi://Polari';
 import Tp from 'gi://TelepathyGLib';
 import Tpl from 'gi://TelepathyLogger';
@@ -65,7 +65,7 @@ const TextView = GObject.registerClass({
         super._init(params);
 
         this.buffer.connect('mark-set', this._onMarkSet.bind(this));
-        this.connect('screen-changed', this._onScreenChanged.bind(this));
+        this.connect('notify::root', this._onScreenChanged.bind(this));
     }
 
     // eslint-disable-next-line camelcase
@@ -119,30 +119,30 @@ const TextView = GObject.registerClass({
         });
     }
 
-    vfunc_get_preferred_width() {
-        return [1, 1];
+    vfunc_measure(orientation, forSize) {
+        const [min, nat] = orientation === Gtk.Orientation.HORIZONTAL
+            ? [1, 1] : super.vfunc_measure(orientation, forSize);
+        return [min, nat, -1, -1];
     }
 
-    vfunc_style_updated() {
+    vfunc_css_changed(change) {
+        super.vfunc_css_changed(change);
+
         const context = this.get_style_context();
         [, this._dimColor] = context.lookup_color('inactive_nick_color');
 
-        super.vfunc_style_updated();
-
         /* pick up DPI changes (e.g. via the 'text-scaling-factor' setting):
            the default handler calls pango_cairo_context_set_resolution(), so
            update the indent after that */
         this._updateIndent();
     }
 
-    vfunc_draw(cr) {
-        super.vfunc_draw(cr);
+    vfunc_snapshot(snapshot) {
+        super.vfunc_snapshot(snapshot);
 
         let mark = this.buffer.get_mark('indicator-line');
-        if (!mark) {
-            cr.$dispose();
-            return Gdk.EVENT_PROPAGATE;
-        }
+        if (!mark)
+            return;
 
         let iter = this.buffer.get_iter_at_mark(mark);
         let location = this.get_iter_location(iter);
@@ -169,28 +169,29 @@ const TextView = GObject.registerClass({
         let baseline = Math.floor(this._layout.get_baseline() / Pango.SCALE);
         let layoutY = y - baseline + Math.floor((layoutHeight - baseline) / 2) + 0.5;
 
-        let [hasClip, clip] = Gdk.cairo_get_clip_rectangle(cr);
-        if (hasClip &&
-            clip.y <= layoutY + layoutHeight &&
-            clip.y + clip.height >= layoutY) {
-            Gdk.cairo_set_source_rgba(cr, this._dimColor);
+        const bounds = new Graphene.Rect();
+        bounds.init(MARGIN, y, width, 1);
 
-            cr.moveTo(layoutX, layoutY);
-            PangoCairo.show_layout(cr, this._layout);
+        snapshot.save();
 
-            let [, color] = this.get_style_context().lookup_color('borders');
-            Gdk.cairo_set_source_rgba(cr, color);
+        snapshot.translate(new Graphene.Point({ x: layoutX, y: layoutY }));
+        snapshot.append_layout(this._layout, this._dimColor);
 
-            cr.setLineWidth(1);
-            cr.moveTo(MARGIN, y);
-            cr.lineTo(layoutX - MARGIN, y);
-            cr.moveTo(layoutX + layoutWidth + MARGIN, y);
-            cr.lineTo(MARGIN + width, y);
-            cr.stroke();
-        }
-        cr.$dispose();
+        snapshot.restore();
+
+        const cr = snapshot.append_cairo(bounds);
 
-        return Gdk.EVENT_PROPAGATE;
+        const [, color] = this.get_style_context().lookup_color('borders');
+        Gdk.cairo_set_source_rgba(cr, color);
+
+        cr.setLineWidth(1);
+        cr.moveTo(MARGIN, y);
+        cr.lineTo(layoutX - MARGIN, y);
+        cr.moveTo(layoutX + layoutWidth + MARGIN, y);
+        cr.lineTo(MARGIN + width, y);
+        cr.stroke();
+
+        cr.$dispose();
     }
 
     _onMarkSet(buffer, iter, mark) {
@@ -358,18 +359,12 @@ export default GObject.registerClass({
             GObject.BindingFlags.SYNC_CREATE,
             (v, source) => [true, MARGIN - source],
             null);
-        this._view.add_events(Gdk.EventMask.LEAVE_NOTIFY_MASK |
-                              Gdk.EventMask.ENTER_NOTIFY_MASK);
         this.set_child(this._view);
         this.show();
 
         this._createTags();
 
-        this.connect('style-updated', this._onStyleUpdated.bind(this));
-        this._onStyleUpdated();
-
         this.connect('destroy', this._onDestroy.bind(this));
-        this.connect('scroll-event', this._onScroll.bind(this));
         this.connect('edge-reached', (w, pos) => {
             if (pos === Gtk.PositionType.BOTTOM)
                 this._autoscroll = true;
@@ -381,23 +376,26 @@ export default GObject.registerClass({
         this.vadjustment.connect('notify::upper',
             this._onUpperChanged.bind(this));
 
-        this._keyController = new Gtk.EventControllerKey({
-            widget: this._view,
+        this._scrollController = new Gtk.EventControllerScroll({
+            flags: Gtk.EventControllerScrollFlags.VERTICAL,
         });
+        this._scrollController.connect('scroll', this._onScroll.bind(this));
+        this.add_controller(this._scrollController);
+
+        this._keyController = new Gtk.EventControllerKey();
         this._keyController.connect('key-pressed', this._onKeyPressed.bind(this));
+        this._view.add_controller(this._keyController);
 
-        this._motionController = new Gtk.EventControllerMotion({
-            widget: this._view,
-        });
+        this._motionController = new Gtk.EventControllerMotion();
         this._motionController.connect('motion',
             this._handleButtonTagsHover.bind(this));
         this._motionController.connect('enter',
             this._handleButtonTagsHover.bind(this));
         this._motionController.connect('leave',
             this._handleButtonTagsHover.bind(this));
+        this._view.add_controller(this._motionController);
 
         this._clickGesture = new Gtk.GestureClick({
-            widget: this._view,
             propagation_phase: Gtk.PropagationPhase.CAPTURE,
             button: 0,
         });
@@ -405,6 +403,7 @@ export default GObject.registerClass({
             this._handleButtonTagPressed.bind(this));
         this._clickGesture.connect('released',
             this._handleButtonTagReleased.bind(this));
+        this._view.add_controller(this._clickGesture);
 
         this._room = room;
         this._state = { lastNick: null, lastTimestamp: 0, lastStatusGroup: 0 };
@@ -417,6 +416,11 @@ export default GObject.registerClass({
         this._backlogTimeoutId = 0;
         this._statusCount = { left: 0, joined: 0, total: 0 };
 
+        this._activeNickColor = new Gdk.RGBA();
+        this._inactiveNickColor = new Gdk.RGBA();
+        this._hoveredLinkColor = new Gdk.RGBA();
+        this._statusHeaderHoverColor = new Gdk.RGBA();
+
         let statusMonitor = UserStatusMonitor.getDefault();
         this._userTracker = statusMonitor.getUserTrackerForAccount(room.account);
 
@@ -525,7 +529,9 @@ export default GObject.registerClass({
         tags.forEach(tagProps => tagTable.add(new Gtk.TextTag(tagProps)));
     }
 
-    _onStyleUpdated() {
+    vfunc_css_changed(change) {
+        super.vfunc_css_changed(change);
+
         const context = this.get_style_context();
         const [, activeColor] =
             context.lookup_color('active_nick_color');
@@ -836,9 +842,9 @@ export default GObject.registerClass({
         const menu = new Gtk.PopoverMenu({
             position: Gtk.PositionType.BOTTOM,
             pointing_to: new Gdk.Rectangle({ x, y }),
+            menu_model: section,
         });
-        menu.set_parent(this);
-        menu.bind_model(section, null);
+        menu.set_parent(this._view);
         menu.popup();
     }
 
diff --git a/src/entryArea.js b/src/entryArea.js
index 1a909584..4350b84c 100644
--- a/src/entryArea.js
+++ b/src/entryArea.js
@@ -175,12 +175,11 @@ export default GObject.registerClass({
         this.connect('destroy', this._onDestroy.bind(this));
         this.connect('notify::sensitive', this._onSensitiveChanged.bind(this));
         this.connect('realize', () => {
-            this._toplevelKeyController = new Gtk.EventControllerKey({
-                widget: this.get_root(),
-                propagation_phase: Gtk.PropagationPhase.CAPTURE,
-            });
+            this._toplevel = this.get_root();
+            this._toplevelKeyController = new Gtk.EventControllerKey();
             this._toplevelKeyController.connect('key-pressed',
                 this._onKeyPressed.bind(this));
+            this._toplevel.add_controller(this._toplevelKeyController);
         });
         this.connect('map', () => {
             EntryArea._nickPopover.unparent();
@@ -252,9 +251,7 @@ export default GObject.registerClass({
         this._cancelButton.connect('clicked', this._onCancelClicked.bind(this));
         this._pasteButton.connect('clicked', this._onPasteClicked.bind(this));
 
-        this._pasteController = new Gtk.EventControllerKey({
-            widget: this._pasteBox,
-        });
+        this._pasteController = new Gtk.EventControllerKey();
         this._pasteController.connect_after('key-pressed', (c, keyval, code, mods) => {
             if (keyval === Gdk.KEY_Escape ||
                 keyval === Gdk.KEY_BackSpace ||
@@ -265,6 +262,7 @@ export default GObject.registerClass({
             }
             return Gdk.EVENT_PROPAGATE;
         });
+        this._pasteBox.add_controller(this._pasteController);
 
         if (!this._room)
             return;
@@ -505,7 +503,7 @@ export default GObject.registerClass({
             EntryArea._nickPopover.disconnect(this._nickChangedId);
         this._nickChangedId = 0;
         if (this._toplevelKeyController)
-            this._toplevelKeyController.run_dispose();
+            this._toplevel.remove_controller(this._toplevelKeyController);
         this._toplevelKeyController = null;
     }
 });
diff --git a/src/initialSetup.js b/src/initialSetup.js
index 7daf2e3e..e70de3eb 100644
--- a/src/initialSetup.js
+++ b/src/initialSetup.js
@@ -91,7 +91,6 @@ export default GObject.registerClass({
         else
             this._nextButton.remove_css_class('suggested-action');
 
-        this._nextButton.grab_default();
         this._updateNextSensitivity();
     }
 
diff --git a/src/joinDialog.js b/src/joinDialog.js
index 7e1953a7..65963203 100644
--- a/src/joinDialog.js
+++ b/src/joinDialog.js
@@ -110,7 +110,7 @@ export default GObject.registerClass({
             let childName = isCustom ? 'custom' : 'predefined';
             this._connectionStack.visible_child_name = childName;
             if (isCustom) {
-                this._addButton.grab_default();
+                this.set_default_widget(this._addButton);
                 this._details.reset();
             }
         });
diff --git a/src/main.js b/src/main.js
index 0af2b10f..8cda0fdb 100755
--- a/src/main.js
+++ b/src/main.js
@@ -20,6 +20,7 @@ globalThis.vprintf = (fmt, ...args) => imports.format.vprintf(fmt, args);
 pkg.require({
     'GdkPixbuf': '2.0',
     'GObject': '2.0',
+    'Gtk': '4.0',
     'Pango': '1.0',
     'PangoCairo': '1.0',
     'Secret': '1',
@@ -28,7 +29,6 @@ pkg.require({
 });
 pkg.requireSymbol('Gio', '2.0', 'Application.send_notification');
 pkg.requireSymbol('GLib', '2.0', 'log_variant');
-pkg.requireSymbol('Gtk', '3.0', 'ScrolledWindow.propagate_natural_width');
 
 if (!pkg.checkSymbol('Soup', '3.0'))
     pkg.requireSymbol('Soup', '2.4');
diff --git a/src/mainWindow.js b/src/mainWindow.js
index 687586de..d3f7a86e 100644
--- a/src/mainWindow.js
+++ b/src/mainWindow.js
@@ -1,4 +1,3 @@
-import Gdk from 'gi://Gdk';
 import Gio from 'gi://Gio';
 import GLib from 'gi://GLib';
 import GObject from 'gi://GObject';
@@ -26,16 +25,19 @@ export const FixedSizeFrame = GObject.registerClass({
             GObject.ParamFlags.READWRITE,
             -1, GLib.MAXINT32, -1),
     },
-}, class FixedSizeFrame extends Gtk.Bin {
+}, class FixedSizeFrame extends Gtk.Widget {
     _init(params) {
         this._height = -1;
         this._width = -1;
 
-        super._init(params);
+        super._init({
+            ...params,
+            layout_manager: new Gtk.BinLayout(),
+        });
     }
 
     _queueRedraw() {
-        let child = this.get_child();
+        const [child] = this;
         child?.queue_resize();
         this.queue_draw();
     }
@@ -67,14 +69,11 @@ export const FixedSizeFrame = GObject.registerClass({
         this._queueRedraw();
     }
 
-    vfunc_get_preferred_width_for_height(forHeight) {
-        let [min, nat] = super.vfunc_get_preferred_width_for_height(forHeight);
-        return [min, this._width < 0 ? nat : this._width];
-    }
-
-    vfunc_get_preferred_height_for_width(forWidth) {
-        let [min, nat] = super.vfunc_get_preferred_height_for_width(forWidth);
-        return [min, this._height < 0 ? nat : this._height];
+    vfunc_measure(orientation, forSize) {
+        const fixedSize = orientation === Gtk.Orientation.HORIZONTAL
+            ? this._width : this._height;
+        const [min, nat] = super.vfunc_measure(orientation, forSize);
+        return [min, fixedSize < 0 ? nat : fixedSize, -1, -1];
     }
 });
 
@@ -193,8 +192,14 @@ export default GObject.registerClass({
             this._updateDecorations.bind(this));
         this._updateDecorations();
 
-        this.connect('window-state-event', this._onWindowStateEvent.bind(this));
-        this.connect('size-allocate', this._onSizeAllocate.bind(this));
+        this.connect('notify::maximized',
+            () => (this._isMaximized = this.maximized));
+        this.connect('notify::fullscreened',
+            () => (this._isFullscreen = this.fullscreened));
+        this.connect('notify::default-width',
+            () => (this._currentSize = this.get_default_size()));
+        this.connect('notify::default-height',
+            () => (this._currentSize = this.get_default_size()));
         this.connect('destroy', this._onDestroy.bind(this));
         this.connect('notify::active-room', () => {
             this._updateUserListLabel();
@@ -242,18 +247,6 @@ export default GObject.registerClass({
             accounts.length > 0 && !accounts.some(a => a.reachable);
     }
 
-    _onWindowStateEvent(widget, event) {
-        let state = event.get_window().get_state();
-
-        this._isFullscreen = (state & Gdk.WindowState.FULLSCREEN) !== 0;
-        this._isMaximized = (state & Gdk.WindowState.MAXIMIZED) !== 0;
-    }
-
-    _onSizeAllocate() {
-        if (!this._isFullscreen && !this._isMaximized)
-            this._currentSize = this.get_size();
-    }
-
     _onDestroy() {
         this._settings.set_boolean('window-maximized', this._isMaximized);
         this._settings.set_value('window-size',
diff --git a/src/roomList.js b/src/roomList.js
index fd6ff396..331da07a 100644
--- a/src/roomList.js
+++ b/src/roomList.js
@@ -21,7 +21,7 @@ function _onPopoverVisibleChanged(popover) {
 const RoomRow = GObject.registerClass({
     Template: 'resource:///org/gnome/Polari/ui/room-list-row.ui',
     InternalChildren: [
-        'eventBox',
+        'box',
         'icon',
         'roomLabel',
         'counter',
@@ -48,17 +48,16 @@ const RoomRow = GObject.registerClass({
 
         this._icon.visible = room.icon !== null;
 
-        this._keyController = new Gtk.EventControllerKey({
-            widget: this,
-        });
+        this._keyController = new Gtk.EventControllerKey();
         this._keyController.connect('key-pressed', this._onKeyPressed.bind(this));
+        this.add_controller(this._keyController);
 
         this._clickGesture = new Gtk.GestureClick({
-            widget: this._eventBox,
             button: Gdk.BUTTON_SECONDARY,
         });
         this._clickGesture.connect('released',
             this._onButtonReleased.bind(this));
+        this._box.add_controller(this._clickGesture);
 
         room.bind_property('display-name',
             this._roomLabel, 'label',
@@ -302,7 +301,7 @@ const RoomListHeader = GObject.registerClass({
             GObject.ParamFlags.READWRITE,
             Gtk.Popover.$gtype),
     },
-}, class RoomListHeader extends Gtk.EventBox {
+}, class RoomListHeader extends Gtk.Grid {
     _init(params) {
         this._account = params.account;
         delete params.account;
@@ -318,8 +317,8 @@ const RoomListHeader = GObject.registerClass({
         this._clickGesture = new Gtk.GestureClick({
             propagation_phase: Gtk.PropagationPhase.CAPTURE,
             button: 0,
-            widget: this,
         });
+        this.add_controller(this._clickGesture);
 
         this._clickGesture.connect('released', () => {
             const button = this._clickGesture.get_current_button();
@@ -795,6 +794,7 @@ class RoomList extends Gtk.ListBox {
 
         if (beforeAccount === account) {
             row.set_header(null);
+            oldHeader?.unparent();
             oldHeader?.run_dispose();
             return;
         }
diff --git a/src/roomStack.js b/src/roomStack.js
index d6f5ce58..7f83b543 100644
--- a/src/roomStack.js
+++ b/src/roomStack.js
@@ -70,8 +70,8 @@ export default GObject.registerClass({
         this._toplevelSignals = [];
     }
 
-    vfunc_size_allocate(allocation) {
-        super.vfunc_size_allocate(allocation);
+    vfunc_size_allocate(width, height, baseline) {
+        super.vfunc_size_allocate(width, height, baseline);
 
         const [firstEntry] =
             this._sizeGroup.get_widgets().filter(w => w.get_mapped());
diff --git a/src/tabCompletion.js b/src/tabCompletion.js
index fa5098b2..6c53bf67 100644
--- a/src/tabCompletion.js
+++ b/src/tabCompletion.js
@@ -10,10 +10,14 @@ export default class TabCompletion {
         this._key = '';
 
         this._keyController = new Gtk.EventControllerKey({
-            widget: this._entry,
+            propagation_phase: Gtk.PropagationPhase.CAPTURE,
         });
         this._keyController.connect('key-pressed', this._onKeyPressed.bind(this));
-        this._entry.connect('focus-out-event', this._cancel.bind(this));
+        this._entry.add_controller(this._keyController);
+
+        this._focusController = new Gtk.EventControllerFocus();
+        this._focusController.connect('leave', this._cancel.bind(this));
+        this._entry.add_controller(this._focusController);
 
         this._entry.connect('unmap', this._cancel.bind(this));
 


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