[fractal/get-widgets-from-ui-struct: 6/6] Access direct_chat_dialog widget and children directly




commit a11a1f72eb8caab7ad5db751babaccedcc22e1c0
Author: Alejandro Domínguez <adomu net-c com>
Date:   Thu Dec 10 19:57:23 2020 +0100

    Access direct_chat_dialog widget and children directly

 fractal-gtk/res/ui/direct_chat.ui    |   1 -
 fractal-gtk/src/meson.build          |   1 -
 fractal-gtk/src/ui/connect/direct.rs | 125 ---------------------
 fractal-gtk/src/ui/connect/mod.rs    |   3 +-
 fractal-gtk/src/ui/invite.rs         |   4 +-
 fractal-gtk/src/ui/member.rs         |  40 +++----
 fractal-gtk/src/ui/mod.rs            |   7 +-
 fractal-gtk/src/ui/start_chat.rs     | 203 ++++++++++++++++++++++++++++-------
 8 files changed, 189 insertions(+), 195 deletions(-)
---
diff --git a/fractal-gtk/res/ui/direct_chat.ui b/fractal-gtk/res/ui/direct_chat.ui
index 65940eed..2726f8e1 100644
--- a/fractal-gtk/res/ui/direct_chat.ui
+++ b/fractal-gtk/res/ui/direct_chat.ui
@@ -13,7 +13,6 @@
     <property name="type_hint">dialog</property>
     <property name="deletable">False</property>
     <property name="gravity">center</property>
-    <property name="transient_for">main_window</property>
     <child internal-child="vbox">
       <object class="GtkBox">
         <property name="can_focus">False</property>
diff --git a/fractal-gtk/src/meson.build b/fractal-gtk/src/meson.build
index 2ec9d8b3..5b36427b 100644
--- a/fractal-gtk/src/meson.build
+++ b/fractal-gtk/src/meson.build
@@ -86,7 +86,6 @@ app_sources = files(
   'model/room.rs',
   'ui/connect/autocomplete.rs',
   'ui/connect/directory.rs',
-  'ui/connect/direct.rs',
   'ui/connect/headerbar.rs',
   'ui/connect/invite.rs',
   'ui/connect/join_room.rs',
diff --git a/fractal-gtk/src/ui/connect/mod.rs b/fractal-gtk/src/ui/connect/mod.rs
index f7e64b55..f69a4130 100644
--- a/fractal-gtk/src/ui/connect/mod.rs
+++ b/fractal-gtk/src/ui/connect/mod.rs
@@ -1,5 +1,4 @@
 mod autocomplete;
-mod direct;
 mod directory;
 mod headerbar;
 mod invite;
@@ -30,7 +29,7 @@ impl UI {
             .connect(&self.builder, &self.main_window, app_runtime.clone());
         invite::connect_dialog(self, app_runtime.clone());
         invite::connect_user(self, app_runtime.clone());
-        direct::connect(self, app_runtime.clone());
+        self.direct_chat_dialog.connect(app_runtime.clone());
         roomlist_search::connect(self, app_runtime);
         swipeable_widgets::connect(self);
     }
diff --git a/fractal-gtk/src/ui/invite.rs b/fractal-gtk/src/ui/invite.rs
index db391f9e..a8dd3362 100644
--- a/fractal-gtk/src/ui/invite.rs
+++ b/fractal-gtk/src/ui/invite.rs
@@ -43,9 +43,7 @@ impl UI {
             }
         }
 
-        if let Some(btn) = self.builder.get_object::<gtk::Button>("direct_chat_button") {
-            btn.set_sensitive(true)
-        }
+        self.direct_chat_dialog.button.set_sensitive(true);
 
         if let Some(btn) = self.builder.get_object::<gtk::Button>("invite_button") {
             btn.set_sensitive(true)
diff --git a/fractal-gtk/src/ui/member.rs b/fractal-gtk/src/ui/member.rs
index 795fa2e6..1747727d 100644
--- a/fractal-gtk/src/ui/member.rs
+++ b/fractal-gtk/src/ui/member.rs
@@ -24,28 +24,30 @@ impl UI {
         users: Vec<Member>,
         search_type: SearchType,
     ) {
-        let (entry_label, listbox_label, scroll_label) = match search_type {
-            SearchType::Invite => ("invite_entry", "user_search_box", "user_search_scroll"),
+        let (entry, listbox, scroll) = match search_type {
+            SearchType::Invite => {
+                let entry = self
+                    .builder
+                    .get_object::<gtk::TextView>("invite_entry")
+                    .expect("Can't find invite_entry in ui file.");
+                let listbox = self
+                    .builder
+                    .get_object::<gtk::ListBox>("user_search_box")
+                    .expect("Can't find user_search_box in ui file.");
+                let scroll = self
+                    .builder
+                    .get_object::<gtk::ScrolledWindow>("user_search_scroll")
+                    .expect("Can't find user_search_scroll in ui file.");
+
+                (entry, listbox, scroll)
+            }
             SearchType::DirectChat => (
-                "to_chat_entry",
-                "direct_chat_search_box",
-                "direct_chat_search_scroll",
+                self.direct_chat_dialog.to_chat_entry.clone(),
+                self.direct_chat_dialog.search_box.clone(),
+                self.direct_chat_dialog.search_scroll.clone(),
             ),
         };
 
-        let entry = self
-            .builder
-            .get_object::<gtk::TextView>(entry_label)
-            .expect("Can't find invite_entry in ui file.");
-        let listbox = self
-            .builder
-            .get_object::<gtk::ListBox>(listbox_label)
-            .expect("Can't find user_search_box in ui file.");
-        let scroll = self
-            .builder
-            .get_object::<gtk::Widget>(scroll_label)
-            .expect("Can't find user_search_scroll in ui file.");
-
         if let Some(buffer) = entry.get_buffer() {
             let start = buffer.get_start_iter();
             let end = buffer.get_end_iter();
@@ -73,7 +75,7 @@ fn search_finished(
     rooms: &RoomList,
     mut users: Vec<Member>,
     listbox: gtk::ListBox,
-    scroll: gtk::Widget,
+    scroll: gtk::ScrolledWindow,
     term: Option<String>,
 ) {
     for ch in listbox.get_children().iter() {
diff --git a/fractal-gtk/src/ui/mod.rs b/fractal-gtk/src/ui/mod.rs
index 3a8d9219..04d464e0 100644
--- a/fractal-gtk/src/ui/mod.rs
+++ b/fractal-gtk/src/ui/mod.rs
@@ -36,6 +36,7 @@ pub struct UI {
     pub leaflet: libhandy::Leaflet,
     pub deck: libhandy::Deck,
     pub account_settings: account::AccountSettings,
+    pub direct_chat_dialog: start_chat::DirectChatDialog,
 }
 
 impl UI {
@@ -89,9 +90,6 @@ impl UI {
 
         // Depends on main_window
         // These are all dialogs transient for main_window
-        builder
-            .add_from_resource("/org/gnome/Fractal/ui/direct_chat.ui")
-            .expect("Can't load ui file: direct_chat.ui");
         builder
             .add_from_resource("/org/gnome/Fractal/ui/invite.ui")
             .expect("Can't load ui file: invite.ui");
@@ -125,6 +123,8 @@ impl UI {
             .get_object::<libhandy::Deck>("main_deck")
             .expect("Couldn't find main_deck in ui file");
 
+        let direct_chat_dialog = start_chat::DirectChatDialog::new(&main_window);
+
         UI {
             builder,
             gtk_app,
@@ -140,6 +140,7 @@ impl UI {
             leaflet,
             deck,
             account_settings,
+            direct_chat_dialog,
         }
     }
 
diff --git a/fractal-gtk/src/ui/start_chat.rs b/fractal-gtk/src/ui/start_chat.rs
index a0b79a38..6e13a440 100644
--- a/fractal-gtk/src/ui/start_chat.rs
+++ b/fractal-gtk/src/ui/start_chat.rs
@@ -1,60 +1,181 @@
 use super::UI;
+use crate::app::AppRuntime;
+use glib::clone;
+use glib::source::Continue;
 use gtk::prelude::*;
+use std::sync::{Arc, Mutex};
 
-impl UI {
-    pub fn show_direct_chat_dialog(&self) {
-        let dialog = self
-            .builder
-            .get_object::<gtk::Dialog>("direct_chat_dialog")
+pub struct DirectChatDialog {
+    pub root: gtk::Dialog,
+    pub cancel: gtk::Button,
+    pub button: gtk::Button,
+    pub to_chat_entry_box: gtk::Box,
+    pub to_chat_entry: gtk::TextView,
+    pub search_scroll: gtk::ScrolledWindow,
+    pub search_box: gtk::ListBox,
+}
+
+impl DirectChatDialog {
+    pub fn new(parent: &libhandy::ApplicationWindow) -> Self {
+        let builder = gtk::Builder::from_resource("/org/gnome/Fractal/ui/direct_chat.ui");
+        let root: gtk::Dialog = builder
+            .get_object("direct_chat_dialog")
             .expect("Can't find direct_chat_dialog in ui file.");
-        let scroll = self
-            .builder
-            .get_object::<gtk::Widget>("direct_chat_search_scroll")
-            .expect("Can't find direct_chat_search_scroll in ui file.");
-        if let Some(btn) = self.builder.get_object::<gtk::Button>("direct_chat_button") {
-            btn.set_sensitive(false)
+        root.set_transient_for(Some(parent));
+
+        Self {
+            root,
+            cancel: builder
+                .get_object("cancel_direct_chat")
+                .expect("Can't find cancel_direct_chat in ui file."),
+            button: builder
+                .get_object("direct_chat_button")
+                .expect("Can't find direct_chat_button in ui file."),
+            to_chat_entry_box: builder
+                .get_object("to_chat_entry_box")
+                .expect("Can't find to_chat_entry_box in ui file."),
+            to_chat_entry: builder
+                .get_object("to_chat_entry")
+                .expect("Can't find to_chat_entry in ui file."),
+            search_scroll: builder
+                .get_object("direct_chat_search_scroll")
+                .expect("Can't find search_scroll in ui file."),
+            search_box: builder
+                .get_object("direct_chat_search_box")
+                .expect("Can't find direct_chat_search_box in ui file."),
         }
-        dialog.present();
-        scroll.hide();
     }
 
-    pub fn close_direct_chat_dialog(&mut self) {
-        let listbox = self
-            .builder
-            .get_object::<gtk::ListBox>("direct_chat_search_box")
-            .expect("Can't find direct_chat_search_box in ui file.");
-        let scroll = self
-            .builder
-            .get_object::<gtk::Widget>("direct_chat_search_scroll")
-            .expect("Can't find direct_chat_search_scroll in ui file.");
-        let to_chat_entry = self
-            .builder
-            .get_object::<gtk::TextView>("to_chat_entry")
-            .expect("Can't find to_chat_entry in ui file.");
-        let entry = self
-            .builder
-            .get_object::<gtk::TextView>("to_chat_entry")
-            .expect("Can't find to_chat_entry in ui file.");
-        let dialog = self
-            .builder
-            .get_object::<gtk::Dialog>("direct_chat_dialog")
-            .expect("Can't find direct_chat_dialog in ui file.");
+    pub fn connect(&self, app_runtime: AppRuntime) {
+        if let Some(buffer) = self.to_chat_entry.get_buffer() {
+            let placeholder_tag = gtk::TextTag::new(Some("placeholder"));
+
+            placeholder_tag.set_property_foreground_rgba(Some(&gdk::RGBA {
+                red: 1.0,
+                green: 1.0,
+                blue: 1.0,
+                alpha: 0.5,
+            }));
+
+            if let Some(tag_table) = buffer.get_tag_table() {
+                tag_table.add(&placeholder_tag);
+            }
+        }
+
+        // this is used to cancel the timeout and not search for every key input. We'll wait 500ms
+        // without key release event to launch the search
+        let source_id: Arc<Mutex<Option<glib::source::SourceId>>> = Arc::new(Mutex::new(None));
+        self.to_chat_entry.connect_key_release_event(clone!(@strong app_runtime => move |entry, _| {
+        {
+            let mut id = source_id.lock().unwrap();
+            if let Some(sid) = id.take() {
+                glib::source::source_remove(sid);
+            }
+        }
+
+        let sid = glib::timeout_add_local(
+            500,
+            clone!(
+                @strong entry,
+                @strong source_id,
+                @strong app_runtime
+                => move || {
+                    if let Some(buffer) = entry.get_buffer() {
+                        let start = buffer.get_start_iter();
+                        let end = buffer.get_end_iter();
+
+                        if let Some(text) =
+                            buffer.get_text(&start, &end, false).map(|gstr| gstr.to_string())
+                        {
+                            app_runtime.update_state_with(|state| state.search_invite_user(text));
+                        }
+                    }
+
+                    *(source_id.lock().unwrap()) = None;
+                    Continue(false)
+                }),
+            );
+
+            *(source_id.lock().unwrap()) = Some(sid);
+            glib::signal::Inhibit(false)
+        }));
+
+        self.to_chat_entry.connect_focus_in_event(clone!(
+            @strong self.to_chat_entry_box as to_chat_entry_box,
+            @strong app_runtime
+            => move |_, _| {
+                to_chat_entry_box.get_style_context().add_class("message-input-focused");
 
+                app_runtime.update_state_with(|state| state.remove_invite_user_dialog_placeholder());
+
+                Inhibit(false)
+            }
+        ));
+
+        self.to_chat_entry.connect_focus_out_event(clone!(
+            @strong self.to_chat_entry_box as to_chat_entry_box,
+            @strong app_runtime
+            => move |_, _| {
+                to_chat_entry_box.get_style_context().remove_class("message-input-focused");
+
+                app_runtime.update_state_with(|state| state.set_invite_user_dialog_placeholder());
+
+                Inhibit(false)
+            }
+        ));
+
+        if let Some(buffer) = self.to_chat_entry.get_buffer() {
+            buffer.connect_delete_range(clone!(@strong app_runtime => move |_, _, _| {
+                glib::idle_add_local(clone!(@strong app_runtime => move || {
+                    app_runtime.update_state_with(|state| state.detect_removed_invite());
+                    Continue(false)
+                }));
+            }));
+        }
+
+        self.root
+            .connect_delete_event(clone!(@strong app_runtime => move |_, _| {
+                app_runtime.update_state_with(|state| state.ui.close_direct_chat_dialog());
+                glib::signal::Inhibit(true)
+            }));
+        self.cancel
+            .connect_clicked(clone!(@strong app_runtime => move |_| {
+                app_runtime.update_state_with(|state| state.ui.close_direct_chat_dialog());
+            }));
+        self.button.set_sensitive(false);
+        self.button.connect_clicked(move |_| {
+            app_runtime.update_state_with(|state| state.start_chat());
+        });
+    }
+
+    pub fn show(&self) {
+        self.button.set_sensitive(false);
+        self.root.present();
+        self.search_scroll.hide();
+    }
+}
+
+impl UI {
+    pub fn show_direct_chat_dialog(&self) {
+        self.direct_chat_dialog.show();
+    }
+
+    pub fn close_direct_chat_dialog(&mut self) {
         self.invite_list = vec![];
-        if let Some(buffer) = to_chat_entry.get_buffer() {
+        if let Some(buffer) = self.direct_chat_dialog.to_chat_entry.get_buffer() {
             let mut start = buffer.get_start_iter();
             let mut end = buffer.get_end_iter();
 
             buffer.delete(&mut start, &mut end);
         }
-        for ch in listbox.get_children().iter() {
-            listbox.remove(ch);
+        for ch in self.direct_chat_dialog.search_box.get_children().iter() {
+            self.direct_chat_dialog.search_box.remove(ch);
         }
-        scroll.hide();
-        if let Some(buffer) = entry.get_buffer() {
+        self.direct_chat_dialog.search_scroll.hide();
+        if let Some(buffer) = self.direct_chat_dialog.to_chat_entry.get_buffer() {
             buffer.set_text("");
         }
-        dialog.hide();
-        dialog.resize(300, 200);
+        self.direct_chat_dialog.root.hide();
+        self.direct_chat_dialog.root.resize(300, 200);
     }
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]