[fractal] account-settings: Use AdwPasswordEntryRow
- From: Kévin Commaille <kcommaille src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [fractal] account-settings: Use AdwPasswordEntryRow
- Date: Mon, 10 Oct 2022 11:13:58 +0000 (UTC)
commit 6e82ea2f60bc2ec1d79be6c2e52a7d6174bfd84a
Author: Kévin Commaille <zecakeh tedomum fr>
Date: Fri Oct 7 18:54:13 2022 +0200
account-settings: Use AdwPasswordEntryRow
data/resources/resources.gresource.xml | 1 -
data/resources/style.css | 93 ++----
.../ui/account-settings-change-password-subpage.ui | 78 ++++-
.../account-settings-import-export-keys-subpage.ui | 53 ++-
data/resources/ui/components-password-entry-row.ui | 100 ------
src/components/mod.rs | 2 -
src/components/password_entry_row.rs | 372 ---------------------
.../security_page/import_export_keys_subpage.rs | 47 ++-
.../user_page/change_password_subpage.rs | 138 ++++----
9 files changed, 225 insertions(+), 659 deletions(-)
---
diff --git a/data/resources/resources.gresource.xml b/data/resources/resources.gresource.xml
index 1ab003879..73bd9ead8 100644
--- a/data/resources/resources.gresource.xml
+++ b/data/resources/resources.gresource.xml
@@ -69,7 +69,6 @@
<file compressed="true" preprocess="xml-stripblanks"
alias="components-loading-listbox-row.ui">ui/components-loading-listbox-row.ui</file>
<file compressed="true" preprocess="xml-stripblanks"
alias="components-location-viewer.ui">ui/components-location-viewer.ui</file>
<file compressed="true" preprocess="xml-stripblanks"
alias="components-media-content-viewer.ui">ui/components-media-content-viewer.ui</file>
- <file compressed="true" preprocess="xml-stripblanks"
alias="components-password-entry-row.ui">ui/components-password-entry-row.ui</file>
<file compressed="true" preprocess="xml-stripblanks"
alias="components-reaction-chooser.ui">ui/components-reaction-chooser.ui</file>
<file compressed="true" preprocess="xml-stripblanks"
alias="components-video-player.ui">ui/components-video-player.ui</file>
<file compressed="true" preprocess="xml-stripblanks"
alias="content-completion-popover.ui">ui/content-completion-popover.ui</file>
diff --git a/data/resources/style.css b/data/resources/style.css
index b3185398b..a7d142847 100644
--- a/data/resources/style.css
+++ b/data/resources/style.css
@@ -67,10 +67,35 @@ button.row {
border-spacing: 24px;
}
+.form-page .card {
+ padding: 2px;
+}
+
+.form-page levelbar.discrete block {
+ min-height: 5px;
+}
+
box.paragraphs {
border-spacing: 12px;
}
+levelbar.discrete.accent block.filled {
+ background-color: @accent_color;
+}
+
+levelbar.discrete.success block.filled {
+ background-color: @success_color;
+}
+
+levelbar.discrete.warning block.filled {
+ background-color: @warning_color;
+}
+
+levelbar.discrete.error block.filled {
+ background-color: @error_color;
+}
+
+
/* Components */
.inline-pill {
@@ -117,74 +142,6 @@ box.paragraphs {
background-color: @yellow_5;
}
-row.entry {
- transition-property: outline, outline-width, outline-offset, outline-color;
- transition-duration: 300ms;
- animation-timing-function: ease-in-out;
- outline: 0 solid transparent;
- outline-offset: 2px;
-}
-
-row.entry:focus-within {
- outline-color: alpha(@accent_color, 0.5);
- outline-width: 2px;
- outline-offset: -2px;
-}
-
-row.entry.success:focus-within {
- outline-color: @success_color;
-}
-
-row.entry.warning:focus-within {
- outline-color: @warning_color;
-}
-
-row.entry.error:focus-within {
- outline-color: @error_color;
-}
-
-row.entry .hint {
- font-size: 0.8em;
-}
-
-row.entry .header {
- margin-top: 6px;
- margin-bottom: 6px;
-}
-
-row.entry text:disabled,
-row.entry text placeholder {
- opacity: 0.5;
-}
-
-row.entry levelbar.discrete block {
- min-height: 5px;
-}
-
-row.entry levelbar.discrete block.filled {
- background-color: alpha(currentColor, 0.5);
-}
-
-row.entry.accent levelbar.discrete block.filled {
- background-color: @accent_color;
-}
-
-row.entry.success levelbar.discrete block.filled {
- background-color: @success_color;
-}
-
-row.entry.warning levelbar.discrete block.filled {
- background-color: @warning_color;
-}
-
-row.entry.error levelbar.discrete block.filled {
- background-color: @error_color;
-}
-
-row .heading {
- font-weight: 600;
-}
-
media-content-viewer controls {
min-width: 300px;
}
diff --git a/data/resources/ui/account-settings-change-password-subpage.ui
b/data/resources/ui/account-settings-change-password-subpage.ui
index 10e8b9169..a3578656f 100644
--- a/data/resources/ui/account-settings-change-password-subpage.ui
+++ b/data/resources/ui/account-settings-change-password-subpage.ui
@@ -76,18 +76,80 @@
</object>
</child>
<child>
- <object class="GtkListBox">
- <style>
- <class name="boxed-list"/>
- </style>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
<child>
- <object class="ComponentsPasswordEntryRow" id="password">
- <property name="title" translatable="yes">New Password</property>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkListBox">
+ <style>
+ <class name="boxed-list"/>
+ </style>
+ <child>
+ <object class="AdwPasswordEntryRow" id="password">
+ <property name="title" translatable="yes">New Password</property>
+ <signal name="entry-activated" handler="handle_proceed" swapped="yes"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLevelBar" id="password_progress">
+ <property name="margin-top">2</property>
+ <property name="margin-bottom">1</property>
+ <property name="mode">discrete</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkRevealer" id="password_error_revealer">
+ <property name="child">
+ <object class="GtkLabel" id="password_error">
+ <style>
+ <class name="caption"/>
+ </style>
+ <property name="wrap">True</property>
+ <property name="wrap-mode">word-char</property>
+ <property name="xalign">0.0</property>
+ </object>
+ </property>
+ </object>
+ </child>
</object>
</child>
<child>
- <object class="ComponentsPasswordEntryRow" id="confirm_password">
- <property name="title" translatable="yes">Confirm New Password</property>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkListBox">
+ <style>
+ <class name="boxed-list"/>
+ </style>
+ <child>
+ <object class="AdwPasswordEntryRow" id="confirm_password">
+ <property name="title" translatable="yes">Confirm New Password</property>
+ <signal name="entry-activated" handler="handle_proceed" swapped="yes"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkRevealer" id="confirm_password_error_revealer">
+ <property name="child">
+ <object class="GtkLabel" id="confirm_password_error">
+ <style>
+ <class name="caption"/>
+ </style>
+ <property name="wrap">True</property>
+ <property name="wrap-mode">word-char</property>
+ <property name="xalign">0.0</property>
+ </object>
+ </property>
+ </object>
+ </child>
</object>
</child>
</object>
diff --git a/data/resources/ui/account-settings-import-export-keys-subpage.ui
b/data/resources/ui/account-settings-import-export-keys-subpage.ui
index 0618e8013..fa866ab33 100644
--- a/data/resources/ui/account-settings-import-export-keys-subpage.ui
+++ b/data/resources/ui/account-settings-import-export-keys-subpage.ui
@@ -66,20 +66,53 @@
</object>
</child>
<child>
- <object class="GtkListBox">
- <style>
- <class name="boxed-list"/>
- </style>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
<child>
- <object class="ComponentsPasswordEntryRow" id="passphrase">
- <property name="title" translatable="yes">Passphrase</property>
- <signal name="activated" handler="handle_proceed" swapped="yes"/>
+ <object class="GtkListBox">
+ <style>
+ <class name="boxed-list"/>
+ </style>
+ <child>
+ <object class="AdwPasswordEntryRow" id="passphrase">
+ <property name="title" translatable="yes">Passphrase</property>
+ <signal name="entry-activated" handler="handle_proceed" swapped="yes"/>
+ </object>
+ </child>
</object>
</child>
<child>
- <object class="ComponentsPasswordEntryRow" id="confirm_passphrase">
- <property name="title" translatable="yes">Confirm Passphrase</property>
- <signal name="activated" handler="handle_proceed" swapped="yes"/>
+ <object class="GtkBox" id="confirm_passphrase_box">
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkListBox">
+ <style>
+ <class name="boxed-list"/>
+ </style>
+ <child>
+ <object class="AdwPasswordEntryRow" id="confirm_passphrase">
+ <property name="title" translatable="yes">Confirm Passphrase</property>
+ <signal name="entry-activated" handler="handle_proceed" swapped="yes"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkRevealer" id="confirm_passphrase_error_revealer">
+ <property name="child">
+ <object class="GtkLabel" id="confirm_passphrase_error">
+ <style>
+ <class name="caption"/>
+ </style>
+ <property name="wrap">True</property>
+ <property name="wrap-mode">word-char</property>
+ <property name="xalign">0.0</property>
+ </object>
+ </property>
+ </object>
+ </child>
</object>
</child>
</object>
diff --git a/src/components/mod.rs b/src/components/mod.rs
index 16bd75a92..ceeee07c9 100644
--- a/src/components/mod.rs
+++ b/src/components/mod.rs
@@ -12,7 +12,6 @@ mod label_with_widgets;
mod loading_listbox_row;
mod location_viewer;
mod media_content_viewer;
-mod password_entry_row;
mod pill;
mod reaction_chooser;
mod room_title;
@@ -35,7 +34,6 @@ pub use self::{
loading_listbox_row::LoadingListBoxRow,
location_viewer::LocationViewer,
media_content_viewer::{ContentType, MediaContentViewer},
- password_entry_row::PasswordEntryRow,
pill::Pill,
reaction_chooser::ReactionChooser,
room_title::RoomTitle,
diff --git a/src/session/account_settings/security_page/import_export_keys_subpage.rs
b/src/session/account_settings/security_page/import_export_keys_subpage.rs
index e4fc9608c..6dc8750db 100644
--- a/src/session/account_settings/security_page/import_export_keys_subpage.rs
+++ b/src/session/account_settings/security_page/import_export_keys_subpage.rs
@@ -9,10 +9,7 @@ use log::error;
use matrix_sdk::encryption::{KeyExportError, RoomKeyImportError};
use crate::{
- components::{PasswordEntryRow, SpinnerButton},
- i18n::ngettext_f,
- session::Session,
- spawn, spawn_tokio, toast,
+ components::SpinnerButton, i18n::ngettext_f, session::Session, spawn, spawn_tokio, toast,
};
#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy, glib::Enum)]
@@ -47,9 +44,15 @@ mod imp {
#[template_child]
pub instructions: TemplateChild<gtk::Label>,
#[template_child]
- pub passphrase: TemplateChild<PasswordEntryRow>,
+ pub passphrase: TemplateChild<adw::PasswordEntryRow>,
#[template_child]
- pub confirm_passphrase: TemplateChild<PasswordEntryRow>,
+ pub confirm_passphrase_box: TemplateChild<gtk::Box>,
+ #[template_child]
+ pub confirm_passphrase: TemplateChild<adw::PasswordEntryRow>,
+ #[template_child]
+ pub confirm_passphrase_error_revealer: TemplateChild<gtk::Revealer>,
+ #[template_child]
+ pub confirm_passphrase_error: TemplateChild<gtk::Label>,
#[template_child]
pub file_row: TemplateChild<adw::ActionRow>,
#[template_child]
@@ -141,18 +144,9 @@ mod imp {
self.passphrase
.connect_changed(clone!(@weak obj => move|_| {
- obj.update_button();
+ obj.validate_passphrase_confirmation();
}));
- self.confirm_passphrase
- .connect_focused(clone!(@weak obj => move |entry, focused| {
- if focused {
- obj.validate_passphrase_confirmation();
- } else {
- entry.remove_css_class("warning");
- entry.remove_css_class("success");
- }
- }));
self.confirm_passphrase
.connect_changed(clone!(@weak obj => move|_| {
obj.validate_passphrase_confirmation();
@@ -238,7 +232,7 @@ impl ImportExportKeysSubpage {
priv_.instructions.set_label(&gettext(
"The backup must be stored in a safe place and must be protected with a strong passphrase
that will be used to encrypt the data.",
));
- priv_.confirm_passphrase.show();
+ priv_.confirm_passphrase_box.show();
priv_.proceed_button.set_label(&gettext("Export Keys"));
} else {
priv_
@@ -250,7 +244,7 @@ impl ImportExportKeysSubpage {
priv_.instructions.set_label(&gettext(
"Enter the passphrase provided when the backup file was created.",
));
- priv_.confirm_passphrase.hide();
+ priv_.confirm_passphrase_box.hide();
priv_.proceed_button.set_label(&gettext("Import Keys"));
}
@@ -313,24 +307,27 @@ impl ImportExportKeysSubpage {
fn validate_passphrase_confirmation(&self) {
let priv_ = self.imp();
let entry = &priv_.confirm_passphrase;
+ let revealer = &priv_.confirm_passphrase_error_revealer;
+ let label = &priv_.confirm_passphrase_error;
let passphrase = priv_.passphrase.text();
let confirmation = entry.text();
if confirmation.is_empty() {
- entry.set_hint("");
+ revealer.set_reveal_child(false);
entry.remove_css_class("success");
entry.remove_css_class("warning");
return;
}
if passphrase == confirmation {
- entry.set_hint("");
+ revealer.set_reveal_child(false);
entry.add_css_class("success");
entry.remove_css_class("warning");
} else {
+ label.set_label(&gettext("Passphrases do not match"));
+ revealer.set_reveal_child(true);
entry.remove_css_class("success");
entry.add_css_class("warning");
- entry.set_hint(&gettext("Passphrases do not match"));
}
self.update_button();
}
@@ -377,8 +374,8 @@ impl ImportExportKeysSubpage {
priv_.proceed_button.set_loading(true);
priv_.file_button.set_sensitive(false);
- priv_.passphrase.set_entry_sensitive(false);
- priv_.confirm_passphrase.set_entry_sensitive(false);
+ priv_.passphrase.set_sensitive(false);
+ priv_.confirm_passphrase.set_sensitive(false);
let encryption = self.session().unwrap().client().encryption();
@@ -439,7 +436,7 @@ impl ImportExportKeysSubpage {
}
priv_.proceed_button.set_loading(false);
priv_.file_button.set_sensitive(true);
- priv_.passphrase.set_entry_sensitive(true);
- priv_.confirm_passphrase.set_entry_sensitive(true);
+ priv_.passphrase.set_sensitive(true);
+ priv_.confirm_passphrase.set_sensitive(true);
}
}
diff --git a/src/session/account_settings/user_page/change_password_subpage.rs
b/src/session/account_settings/user_page/change_password_subpage.rs
index a5bc24079..e4c230813 100644
--- a/src/session/account_settings/user_page/change_password_subpage.rs
+++ b/src/session/account_settings/user_page/change_password_subpage.rs
@@ -17,7 +17,7 @@ use matrix_sdk::{
};
use crate::{
- components::{AuthDialog, AuthError, PasswordEntryRow, SpinnerButton},
+ components::{AuthDialog, AuthError, SpinnerButton},
session::Session,
spawn, toast,
utils::validate_password,
@@ -33,9 +33,19 @@ mod imp {
pub struct ChangePasswordSubpage {
pub session: WeakRef<Session>,
#[template_child]
- pub password: TemplateChild<PasswordEntryRow>,
+ pub password: TemplateChild<adw::PasswordEntryRow>,
#[template_child]
- pub confirm_password: TemplateChild<PasswordEntryRow>,
+ pub password_progress: TemplateChild<gtk::LevelBar>,
+ #[template_child]
+ pub password_error_revealer: TemplateChild<gtk::Revealer>,
+ #[template_child]
+ pub password_error: TemplateChild<gtk::Label>,
+ #[template_child]
+ pub confirm_password: TemplateChild<adw::PasswordEntryRow>,
+ #[template_child]
+ pub confirm_password_error_revealer: TemplateChild<gtk::Revealer>,
+ #[template_child]
+ pub confirm_password_error: TemplateChild<gtk::Label>,
#[template_child]
pub button: TemplateChild<SpinnerButton>,
}
@@ -48,6 +58,7 @@ mod imp {
fn class_init(klass: &mut Self::Class) {
Self::bind_template(klass);
+ Self::Type::bind_template_callbacks(klass);
}
fn instance_init(obj: &InitializingObject<Self>) {
@@ -94,67 +105,25 @@ mod imp {
fn constructed(&self, obj: &Self::Type) {
self.parent_constructed(obj);
- self.password.define_progress_steps(&[
- >k::LEVEL_BAR_OFFSET_LOW,
- "step2",
- "step3",
- >k::LEVEL_BAR_OFFSET_HIGH,
- >k::LEVEL_BAR_OFFSET_FULL,
- ]);
- self.password
- .connect_focused(clone!(@weak obj => move |entry, focused| {
- if focused {
- entry.set_progress_visible(true);
- obj.validate_password();
- } else {
- entry.remove_css_class("warning");
- entry.remove_css_class("success");
- if entry.text().is_empty() {
- entry.set_progress_visible(false);
- }
- }
- }));
- self.password
- .connect_activated(clone!(@weak obj => move|_| {
- spawn!(
- clone!(@weak obj => async move {
- obj.change_password().await;
- })
- );
- }));
+ self.password_progress.set_min_value(0.0);
+ self.password_progress.set_max_value(5.0);
+ self.password_progress
+ .add_offset_value(>k::LEVEL_BAR_OFFSET_LOW, 1.0);
+ self.password_progress.add_offset_value("step2", 2.0);
+ self.password_progress.add_offset_value("step3", 3.0);
+ self.password_progress
+ .add_offset_value(>k::LEVEL_BAR_OFFSET_HIGH, 4.0);
+ self.password_progress
+ .add_offset_value(>k::LEVEL_BAR_OFFSET_FULL, 5.0);
+
self.password.connect_changed(clone!(@weak obj => move|_| {
obj.validate_password();
}));
- self.confirm_password
- .connect_focused(clone!(@weak obj => move |entry, focused| {
- if focused {
- obj.validate_password_confirmation();
- } else {
- entry.remove_css_class("warning");
- entry.remove_css_class("success");
- }
- }));
- self.confirm_password
- .connect_activated(clone!(@weak obj => move|_| {
- spawn!(
- clone!(@weak obj => async move {
- obj.change_password().await;
- })
- );
- }));
self.confirm_password
.connect_changed(clone!(@weak obj => move|_| {
obj.validate_password_confirmation();
}));
-
- self.button.connect_clicked(clone!(@weak obj => move|_| {
- spawn!(
- clone!(@weak obj => async move {
- obj.change_password().await;
- })
- );
- }));
}
}
@@ -168,6 +137,7 @@ glib::wrapper! {
@extends gtk::Widget, gtk::Box, @implements gtk::Accessible;
}
+#[gtk::template_callbacks]
impl ChangePasswordSubpage {
pub fn new(session: &Session) -> Self {
glib::Object::new(&[("session", session)]).expect("Failed to create ChangePasswordSubpage")
@@ -182,68 +152,83 @@ impl ChangePasswordSubpage {
}
fn validate_password(&self) {
- let entry = &self.imp().password;
+ let priv_ = self.imp();
+ let entry = &priv_.password;
+ let progress = &priv_.password_progress;
+ let revealer = &priv_.password_error_revealer;
+ let label = &priv_.password_error;
let password = entry.text();
if password.is_empty() {
- entry.set_hint("");
+ revealer.set_reveal_child(false);
entry.remove_css_class("success");
entry.remove_css_class("warning");
- entry.set_progress_value(0.0);
+ progress.set_value(0.0);
+ progress.remove_css_class("success");
+ progress.remove_css_class("warning");
self.update_button();
return;
}
let validity = validate_password(&password);
- entry.set_progress_value(validity.progress as f64 / 20.0);
+ progress.set_value(validity.progress as f64 / 20.0);
if validity.progress == 100 {
- entry.set_hint("");
+ revealer.set_reveal_child(false);
entry.add_css_class("success");
entry.remove_css_class("warning");
+ progress.add_css_class("success");
+ progress.remove_css_class("warning");
} else {
entry.remove_css_class("success");
entry.add_css_class("warning");
+ progress.remove_css_class("success");
+ progress.add_css_class("warning");
if !validity.has_length {
- entry.set_hint(&gettext("Password must be at least 8 characters long"));
+ label.set_label(&gettext("Password must be at least 8 characters long"));
} else if !validity.has_lowercase {
- entry.set_hint(&gettext(
+ label.set_label(&gettext(
"Password must have at least one lower-case letter",
));
} else if !validity.has_uppercase {
- entry.set_hint(&gettext(
+ label.set_label(&gettext(
"Password must have at least one upper-case letter",
));
} else if !validity.has_number {
- entry.set_hint(&gettext("Password must have at least one digit"));
+ label.set_label(&gettext("Password must have at least one digit"));
} else if !validity.has_symbol {
- entry.set_hint(&gettext("Password must have at least one symbol"));
+ label.set_label(&gettext("Password must have at least one symbol"));
}
+ revealer.set_reveal_child(true);
}
+
self.update_button();
}
fn validate_password_confirmation(&self) {
let priv_ = self.imp();
let entry = &priv_.confirm_password;
+ let revealer = &priv_.confirm_password_error_revealer;
+ let label = &priv_.confirm_password_error;
let password = priv_.password.text();
let confirmation = entry.text();
if confirmation.is_empty() {
- entry.set_hint("");
+ revealer.set_reveal_child(false);
entry.remove_css_class("success");
entry.remove_css_class("warning");
return;
}
if password == confirmation {
- entry.set_hint("");
+ revealer.set_reveal_child(false);
entry.add_css_class("success");
entry.remove_css_class("warning");
} else {
entry.remove_css_class("success");
entry.add_css_class("warning");
- entry.set_hint(&gettext("Passwords do not match"));
+ label.set_label(&gettext("Passwords do not match"));
+ revealer.set_reveal_child(true);
}
self.update_button();
}
@@ -260,6 +245,13 @@ impl ChangePasswordSubpage {
validate_password(&password).progress == 100 && password == confirmation
}
+ #[template_callback]
+ fn handle_proceed(&self) {
+ spawn!(clone!(@weak self as obj => async move {
+ obj.change_password().await;
+ }));
+ }
+
async fn change_password(&self) {
if !self.can_change_password() {
return;
@@ -269,8 +261,8 @@ impl ChangePasswordSubpage {
let password = priv_.password.text();
priv_.button.set_loading(true);
- priv_.password.set_entry_sensitive(false);
- priv_.confirm_password.set_entry_sensitive(false);
+ priv_.password.set_sensitive(false);
+ priv_.confirm_password.set_sensitive(false);
let session = self.session().unwrap();
let dialog = AuthDialog::new(
@@ -323,7 +315,7 @@ impl ChangePasswordSubpage {
},
}
priv_.button.set_loading(false);
- priv_.password.set_entry_sensitive(true);
- priv_.confirm_password.set_entry_sensitive(true);
+ priv_.password.set_sensitive(true);
+ priv_.confirm_password.set_sensitive(true);
}
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]