[fractal/fractal-next] app: Separate greeter and login screens
- From: Julian Sparber <jsparber src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [fractal/fractal-next] app: Separate greeter and login screens
- Date: Tue, 1 Feb 2022 13:39:12 +0000 (UTC)
commit 74cf22df848a00b1459d0c65982ba7b703c4bd7c
Author: Kévin Commaille <zecakeh tedomum fr>
Date: Wed Jan 26 10:21:20 2022 +0100
app: Separate greeter and login screens
data/resources/resources.gresource.xml | 1 +
data/resources/ui/greeter.ui | 134 ++++++++++++++++++++++++
data/resources/ui/login.ui | 157 ++++++++++++----------------
data/resources/ui/window.ui | 6 +-
po/POTFILES.in | 1 +
src/application.rs | 30 +++++-
src/greeter.rs | 60 +++++++++++
src/login.rs | 8 +-
src/main.rs | 3 +-
src/session/sidebar/account_switcher/mod.rs | 2 +-
src/window.rs | 65 ++++++++++--
11 files changed, 349 insertions(+), 118 deletions(-)
---
diff --git a/data/resources/resources.gresource.xml b/data/resources/resources.gresource.xml
index c3016c7aa..f871fc9ff 100644
--- a/data/resources/resources.gresource.xml
+++ b/data/resources/resources.gresource.xml
@@ -50,6 +50,7 @@
<file compressed="true" preprocess="xml-stripblanks"
alias="context-menu-bin.ui">ui/context-menu-bin.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="event-menu.ui">ui/event-menu.ui</file>
<file compressed="true" preprocess="xml-stripblanks"
alias="event-source-dialog.ui">ui/event-source-dialog.ui</file>
+ <file compressed="true" preprocess="xml-stripblanks" alias="greeter.ui">ui/greeter.ui</file>
<file compressed="true" preprocess="xml-stripblanks"
alias="identity-verification-widget.ui">ui/identity-verification-widget.ui</file>
<file compressed="true" preprocess="xml-stripblanks"
alias="in-app-notification.ui">ui/in-app-notification.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="login.ui">ui/login.ui</file>
diff --git a/data/resources/ui/greeter.ui b/data/resources/ui/greeter.ui
new file mode 100644
index 000000000..4d412d3aa
--- /dev/null
+++ b/data/resources/ui/greeter.ui
@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="Greeter" parent="AdwBin">
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkHeaderBar">
+ <property name="title-widget">
+ <object class="AdwWindowTitle">
+ <property name="title">Fractal</property>
+ </object>
+ </property>
+ <child type="start">
+ <object class="GtkButton" id="back_button">
+ <property name="action-name">app.show-sessions</property>
+ <property name="visible" bind-source="back_button" bind-property="sensitive"
bind-flags="sync-create"/>
+ <property name="icon-name">go-previous-symbolic</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="hscrollbar-policy">never</property>
+ <property name="propagate-natural-height">True</property>
+ <property name="vexpand">true</property>
+ <property name="child">
+ <object class="AdwClamp">
+ <property name="maximum-size">440</property>
+ <property name="tightening-threshold">500</property>
+ <property name="margin-top">0</property>
+ <property name="margin-bottom">24</property>
+ <property name="margin-start">24</property>
+ <property name="margin-end">24</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <property name="valign">center</property>
+ <property name="spacing">24</property>
+ <child>
+ <object class="AdwClamp">
+ <property name="maximum-size">360</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <property name="valign">center</property>
+ <property name="spacing">24</property>
+ <child>
+ <object class="GtkPicture">
+ <property
name="file">resource:///org/gnome/FractalNext/icons/scalable/status/welcome.svg</property>
+ <property name="height-request">120</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="wrap">True</property>
+ <property name="wrap-mode">word-char</property>
+ <property name="justify">center</property>
+ <property name="label" translatable="yes" comments="Fractal is the
application name and shouldn't be translated">Welcome to Fractal</property>
+ <style>
+ <class name="title-1"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <style>
+ <class name="card"/>
+ <class name="devnotice"/>
+ </style>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkImage">
+ <property name="icon-name">dialog-warning-symbolic</property>
+ <property name="icon-size">large</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">You are running an early stage
development version. Be aware it is a work in progress and far from complete yet.</property>
+ <property name="wrap">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwClamp">
+ <property name="maximum-size">260</property>
+ <property name="margin-top">10</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <property name="valign">center</property>
+ <property name="spacing">16</property>
+ <child>
+ <object class="GtkButton" id="login_button">
+ <style>
+ <class name="pill"/>
+ <class name="suggested-action"/>
+ </style>
+ <property name="label" translatable="true">_Log in</property>
+ <property name="use-underline">true</property>
+ <property name="action-name">app.show-login</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton">
+ <style>
+ <class name="pill"/>
+ </style>
+ <property name="label" translatable="true">_Create Account</property>
+ <property name="use-underline">true</property>
+ <property name="action-name">app.create-account</property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/data/resources/ui/login.ui b/data/resources/ui/login.ui
index 780d63d29..0743eeeef 100644
--- a/data/resources/ui/login.ui
+++ b/data/resources/ui/login.ui
@@ -8,13 +8,12 @@
<object class="GtkHeaderBar">
<property name="title-widget">
<object class="AdwWindowTitle">
- <property name="title">Fractal</property>
+ <property name="title">Log In</property>
</object>
</property>
<child type="start">
- <object class="GtkButton" id="back_to_session_button">
- <property name="action_name">app.switch-to-sessions</property>
- <property name="visible">false</property>
+ <object class="GtkButton">
+ <property name="action_name">app.show-greeter</property>
<property name="icon-name">go-previous-symbolic</property>
</object>
</child>
@@ -35,108 +34,82 @@
<property name="vexpand">True</property>
<child>
<object class="GtkStackPage">
- <property name="name">greeter</property>
+ <property name="name">credentials</property>
<property name="child">
- <object class="AdwStatusPage">
- <property name="icon-name">welcome</property>
- <property name="title" translatable="yes" comments="Fractal is the application name and
shouldn't be translated">Welcome to Fractal</property>
+ <object class="AdwClamp">
+ <property name="maximum-size">400</property>
+ <property name="tightening-threshold">300</property>
+ <property name="valign">center</property>
<child>
- <object class="AdwClamp">
- <property name="maximum-size">400</property>
- <property name="tightening-threshold">300</property>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <property name="spacing">18</property>
<child>
- <object class="GtkBox">
- <property name="orientation">vertical</property>
- <property name="spacing">18</property>
+ <object class="GtkListBox">
<child>
- <object class="GtkBox">
- <property name="spacing">12</property>
- <child>
- <object class="GtkImage" id="development_icon">
- <property name="icon-name">dialog-warning-symbolic</property>
- <property name="icon-size">large</property>
+ <object class="GtkListBoxRow">
+ <property name="focusable">False</property>
+ <property name="selectable">False</property>
+ <property name="activatable">False</property>
+ <property name="child">
+ <object class="GtkEntry" id="homeserver_entry">
+ <property name="activates-default">True</property>
+ <property name="input_purpose">GTK_INPUT_PURPOSE_URL</property>
+ <property name="placeholder-text">Homeserver</property>
+ <property name="margin-top">6</property>
+ <property name="margin-bottom">6</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
</object>
- </child>
- <child>
- <object class="GtkLabel" id="development_notice">
- <property name="label" translatable="yes">You are running an early stage
development version. Be aware it is a work in progress and far from complete yet.</property>
- <property name="wrap">True</property>
- </object>
- </child>
- <style>
- <class name="card"/>
- <class name="devnotice"/>
- </style>
+ </property>
</object>
</child>
<child>
- <object class="GtkListBox">
- <child>
- <object class="GtkListBoxRow">
- <property name="focusable">False</property>
- <property name="selectable">False</property>
- <property name="activatable">False</property>
- <property name="child">
- <object class="GtkEntry" id="homeserver_entry">
- <property name="activates-default">True</property>
- <property name="input_purpose">GTK_INPUT_PURPOSE_URL</property>
- <property name="placeholder-text">Homeserver</property>
- <property name="margin-top">6</property>
- <property name="margin-bottom">6</property>
- <property name="margin-start">6</property>
- <property name="margin-end">6</property>
- </object>
- </property>
- </object>
- </child>
- <child>
- <object class="GtkListBoxRow">
- <property name="focusable">False</property>
- <property name="selectable">False</property>
- <property name="activatable">False</property>
- <property name="child">
- <object class="GtkEntry" id="username_entry">
- <property name="activates-default">True</property>
- <property name="placeholder-text">Matrix Username</property>
- <property name="margin-top">6</property>
- <property name="margin-bottom">6</property>
- <property name="margin-start">6</property>
- <property name="margin-end">6</property>
- </object>
- </property>
+ <object class="GtkListBoxRow">
+ <property name="focusable">False</property>
+ <property name="selectable">False</property>
+ <property name="activatable">False</property>
+ <property name="child">
+ <object class="GtkEntry" id="username_entry">
+ <property name="activates-default">True</property>
+ <property name="placeholder-text">Matrix Username</property>
+ <property name="margin-top">6</property>
+ <property name="margin-bottom">6</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
</object>
- </child>
- <child>
- <object class="GtkListBoxRow">
- <property name="focusable">False</property>
- <property name="selectable">False</property>
- <property name="activatable">False</property>
- <property name="child">
- <object class="GtkPasswordEntry" id="password_entry">
- <property name="activates-default">True</property>
- <property name="show-peek-icon">True</property>
- <property name="placeholder-text">Password</property>
- <property name="margin-top">6</property>
- <property name="margin-bottom">6</property>
- <property name="margin-start">6</property>
- <property name="margin-end">6</property>
- </object>
- </property>
- </object>
- </child>
- <style>
- <class name="content"/>
- <class name="login"/>
- </style>
+ </property>
</object>
</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://app.element.io/#/forgot_password</property>
+ <object class="GtkListBoxRow">
+ <property name="focusable">False</property>
+ <property name="selectable">False</property>
+ <property name="activatable">False</property>
+ <property name="child">
+ <object class="GtkPasswordEntry" id="password_entry">
+ <property name="activates-default">True</property>
+ <property name="show-peek-icon">True</property>
+ <property name="placeholder-text">Password</property>
+ <property name="margin-top">6</property>
+ <property name="margin-bottom">6</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ </object>
+ </property>
</object>
</child>
+ <style>
+ <class name="content"/>
+ <class name="login"/>
+ </style>
+ </object>
+ </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://app.element.io/#/forgot_password</property>
</object>
</child>
</object>
diff --git a/data/resources/ui/window.ui b/data/resources/ui/window.ui
index c1696d38b..3ddae4837 100644
--- a/data/resources/ui/window.ui
+++ b/data/resources/ui/window.ui
@@ -16,8 +16,11 @@
</child>
<child>
<object class="GtkStack" id="main_stack">
- <property name="visible-child">login</property>
+ <property name="visible-child">greeter</property>
<property name="transition-type">crossfade</property>
+ <child>
+ <object class="Greeter" id="greeter"/>
+ </child>
<child>
<object class="Login" id="login"/>
</child>
@@ -32,4 +35,3 @@
</property>
</template>
</interface>
-
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 58094d6dd..2275d5bdd 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -23,6 +23,7 @@ data/resources/ui/content-state-tombstone.ui
data/resources/ui/content.ui
data/resources/ui/event-menu.ui
data/resources/ui/event-source-dialog.ui
+data/resources/ui/greeter.ui
data/resources/ui/identity-verification-widget.ui
data/resources/ui/login.ui
data/resources/ui/member-menu.ui
diff --git a/src/application.rs b/src/application.rs
index 782abd7aa..04c72dc47 100644
--- a/src/application.rs
+++ b/src/application.rs
@@ -116,19 +116,41 @@ impl Application {
action!(
self,
- "new-login",
+ "new-session",
clone!(@weak self as app => move |_, _| {
- app.get_main_window().switch_to_login_page(true);
+ app.get_main_window().switch_to_greeter_page(true);
})
);
action!(
self,
- "switch-to-sessions",
+ "show-greeter",
clone!(@weak self as app => move |_, _| {
- app.get_main_window().switch_to_sessions_page();
+ app.get_main_window().switch_to_greeter_page(false);
})
);
+
+ action!(
+ self,
+ "show-login",
+ clone!(@weak self as app => move |_, _| {
+ app.get_main_window().switch_to_login_page();
+ })
+ );
+
+ let show_sessions_action = gio::SimpleAction::new("show-sessions", None);
+ show_sessions_action.connect_activate(clone!(@weak self as app => move |_, _| {
+ app.get_main_window().switch_to_sessions_page();
+ }));
+ self.add_action(&show_sessions_action);
+ let win = self.get_main_window();
+ win.connect_notify_local(
+ Some("has-sessions"),
+ clone!(@weak show_sessions_action => move |win, _| {
+ show_sessions_action.set_enabled(win.has_sessions());
+ }),
+ );
+ show_sessions_action.set_enabled(win.has_sessions());
}
/// Sets up keyboard shortcuts for application and window actions.
diff --git a/src/greeter.rs b/src/greeter.rs
new file mode 100644
index 000000000..986d1fa6e
--- /dev/null
+++ b/src/greeter.rs
@@ -0,0 +1,60 @@
+use adw::subclass::prelude::BinImpl;
+use gtk::{self, glib, prelude::*, subclass::prelude::*, CompositeTemplate};
+
+mod imp {
+ use glib::subclass::InitializingObject;
+
+ use super::*;
+
+ #[derive(Debug, Default, CompositeTemplate)]
+ #[template(resource = "/org/gnome/FractalNext/greeter.ui")]
+ pub struct Greeter {
+ #[template_child]
+ pub back_button: TemplateChild<gtk::Button>,
+ #[template_child]
+ pub login_button: TemplateChild<gtk::Button>,
+ }
+
+ #[glib::object_subclass]
+ impl ObjectSubclass for Greeter {
+ const NAME: &'static str = "Greeter";
+ type Type = super::Greeter;
+ type ParentType = adw::Bin;
+
+ fn class_init(klass: &mut Self::Class) {
+ Self::bind_template(klass);
+ klass.set_accessible_role(gtk::AccessibleRole::Group);
+ }
+
+ fn instance_init(obj: &InitializingObject<Self>) {
+ obj.init_template();
+ }
+ }
+
+ impl ObjectImpl for Greeter {}
+
+ impl WidgetImpl for Greeter {}
+
+ impl BinImpl for Greeter {}
+}
+
+glib::wrapper! {
+ pub struct Greeter(ObjectSubclass<imp::Greeter>)
+ @extends gtk::Widget, adw::Bin, @implements gtk::Accessible;
+}
+
+impl Greeter {
+ pub fn new() -> Self {
+ glib::Object::new(&[]).expect("Failed to create Greeter")
+ }
+
+ pub fn default_widget(&self) -> gtk::Widget {
+ self.imp().login_button.get().upcast()
+ }
+}
+
+impl Default for Greeter {
+ fn default() -> Self {
+ Self::new()
+ }
+}
diff --git a/src/login.rs b/src/login.rs
index 487f99ef4..8129f594d 100644
--- a/src/login.rs
+++ b/src/login.rs
@@ -30,8 +30,6 @@ mod imp {
pub username_entry: TemplateChild<gtk::Entry>,
#[template_child]
pub password_entry: TemplateChild<gtk::PasswordEntry>,
- #[template_child]
- pub back_to_session_button: TemplateChild<gtk::Button>,
pub prepared_source_id: RefCell<Option<SignalHandlerId>>,
pub logged_out_source_id: RefCell<Option<SignalHandlerId>>,
pub ready_source_id: RefCell<Option<SignalHandlerId>>,
@@ -193,10 +191,6 @@ impl Login {
self.imp().next_button.get().upcast()
}
- pub fn show_back_to_session_button(&self, show: bool) {
- self.imp().back_to_session_button.set_visible(show);
- }
-
fn set_handler_for_prepared_session(&self, session: &Session) {
let priv_ = self.imp();
priv_
@@ -226,7 +220,7 @@ impl Login {
.logged_out_source_id
.replace(Some(session.connect_logged_out(
clone!(@weak self as login => move |_| {
- login.parent_window().switch_to_login_page(false);
+ login.parent_window().switch_to_login_page();
login.drop_session_reference();
login.unfreeze();
}),
diff --git a/src/main.rs b/src/main.rs
index 101d9a3ec..4f65274ac 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -10,6 +10,7 @@ mod prelude;
mod components;
mod contrib;
mod error;
+mod greeter;
mod login;
mod secret;
mod session;
@@ -23,7 +24,7 @@ use gtk::{gdk::Display, gio, IconTheme};
use once_cell::sync::Lazy;
use self::{
- application::Application, error::Error, login::Login, session::Session,
+ application::Application, error::Error, greeter::Greeter, login::Login, session::Session,
user_facing_error::UserFacingError, window::Window,
};
diff --git a/src/session/sidebar/account_switcher/mod.rs b/src/session/sidebar/account_switcher/mod.rs
index 756d4a403..0badb3204 100644
--- a/src/session/sidebar/account_switcher/mod.rs
+++ b/src/session/sidebar/account_switcher/mod.rs
@@ -69,7 +69,7 @@ mod imp {
.set_visible_child(&session_widget);
}
AccountSwitcherItem::AddAccount => {
- list_view.activate_action("app.new-login", None).unwrap();
+ list_view.activate_action("app.new-session", None).unwrap();
}
_ => {}
}
diff --git a/src/window.rs b/src/window.rs
index 5e84a1f14..843ead1a0 100644
--- a/src/window.rs
+++ b/src/window.rs
@@ -7,11 +7,12 @@ use log::warn;
use crate::{
components::InAppNotification,
config::{APP_ID, PROFILE},
- secret, Application, Error, Login, Session, UserFacingError,
+ secret, Application, Error, Greeter, Login, Session, UserFacingError,
};
mod imp {
use glib::subclass::InitializingObject;
+ use once_cell::sync::Lazy;
use super::*;
@@ -21,6 +22,8 @@ mod imp {
#[template_child]
pub main_stack: TemplateChild<gtk::Stack>,
#[template_child]
+ pub greeter: TemplateChild<Greeter>,
+ #[template_child]
pub login: TemplateChild<Login>,
#[template_child]
pub sessions: TemplateChild<gtk::Stack>,
@@ -46,6 +49,27 @@ mod imp {
}
impl ObjectImpl for Window {
+ fn properties() -> &'static [glib::ParamSpec] {
+ static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
+ vec![glib::ParamSpecBoolean::new(
+ "has-sessions",
+ "Has Sessions",
+ "Whether this window has sessions",
+ false,
+ glib::ParamFlags::READABLE,
+ )]
+ });
+
+ PROPERTIES.as_ref()
+ }
+
+ fn property(&self, obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
+ match pspec.name() {
+ "has-sessions" => obj.has_sessions().to_value(),
+ _ => unimplemented!(),
+ }
+ }
+
fn constructed(&self, obj: &Self::Type) {
self.parent_constructed(obj);
@@ -114,6 +138,8 @@ impl Window {
fn add_session(&self, session: &Session) {
let priv_ = &self.imp();
+ let prev_has_sessions = self.has_sessions();
+
session.set_logged_in_users(&priv_.sessions.pages());
priv_.sessions.add_child(session);
priv_.sessions.set_visible_child(session);
@@ -123,6 +149,10 @@ impl Window {
session.connect_logged_out(clone!(@weak self as obj => move |session| {
obj.remove_session(session)
}));
+
+ if !prev_has_sessions {
+ self.notify("has-sessions");
+ }
}
fn remove_session(&self, session: &Session) {
@@ -133,7 +163,8 @@ impl Window {
if let Some(child) = priv_.sessions.first_child() {
priv_.sessions.set_visible_child(&child);
} else {
- self.switch_to_login_page(false);
+ self.notify("has-sessions");
+ self.switch_to_greeter_page(false);
}
}
@@ -165,6 +196,11 @@ impl Window {
}
}
+ /// Whether this window has sessions.
+ pub fn has_sessions(&self) -> bool {
+ self.imp().sessions.pages().n_items() > 0
+ }
+
pub fn save_window_size(&self) -> Result<(), glib::BoolError> {
let settings = Application::default().settings();
@@ -189,12 +225,17 @@ impl Window {
self.set_property("maximized", &is_maximized);
}
- /// Change the default widget of the window based on the visible child
- /// If the login screen is visible, its login button becomes the default
- /// widget
+ /// Change the default widget of the window based on the visible child.
+ ///
+ /// These are the default widgets:
+ /// - `Greeter` screen => `Login` button.
+ /// - `Login screen` => `Next` button.
fn set_default_by_child(&self) {
let priv_ = self.imp();
- if priv_.main_stack.visible_child() == Some(priv_.login.get().upcast()) {
+
+ if priv_.main_stack.visible_child() == Some(priv_.greeter.get().upcast()) {
+ self.set_default_widget(Some(&priv_.greeter.default_widget()));
+ } else if priv_.main_stack.visible_child() == Some(priv_.login.get().upcast()) {
self.set_default_widget(Some(&priv_.login.default_widget()));
} else {
self.set_default_widget(gtk::Widget::NONE);
@@ -206,15 +247,17 @@ impl Window {
priv_.main_stack.set_visible_child(&priv_.sessions.get());
}
- pub fn switch_to_login_page(&self, clean: bool) {
+ pub fn switch_to_login_page(&self) {
+ let priv_ = self.imp();
+ priv_.main_stack.set_visible_child(&*priv_.login);
+ }
+
+ pub fn switch_to_greeter_page(&self, clean: bool) {
let priv_ = self.imp();
if clean {
priv_.login.clean();
}
- priv_
- .login
- .show_back_to_session_button(priv_.sessions.get().pages().n_items() > 0);
- priv_.main_stack.set_visible_child(&*priv_.login);
+ priv_.main_stack.set_visible_child(&*priv_.greeter);
}
/// This appends a new error to the list of errors
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]