[fractal] room-details: Move general page to own subclass
- From: Julian Sparber <jsparber src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [fractal] room-details: Move general page to own subclass
- Date: Thu, 15 Sep 2022 12:27:52 +0000 (UTC)
commit 988e6bf1c99b930e09ce9056acd2e8b80747b1fd
Author: Julian Sparber <julian sparber net>
Date: Mon Sep 12 11:56:48 2022 +0200
room-details: Move general page to own subclass
data/resources/resources.gresource.xml | 1 +
.../ui/content-room-details-general-page.ui | 180 ++++++++++++++
data/resources/ui/content-room-details.ui | 185 +-------------
po/POTFILES.in | 2 +
.../content/room_details/general_page/mod.rs | 270 +++++++++++++++++++++
src/session/content/room_details/mod.rs | 202 +++------------
6 files changed, 482 insertions(+), 358 deletions(-)
---
diff --git a/data/resources/resources.gresource.xml b/data/resources/resources.gresource.xml
index 1f0edb68b..77945325c 100644
--- a/data/resources/resources.gresource.xml
+++ b/data/resources/resources.gresource.xml
@@ -67,6 +67,7 @@
<file compressed="true" preprocess="xml-stripblanks"
alias="content-message-reply.ui">ui/content-message-reply.ui</file>
<file compressed="true" preprocess="xml-stripblanks"
alias="content-message-row.ui">ui/content-message-row.ui</file>
<file compressed="true" preprocess="xml-stripblanks"
alias="content-public-room-row.ui">ui/content-public-room-row.ui</file>
+ <file compressed="true" preprocess="xml-stripblanks"
alias="content-room-details-general-page.ui">ui/content-room-details-general-page.ui</file>
<file compressed="true" preprocess="xml-stripblanks"
alias="content-room-details.ui">ui/content-room-details.ui</file>
<file compressed="true" preprocess="xml-stripblanks"
alias="content-room-history.ui">ui/content-room-history.ui</file>
<file compressed="true" preprocess="xml-stripblanks"
alias="content-state-creation.ui">ui/content-state-creation.ui</file>
diff --git a/data/resources/ui/content-room-details-general-page.ui
b/data/resources/ui/content-room-details-general-page.ui
new file mode 100644
index 000000000..65ee75088
--- /dev/null
+++ b/data/resources/ui/content-room-details-general-page.ui
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="ContentRoomDetailsGeneralPage" parent="AdwBin">
+ <property name="child">
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkHeaderBar"/>
+ </child>
+ <child>
+ <object class="AdwClamp">
+ <property name="maximum-size">400</property>
+ <property name="tightening-threshold">400</property>
+ <property name="margin-top">12</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <property name="spacing">24</property>
+ <child>
+ <object class="AdwPreferencesGroup">
+ <style>
+ <class name="room-details-group"/>
+ </style>
+ <child>
+ <object class="GtkOverlay">
+ <property name="halign">center</property>
+ <child>
+ <object class="ComponentsAvatar">
+ <property name="size">128</property>
+ <binding name="item">
+ <lookup name="avatar">
+ <lookup name="room">ContentRoomDetailsGeneralPage</lookup>
+ </lookup>
+ </binding>
+ </object>
+ </child>
+ <child type="overlay">
+ <object class="AdwBin" id="avatar_remove_button">
+ <style>
+ <class name="cutout-button"/>
+ </style>
+ <property name="halign">end</property>
+ <property name="valign">start</property>
+ <child>
+ <object class="GtkButton">
+ <property name="icon-name">user-trash-symbolic</property>
+ <property name="action-name">details.remove-avatar</property>
+ <style>
+ <class name="circular"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="overlay">
+ <object class="AdwBin" id="avatar_edit_button">
+ <style>
+ <class name="cutout-button"/>
+ </style>
+ <property name="halign">end</property>
+ <property name="valign">end</property>
+ <child>
+ <object class="GtkButton">
+ <property name="icon-name">document-edit-symbolic</property>
+ <property name="action-name">details.choose-avatar</property>
+ <style>
+ <class name="circular"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="spacing">6</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkEntry" id="room_name_entry">
+ <property name="sensitive">false</property>
+ <property name="activates-default">True</property>
+ <property name="xalign">0.5</property>
+ <property name="buffer">
+ <object class="GtkEntryBuffer" id="room_name_buffer">
+ <binding name="text">
+ <lookup name="display-name">
+ <lookup name="room">ContentRoomDetailsGeneralPage</lookup>
+ </lookup>
+ </binding>
+ </object>
+ </property>
+ <style>
+ <class name="room-details-name"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="room_topic_label">
+ <property name="visible">false</property>
+ <property name="margin-top">12</property>
+ <property name="label" translatable="yes">Description</property>
+ <property name="halign">start</property>
+ <style>
+ <class name="dim-label"/>
+ <class name="caption-heading"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="CustomEntry" id="room_topic_entry">
+ <property name="sensitive">false</property>
+ <property name="margin-bottom">18</property>
+ <child>
+ <object class="GtkTextView" id="room_topic_text_view">
+ <property name="justification">center</property>
+ <property name="wrap-mode">word-char</property>
+ <property name="accepts-tab">False</property>
+ <property name="top-margin">7</property>
+ <property name="bottom-margin">7</property>
+ <property name="buffer">
+ <object class="GtkTextBuffer" id="room_topic_buffer">
+ <binding name="text">
+ <lookup name="topic">
+ <lookup name="room">ContentRoomDetailsGeneralPage</lookup>
+ </lookup>
+ </binding>
+ </object>
+ </property>
+ </object>
+ </child>
+ <style>
+ <class name="room-details-topic"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="edit_toggle">
+ <property name="halign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwPreferencesGroup">
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Members</property>
+ <property name="icon-name">system-users-symbolic</property>
+ <property name="action-name">details.next-page</property>
+ <property name="action-target">'members'</property>
+ <property name="activatable">True</property>
+ <child type="suffix">
+ <object class="GtkLabel" id="members_count">
+ <property name="valign">center</property>
+ <property name="halign">center</property>
+ </object>
+ </child>
+ <child type="suffix">
+ <object class="GtkImage">
+ <property name="valign">center</property>
+ <property name="halign">center</property>
+ <property name="icon-name">go-next-symbolic</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </template>
+</interface>
+
diff --git a/data/resources/ui/content-room-details.ui b/data/resources/ui/content-room-details.ui
index 3432f810c..9a6acb559 100644
--- a/data/resources/ui/content-room-details.ui
+++ b/data/resources/ui/content-room-details.ui
@@ -2,195 +2,12 @@
<interface>
<template class="RoomDetails" parent="AdwWindow">
<property name="title" translatable="yes">Room Details</property>
- <property name="default-widget">edit_toggle</property>
<property name="modal">True</property>
<property name="destroy_with_parent">True</property>
<property name="default-width">640</property>
<property name="default-height">576</property>
<property name="content">
- <object class="GtkStack" id="main_stack">
- <child>
- <object class="GtkStackPage">
- <property name="icon-name">applications-system-symbolic</property>
- <property name="title" translatable="yes">General</property>
- <property name="name">general</property>
- <property name="child">
- <object class="GtkBox">
- <property name="orientation">vertical</property>
- <child>
- <object class="GtkHeaderBar"/>
- </child>
- <child>
- <object class="AdwClamp">
- <property name="maximum-size">400</property>
- <property name="tightening-threshold">400</property>
- <property name="margin-top">12</property>
- <property name="child">
- <object class="GtkBox">
- <property name="orientation">vertical</property>
- <property name="spacing">24</property>
- <child>
- <object class="AdwPreferencesGroup">
- <style>
- <class name="room-details-group"/>
- </style>
- <child>
- <object class="GtkOverlay">
- <property name="halign">center</property>
- <child>
- <object class="ComponentsAvatar">
- <property name="size">128</property>
- <binding name="item">
- <lookup name="avatar">
- <lookup name="room">RoomDetails</lookup>
- </lookup>
- </binding>
- </object>
- </child>
- <child type="overlay">
- <object class="AdwBin" id="avatar_remove_button">
- <style>
- <class name="cutout-button"/>
- </style>
- <property name="halign">end</property>
- <property name="valign">start</property>
- <child>
- <object class="GtkButton">
- <property name="icon-name">user-trash-symbolic</property>
- <property name="action-name">details.remove-avatar</property>
- <style>
- <class name="circular"/>
- </style>
- </object>
- </child>
- </object>
- </child>
- <child type="overlay">
- <object class="AdwBin" id="avatar_edit_button">
- <style>
- <class name="cutout-button"/>
- </style>
- <property name="halign">end</property>
- <property name="valign">end</property>
- <child>
- <object class="GtkButton">
- <property name="icon-name">document-edit-symbolic</property>
- <property name="action-name">details.choose-avatar</property>
- <style>
- <class name="circular"/>
- </style>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkBox">
- <property name="spacing">6</property>
- <property name="orientation">vertical</property>
- <child>
- <object class="GtkEntry" id="room_name_entry">
- <property name="sensitive">false</property>
- <property name="activates-default">True</property>
- <property name="xalign">0.5</property>
- <property name="buffer">
- <object class="GtkEntryBuffer" id="room_name_buffer">
- <binding name="text">
- <lookup name="display-name">
- <lookup name="room">RoomDetails</lookup>
- </lookup>
- </binding>
- </object>
- </property>
- <style>
- <class name="room-details-name"/>
- </style>
- </object>
- </child>
- <child>
- <object class="GtkLabel" id="room_topic_label">
- <property name="visible">false</property>
- <property name="margin-top">12</property>
- <property name="label" translatable="yes">Description</property>
- <property name="halign">start</property>
- <style>
- <class name="dim-label"/>
- <class name="caption-heading"/>
- </style>
- </object>
- </child>
- <child>
- <object class="CustomEntry" id="room_topic_entry">
- <property name="sensitive">false</property>
- <property name="margin-bottom">18</property>
- <child>
- <object class="GtkTextView" id="room_topic_text_view">
- <property name="justification">center</property>
- <property name="wrap-mode">word-char</property>
- <property name="accepts-tab">False</property>
- <property name="top-margin">7</property>
- <property name="bottom-margin">7</property>
- <property name="buffer">
- <object class="GtkTextBuffer" id="room_topic_buffer">
- <binding name="text">
- <lookup name="topic">
- <lookup name="room">RoomDetails</lookup>
- </lookup>
- </binding>
- </object>
- </property>
- </object>
- </child>
- <style>
- <class name="room-details-topic"/>
- </style>
- </object>
- </child>
- <child>
- <object class="GtkButton" id="edit_toggle">
- <property name="halign">center</property>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="AdwPreferencesGroup">
- <child>
- <object class="AdwActionRow">
- <property name="title" translatable="yes">Members</property>
- <property name="icon-name">system-users-symbolic</property>
- <property name="action-name">details.next-page</property>
- <property name="action-target">'members'</property>
- <property name="activatable">True</property>
- <child type="suffix">
- <object class="GtkLabel" id="members_count">
- <property name="valign">center</property>
- <property name="halign">center</property>
- </object>
- </child>
- <child type="suffix">
- <object class="GtkImage">
- <property name="valign">center</property>
- <property name="halign">center</property>
- <property name="icon-name">go-next-symbolic</property>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- </object>
- </property>
- </object>
- </child>
- </object>
- </property>
- </object>
- </child>
- </object>
+ <object class="GtkStack" id="main_stack" />
</property>
<style>
<class name="room-details"/>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index c75a4fbdb..b58010e62 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -21,6 +21,7 @@ data/resources/ui/content-markdown-popover.ui
data/resources/ui/content-member-page.ui
data/resources/ui/content-message-file.ui
data/resources/ui/content-room-details.ui
+data/resources/ui/content-room-details-general-page.ui
data/resources/ui/content-room-history.ui
data/resources/ui/content-state-creation.ui
data/resources/ui/content-state-tombstone.ui
@@ -55,6 +56,7 @@ src/session/account_settings/user_page/deactivate_account_subpage.rs
src/session/account_settings/user_page/mod.rs
src/session/content/explore/public_room_row.rs
src/session/content/invite.rs
+src/session/content/room_details/general_page/mod.rs
src/session/content/room_details/invite_subpage/invitee_list.rs
src/session/content/room_details/member_page/mod.rs
src/session/content/room_details/mod.rs
diff --git a/src/session/content/room_details/general_page/mod.rs
b/src/session/content/room_details/general_page/mod.rs
new file mode 100644
index 000000000..061c7b9a0
--- /dev/null
+++ b/src/session/content/room_details/general_page/mod.rs
@@ -0,0 +1,270 @@
+use std::convert::From;
+
+use adw::{prelude::*, subclass::prelude::*};
+use gettextrs::gettext;
+use gtk::{
+ gdk,
+ glib::{self, clone, closure},
+ CompositeTemplate,
+};
+use log::error;
+use matrix_sdk::ruma::events::RoomEventType;
+
+use crate::{
+ components::CustomEntry,
+ session::{self, room::RoomAction, Room},
+ utils::{and_expr, or_expr},
+};
+
+mod imp {
+ use std::cell::Cell;
+
+ use glib::subclass::InitializingObject;
+ use once_cell::unsync::OnceCell;
+
+ use super::*;
+
+ #[derive(Debug, Default, CompositeTemplate)]
+ #[template(resource = "/org/gnome/Fractal/content-room-details-general-page.ui")]
+ pub struct GeneralPage {
+ pub room: OnceCell<Room>,
+ pub avatar_chooser: OnceCell<gtk::FileChooserNative>,
+ #[template_child]
+ pub avatar_remove_button: TemplateChild<adw::Bin>,
+ #[template_child]
+ pub avatar_edit_button: TemplateChild<adw::Bin>,
+ #[template_child]
+ pub edit_toggle: TemplateChild<gtk::Button>,
+ #[template_child]
+ pub room_name_entry: TemplateChild<gtk::Entry>,
+ #[template_child]
+ pub room_topic_text_view: TemplateChild<gtk::TextView>,
+ #[template_child]
+ pub room_topic_entry: TemplateChild<CustomEntry>,
+ #[template_child]
+ pub room_topic_label: TemplateChild<gtk::Label>,
+ #[template_child]
+ pub members_count: TemplateChild<gtk::Label>,
+ pub edit_mode: Cell<bool>,
+ }
+
+ #[glib::object_subclass]
+ impl ObjectSubclass for GeneralPage {
+ const NAME: &'static str = "ContentRoomDetailsGeneralPage";
+ type Type = super::GeneralPage;
+ type ParentType = adw::Bin;
+
+ fn class_init(klass: &mut Self::Class) {
+ CustomEntry::static_type();
+ Self::bind_template(klass);
+
+ klass.install_action("details.choose-avatar", None, move |widget, _, _| {
+ widget.open_avatar_chooser()
+ });
+ klass.install_action("details.remove-avatar", None, move |widget, _, _| {
+ widget.room().store_avatar(None)
+ });
+ }
+
+ fn instance_init(obj: &InitializingObject<Self>) {
+ obj.init_template();
+ }
+ }
+
+ impl ObjectImpl for GeneralPage {
+ fn properties() -> &'static [glib::ParamSpec] {
+ use once_cell::sync::Lazy;
+ static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
+ vec![glib::ParamSpecObject::new(
+ "room",
+ "Room",
+ "The room backing all details of the preference window",
+ Room::static_type(),
+ glib::ParamFlags::READWRITE | glib::ParamFlags::CONSTRUCT_ONLY,
+ )]
+ });
+
+ PROPERTIES.as_ref()
+ }
+
+ fn set_property(
+ &self,
+ obj: &Self::Type,
+ _id: usize,
+ value: &glib::Value,
+ pspec: &glib::ParamSpec,
+ ) {
+ match pspec.name() {
+ "room" => obj.set_room(value.get().unwrap()),
+ _ => unimplemented!(),
+ }
+ }
+
+ fn property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
+ match pspec.name() {
+ "room" => self.room.get().to_value(),
+ _ => unimplemented!(),
+ }
+ }
+
+ fn constructed(&self, obj: &Self::Type) {
+ self.parent_constructed(obj);
+
+ obj.init_avatar();
+ obj.init_edit_toggle();
+
+ let members = obj.room().members();
+ members.connect_items_changed(clone!(@weak obj => move |members, _, _, _| {
+ obj.member_count_changed(members.n_items());
+ }));
+
+ obj.member_count_changed(members.n_items());
+ }
+ }
+
+ impl WidgetImpl for GeneralPage {}
+ impl BinImpl for GeneralPage {}
+}
+
+glib::wrapper! {
+ /// Preference Window to display and update room details.
+ pub struct GeneralPage(ObjectSubclass<imp::GeneralPage>)
+ @extends gtk::Widget, adw::Bin, @implements gtk::Accessible;
+}
+
+impl GeneralPage {
+ pub fn new(room: &Room) -> Self {
+ glib::Object::new(&[("room", room)]).expect("Failed to create GeneralPage")
+ }
+
+ pub fn room(&self) -> &Room {
+ // Use unwrap because room property is CONSTRUCT_ONLY.
+ self.imp().room.get().unwrap()
+ }
+
+ fn set_room(&self, room: Room) {
+ self.imp().room.set(room).expect("Room already initialized");
+ }
+
+ fn init_avatar(&self) {
+ let priv_ = self.imp();
+ let avatar_remove_button = &priv_.avatar_remove_button;
+ let avatar_edit_button = &priv_.avatar_edit_button;
+
+ // Hide avatar controls when the user is not eligible to perform the actions.
+ let room = self.room();
+
+ let room_avatar_exists = room
+ .property_expression("avatar")
+ .chain_property::<session::Avatar>("image")
+ .chain_closure::<bool>(closure!(
+ |_: Option<glib::Object>, image: Option<gdk::Paintable>| { image.is_some() }
+ ));
+
+ let room_avatar_changeable =
+ room.new_allowed_expr(RoomAction::StateEvent(RoomEventType::RoomAvatar));
+ let room_avatar_removable = and_expr(&room_avatar_changeable, &room_avatar_exists);
+
+ room_avatar_removable.bind(&avatar_remove_button.get(), "visible", gtk::Widget::NONE);
+ room_avatar_changeable.bind(&avatar_edit_button.get(), "visible", gtk::Widget::NONE);
+ }
+
+ fn init_edit_toggle(&self) {
+ let priv_ = self.imp();
+ let edit_toggle = &priv_.edit_toggle;
+ let label_enabled = gettext("Save Details");
+ let label_disabled = gettext("Edit Details");
+
+ edit_toggle.set_label(&label_disabled);
+
+ // Save changes of name and topic on toggle button release.
+ edit_toggle.connect_clicked(clone!(@weak self as this => move |button| {
+ let priv_ = this.imp();
+ if !priv_.edit_mode.get() {
+ priv_.edit_mode.set(true);
+ button.set_label(&label_enabled);
+ priv_.room_topic_text_view.set_justification(gtk::Justification::Left);
+ priv_.room_name_entry.set_xalign(0.0);
+ priv_.room_name_entry.set_halign(gtk::Align::Center);
+ priv_.room_name_entry.set_sensitive(true);
+ priv_.room_name_entry.set_width_chars(25);
+ priv_.room_topic_entry.set_sensitive(true);
+ priv_.room_topic_label.show();
+ return;
+ }
+ priv_.edit_mode.set(false);
+ button.set_label(&label_disabled);
+ priv_.room_topic_text_view.set_justification(gtk::Justification::Center);
+ priv_.room_name_entry.set_xalign(0.5);
+ priv_.room_name_entry.set_sensitive(false);
+ priv_.room_name_entry.set_halign(gtk::Align::Fill);
+ priv_.room_name_entry.set_width_chars(-1);
+ priv_.room_topic_entry.set_sensitive(false);
+ priv_.room_topic_label.hide();
+
+ let room = this.room();
+
+ let room_name = priv_.room_name_entry.buffer().text();
+ let topic_buffer = priv_.room_topic_text_view.buffer();
+ let topic = topic_buffer.text(&topic_buffer.start_iter(), &topic_buffer.end_iter(), true);
+ room.store_room_name(room_name);
+ room.store_topic(topic.to_string());
+ }));
+
+ // Hide edit controls when the user is not eligible to perform the actions.
+ let room = self.room();
+ let room_name_changeable =
+ room.new_allowed_expr(RoomAction::StateEvent(RoomEventType::RoomName));
+ let room_topic_changeable =
+ room.new_allowed_expr(RoomAction::StateEvent(RoomEventType::RoomTopic));
+
+ let edit_toggle_visible = or_expr(room_name_changeable, room_topic_changeable);
+ edit_toggle_visible.bind(&edit_toggle.get(), "visible", gtk::Widget::NONE);
+ }
+
+ fn avatar_chooser(&self) -> Option<>k::FileChooserNative> {
+ if let Some(avatar_chooser) = self.imp().avatar_chooser.get() {
+ Some(avatar_chooser)
+ } else {
+ let window = self.root()?.downcast::<adw::Window>().ok()?;
+
+ let avatar_chooser = gtk::FileChooserNative::new(
+ Some(&gettext("Choose avatar")),
+ Some(&window),
+ gtk::FileChooserAction::Open,
+ None,
+ None,
+ );
+ avatar_chooser.connect_response(
+ clone!(@weak self as this => move |chooser, response| {
+ let file = chooser.file().and_then(|f| f.path());
+ if let (gtk::ResponseType::Accept, Some(file)) = (response, file) {
+ log::debug!("Chose file {:?}", file);
+ this.room().store_avatar(Some(file));
+ }
+ }),
+ );
+
+ // We must keep a reference to FileChooserNative around as it is not
+ // managed by GTK.
+ self.imp()
+ .avatar_chooser
+ .set(avatar_chooser)
+ .expect("File chooser already initialized");
+
+ self.avatar_chooser()
+ }
+ }
+
+ fn open_avatar_chooser(&self) {
+ if let Some(avatar_chooser) = self.avatar_chooser() {
+ avatar_chooser.show();
+ } else {
+ error!("Failed to create the FileChooserNative");
+ }
+ }
+
+ fn member_count_changed(&self, n: u32) {
+ self.imp().members_count.set_text(&format!("{}", n));
+ }
+}
diff --git a/src/session/content/room_details/mod.rs b/src/session/content/room_details/mod.rs
index e5043991c..c6e429c46 100644
--- a/src/session/content/room_details/mod.rs
+++ b/src/session/content/room_details/mod.rs
@@ -1,3 +1,4 @@
+mod general_page;
mod invite_subpage;
mod member_page;
@@ -5,24 +6,17 @@ use std::convert::From;
use adw::{prelude::*, subclass::prelude::*};
use gettextrs::gettext;
-use gtk::{
- gdk,
- glib::{self, clone, closure},
- CompositeTemplate,
-};
-use matrix_sdk::ruma::events::RoomEventType;
-
-pub use self::{invite_subpage::InviteSubpage, member_page::MemberPage};
-use crate::{
- components::CustomEntry,
- session::{self, room::RoomAction, Room},
- utils::{and_expr, or_expr},
-};
+use gtk::{glib, CompositeTemplate};
+use log::warn;
+
+pub use self::{general_page::GeneralPage, invite_subpage::InviteSubpage, member_page::MemberPage};
+use crate::session::Room;
#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy, glib::Enum)]
#[repr(u32)]
#[enum_type(name = "RoomDetailsPageName")]
pub enum PageName {
+ None,
General,
Members,
Invite,
@@ -30,7 +24,7 @@ pub enum PageName {
impl Default for PageName {
fn default() -> Self {
- Self::General
+ Self::None
}
}
@@ -46,6 +40,7 @@ impl glib::variant::FromVariant for PageName {
"general" => Some(PageName::General),
"members" => Some(PageName::Members),
"invite" => Some(PageName::Invite),
+ "" => Some(PageName::None),
_ => None,
}
}
@@ -54,6 +49,7 @@ impl glib::variant::FromVariant for PageName {
impl glib::variant::ToVariant for PageName {
fn to_variant(&self) -> glib::variant::Variant {
match self {
+ PageName::None => "",
PageName::General => "general",
PageName::Members => "members",
PageName::Invite => "invite",
@@ -77,26 +73,8 @@ mod imp {
#[template(resource = "/org/gnome/Fractal/content-room-details.ui")]
pub struct RoomDetails {
pub room: OnceCell<Room>,
- pub avatar_chooser: OnceCell<gtk::FileChooserNative>,
#[template_child]
pub main_stack: TemplateChild<gtk::Stack>,
- #[template_child]
- pub avatar_remove_button: TemplateChild<adw::Bin>,
- #[template_child]
- pub avatar_edit_button: TemplateChild<adw::Bin>,
- #[template_child]
- pub edit_toggle: TemplateChild<gtk::Button>,
- #[template_child]
- pub room_name_entry: TemplateChild<gtk::Entry>,
- #[template_child]
- pub room_topic_text_view: TemplateChild<gtk::TextView>,
- #[template_child]
- pub room_topic_entry: TemplateChild<CustomEntry>,
- #[template_child]
- pub room_topic_label: TemplateChild<gtk::Label>,
- #[template_child]
- pub members_count: TemplateChild<gtk::Label>,
- pub edit_mode: Cell<bool>,
pub list_stack_children: RefCell<HashMap<PageName, glib::WeakRef<gtk::Widget>>>,
pub visible_page: Cell<PageName>,
pub previous_visible_page: RefCell<Vec<PageName>>,
@@ -109,14 +87,8 @@ mod imp {
type ParentType = adw::Window;
fn class_init(klass: &mut Self::Class) {
- CustomEntry::static_type();
Self::bind_template(klass);
- klass.install_action("details.choose-avatar", None, move |widget, _, _| {
- widget.open_avatar_chooser()
- });
- klass.install_action("details.remove-avatar", None, move |widget, _, _| {
- widget.room().store_avatar(None)
- });
+
klass.install_action("details.next-page", Some("s"), move |widget, _, param| {
let page = param
.and_then(|variant| variant.get::<PageName>())
@@ -124,6 +96,7 @@ mod imp {
widget.next_page(page);
});
+
klass.install_action("details.previous-page", None, move |widget, _, _| {
widget.previous_page();
});
@@ -181,27 +154,6 @@ mod imp {
_ => unimplemented!(),
}
}
-
- fn constructed(&self, obj: &Self::Type) {
- self.parent_constructed(obj);
-
- obj.init_avatar();
- obj.init_edit_toggle();
- obj.init_avatar_chooser();
- obj.init_member_action();
-
- self.main_stack
- .connect_visible_child_notify(clone!(@weak obj => move |_| {
- obj.notify("visible-page");
- }));
-
- let members = obj.room().members();
- members.connect_items_changed(clone!(@weak obj => move |members, _, _, _| {
- obj.member_count_changed(members.n_items());
- }));
-
- obj.member_count_changed(members.n_items());
- }
}
impl WidgetImpl for RoomDetails {}
@@ -245,8 +197,20 @@ impl RoomDetails {
match name {
PageName::General => {
+ let general_page = if let Some(general_page) = list_stack_children
+ .get(&PageName::General)
+ .and_then(glib::object::WeakRef::upgrade)
+ {
+ general_page
+ } else {
+ let general_page = GeneralPage::new(self.room()).upcast::<gtk::Widget>();
+ list_stack_children.insert(PageName::General, general_page.downgrade());
+ self.imp().main_stack.add_child(&general_page);
+ general_page
+ };
+
self.set_title(Some(&gettext("Room Details")));
- priv_.main_stack.set_visible_child_name("general");
+ priv_.main_stack.set_visible_child(&general_page);
}
PageName::Members => {
let members_page = if let Some(members_page) = list_stack_children
@@ -265,7 +229,6 @@ impl RoomDetails {
priv_.main_stack.set_visible_child(&members_page);
}
PageName::Invite => {
- priv_.main_stack.set_visible_child_name("general");
let invite_page = if let Some(invite_page) = list_stack_children
.get(&PageName::Invite)
.and_then(glib::object::WeakRef::upgrade)
@@ -281,124 +244,15 @@ impl RoomDetails {
self.set_title(Some(&gettext("Invite new Members")));
priv_.main_stack.set_visible_child(&invite_page);
}
+ PageName::None => {
+ warn!("Can't switch to PageName::None");
+ }
}
priv_.visible_page.set(name);
self.notify("visible-page");
}
- fn init_avatar(&self) {
- let priv_ = self.imp();
- let avatar_remove_button = &priv_.avatar_remove_button;
- let avatar_edit_button = &priv_.avatar_edit_button;
-
- // Hide avatar controls when the user is not eligible to perform the actions.
- let room = self.room();
-
- let room_avatar_exists = room
- .property_expression("avatar")
- .chain_property::<session::Avatar>("image")
- .chain_closure::<bool>(closure!(
- |_: Option<glib::Object>, image: Option<gdk::Paintable>| { image.is_some() }
- ));
-
- let room_avatar_changeable =
- room.new_allowed_expr(RoomAction::StateEvent(RoomEventType::RoomAvatar));
- let room_avatar_removable = and_expr(&room_avatar_changeable, &room_avatar_exists);
-
- room_avatar_removable.bind(&avatar_remove_button.get(), "visible", gtk::Widget::NONE);
- room_avatar_changeable.bind(&avatar_edit_button.get(), "visible", gtk::Widget::NONE);
- }
-
- fn init_edit_toggle(&self) {
- let priv_ = self.imp();
- let edit_toggle = &priv_.edit_toggle;
- let label_enabled = gettext("Save Details");
- let label_disabled = gettext("Edit Details");
-
- edit_toggle.set_label(&label_disabled);
-
- // Save changes of name and topic on toggle button release.
- edit_toggle.connect_clicked(clone!(@weak self as this => move |button| {
- let priv_ = this.imp();
- if !priv_.edit_mode.get() {
- priv_.edit_mode.set(true);
- button.set_label(&label_enabled);
- priv_.room_topic_text_view.set_justification(gtk::Justification::Left);
- priv_.room_name_entry.set_xalign(0.0);
- priv_.room_name_entry.set_halign(gtk::Align::Center);
- priv_.room_name_entry.set_sensitive(true);
- priv_.room_name_entry.set_width_chars(25);
- priv_.room_topic_entry.set_sensitive(true);
- priv_.room_topic_label.show();
- return;
- }
- priv_.edit_mode.set(false);
- button.set_label(&label_disabled);
- priv_.room_topic_text_view.set_justification(gtk::Justification::Center);
- priv_.room_name_entry.set_xalign(0.5);
- priv_.room_name_entry.set_sensitive(false);
- priv_.room_name_entry.set_halign(gtk::Align::Fill);
- priv_.room_name_entry.set_width_chars(-1);
- priv_.room_topic_entry.set_sensitive(false);
- priv_.room_topic_label.hide();
-
- let room = this.room();
-
- let room_name = priv_.room_name_entry.buffer().text();
- let topic_buffer = priv_.room_topic_text_view.buffer();
- let topic = topic_buffer.text(&topic_buffer.start_iter(), &topic_buffer.end_iter(), true);
- room.store_room_name(room_name);
- room.store_topic(topic.to_string());
- }));
-
- // Hide edit controls when the user is not eligible to perform the actions.
- let room = self.room();
- let room_name_changeable =
- room.new_allowed_expr(RoomAction::StateEvent(RoomEventType::RoomName));
- let room_topic_changeable =
- room.new_allowed_expr(RoomAction::StateEvent(RoomEventType::RoomTopic));
-
- let edit_toggle_visible = or_expr(room_name_changeable, room_topic_changeable);
- edit_toggle_visible.bind(&edit_toggle.get(), "visible", gtk::Widget::NONE);
- }
-
- fn init_avatar_chooser(&self) {
- let avatar_chooser = gtk::FileChooserNative::new(
- Some(&gettext("Choose avatar")),
- Some(self),
- gtk::FileChooserAction::Open,
- None,
- None,
- );
- avatar_chooser.connect_response(clone!(@weak self as this => move |chooser, response| {
- let file = chooser.file().and_then(|f| f.path());
- if let (gtk::ResponseType::Accept, Some(file)) = (response, file) {
- log::debug!("Chose file {:?}", file);
- this.room().store_avatar(Some(file));
- }
- }));
-
- // We must keep a reference to FileChooserNative around as it is not
- // managed by GTK.
- self.imp()
- .avatar_chooser
- .set(avatar_chooser)
- .expect("File chooser already initialized");
- }
-
- fn avatar_chooser(&self) -> >k::FileChooserNative {
- self.imp().avatar_chooser.get().unwrap()
- }
-
- fn open_avatar_chooser(&self) {
- self.avatar_chooser().show();
- }
-
- fn member_count_changed(&self, n: u32) {
- self.imp().members_count.set_text(&format!("{}", n));
- }
-
fn next_page(&self, next_page: PageName) {
let priv_ = self.imp();
let prev_page = self.visible_page();
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]