[fractal/ui-refactor: 9/15] Remove OP global variable




commit 815535d61c54c4958ceddf313d06706ee77e17a6
Author: Alejandro Domínguez <adomu net-c com>
Date:   Sat Oct 17 10:08:31 2020 +0200

    Remove OP global variable

 fractal-gtk/src/actions/global.rs             | 293 ++++++++++++++------------
 fractal-gtk/src/app/mod.rs                    |  69 +++---
 fractal-gtk/src/appop/connect/autocomplete.rs |  12 +-
 fractal-gtk/src/appop/connect/mod.rs          |   2 +-
 fractal-gtk/src/uibuilder.rs                  |   8 +-
 fractal-gtk/src/widgets/autocomplete.rs       | 119 +++++------
 fractal-gtk/src/widgets/login.rs              |  18 +-
 7 files changed, 268 insertions(+), 253 deletions(-)
---
diff --git a/fractal-gtk/src/actions/global.rs b/fractal-gtk/src/actions/global.rs
index 0c24ff37..0bc53272 100644
--- a/fractal-gtk/src/actions/global.rs
+++ b/fractal-gtk/src/actions/global.rs
@@ -1,8 +1,6 @@
 use glib::clone;
 use log::{debug, info};
 use std::convert::TryInto;
-use std::rc::Rc;
-use std::sync::{Arc, Mutex};
 
 use crate::app::AppRuntime;
 use crate::appop::AppOp;
@@ -63,9 +61,11 @@ impl From<AppState> for glib::Variant {
     }
 }
 
-/* This creates globale actions which are connected to the application */
-/* TODO: Remove op */
-pub fn new(app: &gtk::Application, app_runtime: AppRuntime, op: &Arc<Mutex<AppOp>>) {
+// This creates global actions which are connected to the application
+pub fn new(appop: &AppOp) {
+    let app = &appop.ui.gtk_app;
+    let app_runtime = appop.app_runtime.clone();
+
     let settings = SimpleAction::new("settings", None);
     let chat = SimpleAction::new("start_chat", None);
     let newr = SimpleAction::new("new_room", None);
@@ -142,164 +142,177 @@ pub fn new(app: &gtk::Application, app_runtime: AppRuntime, op: &Arc<Mutex<AppOp
         app.quit();
     }));
 
-    about.connect_activate(clone!(@strong op => move |_, _| op.lock().unwrap().about_dialog() ));
-    main_menu.connect_activate(clone!(@strong op => move |_, _| op.lock().unwrap().main_menu() ));
+    about.connect_activate(clone!(@strong app_runtime => move |_, _| {
+        app_runtime.update_state_with(|state| state.about_dialog());
+    }));
+    main_menu.connect_activate(clone!(@strong app_runtime => move |_, _| {
+        app_runtime.update_state_with(|state| state.main_menu());
+    }));
 
     settings.connect_activate(move |_, _| {
         info!("SETTINGS");
     });
     settings.set_enabled(false);
 
-    logout.connect_activate(clone!(@strong op => move |_, _| op.lock().unwrap().logout() ));
-    inv.connect_activate(
-        clone!(@strong op => move |_, _| op.lock().unwrap().show_invite_user_dialog() ),
-    );
-    chat.connect_activate(
-        clone!(@strong op => move |_, _| op.lock().unwrap().show_direct_chat_dialog() ),
-    );
-    leave.connect_activate(
-        clone!(@strong op => move |_, _| op.lock().unwrap().leave_active_room() ),
-    );
-    newr.connect_activate(clone!(@strong op => move |_, _| op.lock().unwrap().new_room_dialog() ));
-    joinr.connect_activate(
-        clone!(@strong op => move |_, _| op.lock().unwrap().join_to_room_dialog() ),
-    );
-
-    previous_room.connect_activate(clone!(@strong op => move |_, _| {
-        let mut op = op.lock().unwrap();
-        if let Some(id) = op.roomlist.prev_id() {
-            op.set_active_room_by_id(id);
-        } else if let Some(last_room) = op.roomlist.last_id() {
-            op.set_active_room_by_id(last_room);
-        }
+    logout.connect_activate(clone!(@strong app_runtime => move |_, _| {
+        app_runtime.update_state_with(|state| state.logout());
     }));
-    next_room.connect_activate(clone!(@strong op => move |_, _| {
-        let mut op = op.lock().unwrap();
-        if let Some(id) = op.roomlist.next_id() {
-            op.set_active_room_by_id(id);
-        } else if let Some(first_room) = op.roomlist.first_id() {
-            op.set_active_room_by_id(first_room);
-        }
+    inv.connect_activate(clone!(@strong app_runtime => move |_, _| {
+        app_runtime.update_state_with(|state| state.show_invite_user_dialog());
     }));
-    prev_unread_room.connect_activate(clone!(@strong op => move |_, _| {
-        let mut op = op.lock().unwrap();
-        if let Some(id) = op.roomlist.prev_unread_id() {
-            op.set_active_room_by_id(id);
-        }
+    chat.connect_activate(clone!(@strong app_runtime => move |_, _| {
+        app_runtime.update_state_with(|state| state.show_direct_chat_dialog());
     }));
-    next_unread_room.connect_activate(clone!(@strong op => move |_, _| {
-        let mut op = op.lock().unwrap();
-        if let Some(id) = op.roomlist.next_unread_id() {
-            op.set_active_room_by_id(id);
-        }
+    leave.connect_activate(clone!(@strong app_runtime => move |_, _| {
+        app_runtime.update_state_with(|state| state.leave_active_room());
     }));
-    first_room.connect_activate(clone!(@strong op => move |_, _| {
-        let mut op = op.lock().unwrap();
-        if let Some(id) = op.roomlist.first_id() {
-            op.set_active_room_by_id(id);
-        }
+    newr.connect_activate(clone!(@strong app_runtime => move |_, _| {
+        app_runtime.update_state_with(|state| state.new_room_dialog());
     }));
-    last_room.connect_activate(clone!(@strong op => move |_, _| {
-        let mut op = op.lock().unwrap();
-        if let Some(id) = op.roomlist.last_id() {
-            op.set_active_room_by_id(id);
-        }
+    joinr.connect_activate(clone!(@strong app_runtime => move |_, _| {
+        app_runtime.update_state_with(|state| state.join_to_room_dialog());
     }));
-    older_messages.connect_activate(clone!(@strong op => move |_, _| {
-        if let Some(ref mut hist) = op.lock().unwrap().history {
-            hist.page_up();
-        }
+
+    previous_room.connect_activate(clone!(@strong app_runtime => move |_, _| {
+        app_runtime.update_state_with(|state| {
+            if let Some(id) = state.roomlist.prev_id() {
+                state.set_active_room_by_id(id);
+            } else if let Some(last_room) = state.roomlist.last_id() {
+                state.set_active_room_by_id(last_room);
+            }
+        });
     }));
-    newer_messages.connect_activate(clone!(@strong op => move |_, _| {
-        if let Some(ref mut hist) = op.lock().unwrap().history {
-            hist.page_down();
-        }
+    next_room.connect_activate(clone!(@strong app_runtime => move |_, _| {
+        app_runtime.update_state_with(|state| {
+            if let Some(id) = state.roomlist.next_id() {
+                state.set_active_room_by_id(id);
+            } else if let Some(first_room) = state.roomlist.first_id() {
+                state.set_active_room_by_id(first_room);
+            }
+        });
+    }));
+    prev_unread_room.connect_activate(clone!(@strong app_runtime => move |_, _| {
+        app_runtime.update_state_with(|state| {
+            if let Some(id) = state.roomlist.prev_unread_id() {
+                state.set_active_room_by_id(id);
+            }
+        });
+    }));
+    next_unread_room.connect_activate(clone!(@strong app_runtime => move |_, _| {
+        app_runtime.update_state_with(|state| {
+            if let Some(id) = state.roomlist.next_unread_id() {
+                state.set_active_room_by_id(id);
+            }
+        });
+    }));
+    first_room.connect_activate(clone!(@strong app_runtime => move |_, _| {
+        app_runtime.update_state_with(|state| {
+            if let Some(id) = state.roomlist.first_id() {
+                state.set_active_room_by_id(id);
+            }
+        });
+    }));
+    last_room.connect_activate(clone!(@strong app_runtime => move |_, _| {
+        app_runtime.update_state_with(|state| {
+            if let Some(id) = state.roomlist.last_id() {
+                state.set_active_room_by_id(id);
+            }
+        });
+    }));
+    older_messages.connect_activate(clone!(@strong app_runtime => move |_, _| {
+        app_runtime.update_state_with(|state| {
+            if let Some(ref mut hist) = state.history {
+                hist.page_up();
+            }
+        });
+    }));
+    newer_messages.connect_activate(clone!(@strong app_runtime => move |_, _| {
+        app_runtime.update_state_with(|state| {
+            if let Some(ref mut hist) = state.history {
+                hist.page_down();
+            }
+        });
     }));
 
-    let back_history = op.lock().unwrap().room_back_history.clone();
-
-    account.connect_activate(clone!(
-    @strong op,
-    @weak back_history as back
-    => move |_, _| {
-        op.lock().unwrap().show_account_settings_dialog();
-        back.borrow_mut().push(AppState::AccountSettings);
+    account.connect_activate(clone!(@strong app_runtime => move |_, _| {
+        app_runtime.update_state_with(|state| {
+            state.show_account_settings_dialog();
+            state.room_back_history.borrow_mut().push(AppState::AccountSettings);
+        });
     }));
 
-    directory.connect_activate(clone!(
-    @strong op,
-    @weak back_history as back
-    => move |_, _| {
-        op.lock().unwrap().set_state(AppState::Directory);
-        back.borrow_mut().push(AppState::Directory);
+    directory.connect_activate(clone!(@strong app_runtime => move |_, _| {
+        app_runtime.update_state_with(|state| {
+            state.set_state(AppState::Directory);
+            state.room_back_history.borrow_mut().push(AppState::Directory);
+        });
     }));
 
     /* TODO: We could pass a message to this to highlight it in the room history, might be
      * handy when opening the room from a notification */
-    open_room.connect_activate(clone!(
-    @strong op,
-    @weak back_history as back
-    => move |_, data| {
-        if let Some(id) = get_room_id(data) {
-            op.lock().unwrap().set_active_room_by_id(id);
-           /* This does nothing if fractal is already in focus */
-            op.lock().unwrap().activate();
-        }
-        // Push a new state only if the current state is not already Room
-        let push = if let Some(last) = back.borrow().last() {
-            last != &AppState::Room
-        } else {
-            true
-        };
-        if push {
-            back.borrow_mut().push(AppState::Room);
-        }
+    open_room.connect_activate(clone!(@strong app_runtime => move |_, data| {
+        let data = data.cloned();
+        app_runtime.update_state_with(move |state| {
+            if let Some(id) = get_room_id(data.as_ref()) {
+                state.set_active_room_by_id(id);
+                // This does nothing if fractal is already in focus
+                state.activate();
+            }
+            // Push a new state only if the current state is not already Room
+            let push = if let Some(last) = state.room_back_history.borrow().last() {
+                last != &AppState::Room
+            } else {
+                true
+            };
+            if push {
+                state.room_back_history.borrow_mut().push(AppState::Room);
+            }
+        });
     }));
 
-    room_settings.connect_activate(clone!(
-    @strong op,
-    @weak back_history as back
-    => move |_, _| {
-        op.lock().unwrap().create_room_settings();
-        back.borrow_mut().push(AppState::RoomSettings);
+    room_settings.connect_activate(clone!(@strong app_runtime => move |_, _| {
+        app_runtime.update_state_with(|state| {
+            state.create_room_settings();
+            state.room_back_history.borrow_mut().push(AppState::RoomSettings);
+        });
     }));
 
-    media_viewer.connect_activate(clone!(
-    @weak back_history as back
-    => move |_, data| {
+    media_viewer.connect_activate(clone!(@strong app_runtime => move |_, data| {
         open_viewer(&app_runtime, data.cloned());
-        back.borrow_mut().push(AppState::MediaViewer);
+        app_runtime.update_state_with(|state| {
+            state.room_back_history.borrow_mut().push(AppState::MediaViewer);
+        });
     }));
 
-    deck_back.connect_activate(clone!(@strong op => move |_, _| {
-        let deck = op.lock().unwrap().deck.clone();
-        if deck.get_can_swipe_back() {
-            deck.navigate(libhandy::NavigationDirection::Back);
-        }
+    deck_back.connect_activate(clone!(@strong app_runtime => move |_, _| {
+        app_runtime.update_state_with(|state| {
+            let deck = state.deck.clone();
+            if deck.get_can_swipe_back() {
+                deck.navigate(libhandy::NavigationDirection::Back);
+            }
+        });
     }));
 
-    let mv = op.lock().unwrap().media_viewer.clone();
-    let back_weak = Rc::downgrade(&back_history);
-    back.connect_activate(clone!(@weak mv, @strong op => move |_, _| {
-        if let Some(mut mv) = mv.borrow_mut().take() {
-            mv.disconnect_signal_id();
-        }
+    back.connect_activate(clone!(@strong app_runtime => move |_, _| {
+        app_runtime.update_state_with(|state| {
+            if let Some(mut mv) = state.media_viewer.borrow_mut().take() {
+                mv.disconnect_signal_id();
+            }
 
-        // Remove the current state from the store
-        if let Some(back) = back_weak.upgrade() {
-            back.borrow_mut().pop();
-            let mut op = op.lock().unwrap();
-            if let Some(state) = back.borrow().last() {
-                debug!("Go back to state {:?}", state);
-                op.set_state(*state);
+            // Remove the current state from the store
+            state.room_back_history.borrow_mut().pop();
+            let app_state = state.room_back_history.borrow().last().cloned();
+            if let Some(app_state) = app_state {
+                debug!("Go back to state {:?}", app_state);
+                state.set_state(app_state);
             } else {
                 // Fallback when there is no back history
                 debug!("There is no state to go back to. Go back to state NoRoom");
-                if op.login_data.is_some() {
-                    op.set_state(AppState::NoRoom);
+                if state.login_data.is_some() {
+                    state.set_state(AppState::NoRoom);
                 }
             }
-        }
+        });
     }));
 
     send_file.connect_activate(clone!(@weak app => move |_, _| {
@@ -310,22 +323,24 @@ pub fn new(app: &gtk::Application, app_runtime: AppRuntime, op: &Arc<Mutex<AppOp
         }
     }));
 
-    send_message.connect_activate(clone!(@strong op => move |_, _| {
-        let msg_entry = op.lock().unwrap().ui.sventry.view.clone();
-        if let Some(buffer) = msg_entry.get_buffer() {
-            let start = buffer.get_start_iter();
-            let end = buffer.get_end_iter();
+    send_message.connect_activate(move |_, _| {
+        app_runtime.update_state_with(|state| {
+            let msg_entry = state.ui.sventry.view.clone();
+            if let Some(buffer) = msg_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) {
-                op.lock().unwrap().send_message(text.to_string());
-            }
+                if let Some(text) = buffer.get_text(&start, &end, false) {
+                    state.send_message(text.to_string());
+                }
 
-            buffer.set_text("");
-        }
-    }));
+                buffer.set_text("");
+            }
+        });
+    });
 
     send_message.set_enabled(false);
-    let buffer = op.lock().unwrap().ui.sventry.buffer.clone();
+    let buffer = appop.ui.sventry.buffer.clone();
     buffer.connect_changed(move |buffer| {
         if 0 < buffer.get_char_count() {
             send_message.set_enabled(true);
diff --git a/fractal-gtk/src/app/mod.rs b/fractal-gtk/src/app/mod.rs
index bec097b1..7907970a 100644
--- a/fractal-gtk/src/app/mod.rs
+++ b/fractal-gtk/src/app/mod.rs
@@ -6,7 +6,6 @@ use lazy_static::lazy_static;
 use libhandy::prelude::*;
 use std::cell::RefCell;
 use std::rc::Rc;
-use std::sync::{Arc, Mutex};
 use tokio::runtime::Runtime as TokioRuntime;
 
 use log::error;
@@ -22,11 +21,7 @@ mod windowstate;
 
 use windowstate::WindowState;
 
-type GlobalState = Arc<Mutex<AppOp>>;
-
 static mut APP_RUNTIME: Option<AppRuntime> = None;
-// TODO: Deprecated. It should be removed
-static mut OP: Option<GlobalState> = None;
 
 lazy_static! {
     pub static ref RUNTIME: TokioRuntime = TokioRuntime::new().unwrap();
@@ -49,22 +44,22 @@ macro_rules! APPOP {
 pub struct AppRuntime(glib::Sender<Box<dyn FnOnce(&mut AppOp)>>);
 
 impl AppRuntime {
-    fn init(ui: uibuilder::UI) {
+    fn init(ui: uibuilder::UI) -> Self {
         let (app_tx, app_rx) = glib::MainContext::channel(Default::default());
         let app_runtime = Self(app_tx);
-        let state = AppOp::new(ui, app_runtime.clone());
+        let mut state = AppOp::new(ui, app_runtime.clone());
 
         unsafe {
-            OP = Some(Arc::new(Mutex::new(state)));
-            APP_RUNTIME = Some(app_runtime);
+            APP_RUNTIME = Some(app_runtime.clone());
         }
 
-        let state = get_op();
         app_rx.attach(None, move |update_state| {
-            update_state(&mut state.lock().unwrap());
+            update_state(&mut state);
 
             glib::Continue(true)
         });
+
+        app_runtime
     }
 
     pub fn update_state_with(&self, update_fn: impl FnOnce(&mut AppOp) + 'static) {
@@ -75,13 +70,14 @@ impl AppRuntime {
 // Our application struct for containing all the state we have to carry around.
 // TODO: subclass gtk::Application once possible
 pub struct App {
+    app_runtime: AppRuntime,
     ui: uibuilder::UI,
 }
 
 pub type AppRef = Rc<App>;
 
 impl App {
-    pub fn new(gtk_app: &gtk::Application) -> AppRef {
+    pub fn new(gtk_app: gtk::Application) -> AppRef {
         // Set up the textdomain for gettext
         setlocale(LocaleCategory::LcAll, "");
         bindtextdomain("fractal", config::LOCALEDIR);
@@ -99,12 +95,8 @@ impl App {
             gtk::STYLE_PROVIDER_PRIORITY_APPLICATION,
         );
 
-        let ui = uibuilder::UI::new();
-        AppRuntime::init(ui.clone());
-
-        ui.main_window.set_application(Some(gtk_app));
-
-        ui.main_window.set_title("Fractal");
+        let ui = uibuilder::UI::new(gtk_app.clone());
+        let app_runtime = AppRuntime::init(ui.clone());
 
         let settings: gio::Settings = gio::Settings::new("org.gnome.Fractal");
         let window_state = WindowState::load_from_gsettings(&settings);
@@ -170,23 +162,25 @@ impl App {
             .expect("Can't find main_content_stack in ui file.");
 
         // Add login view to the main stack
-        let login = widgets::LoginWidget::new(get_op());
+        let login = widgets::LoginWidget::new(app_runtime.clone());
         main_stack.add_named(&login.container, "login");
 
-        gtk_app.set_accels_for_action("login.back", &["Escape"]);
-
-        actions::Global::new(gtk_app, get_app_runtime().clone(), get_op());
-
-        let app = AppRef::new(Self { ui });
-
-        get_app_runtime().update_state_with(|state| state.connect_gtk());
+        app_runtime.update_state_with(|state| {
+            state
+                .ui
+                .gtk_app
+                .set_accels_for_action("login.back", &["Escape"]);
+            actions::Global::new(state);
+            state.connect_gtk();
+        });
 
-        app
+        AppRef::new(Self { app_runtime, ui })
     }
 
     pub fn on_startup(gtk_app: &gtk::Application) {
         // Create application.
-        let app = App::new(gtk_app);
+        let app = App::new(gtk_app.clone());
+        let app_runtime = app.app_runtime.clone();
 
         // Initialize libhandy
         libhandy::init();
@@ -197,8 +191,10 @@ impl App {
 
         app.ui
             .main_window
-            .connect_property_has_toplevel_focus_notify(clone!(@weak app => move |_| {
-                get_op().lock().unwrap().mark_active_room_messages();
+            .connect_property_has_toplevel_focus_notify(clone!(@strong app_runtime => move |_| {
+                app_runtime.update_state_with(|state| {
+                    state.mark_active_room_messages();
+                });
             }));
 
         app.ui.main_window.connect_delete_event(move |window, _| {
@@ -211,7 +207,9 @@ impl App {
             Inhibit(false)
         });
 
-        get_op().lock().unwrap().init();
+        app_runtime.update_state_with(|state| {
+            state.init();
+        });
 
         // When the application is shut down we drop our app struct
         let app_container = RefCell::new(Some(app));
@@ -230,15 +228,12 @@ impl App {
     }
 
     fn on_shutdown(self: AppRef) {
-        get_op().lock().unwrap().quit();
+        self.app_runtime.update_state_with(|state| {
+            state.quit();
+        });
     }
 }
 
-// TODO: Deprecated. It should be removed
-pub fn get_op() -> &'static GlobalState {
-    unsafe { OP.as_ref().expect("Fatal: AppOp has not been initialized") }
-}
-
 pub fn get_app_runtime() -> &'static AppRuntime {
     unsafe {
         APP_RUNTIME
diff --git a/fractal-gtk/src/appop/connect/autocomplete.rs b/fractal-gtk/src/appop/connect/autocomplete.rs
index a55ac8bf..01af58d6 100644
--- a/fractal-gtk/src/appop/connect/autocomplete.rs
+++ b/fractal-gtk/src/appop/connect/autocomplete.rs
@@ -2,9 +2,10 @@ use gtk::prelude::*;
 
 use crate::widgets;
 
+use crate::app::AppRuntime;
 use crate::uibuilder::UI;
 
-pub fn connect(ui: &UI) {
+pub fn connect(ui: &UI, app_runtime: AppRuntime) {
     let popover = ui
         .builder
         .get_object::<gtk::Popover>("autocomplete_popover")
@@ -18,5 +19,12 @@ pub fn connect(ui: &UI) {
         .get_object("main_window")
         .expect("Can't find main_window in ui file.");
 
-    widgets::Autocomplete::new(window, ui.sventry.view.clone(), popover, listbox).connect();
+    widgets::Autocomplete::new(
+        app_runtime,
+        window,
+        ui.sventry.view.clone(),
+        popover,
+        listbox,
+    )
+    .connect();
 }
diff --git a/fractal-gtk/src/appop/connect/mod.rs b/fractal-gtk/src/appop/connect/mod.rs
index 20e0b11e..2e7e9079 100644
--- a/fractal-gtk/src/appop/connect/mod.rs
+++ b/fractal-gtk/src/appop/connect/mod.rs
@@ -23,7 +23,7 @@ impl AppOp {
         headerbar::connect(ui);
         send::connect(ui, app_runtime.clone());
         markdown::connect(ui, app_runtime.clone());
-        autocomplete::connect(ui);
+        autocomplete::connect(ui, app_runtime.clone());
         language::connect(ui, app_runtime.clone());
         directory::connect(ui, app_runtime.clone());
         leave_room::connect(ui, app_runtime.clone());
diff --git a/fractal-gtk/src/uibuilder.rs b/fractal-gtk/src/uibuilder.rs
index dfbd6835..1365e8ec 100644
--- a/fractal-gtk/src/uibuilder.rs
+++ b/fractal-gtk/src/uibuilder.rs
@@ -5,13 +5,14 @@ use gtk::{self, prelude::*};
 #[derive(Clone, Debug)]
 pub struct UI {
     pub builder: gtk::Builder,
+    pub gtk_app: gtk::Application,
     pub main_window: libhandy::ApplicationWindow,
     pub sventry: SVEntry,
     pub sventry_box: Box<gtk::Stack>,
 }
 
 impl UI {
-    pub fn new() -> UI {
+    pub fn new(gtk_app: gtk::Application) -> UI {
         // The order here is important because some ui file depends on others
 
         let builder = gtk::Builder::new();
@@ -86,12 +87,15 @@ impl UI {
             .add_from_resource("/org/gnome/Fractal/ui/account_settings.ui")
             .expect("Can't load ui file: account_settings.ui");
 
-        let main_window = builder
+        let main_window: libhandy::ApplicationWindow = builder
             .get_object("main_window")
             .expect("Couldn't find main_window in ui file.");
+        main_window.set_application(Some(&gtk_app));
+        main_window.set_title("Fractal");
 
         UI {
             builder,
+            gtk_app,
             main_window,
             sventry,
             sventry_box,
diff --git a/fractal-gtk/src/widgets/autocomplete.rs b/fractal-gtk/src/widgets/autocomplete.rs
index 7244e314..29b8a499 100644
--- a/fractal-gtk/src/widgets/autocomplete.rs
+++ b/fractal-gtk/src/widgets/autocomplete.rs
@@ -3,18 +3,18 @@ use log::info;
 use std::cell::RefCell;
 use std::collections::HashMap;
 use std::rc::Rc;
-use std::sync::{Arc, Mutex};
 
 use gtk::prelude::*;
 use gtk::TextTag;
 
 use crate::model::member::Member;
 
-use crate::app;
+use crate::app::AppRuntime;
 use crate::appop::AppOp;
 use crate::widgets;
 
 pub struct Autocomplete {
+    app_runtime: AppRuntime,
     entry: sourceview4::View,
     listbox: gtk::ListBox,
     popover: gtk::Popover,
@@ -23,18 +23,19 @@ pub struct Autocomplete {
     popover_position: Option<i32>,
     popover_search: Option<String>,
     popover_closing: bool,
-    op: Arc<Mutex<AppOp>>,
 }
 
 impl Autocomplete {
     pub fn new(
+        app_runtime: AppRuntime,
         window: gtk::Window,
-        msg_entry: sourceview4::View,
+        entry: sourceview4::View,
         popover: gtk::Popover,
         listbox: gtk::ListBox,
     ) -> Autocomplete {
         Autocomplete {
-            entry: msg_entry,
+            app_runtime,
+            entry,
             listbox,
             popover,
             window,
@@ -42,11 +43,11 @@ impl Autocomplete {
             popover_position: None,
             popover_search: None,
             popover_closing: false,
-            op: app::get_op().clone(),
         }
     }
 
     pub fn connect(self) {
+        let app_runtime = self.app_runtime.clone();
         let this: Rc<RefCell<Autocomplete>> = Rc::new(RefCell::new(self));
 
         let context = this.borrow().entry.get_style_context();
@@ -275,42 +276,41 @@ impl Autocomplete {
                         }
 
                         if own.borrow().popover_position.is_some() {
-                            let list = {
-                                own.borrow()
-                                    .autocomplete(text, buffer.get_property_cursor_position())
-                            };
-                            let widget_list = { own.borrow_mut().autocomplete_show_popover(list) };
-                            for (alias, widget) in widget_list.iter() {
-                                widget.connect_key_press_event(clone!(
-                                @strong own,
-                                @strong alias
-                                => move |_, ev| {
-                                    own.borrow_mut().autocomplete_insert(alias.clone());
-                                    let ev = {
-                                        let ev: &gdk::Event = ev;
-
-                                        ev.clone()
-                                            .downcast::<gdk::EventKey>()
-                                            .unwrap()
-                                    };
-                                    /* Submit on enter */
-                                    if ev.get_keyval() == gdk::keys::constants::Return
-                                        || ev.get_keyval() == gdk::keys::constants::Tab
-                                    {
+                            app_runtime.update_state_with(clone!(@strong own => move |state| {
+                                let list = own
+                                    .borrow()
+                                    .autocomplete(text, buffer.get_property_cursor_position(), state);
+                                let widget_list = own
+                                    .borrow_mut()
+                                    .autocomplete_show_popover(list, state);
+                                for (alias, widget) in widget_list.iter() {
+                                    widget.connect_key_press_event(clone!(
+                                    @strong own,
+                                    @strong alias
+                                    => move |_, ev| {
+                                        own.borrow_mut().autocomplete_insert(alias.clone());
+                                        let ev = ev
+                                            .downcast_ref::<gdk::EventKey>()
+                                            .unwrap();
+                                        // Submit on enter
+                                        if ev.get_keyval() == gdk::keys::constants::Return
+                                            || ev.get_keyval() == gdk::keys::constants::Tab
+                                        {
+                                            own.borrow_mut().autocomplete_enter();
+                                        }
+                                        Inhibit(true)
+                                    }));
+
+                                    widget.connect_button_press_event(clone!(
+                                    @strong own,
+                                    @strong alias
+                                    => move |_, _| {
+                                        own.borrow_mut().autocomplete_insert(alias.clone());
                                         own.borrow_mut().autocomplete_enter();
-                                    }
-                                    Inhibit(true)
-                                }));
-
-                                widget.connect_button_press_event(clone!(
-                                @strong own,
-                                @strong alias
-                                => move |_, _| {
-                                    own.borrow_mut().autocomplete_insert(alias.clone());
-                                    own.borrow_mut().autocomplete_enter();
-                                    Inhibit(true)
-                                }));
-                            }
+                                        Inhibit(true)
+                                    }));
+                                };
+                            }));
                         }
                     }
                 }
@@ -423,33 +423,31 @@ impl Autocomplete {
     pub fn autocomplete_show_popover(
         &mut self,
         list: Vec<Member>,
+        op: &AppOp,
     ) -> HashMap<String, gtk::EventBox> {
         for ch in self.listbox.get_children().iter() {
             self.listbox.remove(ch);
         }
 
-        let mut widget_list: HashMap<String, gtk::EventBox> = HashMap::new();
-
-        if !list.is_empty() {
-            for m in list.iter() {
-                let alias = &m
+        let widget_list: HashMap<String, gtk::EventBox> = list
+            .iter()
+            .map(|member| {
+                let alias = member
                     .alias
                     .clone()
                     .unwrap_or_default()
                     .trim_end_matches(" (IRC)")
                     .to_owned();
-                let widget;
-                {
-                    let guard = self.op.lock().unwrap();
-                    let mb = widgets::MemberBox::new(&m, &guard);
-                    widget = mb.widget(true);
-                }
+                let widget = widgets::MemberBox::new(&member, op).widget(true);
 
-                let w = widget.clone();
-                let a = alias.clone();
-                widget_list.insert(a, w);
-                self.listbox.add(&widget);
-            }
+                (alias, widget)
+            })
+            .collect();
+
+        if !widget_list.is_empty() {
+            widget_list
+                .values()
+                .for_each(|widget| self.listbox.add(widget));
 
             self.popover.set_relative_to(Some(&self.entry));
             self.popover
@@ -472,10 +470,9 @@ impl Autocomplete {
         widget_list
     }
 
-    pub fn autocomplete(&self, text: Option<String>, pos: i32) -> Vec<Member> {
+    pub fn autocomplete(&self, text: Option<String>, pos: i32, op: &AppOp) -> Vec<Member> {
         let mut list: Vec<Member> = vec![];
-        let guard = self.op.lock().unwrap();
-        let rooms = &guard.rooms;
+        let rooms = &op.rooms;
         match text {
             None => {}
             Some(txt) => {
@@ -495,7 +492,7 @@ impl Autocomplete {
                         };
 
                         /* Search for the 5 most recent active users */
-                        if let Some(aroom) = guard.active_room.clone() {
+                        if let Some(aroom) = op.active_room.clone() {
                             if let Some(r) = rooms.get(&aroom) {
                                 let mut count = 0;
                                 for (_, m) in r.members.iter() {
diff --git a/fractal-gtk/src/widgets/login.rs b/fractal-gtk/src/widgets/login.rs
index cbd59caa..a4ea0fc4 100644
--- a/fractal-gtk/src/widgets/login.rs
+++ b/fractal-gtk/src/widgets/login.rs
@@ -8,8 +8,7 @@ use url::Url;
 use crate::actions;
 use crate::actions::global::AppState;
 use crate::actions::login::LoginState;
-use crate::app::RUNTIME;
-use crate::appop::AppOp;
+use crate::app::{AppRuntime, RUNTIME};
 use crate::globals;
 use crate::util::i18n::i18n;
 use crate::widgets::ErrorDialog;
@@ -17,7 +16,6 @@ use crate::widgets::ErrorDialog;
 use crate::backend::register::get_well_known;
 
 use std::convert::TryInto;
-use std::sync::{Arc, Mutex};
 
 #[derive(Debug, Clone)]
 pub struct LoginWidget {
@@ -31,7 +29,7 @@ pub struct LoginWidget {
 }
 
 impl LoginWidget {
-    pub fn new(op: &Arc<Mutex<AppOp>>) -> Self {
+    pub fn new(app_runtime: AppRuntime) -> Self {
         let widget = Self::default();
 
         let server_entry = &widget.server_entry;
@@ -59,8 +57,6 @@ impl LoginWidget {
                 }
             }));
 
-        let op = op.clone();
-
         let login = widget
             .actions
             .lookup_action("login")
@@ -126,11 +122,11 @@ impl LoginWidget {
                     .unwrap_or((homeserver_url, globals::DEFAULT_IDENTITYSERVER.clone()));
 
                 err_label.hide();
-                op.lock().unwrap().set_state(AppState::Loading);
-                op.lock().unwrap().since = None;
-                op.lock()
-                    .unwrap()
-                    .connect(username, password, homeserver_url, idserver);
+                app_runtime.update_state_with(|state| {
+                    state.set_state(AppState::Loading);
+                    state.since = None;
+                    state.connect(username, password, homeserver_url, idserver);
+                });
             } else {
                 err_label.show();
             }


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