[fractal/fractal-next] Add base layout



commit 9e8fa8aab548c69979237df3b698f6be6765920b
Author: Julian Sparber <julian sparber net>
Date:   Sat Feb 13 00:16:16 2021 +0100

    Add base layout

 Cargo.toml                                         |   2 +-
 data/resources.gresource.xml                       |  14 +++
 .../icons/scalable/actions/send-symbolic.svg       |  52 ++++++++++
 data/resources/style.css                           |  14 ++-
 data/resources/ui/content.ui                       |  88 +++++++++++++++++
 data/resources/ui/login.ui                         |  18 ++++
 data/resources/ui/session.ui                       |  19 ++++
 data/resources/ui/sidebar.ui                       |  56 +++++++++++
 data/resources/ui/window.ui                        |  44 +++------
 src/application.rs                                 |  21 ++--
 src/main.rs                                        |   4 +-
 src/meson.build                                    |   7 +-
 src/widgets/content.rs                             | 110 +++++++++++++++++++++
 src/widgets/login.rs                               |  58 +++++++++++
 src/widgets/mod.rs                                 |  11 +++
 src/widgets/session.rs                             |  63 ++++++++++++
 src/widgets/sidebar.rs                             | 110 +++++++++++++++++++++
 src/{ => widgets}/window.rs                        |  63 ++++++------
 18 files changed, 679 insertions(+), 75 deletions(-)
---
diff --git a/Cargo.toml b/Cargo.toml
index 3e207658..f3fb9d4a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,6 +17,6 @@ git = "https://github.com/gtk-rs/gtk4-rs";
 # We need to use the same version as libadwaita does
 #rev = "abea0c9980bc083494eceb30dfab5eeb99a73118"
 
-[dependencies.libadwaita]
+[dependencies.adw]
 package = "libadwaita"
 git = "https://gitlab.gnome.org/bilelmoussaoui/libadwaita-rs.git";
diff --git a/data/resources.gresource.xml b/data/resources.gresource.xml
new file mode 100644
index 00000000..025bbe47
--- /dev/null
+++ b/data/resources.gresource.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/org/gnome/FractalNext/">
+    <file compressed="true" preprocess="xml-stripblanks" 
alias="shortcuts.ui">resources/ui/shortcuts.ui</file>
+    <file compressed="true" preprocess="xml-stripblanks" alias="content.ui">resources/ui/content.ui</file>
+    <file compressed="true" preprocess="xml-stripblanks" alias="login.ui">resources/ui/login.ui</file>
+    <file compressed="true" preprocess="xml-stripblanks" alias="session.ui">resources/ui/session.ui</file>
+    <file compressed="true" preprocess="xml-stripblanks" alias="sidebar.ui">resources/ui/sidebar.ui</file>
+    <file compressed="true" preprocess="xml-stripblanks" alias="window.ui">resources/ui/window.ui</file>
+
+    <file compressed="true" alias="style.css">resources/style.css</file>
+    <file preprocess="xml-stripblanks" 
alias="icons/scalable/actions/send-symbolic.svg">resources/icons/scalable/actions/send-symbolic.svg</file>
+  </gresource>
+</gresources>
diff --git a/data/resources/icons/scalable/actions/send-symbolic.svg 
b/data/resources/icons/scalable/actions/send-symbolic.svg
new file mode 100644
index 00000000..01f60ca8
--- /dev/null
+++ b/data/resources/icons/scalable/actions/send-symbolic.svg
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:osb="http://www.openswatchbook.org/uri/2009/osb";
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   height="16"
+   id="svg7384"
+   version="1.1"
+   width="16">
+  <metadata
+     id="metadata90">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+        <dc:title>Gnome Symbolic Icon Theme</dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <title
+     id="title9167">Gnome Symbolic Icon Theme</title>
+  <defs
+     id="defs7386">
+    <linearGradient
+       id="linearGradient7212"
+       osb:paint="solid">
+      <stop
+         id="stop7214"
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;" />
+    </linearGradient>
+  </defs>
+  <g
+     id="layer9"
+     style="display:inline"
+     transform="translate(-323.0002,125)">
+    <g
+       style="enable-background:new"
+       id="g876"
+       transform="translate(311.00017,-893)">
+      <path
+         id="path20012"
+         d="m 12.999904,769 14.000126,7.00002 -14.000126,6.99999 v -6 L 22.000471,776.00002 12.999904,775 Z"
+         
style="fill:#241f31;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" 
/>
+    </g>
+  </g>
+</svg>
diff --git a/data/resources/style.css b/data/resources/style.css
index 3c4bd471..c3d88b6e 100644
--- a/data/resources/style.css
+++ b/data/resources/style.css
@@ -1,4 +1,16 @@
-.title-header{
+.title-header {
   font-size: 36px;
   font-weight: bold;
 }
+
+.content {
+  background-color: @theme_base_color;
+}
+
+.content:backdrop {
+  background-color: @theme_unfocused_base_color;
+}
+
+.send-message-area {
+  margin: 6px;
+}
diff --git a/data/resources/ui/content.ui b/data/resources/ui/content.ui
new file mode 100644
index 00000000..411aa691
--- /dev/null
+++ b/data/resources/ui/content.ui
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="FrctlContent" parent="AdwBin">
+    <property name="vexpand">True</property>
+    <property name="hexpand">True</property>
+    <child>
+      <object class="GtkBox">
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="AdwHeaderBar" id="headerbar">
+            <property name="show-start-title-buttons" bind-source="FrctlContent" bind-property="compact" 
bind-flags="sync-create" />
+            <child type="end">
+              <object class="GtkMenuButton" id="room_menu">
+                <property name="icon-name">view-more-symbolic</property>
+              </object>
+            </child>
+            <child type="end">
+              <object class="GtkToggleButton" id="search_content_button">
+                <property name="icon-name">system-search-symbolic</property>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkSearchBar" id="room_search">
+            <property name="search-mode-enabled" bind-source="search_content_button" bind-property="active" 
/>
+            <property name="child">
+              <object class="AdwClamp">
+                <property name="hexpand">True</property>
+                <child>
+                  <object class="GtkSearchEntry"/>
+                </child>
+              </object>
+            </property>
+          </object>
+        </child>
+        <child>
+          <object class="AdwClamp">
+            <property name="vexpand">True</property>
+            <property name="hexpand">True</property>
+            <style>
+              <class name="content"/>
+            </style>
+            <child>
+              <object class="GtkListView" id="room_history">
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkSeparator" />
+        </child>
+        <child>
+          <object class="AdwClamp">
+            <child>
+              <object class="GtkBox">
+                <property name="spacing">6</property>
+                <style>
+                  <class name="send-message-area"/>
+                </style>
+                <child>
+                  <object class="GtkButton">
+                    <property name="icon-name">mail-attachment-symbolic</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkButton">
+                    <property name="icon-name">format-justify-left-symbolic</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkEntry" id="message_entry">
+                    <property name="hexpand">True</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkButton">
+                    <property name="icon-name">send-symbolic</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/data/resources/ui/login.ui b/data/resources/ui/login.ui
new file mode 100644
index 00000000..94347e78
--- /dev/null
+++ b/data/resources/ui/login.ui
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="FrctlLogin" parent="AdwBin">
+    <child>
+      <object class="GtkBox">
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkHeaderBar" id="headerbar" />
+        </child>
+        <child>
+          <object class="AdwStatusPage">
+            <!-- TODO implement login -->
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/data/resources/ui/session.ui b/data/resources/ui/session.ui
new file mode 100644
index 00000000..0f0c5968
--- /dev/null
+++ b/data/resources/ui/session.ui
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="FrctlSession" parent="AdwBin">
+    <child>
+      <object class="AdwLeaflet" id="session">
+        <child>
+          <object class="FrctlSidebar" id="sidebar">
+            <property name="compact" bind-source="session" bind-property="folded" bind-flags="sync-create" />
+          </object>
+        </child>
+        <child>
+          <object class="FrctlContent" id="content">
+            <property name="compact" bind-source="session" bind-property="folded" bind-flags="sync-create" />
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/data/resources/ui/sidebar.ui b/data/resources/ui/sidebar.ui
new file mode 100644
index 00000000..4e315197
--- /dev/null
+++ b/data/resources/ui/sidebar.ui
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <menu id="primary_menu">
+    <section>
+      <item>
+        <attribute name="label" translatable="yes">_Preferences</attribute>
+        <attribute name="action">app.preferences</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">_Keyboard Shortcuts</attribute>
+        <attribute name="action">win.show-help-overlay</attribute>
+      </item>
+      <item>
+        <attribute name="label" translatable="yes">_About Fractal</attribute>
+        <attribute name="action">app.about</attribute>
+      </item>
+    </section>
+  </menu>
+  <template class="FrctlSidebar" parent="AdwBin">
+    <style>
+      <class name="sidebar"/>
+    </style>
+    <child>
+      <object class="GtkBox">
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="AdwHeaderBar" id="headerbar">
+            <property name="show-end-title-buttons" bind-source="FrctlSidebar" bind-property="compact" 
bind-flags="sync-create" />
+            <child type="start">
+              <object class="GtkToggleButton" id="search_button">
+                <property name="icon-name">system-search-symbolic</property>
+              </object>
+            </child>
+            <child type="end">
+              <object class="GtkMenuButton" id="appmenu_button">
+                <property name="icon-name">open-menu-symbolic</property>
+                <property name="menu-model">primary_menu</property>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkSearchBar" id="room_search">
+            <property name="search-mode-enabled" bind-source="search_button" bind-property="active" />
+            <property name="child">
+              <object class="GtkSearchEntry"/>
+            </property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkListView" id="listview" />
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/data/resources/ui/window.ui b/data/resources/ui/window.ui
index c89a8a35..95c2b884 100644
--- a/data/resources/ui/window.ui
+++ b/data/resources/ui/window.ui
@@ -1,39 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <menu id="primary_menu">
-    <section>
-      <item>
-        <attribute name="label" translatable="yes">_Preferences</attribute>
-        <attribute name="action">app.preferences</attribute>
-      </item>
-      <item>
-        <attribute name="label" translatable="yes">_Keyboard Shortcuts</attribute>
-        <attribute name="action">win.show-help-overlay</attribute>
-      </item>
-      <item>
-        <attribute name="label" translatable="yes">_About GTK Rust Template</attribute>
-        <attribute name="action">app.about</attribute>
-      </item>
-    </section>
-  </menu>
-  <template class="ExampleApplicationWindow" parent="GtkApplicationWindow">
+  <template class="FrctlWindow" parent="AdwApplicationWindow">
     <property name="default-width">600</property>
     <property name="default-height">400</property>
-    <child type="titlebar">
-      <object class="GtkHeaderBar" id="headerbar">
-        <child type="end">
-          <object class="GtkMenuButton" id="appmenu_button">
-            <property name="icon-name">open-menu-symbolic</property>
-            <property name="menu-model">primary_menu</property>
-          </object>
-        </child>
-      </object>
-    </child>
     <child>
-      <object class="GtkLabel" id="label">
-        <property name="label" translatable="yes">Hello world!</property>
-        <style>
-          <class name="title-header"/>
-        </style>
+      <object class="GtkStack" id="main_stack">
+        <property name="visible-child">session</property>
+        <property name="transition-type">crossfade</property>
+        <child>
+          <object class="FrctlLogin" id="login" />
+        </child>
+        <!-- We currently only support one session, add other sessions to the main_Stack -->
+        <child>
+          <object class="FrctlSession" id="session" />
+        </child>
       </object>
     </child>
   </template>
diff --git a/src/application.rs b/src/application.rs
index 7f8a50ed..996a14bb 100644
--- a/src/application.rs
+++ b/src/application.rs
@@ -1,5 +1,5 @@
 use crate::config;
-use crate::window::ExampleApplicationWindow;
+use crate::widgets::FrctlWindow;
 use gio::ApplicationFlags;
 use glib::clone;
 use glib::WeakRef;
@@ -17,7 +17,7 @@ mod imp {
 
     #[derive(Debug)]
     pub struct ExampleApplication {
-        pub window: OnceCell<WeakRef<ExampleApplicationWindow>>,
+        pub window: OnceCell<WeakRef<FrctlWindow>>,
     }
 
     impl ObjectSubclass for ExampleApplication {
@@ -43,8 +43,7 @@ mod imp {
         fn activate(&self, app: &Self::Type) {
             debug!("GtkApplication<ExampleApplication>::activate");
 
-            let priv_ = ExampleApplication::from_instance(app);
-            if let Some(window) = priv_.window.get() {
+            if let Some(window) = self.window.get() {
                 let window = window.upgrade().unwrap();
                 window.show();
                 window.present();
@@ -54,7 +53,7 @@ mod imp {
             app.set_resource_base_path(Some("/org/gnome/FractalNext/"));
             app.setup_css();
 
-            let window = ExampleApplicationWindow::new(app);
+            let window = FrctlWindow::new(app);
             self.window
                 .set(window.downgrade())
                 .expect("Window already set.");
@@ -83,14 +82,18 @@ impl ExampleApplication {
     pub fn new() -> Self {
         glib::Object::new(&[
             ("application-id", &Some(config::APP_ID)),
-            ("flags", &ApplicationFlags::empty()),
+            ("flags", &ApplicationFlags::default()),
         ])
         .expect("Application initialization failed...")
     }
 
-    fn get_main_window(&self) -> ExampleApplicationWindow {
-        let priv_ = imp::ExampleApplication::from_instance(self);
-        priv_.window.get().unwrap().upgrade().unwrap()
+    fn get_main_window(&self) -> FrctlWindow {
+        imp::ExampleApplication::from_instance(self)
+            .window
+            .get()
+            .unwrap()
+            .upgrade()
+            .unwrap()
     }
 
     fn setup_gactions(&self) {
diff --git a/src/main.rs b/src/main.rs
index 4ab963e9..7a2ec43b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,8 +1,9 @@
 mod application;
 #[rustfmt::skip]
 mod config;
-mod window;
+mod widgets;
 
+use adw;
 use application::ExampleApplication;
 use config::{GETTEXT_PACKAGE, LOCALEDIR, RESOURCES_FILE};
 use gettextrs::*;
@@ -23,6 +24,7 @@ fn main() {
     gtk::glib::set_prgname(Some("fractal"));
 
     gtk::init().expect("Unable to start GTK4");
+    adw::init();
 
     let res = gio::Resource::load(RESOURCES_FILE).expect("Could not load gresource file");
     gio::resources_register(&res);
diff --git a/src/meson.build b/src/meson.build
index bc737b2c..27d59b67 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -22,7 +22,12 @@ sources = files(
   'application.rs',
   'config.rs',
   'main.rs',
-  'window.rs',
+  'widgets/window.rs',
+  'widgets/content.rs',
+  'widgets/sidebar.rs',
+  'widgets/login.rs',
+  'widgets/mod.rs',
+  'widgets/session.rs',
 )
 
 custom_target(
diff --git a/src/widgets/content.rs b/src/widgets/content.rs
new file mode 100644
index 00000000..4554e3e3
--- /dev/null
+++ b/src/widgets/content.rs
@@ -0,0 +1,110 @@
+use adw;
+use adw::subclass::prelude::BinImpl;
+use gtk::subclass::prelude::*;
+use gtk::{self, prelude::*};
+use gtk::{glib, CompositeTemplate};
+
+mod imp {
+    use super::*;
+    use glib::subclass;
+    use std::cell::Cell;
+
+    #[derive(Debug, CompositeTemplate)]
+    #[template(resource = "/org/gnome/FractalNext/content.ui")]
+    pub struct FrctlContent {
+        pub compact: Cell<bool>,
+        #[template_child]
+        pub headerbar: TemplateChild<adw::HeaderBar>,
+        #[template_child]
+        pub room_history: TemplateChild<gtk::ListView>,
+    }
+
+    impl ObjectSubclass for FrctlContent {
+        const NAME: &'static str = "FrctlContent";
+        type Type = super::FrctlContent;
+        type ParentType = adw::Bin;
+        type Interfaces = ();
+        type Instance = subclass::simple::InstanceStruct<Self>;
+        type Class = subclass::simple::ClassStruct<Self>;
+
+        glib::object_subclass!();
+
+        fn new() -> Self {
+            Self {
+                compact: Cell::new(false),
+                headerbar: TemplateChild::default(),
+                room_history: TemplateChild::default(),
+            }
+        }
+
+        fn class_init(klass: &mut Self::Class) {
+            Self::bind_template(klass);
+        }
+
+        // You must call `Widget`'s `init_template()` within `instance_init()`.
+        fn instance_init(obj: &glib::subclass::InitializingObject<Self::Type>) {
+            obj.init_template();
+        }
+    }
+
+    impl ObjectImpl for FrctlContent {
+        fn properties() -> &'static [glib::ParamSpec] {
+            use once_cell::sync::Lazy;
+            static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
+                vec![glib::ParamSpec::boolean(
+                    "compact",
+                    "Compact",
+                    "Wheter a compact view is used or not",
+                    false,
+                    glib::ParamFlags::READWRITE,
+                )]
+            });
+
+            PROPERTIES.as_ref()
+        }
+
+        fn set_property(
+            &self,
+            _obj: &Self::Type,
+            _id: usize,
+            value: &glib::Value,
+            pspec: &glib::ParamSpec,
+        ) {
+            match pspec.get_name() {
+                "compact" => {
+                    let compact = value
+                        .get()
+                        .expect("type conformity checked by `Object::set_property`");
+                    self.compact.set(compact.unwrap());
+                }
+                _ => unimplemented!(),
+            }
+        }
+
+        fn get_property(
+            &self,
+            _obj: &Self::Type,
+            _id: usize,
+            pspec: &glib::ParamSpec,
+        ) -> glib::Value {
+            match pspec.get_name() {
+                "compact" => self.compact.get().to_value(),
+                _ => unimplemented!(),
+            }
+        }
+    }
+
+    impl WidgetImpl for FrctlContent {}
+    impl BinImpl for FrctlContent {}
+}
+
+glib::wrapper! {
+    pub struct FrctlContent(ObjectSubclass<imp::FrctlContent>)
+        @extends gtk::Widget, adw::Bin, @implements gtk::Accessible;
+}
+
+impl FrctlContent {
+    pub fn new() -> Self {
+        glib::Object::new(&[]).expect("Failed to create FrctlContent")
+    }
+}
diff --git a/src/widgets/login.rs b/src/widgets/login.rs
new file mode 100644
index 00000000..06b607ea
--- /dev/null
+++ b/src/widgets/login.rs
@@ -0,0 +1,58 @@
+use adw;
+use adw::subclass::prelude::BinImpl;
+use gtk::subclass::prelude::*;
+use gtk::{self, prelude::*};
+use gtk::{glib, CompositeTemplate};
+
+mod imp {
+    use super::*;
+    use glib::subclass;
+
+    #[derive(Debug, CompositeTemplate)]
+    #[template(resource = "/org/gnome/FractalNext/login.ui")]
+    pub struct FrctlLogin {
+        #[template_child]
+        pub headerbar: TemplateChild<gtk::HeaderBar>,
+    }
+
+    impl ObjectSubclass for FrctlLogin {
+        const NAME: &'static str = "FrctlLogin";
+        type Type = super::FrctlLogin;
+        type ParentType = adw::Bin;
+        type Interfaces = ();
+        type Instance = subclass::simple::InstanceStruct<Self>;
+        type Class = subclass::simple::ClassStruct<Self>;
+
+        glib::object_subclass!();
+
+        fn new() -> Self {
+            Self {
+                headerbar: TemplateChild::default(),
+            }
+        }
+
+        fn class_init(klass: &mut Self::Class) {
+            Self::bind_template(klass);
+        }
+
+        // You must call `Widget`'s `init_template()` within `instance_init()`.
+        fn instance_init(obj: &glib::subclass::InitializingObject<Self::Type>) {
+            obj.init_template();
+        }
+    }
+
+    impl ObjectImpl for FrctlLogin {}
+    impl WidgetImpl for FrctlLogin {}
+    impl BinImpl for FrctlLogin {}
+}
+
+glib::wrapper! {
+    pub struct FrctlLogin(ObjectSubclass<imp::FrctlLogin>)
+        @extends gtk::Widget, adw::Bin, @implements gtk::Accessible;
+}
+
+impl FrctlLogin {
+    pub fn new() -> Self {
+        glib::Object::new(&[]).expect("Failed to create FrctlLogin")
+    }
+}
diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs
new file mode 100644
index 00000000..126ba693
--- /dev/null
+++ b/src/widgets/mod.rs
@@ -0,0 +1,11 @@
+mod content;
+mod login;
+mod session;
+mod sidebar;
+mod window;
+
+pub use crate::widgets::content::FrctlContent;
+pub use crate::widgets::login::FrctlLogin;
+pub use crate::widgets::session::FrctlSession;
+pub use crate::widgets::sidebar::FrctlSidebar;
+pub use crate::widgets::window::FrctlWindow;
diff --git a/src/widgets/session.rs b/src/widgets/session.rs
new file mode 100644
index 00000000..0f3b222b
--- /dev/null
+++ b/src/widgets/session.rs
@@ -0,0 +1,63 @@
+use crate::widgets::FrctlContent;
+use crate::widgets::FrctlSidebar;
+use adw;
+use adw::subclass::prelude::BinImpl;
+use gtk::subclass::prelude::*;
+use gtk::{self, prelude::*};
+use gtk::{glib, CompositeTemplate};
+
+mod imp {
+    use super::*;
+    use glib::subclass;
+
+    #[derive(Debug, CompositeTemplate)]
+    #[template(resource = "/org/gnome/FractalNext/session.ui")]
+    pub struct FrctlSession {
+        #[template_child]
+        pub sidebar: TemplateChild<FrctlSidebar>,
+        #[template_child]
+        pub content: TemplateChild<FrctlContent>,
+    }
+
+    impl ObjectSubclass for FrctlSession {
+        const NAME: &'static str = "FrctlSession";
+        type Type = super::FrctlSession;
+        type ParentType = adw::Bin;
+        type Interfaces = ();
+        type Instance = subclass::simple::InstanceStruct<Self>;
+        type Class = subclass::simple::ClassStruct<Self>;
+
+        glib::object_subclass!();
+
+        fn new() -> Self {
+            Self {
+                sidebar: TemplateChild::default(),
+                content: TemplateChild::default(),
+            }
+        }
+
+        fn class_init(klass: &mut Self::Class) {
+            Self::bind_template(klass);
+        }
+
+        // You must call `Widget`'s `init_template()` within `instance_init()`.
+        fn instance_init(obj: &glib::subclass::InitializingObject<Self::Type>) {
+            obj.init_template();
+        }
+    }
+
+    impl ObjectImpl for FrctlSession {}
+    impl WidgetImpl for FrctlSession {}
+    impl BinImpl for FrctlSession {}
+}
+
+glib::wrapper! {
+    pub struct FrctlSession(ObjectSubclass<imp::FrctlSession>)
+        @extends gtk::Widget, adw::Bin, @implements gtk::Accessible;
+}
+
+impl FrctlSession {
+    pub fn new() -> Self {
+        glib::Object::new(&[]).expect("Failed to create FrctlSession")
+    }
+}
diff --git a/src/widgets/sidebar.rs b/src/widgets/sidebar.rs
new file mode 100644
index 00000000..003fc346
--- /dev/null
+++ b/src/widgets/sidebar.rs
@@ -0,0 +1,110 @@
+use adw;
+use adw::subclass::prelude::BinImpl;
+use gtk::subclass::prelude::*;
+use gtk::{self, prelude::*};
+use gtk::{glib, CompositeTemplate};
+
+mod imp {
+    use super::*;
+    use glib::subclass;
+    use std::cell::Cell;
+
+    #[derive(Debug, CompositeTemplate)]
+    #[template(resource = "/org/gnome/FractalNext/sidebar.ui")]
+    pub struct FrctlSidebar {
+        pub compact: Cell<bool>,
+        #[template_child]
+        pub headerbar: TemplateChild<adw::HeaderBar>,
+        #[template_child]
+        pub listview: TemplateChild<gtk::ListView>,
+    }
+
+    impl ObjectSubclass for FrctlSidebar {
+        const NAME: &'static str = "FrctlSidebar";
+        type Type = super::FrctlSidebar;
+        type ParentType = adw::Bin;
+        type Interfaces = ();
+        type Instance = subclass::simple::InstanceStruct<Self>;
+        type Class = subclass::simple::ClassStruct<Self>;
+
+        glib::object_subclass!();
+
+        fn new() -> Self {
+            Self {
+                compact: Cell::new(false),
+                listview: TemplateChild::default(),
+                headerbar: TemplateChild::default(),
+            }
+        }
+
+        fn class_init(klass: &mut Self::Class) {
+            Self::bind_template(klass);
+        }
+
+        // You must call `Widget`'s `init_template()` within `instance_init()`.
+        fn instance_init(obj: &glib::subclass::InitializingObject<Self::Type>) {
+            obj.init_template();
+        }
+    }
+
+    impl ObjectImpl for FrctlSidebar {
+        fn properties() -> &'static [glib::ParamSpec] {
+            use once_cell::sync::Lazy;
+            static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
+                vec![glib::ParamSpec::boolean(
+                    "compact",
+                    "Compact",
+                    "Wheter a compact view is used or not",
+                    false,
+                    glib::ParamFlags::READWRITE,
+                )]
+            });
+
+            PROPERTIES.as_ref()
+        }
+
+        fn set_property(
+            &self,
+            _obj: &Self::Type,
+            _id: usize,
+            value: &glib::Value,
+            pspec: &glib::ParamSpec,
+        ) {
+            match pspec.get_name() {
+                "compact" => {
+                    let compact = value
+                        .get()
+                        .expect("type conformity checked by `Object::set_property`");
+                    self.compact.set(compact.unwrap());
+                }
+                _ => unimplemented!(),
+            }
+        }
+
+        fn get_property(
+            &self,
+            _obj: &Self::Type,
+            _id: usize,
+            pspec: &glib::ParamSpec,
+        ) -> glib::Value {
+            match pspec.get_name() {
+                "compact" => self.compact.get().to_value(),
+                _ => unimplemented!(),
+            }
+        }
+    }
+
+    impl WidgetImpl for FrctlSidebar {}
+    impl BinImpl for FrctlSidebar {}
+}
+
+glib::wrapper! {
+    pub struct FrctlSidebar(ObjectSubclass<imp::FrctlSidebar>)
+        @extends gtk::Widget, adw::Bin, @implements gtk::Accessible;
+}
+
+impl FrctlSidebar {
+    pub fn new() -> Self {
+        glib::Object::new(&[]).expect("Failed to create FrctlSidebar")
+    }
+}
diff --git a/src/window.rs b/src/widgets/window.rs
similarity index 61%
rename from src/window.rs
rename to src/widgets/window.rs
index ed668918..379ed6af 100644
--- a/src/window.rs
+++ b/src/widgets/window.rs
@@ -1,5 +1,8 @@
 use crate::application::ExampleApplication;
 use crate::config::{APP_ID, PROFILE};
+use crate::widgets::FrctlLogin;
+use crate::widgets::FrctlSession;
+use adw::subclass::prelude::AdwApplicationWindowImpl;
 use glib::signal::Inhibit;
 use gtk::subclass::prelude::*;
 use gtk::{self, prelude::*};
@@ -12,16 +15,22 @@ mod imp {
 
     #[derive(Debug, CompositeTemplate)]
     #[template(resource = "/org/gnome/FractalNext/window.ui")]
-    pub struct ExampleApplicationWindow {
+    pub struct FrctlWindow {
         #[template_child]
-        pub headerbar: TemplateChild<gtk::HeaderBar>,
+        pub main_stack: TemplateChild<gtk::Stack>,
+        #[template_child]
+        pub login: TemplateChild<FrctlLogin>,
+        // Eventually we want to create the session dynamically, since we want multi account
+        // support
+        #[template_child]
+        pub session: TemplateChild<FrctlSession>,
         pub settings: gio::Settings,
     }
 
-    impl ObjectSubclass for ExampleApplicationWindow {
-        const NAME: &'static str = "ExampleApplicationWindow";
-        type Type = super::ExampleApplicationWindow;
-        type ParentType = gtk::ApplicationWindow;
+    impl ObjectSubclass for FrctlWindow {
+        const NAME: &'static str = "FrctlWindow";
+        type Type = super::FrctlWindow;
+        type ParentType = adw::ApplicationWindow;
         type Interfaces = ();
         type Instance = subclass::simple::InstanceStruct<Self>;
         type Class = subclass::simple::ClassStruct<Self>;
@@ -30,7 +39,9 @@ mod imp {
 
         fn new() -> Self {
             Self {
-                headerbar: TemplateChild::default(),
+                main_stack: TemplateChild::default(),
+                login: TemplateChild::default(),
+                session: TemplateChild::default(),
                 settings: gio::Settings::new(APP_ID),
             }
         }
@@ -45,7 +56,7 @@ mod imp {
         }
     }
 
-    impl ObjectImpl for ExampleApplicationWindow {
+    impl ObjectImpl for FrctlWindow {
         fn constructed(&self, obj: &Self::Type) {
             self.parent_constructed(obj);
 
@@ -55,7 +66,7 @@ mod imp {
 
             // Devel Profile
             if PROFILE == "Devel" {
-                obj.get_style_context().add_class("devel");
+                obj.add_css_class("devel");
             }
 
             // load latest window state
@@ -63,7 +74,7 @@ mod imp {
         }
     }
 
-    impl WindowImpl for ExampleApplicationWindow {
+    impl WindowImpl for FrctlWindow {
         // save window state on delete event
         fn close_request(&self, obj: &Self::Type) -> Inhibit {
             if let Err(err) = obj.save_window_size() {
@@ -73,29 +84,24 @@ mod imp {
         }
     }
 
-    impl WidgetImpl for ExampleApplicationWindow {}
-    impl ApplicationWindowImpl for ExampleApplicationWindow {}
+    impl WidgetImpl for FrctlWindow {}
+    impl ApplicationWindowImpl for FrctlWindow {}
+    impl AdwApplicationWindowImpl for FrctlWindow {}
 }
 
 glib::wrapper! {
-    pub struct ExampleApplicationWindow(ObjectSubclass<imp::ExampleApplicationWindow>)
-        @extends gtk::Widget, gtk::Window, gtk::ApplicationWindow, @implements gio::ActionMap, 
gio::ActionGroup;
+    pub struct FrctlWindow(ObjectSubclass<imp::FrctlWindow>)
+        @extends gtk::Widget, gtk::Window, gtk::ApplicationWindow, adw::ApplicationWindow, @implements 
gio::ActionMap, gio::ActionGroup;
 }
 
-impl ExampleApplicationWindow {
-    pub fn new(app: &ExampleApplication) -> Self {
-        let window: Self =
-            glib::Object::new(&[]).expect("Failed to create ExampleApplicationWindow");
-        window.set_application(Some(app));
-
-        // Set icons for shell
-        gtk::Window::set_default_icon_name(APP_ID);
-
-        window
+impl FrctlWindow {
+    pub fn new(app: &FrctlApplication) -> Self {
+        glib::Object::new(&[("application", &Some(app)), ("icon-name", &Some(APP_ID))])
+            .expect("Failed to create FrctlWindow")
     }
 
     pub fn save_window_size(&self) -> Result<(), glib::BoolError> {
-        let settings = &imp::ExampleApplicationWindow::from_instance(self).settings;
+        let settings = &imp::FrctlWindow::from_instance(self).settings;
 
         let size = self.get_default_size();
 
@@ -108,16 +114,13 @@ impl ExampleApplicationWindow {
     }
 
     fn load_window_size(&self) {
-        let settings = &imp::ExampleApplicationWindow::from_instance(self).settings;
+        let settings = &imp::FrctlWindow::from_instance(self).settings;
 
         let width = settings.get_int("window-width");
         let height = settings.get_int("window-height");
         let is_maximized = settings.get_boolean("is-maximized");
 
         self.set_default_size(width, height);
-
-        if is_maximized {
-            self.maximize();
-        }
+        self.set_property_maximized(is_maximized);
     }
 }


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