From f6c526b596708f1c682df19116a2d4220cfc1b34 Mon Sep 17 00:00:00 2001 From: Ferdinand Schober Date: Fri, 14 Mar 2025 14:57:14 +0100 Subject: [PATCH] properly synchronize frontend --- lan-mouse-gtk/resources/client_row.ui | 2 - lan-mouse-gtk/src/client_object/imp.rs | 2 - lan-mouse-gtk/src/client_row/imp.rs | 101 ++++++++++++++++++++--- lan-mouse-gtk/src/window.rs | 110 +++++++++++++++---------- src/service.rs | 1 + 5 files changed, 158 insertions(+), 58 deletions(-) diff --git a/lan-mouse-gtk/resources/client_row.ui b/lan-mouse-gtk/resources/client_row.ui index 70d439b..87c8e0f 100644 --- a/lan-mouse-gtk/resources/client_row.ui +++ b/lan-mouse-gtk/resources/client_row.ui @@ -5,7 +5,6 @@ - center end enable @@ -68,7 +67,6 @@ - diff --git a/lan-mouse-gtk/src/client_object/imp.rs b/lan-mouse-gtk/src/client_object/imp.rs index a3f37f6..fbcd5db 100644 --- a/lan-mouse-gtk/src/client_object/imp.rs +++ b/lan-mouse-gtk/src/client_object/imp.rs @@ -1,4 +1,3 @@ -use std::cell::Cell; use std::cell::RefCell; use glib::Properties; @@ -21,7 +20,6 @@ pub struct ClientObject { #[property(name = "resolving", get, set, type = bool, member = resolving)] #[property(name = "ips", get, set, type = Vec, member = ips)] pub data: RefCell, - pub ignore_next_update: Cell, } #[glib::object_subclass] diff --git a/lan-mouse-gtk/src/client_row/imp.rs b/lan-mouse-gtk/src/client_row/imp.rs index 549b0ea..629ab95 100644 --- a/lan-mouse-gtk/src/client_row/imp.rs +++ b/lan-mouse-gtk/src/client_row/imp.rs @@ -3,8 +3,8 @@ use std::cell::RefCell; use adw::subclass::prelude::*; use adw::{prelude::*, ActionRow, ComboRow}; use glib::{subclass::InitializingObject, Binding}; -use gtk::glib::clone; use gtk::glib::subclass::Signal; +use gtk::glib::{clone, SignalHandlerId}; use gtk::{glib, Button, CompositeTemplate, Entry, Switch}; use std::sync::OnceLock; @@ -28,6 +28,10 @@ pub struct ClientRow { #[template_child] pub dns_loading_indicator: TemplateChild, pub bindings: RefCell>, + pub hostname_change_handler: RefCell>, + pub port_change_handler: RefCell>, + pub position_change_handler: RefCell>, + pub set_state_handler: RefCell>, } #[glib::object_subclass] @@ -59,13 +63,42 @@ impl ObjectImpl for ClientRow { row.handle_client_delete(button); } )); - self.hostname.connect_changed(clone!( + let handler = self.hostname.connect_changed(clone!( #[weak(rename_to = row)] self, move |entry| { row.handle_hostname_changed(entry); } )); + self.hostname_change_handler.replace(Some(handler)); + let handler = self.port.connect_changed(clone!( + #[weak(rename_to = row)] + self, + move |entry| { + row.handle_port_changed(entry); + } + )); + self.port_change_handler.replace(Some(handler)); + let handler = self.position.connect_selected_notify(clone!( + #[weak(rename_to = row)] + self, + move |position| { + row.handle_position_changed(position); + } + )); + self.position_change_handler.replace(Some(handler)); + // + let handler = self.enable_switch.connect_state_set(clone!( + #[weak(rename_to = row)] + self, + #[upgrade_or] + glib::Propagation::Proceed, + move |switch, state| { + row.handle_activate_switch(state, switch); + glib::Propagation::Proceed + } + )); + self.set_state_handler.replace(Some(handler)); } fn signals() -> &'static [glib::subclass::Signal] { @@ -109,24 +142,70 @@ impl ClientRow { self.obj().emit_by_name::<()>("request-delete", &[]); } - #[template_callback] - fn handle_port_changed(&self) { - if let Ok(port) = self.port.text().parse::() { + fn handle_port_changed(&self, port_entry: &Entry) { + if let Ok(port) = port_entry.text().parse::() { self.obj() .emit_by_name::<()>("request-port-change", &[&(port as u32)]); } } - // #[template_callback] - fn handle_hostname_changed(&self, entry: &Entry) { + fn handle_hostname_changed(&self, hostname_entry: &Entry) { + log::error!("hostname changed: {}", hostname_entry.text()); self.obj() - .emit_by_name::<()>("request-hostname-change", &[&entry.text()]); + .emit_by_name::<()>("request-hostname-change", &[&hostname_entry.text()]); } - #[template_callback] - fn handle_position_changed(&self) { + fn handle_position_changed(&self, position: &ComboRow) { self.obj() - .emit_by_name("request-position-change", &[&self.position.selected()]) + .emit_by_name("request-position-change", &[&position.selected()]) + } + + pub fn block_hostname_change(&self) { + let handler = self.hostname_change_handler.borrow(); + let handler = handler.as_ref().expect("signal handler"); + self.hostname.block_signal(handler); + } + + pub fn unblock_hostname_change(&self) { + let handler = self.hostname_change_handler.borrow(); + let handler = handler.as_ref().expect("signal handler"); + self.hostname.unblock_signal(handler); + } + + pub fn block_port_change(&self) { + let handler = self.port_change_handler.borrow(); + let handler = handler.as_ref().expect("signal handler"); + self.port.block_signal(handler); + } + + pub fn unblock_port_change(&self) { + let handler = self.port_change_handler.borrow(); + let handler = handler.as_ref().expect("signal handler"); + self.port.unblock_signal(handler); + } + + pub fn block_position_change(&self) { + let handler = self.position_change_handler.borrow(); + let handler = handler.as_ref().expect("signal handler"); + self.position.block_signal(handler); + } + + pub fn unblock_position_change(&self) { + let handler = self.position_change_handler.borrow(); + let handler = handler.as_ref().expect("signal handler"); + self.position.unblock_signal(handler); + } + + pub fn block_active_switch(&self) { + let handler = self.set_state_handler.borrow(); + let handler = handler.as_ref().expect("signal handler"); + self.enable_switch.block_signal(handler); + } + + pub fn unblock_active_switch(&self) { + let handler = self.set_state_handler.borrow(); + let handler = handler.as_ref().expect("signal handler"); + self.enable_switch.unblock_signal(handler); } } diff --git a/lan-mouse-gtk/src/window.rs b/lan-mouse-gtk/src/window.rs index 837947c..580ad9c 100644 --- a/lan-mouse-gtk/src/window.rs +++ b/lan-mouse-gtk/src/window.rs @@ -8,7 +8,7 @@ use glib::{clone, Object}; use gtk::{ gio, glib::{self, closure_local}, - ListBox, NoSelection, + NoSelection, }; use lan_mouse_ipc::{ @@ -58,6 +58,14 @@ impl Window { self.clients().item(idx).map(|o| o.downcast().unwrap()) } + fn row_by_idx(&self, idx: i32) -> Option { + self.imp() + .client_list + .get() + .row_at_index(idx) + .map(|o| o.downcast().expect("expected ClientRow")) + } + fn authorized_by_idx(&self, idx: u32) -> Option { self.authorized().item(idx).map(|o| o.downcast().unwrap()) } @@ -118,8 +126,11 @@ impl Window { #[strong] window, move |row: ClientRow, hostname: String| { + log::info!("request-hostname-change"); if let Some(client) = window.client_by_idx(row.index() as u32) { let hostname = Some(hostname).filter(|s| !s.is_empty()); + /* changed in response to FrontendEvent + * -> do not request additional update */ window.request(FrontendRequest::UpdateHostname( client.handle(), hostname, @@ -264,14 +275,10 @@ impl Window { self.update_dns_state(handle, !state.ips.is_empty()); } - pub fn client_idx(&self, handle: ClientHandle) -> Option { - self.clients().iter::().position(|c| { - if let Ok(c) = c { - c.handle() == handle - } else { - false - } - }) + pub(super) fn client_idx(&self, handle: ClientHandle) -> Option { + self.clients() + .iter::() + .position(|c| c.ok().map(|c| c.handle() == handle).unwrap_or_default()) } pub fn delete_client(&self, handle: ClientHandle) { @@ -287,41 +294,51 @@ impl Window { } pub fn update_client_config(&self, handle: ClientHandle, client: ClientConfig) { - let Some(idx) = self.client_idx(handle) else { - log::warn!("could not find client with handle {}", handle); + let Some(row) = self.row_for_handle(handle) else { + log::warn!("could not find row for handle {}", handle); + return; + }; + let Some(client_object) = self.client_object_for_handle(handle) else { + log::warn!("could not find row for handle {}", handle); return; }; - let client_object = self.clients().item(idx as u32).unwrap(); - let client_object: &ClientObject = client_object.downcast_ref().unwrap(); - let data = client_object.get_data(); - /* only change if it actually has changed, otherwise - * the update signal is triggered */ - if data.hostname != client.hostname { - client_object.set_hostname(client.hostname.unwrap_or("".into())); - } - if data.port != client.port as u32 { - client_object.set_port(client.port as u32); - } - if data.position != client.pos.to_string() { - client_object.set_position(client.pos.to_string()); - } + row.imp().block_hostname_change(); + client_object.set_hostname(client.hostname.unwrap_or("".into())); + row.imp().unblock_hostname_change(); + + row.imp().block_port_change(); + client_object.set_port(client.port as u32); + row.imp().unblock_port_change(); + + row.imp().block_position_change(); + client_object.set_position(client.pos.to_string()); + row.imp().unblock_position_change(); } pub fn update_client_state(&self, handle: ClientHandle, state: ClientState) { - let Some(idx) = self.client_idx(handle) else { - log::warn!("could not find client with handle {}", handle); + let Some(row) = self.row_for_handle(handle) else { + log::warn!("could not find row for handle {}", handle); + return; + }; + let Some(client_object) = self.client_object_for_handle(handle) else { + log::warn!("could not find row for handle {}", handle); return; }; - let client_object = self.clients().item(idx as u32).unwrap(); - let client_object: &ClientObject = client_object.downcast_ref().unwrap(); - let data = client_object.get_data(); + /* activation state */ + row.imp().block_active_switch(); client_object.set_active(state.active); + row.imp().unblock_active_switch(); log::info!("set active to {}", state.active); + /* dns state */ client_object.set_resolving(state.resolving); - log::info!("resolving {}: {}", data.handle, state.resolving); + log::info!( + "resolving {}: {}", + client_object.get_data().handle, + state.resolving + ); self.update_dns_state(handle, !state.ips.is_empty()); let ips = state @@ -332,18 +349,25 @@ impl Window { client_object.set_ips(ips); } - pub fn update_dns_state(&self, handle: ClientHandle, resolved: bool) { - let Some(idx) = self.client_idx(handle) else { - log::warn!("could not find client with handle {}", handle); - return; - }; - let list_box: ListBox = self.imp().client_list.get(); - let row = list_box.row_at_index(idx as i32).unwrap(); - let client_row: ClientRow = row.downcast().expect("expected ClientRow Object"); - if resolved { - client_row.imp().dns_button.set_css_classes(&["success"]) - } else { - client_row.imp().dns_button.set_css_classes(&["warning"]) + fn client_object_for_handle(&self, handle: ClientHandle) -> Option { + self.client_idx(handle) + .map(|i| self.client_by_idx(i as u32)) + .flatten() + } + + fn row_for_handle(&self, handle: ClientHandle) -> Option { + self.client_idx(handle) + .map(|i| self.row_by_idx(i as i32)) + .flatten() + } + + fn update_dns_state(&self, handle: ClientHandle, resolved: bool) { + if let Some(client_row) = self.row_for_handle(handle) { + if resolved { + client_row.imp().dns_button.set_css_classes(&["success"]) + } else { + client_row.imp().dns_button.set_css_classes(&["warning"]) + } } } diff --git a/src/service.rs b/src/service.rs index ce08a0c..5ae65d8 100644 --- a/src/service.rs +++ b/src/service.rs @@ -455,6 +455,7 @@ impl Service { } fn update_hostname(&mut self, handle: ClientHandle, hostname: Option) { + log::info!("hostname changed: {hostname:?}"); if self.client_manager.set_hostname(handle, hostname.clone()) { self.resolve(handle); }