[fractal/fractal-next] toast: Simplify the API
- From: Julian Sparber <jsparber src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [fractal/fractal-next] toast: Simplify the API
- Date: Wed, 23 Feb 2022 17:06:55 +0000 (UTC)
commit d5b4b3f3f215e63375fdc75867b94b08c5bde06f
Author: Kévin Commaille <zecakeh tedomum fr>
Date: Wed Feb 16 13:22:21 2022 +0100
toast: Simplify the API
src/components/in_app_notification.rs | 2 +-
src/components/toast.rs | 137 ++++++++++++++++++---
src/login.rs | 20 +--
.../content/verification/session_verification.rs | 10 +-
src/session/mod.rs | 33 +----
src/session/room/event_actions.rs | 22 +---
src/session/room/mod.rs | 114 ++++++++---------
src/session/room_list.rs | 8 +-
src/session/verification/identity_verification.rs | 10 +-
src/window.rs | 16 +--
10 files changed, 192 insertions(+), 180 deletions(-)
---
diff --git a/src/components/in_app_notification.rs b/src/components/in_app_notification.rs
index 64dbf7edb..5a8f06f31 100644
--- a/src/components/in_app_notification.rs
+++ b/src/components/in_app_notification.rs
@@ -155,7 +155,7 @@ impl InAppNotification {
.as_ref()
.and_then(|error_list| error_list.item(0))
.and_then(|obj| obj.downcast::<Toast>().ok())
- .and_then(|error| error.widget())
+ .map(|error| error.widget())
{
if let Some(current_widget) = priv_.current_widget.take() {
priv_.box_.remove(¤t_widget);
diff --git a/src/components/toast.rs b/src/components/toast.rs
index 8a9f52d38..df52cbd42 100644
--- a/src/components/toast.rs
+++ b/src/components/toast.rs
@@ -1,15 +1,18 @@
-use gtk::{glib, subclass::prelude::*};
+use gtk::{glib, prelude::*, subclass::prelude::*};
-type WidgetBuilderFn = Box<dyn Fn(&super::Toast) -> Option<gtk::Widget> + 'static>;
+use crate::components::LabelWithWidgets;
mod imp {
use std::cell::RefCell;
+ use once_cell::sync::Lazy;
+
use super::*;
#[derive(Default)]
pub struct Toast {
- pub widget_builder: RefCell<Option<WidgetBuilderFn>>,
+ pub title: RefCell<Option<String>>,
+ pub widgets: RefCell<Vec<gtk::Widget>>,
}
#[glib::object_subclass]
@@ -19,7 +22,41 @@ mod imp {
type ParentType = glib::Object;
}
- impl ObjectImpl for Toast {}
+ impl ObjectImpl for Toast {
+ fn properties() -> &'static [glib::ParamSpec] {
+ static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
+ vec![glib::ParamSpecString::new(
+ "title",
+ "Title",
+ "The title of the toast",
+ None,
+ glib::ParamFlags::READWRITE | glib::ParamFlags::EXPLICIT_NOTIFY,
+ )]
+ });
+
+ PROPERTIES.as_ref()
+ }
+
+ fn set_property(
+ &self,
+ obj: &Self::Type,
+ _id: usize,
+ value: &glib::Value,
+ pspec: &glib::ParamSpec,
+ ) {
+ match pspec.name() {
+ "title" => obj.set_title(value.get().unwrap()),
+ _ => unimplemented!(),
+ }
+ }
+
+ fn property(&self, obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
+ match pspec.name() {
+ "title" => obj.title().to_value(),
+ _ => unimplemented!(),
+ }
+ }
+ }
}
glib::wrapper! {
@@ -28,22 +65,88 @@ glib::wrapper! {
}
impl Toast {
- pub fn new<F: Fn(&Self) -> Option<gtk::Widget> + 'static>(f: F) -> Self {
- let obj: Self = glib::Object::new(&[]).expect("Failed to create Toast");
- obj.set_widget_builder(f);
- obj
+ pub fn new(title: &str) -> Self {
+ glib::Object::new(&[("title", &title)]).expect("Failed to create Toast")
+ }
+
+ pub fn builder() -> ToastBuilder {
+ ToastBuilder::new()
+ }
+
+ pub fn title(&self) -> Option<String> {
+ self.imp().title.borrow().clone()
+ }
+
+ pub fn set_title(&self, title: Option<&str>) {
+ let priv_ = self.imp();
+ if priv_.title.borrow().as_deref() == title {
+ return;
+ }
+
+ priv_.title.replace(title.map(ToOwned::to_owned));
+ self.notify("title");
+ }
+
+ pub fn widgets(&self) -> Vec<gtk::Widget> {
+ self.imp().widgets.borrow().clone()
+ }
+
+ pub fn set_widgets(&self, widgets: &[&impl IsA<gtk::Widget>]) {
+ self.imp()
+ .widgets
+ .replace(widgets.iter().map(|w| w.upcast_ref().clone()).collect());
+ }
+
+ pub fn widget(&self) -> gtk::Widget {
+ if self.widgets().is_empty() {
+ gtk::Label::builder()
+ .wrap(true)
+ .label(&self.title().unwrap_or_default())
+ .build()
+ .upcast()
+ } else {
+ LabelWithWidgets::new(&self.title().unwrap_or_default(), self.widgets()).upcast()
+ }
+ }
+}
+
+impl From<Toast> for adw::Toast {
+ fn from(toast: Toast) -> Self {
+ if toast.widgets().is_empty() {
+ adw::Toast::new(&toast.title().unwrap_or_default())
+ } else {
+ // When AdwToast supports custom titles.
+ todo!()
+ }
+ }
+}
+
+#[derive(Debug, Default, Clone)]
+pub struct ToastBuilder {
+ title: Option<String>,
+ widgets: Option<Vec<gtk::Widget>>,
+}
+
+impl ToastBuilder {
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ pub fn title(mut self, title: &str) -> Self {
+ self.title = Some(title.to_owned());
+ self
}
- /// Set a function that builds the widget used to display this error in the
- /// UI
- pub fn set_widget_builder<F: Fn(&Self) -> Option<gtk::Widget> + 'static>(&self, f: F) {
- self.imp().widget_builder.replace(Some(Box::new(f)));
+ pub fn widgets(mut self, widgets: &[&impl IsA<gtk::Widget>]) -> Self {
+ self.widgets = Some(widgets.iter().map(|w| w.upcast_ref().clone()).collect());
+ self
}
- /// Produces a widget via the function set in `Self::set_widget_builder()`
- pub fn widget(&self) -> Option<gtk::Widget> {
- let widget_builder = self.imp().widget_builder.borrow();
- let widget_builder = widget_builder.as_ref()?;
- widget_builder(self)
+ pub fn build(&self) -> Toast {
+ let toast = Toast::new(self.title.as_ref().unwrap());
+ if let Some(widgets) = &self.widgets {
+ toast.set_widgets(widgets.iter().collect::<Vec<_>>().as_slice());
+ }
+ toast
}
}
diff --git a/src/login.rs b/src/login.rs
index 13692fe8d..5285dfac7 100644
--- a/src/login.rs
+++ b/src/login.rs
@@ -311,15 +311,7 @@ impl Login {
}
Err(error) => {
warn!("Failed to discover homeserver: {}", error);
- let error_string = error.to_user_facing();
-
- obj.parent_window().append_error(&Toast::new(move |_| {
- let error_label = gtk::Label::builder()
- .label(&error_string)
- .wrap(true)
- .build();
- Some(error_label.upcast())
- }));
+ obj.parent_window().append_error(&Toast::new(&error.to_user_facing()));
}
};
obj.unfreeze();
@@ -353,15 +345,7 @@ impl Login {
}
Err(error) => {
warn!("Failed to check homeserver: {}", error);
- let error_string = error.to_user_facing();
-
- obj.parent_window().append_error(&Toast::new(move |_| {
- let error_label = gtk::Label::builder()
- .label(&error_string)
- .wrap(true)
- .build();
- Some(error_label.upcast())
- }));
+ obj.parent_window().append_error(&Toast::new(&error.to_user_facing()));
}
};
obj.unfreeze();
diff --git a/src/session/content/verification/session_verification.rs
b/src/session/content/verification/session_verification.rs
index 4bf642c49..b9de6140e 100644
--- a/src/session/content/verification/session_verification.rs
+++ b/src/session/content/verification/session_verification.rs
@@ -315,16 +315,8 @@ impl SessionVerification {
};
if let Some(error_message) = error_message {
- let error = Toast::new(move |_| {
- let error_label = gtk::Label::builder()
- .label(&error_message)
- .wrap(true)
- .build();
- Some(error_label.upcast())
- });
-
if let Some(window) = obj.parent_window() {
- window.append_error(&error);
+ window.append_error(&Toast::new(&error_message));
}
} else {
// TODO tell user that the a crypto identity was created
diff --git a/src/session/mod.rs b/src/session/mod.rs
index 653e9cec8..9c39bf706 100644
--- a/src/session/mod.rs
+++ b/src/session/mod.rs
@@ -422,18 +422,10 @@ impl Session {
Ok(()) => None,
Err(error) => {
warn!("Couldn't store session: {:?}", error);
- let error_string = error.to_user_facing();
- Some(Toast::new(move |_| {
- let error_label = gtk::Label::builder()
- .label(
- &(gettext("Unable to store session")
- + ": "
- + &error_string),
- )
- .wrap(true)
- .build();
- Some(error_label.upcast())
- }))
+ Some(Toast::new(&gettext!(
+ "Unable to store session: {}",
+ &error.to_user_facing()
+ )))
}
}
} else {
@@ -453,15 +445,7 @@ impl Session {
priv_.logout_on_dispose.set(false);
- let error_string = error.to_user_facing();
-
- Some(Toast::new(move |_| {
- let error_label = gtk::Label::builder()
- .label(&error_string)
- .wrap(true)
- .build();
- Some(error_label.upcast())
- }))
+ Some(Toast::new(&error.to_user_facing()))
}
};
@@ -715,13 +699,8 @@ impl Session {
Ok(_) => self.cleanup_session(),
Err(error) => {
error!("Couldn’t logout the session {}", error);
- let error = Toast::new(move |_| {
- let label = gtk::Label::new(Some(&gettext("Failed to logout the session.")));
- Some(label.upcast())
- });
-
if let Some(window) = self.parent_window() {
- window.append_error(&error);
+ window.append_error(&Toast::new(&gettext("Failed to logout the session.")));
}
}
}
diff --git a/src/session/room/event_actions.rs b/src/session/room/event_actions.rs
index bfcba25da..8072008fb 100644
--- a/src/session/room/event_actions.rs
+++ b/src/session/room/event_actions.rs
@@ -195,16 +195,7 @@ where
Ok(res) => res,
Err(err) => {
error!("Could not get file: {}", err);
-
- let error_message = err.to_user_facing();
- let error = Toast::new(move |_| {
- let error_label = gtk::Label::builder()
- .label(&error_message)
- .wrap(true)
- .build();
- Some(error_label.upcast())
- });
- window.append_error(&error);
+ window.append_error(&Toast::new(&err.to_user_facing()));
return;
}
@@ -253,16 +244,7 @@ where
Ok(res) => res,
Err(err) => {
error!("Could not get file: {}", err);
-
- let error_message = err.to_user_facing();
- let error = Toast::new(move |_| {
- let error_label = gtk::Label::builder()
- .label(&error_message)
- .wrap(true)
- .build();
- Some(error_label.upcast())
- });
- window.append_error(&error);
+ window.append_error(&Toast::new(&err.to_user_facing()));
return;
}
diff --git a/src/session/room/mod.rs b/src/session/room/mod.rs
index 0996f0677..62c8f668c 100644
--- a/src/session/room/mod.rs
+++ b/src/session/room/mod.rs
@@ -56,7 +56,7 @@ pub use self::{
timeline::{Timeline, TimelineState},
};
use crate::{
- components::{LabelWithWidgets, Pill, Toast},
+ components::{Pill, Toast},
prelude::*,
session::{
avatar::update_room_avatar_from_file, room::member_list::MemberList, Avatar, Session, User,
@@ -406,18 +406,13 @@ impl Room {
}
Err(error) => {
error!("Couldn’t forget the room: {}", error);
- let error = Toast::new(
- clone!(@weak obj => @default-return None, move |_| {
- let error_message = gettext(
- "Failed to forget <widget>."
- );
- let room_pill = Pill::new();
- room_pill.set_room(Some(obj));
- let label = LabelWithWidgets::new(&error_message,
vec![room_pill]);
-
- Some(label.upcast())
- }),
- );
+
+ let room_pill = Pill::new();
+ room_pill.set_room(Some(obj.clone()));
+ let error = Toast::builder()
+ .title(&gettext("Failed to forget <widget>."))
+ .widgets(&[&room_pill])
+ .build();
if let Some(window) = obj.session().parent_window() {
window.append_error(&error);
@@ -557,20 +552,17 @@ impl Room {
Ok(_) => {},
Err(error) => {
error!("Couldn’t set the room category: {}", error);
- let error = Toast::new(
- clone!(@weak obj => @default-return None, move |_| {
- let error_message = gettext!(
- "Failed to move <widget> from {} to {}.",
- previous_category.to_string(),
- category.to_string()
- );
- let room_pill = Pill::new();
- room_pill.set_room(Some(obj));
- let label = LabelWithWidgets::new(&error_message,
vec![room_pill]);
-
- Some(label.upcast())
- }),
- );
+
+ let room_pill = Pill::new();
+ room_pill.set_room(Some(obj.clone()));
+ let error = Toast::builder()
+ .title(&gettext!(
+ "Failed to move <widget> from {} to {}.",
+ previous_category.to_string(),
+ category.to_string()
+ ))
+ .widgets(&[&room_pill])
+ .build();
if let Some(window) = obj.session().parent_window() {
window.append_error(&error);
@@ -1064,13 +1056,15 @@ impl Room {
Ok(result) => Ok(result),
Err(error) => {
error!("Accepting invitation failed: {}", error);
- let error = Toast::new(clone!(@strong self as room => move |_| {
- let error_message = gettext("Failed to accept invitation for <widget>. Try again
later.");
- let room_pill = Pill::new();
- room_pill.set_room(Some(room.clone()));
- let error_label = LabelWithWidgets::new(&error_message, vec![room_pill]);
- Some(error_label.upcast())
- }));
+
+ let room_pill = Pill::new();
+ room_pill.set_room(Some(self.clone()));
+ let error = Toast::builder()
+ .title(&gettext(
+ "Failed to accept invitation for <widget>. Try again later.",
+ ))
+ .widgets(&[&room_pill])
+ .build();
if let Some(window) = self.session().parent_window() {
window.append_error(&error);
@@ -1094,13 +1088,15 @@ impl Room {
Ok(result) => Ok(result),
Err(error) => {
error!("Rejecting invitation failed: {}", error);
- let error = Toast::new(clone!(@strong self as room => move |_| {
- let error_message = gettext("Failed to reject invitation for <widget>. Try again
later.");
- let room_pill = Pill::new();
- room_pill.set_room(Some(room.clone()));
- let error_label = LabelWithWidgets::new(&error_message, vec![room_pill]);
- Some(error_label.upcast())
- }));
+
+ let room_pill = Pill::new();
+ room_pill.set_room(Some(self.clone()));
+ let error = Toast::builder()
+ .title(&gettext(
+ "Failed to reject invitation for <widget>. Try again later.",
+ ))
+ .widgets(&[&room_pill])
+ .build();
if let Some(window) = self.session().parent_window() {
window.append_error(&error);
@@ -1241,25 +1237,23 @@ impl Room {
if !failed_invites.is_empty() {
let no_failed = failed_invites.len();
let first_failed = failed_invites.first().unwrap();
- let error = Toast::new(
- clone!(@strong self as room, @strong first_failed => move |_| {
- // TODO: should we show all the failed users?
- let error_message = if no_failed == 1 {
- gettext("Failed to invite <widget> to <widget>. Try again later.")
- } else if no_failed == 2 {
- gettext("Failed to invite <widget> and some other user to <widget>. Try
again later.")
- } else {
- gettext("Failed to invite <widget> and some other users to <widget>. Try
again later.")
- };
-
- let user_pill = Pill::new();
- user_pill.set_user(Some(first_failed.clone()));
- let room_pill = Pill::new();
- room_pill.set_room(Some(room.clone()));
- let error_label = LabelWithWidgets::new(&error_message, vec![user_pill,
room_pill]);
- Some(error_label.upcast())
- }),
- );
+
+ // TODO: should we show all the failed users?
+ let error_message = if no_failed == 1 {
+ gettext("Failed to invite <widget> to <widget>. Try again later.")
+ } else if no_failed == 2 {
+ gettext("Failed to invite <widget> and some other user to <widget>. Try again later.")
+ } else {
+ gettext("Failed to invite <widget> and some other users to <widget>. Try again later.")
+ };
+ let user_pill = Pill::new();
+ user_pill.set_user(Some(first_failed.clone()));
+ let room_pill = Pill::new();
+ room_pill.set_room(Some(self.clone()));
+ let error = Toast::builder()
+ .title(&error_message)
+ .widgets(&[&user_pill, &room_pill])
+ .build();
if let Some(window) = self.session().parent_window() {
window.append_error(&error);
diff --git a/src/session/room_list.rs b/src/session/room_list.rs
index b4c0c09cb..548bdbaad 100644
--- a/src/session/room_list.rs
+++ b/src/session/room_list.rs
@@ -323,13 +323,7 @@ impl RoomList {
obj.pending_rooms_remove(&identifier);
error!("Joining room {} failed: {}", identifier, error);
let error = Toast::new(
- clone!(@strong obj => move |_| {
- let error_message = gettext!(
- "Failed to join room {}. Try again later.", identifier
- );
- let error_label =
gtk::Label::builder().label(&error_message).wrap(true).build();
- Some(error_label.upcast())
- }),
+ &gettext!("Failed to join room {}. Try again later.", identifier)
);
if let Some(window) = obj.session().parent_window() {
diff --git a/src/session/verification/identity_verification.rs
b/src/session/verification/identity_verification.rs
index 9a3ea6dad..59e6e4fff 100644
--- a/src/session/verification/identity_verification.rs
+++ b/src/session/verification/identity_verification.rs
@@ -654,16 +654,8 @@ impl IdentityVerification {
gettext("An unknown error occurred during the verification process.")
});
- let error = Toast::new(move |_| {
- let error_label = gtk::Label::builder()
- .label(&error_message)
- .wrap(true)
- .build();
- Some(error_label.upcast())
- });
-
if let Some(window) = self.session().parent_window() {
- window.append_error(&error);
+ window.append_error(&Toast::new(&error_message));
}
}
diff --git a/src/window.rs b/src/window.rs
index ad4cf1f27..f92c9a54f 100644
--- a/src/window.rs
+++ b/src/window.rs
@@ -180,18 +180,10 @@ impl Window {
}
Err(error) => {
warn!("Failed to restore previous sessions: {:?}", error);
- let error_string = error.to_user_facing();
- self.append_error(&Toast::new(move |_| {
- let error_label = gtk::Label::builder()
- .label(
- &(gettext("Unable to restore previous sessions")
- + ": "
- + &error_string),
- )
- .wrap(true)
- .build();
- Some(error_label.upcast())
- }));
+ self.append_error(&Toast::new(&gettext!(
+ "Unable to restore previous sessions: {}",
+ &error.to_user_facing()
+ )));
}
}
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]