[fractal/fractal-next] components: Add widget for in-app-notification
- From: Julian Sparber <jsparber src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [fractal/fractal-next] components: Add widget for in-app-notification
- Date: Mon, 31 May 2021 14:24:37 +0000 (UTC)
commit b439abe55c4e1bcdb2892e0768505028f0155c68
Author: Julian Sparber <julian sparber net>
Date: Fri May 21 18:07:05 2021 +0200
components: Add widget for in-app-notification
data/resources/resources.gresource.xml | 1 +
data/resources/style.css | 7 +-
data/resources/ui/in-app-notification.ui | 35 ++++++
po/POTFILES.in | 2 +
src/components/in_app_notification.rs | 185 +++++++++++++++++++++++++++++++
src/components/mod.rs | 2 +
src/meson.build | 1 +
7 files changed, 232 insertions(+), 1 deletion(-)
---
diff --git a/data/resources/resources.gresource.xml b/data/resources/resources.gresource.xml
index 23a37a07..f106321d 100644
--- a/data/resources/resources.gresource.xml
+++ b/data/resources/resources.gresource.xml
@@ -20,6 +20,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="pill.ui">ui/pill.ui</file>
<file compressed="true" preprocess="xml-stripblanks"
alias="spinner-button.ui">ui/spinner-button.ui</file>
+ <file compressed="true" preprocess="xml-stripblanks"
alias="in-app-notification.ui">ui/in-app-notification.ui</file>
<file compressed="true">style.css</file>
<file preprocess="xml-stripblanks">icons/scalable/actions/send-symbolic.svg</file>
<file preprocess="xml-stripblanks">icons/scalable/status/welcome.svg</file>
diff --git a/data/resources/style.css b/data/resources/style.css
index 4f385eea..8eedf99e 100644
--- a/data/resources/style.css
+++ b/data/resources/style.css
@@ -15,7 +15,7 @@
}
.app-notification .pill {
- background-color: alpha(@theme_fg_color, 0.35);
+ background-color: alpha(@theme_bg_color, 0.2);
}
/* Login */
@@ -153,3 +153,8 @@ headerbar.flat {
.invite-room-name {
font-size: 24px;
}
+
+.app-notification {
+ border-radius: 9999px;
+ padding-left: 24px;
+}
diff --git a/data/resources/ui/in-app-notification.ui b/data/resources/ui/in-app-notification.ui
new file mode 100644
index 00000000..6b695182
--- /dev/null
+++ b/data/resources/ui/in-app-notification.ui
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="InAppNotification" parent="AdwBin">
+ <property name="valign">end</property>
+ <property name="halign">center</property>
+ <property name="margin-bottom">100</property>
+ <property name="margin-start">24</property>
+ <property name="margin-end">24</property>
+ <property name="child">
+ <object class="GtkRevealer" id="revealer">
+ <property name="transition-type">crossfade</property>
+ <property name="child">
+ <object class="GtkBox" id="box_">
+ <property name="valign">center</property>
+ <child>
+ <object class="GtkButton">
+ <property name="valign">center</property>
+ <property name="icon-name">window-close-symbolic</property>
+ <property name="action-name">in-app-notification.close</property>
+ <style>
+ <class name="flat"/>
+ <class name="circular"/>
+ </style>
+ </object>
+ </child>
+ <style>
+ <class name="app-notification"/>
+ </style>
+ </object>
+ </property>
+ </object>
+ </property>
+ </template>
+</interface>
+
diff --git a/po/POTFILES.in b/po/POTFILES.in
index bcbe1a39..93ff03cc 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -16,6 +16,7 @@ data/resources/ui/content-state-row.ui
data/resources/ui/content.ui
data/resources/ui/context-menu-bin.ui
data/resources/ui/login.ui
+data/resources/ui/in-app-notification.ui
data/resources/ui/session.ui
data/resources/ui/shortcuts.ui
data/resources/ui/sidebar-category-row.ui
@@ -30,6 +31,7 @@ data/resources/ui/window.ui
src/application.rs
src/components/context_menu_bin.rs
src/components/label_with_widgets.rs
+src/components/in_app_notification.rs
src/components/mod.rs
src/components/spinner_button.rs
src/components/pill.rs
diff --git a/src/components/in_app_notification.rs b/src/components/in_app_notification.rs
new file mode 100644
index 00000000..821f4022
--- /dev/null
+++ b/src/components/in_app_notification.rs
@@ -0,0 +1,185 @@
+use crate::Error;
+use adw::subclass::prelude::*;
+use gtk::prelude::*;
+use gtk::subclass::prelude::*;
+use gtk::{gio, glib, glib::clone, CompositeTemplate};
+
+mod imp {
+ use super::*;
+ use glib::{signal::SignalHandlerId, subclass::InitializingObject};
+ use std::cell::{Cell, RefCell};
+
+ #[derive(Debug, Default, CompositeTemplate)]
+ #[template(resource = "/org/gnome/FractalNext/in-app-notification.ui")]
+ pub struct InAppNotification {
+ pub error_list: RefCell<Option<gio::ListStore>>,
+ pub handler: RefCell<Option<SignalHandlerId>>,
+ #[template_child]
+ pub revealer: TemplateChild<gtk::Revealer>,
+ #[template_child]
+ pub box_: TemplateChild<gtk::Box>,
+ pub current_widget: RefCell<Option<gtk::Widget>>,
+ pub shows_error: Cell<bool>,
+ }
+
+ #[glib::object_subclass]
+ impl ObjectSubclass for InAppNotification {
+ const NAME: &'static str = "InAppNotification";
+ type Type = super::InAppNotification;
+ type ParentType = adw::Bin;
+
+ fn class_init(klass: &mut Self::Class) {
+ Self::bind_template(klass);
+
+ klass.install_action("in-app-notification.close", None, move |widget, _, _| {
+ widget.dismiss()
+ });
+ }
+
+ fn instance_init(obj: &InitializingObject<Self>) {
+ obj.init_template();
+ }
+ }
+
+ impl ObjectImpl for InAppNotification {
+ fn properties() -> &'static [glib::ParamSpec] {
+ use once_cell::sync::Lazy;
+ static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
+ vec![glib::ParamSpec::new_object(
+ "error-list",
+ "Error List",
+ "The list of errors to display",
+ gio::ListStore::static_type(),
+ glib::ParamFlags::READWRITE,
+ )]
+ });
+
+ PROPERTIES.as_ref()
+ }
+
+ fn set_property(
+ &self,
+ obj: &Self::Type,
+ _id: usize,
+ value: &glib::Value,
+ pspec: &glib::ParamSpec,
+ ) {
+ match pspec.name() {
+ "error-list" => obj.set_error_list(value.get().unwrap()),
+ _ => unimplemented!(),
+ }
+ }
+
+ fn property(&self, obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
+ match pspec.name() {
+ "error-list" => obj.error_list().to_value(),
+ _ => unimplemented!(),
+ }
+ }
+
+ fn constructed(&self, obj: &Self::Type) {
+ self.parent_constructed(obj);
+ self.revealer
+ .connect_child_revealed_notify(clone!(@weak obj => move |revealer| {
+ let priv_ = imp::InAppNotification::from_instance(&obj);
+ revealer.set_visible(priv_.shows_error.get());
+ }));
+ }
+
+ fn dispose(&self, _obj: &Self::Type) {
+ if let Some(id) = self.handler.take() {
+ self.error_list.borrow().as_ref().unwrap().disconnect(id);
+ }
+ }
+ }
+
+ impl WidgetImpl for InAppNotification {}
+
+ impl BinImpl for InAppNotification {}
+}
+
+glib::wrapper! {
+ pub struct InAppNotification(ObjectSubclass<imp::InAppNotification>)
+ @extends gtk::Widget, adw::Bin, @implements gtk::Accessible;
+}
+
+impl InAppNotification {
+ pub fn new(error_list: &gio::ListStore) -> Self {
+ glib::Object::new(&[("error-list", &error_list)])
+ .expect("Failed to create InAppNotification")
+ }
+
+ pub fn set_error_list(&self, error_list: Option<gio::ListStore>) {
+ let priv_ = imp::InAppNotification::from_instance(self);
+ if self.error_list() == error_list {
+ return;
+ }
+
+ if let Some(id) = priv_.handler.take() {
+ priv_.error_list.borrow().as_ref().unwrap().disconnect(id);
+ }
+
+ if let Some(ref error_list) = error_list {
+ let handler = error_list.connect_items_changed(
+ clone!(@weak self as obj => move |_, position, removed, added| {
+ let priv_ = imp::InAppNotification::from_instance(&obj);
+ // If the first error is removed we need to display the next error
+ if position == 0 && removed > 0 {
+ obj.next();
+ }
+
+ if added > 0 && !priv_.shows_error.get() {
+ obj.next();
+ }
+
+ }),
+ );
+ priv_.handler.replace(Some(handler));
+ }
+ priv_.error_list.replace(error_list);
+
+ self.next();
+ self.notify("error-list");
+ }
+
+ pub fn error_list(&self) -> Option<gio::ListStore> {
+ let priv_ = imp::InAppNotification::from_instance(self);
+ priv_.error_list.borrow().to_owned()
+ }
+
+ /// Show the next message in the `error-list`
+ fn next(&self) {
+ let priv_ = imp::InAppNotification::from_instance(self);
+
+ let shows_error = if let Some(widget) = priv_
+ .error_list
+ .borrow()
+ .as_ref()
+ .and_then(|error_list| error_list.item(0))
+ .and_then(|obj| obj.downcast::<Error>().ok())
+ .and_then(|error| error.widget())
+ {
+ if let Some(current_widget) = priv_.current_widget.take() {
+ priv_.box_.remove(¤t_widget);
+ }
+ priv_.box_.prepend(&widget);
+ priv_.current_widget.replace(Some(widget));
+ true
+ } else {
+ false
+ };
+
+ priv_.shows_error.set(shows_error);
+ if shows_error {
+ priv_.revealer.show();
+ }
+ priv_.revealer.set_reveal_child(shows_error);
+ }
+
+ fn dismiss(&self) {
+ let priv_ = imp::InAppNotification::from_instance(self);
+ if let Some(error_list) = &*priv_.error_list.borrow() {
+ error_list.remove(0);
+ }
+ }
+}
diff --git a/src/components/mod.rs b/src/components/mod.rs
index 04674049..158f2a50 100644
--- a/src/components/mod.rs
+++ b/src/components/mod.rs
@@ -1,9 +1,11 @@
mod context_menu_bin;
+mod in_app_notification;
mod label_with_widgets;
mod pill;
mod spinner_button;
pub use self::context_menu_bin::{ContextMenuBin, ContextMenuBinImpl};
+pub use self::in_app_notification::InAppNotification;
pub use self::label_with_widgets::LabelWithWidgets;
pub use self::pill::Pill;
pub use self::spinner_button::SpinnerButton;
diff --git a/src/meson.build b/src/meson.build
index b2c8fd8c..e149e2bc 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -24,6 +24,7 @@ sources = files(
'components/label_with_widgets.rs',
'components/mod.rs',
'components/pill.rs',
+ 'components/in_app_notification.rs',
'components/spinner_button.rs',
'config.rs',
'error.rs',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]