mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-04-16 15:21:29 +03:00
Gtk frontend rework (#276)
client configuration now applies immediately instead of after enabling / disabling clients. Also fixes a potential feedback loop when changing settings.
This commit is contained in:
committed by
GitHub
parent
f247300f8c
commit
50a778452e
@@ -8,7 +8,7 @@ use glib::{clone, Object};
|
||||
use gtk::{
|
||||
gio,
|
||||
glib::{self, closure_local},
|
||||
ListBox, NoSelection,
|
||||
NoSelection,
|
||||
};
|
||||
|
||||
use lan_mouse_ipc::{
|
||||
@@ -28,7 +28,7 @@ glib::wrapper! {
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub(crate) fn new(app: &adw::Application, conn: FrontendRequestWriter) -> Self {
|
||||
pub(super) fn new(app: &adw::Application, conn: FrontendRequestWriter) -> Self {
|
||||
let window: Self = Object::builder().property("application", app).build();
|
||||
window
|
||||
.imp()
|
||||
@@ -38,7 +38,7 @@ impl Window {
|
||||
window
|
||||
}
|
||||
|
||||
pub fn clients(&self) -> gio::ListStore {
|
||||
fn clients(&self) -> gio::ListStore {
|
||||
self.imp()
|
||||
.clients
|
||||
.borrow()
|
||||
@@ -46,7 +46,7 @@ impl Window {
|
||||
.expect("Could not get clients")
|
||||
}
|
||||
|
||||
pub fn authorized(&self) -> gio::ListStore {
|
||||
fn authorized(&self) -> gio::ListStore {
|
||||
self.imp()
|
||||
.authorized
|
||||
.borrow()
|
||||
@@ -62,6 +62,14 @@ impl Window {
|
||||
self.authorized().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 setup_authorized(&self) {
|
||||
let store = gio::ListStore::new::<KeyObject>();
|
||||
self.imp().authorized.replace(Some(store));
|
||||
@@ -112,16 +120,57 @@ impl Window {
|
||||
.expect("Expected object of type `ClientObject`.");
|
||||
let row = window.create_client_row(client_object);
|
||||
row.connect_closure(
|
||||
"request-update",
|
||||
"request-hostname-change",
|
||||
false,
|
||||
closure_local!(
|
||||
#[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,
|
||||
));
|
||||
}
|
||||
}
|
||||
),
|
||||
);
|
||||
row.connect_closure(
|
||||
"request-port-change",
|
||||
false,
|
||||
closure_local!(
|
||||
#[strong]
|
||||
window,
|
||||
move |row: ClientRow, port: u32| {
|
||||
if let Some(client) = window.client_by_idx(row.index() as u32) {
|
||||
window.request(FrontendRequest::UpdatePort(
|
||||
client.handle(),
|
||||
port as u16,
|
||||
));
|
||||
}
|
||||
}
|
||||
),
|
||||
);
|
||||
row.connect_closure(
|
||||
"request-activate",
|
||||
false,
|
||||
closure_local!(
|
||||
#[strong]
|
||||
window,
|
||||
move |row: ClientRow, active: bool| {
|
||||
if let Some(client) = window.client_by_idx(row.index() as u32) {
|
||||
window.request_client_activate(&client, active);
|
||||
window.request_client_update(&client);
|
||||
window.request_client_state(&client);
|
||||
log::info!(
|
||||
"request: {} client",
|
||||
if active { "activating" } else { "deactivating" }
|
||||
);
|
||||
window.request(FrontendRequest::Activate(
|
||||
client.handle(),
|
||||
active,
|
||||
));
|
||||
}
|
||||
}
|
||||
),
|
||||
@@ -134,7 +183,7 @@ impl Window {
|
||||
window,
|
||||
move |row: ClientRow| {
|
||||
if let Some(client) = window.client_by_idx(row.index() as u32) {
|
||||
window.request_client_delete(&client);
|
||||
window.request(FrontendRequest::Delete(client.handle()));
|
||||
}
|
||||
}
|
||||
),
|
||||
@@ -147,9 +196,31 @@ impl Window {
|
||||
window,
|
||||
move |row: ClientRow| {
|
||||
if let Some(client) = window.client_by_idx(row.index() as u32) {
|
||||
window.request_client_update(&client);
|
||||
window.request_dns(&client);
|
||||
window.request_client_state(&client);
|
||||
window.request(FrontendRequest::ResolveDns(
|
||||
client.get_data().handle,
|
||||
));
|
||||
}
|
||||
}
|
||||
),
|
||||
);
|
||||
row.connect_closure(
|
||||
"request-position-change",
|
||||
false,
|
||||
closure_local!(
|
||||
#[strong]
|
||||
window,
|
||||
move |row: ClientRow, pos_idx: u32| {
|
||||
if let Some(client) = window.client_by_idx(row.index() as u32) {
|
||||
let position = match pos_idx {
|
||||
0 => Position::Left,
|
||||
1 => Position::Right,
|
||||
2 => Position::Top,
|
||||
_ => Position::Bottom,
|
||||
};
|
||||
window.request(FrontendRequest::UpdatePosition(
|
||||
client.handle(),
|
||||
position,
|
||||
));
|
||||
}
|
||||
}
|
||||
),
|
||||
@@ -160,10 +231,14 @@ impl Window {
|
||||
);
|
||||
}
|
||||
|
||||
fn setup_icon(&self) {
|
||||
self.set_icon_name(Some("de.feschber.LanMouse"));
|
||||
}
|
||||
|
||||
/// workaround for a bug in libadwaita that shows an ugly line beneath
|
||||
/// the last element if a placeholder is set.
|
||||
/// https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/6308
|
||||
pub fn update_placeholder_visibility(&self) {
|
||||
fn update_placeholder_visibility(&self) {
|
||||
let visible = self.clients().n_items() == 0;
|
||||
let placeholder = self.imp().client_placeholder.get();
|
||||
self.imp().client_list.set_placeholder(match visible {
|
||||
@@ -172,7 +247,7 @@ impl Window {
|
||||
});
|
||||
}
|
||||
|
||||
pub fn update_auth_placeholder_visibility(&self) {
|
||||
fn update_auth_placeholder_visibility(&self) {
|
||||
let visible = self.authorized().n_items() == 0;
|
||||
let placeholder = self.imp().authorized_placeholder.get();
|
||||
self.imp().authorized_list.set_placeholder(match visible {
|
||||
@@ -181,10 +256,6 @@ impl Window {
|
||||
});
|
||||
}
|
||||
|
||||
fn setup_icon(&self) {
|
||||
self.set_icon_name(Some("de.feschber.LanMouse"));
|
||||
}
|
||||
|
||||
fn create_client_row(&self, client_object: &ClientObject) -> ClientRow {
|
||||
let row = ClientRow::new(client_object);
|
||||
row.bind(client_object);
|
||||
@@ -197,24 +268,46 @@ impl Window {
|
||||
row
|
||||
}
|
||||
|
||||
pub fn new_client(&self, handle: ClientHandle, client: ClientConfig, state: ClientState) {
|
||||
pub(super) fn new_client(
|
||||
&self,
|
||||
handle: ClientHandle,
|
||||
client: ClientConfig,
|
||||
state: ClientState,
|
||||
) {
|
||||
let client = ClientObject::new(handle, client, state.clone());
|
||||
self.clients().append(&client);
|
||||
self.update_placeholder_visibility();
|
||||
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
|
||||
pub(super) fn update_client_list(
|
||||
&self,
|
||||
clients: Vec<(ClientHandle, ClientConfig, ClientState)>,
|
||||
) {
|
||||
for (handle, client, state) in clients {
|
||||
if self.client_idx(handle).is_some() {
|
||||
self.update_client_config(handle, client);
|
||||
self.update_client_state(handle, state);
|
||||
} else {
|
||||
false
|
||||
self.new_client(handle, client, state);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn delete_client(&self, handle: ClientHandle) {
|
||||
pub(super) fn update_port(&self, port: u16, msg: Option<String>) {
|
||||
if let Some(msg) = msg {
|
||||
self.show_toast(msg.as_str());
|
||||
}
|
||||
self.imp().set_port(port);
|
||||
}
|
||||
|
||||
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(super) fn delete_client(&self, handle: ClientHandle) {
|
||||
let Some(idx) = self.client_idx(handle) else {
|
||||
log::warn!("could not find client with handle {handle}");
|
||||
return;
|
||||
@@ -226,46 +319,31 @@ 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);
|
||||
pub(super) fn update_client_config(&self, handle: ClientHandle, client: ClientConfig) {
|
||||
let Some(row) = self.row_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.set_hostname(client.hostname);
|
||||
row.set_port(client.port);
|
||||
row.set_position(client.pos);
|
||||
}
|
||||
|
||||
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);
|
||||
pub(super) fn update_client_state(&self, handle: ClientHandle, state: ClientState) {
|
||||
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();
|
||||
|
||||
if state.active != data.active {
|
||||
client_object.set_active(state.active);
|
||||
log::debug!("set active to {}", state.active);
|
||||
}
|
||||
/* activation state */
|
||||
row.set_active(state.active);
|
||||
|
||||
if state.resolving != data.resolving {
|
||||
client_object.set_resolving(state.resolving);
|
||||
log::debug!("resolving {}: {}", data.handle, state.resolving);
|
||||
}
|
||||
/* dns state */
|
||||
client_object.set_resolving(state.resolving);
|
||||
|
||||
self.update_dns_state(handle, !state.ips.is_empty());
|
||||
let ips = state
|
||||
@@ -276,22 +354,23 @@ 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)
|
||||
.and_then(|i| self.client_by_idx(i as u32))
|
||||
}
|
||||
|
||||
fn row_for_handle(&self, handle: ClientHandle) -> Option<ClientRow> {
|
||||
self.client_idx(handle)
|
||||
.and_then(|i| self.row_by_idx(i as i32))
|
||||
}
|
||||
|
||||
fn update_dns_state(&self, handle: ClientHandle, resolved: bool) {
|
||||
if let Some(client_row) = self.row_for_handle(handle) {
|
||||
client_row.set_dns_state(resolved);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn request_port_change(&self) {
|
||||
fn request_port_change(&self) {
|
||||
let port = self
|
||||
.imp()
|
||||
.port_entry
|
||||
@@ -303,55 +382,19 @@ impl Window {
|
||||
self.request(FrontendRequest::ChangePort(port));
|
||||
}
|
||||
|
||||
pub fn request_capture(&self) {
|
||||
fn request_capture(&self) {
|
||||
self.request(FrontendRequest::EnableCapture);
|
||||
}
|
||||
|
||||
pub fn request_emulation(&self) {
|
||||
fn request_emulation(&self) {
|
||||
self.request(FrontendRequest::EnableEmulation);
|
||||
}
|
||||
|
||||
pub fn request_client_state(&self, client: &ClientObject) {
|
||||
self.request_client_state_for(client.handle());
|
||||
}
|
||||
|
||||
pub fn request_client_state_for(&self, handle: ClientHandle) {
|
||||
self.request(FrontendRequest::GetState(handle));
|
||||
}
|
||||
|
||||
pub fn request_client_create(&self) {
|
||||
fn request_client_create(&self) {
|
||||
self.request(FrontendRequest::Create);
|
||||
}
|
||||
|
||||
pub fn request_dns(&self, client: &ClientObject) {
|
||||
self.request(FrontendRequest::ResolveDns(client.get_data().handle));
|
||||
}
|
||||
|
||||
pub fn request_client_update(&self, client: &ClientObject) {
|
||||
let handle = client.handle();
|
||||
let data = client.get_data();
|
||||
let position = Position::try_from(data.position.as_str()).expect("invalid position");
|
||||
let hostname = data.hostname;
|
||||
let port = data.port as u16;
|
||||
|
||||
for event in [
|
||||
FrontendRequest::UpdateHostname(handle, hostname),
|
||||
FrontendRequest::UpdatePosition(handle, position),
|
||||
FrontendRequest::UpdatePort(handle, port),
|
||||
] {
|
||||
self.request(event);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn request_client_activate(&self, client: &ClientObject, active: bool) {
|
||||
self.request(FrontendRequest::Activate(client.handle(), active));
|
||||
}
|
||||
|
||||
pub fn request_client_delete(&self, client: &ClientObject) {
|
||||
self.request(FrontendRequest::Delete(client.handle()));
|
||||
}
|
||||
|
||||
pub fn open_fingerprint_dialog(&self) {
|
||||
fn open_fingerprint_dialog(&self) {
|
||||
let window = FingerprintWindow::new();
|
||||
window.set_transient_for(Some(self));
|
||||
window.connect_closure(
|
||||
@@ -369,15 +412,15 @@ impl Window {
|
||||
window.present();
|
||||
}
|
||||
|
||||
pub fn request_fingerprint_add(&self, desc: String, fp: String) {
|
||||
fn request_fingerprint_add(&self, desc: String, fp: String) {
|
||||
self.request(FrontendRequest::AuthorizeKey(desc, fp));
|
||||
}
|
||||
|
||||
pub fn request_fingerprint_remove(&self, fp: String) {
|
||||
fn request_fingerprint_remove(&self, fp: String) {
|
||||
self.request(FrontendRequest::RemoveAuthorizedKey(fp));
|
||||
}
|
||||
|
||||
pub fn request(&self, request: FrontendRequest) {
|
||||
fn request(&self, request: FrontendRequest) {
|
||||
let mut requester = self.imp().frontend_request_writer.borrow_mut();
|
||||
let requester = requester.as_mut().unwrap();
|
||||
if let Err(e) = requester.request(request) {
|
||||
@@ -385,18 +428,18 @@ impl Window {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn show_toast(&self, msg: &str) {
|
||||
pub(super) fn show_toast(&self, msg: &str) {
|
||||
let toast = adw::Toast::new(msg);
|
||||
let toast_overlay = &self.imp().toast_overlay;
|
||||
toast_overlay.add_toast(toast);
|
||||
}
|
||||
|
||||
pub fn set_capture(&self, active: bool) {
|
||||
pub(super) fn set_capture(&self, active: bool) {
|
||||
self.imp().capture_active.replace(active);
|
||||
self.update_capture_emulation_status();
|
||||
}
|
||||
|
||||
pub fn set_emulation(&self, active: bool) {
|
||||
pub(super) fn set_emulation(&self, active: bool) {
|
||||
self.imp().emulation_active.replace(active);
|
||||
self.update_capture_emulation_status();
|
||||
}
|
||||
@@ -411,7 +454,7 @@ impl Window {
|
||||
.set_visible(!capture || !emulation);
|
||||
}
|
||||
|
||||
pub(crate) fn set_authorized_keys(&self, fingerprints: HashMap<String, String>) {
|
||||
pub(super) fn set_authorized_keys(&self, fingerprints: HashMap<String, String>) {
|
||||
let authorized = self.authorized();
|
||||
// clear list
|
||||
authorized.remove_all();
|
||||
@@ -423,7 +466,7 @@ impl Window {
|
||||
self.update_auth_placeholder_visibility();
|
||||
}
|
||||
|
||||
pub(crate) fn set_pk_fp(&self, fingerprint: &str) {
|
||||
pub(super) fn set_pk_fp(&self, fingerprint: &str) {
|
||||
self.imp().fingerprint_row.set_subtitle(fingerprint);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user