[fractal] use Gaction to change avatar in room/account settings
- From: Jordan Petridis <jpetridis src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [fractal] use Gaction to change avatar in room/account settings
- Date: Wed, 16 Jan 2019 12:06:19 +0000 (UTC)
commit cf68e36c75e50436b0a306695d758326565149e2
Author: Julian Sparber <julian sparber net>
Date: Fri Dec 28 15:30:55 2018 +0100
use Gaction to change avatar in room/account settings
fractal-gtk/src/actions/account_settings.rs | 40 +++++++++++
fractal-gtk/src/actions/mod.rs | 65 ++++++++++++++++++
fractal-gtk/src/actions/room_history.rs | 36 +++-------
fractal-gtk/src/actions/room_settings.rs | 46 +++++++++++++
fractal-gtk/src/app/connect/account.rs | 57 +++++++++-------
fractal-gtk/src/appop/account.rs | 18 -----
fractal-gtk/src/appop/room_settings.rs | 13 +++-
fractal-gtk/src/widgets/room_settings.rs | 101 ++++++++++++----------------
8 files changed, 246 insertions(+), 130 deletions(-)
---
diff --git a/fractal-gtk/src/actions/account_settings.rs b/fractal-gtk/src/actions/account_settings.rs
new file mode 100644
index 00000000..a3fdfe03
--- /dev/null
+++ b/fractal-gtk/src/actions/account_settings.rs
@@ -0,0 +1,40 @@
+use crate::i18n::i18n;
+use gio::prelude::*;
+use gio::SimpleAction;
+use gio::SimpleActionGroup;
+use gtk;
+use gtk::prelude::*;
+use std::sync::mpsc::Sender;
+
+use crate::backend::BKCommand;
+
+use crate::widgets::ErrorDialog;
+use crate::widgets::FileDialog::open;
+
+use crate::actions::ButtonState;
+
+// This creates all actions a user can perform in the account settings
+pub fn new(window: >k::Window, backend: &Sender<BKCommand>) -> gio::SimpleActionGroup {
+ let actions = SimpleActionGroup::new();
+ // TODO create two stats loading interaction and connect it to the avatar box
+ let change_avatar =
+ SimpleAction::new_stateful("change-avatar", None, &ButtonState::Sensitive.into());
+
+ actions.add_action(&change_avatar);
+
+ let window_weak = window.downgrade();
+ let backend = backend.clone();
+ change_avatar.connect_activate(move |a, _| {
+ let window = upgrade_weak!(window_weak);
+ if let Some(path) = open(&window, i18n("Select a new avatar").as_str()) {
+ if let Some(file) = path.to_str() {
+ a.change_state(&ButtonState::Insensitive.into());
+ let _ = backend.send(BKCommand::SetUserAvatar(file.to_string()));
+ } else {
+ ErrorDialog::new(false, &i18n("Couldn't open file"));
+ }
+ }
+ });
+
+ actions
+}
diff --git a/fractal-gtk/src/actions/mod.rs b/fractal-gtk/src/actions/mod.rs
index f1710986..70697a83 100644
--- a/fractal-gtk/src/actions/mod.rs
+++ b/fractal-gtk/src/actions/mod.rs
@@ -1,6 +1,71 @@
+use gio::SimpleAction;
+use gio::SimpleActionExt;
+use glib::Cast;
+use glib::ObjectExt;
+use glib::ToVariant;
+use gtk::WidgetExt;
+
+pub mod account_settings;
pub mod global;
pub mod room_history;
+pub mod room_settings;
+pub use self::account_settings as AccountSettings;
pub use self::global as Global;
pub use self::global::AppState;
pub use self::room_history as RoomHistory;
+pub use self::room_settings as RoomSettings;
+
+#[derive(Debug, Clone, PartialEq)]
+pub enum ButtonState {
+ Sensitive,
+ Insensitive,
+}
+
+impl<'a> From<&'a glib::Variant> for ButtonState {
+ fn from(v: &glib::Variant) -> ButtonState {
+ v.get::<bool>().expect("Invalid button state type").into()
+ }
+}
+
+impl From<bool> for ButtonState {
+ fn from(v: bool) -> ButtonState {
+ if v {
+ ButtonState::Sensitive
+ } else {
+ ButtonState::Insensitive
+ }
+ }
+}
+
+impl From<ButtonState> for bool {
+ fn from(v: ButtonState) -> bool {
+ (v == ButtonState::Sensitive)
+ }
+}
+
+impl From<ButtonState> for glib::Variant {
+ fn from(v: ButtonState) -> glib::Variant {
+ (v == ButtonState::Sensitive).to_variant()
+ }
+}
+
+pub trait StateExt {
+ fn bind_button_state(&self, button: >k::Button);
+}
+
+// FIXME: workaround till we get GPropertyAction
+impl StateExt for gio::Action {
+ fn bind_button_state(&self, button: >k::Button) {
+ let button = button.downgrade();
+ if let Some(action) = self.downcast_ref::<SimpleAction>() {
+ action.connect_change_state(move |_, data| {
+ if let Some(data) = data {
+ let state: ButtonState = data.into();
+ let button = upgrade_weak!(button);
+ button.set_sensitive(state.into());
+ }
+ });
+ }
+ }
+}
diff --git a/fractal-gtk/src/actions/room_history.rs b/fractal-gtk/src/actions/room_history.rs
index 255115af..f9c363ec 100644
--- a/fractal-gtk/src/actions/room_history.rs
+++ b/fractal-gtk/src/actions/room_history.rs
@@ -15,9 +15,9 @@ use gio::SimpleActionExt;
use gio::SimpleActionGroup;
use gtk;
use gtk::prelude::*;
-use gtk::ResponseType;
use crate::widgets::ErrorDialog;
+use crate::widgets::FileDialog::save;
use crate::widgets::SourceDialog;
/* This creates all actions the room history can perform */
@@ -113,9 +113,13 @@ pub fn new(backend: Sender<BKCommand>, ui: UI) -> gio::SimpleActionGroup {
gtk::Continue(true)
},
Ok(fname) => {
- let parent = upgrade_weak!(parent_weak, gtk::Continue(true));
- open_save_as_dialog(&parent, fname, &name);
-
+ let window = upgrade_weak!(parent_weak, gtk::Continue(true));
+ if let Some(path) = save(&window, &name) {
+ // TODO use glib to copy file
+ if let Err(_) = fs::copy(fname.clone(), path) {
+ ErrorDialog::new(false, &i18n("Couldn't save file"));
+ }
+ }
gtk::Continue(false)
}
}),
@@ -187,30 +191,6 @@ fn get_room_id(data: &Option<glib::Variant>) -> Option<String> {
data.as_ref()?.get_str().map(|s| s.to_string())
}
-fn open_save_as_dialog(parent: >k::Window, src: String, name: &str) {
- let file_chooser = gtk::FileChooserNative::new(
- Some(i18n("Save media as").as_str()),
- Some(parent),
- gtk::FileChooserAction::Save,
- Some(i18n("_Save").as_str()),
- Some(i18n("_Cancel").as_str()),
- );
-
- file_chooser.set_current_folder(dirs::download_dir().unwrap_or_default());
- file_chooser.set_current_name(name);
-
- file_chooser.connect_response(move |fcd, res| {
- if ResponseType::from(res) == ResponseType::Accept {
- if let Err(_) = fs::copy(src.clone(), fcd.get_filename().unwrap_or_default()) {
- let msg = i18n("Could not save the file");
- ErrorDialog::new(false, &msg);
- }
- }
- });
-
- file_chooser.run();
-}
-
fn request_more_messages(backend: &Sender<BKCommand>, id: Option<String>) -> Option<()> {
let op = App::get_op()?;
let op = op.lock().unwrap();
diff --git a/fractal-gtk/src/actions/room_settings.rs b/fractal-gtk/src/actions/room_settings.rs
new file mode 100644
index 00000000..45494841
--- /dev/null
+++ b/fractal-gtk/src/actions/room_settings.rs
@@ -0,0 +1,46 @@
+use gio::prelude::*;
+use gio::SimpleAction;
+use gio::SimpleActionGroup;
+use glib;
+use gtk;
+use gtk::prelude::*;
+use std::sync::mpsc::Sender;
+
+use crate::backend::BKCommand;
+use crate::i18n::i18n;
+
+use crate::widgets::ErrorDialog;
+use crate::widgets::FileDialog::open;
+
+use crate::actions::ButtonState;
+
+// This creates all actions a user can perform in the room settings
+pub fn new(window: >k::Window, backend: &Sender<BKCommand>) -> gio::SimpleActionGroup {
+ let actions = SimpleActionGroup::new();
+ // TODO create two stats loading interaction and conect it to the avatar box
+ let change_avatar = SimpleAction::new_stateful(
+ "change-avatar",
+ glib::VariantTy::new("s").ok(),
+ &ButtonState::Sensitive.into(),
+ );
+
+ actions.add_action(&change_avatar);
+
+ let window_weak = window.downgrade();
+ let backend = backend.clone();
+ change_avatar.connect_activate(move |a, data| {
+ if let Some(id) = data.as_ref().map(|x| x.to_string()) {
+ let window = upgrade_weak!(window_weak);
+ if let Some(path) = open(&window, i18n("Select a new avatar").as_str()) {
+ if let Some(file) = path.to_str() {
+ a.change_state(&ButtonState::Insensitive.into());
+ let _ = backend.send(BKCommand::SetRoomAvatar(id, file.to_string()));
+ } else {
+ ErrorDialog::new(false, &i18n("Couldn't open file"));
+ }
+ }
+ }
+ });
+
+ actions
+}
diff --git a/fractal-gtk/src/app/connect/account.rs b/fractal-gtk/src/app/connect/account.rs
index 087ded11..9e693793 100644
--- a/fractal-gtk/src/app/connect/account.rs
+++ b/fractal-gtk/src/app/connect/account.rs
@@ -1,11 +1,12 @@
use fractal_api::clone;
+use gio::ActionMapExt;
+use glib;
use gtk;
use gtk::prelude::*;
-use glib;
-
use crate::app::App;
-use crate::i18n::i18n;
+
+use crate::actions::{AccountSettings, StateExt};
impl App {
pub fn connect_account_settings(&self) {
@@ -92,28 +93,38 @@ impl App {
.get_object::<gtk::Button>("account_settings_delete_btn")
.expect("Can't find account_settings_delete_btn in ui file.");
+ // FIXME: don't clone the backend
+ let backend = self.op.lock().unwrap().backend.clone();
+ let window = self.main_window.upcast_ref::<gtk::Window>();
+ let actions = AccountSettings::new(&window, &backend);
+ let container = self
+ .ui
+ .builder
+ .get_object::<gtk::Box>("account_settings_box")
+ .expect("Can't find account_settings_box in ui file.");
+ container.insert_action_group("user-settings", &actions);
+
/* Body */
- avatar_btn.connect_clicked(clone!(op, builder => move |_| {
- let window = builder
- .get_object::<gtk::Window>("main_window")
- .expect("Can't find main_window in ui file.");
- let file_chooser = gtk::FileChooserNative::new(
- i18n("Pick a new avatar").as_str(),
- Some(&window),
- gtk::FileChooserAction::Open,
- Some(i18n("Select").as_str()),
- None
- );
- /* http://gtk-rs.org/docs/gtk/struct.FileChooser.html */
- let result = file_chooser.run();
- if gtk::ResponseType::from(result) == gtk::ResponseType::Accept {
- if let Some(file) = file_chooser.get_filename() {
- if let Some(path) = file.to_str() {
- op.lock().unwrap().update_avatar_account_settings(String::from(path));
- }
+ if let Some(action) = actions.lookup_action("change-avatar") {
+ action.bind_button_state(&avatar_btn);
+ avatar_btn.set_action_name("user-settings.change-avatar");
+ let avatar_spinner = self
+ .ui
+ .builder
+ .get_object::<gtk::Spinner>("account_settings_avatar_spinner")
+ .expect("Can't find account_settings_avatar_spinner in ui file.");
+ let spinner = avatar_spinner.downgrade();
+ avatar_btn.connect_property_sensitive_notify(move |w| {
+ let spinner = upgrade_weak!(spinner);
+ if !w.get_sensitive() {
+ spinner.start();
+ spinner.show();
+ } else {
+ spinner.hide();
+ spinner.stop();
}
- }
- }));
+ });
+ }
let button = name_btn.clone();
name_entry.connect_property_text_notify(clone!(op => move |w| {
diff --git a/fractal-gtk/src/appop/account.rs b/fractal-gtk/src/appop/account.rs
index 04a16b5d..d0a84f8d 100644
--- a/fractal-gtk/src/appop/account.rs
+++ b/fractal-gtk/src/appop/account.rs
@@ -475,24 +475,6 @@ impl AppOp {
}
}
- pub fn update_avatar_account_settings(&mut self, file: String) {
- let avatar_spinner = self
- .ui
- .builder
- .get_object::<gtk::Spinner>("account_settings_avatar_spinner")
- .expect("Can't find account_settings_avatar_spinner in ui file.");
- let avatar_btn = self
- .ui
- .builder
- .get_object::<gtk::Button>("account_settings_avatar_button")
- .expect("Can't find account_settings_avatar_button in ui file.");
- let command = BKCommand::SetUserAvatar(file.clone());
- self.backend.send(command).unwrap();
- avatar_btn.set_sensitive(false);
- avatar_spinner.show();
- self.show_avatar();
- }
-
pub fn show_new_username(&mut self, name: Option<String>) {
let entry = self
.ui
diff --git a/fractal-gtk/src/appop/room_settings.rs b/fractal-gtk/src/appop/room_settings.rs
index 9505aae3..6bb3fb9d 100644
--- a/fractal-gtk/src/appop/room_settings.rs
+++ b/fractal-gtk/src/appop/room_settings.rs
@@ -8,6 +8,11 @@ use crate::widgets;
impl AppOp {
pub fn create_room_settings(&mut self) -> Option<()> {
+ let window = self
+ .ui
+ .builder
+ .get_object::<gtk::Window>("main_window")
+ .expect("Can't find main_window in ui file.");
let stack = self
.ui
.builder
@@ -21,8 +26,12 @@ impl AppOp {
{
let room = self.rooms.get(&self.active_room.clone()?)?;
- let mut panel =
- widgets::RoomSettings::new(self.backend.clone(), self.uid.clone(), room.clone());
+ let mut panel = widgets::RoomSettings::new(
+ &window,
+ self.backend.clone(),
+ self.uid.clone(),
+ room.clone(),
+ );
let (body, header) = panel.create()?;
/* remove old panel */
diff --git a/fractal-gtk/src/widgets/room_settings.rs b/fractal-gtk/src/widgets/room_settings.rs
index 07d525e9..b983983c 100644
--- a/fractal-gtk/src/widgets/room_settings.rs
+++ b/fractal-gtk/src/widgets/room_settings.rs
@@ -1,12 +1,15 @@
use fractal_api::clone;
use std::cell::RefCell;
use std::rc::Rc;
+use std::sync::mpsc::Sender;
-use crate::i18n::i18n;
use crate::i18n::ni18n_f;
+use gio::prelude::*;
use gtk;
use gtk::prelude::*;
+use crate::actions;
+use crate::actions::{ButtonState, StateExt};
use crate::backend::BKCommand;
use crate::cache::download_to_cache;
use crate::types::Member;
@@ -15,10 +18,10 @@ use crate::widgets;
use crate::widgets::avatar::AvatarExt;
use crate::widgets::members_list::MembersList;
use fractal_api::types::Room;
-use std::sync::mpsc::Sender;
#[derive(Debug, Clone)]
pub struct RoomSettings {
+ actions: gio::SimpleActionGroup,
room: Room,
uid: Option<String>,
builder: gtk::Builder,
@@ -27,14 +30,27 @@ pub struct RoomSettings {
}
impl RoomSettings {
- pub fn new(backend: Sender<BKCommand>, uid: Option<String>, room: Room) -> RoomSettings {
+ pub fn new(
+ window: >k::Window,
+ backend: Sender<BKCommand>,
+ uid: Option<String>,
+ room: Room,
+ ) -> RoomSettings {
let builder = gtk::Builder::new();
builder
.add_from_resource("/org/gnome/Fractal/ui/room_settings.ui")
.expect("Can't load ui file: room_settings.ui");
+ let stack = builder
+ .get_object::<gtk::Stack>("room_settings_stack")
+ .expect("Can't find room_settings_stack in ui file.");
+
+ let actions = actions::RoomSettings::new(&window, &backend);
+ stack.insert_action_group("room-settings", Some(&actions));
+
RoomSettings {
+ actions,
room: room,
uid: uid,
builder: builder,
@@ -114,6 +130,7 @@ impl RoomSettings {
button.set_visible(result.is_some());
}));
+ // TODO: create actions for all button
let button = name_btn.clone();
name_entry.connect_activate(move |_w| {
let _ = button.emit("clicked", &[]);
@@ -132,33 +149,27 @@ impl RoomSettings {
this.borrow_mut().update_room_topic();
}));
- /* Connect avatar button */
- avatar_btn.connect_clicked(clone!(this => move |w| {
- this.borrow_mut().create_file_chooser(w);
- }));
- }
-
- fn create_file_chooser(&mut self, w: >k::Button) -> Option<()> {
- let window = w.get_toplevel()?;
- if let Ok(window) = window.downcast::<gtk::Window>() {
- /* http://gtk-rs.org/docs/gtk/struct.FileChooser.html */
- let file_chooser = gtk::FileChooserNative::new(
- i18n("Pick a new room avatar").as_str(),
- Some(&window),
- gtk::FileChooserAction::Open,
- Some(i18n("Select").as_str()),
- None,
- );
- let result = file_chooser.run();
- if gtk::ResponseType::from(result) == gtk::ResponseType::Accept {
- if let Some(file) = file_chooser.get_filename() {
- if let Some(path) = file.to_str() {
- self.update_room_avatar(String::from(path));
- }
+ if let Some(action) = self.actions.lookup_action("change-avatar") {
+ action.bind_button_state(&avatar_btn);
+ let data = glib::Variant::from(&self.room.id);
+ avatar_btn.set_action_target_value(&data);
+ avatar_btn.set_action_name("room-settings.change-avatar");
+ let avatar_spinner = self
+ .builder
+ .get_object::<gtk::Spinner>("room_settings_avatar_spinner")
+ .expect("Can't find room_settings_avatar_spinner in ui file.");
+ let spinner = avatar_spinner.downgrade();
+ avatar_btn.connect_property_sensitive_notify(move |w| {
+ let spinner = upgrade_weak!(spinner);
+ if !w.get_sensitive() {
+ spinner.start();
+ spinner.show();
+ } else {
+ spinner.hide();
+ spinner.stop();
}
- }
+ });
}
- None
}
fn init_room_settings(&mut self) -> Option<()> {
@@ -439,24 +450,6 @@ impl RoomSettings {
return None;
}
- pub fn update_room_avatar(&mut self, file: String) -> Option<()> {
- let avatar_spinner = self
- .builder
- .get_object::<gtk::Spinner>("room_settings_avatar_spinner")
- .expect("Can't find room_settings_avatar_spinner in ui file.");
- let avatar_btn = self
- .builder
- .get_object::<gtk::Button>("room_settings_avatar_button")
- .expect("Can't find room_settings_avatar_button in ui file.");
- let room = &self.room;
- let command = BKCommand::SetRoomAvatar(room.id.clone(), file.clone());
- self.backend.send(command).unwrap();
- self.room_settings_show_avatar(true);
- avatar_btn.set_sensitive(false);
- avatar_spinner.show();
- None
- }
-
pub fn update_room_name(&mut self) -> Option<()> {
let entry = self
.builder
@@ -530,19 +523,9 @@ impl RoomSettings {
}
pub fn show_new_room_avatar(&self) {
- let avatar_spinner = self
- .builder
- .get_object::<gtk::Spinner>("room_settings_avatar_spinner")
- .expect("Can't find room_settings_avatar_spinner in ui file.");
- let avatar_btn = self
- .builder
- .get_object::<gtk::Button>("room_settings_avatar_button")
- .expect("Can't find room_settings_avatar_button in ui file.");
-
- /* We could update the avatar for this room,
- * but we are waiting for the new avatar event */
- avatar_spinner.hide();
- avatar_btn.set_sensitive(true);
+ if let Some(action) = self.actions.lookup_action("change-avatar") {
+ action.change_state(&ButtonState::Sensitive.into());
+ }
}
pub fn show_new_room_name(&self) {
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]