properly synchronize frontend

This commit is contained in:
Ferdinand Schober
2025-03-14 14:57:14 +01:00
parent 639e86d95e
commit f6c526b596
5 changed files with 158 additions and 58 deletions

View File

@@ -5,7 +5,6 @@
<!-- enabled -->
<child type="prefix">
<object class="GtkSwitch" id="enable_switch">
<signal name="state_set" handler="handle_activate_switch" swapped="true"/>
<property name="valign">center</property>
<property name="halign">end</property>
<property name="tooltip-text" translatable="yes">enable</property>
@@ -68,7 +67,6 @@
</items>
</object>
</property>
<signal name="notify::selected" handler="handle_position_changed" swapped="true"/>
</object>
</child>
<!-- delete button -->

View File

@@ -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<String>, member = ips)]
pub data: RefCell<ClientData>,
pub ignore_next_update: Cell<bool>,
}
#[glib::object_subclass]

View File

@@ -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<gtk::Spinner>,
pub bindings: RefCell<Vec<Binding>>,
pub hostname_change_handler: RefCell<Option<SignalHandlerId>>,
pub port_change_handler: RefCell<Option<SignalHandlerId>>,
pub position_change_handler: RefCell<Option<SignalHandlerId>>,
pub set_state_handler: RefCell<Option<SignalHandlerId>>,
}
#[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));
// <signal name="state_set" handler="handle_activate_switch" swapped="true"/>
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::<u16>() {
fn handle_port_changed(&self, port_entry: &Entry) {
if let Ok(port) = port_entry.text().parse::<u16>() {
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);
}
}

View File

@@ -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<ClientRow> {
self.imp()
.client_list
.get()
.row_at_index(idx)
.map(|o| o.downcast().expect("expected ClientRow"))
}
fn authorized_by_idx(&self, idx: u32) -> Option<KeyObject> {
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<usize> {
self.clients().iter::<ClientObject>().position(|c| {
if let Ok(c) = c {
c.handle() == handle
} else {
false
}
})
pub(super) fn client_idx(&self, handle: ClientHandle) -> Option<usize> {
self.clients()
.iter::<ClientObject>()
.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<ClientObject> {
self.client_idx(handle)
.map(|i| self.client_by_idx(i as u32))
.flatten()
}
fn row_for_handle(&self, handle: ClientHandle) -> Option<ClientRow> {
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"])
}
}
}

View File

@@ -455,6 +455,7 @@ impl Service {
}
fn update_hostname(&mut self, handle: ClientHandle, hostname: Option<String>) {
log::info!("hostname changed: {hostname:?}");
if self.client_manager.set_hostname(handle, hostname.clone()) {
self.resolve(handle);
}