login: Port to glib::Properties macro

This commit is contained in:
Kévin Commaille 2023-12-09 19:07:29 +01:00
parent 122484d3e1
commit e13604e5bc
No known key found for this signature in database
GPG Key ID: 29A48C1F03620416
5 changed files with 143 additions and 314 deletions

View File

@ -5,13 +5,15 @@ use gtk::{gdk, glib, prelude::*, CompositeTemplate};
mod imp {
use glib::subclass::InitializingObject;
use once_cell::sync::Lazy;
use super::*;
#[derive(Debug, Default, CompositeTemplate)]
#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
#[template(resource = "/org/gnome/Fractal/ui/login/advanced_dialog.ui")]
#[properties(wrapper_type = super::LoginAdvancedDialog)]
pub struct LoginAdvancedDialog {
/// Whether auto-discovery is enabled.
#[property(get, set, default = true)]
pub autodiscovery: Cell<bool>,
}
@ -37,32 +39,8 @@ mod imp {
}
}
impl ObjectImpl for LoginAdvancedDialog {
fn properties() -> &'static [glib::ParamSpec] {
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
vec![glib::ParamSpecBoolean::builder("autodiscovery")
.default_value(true)
.construct()
.build()]
});
PROPERTIES.as_ref()
}
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
match pspec.name() {
"autodiscovery" => self.obj().autodiscovery().to_value(),
_ => unimplemented!(),
}
}
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
match pspec.name() {
"autodiscovery" => self.obj().set_autodiscovery(value.get().unwrap()),
_ => unimplemented!(),
}
}
}
#[glib::derived_properties]
impl ObjectImpl for LoginAdvancedDialog {}
impl WidgetImpl for LoginAdvancedDialog {}
impl WindowImpl for LoginAdvancedDialog {}
@ -82,19 +60,6 @@ impl LoginAdvancedDialog {
.build()
}
/// Whether auto-discovery is enabled.
pub fn autodiscovery(&self) -> bool {
self.imp().autodiscovery.get()
}
/// Set whether auto-discovery is enabled.
pub fn set_autodiscovery(&self, autodiscovery: bool) {
let imp = self.imp();
imp.autodiscovery.set(autodiscovery);
self.notify("autodiscovery");
}
pub async fn run_future(&self) {
let (sender, receiver) = futures_channel::oneshot::channel();
let sender = Cell::new(Some(sender));

View File

@ -16,12 +16,12 @@ use crate::{
mod imp {
use glib::subclass::InitializingObject;
use once_cell::sync::Lazy;
use super::*;
#[derive(Debug, Default, CompositeTemplate)]
#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
#[template(resource = "/org/gnome/Fractal/ui/login/homeserver_page.ui")]
#[properties(wrapper_type = super::LoginHomeserverPage)]
pub struct LoginHomeserverPage {
#[template_child]
pub homeserver_entry: TemplateChild<adw::EntryRow>,
@ -30,6 +30,7 @@ mod imp {
#[template_child]
pub next_button: TemplateChild<SpinnerButton>,
/// The parent `Login` object.
#[property(get, set = Self::set_login, explicit_notify, nullable)]
pub login: BoundObjectWeakRef<Login>,
}
@ -49,31 +50,35 @@ mod imp {
}
}
impl ObjectImpl for LoginHomeserverPage {
fn properties() -> &'static [glib::ParamSpec] {
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> =
Lazy::new(|| vec![glib::ParamSpecObject::builder::<Login>("login").build()]);
PROPERTIES.as_ref()
}
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
match pspec.name() {
"login" => self.obj().login().to_value(),
_ => unimplemented!(),
}
}
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
match pspec.name() {
"login" => self.obj().set_login(value.get().unwrap()),
_ => unimplemented!(),
}
}
}
#[glib::derived_properties]
impl ObjectImpl for LoginHomeserverPage {}
impl WidgetImpl for LoginHomeserverPage {}
impl BinImpl for LoginHomeserverPage {}
impl LoginHomeserverPage {
/// Set the parent `Login` object.
fn set_login(&self, login: Option<&Login>) {
let obj = self.obj();
self.login.disconnect_signals();
if let Some(login) = login {
let handler = login.connect_notify_local(
Some("autodiscovery"),
clone!(@weak obj => move |_, _| {
obj.update_next_state();
obj.update_text();
}),
);
self.login.set(login, vec![handler]);
}
obj.update_next_state();
obj.update_text();
}
}
}
glib::wrapper! {
@ -88,33 +93,6 @@ impl LoginHomeserverPage {
glib::Object::new()
}
/// The parent `Login` object.
pub fn login(&self) -> Option<Login> {
self.imp().login.obj()
}
/// Set the parent `Login` object.
fn set_login(&self, login: Option<&Login>) {
let imp = self.imp();
imp.login.disconnect_signals();
if let Some(login) = login {
let handler = login.connect_notify_local(
Some("autodiscovery"),
clone!(@weak self as obj => move |_, _| {
obj.update_next_state();
obj.update_text();
}),
);
imp.login.set(login, vec![handler]);
}
self.update_next_state();
self.update_text();
}
/// Update the text of this page according to the current settings.
fn update_text(&self) {
let Some(login) = self.login() else {

View File

@ -86,18 +86,22 @@ impl From<IdpBrand> for &str {
}
mod imp {
use std::cell::{Cell, RefCell};
use std::cell::{Cell, OnceCell};
use glib::subclass::InitializingObject;
use once_cell::sync::Lazy;
use super::*;
#[derive(Debug, Default, CompositeTemplate)]
#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
#[template(resource = "/org/gnome/Fractal/ui/login/idp_button.ui")]
#[properties(wrapper_type = super::IdpButton)]
pub struct IdpButton {
/// The brand of this button.
#[property(get, construct_only, builder(IdpBrand::default()))]
pub brand: Cell<IdpBrand>,
pub id: RefCell<Option<String>>,
/// The ID of the identity provider of this button.
#[property(get, set = Self::set_id, construct_only)]
pub id: OnceCell<String>,
}
#[glib::object_subclass]
@ -116,46 +120,8 @@ mod imp {
}
}
#[glib::derived_properties]
impl ObjectImpl for IdpButton {
fn properties() -> &'static [glib::ParamSpec] {
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
vec![
glib::ParamSpecEnum::builder::<IdpBrand>("brand")
.construct_only()
.build(),
glib::ParamSpecString::builder("id")
.construct_only()
.build(),
]
});
PROPERTIES.as_ref()
}
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
let obj = self.obj();
match pspec.name() {
"id" => obj.id().unwrap().to_value(),
"brand" => obj.brand().to_value(),
_ => unimplemented!(),
}
}
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
let obj = self.obj();
match pspec.name() {
"brand" => {
obj.set_brand(value.get().unwrap());
}
"id" => {
obj.set_id(value.get().unwrap());
}
_ => unimplemented!(),
};
}
fn constructed(&self) {
self.parent_constructed();
let obj = self.obj();
@ -168,6 +134,15 @@ mod imp {
impl WidgetImpl for IdpButton {}
impl ButtonImpl for IdpButton {}
impl IdpButton {
/// Set the id of the identity-provider represented by this button.
fn set_id(&self, id: String) {
self.obj()
.set_action_target_value(Some(&Some(&id).to_variant()));
self.id.set(id).unwrap();
}
}
}
glib::wrapper! {
@ -177,31 +152,6 @@ glib::wrapper! {
}
impl IdpButton {
pub fn update_icon(&self) {
self.set_icon_name(self.brand().icon());
}
/// Set the id of the identity-provider represented by this button.
pub fn set_id(&self, id: String) {
self.set_action_target_value(Some(&Some(&id).to_variant()));
self.imp().id.replace(Some(id));
}
/// Set the brand of this button.
pub fn set_brand(&self, brand: IdpBrand) {
self.imp().brand.replace(brand);
}
/// The id of the identity-provider represented by this button.
pub fn id(&self) -> Option<String> {
self.imp().id.borrow().clone()
}
/// The brand of this button.
pub fn brand(&self) -> IdpBrand {
self.imp().brand.get()
}
pub fn new_from_identity_provider(idp: &IdentityProvider) -> Option<Self> {
let gidp: IdpBrand = idp.brand.as_ref()?.try_into().ok()?;
@ -212,4 +162,8 @@ impl IdpButton {
.build(),
)
}
pub fn update_icon(&self) {
self.set_icon_name(self.brand().icon());
}
}

View File

@ -12,12 +12,12 @@ use crate::{
mod imp {
use glib::subclass::InitializingObject;
use once_cell::sync::Lazy;
use super::*;
#[derive(Debug, Default, CompositeTemplate)]
#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
#[template(resource = "/org/gnome/Fractal/ui/login/method_page.ui")]
#[properties(wrapper_type = super::LoginMethodPage)]
pub struct LoginMethodPage {
#[template_child]
pub title: TemplateChild<gtk::Label>,
@ -32,6 +32,7 @@ mod imp {
#[template_child]
pub next_button: TemplateChild<SpinnerButton>,
/// The parent `Login` object.
#[property(get, set = Self::set_login, nullable)]
pub login: BoundObjectWeakRef<Login>,
}
@ -51,31 +52,37 @@ mod imp {
}
}
impl ObjectImpl for LoginMethodPage {
fn properties() -> &'static [glib::ParamSpec] {
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> =
Lazy::new(|| vec![glib::ParamSpecObject::builder::<Login>("login").build()]);
PROPERTIES.as_ref()
}
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
match pspec.name() {
"login" => self.obj().login().to_value(),
_ => unimplemented!(),
}
}
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
match pspec.name() {
"login" => self.obj().set_login(value.get().unwrap()),
_ => unimplemented!(),
}
}
}
#[glib::derived_properties]
impl ObjectImpl for LoginMethodPage {}
impl WidgetImpl for LoginMethodPage {}
impl BinImpl for LoginMethodPage {}
impl LoginMethodPage {
/// Set the parent `Login` object.
fn set_login(&self, login: Option<&Login>) {
let obj = self.obj();
self.login.disconnect_signals();
if let Some(login) = login {
let domain_handler = login.connect_domain_notify(clone!(@weak obj => move |_| {
obj.update_domain_name();
}));
let login_types_handler =
login.connect_login_types_notify(clone!(@weak obj => move |_| {
obj.update_sso();
}));
self.login
.set(login, vec![domain_handler, login_types_handler]);
}
obj.update_domain_name();
obj.update_sso();
obj.update_next_state();
}
}
}
glib::wrapper! {
@ -89,39 +96,6 @@ impl LoginMethodPage {
pub fn new() -> Self {
glib::Object::new()
}
/// The parent `Login` object.
pub fn login(&self) -> Option<Login> {
self.imp().login.obj()
}
/// Set the parent `Login` object.
fn set_login(&self, login: Option<&Login>) {
let imp = self.imp();
imp.login.disconnect_signals();
if let Some(login) = login {
let domain_handler = login.connect_notify_local(
Some("domain"),
clone!(@weak self as obj => move |_, _| {
obj.update_domain_name();
}),
);
let login_types_handler = login.connect_notify_local(
Some("login-types"),
clone!(@weak self as obj => move |_, _| {
obj.update_sso();
}),
);
imp.login
.set(login, vec![domain_handler, login_types_handler]);
}
self.update_domain_name();
self.update_sso();
self.update_next_state();
}
/// The username entered by the user.
pub fn username(&self) -> String {
@ -162,7 +136,7 @@ impl LoginMethodPage {
};
let imp = self.imp();
let login_types = login.login_types();
let login_types = login.login_types().0;
let sso_login = match login_types.into_iter().find_map(|t| match t {
LoginType::Sso(sso) => Some(sso),
_ => None,

View File

@ -26,7 +26,7 @@ use crate::{
spawn, spawn_tokio, toast, Application, Window, RUNTIME,
};
#[derive(Clone, Debug, glib::Boxed)]
#[derive(Clone, Debug, Default, glib::Boxed)]
#[boxed_type(name = "BoxedLoginTypes")]
pub struct BoxedLoginTypes(Vec<LoginType>);
@ -52,12 +52,12 @@ mod imp {
use std::cell::{Cell, RefCell};
use glib::{subclass::InitializingObject, SignalHandlerId};
use once_cell::sync::Lazy;
use super::*;
#[derive(Debug, Default, CompositeTemplate)]
#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
#[template(resource = "/org/gnome/Fractal/ui/login/mod.ui")]
#[properties(wrapper_type = super::Login)]
pub struct Login {
#[template_child]
pub back_button: TemplateChild<gtk::Button>,
@ -77,12 +77,16 @@ mod imp {
pub logged_out_source_id: RefCell<Option<SignalHandlerId>>,
pub ready_source_id: RefCell<Option<SignalHandlerId>>,
/// Whether auto-discovery is enabled.
#[property(get, set = Self::set_autodiscovery, construct, explicit_notify, default = true)]
pub autodiscovery: Cell<bool>,
/// The login types supported by the homeserver.
pub login_types: RefCell<Vec<LoginType>>,
#[property(get)]
pub login_types: RefCell<BoxedLoginTypes>,
/// The domain of the homeserver to log into.
#[property(get = Self::domain, type = Option<String>)]
pub domain: RefCell<Option<OwnedServerName>>,
/// The URL of the homeserver to log into.
#[property(get = Self::homeserver, type = Option<String>)]
pub homeserver: RefCell<Option<Url>>,
/// The Matrix client used to log in.
pub client: RefCell<Option<Client>>,
@ -121,47 +125,8 @@ mod imp {
}
}
#[glib::derived_properties]
impl ObjectImpl for Login {
fn properties() -> &'static [glib::ParamSpec] {
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
vec![
glib::ParamSpecString::builder("domain").read_only().build(),
glib::ParamSpecString::builder("homeserver")
.read_only()
.build(),
glib::ParamSpecBoolean::builder("autodiscovery")
.default_value(true)
.construct()
.explicit_notify()
.build(),
glib::ParamSpecBoxed::builder::<BoxedLoginTypes>("login-types")
.read_only()
.build(),
]
});
PROPERTIES.as_ref()
}
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
let obj = self.obj();
match pspec.name() {
"domain" => obj.domain().to_value(),
"homeserver" => obj.homeserver_pretty().to_value(),
"autodiscovery" => obj.autodiscovery().to_value(),
"login-types" => BoxedLoginTypes(obj.login_types()).to_value(),
_ => unimplemented!(),
}
}
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
match pspec.name() {
"autodiscovery" => self.obj().set_autodiscovery(value.get().unwrap()),
_ => unimplemented!(),
}
}
fn constructed(&self) {
let obj = self.obj();
obj.action_set_enabled("login.next", false);
@ -190,12 +155,43 @@ mod imp {
}
impl WidgetImpl for Login {}
impl BinImpl for Login {}
impl Login {
/// Set whether auto-discovery is enabled.
pub fn set_autodiscovery(&self, autodiscovery: bool) {
if self.autodiscovery.get() == autodiscovery {
return;
}
self.autodiscovery.set(autodiscovery);
self.obj().notify_autodiscovery();
}
/// The domain of the homeserver to log into.
///
/// If autodiscovery is enabled, this is the server name, otherwise,
/// this is the prettified homeserver URL.
fn domain(&self) -> Option<String> {
if self.autodiscovery.get() {
self.domain.borrow().clone().map(Into::into)
} else {
self.homeserver()
}
}
/// The pretty-formatted URL of the homeserver to log into.
fn homeserver(&self) -> Option<String> {
self.homeserver
.borrow()
.as_ref()
.map(|url| url.as_ref().trim_end_matches('/').to_owned())
}
}
}
glib::wrapper! {
/// A widget handling the login flows.
/// A widget managing the login flows.
pub struct Login(ObjectSubclass<imp::Login>)
@extends gtk::Widget, adw::Bin, @implements gtk::Accessible;
}
@ -231,7 +227,7 @@ impl Login {
fn set_client(&self, client: Option<Client>) {
let homeserver = client.as_ref().map(|client| client.homeserver());
self.set_homeserver(homeserver);
self.set_homeserver_url(homeserver);
self.imp().client.replace(client);
}
@ -253,18 +249,6 @@ impl Login {
}
}
/// The domain of the homeserver to log into.
///
/// If autodiscovery is enabled, this is the server name, otherwise, this is
/// the prettified homeserver URL.
pub fn domain(&self) -> Option<String> {
if self.autodiscovery() {
self.imp().domain.borrow().clone().map(Into::into)
} else {
self.homeserver_pretty()
}
}
fn set_domain(&self, domain: Option<OwnedServerName>) {
let imp = self.imp();
@ -277,58 +261,31 @@ impl Login {
}
/// The URL of the homeserver to log into.
pub fn homeserver(&self) -> Option<Url> {
pub fn homeserver_url(&self) -> Option<Url> {
self.imp().homeserver.borrow().clone()
}
/// The pretty-formatted URL of the homeserver to log into.
pub fn homeserver_pretty(&self) -> Option<String> {
let homeserver = self.homeserver();
homeserver
.as_ref()
.and_then(|url| url.as_ref().strip_suffix('/').map(ToOwned::to_owned))
.or_else(|| homeserver.as_ref().map(ToString::to_string))
}
/// Set the homeserver to log into.
pub fn set_homeserver(&self, homeserver: Option<Url>) {
pub fn set_homeserver_url(&self, homeserver: Option<Url>) {
let imp = self.imp();
if self.homeserver() == homeserver {
if self.homeserver_url() == homeserver {
return;
}
imp.homeserver.replace(homeserver);
self.notify("homeserver");
self.notify_homeserver();
if !self.autodiscovery() {
self.notify("domain");
self.notify_domain();
}
}
/// Whether auto-discovery is enabled.
pub fn autodiscovery(&self) -> bool {
self.imp().autodiscovery.get()
}
/// Set whether auto-discovery is enabled
pub fn set_autodiscovery(&self, autodiscovery: bool) {
if self.autodiscovery() == autodiscovery {
return;
}
self.imp().autodiscovery.set(autodiscovery);
self.notify("autodiscovery");
}
/// The login types supported by the homeserver.
pub fn login_types(&self) -> Vec<LoginType> {
self.imp().login_types.borrow().clone()
}
/// Set the login types supported by the homeserver.
fn set_login_types(&self, types: Vec<LoginType>) {
self.imp().login_types.replace(types);
self.notify("login-types");
self.imp().login_types.replace(BoxedLoginTypes(types));
self.notify_login_types();
}
/// Whether the password login type is supported.
@ -336,6 +293,7 @@ impl Login {
self.imp()
.login_types
.borrow()
.0
.iter()
.any(|t| matches!(t, LoginType::Password(_)))
}
@ -577,7 +535,7 @@ impl Login {
self.set_autodiscovery(true);
self.set_login_types(vec![]);
self.set_domain(None);
self.set_homeserver(None);
self.set_homeserver_url(None);
self.drop_client();
self.drop_session();