diff --git a/data/resources/resources.gresource.xml b/data/resources/resources.gresource.xml index e3fa5bdb..2bde98bb 100644 --- a/data/resources/resources.gresource.xml +++ b/data/resources/resources.gresource.xml @@ -25,6 +25,7 @@ ui/window.ui ui/context-menu-bin.ui ui/pill.ui + ui/room-title.ui ui/spinner-button.ui ui/in-app-notification.ui ui/components-avatar.ui diff --git a/data/resources/ui/content-room-history.ui b/data/resources/ui/content-room-history.ui index 977d8238..cab3b0f0 100644 --- a/data/resources/ui/content-room-history.ui +++ b/data/resources/ui/content-room-history.ui @@ -31,7 +31,7 @@ - + ContentRoomHistory diff --git a/data/resources/ui/room-title.ui b/data/resources/ui/room-title.ui new file mode 100644 index 00000000..26718b2f --- /dev/null +++ b/data/resources/ui/room-title.ui @@ -0,0 +1,42 @@ + + + + diff --git a/src/components/mod.rs b/src/components/mod.rs index 7296e89d..5d2e3e74 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -3,6 +3,7 @@ mod context_menu_bin; mod in_app_notification; mod label_with_widgets; mod pill; +mod room_title; mod spinner_button; pub use self::avatar::Avatar; @@ -10,4 +11,5 @@ pub use self::context_menu_bin::{ContextMenuBin, ContextMenuBinExt, ContextMenuB pub use self::in_app_notification::InAppNotification; pub use self::label_with_widgets::LabelWithWidgets; pub use self::pill::Pill; +pub use self::room_title::RoomTitle; pub use self::spinner_button::SpinnerButton; diff --git a/src/components/room_title.rs b/src/components/room_title.rs new file mode 100644 index 00000000..26698a66 --- /dev/null +++ b/src/components/room_title.rs @@ -0,0 +1,140 @@ +use adw::subclass::prelude::*; +use gtk::prelude::*; +use gtk::subclass::prelude::*; +use gtk::{glib, CompositeTemplate}; + +mod imp { + use super::*; + use glib::subclass::InitializingObject; + use std::cell::RefCell; + + #[derive(Debug, Default, CompositeTemplate)] + #[template(resource = "/org/gnome/FractalNext/room-title.ui")] + pub struct RoomTitle { + // The markup for the title + pub title: RefCell>, + // The markup for the subtitle + pub subtitle: RefCell>, + #[template_child] + pub title_label: TemplateChild, + #[template_child] + pub subtitle_label: TemplateChild, + } + + #[glib::object_subclass] + impl ObjectSubclass for RoomTitle { + const NAME: &'static str = "RoomTitle"; + type Type = super::RoomTitle; + type ParentType = adw::Bin; + + fn class_init(klass: &mut Self::Class) { + Self::bind_template(klass); + } + + fn instance_init(obj: &InitializingObject) { + obj.init_template(); + } + } + + impl ObjectImpl for RoomTitle { + fn properties() -> &'static [glib::ParamSpec] { + use once_cell::sync::Lazy; + static PROPERTIES: Lazy> = Lazy::new(|| { + vec![ + glib::ParamSpec::new_string( + "title", + "Title", + "The title of the room", + None, + glib::ParamFlags::READWRITE | glib::ParamFlags::EXPLICIT_NOTIFY, + ), + glib::ParamSpec::new_string( + "subtitle", + "Subtitle", + "The subtitle of the room", + None, + glib::ParamFlags::READWRITE | glib::ParamFlags::EXPLICIT_NOTIFY, + ), + ] + }); + + PROPERTIES.as_ref() + } + + fn property(&self, obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + match pspec.name() { + "title" => obj.title().to_value(), + "subtitle" => obj.subtitle().to_value(), + _ => unimplemented!(), + } + } + + 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()), + "subtitle" => obj.set_subtitle(value.get().unwrap()), + _ => unimplemented!(), + } + } + + fn constructed(&self, obj: &Self::Type) { + self.parent_constructed(obj); + } + } + + impl WidgetImpl for RoomTitle {} + impl BinImpl for RoomTitle {} +} + +glib::wrapper! { + pub struct RoomTitle(ObjectSubclass) + @extends gtk::Widget, adw::Bin, @implements gtk::Accessible; +} + +impl RoomTitle { + pub fn new() -> Self { + glib::Object::new(&[]).expect("Failed to create RoomTitle") + } + + pub fn set_title(&self, title: Option) { + let priv_ = imp::RoomTitle::from_instance(self); + // If there's an existing title, check that current title and new title aren't equal + if priv_.title.borrow().as_deref() != title.as_deref() { + priv_.title.replace(title); + priv_ + .title_label + .set_visible(priv_.title.borrow().is_some()); + } + + self.notify("title"); + } + + pub fn title(&self) -> Option { + let priv_ = imp::RoomTitle::from_instance(self); + priv_.title.borrow().clone() + } + + pub fn set_subtitle(&self, subtitle: Option) { + let priv_ = imp::RoomTitle::from_instance(self); + // If there's an existing subtitle, check that current subtitle and new subtitle aren't equal + if priv_.subtitle.borrow().as_deref() != subtitle.as_deref() { + priv_.subtitle.replace(subtitle); + priv_ + .subtitle_label + .set_visible(priv_.subtitle.borrow().is_some()); + } + + self.notify("subtitle"); + } + + pub fn subtitle(&self) -> Option { + let priv_ = imp::RoomTitle::from_instance(self); + priv_.subtitle.borrow().clone() + } +} diff --git a/src/meson.build b/src/meson.build index db9f9599..b3e129b7 100644 --- a/src/meson.build +++ b/src/meson.build @@ -25,6 +25,7 @@ sources = files( 'components/label_with_widgets.rs', 'components/mod.rs', 'components/pill.rs', + 'components/room_title.rs', 'components/in_app_notification.rs', 'components/spinner_button.rs', 'config.rs', diff --git a/src/session/content/room_history.rs b/src/session/content/room_history.rs index d6c902c3..cb06ea94 100644 --- a/src/session/content/room_history.rs +++ b/src/session/content/room_history.rs @@ -1,3 +1,4 @@ +use crate::components::RoomTitle; use crate::session::{content::ItemRow, content::MarkdownPopover, room::Room, room::RoomType}; use adw::subclass::prelude::*; use gtk::{ @@ -22,6 +23,8 @@ mod imp { #[template_child] pub headerbar: TemplateChild, #[template_child] + pub room_title: TemplateChild, + #[template_child] pub room_menu: TemplateChild, #[template_child] pub listview: TemplateChild,