[fractal/wip/christopherdavis/port-to-libhandy-1.0: 2/2] fractal-gtk: Refactor window to use HdyDeck




commit 8d7809b1826981306759a3acdfc080c4714bcedb
Author: Christopher Davis <brainblasted disroot org>
Date:   Fri Sep 11 02:00:00 2020 -0700

    fractal-gtk: Refactor window to use HdyDeck
    
    Helps simplify how we handle pages, as well as
    adding support for swiping between the main view
    and different subviews.

 Cargo.lock                                       |    1 +
 fractal-gtk/Cargo.toml                           |    1 +
 fractal-gtk/res/ui/account_settings.ui           |   68 +-
 fractal-gtk/res/ui/login_flow.ui                 |  575 +++++-----
 fractal-gtk/res/ui/main_window.ui                | 1208 +++++++++++-----------
 fractal-gtk/res/ui/media_viewer.ui               |  154 +--
 fractal-gtk/res/ui/room_settings.ui              |   68 +-
 fractal-gtk/src/actions/global.rs                |   14 +-
 fractal-gtk/src/actions/login.rs                 |   44 +-
 fractal-gtk/src/actions/message.rs               |    8 +-
 fractal-gtk/src/app/connect/autocomplete.rs      |   12 +-
 fractal-gtk/src/app/connect/headerbar.rs         |   16 +-
 fractal-gtk/src/app/connect/mod.rs               |    2 +
 fractal-gtk/src/app/connect/swipeable_widgets.rs |   57 +
 fractal-gtk/src/app/mod.rs                       |   44 +-
 fractal-gtk/src/appop/about.rs                   |    2 +-
 fractal-gtk/src/appop/account.rs                 |    8 +-
 fractal-gtk/src/appop/attach.rs                  |    2 +-
 fractal-gtk/src/appop/media_viewer.rs            |   19 +-
 fractal-gtk/src/appop/message.rs                 |    2 +-
 fractal-gtk/src/appop/mod.rs                     |   12 +-
 fractal-gtk/src/appop/room.rs                    |    2 +-
 fractal-gtk/src/appop/room_settings.rs           |   24 +-
 fractal-gtk/src/appop/state.rs                   |  132 ++-
 fractal-gtk/src/main.rs                          |    6 +
 fractal-gtk/src/meson.build                      |    1 +
 fractal-gtk/src/widgets/kicked_dialog.rs         |    2 +-
 fractal-gtk/src/widgets/login.rs                 |   11 +-
 fractal-gtk/src/widgets/room_settings.rs         |   10 +-
 29 files changed, 1262 insertions(+), 1243 deletions(-)
---
diff --git a/Cargo.lock b/Cargo.lock
index 6fa4b9ca..726c3d50 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -563,6 +563,7 @@ dependencies = [
  "gstreamer-pbutils",
  "gstreamer-player",
  "gtk",
+ "gtk-sys",
  "html2pango",
  "itertools 0.8.2",
  "lazy_static",
diff --git a/fractal-gtk/Cargo.toml b/fractal-gtk/Cargo.toml
index 61a0805f..5474556b 100644
--- a/fractal-gtk/Cargo.toml
+++ b/fractal-gtk/Cargo.toml
@@ -31,6 +31,7 @@ serde_json = "1.0.48"
 letter-avatar = "1.3.0"
 sourceview4 = "0.2.0"
 gspell = "0.5.0"
+gtk-sys = "0.10.0"
 
 [dependencies.gst]
 version = "0.16.1"
diff --git a/fractal-gtk/res/ui/account_settings.ui b/fractal-gtk/res/ui/account_settings.ui
index 8e7730b1..bc550faa 100644
--- a/fractal-gtk/res/ui/account_settings.ui
+++ b/fractal-gtk/res/ui/account_settings.ui
@@ -5,12 +5,42 @@
   <object class="GtkBox" id="account_settings_box">
     <property name="visible">True</property>
     <property name="can_focus">False</property>
+    <property name="orientation">vertical</property>
+    <child>
+      <object class="HdyHeaderBar">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="show_close_button">True</property>
+        <property name="title" translatable="yes">Account Settings</property>
+        <child>
+          <object class="GtkButton" id="account_settings_back_button">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="action_name">app.deck-back</property>
+            <child>
+              <object class="GtkImage">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="icon_name">go-previous-symbolic</property>
+              </object>
+            </child>
+            <child internal-child="accessible">
+              <object class="AtkObject" id="back_button-atkobject">
+                <property name="AtkObject::accessible-name" translatable="yes">Back</property>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
     <child>
       <object class="GtkScrolledWindow" id="account_settings_scroll">
         <property name="width_request">200</property>
         <property name="visible">True</property>
         <property name="can_focus">True</property>
         <property name="hscrollbar_policy">never</property>
+        <property name="expand">True</property>
         <child>
           <object class="GtkViewport">
             <property name="width_request">200</property>
@@ -567,44 +597,6 @@
           </object>
         </child>
       </object>
-      <packing>
-        <property name="expand">False</property>
-        <property name="fill">True</property>
-        <property name="position">0</property>
-      </packing>
-    </child>
-  </object>
-  <object class="GtkBox" id="account_settings_headerbar">
-    <property name="visible">True</property>
-    <property name="can_focus">False</property>
-    <child>
-      <object class="GtkHeaderBar">
-        <property name="visible">True</property>
-        <property name="can_focus">False</property>
-        <property name="show_close_button">True</property>
-        <property name="expand">True</property>
-        <property name="title" translatable="yes">Account Settings</property>
-        <child>
-          <object class="GtkButton" id="account_settings_back_button">
-            <property name="visible">True</property>
-            <property name="can_focus">True</property>
-            <property name="receives_default">True</property>
-            <property name="action_name">app.back</property>
-            <child>
-              <object class="GtkImage">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="icon_name">go-previous-symbolic</property>
-              </object>
-            </child>
-            <child internal-child="accessible">
-              <object class="AtkObject" id="back_button-atkobject">
-                <property name="AtkObject::accessible-name" translatable="yes">Back</property>
-              </object>
-            </child>
-          </object>
-        </child>
-      </object>
     </child>
   </object>
   <object class="GtkDialog" id="account_settings_email_dialog">
diff --git a/fractal-gtk/res/ui/login_flow.ui b/fractal-gtk/res/ui/login_flow.ui
index ebaac45a..5dccbc8e 100644
--- a/fractal-gtk/res/ui/login_flow.ui
+++ b/fractal-gtk/res/ui/login_flow.ui
@@ -2,63 +2,80 @@
 <!-- Generated with glade 3.22.0 -->
 <interface>
   <requires lib="gtk+" version="3.22"/>
-  <object class="GtkStack" id="login_flow_stack">
+  <object class="HdyDeck" id="login_flow_deck">
     <property name="can_focus">False</property>
+    <property name="can_swipe_back">True</property>
     <property name="hhomogeneous">True</property>
-    <property name="transition_type">GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT</property>
+    <property name="visible_child_name">greeter</property>
     <child>
-      <object class="GtkBox" id="login_greeter">
+      <object class="GtkBox">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
-        <property name="halign">center</property>
-        <property name="valign">center</property>
         <property name="orientation">vertical</property>
-        <property name="spacing">18</property>
         <child>
-          <object class="GtkImage" id="login_greeter_image">
+          <object class="HdyHeaderBar" id="login_greeter_header">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
-            <property name="halign">center</property>
-            <property name="margin_start">18</property>
-            <property name="margin_end">18</property>
-            <property name="margin_top">18</property>
-            <property name="pixel_size">128</property>
-            <property name="icon_name">chat-icon</property>
+            <property name="show_close_button">True</property>
+            <property name="title" translatable="yes">Fractal</property>
           </object>
         </child>
         <child>
-          <object class="GtkLabel">
+          <object class="GtkBox" id="login_greeter">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
-            <property name="label" translatable="yes">Welcome to Fractal</property>
-            <property name="margin_bottom">48</property>
-            <property name="wrap">True</property>
-            <property name="wrap_mode">PANGO_WRAP_WORD_CHAR</property>
-            <attributes>
-              <attribute name="weight" value="ultrabold"/>
-              <attribute name="scale" value="1.7"/>
-            </attributes>
-          </object>
-        </child>
-        <child>
-          <object class="GtkButton" id="login_button">
-            <property name="visible">True</property>
-            <property name="use_underline">True</property>
-            <property name="label" translatable="yes">_Log In</property>
-            <property name="action_name">login.server_chooser</property>
-            <property name="height-request">48</property>
-            <style>
-              <class name="suggested-action"/>
-            </style>
-          </object>
-        </child>
-        <child>
-          <object class="GtkButton" id="create_account_button">
-            <property name="visible">True</property>
-            <property name="use_underline">True</property>
-            <property name="label" translatable="yes">_Create Account</property>
-            <property name="height-request">48</property>
-            <property name="action_name">login.create-account</property>
+            <property name="halign">center</property>
+            <property name="valign">center</property>
+            <property name="expand">True</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">18</property>
+            <child>
+              <object class="GtkImage" id="login_greeter_image">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="halign">center</property>
+                <property name="margin_start">18</property>
+                <property name="margin_end">18</property>
+                <property name="margin_top">18</property>
+                <property name="pixel_size">128</property>
+                <property name="icon_name">chat-icon</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" translatable="yes">Welcome to Fractal</property>
+                <property name="margin_bottom">48</property>
+                <property name="wrap">True</property>
+                <property name="wrap_mode">PANGO_WRAP_WORD_CHAR</property>
+                <attributes>
+                  <attribute name="weight" value="ultrabold"/>
+                  <attribute name="scale" value="1.7"/>
+                </attributes>
+              </object>
+            </child>
+            <child>
+              <object class="GtkButton" id="login_button">
+                <property name="visible">True</property>
+                <property name="use_underline">True</property>
+                <property name="label" translatable="yes">_Log In</property>
+                <property name="action_name">login.server_chooser</property>
+                <property name="height-request">48</property>
+                <style>
+                  <class name="suggested-action"/>
+                </style>
+              </object>
+            </child>
+            <child>
+              <object class="GtkButton" id="create_account_button">
+                <property name="visible">True</property>
+                <property name="use_underline">True</property>
+                <property name="label" translatable="yes">_Create Account</property>
+                <property name="height-request">48</property>
+                <property name="action_name">login.create-account</property>
+              </object>
+            </child>
           </object>
         </child>
       </object>
@@ -67,314 +84,310 @@
       </packing>
     </child>
     <child>
-      <object class="GtkBox" id="login_server_chooser">
+      <object class="GtkBox">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
-        <property name="halign">center</property>
-        <property name="valign">center</property>
         <property name="orientation">vertical</property>
-        <property name="spacing">18</property>
+        <property name="expand">True</property>
         <child>
-          <object class="GtkImage">
+          <object class="HdyHeaderBar" id="login_server_chooser_header">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
-            <property name="halign">center</property>
-            <property name="margin_start">18</property>
-            <property name="margin_end">18</property>
-            <property name="pixel_size">128</property>
-            <property name="icon_name">network-server-symbolic</property>
-            <style>
-              <class name="dim-label"/>
-            </style>
-          </object>
-        </child>
-        <child>
-          <object class="GtkLabel">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="label" translatable="yes">What is your Provider?</property>
-            <property name="margin_bottom">30</property>
-            <property name="wrap">True</property>
-            <property name="wrap_mode">PANGO_WRAP_WORD_CHAR</property>
-            <attributes>
-              <attribute name="weight" value="ultrabold"/>
-              <attribute name="scale" value="1.7"/>
-            </attributes>
+            <property name="show_close_button">True</property>
+            <property name="width_request">360</property>
+            <property name="title" translatable="yes">Choose Provider</property>
+            <child>
+              <object class="GtkButton">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="action_name">login.back</property>
+                <child>
+                  <object class="GtkImage">
+                    <property name="visible">True</property>
+                    <property name="icon_name">go-previous-symbolic</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="pack_type">start</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton">
+                <property name="visible">True</property>
+                <property name="use_underline">True</property>
+                <property name="can_focus">True</property>
+                <property name="action_name">login.credentials</property>
+                <property name="label" translatable="yes">_Next</property>
+                <style>
+                  <class name="suggested-action"/>
+                </style>
+              </object>
+              <packing>
+                <property name="pack_type">end</property>
+              </packing>
+            </child>
           </object>
         </child>
         <child>
-          <object class="GtkBox">
+          <object class="GtkBox" id="login_server_chooser">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
+            <property name="halign">center</property>
+            <property name="valign">center</property>
+            <property name="expand">True</property>
             <property name="orientation">vertical</property>
-            <property name="spacing">6</property>
+            <property name="spacing">18</property>
             <child>
-              <object class="GtkEntry" id="server_chooser_entry">
+              <object class="GtkImage">
                 <property name="visible">True</property>
+                <property name="can_focus">False</property>
                 <property name="halign">center</property>
-                <property name="max_width_chars">-1</property>
-                <property name="width_request">300</property>
-                <property name="input_purpose">GTK_INPUT_PURPOSE_URL</property>
+                <property name="margin_start">18</property>
+                <property name="margin_end">18</property>
+                <property name="pixel_size">128</property>
+                <property name="icon_name">network-server-symbolic</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
               </object>
             </child>
             <child>
               <object class="GtkLabel">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
-                <property name="label" translatable="yes">Matrix provider domain, e.g. myserver.co</property>
+                <property name="label" translatable="yes">What is your Provider?</property>
+                <property name="margin_bottom">30</property>
+                <property name="wrap">True</property>
+                <property name="wrap_mode">PANGO_WRAP_WORD_CHAR</property>
+                <attributes>
+                  <attribute name="weight" value="ultrabold"/>
+                  <attribute name="scale" value="1.7"/>
+                </attributes>
+              </object>
+            </child>
+            <child>
+              <object class="GtkBox">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">6</property>
+                <child>
+                  <object class="GtkEntry" id="server_chooser_entry">
+                    <property name="visible">True</property>
+                    <property name="halign">center</property>
+                    <property name="max_width_chars">-1</property>
+                    <property name="width_request">300</property>
+                    <property name="input_purpose">GTK_INPUT_PURPOSE_URL</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">Matrix provider domain, e.g. 
myserver.co</property>
+                    <property name="wrap">True</property>
+                    <property name="wrap_mode">PANGO_WRAP_WORD_CHAR</property>
+                    <style>
+                      <class name="dim-label"/>
+                      <class name="small-label"/>
+                    </style>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkLabel" id="server_err_label">
+                <property name="visible">False</property>
+                <property name="can_focus">False</property>
+                <property name="no_show_all">True</property>
+                <property name="label" translatable="yes">The domain may not be empty.</property>
                 <property name="wrap">True</property>
                 <property name="wrap_mode">PANGO_WRAP_WORD_CHAR</property>
                 <style>
-                  <class name="dim-label"/>
-                  <class name="small-label"/>
+                  <class name="error-label"/>
                 </style>
               </object>
             </child>
           </object>
         </child>
-        <child>
-          <object class="GtkLabel" id="server_err_label">
-            <property name="visible">False</property>
-            <property name="can_focus">False</property>
-            <property name="no_show_all">True</property>
-            <property name="label" translatable="yes">The domain may not be empty.</property>
-            <property name="wrap">True</property>
-            <property name="wrap_mode">PANGO_WRAP_WORD_CHAR</property>
-            <style>
-              <class name="error-label"/>
-            </style>
-          </object>
-        </child>
       </object>
       <packing>
         <property name="name">server-chooser</property>
       </packing>
     </child>
     <child>
-      <object class="GtkGrid" id="login_credentials">
+      <object class="GtkBox">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
-        <property name="halign">center</property>
-        <property name="valign">center</property>
-        <property name="column_spacing">12</property>
-        <property name="row_spacing">24</property>
-        <child>
-          <object class="GtkLabel">
-            <property name="visible">True</property>
-            <property name="use_underline">True</property>
-            <property name="can_focus">False</property>
-            <property name="label" translatable="yes">_User ID</property>
-            <property name="halign">end</property>
-            <property name="valign">end</property>
-            <property name="wrap">True</property>
-            <property name="wrap_mode">PANGO_WRAP_WORD_CHAR</property>
-            <property name="mnemonic_widget">username_entry</property>
-            <style>
-              <class name="dim-label"/>
-            </style>
-          </object>
-        </child>
+        <property name="orientation">vertical</property>
         <child>
-          <object class="GtkLabel">
+          <object class="GtkHeaderBar" id="login_credentials_header">
             <property name="visible">True</property>
-            <property name="use_underline">True</property>
             <property name="can_focus">False</property>
-            <property name="label" translatable="yes">_Password</property>
-            <property name="halign">end</property>
-            <property name="wrap">True</property>
-            <property name="wrap_mode">PANGO_WRAP_WORD_CHAR</property>
-            <property name="mnemonic_widget">password_entry</property>
-            <style>
-              <class name="dim-label"/>
-            </style>
+            <property name="show_close_button">True</property>
+            <property name="title" translatable="yes">Log In</property>
+            <child>
+              <object class="GtkButton">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="action_name">login.back</property>
+                <child>
+                  <object class="GtkImage">
+                    <property name="visible">True</property>
+                    <property name="icon_name">go-previous-symbolic</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="pack_type">start</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton">
+                <property name="visible">True</property>
+                <property name="use_underline">True</property>
+                <property name="can_focus">True</property>
+                <property name="action_name">login.login</property>
+                <property name="label" translatable="yes">_Log In</property>
+                <style>
+                  <class name="suggested-action"/>
+                </style>
+              </object>
+              <packing>
+                <property name="pack_type">end</property>
+              </packing>
+            </child>
           </object>
-          <packing>
-            <property name="top-attach">3</property>
-            <property name="left-attach">0</property>
-          </packing>
         </child>
         <child>
-          <object class="GtkBox">
+          <object class="GtkGrid" id="login_credentials">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
-            <property name="orientation">vertical</property>
-            <property name="spacing">6</property>
+            <property name="halign">center</property>
+            <property name="valign">center</property>
+            <property name="expand">True</property>
+            <property name="column_spacing">12</property>
+            <property name="row_spacing">24</property>
             <child>
-              <object class="GtkEntry" id="username_entry">
+              <object class="GtkLabel">
                 <property name="visible">True</property>
-                <property name="max_width_chars">-1</property>
-                <property name="width_request">232</property>
-                <property name="can_focus">True</property>
+                <property name="use_underline">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" translatable="yes">_User ID</property>
+                <property name="halign">end</property>
+                <property name="valign">end</property>
+                <property name="wrap">True</property>
+                <property name="wrap_mode">PANGO_WRAP_WORD_CHAR</property>
+                <property name="mnemonic_widget">username_entry</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
               </object>
             </child>
             <child>
               <object class="GtkLabel">
                 <property name="visible">True</property>
+                <property name="use_underline">True</property>
                 <property name="can_focus">False</property>
-                <property name="label" translatable="yes">User name, email, or phone number</property>
-                <property name="halign">start</property>
+                <property name="label" translatable="yes">_Password</property>
+                <property name="halign">end</property>
                 <property name="wrap">True</property>
                 <property name="wrap_mode">PANGO_WRAP_WORD_CHAR</property>
+                <property name="mnemonic_widget">password_entry</property>
                 <style>
                   <class name="dim-label"/>
-                  <class name="small-font"/>
                 </style>
               </object>
+              <packing>
+                <property name="top-attach">3</property>
+                <property name="left-attach">0</property>
+              </packing>
             </child>
-          </object>
-          <packing>
-            <property name="left-attach">1</property>
-            <property name="height">2</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkEntry" id="password_entry">
-            <property name="visible">True</property>
-            <property name="max_width_chars">-1</property>
-            <property name="width_request">232</property>
-            <property name="can_focus">True</property>
-            <property name="visibility">False</property>
-            <property name="input_purpose">GTK_INPUT_PURPOSE_PASSWORD</property>
-          </object>
-          <packing>
-            <property name="top-attach">3</property>
-            <property name="left-attach">1</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkLinkButton" id="forgot_password">
-            <property name="use_underline">True</property>
-            <property name="label" translatable="yes">_Forgot Password?</property>
-            <property name="uri">https://riot.im/app/#/login</property>
-            <property name="halign">start</property>
-            <style>
-              <class name="forgot-password"/>
-            </style>
-          </object>
-          <packing>
-            <property name="top-attach">4</property>
-            <property name="left-attach">1</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkLabel" id="credentials_err_label">
-            <property name="visible">False</property>
-            <property name="can_focus">False</property>
-            <property name="no_show_all">True</property>
-            <property name="xalign">0</property>
-            <property name="label" translatable="yes">Invalid username or password</property>
-            <property name="wrap">True</property>
-            <property name="wrap_mode">PANGO_WRAP_WORD_CHAR</property>
-            <style>
-              <class name="error-label"/>
-            </style>
-          </object>
-          <packing>
-            <property name="top-attach">5</property>
-            <property name="left-attach">1</property>
-          </packing>
-        </child>
-      </object>
-      <packing>
-        <property name="name">credentials</property>
-      </packing>
-    </child>
-  </object>
-  <object class="GtkStack" id="login_flow_headers">
-    <property name="can_focus">False</property>
-    <property name="hhomogeneous">True</property>
-    <property name="visible_child_name" bind-source="login_flow_stack" bind-property="visible-child-name" 
bind-flags="sync-create"/>
-    <property name="transition_duration" bind-source="login_flow_stack" bind-property="transition-duration" 
bind-flags="sync-create"/>
-    <property name="transition_type" bind-source="login_flow_stack" bind-property="transition-type" 
bind-flags="sync-create"/>
-    <child>
-      <object class="GtkHeaderBar" id="login_greeter_header">
-        <property name="visible">True</property>
-        <property name="can_focus">False</property>
-        <property name="show_close_button">True</property>
-        <property name="title" translatable="yes">Fractal</property>
-      </object>
-      <packing>
-        <property name="name">greeter</property>
-      </packing>
-    </child>
-    <child>
-      <object class="GtkHeaderBar" id="login_server_chooser_header">
-        <property name="visible">True</property>
-        <property name="can_focus">False</property>
-        <property name="show_close_button">True</property>
-        <property name="width_request">360</property>
-        <property name="title" translatable="yes">Choose Provider</property>
-        <child>
-          <object class="GtkButton">
-            <property name="visible">True</property>
-            <property name="can_focus">True</property>
-            <property name="action_name">login.back</property>
             <child>
-              <object class="GtkImage">
+              <object class="GtkBox">
                 <property name="visible">True</property>
-                <property name="icon_name">go-previous-symbolic</property>
+                <property name="can_focus">False</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">6</property>
+                <child>
+                  <object class="GtkEntry" id="username_entry">
+                    <property name="visible">True</property>
+                    <property name="max_width_chars">-1</property>
+                    <property name="width_request">232</property>
+                    <property name="can_focus">True</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">User name, email, or phone number</property>
+                    <property name="halign">start</property>
+                    <property name="wrap">True</property>
+                    <property name="wrap_mode">PANGO_WRAP_WORD_CHAR</property>
+                    <style>
+                      <class name="dim-label"/>
+                      <class name="small-font"/>
+                    </style>
+                  </object>
+                </child>
               </object>
+              <packing>
+                <property name="left-attach">1</property>
+                <property name="height">2</property>
+              </packing>
             </child>
-          </object>
-          <packing>
-            <property name="pack_type">start</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkButton">
-            <property name="visible">True</property>
-            <property name="use_underline">True</property>
-            <property name="can_focus">True</property>
-            <property name="action_name">login.credentials</property>
-            <property name="label" translatable="yes">_Next</property>
-            <style>
-              <class name="suggested-action"/>
-            </style>
-          </object>
-          <packing>
-            <property name="pack_type">end</property>
-          </packing>
-        </child>
-      </object>
-      <packing>
-        <property name="name">server-chooser</property>
-      </packing>
-    </child>
-    <child>
-      <object class="GtkHeaderBar" id="login_credentials_header">
-        <property name="visible">True</property>
-        <property name="can_focus">False</property>
-        <property name="show_close_button">True</property>
-        <property name="title" translatable="yes">Log In</property>
-        <child>
-          <object class="GtkButton">
-            <property name="visible">True</property>
-            <property name="can_focus">True</property>
-            <property name="action_name">login.back</property>
             <child>
-              <object class="GtkImage">
+              <object class="GtkEntry" id="password_entry">
                 <property name="visible">True</property>
-                <property name="icon_name">go-previous-symbolic</property>
+                <property name="max_width_chars">-1</property>
+                <property name="width_request">232</property>
+                <property name="can_focus">True</property>
+                <property name="visibility">False</property>
+                <property name="input_purpose">GTK_INPUT_PURPOSE_PASSWORD</property>
               </object>
+              <packing>
+                <property name="top-attach">3</property>
+                <property name="left-attach">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLinkButton" id="forgot_password">
+                <property name="use_underline">True</property>
+                <property name="label" translatable="yes">_Forgot Password?</property>
+                <property name="uri">https://riot.im/app/#/login</property>
+                <property name="halign">start</property>
+                <style>
+                  <class name="forgot-password"/>
+                </style>
+              </object>
+              <packing>
+                <property name="top-attach">4</property>
+                <property name="left-attach">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="credentials_err_label">
+                <property name="visible">False</property>
+                <property name="can_focus">False</property>
+                <property name="no_show_all">True</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Invalid username or password</property>
+                <property name="wrap">True</property>
+                <property name="wrap_mode">PANGO_WRAP_WORD_CHAR</property>
+                <style>
+                  <class name="error-label"/>
+                </style>
+              </object>
+              <packing>
+                <property name="top-attach">5</property>
+                <property name="left-attach">1</property>
+              </packing>
             </child>
           </object>
           <packing>
-            <property name="pack_type">start</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkButton">
-            <property name="visible">True</property>
-            <property name="use_underline">True</property>
-            <property name="can_focus">True</property>
-            <property name="action_name">login.login</property>
-            <property name="label" translatable="yes">_Log In</property>
-            <style>
-              <class name="suggested-action"/>
-            </style>
-          </object>
-          <packing>
-            <property name="pack_type">end</property>
+            <property name="name">credentials</property>
           </packing>
         </child>
       </object>
diff --git a/fractal-gtk/res/ui/main_window.ui b/fractal-gtk/res/ui/main_window.ui
index 4762cd2f..105da575 100644
--- a/fractal-gtk/res/ui/main_window.ui
+++ b/fractal-gtk/res/ui/main_window.ui
@@ -2,610 +2,699 @@
 <!-- Generated with glade 3.20.2 -->
 <interface>
   <requires lib="gtk+" version="3.20"/>
-  <object class="GtkApplicationWindow" id="main_window">
+  <object class="HdyApplicationWindow" id="main_window">
     <property name="can_focus">False</property>
     <property name="default_width">860</property>
     <property name="default_height">640</property>
     <property name="show_menubar">False</property>
     <child>
-      <object class="GtkStack" id="main_content_stack">
-        <property name="can_focus">False</property>
-        <property name="hhomogeneous">False</property>
+      <object class="GtkBox">
+        <property name="visible">True</property>
         <child>
-          <object class="HdyLeaflet" id="chat_state_leaflet">
-            <property name="can-swipe-back" bind-source="header_leaflet" bind-property="can-swipe-back" 
bind-flags="bidirectional|sync-create"/>
-            <property name="child-transition-duration" bind-source="header_leaflet" 
bind-property="child-transition-duration" bind-flags="bidirectional|sync-create"/>
-            <property name="mode-transition-duration" bind-source="header_leaflet" 
bind-property="mode-transition-duration" bind-flags="bidirectional|sync-create"/>
-            <property name="transition-type" bind-source="header_leaflet" bind-property="transition-type" 
bind-flags="bidirectional|sync-create"/>
-            <property name="visible">True</property>
+          <object class="GtkStack" id="main_content_stack">
             <property name="can_focus">False</property>
-            <property name="hhomogeneous-folded">True</property>
+            <property name="hhomogeneous">False</property>
             <child>
-              <object class="GtkBox" id="sidebar-box">
-                <property name="width_request">200</property>
+              <object class="HdyDeck" id="main_deck">
                 <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="halign">fill</property>
-                <property name="hexpand">False</property>
-                <property name="orientation">vertical</property>
-                <child>
-                  <object class="GtkSearchBar" id="room_list_searchbar">
-                    <property name="width_request">200</property>
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <child>
-                      <object class="GtkSearchEntry" id="room_list_search">
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="primary_icon_name">edit-find-symbolic</property>
-                        <property name="primary_icon_activatable">False</property>
-                        <property name="primary_icon_sensitive">False</property>
-                      </object>
-                    </child>
-                  </object>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="fill">True</property>
-                    <property name="position">0</property>
-                  </packing>
-                </child>
+                <property name="expand">True</property>
+                <property name="can_swipe_back">True</property>
+                <property name="visible_child_name">chat</property>
+                <property name="hhomogeneous">False</property>
                 <child>
-                  <object class="GtkScrolledWindow" id="roomlist_scroll">
-                    <property name="width_request">200</property>
+                  <object class="HdyLeaflet" id="chat_page">
                     <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="hscrollbar_policy">never</property>
+                    <property name="visible_child">room_list</property>
+                    <property name="can_swipe_back">True</property>
                     <child>
-                      <object class="GtkViewport">
-                        <property name="width_request">200</property>
+                      <object class="GtkBox" id="room_list">
                         <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="shadow_type">none</property>
+                        <property name="orientation">vertical</property>
                         <child>
-                          <object class="GtkBox" id="room_container">
+                          <object class="HdyHeaderBar" id="left-header"> <!--left titlebar-->
                             <property name="visible">True</property>
                             <property name="can_focus">False</property>
-                            <property name="orientation">vertical</property>
+                            <property name="halign">fill</property>
+                            <property name="show-close-button">True</property>
                             <child>
-                              <placeholder/>
+                              <object class="GtkMenuButton" id="main_menu_button">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">True</property>
+                                <property name="popover">user_popover</property>
+                                <child>
+                                  <object class="GtkImage">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">False</property>
+                                    <property name="icon_name">open-menu-symbolic</property>
+                                  </object>
+                                </child>
+                                <accessibility>
+
+                                </accessibility>
+                                <child internal-child="accessible">
+                                  <object class="AtkObject" id="a11y-main_menu_button">
+                                    <property name="AtkObject::accessible_name" 
translatable="yes">User</property>
+                                  </object>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="pack_type">end</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkMenuButton" id="add_room_menu">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">True</property>
+                                <property name="popover">add_room_popover</property>
+                                <child>
+                                  <object class="GtkImage">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">False</property>
+                                    <property name="icon_name">list-add-symbolic</property>
+                                  </object>
+                                </child>
+                                <accessibility>
+
+                                </accessibility>
+                                <child internal-child="accessible">
+                                  <object class="AtkObject" id="a11y-add_room_menu">
+                                    <property name="AtkObject::accessible_name" 
translatable="yes">Add</property>
+                                  </object>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="pack_type">end</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkToggleButton" id="room_search_button">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">True</property>
+                                <child>
+                                  <object class="GtkImage">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">False</property>
+                                    <property name="icon_name">system-search-symbolic</property>
+                                  </object>
+                                </child>
+                                <accelerator key="k" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+                                <style>
+                                  <class name="room-search-button"/>
+                                </style>
+                                <accessibility>
+
+                                </accessibility>
+                                <child internal-child="accessible">
+                                  <object class="AtkObject" id="a11y-room_search_button">
+                                    <property name="AtkObject::accessible_name" translatable="yes">Room 
search</property>
+                                  </object>
+                                </child>
+                              </object>
                             </child>
-                            <style>
-                              <class name="rooms-sidebar"/>
-                              <class name="sidebar"/>
-                            </style>
                           </object>
                         </child>
-                      </object>
-                    </child>
-                  </object>
-                  <packing>
-                    <property name="expand">True</property>
-                    <property name="fill">True</property>
-                    <property name="position">1</property>
-                  </packing>
-                </child>
-              </object>
-              <packing>
-                <property name="name">sidebar</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkSeparator" id="content_separator">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <style>
-                    <class name="sidebar"/>
-                </style>
-              </object>
-            </child>
-            <child>
-              <object class="GtkOverlay" id="inapp">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <child>
-                  <object class="GtkStack" id="room_view_stack">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <child>
-                      <object class="GtkBox" id="focused_room">
-                        <property name="name">room</property>
-                        <property name="can_focus">False</property>
                         <child>
-                          <object class="GtkBox">
+                          <object class="GtkBox" id="sidebar-box">
+                            <property name="width_request">200</property>
                             <property name="visible">True</property>
                             <property name="can_focus">False</property>
+                            <property name="halign">fill</property>
+                            <property name="hexpand">False</property>
+                            <property name="vexpand">True</property>
                             <property name="orientation">vertical</property>
                             <child>
-                              <object class="GtkBox" id="history_container">
+                              <object class="GtkSearchBar" id="room_list_searchbar">
+                                <property name="width_request">200</property>
                                 <property name="visible">True</property>
                                 <property name="can_focus">False</property>
-                                <property name="orientation">vertical</property>
+                                <child>
+                                  <object class="GtkSearchEntry" id="room_list_search">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="primary_icon_name">edit-find-symbolic</property>
+                                    <property name="primary_icon_activatable">False</property>
+                                    <property name="primary_icon_sensitive">False</property>
+                                  </object>
+                                </child>
                               </object>
                               <packing>
-                                <property name="expand">True</property>
+                                <property name="expand">False</property>
                                 <property name="fill">True</property>
-                                <property name="position">1</property>
+                                <property name="position">0</property>
                               </packing>
                             </child>
                             <child>
-                              <object class="GtkBox" id="room_parent">
+                              <object class="GtkScrolledWindow" id="roomlist_scroll">
+                                <property name="width_request">200</property>
                                 <property name="visible">True</property>
-                                <style>
-                                  <class name="message-input-area" />
-                                </style>
+                                <property name="can_focus">True</property>
+                                <property name="hscrollbar_policy">never</property>
+                                <child>
+                                  <object class="GtkViewport">
+                                    <property name="width_request">200</property>
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">False</property>
+                                    <property name="shadow_type">none</property>
+                                    <child>
+                                      <object class="GtkBox" id="room_container">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                        <property name="orientation">vertical</property>
+                                        <child>
+                                          <placeholder/>
+                                        </child>
+                                        <style>
+                                          <class name="rooms-sidebar"/>
+                                          <class name="sidebar"/>
+                                        </style>
+                                      </object>
+                                    </child>
+                                  </object>
+                                </child>
                               </object>
+                              <packing>
+                                <property name="expand">True</property>
+                                <property name="fill">True</property>
+                                <property name="position">1</property>
+                              </packing>
                             </child>
                           </object>
-                          <packing>
-                            <property name="expand">True</property>
-                            <property name="fill">True</property>
-                            <property name="position">0</property>
-                          </packing>
                         </child>
                       </object>
                       <packing>
-                        <property name="name">room_view</property>
-                        <property name="title">room_view</property>
+                        <property name="name">sidebar</property>
                       </packing>
                     </child>
                     <child>
-                      <object class="GtkSpinner">
+                      <object class="GtkSeparator" id="header_separator">
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
-                        <property name="active">True</property>
+                        <property name="orientation">vertical</property>
+                        <style>
+                          <class name="sidebar"/>
+                        </style>
                       </object>
                       <packing>
-                        <property name="name">loading</property>
-                        <property name="title">loading</property>
-                        <property name="position">1</property>
+                        <property name="navigatable">False</property>
                       </packing>
                     </child>
                     <child>
-                      <object class="GtkBox">
+                      <object class="GtkBox" id="room_view">
                         <property name="visible">True</property>
-                        <property name="can_focus">False</property>
                         <property name="orientation">vertical</property>
-                        <property name="margin_bottom">30</property>
-                        <property name="valign">center</property>
-                        <child>
-                          <object class="GtkImage">
-                            <property name="visible">True</property>
-                            <property name="can_focus">False</property>
-                            <property name="margin_bottom">16</property>
-                            <property name="resource">/org/gnome/Fractal/icons/chat-icon.svg</property>
-                          </object>
-                        </child>
                         <child>
-                          <object class="GtkLabel">
-                            <property name="visible">True</property>
-                            <property name="can_focus">False</property>
-                            <property name="label" translatable="yes">No room selected</property>
-                            <property name="margin_bottom">3</property>
-                            <property name="justify">center</property>
-                            <attributes>
-                              <attribute name="weight" value="bold"/>
-                            </attributes>
-                            <style>
-                              <class name="noroom-title"/>
-                            </style>
+                          <object class="HdyHeaderBar" id="room_header_bar"> <!--right titlebar-->
+                            <property name="show-close-button">True</property>
+                            <property name="has-subtitle">False</property>
+                            <property name="hexpand">true</property>
+                            <property name="width-request">360</property>
+                            <child>
+                              <object class="GtkRevealer">
+                                <property name="reveal-child" bind-source="chat_page" bind-property="folded" 
bind-flags="sync-create"/>
+                                <property name="transition-duration" bind-source="chat_page" 
bind-property="mode-transition-duration" bind-flags="bidirectional|sync-create"/>
+                                <property name="transition-type">crossfade</property>
+                                <property name="visible">True</property>
+                                <child>
+                                  <object class="GtkButton" id="leaflet_back_button">
+                                    <property name="action_name">app.back</property>
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="receives_default">True</property>
+                                    <child>
+                                      <object class="GtkImage">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                        <property name="icon_name">go-previous-symbolic</property>
+                                      </object>
+                                    </child>
+                                    <accessibility>
+
+                                    </accessibility>
+                                    <child internal-child="accessible">
+                                      <object class="AtkObject" id="a11y-leaflet_back_button">
+                                        <property name="AtkObject::accessible_name" 
translatable="yes">Back</property>
+                                      </object>
+                                    </child>
+                                  </object>
+                                </child>
+                              </object>
+                            </child>
+                            <child type="title">
+                              <object class="GtkScrolledWindow">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="hexpand">True</property>
+                                <property name="valign">center</property>
+                                <property name="hscrollbar_policy">never</property>
+                                <property name="vscrollbar_policy">never</property>
+                                <property name="propagate_natural_height">True</property>
+                                <property name="propagate_natural_width">False</property>
+                                <child>
+                                  <object class="GtkBox" id="room_info">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">False</property>
+                                    <property name="orientation">vertical</property>
+                                    <child>
+                                      <object class="GtkLabel" id="room_name">
+                                        <property name="can_focus">False</property>
+                                        <!-- Translators: This string is replaced not user-visible -->
+                                        <property name="label">Room name</property>
+                                        <property name="ellipsize">end</property>
+                                        <style>
+                                          <class name="title"/>
+                                        </style>
+                                      </object>
+                                      <packing>
+                                        <property name="expand">False</property>
+                                        <property name="fill">True</property>
+                                        <property name="position">0</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <object class="GtkLabel" id="room_topic">
+                                        <property name="can_focus">False</property>
+                                        <!-- Translators: This string is replaced not user-visible -->
+                                        <property name="label">Room topic</property>
+                                        <property name="ellipsize">end</property>
+                                        <style>
+                                          <class name="subtitle"/>
+                                        </style>
+                                      </object>
+                                      <packing>
+                                        <property name="expand">False</property>
+                                        <property name="fill">True</property>
+                                        <property name="position">1</property>
+                                      </packing>
+                                    </child>
+                                  </object>
+                                </child>
+                              </object>
+                            </child>
+                            <child>
+                              <object class="GtkMenuButton" id="room_menu_button">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">True</property>
+                                <property name="popover">room_popover</property>
+                                <child>
+                                  <object class="GtkImage">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">False</property>
+                                    <property name="icon_name">view-more-symbolic</property>
+                                  </object>
+                                </child>
+                                <accessibility>
+                                </accessibility>
+                                <child internal-child="accessible">
+                                  <object class="AtkObject" id="a11y-room_menu_button">
+                                    <property name="AtkObject::accessible_name" translatable="yes">Room 
Menu</property>
+                                  </object>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="pack_type">end</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
                           </object>
                         </child>
                         <child>
-                          <object class="GtkLabel">
+                          <object class="GtkOverlay" id="inapp">
                             <property name="visible">True</property>
                             <property name="can_focus">False</property>
-                            <property name="label" translatable="yes">Join a room to start 
chatting</property>
-                            <property name="justify">center</property>
-                            <style>
-                              <class name="noroom-description"/>
-                            </style>
-                          </object>
-                        </child>
-                      </object>
-                      <packing>
-                        <property name="name">noroom</property>
-                        <property name="title" translatable="yes">No room</property>
-                        <property name="position">2</property>
-                      </packing>
-                    </child>
-                  </object>
-                  <packing>
-                    <property name="index">-1</property>
-                  </packing>
-                </child>
-                <child type="overlay">
-                  <object class="GtkOverlay" id="inapp_notify">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="valign">start</property>
-                    <property name="halign">center</property>
-                    <child>
-                      <object class="GtkRevealer" id="inapp_revealer">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="halign">center</property>
-                        <child>
-                          <object class="GtkFrame" id="inapp_frame">
-                            <property name="visible">True</property>
-                            <property name="can_focus">False</property>
-                            <property name="label_xalign">0</property>
-                            <property name="shadow_type">none</property>
                             <child>
-                              <object class="GtkBox" id="inapp_box">
+                              <object class="GtkStack" id="room_view_stack">
                                 <property name="visible">True</property>
                                 <property name="can_focus">False</property>
-                                <property name="spacing">10</property>
                                 <child>
-                                  <object class="GtkSpinner" id="inapp_spinner">
+                                  <object class="GtkBox" id="focused_room">
+                                    <property name="can_focus">False</property>
+                                    <child>
+                                      <object class="GtkBox">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                        <property name="orientation">vertical</property>
+                                        <child>
+                                          <object class="GtkBox" id="history_container">
+                                            <property name="visible">True</property>
+                                            <property name="can_focus">False</property>
+                                            <property name="orientation">vertical</property>
+                                          </object>
+                                          <packing>
+                                            <property name="expand">True</property>
+                                            <property name="fill">True</property>
+                                            <property name="position">1</property>
+                                          </packing>
+                                        </child>
+                                        <child>
+                                          <object class="GtkBox" id="room_parent">
+                                            <property name="visible">True</property>
+                                            <style>
+                                              <class name="message-input-area" />
+                                            </style>
+                                          </object>
+                                        </child>
+                                      </object>
+                                      <packing>
+                                        <property name="expand">True</property>
+                                        <property name="fill">True</property>
+                                        <property name="position">0</property>
+                                      </packing>
+                                    </child>
+                                  </object>
+                                  <packing>
+                                    <property name="name">room_view</property>
+                                    <property name="title">room_view</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkSpinner">
                                     <property name="visible">True</property>
-                                    <property name="active">True</property>
                                     <property name="can_focus">False</property>
+                                    <property name="active">True</property>
                                   </object>
                                   <packing>
-                                    <property name="expand">False</property>
-                                    <property name="fill">True</property>
-                                    <property name="position">0</property>
+                                    <property name="name">loading</property>
+                                    <property name="title">loading</property>
+                                    <property name="position">1</property>
                                   </packing>
                                 </child>
                                 <child>
-                                  <object class="GtkLabel" id="inapp_label">
+                                  <object class="GtkBox">
                                     <property name="visible">True</property>
                                     <property name="can_focus">False</property>
+                                    <property name="orientation">vertical</property>
+                                    <property name="margin_bottom">30</property>
+                                    <property name="valign">center</property>
+                                    <property name="expand">True</property>
+                                    <child>
+                                      <object class="GtkImage">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                        <property name="margin_bottom">16</property>
+                                        <property 
name="resource">/org/gnome/Fractal/icons/chat-icon.svg</property>
+                                      </object>
+                                    </child>
+                                    <child>
+                                      <object class="GtkLabel">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                        <property name="label" translatable="yes">No room selected</property>
+                                        <property name="margin_bottom">3</property>
+                                        <property name="justify">center</property>
+                                        <attributes>
+                                          <attribute name="weight" value="bold"/>
+                                        </attributes>
+                                        <style>
+                                          <class name="noroom-title"/>
+                                        </style>
+                                      </object>
+                                    </child>
+                                    <child>
+                                      <object class="GtkLabel">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                        <property name="label" translatable="yes">Join a room to start 
chatting</property>
+                                        <property name="justify">center</property>
+                                        <style>
+                                          <class name="noroom-description"/>
+                                        </style>
+                                      </object>
+                                    </child>
                                   </object>
                                   <packing>
-                                    <property name="expand">False</property>
-                                    <property name="fill">True</property>
-                                    <property name="pack_type">end</property>
-                                    <property name="position">0</property>
+                                    <property name="name">noroom</property>
+                                    <property name="title" translatable="yes">No room</property>
+                                    <property name="position">2</property>
                                   </packing>
                                 </child>
                               </object>
+                              <packing>
+                                <property name="index">-1</property>
+                              </packing>
                             </child>
-                            <child type="label_item">
-                              <placeholder/>
-                            </child>
-                            <style>
-                              <class name="app-notification"/>
-                            </style>
-                          </object>
-                        </child>
-                      </object>
-                      <packing>
-                        <property name="pass_through">True</property>
-                        <property name="index">-1</property>
-                      </packing>
-                    </child>
-                  </object>
-                </child>
-              </object>
-              <packing>
-                <property name="name">content</property>
-              </packing>
-            </child>
-          </object>
-          <packing>
-            <property name="name">chat</property>
-            <property name="title" translatable="yes">Chat</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkBox" id="directory_state">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="orientation">vertical</property>
-            <child>
-              <object class="GtkScrolledWindow" id="directory_scroll">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="hscrollbar_policy">never</property>
-                <child>
-                  <object class="GtkViewport">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <child>
-                      <object class="GtkStack" id="directory_stack">
-                        <property name="can_focus">False</property>
-                        <child>
-                          <object class="GtkBox" id="directory_spinner">
-                            <property name="visible">True</property>
-                            <property name="can_focus">False</property>
-                            <child>
-                              <object class="GtkSpinner">
+                            <child type="overlay">
+                              <object class="GtkOverlay" id="inapp_notify">
                                 <property name="visible">True</property>
                                 <property name="can_focus">False</property>
-                                <property name="active">True</property>
+                                <property name="valign">start</property>
+                                <property name="halign">center</property>
+                                <child>
+                                  <object class="GtkRevealer" id="inapp_revealer">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">False</property>
+                                    <property name="halign">center</property>
+                                    <child>
+                                      <object class="GtkFrame" id="inapp_frame">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                        <property name="label_xalign">0</property>
+                                        <property name="shadow_type">none</property>
+                                        <child>
+                                          <object class="GtkBox" id="inapp_box">
+                                            <property name="visible">True</property>
+                                            <property name="can_focus">False</property>
+                                            <property name="spacing">10</property>
+                                            <child>
+                                              <object class="GtkSpinner" id="inapp_spinner">
+                                                <property name="visible">True</property>
+                                                <property name="active">True</property>
+                                                <property name="can_focus">False</property>
+                                              </object>
+                                              <packing>
+                                                <property name="expand">False</property>
+                                                <property name="fill">True</property>
+                                                <property name="position">0</property>
+                                              </packing>
+                                            </child>
+                                            <child>
+                                              <object class="GtkLabel" id="inapp_label">
+                                                <property name="visible">True</property>
+                                                <property name="can_focus">False</property>
+                                              </object>
+                                              <packing>
+                                                <property name="expand">False</property>
+                                                <property name="fill">True</property>
+                                                <property name="pack_type">end</property>
+                                                <property name="position">0</property>
+                                              </packing>
+                                            </child>
+                                          </object>
+                                        </child>
+                                        <child type="label_item">
+                                          <placeholder/>
+                                        </child>
+                                        <style>
+                                          <class name="app-notification"/>
+                                        </style>
+                                      </object>
+                                    </child>
+                                  </object>
+                                  <packing>
+                                    <property name="pass_through">True</property>
+                                    <property name="index">-1</property>
+                                  </packing>
+                                </child>
                               </object>
-                              <packing>
-                                <property name="expand">True</property>
-                                <property name="fill">True</property>
-                                <property name="position">0</property>
-                              </packing>
                             </child>
                           </object>
                         </child>
                       </object>
-                    </child>
-                  </object>
-                </child>
-              </object>
-              <packing>
-                <property name="expand">True</property>
-                <property name="fill">True</property>
-                <property name="position">1</property>
-              </packing>
-            </child>
-          </object>
-          <packing>
-            <property name="name">directory</property>
-            <property name="title" translatable="yes">Directory</property>
-            <property name="position">1</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkBox" id="loading_state">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="orientation">vertical</property>
-            <child>
-              <object class="GtkSpinner">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="active">True</property>
-              </object>
-              <packing>
-                <property name="expand">True</property>
-                <property name="fill">True</property>
-                <property name="position">0</property>
-              </packing>
-            </child>
-          </object>
-          <packing>
-            <property name="name">loading</property>
-            <property name="title" translatable="yes">Loading</property>
-            <property name="position">2</property>
-          </packing>
-        </child>
-      </object>
-    </child>
-    <child type="titlebar">
-      <object class="HdyTitleBar">
-        <property name="visible">True</property>
-        <child>
-          <object class="GtkStack" id="headerbar_stack">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="hexpand">True</property>
-            <property name="hhomogeneous">False</property>
-            <child>
-              <object class="HdyLeaflet" id="header_leaflet"> <!--message view-->
-                <property name="visible">True</property>
-                <property name="can-swipe-back">True</property>
-                <property name="transition-type">over</property>
-                <property name="hhomogeneous-folded">True</property>
-                <child>
-                  <object class="GtkHeaderBar" id="left-header"> <!--left titlebar-->
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="halign">fill</property>
-                    <property name="show-close-button">True</property>
-                    <child>
-                      <object class="GtkMenuButton" id="main_menu_button">
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="receives_default">True</property>
-                        <property name="popover">user_popover</property>
-                        <child>
-                          <object class="GtkImage">
-                            <property name="visible">True</property>
-                            <property name="can_focus">False</property>
-                            <property name="icon_name">open-menu-symbolic</property>
-                          </object>
-                        </child>
-                        <accessibility>
-
-                        </accessibility>
-                        <child internal-child="accessible">
-                          <object class="AtkObject" id="a11y-main_menu_button">
-                            <property name="AtkObject::accessible_name" translatable="yes">User</property>
-                          </object>
-                        </child>
-                      </object>
-                      <packing>
-                        <property name="pack_type">end</property>
-                        <property name="position">0</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <object class="GtkMenuButton" id="add_room_menu">
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="receives_default">True</property>
-                        <property name="popover">add_room_popover</property>
-                        <child>
-                          <object class="GtkImage">
-                            <property name="visible">True</property>
-                            <property name="can_focus">False</property>
-                            <property name="icon_name">list-add-symbolic</property>
-                          </object>
-                        </child>
-                        <accessibility>
-
-                        </accessibility>
-                        <child internal-child="accessible">
-                          <object class="AtkObject" id="a11y-add_room_menu">
-                            <property name="AtkObject::accessible_name" translatable="yes">Add</property>
-                          </object>
-                        </child>
-                      </object>
                       <packing>
-                        <property name="pack_type">end</property>
-                        <property name="position">1</property>
+                        <property name="name">content</property>
                       </packing>
                     </child>
-                    <child>
-                      <object class="GtkToggleButton" id="room_search_button">
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="receives_default">True</property>
-                        <child>
-                          <object class="GtkImage">
-                            <property name="visible">True</property>
-                            <property name="can_focus">False</property>
-                            <property name="icon_name">system-search-symbolic</property>
-                          </object>
-                        </child>
-                        <accelerator key="k" signal="activate" modifiers="GDK_CONTROL_MASK"/>
-                        <style>
-                          <class name="room-search-button"/>
-                        </style>
-                        <accessibility>
-
-                        </accessibility>
-                        <child internal-child="accessible">
-                          <object class="AtkObject" id="a11y-room_search_button">
-                            <property name="AtkObject::accessible_name" translatable="yes">Room 
search</property>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
                   </object>
                   <packing>
-                    <property name="name">sidebar</property>
+                    <property name="name">chat</property>
                   </packing>
                 </child>
                 <child>
-                  <object class="GtkSeparator" id="header_separator">
-                    <property name="visible">True</property>
+                  <object class="GtkStack" id="subview_stack">
                     <property name="can_focus">False</property>
-                    <property name="orientation">vertical</property>
-                    <style>
-                      <class name="sidebar"/>
-                    </style>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkHeaderBar" id="room_header_bar"> <!--right titlebar-->
-                    <property name="show-close-button">True</property>
-                    <property name="has-subtitle">False</property>
-                    <property name="hexpand">true</property>
-                    <property name="width-request">360</property>
+                    <property name="hhomogeneous">False</property>
                     <child>
-                      <object class="GtkRevealer">
-                        <property name="reveal-child" bind-source="header_leaflet" bind-property="folded" 
bind-flags="sync-create"/>
-                        <property name="transition-duration" bind-source="header_leaflet" 
bind-property="mode-transition-duration" bind-flags="bidirectional|sync-create"/>
-                        <property name="transition-type">crossfade</property>
+                      <object class="GtkBox">
                         <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="orientation">vertical</property>
                         <child>
-                          <object class="GtkButton" id="leaflet_back_button">
-                            <property name="action_name">app.back</property>
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="receives_default">True</property>
+                          <object class="HdyHeaderBar">
+                            <property name="can_focus">False</property>
+                            <property name="show_close_button">True</property>
+                            <property name="width_request">360</property>
+                            <property name="centering_policy">HDY_CENTERING_POLICY_STRICT</property>
                             <child>
-                              <object class="GtkImage">
+                              <object class="GtkButton" id="back_button">
                                 <property name="visible">True</property>
-                                <property name="can_focus">False</property>
-                                <property name="icon_name">go-previous-symbolic</property>
-                              </object>
-                            </child>
-                            <accessibility>
-
-                            </accessibility>
-                            <child internal-child="accessible">
-                              <object class="AtkObject" id="a11y-leaflet_back_button">
-                                <property name="AtkObject::accessible_name" 
translatable="yes">Back</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">True</property>
+                                <property name="action_name">app.deck-back</property>
+                                <child>
+                                  <object class="GtkImage">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">False</property>
+                                    <property name="icon_name">go-previous-symbolic</property>
+                                  </object>
+                                </child>
+                                <child internal-child="accessible">
+                                  <object class="AtkObject" id="back_button-atkobject">
+                                    <property name="AtkObject::accessible-name" 
translatable="yes">Back</property>
+                                  </object>
+                                </child>
                               </object>
                             </child>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                    <child type="title">
-                      <object class="GtkScrolledWindow">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="hexpand">True</property>
-                        <property name="hscrollbar_policy">never</property>
-                        <property name="vscrollbar_policy">never</property>
-                        <property name="propagate_natural_height">True</property>
-                        <property name="propagate_natural_width">False</property>
-                        <child>
-                          <object class="GtkBox" id="room_info">
-                            <property name="visible">True</property>
-                            <property name="can_focus">False</property>
-                            <property name="orientation">vertical</property>
-                            <child>
-                              <object class="GtkLabel" id="room_name">
+                            <child type="title">
+                              <object class="HdyClamp">
                                 <property name="can_focus">False</property>
-                                <!-- Translators: This string is replaced not user-visible -->
-                                <property name="label">Room name</property>
-                                <property name="ellipsize">end</property>
-                                <style>
-                                  <class name="title"/>
-                                </style>
+                                <property name="visible">True</property>
+                                <property name="maximum_size">288</property>
+                                <property name="tightening_threshold">288</property>
+                                <child>
+                                  <object class="GtkSearchEntry" id="directory_search_entry">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="hexpand">True</property>
+                                    <property name="primary_icon_name">edit-find-symbolic</property>
+                                    <property name="primary_icon_activatable">False</property>
+                                    <property name="primary_icon_sensitive">False</property>
+                                  </object>
+                                </child>
                               </object>
-                              <packing>
-                                <property name="expand">False</property>
-                                <property name="fill">True</property>
-                                <property name="position">0</property>
-                              </packing>
                             </child>
                             <child>
-                              <object class="GtkLabel" id="room_topic">
-                                <property name="can_focus">False</property>
-                                <!-- Translators: This string is replaced not user-visible -->
-                                <property name="label">Room topic</property>
-                                <property name="ellipsize">end</property>
-                                <style>
-                                  <class name="subtitle"/>
-                                </style>
+                              <object class="GtkMenuButton" id="server_chooser_menu">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">True</property>
+                                <property name="popover">server_chooser_popover</property>
+                                <child>
+                                  <object class="HdySqueezer">
+                                    <child>
+                                      <object class="GtkBox">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                        <property name="spacing">6</property>
+                                        <child>
+                                          <object class="GtkLabel" id="directory_choice_label">
+                                            <property name="visible">True</property>
+                                            <property name="can_focus">False</property>
+                                            <property name="label" translatable="yes">Default Matrix 
Server</property>
+                                          </object>
+                                          <packing>
+                                            <property name="expand">False</property>
+                                            <property name="fill">True</property>
+                                            <property name="position">0</property>
+                                          </packing>
+                                        </child>
+                                        <child>
+                                          <object class="GtkImage">
+                                            <property name="visible">True</property>
+                                            <property name="can_focus">False</property>
+                                            <property name="icon_name">pan-down-symbolic</property>
+                                          </object>
+                                          <packing>
+                                            <property name="expand">False</property>
+                                            <property name="fill">True</property>
+                                            <property name="position">1</property>
+                                          </packing>
+                                        </child>
+                                      </object>
+                                    </child>
+                                    <child>
+                                      <object class="GtkBox">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                        <property name="spacing">6</property>
+                                        <child>
+                                          <object class="GtkImage">
+                                            <property name="visible">True</property>
+                                            <property name="can_focus">False</property>
+                                            <property name="icon_name">network-server-symbolic</property>
+                                          </object>
+                                        </child>
+                                        <child>
+                                          <object class="GtkImage">
+                                            <property name="visible">True</property>
+                                            <property name="can_focus">False</property>
+                                            <property name="icon_name">pan-down-symbolic</property>
+                                          </object>
+                                        </child>
+                                      </object>
+                                    </child>
+                                  </object>
+                                </child>
                               </object>
                               <packing>
-                                <property name="expand">False</property>
-                                <property name="fill">True</property>
-                                <property name="position">1</property>
+                                <property name="pack_type">end</property>
                               </packing>
                             </child>
                           </object>
                         </child>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkMenuButton" id="room_menu_button">
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="receives_default">True</property>
-                        <property name="popover">room_popover</property>
                         <child>
-                          <object class="GtkImage">
+                          <object class="GtkBox" id="directory_state">
                             <property name="visible">True</property>
                             <property name="can_focus">False</property>
-                            <property name="icon_name">view-more-symbolic</property>
-                          </object>
-                        </child>
-                        <accessibility>
-                        </accessibility>
-                        <child internal-child="accessible">
-                          <object class="AtkObject" id="a11y-room_menu_button">
-                            <property name="AtkObject::accessible_name" translatable="yes">Room 
Menu</property>
+                            <property name="orientation">vertical</property>
+                            <child>
+                              <object class="GtkScrolledWindow" id="directory_scroll">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="hscrollbar_policy">never</property>
+                                <property name="expand">True</property>
+                                <property name="halign">fill</property>
+                                <property name="valign">fill</property>
+                                <child>
+                                  <object class="GtkViewport">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">False</property>
+                                    <child>
+                                      <object class="GtkStack" id="directory_stack">
+                                        <property name="can_focus">False</property>
+                                        <child>
+                                          <object class="GtkBox" id="directory_spinner">
+                                            <property name="visible">True</property>
+                                            <property name="can_focus">False</property>
+                                            <child>
+                                              <object class="GtkSpinner">
+                                                <property name="visible">True</property>
+                                                <property name="can_focus">False</property>
+                                                <property name="active">True</property>
+                                              </object>
+                                              <packing>
+                                                <property name="expand">True</property>
+                                                <property name="fill">True</property>
+                                                <property name="position">0</property>
+                                              </packing>
+                                            </child>
+                                          </object>
+                                        </child>
+                                      </object>
+                                    </child>
+                                  </object>
+                                </child>
+                              </object>
+                            </child>
                           </object>
                         </child>
                       </object>
                       <packing>
-                        <property name="pack_type">end</property>
-                        <property name="position">1</property>
+                        <property name="name">directory</property>
                       </packing>
                     </child>
                   </object>
                   <packing>
-                    <property name="name">content</property>
+                    <property name="name">subview</property>
                   </packing>
                 </child>
               </object>
               <packing>
-                <property name="name">normal</property>
-                <property name="title">normal</property>
-                <property name="position">1</property>
+                <property name="name">main_view</property>
               </packing>
             </child>
             <child>
@@ -614,147 +703,34 @@
                 <property name="can_focus">False</property>
                 <property name="orientation">vertical</property>
                 <child>
-                  <object class="GtkHeaderBar">
+                  <object class="HdyHeaderBar">
                     <property name="can_focus">False</property>
                     <property name="show-close-button">True</property>
                     <property name="title">Fractal</property>
                   </object>
                 </child>
-              </object>
-              <packing>
-                <property name="name">loading</property>
-                <property name="title">loading</property>
-                <property name="position">1</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkBox">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="orientation">vertical</property>
                 <child>
-                  <object class="HdyHeaderBar">
+                  <object class="GtkBox" id="loading_state">
+                    <property name="visible">True</property>
                     <property name="can_focus">False</property>
-                    <property name="show_close_button">True</property>
-                    <property name="width_request">360</property>
-                    <property name="centering_policy">HDY_CENTERING_POLICY_STRICT</property>
+                    <property name="orientation">vertical</property>
                     <child>
-                      <object class="GtkButton" id="back_button">
+                      <object class="GtkSpinner">
                         <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="receives_default">True</property>
-                        <property name="action_name">app.back</property>
-                        <child>
-                          <object class="GtkImage">
-                            <property name="visible">True</property>
-                            <property name="can_focus">False</property>
-                            <property name="icon_name">go-previous-symbolic</property>
-                          </object>
-                        </child>
-                        <child internal-child="accessible">
-                          <object class="AtkObject" id="back_button-atkobject">
-                            <property name="AtkObject::accessible-name" translatable="yes">Back</property>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                    <child type="title">
-                      <object class="HdyClamp">
                         <property name="can_focus">False</property>
-                        <property name="visible">True</property>
-                        <property name="maximum_size">288</property>
-                        <property name="tightening_threshold">288</property>
-                        <child>
-                          <object class="GtkSearchEntry" id="directory_search_entry">
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="hexpand">True</property>
-                            <property name="primary_icon_name">edit-find-symbolic</property>
-                            <property name="primary_icon_activatable">False</property>
-                            <property name="primary_icon_sensitive">False</property>
-                          </object>
-                        </child>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkMenuButton" id="server_chooser_menu">
-                        <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="receives_default">True</property>
-                        <property name="popover">server_chooser_popover</property>
-                        <child>
-                          <object class="HdySqueezer">
-                            <child>
-                              <object class="GtkBox">
-                                <property name="visible">True</property>
-                                <property name="can_focus">False</property>
-                                <property name="spacing">6</property>
-                                <child>
-                                  <object class="GtkLabel" id="directory_choice_label">
-                                    <property name="visible">True</property>
-                                    <property name="can_focus">False</property>
-                                    <property name="label" translatable="yes">Default Matrix 
Server</property>
-                                  </object>
-                                  <packing>
-                                    <property name="expand">False</property>
-                                    <property name="fill">True</property>
-                                    <property name="position">0</property>
-                                  </packing>
-                                </child>
-                                <child>
-                                  <object class="GtkImage">
-                                    <property name="visible">True</property>
-                                    <property name="can_focus">False</property>
-                                    <property name="icon_name">pan-down-symbolic</property>
-                                  </object>
-                                  <packing>
-                                    <property name="expand">False</property>
-                                    <property name="fill">True</property>
-                                    <property name="position">1</property>
-                                  </packing>
-                                </child>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkBox">
-                                <property name="visible">True</property>
-                                <property name="can_focus">False</property>
-                                <property name="spacing">6</property>
-                                <child>
-                                  <object class="GtkImage">
-                                    <property name="visible">True</property>
-                                    <property name="can_focus">False</property>
-                                    <property name="icon_name">network-server-symbolic</property>
-                                  </object>
-                                </child>
-                                <child>
-                                  <object class="GtkImage">
-                                    <property name="visible">True</property>
-                                    <property name="can_focus">False</property>
-                                    <property name="icon_name">pan-down-symbolic</property>
-                                  </object>
-                                </child>
-                              </object>
-                            </child>
-                          </object>
-                        </child>
+                        <property name="active">True</property>
+                        <property name="expand">True</property>
+                        <property name="valign">fill</property>
+                        <property name="halign">fill</property>
                       </object>
                       <packing>
-                        <property name="pack_type">end</property>
                       </packing>
                     </child>
                   </object>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="fill">True</property>
-                    <property name="position">0</property>
-                  </packing>
                 </child>
               </object>
               <packing>
-                <property name="name">back</property>
-                <property name="title" translatable="yes">Back</property>
-                <property name="position">2</property>
+                <property name="name">loading</property>
               </packing>
             </child>
           </object>
@@ -765,38 +741,10 @@
       <class name="main-window"/>
     </style>
   </object>
-<!-- Synchronize left header and sidebar -->
-  <object class="GtkSizeGroup">
-    <property name="mode">horizontal</property>
-    <widgets>
-      <widget name="left-header"/>
-      <widget name="sidebar-box"/>
-    </widgets>
-  </object>
-  <object class="GtkSizeGroup">
-    <property name="mode">horizontal</property>
-    <widgets>
-      <widget name="inapp"/>
-      <widget name="room_header_bar"/>
-    </widgets>
-  </object>
-  <object class="GtkSizeGroup">
-    <property name="mode">horizontal</property>
-    <widgets>
-      <widget name="header_separator"/>
-      <widget name="content_separator"/>
-    </widgets>
-  </object>
   <object class="HdyHeaderGroup">
     <headerbars>
       <headerbar name="left-header"/>
       <headerbar name="room_header_bar"/>
     </headerbars>
   </object>
-  <object class="HdySwipeGroup" id="swipe_group">
-    <swipeables>
-      <swipeable name="chat_state_leaflet"/>
-      <swipeable name="header_leaflet"/>
-    </swipeables>
-  </object>
 </interface>
diff --git a/fractal-gtk/res/ui/media_viewer.ui b/fractal-gtk/res/ui/media_viewer.ui
index 3ecf81a5..4ebdaf56 100644
--- a/fractal-gtk/res/ui/media_viewer.ui
+++ b/fractal-gtk/res/ui/media_viewer.ui
@@ -5,7 +5,84 @@
   <object class="GtkBox" id="media_viewer_box">
     <property name="visible">True</property>
     <property name="can_focus">False</property>
-    <property name="homogeneous">True</property>
+    <property name="orientation">vertical</property>
+    <child>
+      <object class="GtkBox" id="media_viewer_headerbar_box">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <child>
+          <object class="GtkHeaderBar" id="media_viewer_headerbar">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <!-- Translators: This string is replaced not user-visible -->
+            <property name="title">Media viewer</property>
+            <property name="show_close_button">True</property>
+            <child>
+              <object class="GtkButton" id="media_viewer_back_button">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="action_name">app.deck-back</property>
+                <child>
+                  <object class="GtkImage">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="icon_name">go-previous-symbolic</property>
+                  </object>
+                </child>
+                <child internal-child="accessible">
+                  <object class="AtkObject" id="media_viewer_back_button-atkobject">
+                    <property name="AtkObject::accessible-name" translatable="yes">Back</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkMenuButton" id="media_viewer_menu_button">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <child>
+                  <object class="GtkImage">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="icon_name">view-more-symbolic</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="pack_type">end</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="full_screen_button">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="tooltip_text" translatable="yes">Toggle fullscreen</property>
+                <child>
+                  <object class="GtkImage" id="full_screen_button_icon">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="icon_name">view-fullscreen-symbolic</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="pack_type">end</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </object>
+    </child>
     <child>
       <object class="GtkOverlay">
         <property name="visible">True</property>
@@ -208,79 +285,4 @@
       </packing>
     </child>
   </object>
-  <object class="GtkBox" id="media_viewer_headerbar_box">
-    <property name="visible">True</property>
-    <property name="can_focus">False</property>
-    <child>
-      <object class="GtkHeaderBar" id="media_viewer_headerbar">
-        <property name="visible">True</property>
-        <property name="can_focus">False</property>
-        <!-- Translators: This string is replaced not user-visible -->
-        <property name="title">Media viewer</property>
-        <property name="show_close_button">True</property>
-        <child>
-          <object class="GtkButton" id="media_viewer_back_button">
-            <property name="visible">True</property>
-            <property name="can_focus">True</property>
-            <property name="receives_default">True</property>
-            <property name="action_name">app.back</property>
-            <child>
-              <object class="GtkImage">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="icon_name">go-previous-symbolic</property>
-              </object>
-            </child>
-            <child internal-child="accessible">
-              <object class="AtkObject" id="media_viewer_back_button-atkobject">
-                <property name="AtkObject::accessible-name" translatable="yes">Back</property>
-              </object>
-            </child>
-          </object>
-        </child>
-        <child>
-          <object class="GtkMenuButton" id="media_viewer_menu_button">
-            <property name="visible">True</property>
-            <property name="can_focus">True</property>
-            <property name="receives_default">True</property>
-            <child>
-              <object class="GtkImage">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="icon_name">view-more-symbolic</property>
-              </object>
-            </child>
-          </object>
-          <packing>
-            <property name="pack_type">end</property>
-            <property name="position">1</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkButton" id="full_screen_button">
-            <property name="visible">True</property>
-            <property name="can_focus">True</property>
-            <property name="receives_default">True</property>
-            <property name="tooltip_text" translatable="yes">Toggle fullscreen</property>
-            <child>
-              <object class="GtkImage" id="full_screen_button_icon">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="icon_name">view-fullscreen-symbolic</property>
-              </object>
-            </child>
-          </object>
-          <packing>
-            <property name="pack_type">end</property>
-            <property name="position">2</property>
-          </packing>
-        </child>
-      </object>
-      <packing>
-        <property name="expand">True</property>
-        <property name="fill">True</property>
-        <property name="position">0</property>
-      </packing>
-    </child>
-  </object>
 </interface>
diff --git a/fractal-gtk/res/ui/room_settings.ui b/fractal-gtk/res/ui/room_settings.ui
index 5d91404b..1832290b 100644
--- a/fractal-gtk/res/ui/room_settings.ui
+++ b/fractal-gtk/res/ui/room_settings.ui
@@ -5,9 +5,39 @@
   <object class="GtkBox" id="room_settings_box">
     <property name="visible">True</property>
     <property name="can_focus">False</property>
+    <property name="orientation">vertical</property>
+    <child>
+      <object class="HdyHeaderBar">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="show_close_button">True</property>
+        <property name="title" translatable="yes">Details</property>
+        <child>
+          <object class="GtkButton" id="room_settings_back_button">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="action_name">app.deck-back</property>
+            <child>
+              <object class="GtkImage">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="icon_name">go-previous-symbolic</property>
+              </object>
+            </child>
+            <child internal-child="accessible">
+              <object class="AtkObject" id="room_settings_back_button-atkobject">
+                <property name="AtkObject::accessible-name" translatable="yes">Back</property>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
     <child>
       <object class="GtkScrolledWindow" id="room_settings_scroll">
         <property name="visible">True</property>
+        <property name="expand">True</property>
         <property name="can_focus">True</property>
         <property name="hscrollbar_policy">never</property>
         <child>
@@ -999,44 +1029,6 @@
           </object>
         </child>
       </object>
-      <packing>
-        <property name="expand">False</property>
-        <property name="fill">True</property>
-        <property name="position">0</property>
-      </packing>
-    </child>
-  </object>
-  <object class="GtkBox" id="room_settings_headerbar">
-    <property name="visible">True</property>
-    <property name="can_focus">False</property>
-    <child>
-      <object class="GtkHeaderBar">
-        <property name="visible">True</property>
-        <property name="can_focus">False</property>
-        <property name="show_close_button">True</property>
-        <property name="expand">True</property>
-        <property name="title" translatable="yes">Details</property>
-        <child>
-          <object class="GtkButton" id="room_settings_back_button">
-            <property name="visible">True</property>
-            <property name="can_focus">True</property>
-            <property name="receives_default">True</property>
-            <property name="action_name">app.back</property>
-            <child>
-              <object class="GtkImage">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="icon_name">go-previous-symbolic</property>
-              </object>
-            </child>
-            <child internal-child="accessible">
-              <object class="AtkObject" id="room_settings_back_button-atkobject">
-                <property name="AtkObject::accessible-name" translatable="yes">Back</property>
-              </object>
-            </child>
-          </object>
-        </child>
-      </object>
     </child>
   </object>
 </interface>
diff --git a/fractal-gtk/src/actions/global.rs b/fractal-gtk/src/actions/global.rs
index 3d2b5d47..a18afe0a 100644
--- a/fractal-gtk/src/actions/global.rs
+++ b/fractal-gtk/src/actions/global.rs
@@ -13,8 +13,9 @@ use fractal_api::identifiers::{EventId, RoomId};
 use gio::prelude::*;
 use gio::SimpleAction;
 use gtk::prelude::*;
+use libhandy::prelude::*;
 
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Copy, Clone, PartialEq)]
 pub enum AppState {
     Login,
     Loading,
@@ -81,6 +82,7 @@ pub fn new(app: &gtk::Application, op: &Arc<Mutex<AppOp>>) {
     let main_menu = SimpleAction::new("main_menu", None);
 
     let open_room = SimpleAction::new("open-room", glib::VariantTy::new("s").ok());
+    let deck_back = SimpleAction::new("deck-back", None);
     let back = SimpleAction::new("back", None);
     let media_viewer = SimpleAction::new("open-media-viewer", glib::VariantTy::new("s").ok());
     let account = SimpleAction::new("open-account-settings", None);
@@ -115,6 +117,7 @@ pub fn new(app: &gtk::Application, op: &Arc<Mutex<AppOp>>) {
     app.add_action(&shortcuts);
     app.add_action(&about);
     app.add_action(&open_room);
+    app.add_action(&deck_back);
     app.add_action(&back);
     app.add_action(&directory);
     app.add_action(&room_settings);
@@ -264,6 +267,13 @@ pub fn new(app: &gtk::Application, op: &Arc<Mutex<AppOp>>) {
         back.borrow_mut().push(AppState::MediaViewer);
     }));
 
+    deck_back.connect_activate(clone!(@strong op => move |_, _| {
+        let deck = op.lock().unwrap().deck.clone();
+        if deck.get_can_swipe_back() {
+            deck.navigate(libhandy::NavigationDirection::Back);
+        }
+    }));
+
     let mv = op.lock().unwrap().media_viewer.clone();
     let back_weak = Rc::downgrade(&back_history);
     back.connect_activate(clone!(@weak mv => move |_, _| {
@@ -389,7 +399,7 @@ pub fn activate_action(action_group_name: &str, action_name: &str) {
             .unwrap()
             .ui
             .builder
-            .get_object::<gtk::Window>("main_window")
+            .get_object::<libhandy::ApplicationWindow>("main_window")
             .expect("Can't find main_window in ui file.");
         if let Some(action_group) = main_window.get_action_group(action_group_name) {
             action_group.activate_action(action_name, None);
diff --git a/fractal-gtk/src/actions/login.rs b/fractal-gtk/src/actions/login.rs
index 8bac5a90..e4c007d0 100644
--- a/fractal-gtk/src/actions/login.rs
+++ b/fractal-gtk/src/actions/login.rs
@@ -1,3 +1,4 @@
+use libhandy::prelude::*;
 use log::{debug, warn};
 use std::cell::RefCell;
 use std::rc::Rc;
@@ -41,8 +42,7 @@ impl ToString for LoginState {
 }
 
 pub fn new(
-    stack: &gtk::Stack,
-    headers: &gtk::Stack,
+    deck: &libhandy::Deck,
     server_entry: &gtk::Entry,
     err_label: &gtk::Label,
 ) -> SimpleActionGroup {
@@ -60,8 +60,8 @@ pub fn new(
     actions.add_action(&back);
     actions.add_action(&login);
 
-    create_account.connect_activate(clone!(@weak stack => move |_, _| {
-        let toplevel = stack
+    create_account.connect_activate(clone!(@weak deck => move |_, _| {
+        let toplevel = deck
             .get_toplevel()
             .expect("Could not grab toplevel widget")
             .downcast::<gtk::Window>()
@@ -73,40 +73,25 @@ pub fn new(
         }
     }));
 
-    let back_history: Rc<RefCell<Vec<LoginState>>> = Rc::new(RefCell::new(vec![]));
-
-    server_chooser.connect_activate(
-        clone!(@weak stack, @weak back_history as back => move |_, _| {
-            let state = LoginState::ServerChooser;
-            stack.set_visible_child_name(&state.to_string());
-            back.borrow_mut().push(state);
-        }),
-    );
+    server_chooser.connect_activate(clone!(@weak deck => move |_, _| {
+        deck.navigate(libhandy::NavigationDirection::Forward);
+    }));
 
     credentials.connect_activate(clone!(
-    @weak stack,
-    @weak back_history as back,
+    @weak err_label,
     @weak server_entry,
-    @weak err_label
-    => move |_, _| {
+    @weak deck => move |_, _| {
         if server_entry.get_text().is_empty() {
             err_label.show();
         } else {
             err_label.hide();
-            let state = LoginState::Credentials;
-            stack.set_visible_child_name(&state.to_string());
-            back.borrow_mut().push(state);
+            deck.navigate(libhandy::NavigationDirection::Forward);
         }
     }));
 
-    back.connect_activate(clone!(@weak stack => move |_, _| {
-        back_history.borrow_mut().pop();
-        if let Some(state) = back_history.borrow().last() {
-            debug!("Go back to state {}", state.to_string());
-            stack.set_visible_child_name(&state.to_string());
-        } else {
-            debug!("There is no state to go back to. Go back to state greeter");
-            stack.set_visible_child_name(&LoginState::Greeter.to_string());
+    back.connect_activate(clone!(@weak deck => move |_, _| {
+        if let Some(_) = deck.get_adjacent_child(libhandy::NavigationDirection::Back) {
+            deck.navigate(libhandy::NavigationDirection::Back);
         }
     }));
 
@@ -123,8 +108,7 @@ pub fn new(
         })
     });
 
-    stack.insert_action_group("login", Some(&actions));
-    headers.insert_action_group("login", Some(&actions));
+    deck.insert_action_group("login", Some(&actions));
 
     actions
 }
diff --git a/fractal-gtk/src/actions/message.rs b/fractal-gtk/src/actions/message.rs
index d08d2f2d..7ccb496c 100644
--- a/fractal-gtk/src/actions/message.rs
+++ b/fractal-gtk/src/actions/message.rs
@@ -63,13 +63,13 @@ pub fn new(
     actions.add_action(&show_source);
     actions.add_action(&load_more_messages);
 
-    let parent: gtk::Window = ui
+    let parent: libhandy::ApplicationWindow = ui
         .builder
         .get_object("main_window")
         .expect("Can't find main_window in ui file.");
     show_source.connect_activate(clone!(@weak parent => move |_, data| {
         let viewer = SourceDialog::new();
-        viewer.set_parent_window(&parent);
+        viewer.set_parent_window(&parent.upcast_ref());
         if let Some(m) = get_message(data) {
             let error = i18n("This message has no source.");
             let source = m.source.as_ref().unwrap_or(&error);
@@ -80,7 +80,7 @@ pub fn new(
 
     let window = ui
         .builder
-        .get_object::<gtk::ApplicationWindow>("main_window")
+        .get_object::<libhandy::ApplicationWindow>("main_window")
         .expect("Couldn't find main_window in ui file.");
     reply.connect_activate(clone!(
     @weak back_history,
@@ -162,7 +162,7 @@ pub fn new(
                                 Continue(true)
                             },
                             Ok(Ok(fname)) => {
-                                if let Some(path) = save(&window, &name, &[]) {
+                                if let Some(path) = save(&window.upcast_ref(), &name, &[]) {
                                     // TODO use glib to copy file
                                     if fs::copy(fname, path).is_err() {
                                         ErrorDialog::new(false, &i18n("Couldn’t save file"));
diff --git a/fractal-gtk/src/app/connect/autocomplete.rs b/fractal-gtk/src/app/connect/autocomplete.rs
index d7c0b9e1..a67c4b6e 100644
--- a/fractal-gtk/src/app/connect/autocomplete.rs
+++ b/fractal-gtk/src/app/connect/autocomplete.rs
@@ -16,14 +16,20 @@ impl App {
             .builder
             .get_object::<gtk::ListBox>("autocomplete_listbox")
             .expect("Can't find autocomplete_listbox in ui file.");
-        let window: gtk::Window = self
+        let window: libhandy::ApplicationWindow = self
             .ui
             .builder
             .get_object("main_window")
             .expect("Can't find main_window in ui file.");
 
         let op = self.op.clone();
-        widgets::Autocomplete::new(op, window, self.ui.sventry.view.clone(), popover, listbox)
-            .connect();
+        widgets::Autocomplete::new(
+            op,
+            window.upcast(),
+            self.ui.sventry.view.clone(),
+            popover,
+            listbox,
+        )
+        .connect();
     }
 }
diff --git a/fractal-gtk/src/app/connect/headerbar.rs b/fractal-gtk/src/app/connect/headerbar.rs
index 961d5615..233f8661 100644
--- a/fractal-gtk/src/app/connect/headerbar.rs
+++ b/fractal-gtk/src/app/connect/headerbar.rs
@@ -6,13 +6,13 @@ use crate::app::App;
 impl App {
     pub fn connect_headerbars(&self) {
         if let Some(set) = gtk::Settings::get_default() {
-            let left_header: gtk::HeaderBar = self
+            let left_header: libhandy::HeaderBar = self
                 .ui
                 .builder
                 .get_object("left-header")
                 .expect("Can't find left-header in ui file.");
 
-            let right_header: gtk::HeaderBar = self
+            let right_header: libhandy::HeaderBar = self
                 .ui
                 .builder
                 .get_object("room_header_bar")
@@ -25,8 +25,8 @@ impl App {
                 // Check if the close button is to the right; If not,
                 // change the headerbar controls
                 if decor_split.len() > 1 && !decor_split[1].contains("close") {
-                    right_header.set_show_close_button(false);
-                    left_header.set_show_close_button(true);
+                    libhandy::HeaderBarExt::set_show_close_button(&right_header, false);
+                    libhandy::HeaderBarExt::set_show_close_button(&left_header, true);
                 }
             };
 
@@ -41,11 +41,11 @@ impl App {
                         // Change the headerbar controls depending on position
                         // of close
                         if decor_split.len() > 1 && decor_split[1].contains("close") {
-                            left_header.set_show_close_button(false);
-                            right_header.set_show_close_button(true);
+                            libhandy::HeaderBarExt::set_show_close_button(&left_header, false);
+                            libhandy::HeaderBarExt::set_show_close_button(&right_header, true);
                         } else {
-                            right_header.set_show_close_button(false);
-                            left_header.set_show_close_button(true);
+                            libhandy::HeaderBarExt::set_show_close_button(&right_header, false);
+                            libhandy::HeaderBarExt::set_show_close_button(&left_header, true);
                         }
                     };
                 }));
diff --git a/fractal-gtk/src/app/connect/mod.rs b/fractal-gtk/src/app/connect/mod.rs
index a180abed..2a2514bb 100644
--- a/fractal-gtk/src/app/connect/mod.rs
+++ b/fractal-gtk/src/app/connect/mod.rs
@@ -11,6 +11,7 @@ mod markdown;
 mod new_room;
 mod roomlist_search;
 mod send;
+mod swipeable_widgets;
 
 use crate::app::App;
 
@@ -34,5 +35,6 @@ impl App {
         self.connect_direct_chat();
 
         self.connect_roomlist_search();
+        self.connect_swipeable_widgets();
     }
 }
diff --git a/fractal-gtk/src/app/connect/swipeable_widgets.rs 
b/fractal-gtk/src/app/connect/swipeable_widgets.rs
new file mode 100644
index 00000000..004e493c
--- /dev/null
+++ b/fractal-gtk/src/app/connect/swipeable_widgets.rs
@@ -0,0 +1,57 @@
+use gio::prelude::*;
+use gtk::prelude::*;
+use libhandy::prelude::*;
+
+use crate::app::App;
+
+impl App {
+    // Set up HdyDeck and HdyLeaflet so that swipes trigger the
+    // same behaviour as the back button.
+    pub fn connect_swipeable_widgets(&self) {
+        let deck: libhandy::Deck = self
+            .ui
+            .builder
+            .get_object("main_deck")
+            .expect("Can't find main_deck in UI file");
+        let leaflet: libhandy::Leaflet = self
+            .ui
+            .builder
+            .get_object("chat_page")
+            .expect("Can't find chat_page in UI file");
+
+        let app = gio::Application::get_default()
+            .expect("Could not get default application")
+            .downcast::<gtk::Application>()
+            .unwrap();
+
+        deck.connect_property_transition_running_notify(clone!(@weak app => move |deck| {
+            let child: Option<String> = deck.get_visible_child_name().map(|g| g.to_string());
+            if !deck.get_transition_running() && child == Some("chat".to_string()) {
+                app.activate_action("back", None);
+            }
+        }));
+
+        deck.connect_property_visible_child_notify(clone!(@weak app => move |deck| {
+            let child: Option<String> = deck.get_visible_child_name().map(|g| g.to_string());
+            if !deck.get_transition_running() && child == Some("chat".to_string()) {
+                app.activate_action("back", None);
+            }
+        }));
+
+        leaflet.connect_property_child_transition_running_notify(
+            clone!(@weak app => move |leaflet| {
+                let child: Option<String> = leaflet.get_visible_child_name().map(|g| g.to_string());
+                if !leaflet.get_child_transition_running() && child == Some("sidebar".to_string()) {
+                    app.activate_action("back", None);
+                }
+            }),
+        );
+
+        leaflet.connect_property_visible_child_notify(clone!(@weak app => move |leaflet| {
+            let child: Option<String> = deck.get_visible_child_name().map(|g| g.to_string());
+            if !leaflet.get_child_transition_running() && child == Some("sidebar".to_string()) {
+                app.activate_action("back", None);
+            }
+        }));
+    }
+}
diff --git a/fractal-gtk/src/app/mod.rs b/fractal-gtk/src/app/mod.rs
index 68b7a983..54034940 100644
--- a/fractal-gtk/src/app/mod.rs
+++ b/fractal-gtk/src/app/mod.rs
@@ -41,7 +41,7 @@ macro_rules! APPOP {
 // Our application struct for containing all the state we have to carry around.
 // TODO: subclass gtk::Application once possible
 pub struct App {
-    main_window: gtk::ApplicationWindow,
+    main_window: libhandy::ApplicationWindow,
     /* Add widget directly here in place of uibuilder::UI*/
     ui: uibuilder::UI,
 
@@ -71,7 +71,7 @@ impl App {
         );
 
         let ui = uibuilder::UI::new();
-        let window: gtk::ApplicationWindow = ui
+        let window: libhandy::ApplicationWindow = ui
             .builder
             .get_object("main_window")
             .expect("Couldn't find main_window in ui file.");
@@ -98,8 +98,8 @@ impl App {
 
         let leaflet = ui
             .builder
-            .get_object::<libhandy::Leaflet>("chat_state_leaflet")
-            .expect("Can't find chat_state_leaflet in ui file.");
+            .get_object::<libhandy::Leaflet>("chat_page")
+            .expect("Can't find chat_page in ui file.");
         let container = ui
             .builder
             .get_object::<gtk::Box>("history_container")
@@ -124,33 +124,28 @@ impl App {
             }
         }));
 
-        let stack = ui
+        let view_stack = ui
             .builder
-            .get_object::<gtk::Stack>("main_content_stack")
-            .expect("Can't find main_content_stack in ui file.");
-        let stack_header = ui
-            .builder
-            .get_object::<gtk::Stack>("headerbar_stack")
-            .expect("Can't find headerbar_stack in ui file.");
+            .get_object::<gtk::Stack>("subview_stack")
+            .expect("Can't find subview_stack in ui file.");
 
-        /* Add account settings view to the main stack */
+        /* Add account settings view to the view stack */
         let child = ui
             .builder
             .get_object::<gtk::Box>("account_settings_box")
             .expect("Can't find account_settings_box in ui file.");
-        let child_header = ui
-            .builder
-            .get_object::<gtk::Box>("account_settings_headerbar")
-            .expect("Can't find account_settings_headerbar in ui file.");
-        stack.add_named(&child, "account-settings");
-        stack_header.add_named(&child_header, "account-settings");
+        view_stack.add_named(&child, "account-settings");
 
         let op = Arc::new(Mutex::new(AppOp::new(ui.clone())));
 
+        let main_stack = ui
+            .builder
+            .get_object::<gtk::Stack>("main_content_stack")
+            .expect("Can't find main_content_stack in ui file.");
+
         // Add login view to the main stack
         let login = widgets::LoginWidget::new(&op);
-        stack.add_named(&login.container, "login");
-        stack_header.add_named(&login.headers, "login");
+        main_stack.add_named(&login.container, "login");
 
         gtk_app.set_accels_for_action("login.back", &["Escape"]);
 
@@ -158,6 +153,12 @@ impl App {
             OP = Some(Arc::downgrade(&op));
         }
 
+        // TODO: Urgent: hook up with swipe signals
+        let deck = ui
+            .builder
+            .get_object::<libhandy::Deck>("main_deck")
+            .expect("Can't find main_deck in ui file");
+
         actions::Global::new(gtk_app, &op);
 
         let app = AppRef::new(Self {
@@ -186,7 +187,8 @@ impl App {
 
         app.main_window.connect_delete_event(move |window, _| {
             let settings: gio::Settings = gio::Settings::new("org.gnome.Fractal");
-            let window_state = WindowState::from_window(window);
+            let w = window.upcast_ref();
+            let window_state = WindowState::from_window(w);
             if let Err(err) = window_state.save_in_gsettings(&settings) {
                 error!("Can't save the window settings: {:?}", err);
             }
diff --git a/fractal-gtk/src/appop/about.rs b/fractal-gtk/src/appop/about.rs
index 8820c36e..87d1dd42 100644
--- a/fractal-gtk/src/appop/about.rs
+++ b/fractal-gtk/src/appop/about.rs
@@ -7,7 +7,7 @@ use crate::config;
 
 impl AppOp {
     pub fn about_dialog(&self) {
-        let window: gtk::ApplicationWindow = self
+        let window: libhandy::ApplicationWindow = self
             .ui
             .builder
             .get_object("main_window")
diff --git a/fractal-gtk/src/appop/account.rs b/fractal-gtk/src/appop/account.rs
index 23f5be35..cf092469 100644
--- a/fractal-gtk/src/appop/account.rs
+++ b/fractal-gtk/src/appop/account.rs
@@ -74,7 +74,7 @@ impl AppOp {
         let parent = self
             .ui
             .builder
-            .get_object::<gtk::Window>("main_window")
+            .get_object::<libhandy::ApplicationWindow>("main_window")
             .expect("Can't find main_window in ui file.");
 
         let entry = gtk::Entry::new();
@@ -141,7 +141,7 @@ impl AppOp {
         let parent = self
             .ui
             .builder
-            .get_object::<gtk::Window>("main_window")
+            .get_object::<libhandy::ApplicationWindow>("main_window")
             .expect("Can't find main_window in ui file.");
 
         let msg = i18n("In order to add this email address, go to your inbox and follow the link you 
received. Once you’ve done that, click Continue.");
@@ -200,7 +200,7 @@ impl AppOp {
         let parent = self
             .ui
             .builder
-            .get_object::<gtk::Window>("main_window")
+            .get_object::<libhandy::ApplicationWindow>("main_window")
             .expect("Can't find main_window in ui file.");
 
         let msg = error;
@@ -773,7 +773,7 @@ impl AppOp {
         let parent = self
             .ui
             .builder
-            .get_object::<gtk::Window>("main_window")
+            .get_object::<libhandy::ApplicationWindow>("main_window")
             .expect("Can't find main_window in ui file.");
 
         let msg = i18n("Are you sure you want to delete your account?");
diff --git a/fractal-gtk/src/appop/attach.rs b/fractal-gtk/src/appop/attach.rs
index 44315b98..82147a76 100644
--- a/fractal-gtk/src/appop/attach.rs
+++ b/fractal-gtk/src/appop/attach.rs
@@ -27,7 +27,7 @@ impl AppOp {
         };
 
         if let Some(pb) = scaled {
-            let window: gtk::ApplicationWindow = self
+            let window: libhandy::ApplicationWindow = self
                 .ui
                 .builder
                 .get_object("main_window")
diff --git a/fractal-gtk/src/appop/media_viewer.rs b/fractal-gtk/src/appop/media_viewer.rs
index 8a76fee1..95a74b1a 100644
--- a/fractal-gtk/src/appop/media_viewer.rs
+++ b/fractal-gtk/src/appop/media_viewer.rs
@@ -1,4 +1,5 @@
 use gtk::prelude::*;
+use libhandy::prelude::*;
 
 use log::error;
 
@@ -18,19 +19,15 @@ impl AppOp {
         let stack = self
             .ui
             .builder
-            .get_object::<gtk::Stack>("main_content_stack")
-            .expect("Can't find main_content_stack in ui file.");
-        let stack_header = self
-            .ui
-            .builder
-            .get_object::<gtk::Stack>("headerbar_stack")
-            .expect("Can't find headerbar_stack in ui file.");
+            .get_object::<gtk::Stack>("subview_stack")
+            .expect("Can't find subview_stack in ui file.");
 
         let main_window = self
             .ui
             .builder
-            .get_object::<gtk::Window>("main_window")
-            .expect("Can't find main_window in ui file.");
+            .get_object::<libhandy::ApplicationWindow>("main_window")
+            .expect("Can't find main_window in ui file.")
+            .upcast::<gtk::Window>();
 
         {
             let room_id = self.active_room.as_ref()?;
@@ -66,12 +63,8 @@ impl AppOp {
             if let Some(widget) = stack.get_child_by_name("media-viewer") {
                 stack.remove(&widget);
             }
-            if let Some(widget) = stack_header.get_child_by_name("media-viewer") {
-                stack_header.remove(&widget);
-            }
 
             stack.add_named(&body, "media-viewer");
-            stack_header.add_named(&header, "media-viewer");
         }
 
         self.set_state(AppState::MediaViewer);
diff --git a/fractal-gtk/src/appop/message.rs b/fractal-gtk/src/appop/message.rs
index d81e4af4..42d7f03f 100644
--- a/fractal-gtk/src/appop/message.rs
+++ b/fractal-gtk/src/appop/message.rs
@@ -143,7 +143,7 @@ impl AppOp {
 
     pub fn mark_last_message_as_read(&mut self, Force(force): Force) -> Option<()> {
         let login_data = self.login_data.clone()?;
-        let window: gtk::Window = self
+        let window: libhandy::ApplicationWindow = self
             .ui
             .builder
             .get_object("main_window")
diff --git a/fractal-gtk/src/appop/mod.rs b/fractal-gtk/src/appop/mod.rs
index 8b7a4f1b..e5ce84df 100644
--- a/fractal-gtk/src/appop/mod.rs
+++ b/fractal-gtk/src/appop/mod.rs
@@ -116,6 +116,7 @@ pub struct AppOp {
 
     pub directory: Vec<Room>,
     pub leaflet: libhandy::Leaflet,
+    pub deck: libhandy::Deck,
 
     pub thread_pool: ThreadPool,
     pub user_info_cache: UserInfoCache,
@@ -127,8 +128,12 @@ impl AppOp {
     pub fn new(ui: uibuilder::UI) -> AppOp {
         let leaflet = ui
             .builder
-            .get_object::<libhandy::Leaflet>("header_leaflet")
-            .expect("Couldn't find header_leaflet in ui file");
+            .get_object::<libhandy::Leaflet>("chat_page")
+            .expect("Couldn't find chat_page in ui file");
+        let deck = ui
+            .builder
+            .get_object::<libhandy::Deck>("main_deck")
+            .expect("Couldn't find main_deck in ui file");
 
         AppOp {
             ui,
@@ -159,6 +164,7 @@ impl AppOp {
 
             directory: vec![],
             leaflet,
+            deck,
 
             thread_pool: ThreadPool::new(20),
             user_info_cache: Arc::new(Mutex::new(
@@ -192,7 +198,7 @@ impl AppOp {
         }
     }
 
-    fn get_window(&self) -> gtk::Window {
+    fn get_window(&self) -> libhandy::ApplicationWindow {
         self.ui
             .builder
             .get_object("main_window")
diff --git a/fractal-gtk/src/appop/room.rs b/fractal-gtk/src/appop/room.rs
index 8e6bbd82..a6587ddb 100644
--- a/fractal-gtk/src/appop/room.rs
+++ b/fractal-gtk/src/appop/room.rs
@@ -363,7 +363,7 @@ impl AppOp {
     }
 
     pub fn kicked_room(&self, room_name: String, reason: String, kicker: String) {
-        let parent: gtk::Window = self
+        let parent: libhandy::ApplicationWindow = self
             .ui
             .builder
             .get_object("main_window")
diff --git a/fractal-gtk/src/appop/room_settings.rs b/fractal-gtk/src/appop/room_settings.rs
index 841e109d..d204ad85 100644
--- a/fractal-gtk/src/appop/room_settings.rs
+++ b/fractal-gtk/src/appop/room_settings.rs
@@ -8,21 +8,17 @@ use crate::widgets;
 impl AppOp {
     pub fn create_room_settings(&mut self) -> Option<()> {
         let login_data = self.login_data.clone()?;
-        let window = self
+        let window: gtk::Window = self
             .ui
             .builder
-            .get_object::<gtk::Window>("main_window")
-            .expect("Can't find main_window in ui file.");
+            .get_object::<libhandy::ApplicationWindow>("main_window")
+            .expect("Can't find main_window in ui file.")
+            .upcast::<gtk::Window>();
         let stack = self
             .ui
             .builder
-            .get_object::<gtk::Stack>("main_content_stack")
-            .expect("Can't find main_content_stack in ui file.");
-        let stack_header = self
-            .ui
-            .builder
-            .get_object::<gtk::Stack>("headerbar_stack")
-            .expect("Can't find headerbar_stack in ui file.");
+            .get_object::<gtk::Stack>("subview_stack")
+            .expect("Can't find subview_stack in ui file.");
 
         {
             let room = self.rooms.get(&self.active_room.clone()?)?;
@@ -33,18 +29,14 @@ impl AppOp {
                 login_data.server_url,
                 login_data.access_token,
             );
-            let (body, header) = panel.create()?;
+            let page = panel.create()?;
 
             /* remove old panel */
             if let Some(widget) = stack.get_child_by_name("room-settings") {
                 stack.remove(&widget);
             }
-            if let Some(widget) = stack_header.get_child_by_name("room-settings") {
-                stack_header.remove(&widget);
-            }
 
-            stack.add_named(&body, "room-settings");
-            stack_header.add_named(&header, "room-settings");
+            stack.add_named(&page, "room-settings");
 
             self.room_settings = Some(panel);
         }
diff --git a/fractal-gtk/src/appop/state.rs b/fractal-gtk/src/appop/state.rs
index 6a062a9b..17c150e1 100644
--- a/fractal-gtk/src/appop/state.rs
+++ b/fractal-gtk/src/appop/state.rs
@@ -1,5 +1,5 @@
 use gtk::prelude::*;
-use libhandy::LeafletExt;
+use libhandy::prelude::*;
 
 use super::RoomSearchPagination;
 use crate::actions::AppState;
@@ -8,6 +8,71 @@ use crate::appop::AppOp;
 impl AppOp {
     pub fn set_state(&mut self, state: AppState) {
         self.state = state;
+
+        match self.state {
+            AppState::Login => self.set_stack_state("login"),
+            AppState::NoRoom | AppState::Room => {
+                self.set_stack_state("main_view");
+                self.set_chat_state(state);
+            }
+            AppState::Directory => self.set_deck_state(Some("directory"), state),
+            AppState::Loading => self.set_stack_state("loading"),
+            AppState::AccountSettings => self.set_deck_state(Some("account-settings"), state),
+            AppState::RoomSettings => self.set_deck_state(Some("room-settings"), state),
+            AppState::MediaViewer => self.set_deck_state(Some("media-viewer"), state),
+        };
+
+        //set focus for room directory
+        if let AppState::Directory = self.state {
+            self.ui
+                .builder
+                .get_object::<gtk::Widget>("directory_search_entry")
+                .expect("Can't find widget to set focus in ui file.")
+                .grab_focus();
+            self.directory_pagination = RoomSearchPagination::Initial;
+            self.search_rooms();
+        }
+    }
+
+    fn set_deck_state(&self, view: Option<&str>, state: AppState) {
+        let deck = self
+            .ui
+            .builder
+            .get_object::<libhandy::Deck>("main_deck")
+            .expect("Could not find main_deck in ui file");
+        let stack = self
+            .ui
+            .builder
+            .get_object::<gtk::Stack>("subview_stack")
+            .expect("Could not find subview_stack in ui file");
+        let direction = match state {
+            AppState::Room | AppState::NoRoom => libhandy::NavigationDirection::Back,
+            _ => libhandy::NavigationDirection::Forward,
+        };
+
+        if let Some(v) = view {
+            stack.set_visible_child_name(v);
+        }
+
+        if let Some(_) = deck.get_adjacent_child(direction) {
+            deck.navigate(direction);
+        }
+    }
+
+    fn set_stack_state(&self, state: &str) {
+        self.ui
+            .builder
+            .get_object::<gtk::Stack>("main_content_stack")
+            .expect("Can't find main_content_stack in ui file.")
+            .set_visible_child_name(state);
+    }
+
+    fn set_chat_state(&mut self, state: AppState) {
+        let deck = self
+            .ui
+            .builder
+            .get_object::<libhandy::Deck>("main_deck")
+            .expect("Could not find main_deck in ui file");
         let stack = self
             .ui
             .builder
@@ -16,74 +81,27 @@ impl AppOp {
         let headerbar = self
             .ui
             .builder
-            .get_object::<gtk::HeaderBar>("room_header_bar")
+            .get_object::<libhandy::HeaderBar>("room_header_bar")
             .expect("Can't find room_header_bar in ui file.");
 
-        let widget_name = match self.state {
-            AppState::Login => "login",
+        match state {
             AppState::NoRoom => {
                 self.set_state_no_room(&headerbar);
-                self.leaflet.set_visible_child_name("sidebar");
+                self.leaflet.navigate(libhandy::NavigationDirection::Back);
                 stack.set_visible_child_name("noroom");
-                "chat"
             }
             AppState::Room => {
                 self.set_state_room(&headerbar);
-                self.leaflet.set_visible_child_name("content");
+                self.leaflet
+                    .navigate(libhandy::NavigationDirection::Forward);
                 stack.set_visible_child_name("room_view");
-                "chat"
             }
-            AppState::Directory => "directory",
-            AppState::Loading => "loading",
-            AppState::AccountSettings => "account-settings",
-            AppState::RoomSettings => "room-settings",
-            AppState::MediaViewer => "media-viewer",
-        };
-
-        self.ui
-            .builder
-            .get_object::<gtk::Stack>("main_content_stack")
-            .expect("Can't find main_content_stack in ui file.")
-            .set_visible_child_name(widget_name);
-
-        //setting headerbar
-        let bar_name = match self.state {
-            AppState::Login => "login",
-            AppState::Directory => "back",
-            AppState::Loading => "loading",
-            AppState::AccountSettings => "account-settings",
-            AppState::RoomSettings => "room-settings",
-            AppState::MediaViewer => "media-viewer",
-            _ => "normal",
-        };
-
-        self.ui
-            .builder
-            .get_object::<gtk::Stack>("headerbar_stack")
-            .expect("Can't find headerbar_stack in ui file.")
-            .set_visible_child_name(bar_name);
-
-        //set focus for views
-        let widget_focus = match self.state {
-            AppState::Directory => "directory_search_entry",
-            _ => "",
-        };
-
-        if !widget_focus.is_empty() {
-            self.ui
-                .builder
-                .get_object::<gtk::Widget>(widget_focus)
-                .expect("Can't find widget to set focus in ui file.")
-                .grab_focus();
-        }
-
-        if let AppState::Directory = self.state {
-            self.directory_pagination = RoomSearchPagination::Initial;
-            self.search_rooms();
+            _ => (),
         }
+        libhandy::DeckExt::set_visible_child_name(&deck, "chat");
     }
 
-    fn set_state_room(&self, headerbar: &gtk::HeaderBar) {
+    fn set_state_room(&self, headerbar: &libhandy::HeaderBar) {
         for ch in headerbar.get_children().iter() {
             ch.show();
         }
@@ -105,7 +123,7 @@ impl AppOp {
     }
 
     // WORKAROUND this is needed because NoRoom isn't a real app state
-    fn set_state_no_room(&mut self, headerbar: &gtk::HeaderBar) {
+    fn set_state_no_room(&mut self, headerbar: &libhandy::HeaderBar) {
         for ch in headerbar.get_children().iter() {
             ch.hide();
 
diff --git a/fractal-gtk/src/main.rs b/fractal-gtk/src/main.rs
index db5fe211..5a10c159 100644
--- a/fractal-gtk/src/main.rs
+++ b/fractal-gtk/src/main.rs
@@ -1,3 +1,6 @@
+#[macro_use]
+extern crate glib;
+
 mod backend;
 mod client;
 mod config;
@@ -58,6 +61,9 @@ fn main() -> Result<(), Box<dyn Error>> {
 
     static_resources::init().expect("GResource initialization failed.");
 
+    gtk::init().expect("Failed to initialize GTK");
+    libhandy::init();
+
     // Initialize GStreamer. This checks, among other things, what plugins are available
     gst::init()?;
 
diff --git a/fractal-gtk/src/meson.build b/fractal-gtk/src/meson.build
index cf955208..83c2c7c6 100644
--- a/fractal-gtk/src/meson.build
+++ b/fractal-gtk/src/meson.build
@@ -66,6 +66,7 @@ app_sources = files(
   'app/connect/new_room.rs',
   'app/connect/roomlist_search.rs',
   'app/connect/send.rs',
+  'app/connect/swipeable_widgets.rs',
   'app/mod.rs',
   'app/windowstate.rs',
   'appop/about.rs',
diff --git a/fractal-gtk/src/widgets/kicked_dialog.rs b/fractal-gtk/src/widgets/kicked_dialog.rs
index e89b02cb..5408a3d2 100644
--- a/fractal-gtk/src/widgets/kicked_dialog.rs
+++ b/fractal-gtk/src/widgets/kicked_dialog.rs
@@ -56,7 +56,7 @@ impl KickedDialog {
     }
 
     /* This sets the transient_for parent */
-    pub fn set_parent_window(&self, parent: &gtk::Window) {
+    pub fn set_parent_window<P: glib::IsA<gtk::Window>>(&self, parent: &P) {
         self.widgets
             .msg_kicked_window
             .set_transient_for(Some(parent));
diff --git a/fractal-gtk/src/widgets/login.rs b/fractal-gtk/src/widgets/login.rs
index 514ffe36..b139e67c 100644
--- a/fractal-gtk/src/widgets/login.rs
+++ b/fractal-gtk/src/widgets/login.rs
@@ -2,6 +2,7 @@ use fractal_api::url::Url;
 use gio::prelude::*;
 use glib::clone;
 use gtk::prelude::*;
+use libhandy::prelude::*;
 use log::info;
 
 use crate::actions;
@@ -18,8 +19,7 @@ use std::sync::{Arc, Mutex};
 
 #[derive(Debug, Clone)]
 pub struct LoginWidget {
-    pub container: gtk::Stack,
-    pub headers: gtk::Stack,
+    pub container: libhandy::Deck,
     pub server_entry: gtk::Entry,
     pub username_entry: gtk::Entry,
     pub password_entry: gtk::Entry,
@@ -157,8 +157,7 @@ impl Default for LoginWidget {
     fn default() -> Self {
         let builder = gtk::Builder::from_resource("/org/gnome/Fractal/ui/login_flow.ui");
 
-        let container: gtk::Stack = builder.get_object("login_flow_stack").unwrap();
-        let headers: gtk::Stack = builder.get_object("login_flow_headers").unwrap();
+        let container: libhandy::Deck = builder.get_object("login_flow_deck").unwrap();
         let server_entry = builder.get_object("server_chooser_entry").unwrap();
         let username_entry = builder.get_object("username_entry").unwrap();
         let password_entry = builder.get_object("password_entry").unwrap();
@@ -166,14 +165,12 @@ impl Default for LoginWidget {
         let server_err_label = builder.get_object("server_err_label").unwrap();
         let credentials_err_label = builder.get_object("credentials_err_label").unwrap();
 
-        let actions = actions::Login::new(&container, &headers, &server_entry, &server_err_label);
+        let actions = actions::Login::new(&container, &server_entry, &server_err_label);
 
         container.show_all();
-        headers.show_all();
 
         LoginWidget {
             container,
-            headers,
             server_entry,
             username_entry,
             password_entry,
diff --git a/fractal-gtk/src/widgets/room_settings.rs b/fractal-gtk/src/widgets/room_settings.rs
index d0d8ee2e..2594f21b 100644
--- a/fractal-gtk/src/widgets/room_settings.rs
+++ b/fractal-gtk/src/widgets/room_settings.rs
@@ -68,15 +68,11 @@ impl RoomSettings {
 
     /* creates a empty list with members.len() rows, the content will be loaded when the row is
      * drawn */
-    pub fn create(&mut self) -> Option<(gtk::Box, gtk::Box)> {
-        let body = self
+    pub fn create(&mut self) -> Option<gtk::Box> {
+        let page = self
             .builder
             .get_object::<gtk::Box>("room_settings_box")
             .expect("Can't find room_settings_box in ui file.");
-        let header = self
-            .builder
-            .get_object::<gtk::Box>("room_settings_headerbar")
-            .expect("Can't find room_settings_headerbar in ui file.");
         let stack = self
             .builder
             .get_object::<gtk::Stack>("room_settings_stack")
@@ -94,7 +90,7 @@ impl RoomSettings {
         self.init_room_settings();
         self.connect();
 
-        Some((body, header))
+        Some(page)
     }
 
     #[allow(dead_code)]


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