diff --git a/.gitmodules b/.gitmodules index d3c4477..32f55f9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ [submodule "obws"] path = obws url = https://git.asonix.dog/asonix/obws -[submodule "libhandy-rs"] - path = libhandy-rs - url = https://git.asonix.dog/asonix/libhandy-rs diff --git a/libhandy-rs b/libhandy-rs deleted file mode 160000 index 12924ef..0000000 --- a/libhandy-rs +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 12924ef6d6287fedcc4ffb4fc5b6ab78448d2ff3 diff --git a/streamdeck/Cargo.lock b/streamdeck/Cargo.lock index f1373a3..b181bfe 100644 --- a/streamdeck/Cargo.lock +++ b/streamdeck/Cargo.lock @@ -47,7 +47,7 @@ dependencies = [ [[package]] name = "atk" version = "0.13.0" -source = "git+https://github.com/gtk-rs/gtk3-rs#2f86e52185021b540913ae8e87cf857a1dbd1ea6" +source = "git+https://github.com/gtk-rs/gtk3-rs#dff1e67f2da991a27fb18654cb70b7f169262dc2" dependencies = [ "atk-sys", "bitflags", @@ -58,7 +58,7 @@ dependencies = [ [[package]] name = "atk-sys" version = "0.13.0" -source = "git+https://github.com/gtk-rs/gtk3-rs#2f86e52185021b540913ae8e87cf857a1dbd1ea6" +source = "git+https://github.com/gtk-rs/gtk3-rs#dff1e67f2da991a27fb18654cb70b7f169262dc2" dependencies = [ "glib-sys", "gobject-sys", @@ -104,7 +104,7 @@ checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" [[package]] name = "cairo-rs" version = "0.13.0" -source = "git+https://github.com/gtk-rs/gtk-rs-core#ba69d8da4a4cfb37a1c250c0f3dd13d1ba9ddeb9" +source = "git+https://github.com/gtk-rs/gtk-rs-core#9d6448fe4f1a24923835943335cde652315858c5" dependencies = [ "bitflags", "cairo-sys-rs", @@ -116,7 +116,7 @@ dependencies = [ [[package]] name = "cairo-sys-rs" version = "0.13.0" -source = "git+https://github.com/gtk-rs/gtk-rs-core#ba69d8da4a4cfb37a1c250c0f3dd13d1ba9ddeb9" +source = "git+https://github.com/gtk-rs/gtk-rs-core#9d6448fe4f1a24923835943335cde652315858c5" dependencies = [ "glib-sys", "libc", @@ -147,7 +147,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "command" version = "0.1.0" -source = "git+https://git.asonix.dog/asonix/streamdeck-workspace?branch=main#e413cf6720fb0be076e534bf1da356e7781fcda4" +source = "git+https://git.asonix.dog/asonix/streamdeck-workspace?branch=main#7d35810867c2669dce04acfb9fd36223e52109c8" dependencies = [ "serde", ] @@ -332,7 +332,7 @@ dependencies = [ [[package]] name = "gdk" version = "0.13.0" -source = "git+https://github.com/gtk-rs/gtk3-rs#2f86e52185021b540913ae8e87cf857a1dbd1ea6" +source = "git+https://github.com/gtk-rs/gtk3-rs#dff1e67f2da991a27fb18654cb70b7f169262dc2" dependencies = [ "bitflags", "cairo-rs", @@ -347,7 +347,7 @@ dependencies = [ [[package]] name = "gdk-pixbuf" version = "0.13.0" -source = "git+https://github.com/gtk-rs/gtk-rs-core#ba69d8da4a4cfb37a1c250c0f3dd13d1ba9ddeb9" +source = "git+https://github.com/gtk-rs/gtk-rs-core#9d6448fe4f1a24923835943335cde652315858c5" dependencies = [ "gdk-pixbuf-sys", "gio", @@ -358,7 +358,7 @@ dependencies = [ [[package]] name = "gdk-pixbuf-sys" version = "0.13.0" -source = "git+https://github.com/gtk-rs/gtk-rs-core#ba69d8da4a4cfb37a1c250c0f3dd13d1ba9ddeb9" +source = "git+https://github.com/gtk-rs/gtk-rs-core#9d6448fe4f1a24923835943335cde652315858c5" dependencies = [ "gio-sys", "glib-sys", @@ -370,7 +370,7 @@ dependencies = [ [[package]] name = "gdk-sys" version = "0.13.0" -source = "git+https://github.com/gtk-rs/gtk3-rs#2f86e52185021b540913ae8e87cf857a1dbd1ea6" +source = "git+https://github.com/gtk-rs/gtk3-rs#dff1e67f2da991a27fb18654cb70b7f169262dc2" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -397,7 +397,7 @@ dependencies = [ [[package]] name = "gio" version = "0.13.0" -source = "git+https://github.com/gtk-rs/gtk-rs-core#ba69d8da4a4cfb37a1c250c0f3dd13d1ba9ddeb9" +source = "git+https://github.com/gtk-rs/gtk-rs-core#9d6448fe4f1a24923835943335cde652315858c5" dependencies = [ "bitflags", "futures-channel", @@ -413,7 +413,7 @@ dependencies = [ [[package]] name = "gio-sys" version = "0.13.0" -source = "git+https://github.com/gtk-rs/gtk-rs-core#ba69d8da4a4cfb37a1c250c0f3dd13d1ba9ddeb9" +source = "git+https://github.com/gtk-rs/gtk-rs-core#9d6448fe4f1a24923835943335cde652315858c5" dependencies = [ "glib-sys", "gobject-sys", @@ -425,7 +425,7 @@ dependencies = [ [[package]] name = "glib" version = "0.13.0" -source = "git+https://github.com/gtk-rs/gtk-rs-core#ba69d8da4a4cfb37a1c250c0f3dd13d1ba9ddeb9" +source = "git+https://github.com/gtk-rs/gtk-rs-core#9d6448fe4f1a24923835943335cde652315858c5" dependencies = [ "bitflags", "futures-channel", @@ -443,7 +443,7 @@ dependencies = [ [[package]] name = "glib-macros" version = "0.13.0" -source = "git+https://github.com/gtk-rs/gtk-rs-core#ba69d8da4a4cfb37a1c250c0f3dd13d1ba9ddeb9" +source = "git+https://github.com/gtk-rs/gtk-rs-core#9d6448fe4f1a24923835943335cde652315858c5" dependencies = [ "anyhow", "heck", @@ -457,7 +457,7 @@ dependencies = [ [[package]] name = "glib-sys" version = "0.13.0" -source = "git+https://github.com/gtk-rs/gtk-rs-core#ba69d8da4a4cfb37a1c250c0f3dd13d1ba9ddeb9" +source = "git+https://github.com/gtk-rs/gtk-rs-core#9d6448fe4f1a24923835943335cde652315858c5" dependencies = [ "libc", "system-deps", @@ -466,7 +466,7 @@ dependencies = [ [[package]] name = "gobject-sys" version = "0.13.0" -source = "git+https://github.com/gtk-rs/gtk-rs-core#ba69d8da4a4cfb37a1c250c0f3dd13d1ba9ddeb9" +source = "git+https://github.com/gtk-rs/gtk-rs-core#9d6448fe4f1a24923835943335cde652315858c5" dependencies = [ "glib-sys", "libc", @@ -476,7 +476,7 @@ dependencies = [ [[package]] name = "gtk" version = "0.13.0" -source = "git+https://github.com/gtk-rs/gtk3-rs#2f86e52185021b540913ae8e87cf857a1dbd1ea6" +source = "git+https://github.com/gtk-rs/gtk3-rs#dff1e67f2da991a27fb18654cb70b7f169262dc2" dependencies = [ "atk", "bitflags", @@ -499,7 +499,7 @@ dependencies = [ [[package]] name = "gtk-sys" version = "0.13.0" -source = "git+https://github.com/gtk-rs/gtk3-rs#2f86e52185021b540913ae8e87cf857a1dbd1ea6" +source = "git+https://github.com/gtk-rs/gtk3-rs#dff1e67f2da991a27fb18654cb70b7f169262dc2" dependencies = [ "atk-sys", "cairo-sys-rs", @@ -516,7 +516,7 @@ dependencies = [ [[package]] name = "gtk3-macros" version = "0.1.0" -source = "git+https://github.com/gtk-rs/gtk3-rs#2f86e52185021b540913ae8e87cf857a1dbd1ea6" +source = "git+https://github.com/gtk-rs/gtk3-rs#dff1e67f2da991a27fb18654cb70b7f169262dc2" dependencies = [ "anyhow", "heck", @@ -596,7 +596,7 @@ checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e" [[package]] name = "libhandy" version = "0.8.0" -source = "git+https://git.asonix.dog/asonix/libhandy-rs?branch=main#12924ef6d6287fedcc4ffb4fc5b6ab78448d2ff3" +source = "git+https://gitlab.gnome.org/World/Rust/libhandy-rs#307a0e2fffa067724848dd7fc9a7eb7685102f94" dependencies = [ "bitflags", "gdk", @@ -619,7 +619,7 @@ dependencies = [ [[package]] name = "libhandy-sys" version = "0.8.0" -source = "git+https://git.asonix.dog/asonix/libhandy-rs?branch=main#12924ef6d6287fedcc4ffb4fc5b6ab78448d2ff3" +source = "git+https://gitlab.gnome.org/World/Rust/libhandy-rs#307a0e2fffa067724848dd7fc9a7eb7685102f94" dependencies = [ "gdk-pixbuf-sys", "gdk-sys", @@ -645,7 +645,7 @@ dependencies = [ [[package]] name = "marble" version = "0.1.0" -source = "git+https://git.asonix.dog/asonix/streamdeck-workspace?branch=main#e413cf6720fb0be076e534bf1da356e7781fcda4" +source = "git+https://git.asonix.dog/asonix/streamdeck-workspace?branch=main#7d35810867c2669dce04acfb9fd36223e52109c8" dependencies = [ "event-listener", "futures-core", @@ -701,7 +701,7 @@ checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" [[package]] name = "pango" version = "0.13.0" -source = "git+https://github.com/gtk-rs/gtk-rs-core#ba69d8da4a4cfb37a1c250c0f3dd13d1ba9ddeb9" +source = "git+https://github.com/gtk-rs/gtk-rs-core#9d6448fe4f1a24923835943335cde652315858c5" dependencies = [ "bitflags", "glib", @@ -713,7 +713,7 @@ dependencies = [ [[package]] name = "pango-sys" version = "0.13.0" -source = "git+https://github.com/gtk-rs/gtk-rs-core#ba69d8da4a4cfb37a1c250c0f3dd13d1ba9ddeb9" +source = "git+https://github.com/gtk-rs/gtk-rs-core#9d6448fe4f1a24923835943335cde652315858c5" dependencies = [ "glib-sys", "gobject-sys", @@ -1035,7 +1035,9 @@ dependencies = [ "async-io", "command", "env_logger", + "event-listener", "futures-channel", + "futures-core", "futures-executor", "futures-util", "gdk", diff --git a/streamdeck/Cargo.toml b/streamdeck/Cargo.toml index b8232db..218cf8e 100644 --- a/streamdeck/Cargo.toml +++ b/streamdeck/Cargo.toml @@ -11,7 +11,9 @@ anyhow = "1" async-io = "1.4.1" command = { git = "https://git.asonix.dog/asonix/streamdeck-workspace", branch = "main" } env_logger = "0.8.3" +event-listener = "2.5.1" futures-channel = { version = "0.3.14", features = ["sink"] } +futures-core = "0.3.14" futures-executor = { version = "0.3.14", features = ["thread-pool"] } futures-util = { version = "0.3", features = ["sink"] } gdk = { git = "https://github.com/gtk-rs/gtk3-rs" } @@ -20,7 +22,7 @@ gtk = { git = "https://github.com/gtk-rs/gtk3-rs" } gio = { git = "https://github.com/gtk-rs/gtk-rs-core" } glib = { git = "https://github.com/gtk-rs/gtk-rs-core" } glib-sys = { git = "https://github.com/gtk-rs/gtk-rs-core" } -libhandy = { git = "https://git.asonix.dog/asonix/libhandy-rs", branch = "main" } +libhandy = { git = "https://gitlab.gnome.org/World/Rust/libhandy-rs" } log = "0.4" marble = { git = "https://git.asonix.dog/asonix/streamdeck-workspace", branch = "main" } once_cell = "1.7.2" diff --git a/streamdeck/src/command.rs b/streamdeck/src/command.rs new file mode 100644 index 0000000..0e62e38 --- /dev/null +++ b/streamdeck/src/command.rs @@ -0,0 +1,167 @@ +use command::Command; +use event_listener::{Event, EventListener}; +use futures_core::stream::Stream; +use gtk::prelude::*; +use std::{ + future::Future, + pin::Pin, + rc::Rc, + task::{Context, Poll}, +}; + +pub(crate) fn render_text(command: &Command, grid: >k::Grid) { + let row_command = gtk::Label::new(None); + row_command.set_halign(gtk::Align::Start); + row_command.set_valign(gtk::Align::Center); + + grid.add(&row_command); + + let cmd_type = CommandType::from_command(command); + match command { + Command::SwitchScene { name } => { + row_command.set_label(cmd_type.as_name()); + render_switch_scene(&name, grid); + } + } +} + +fn render_switch_scene(name: &str, grid: >k::Grid) { + let to = gtk::Label::new(Some("to")); + to.set_halign(gtk::Align::Start); + to.set_valign(gtk::Align::Center); + + let scene_name = gtk::Label::new(Some(name)); + scene_name.set_halign(gtk::Align::Start); + scene_name.set_valign(gtk::Align::Center); + + grid.add(&to); + grid.add(&scene_name); + grid.show_all(); +} + +pub(crate) fn render_config(command: Option<&Command>, grid: >k::Grid) -> ComboBoxHandle { + let combobox = gtk::ComboBoxText::new(); + combobox.set_id_column(1); + + for typ in CommandType::available() { + combobox.append(Some(typ.as_id()), typ.as_name()); + } + + let event = Rc::new(Event::default()); + + let handle = ComboBoxHandle { + obj: combobox.clone(), + event: event.clone(), + }; + + let command_type = command + .as_ref() + .map(|c| CommandType::from_command(c)) + .unwrap_or(CommandType::SwitchScene); + + combobox.connect_changed(move |_combobox| { + event.notify(usize::MAX); + }); + + handle +} + +#[derive(Clone, Copy, Debug)] +pub(crate) enum CommandType { + SwitchScene, +} + +#[derive(Clone)] +pub(crate) struct ComboBoxHandle { + obj: gtk::ComboBoxText, + event: Rc, +} + +pub(crate) struct SelectedStream { + listener: Option, + event: Rc, + obj: gtk::ComboBoxText, +} + +impl CommandType { + fn as_id(&self) -> &'static str { + match self { + CommandType::SwitchScene => "SwitchScene", + } + } + + fn as_name(&self) -> &'static str { + match self { + CommandType::SwitchScene => "Switch Scene", + } + } + + fn available() -> &'static [Self] { + &[CommandType::SwitchScene] + } + + fn from_command(command: &Command) -> Self { + match command { + Command::SwitchScene { .. } => Self::SwitchScene, + } + } +} + +impl ComboBoxHandle { + pub(crate) fn select_first(&self) { + if self.obj.active_id().is_some() { + return; + } + + let cmds = CommandType::available(); + if cmds.is_empty() { + return; + } + + self.obj.set_active_id(Some(cmds[0].as_id())); + } + + pub(crate) fn selected(&self) -> SelectedStream { + SelectedStream { + listener: Some(self.event.listen()), + event: self.event.clone(), + obj: self.obj.clone(), + } + } +} + +impl Stream for SelectedStream { + type Item = String; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut listener = if let Some(listener) = self.listener.take() { + listener + } else { + self.event.listen() + }; + + match Pin::new(&mut listener).poll(cx) { + Poll::Ready(()) => { + let mut listener; + while { + listener = self.event.listen(); + + Pin::new(&mut listener).poll(cx).is_ready() + } {} + + self.listener = Some(listener); + + if let Some(selected) = self.obj.active_id() { + Poll::Ready(Some(selected.into())) + } else { + Poll::Pending + } + } + + Poll::Pending => { + self.listener = Some(listener); + Poll::Pending + } + } + } +} diff --git a/streamdeck/src/daemon.rs b/streamdeck/src/daemon.rs index dd31c32..b46ad7d 100644 --- a/streamdeck/src/daemon.rs +++ b/streamdeck/src/daemon.rs @@ -372,7 +372,7 @@ struct Daemon { fn connect() -> futures_channel::mpsc::Sender { let (tx, mut rx) = futures_channel::mpsc::channel(16); - log::info!("Spawning connection pool"); + log::debug!("Spawning connection pool"); let mut senders = (0..16).map(|_| spawn_connection()).collect::>(); @@ -397,7 +397,7 @@ fn connect() -> futures_channel::mpsc::Sender { count = (count + 1) % len; } - log::info!("Shutting down dbus connection pool"); + log::debug!("Shutting down dbus connection pool"); }); tx diff --git a/streamdeck/src/dialogs/edit_deck_dialog.rs b/streamdeck/src/dialogs/edit_deck_dialog.rs new file mode 100644 index 0000000..61849a6 --- /dev/null +++ b/streamdeck/src/dialogs/edit_deck_dialog.rs @@ -0,0 +1,52 @@ +use crate::daemon::Handle; +use gtk::prelude::*; +use marble::widgets::Dialog; +use std::{cell::RefCell, rc::Rc}; + +pub(crate) fn build(serial_number: String, deck_name: String) -> Dialog { + let dialog = Dialog::new(); + + let label = gtk::Label::new(Some("Edit deck name")); + let entry = gtk::Entry::new(); + entry.set_valign(gtk::Align::Center); + entry + .style_context() + .add_class(marble::STYLE_CLASS_H3_LABEL); + entry.set_text(&deck_name); + + let grid = gtk::Grid::new(); + grid.set_column_spacing(12); + grid.set_row_spacing(12); + grid.set_halign(gtk::Align::Center); + grid.set_margin(24); + + grid.attach(&label, 0, 0, 1, 1); + grid.attach(&entry, 1, 0, 3, 1); + + dialog.content_area().add(&grid); + + let serial_number = Rc::new(serial_number); + let previous_name = Rc::new(RefCell::new(deck_name)); + entry.connect_changed(move |entry| { + let serial_number = Rc::clone(&serial_number); + let previous_name = Rc::clone(&previous_name); + + if previous_name.borrow().as_str() == entry.text() { + return; + } + + *previous_name.borrow_mut() = entry.text().into(); + + glib::MainContext::default().spawn_local(async move { + let _ = Handle::current() + .set_deck_name( + String::clone(&serial_number), + String::clone(&previous_name.borrow()), + ) + .await; + }); + }); + + dialog.show_all(); + dialog +} diff --git a/streamdeck/src/dialogs/mod.rs b/streamdeck/src/dialogs/mod.rs new file mode 100644 index 0000000..0576b5d --- /dev/null +++ b/streamdeck/src/dialogs/mod.rs @@ -0,0 +1 @@ +pub mod edit_deck_dialog; diff --git a/streamdeck/src/main.rs b/streamdeck/src/main.rs index 053577a..ca0ff0f 100644 --- a/streamdeck/src/main.rs +++ b/streamdeck/src/main.rs @@ -2,8 +2,10 @@ use gio::{prelude::*, Settings}; use once_cell::unsync::Lazy; mod application; +mod command; mod config; mod daemon; +mod dialogs; mod main_window; mod views; mod widgets; diff --git a/streamdeck/src/meson.build b/streamdeck/src/meson.build index 3072db3..0019e39 100644 --- a/streamdeck/src/meson.build +++ b/streamdeck/src/meson.build @@ -22,6 +22,8 @@ run_command( ) rust_sources = files( + 'dialogs/edit_deck_dialog.rs', + 'dialogs/mod.rs', 'views/deck_stack.rs', 'views/deck_view.rs', 'views/mod.rs', @@ -32,6 +34,7 @@ rust_sources = files( 'widgets/deck_list.rs', 'widgets/mod.rs', 'application.rs', + 'command.rs', 'config.rs', 'daemon.rs', 'main.rs', diff --git a/streamdeck/src/views/deck_stack.rs b/streamdeck/src/views/deck_stack.rs index 3cfc1de..2366251 100644 --- a/streamdeck/src/views/deck_stack.rs +++ b/streamdeck/src/views/deck_stack.rs @@ -12,9 +12,7 @@ impl DeckStack { } pub(crate) fn select_deck(&self, serial_number: &str) { - log::info!("Selecting"); if self.child_by_name(serial_number).is_some() { - log::info!("has child"); self.set_visible_child_name(serial_number); } self.show_all(); diff --git a/streamdeck/src/widgets/command_list.rs b/streamdeck/src/widgets/command_list.rs index e005754..238105e 100644 --- a/streamdeck/src/widgets/command_list.rs +++ b/streamdeck/src/widgets/command_list.rs @@ -1,5 +1,5 @@ use crate::{ - daemon::{CommandInfo, Handle}, + daemon::{CommandInfo, Handle, InputName}, widgets::CommandRow, }; use gtk::{prelude::*, subclass::prelude::*}; @@ -21,31 +21,50 @@ impl CommandList { let inner = imp::CommandList::from_instance(self); inner.serial_number.set(serial_number.clone()).unwrap(); + let serial_number2 = serial_number.clone(); glib::MainContext::default().spawn_local(glib::clone!(@weak self as obj => async move { - // we have access to obj - if let Ok(commands) = Handle::current().get_commands(serial_number).await { - for info in commands { - obj.add_command(info); - } + let mut handle = Handle::current(); + + if let Ok(commands) = handle.get_commands(serial_number).await { + obj.add_commands(commands); + } + + if let Ok(names) = Handle::current().get_input_names(serial_number2).await { + obj.set_names(names); } })); } - fn add_command(&self, command_info: CommandInfo) { + fn add_commands(&self, commands: Vec) { let inner = imp::CommandList::from_instance(self); + let list_box = inner.list_box.get().unwrap(); + let serial_number = inner.serial_number.get().unwrap(); let mut rows = inner.rows.borrow_mut(); - if rows.contains_key(&command_info.key) { - return; + for command_info in commands { + if rows.contains_key(&command_info.key) { + return; + } + + let key = command_info.key; + let row = CommandRow::new(serial_number, command_info); + + list_box.add(&row); + rows.insert(key, row); + } + } + + fn set_names(&self, names: Vec) { + let inner = imp::CommandList::from_instance(self); + let rows = inner.rows.borrow(); + + for name in names { + if let Some(row) = rows.get(&name.key) { + row.set_name(&name.name); + } } - let serial_number = inner.serial_number.get().unwrap(); - let list_box = inner.list_box.get().unwrap(); - let key = command_info.key; - let row = CommandRow::new(serial_number, command_info); - - list_box.add(&row); - rows.insert(key, row); + self.show_all(); } } diff --git a/streamdeck/src/widgets/command_row.rs b/streamdeck/src/widgets/command_row.rs index 42fcf28..2190732 100644 --- a/streamdeck/src/widgets/command_row.rs +++ b/streamdeck/src/widgets/command_row.rs @@ -19,7 +19,9 @@ impl CommandRow { row_title.set_label(&format!("{}", command_info.key)); } - // TODO: Render based on command + let command_grid = inner.command_grid.get().unwrap(); + + crate::command::render_text(&command_info.command, command_grid); inner.input_key.set(command_info.key).unwrap(); inner @@ -30,6 +32,12 @@ impl CommandRow { this.show_all(); this } + + pub(crate) fn set_name(&self, name: &str) { + let this = imp::CommandRow::from_instance(self); + let row_title = this.row_title.get().unwrap(); + row_title.set_label(name); + } } mod imp { @@ -41,7 +49,6 @@ mod imp { #[derive(Debug, Default)] pub struct CommandRow { pub(super) row_title: OnceCell, - pub(super) row_command: OnceCell, pub(super) command_grid: OnceCell, pub(super) serial_number: OnceCell, pub(super) input_key: OnceCell, @@ -61,10 +68,6 @@ mod imp { row_title.set_halign(gtk::Align::Start); row_title.set_valign(gtk::Align::Center); - let row_command = gtk::Label::new(None); - row_command.set_halign(gtk::Align::Start); - row_command.set_valign(gtk::Align::Center); - let command_grid = gtk::Grid::new(); command_grid.set_halign(gtk::Align::Start); command_grid.set_valign(gtk::Align::Center); @@ -73,8 +76,6 @@ mod imp { command_grid.set_margin_start(3); command_grid.set_column_spacing(3); - command_grid.add(&row_command); - let row_edit = gtk::Button::from_icon_name(Some("document-edit"), gtk::IconSize::Button); row_edit.set_tooltip_text(Some("Edit")); @@ -96,7 +97,6 @@ mod imp { obj.show_all(); self.row_title.set(row_title).unwrap(); - self.row_command.set(row_command).unwrap(); self.command_grid.set(command_grid).unwrap(); // TODO: Click connect diff --git a/streamdeck/src/widgets/deck_item.rs b/streamdeck/src/widgets/deck_item.rs index 29e54a1..d1d5910 100644 --- a/streamdeck/src/widgets/deck_item.rs +++ b/streamdeck/src/widgets/deck_item.rs @@ -1,5 +1,7 @@ use crate::daemon::DeckInfo; use glib::object::ObjectExt; +use gtk::{prelude::*, subclass::prelude::*}; +use marble::widgets::Dialog; glib::wrapper! { pub struct DeckItem(ObjectSubclass) @@ -17,14 +19,58 @@ impl DeckItem { } pub(crate) fn serial_number(&self) -> Option { - let value = self.property("serial_number").ok()?; + let value = self.property("serial-number").ok()?; value.get().ok() } + + pub(crate) fn device_name(&self) -> Option { + let value = self.property("device-name").ok()?; + + value.get().ok() + } + + fn handle_clicked(&self) -> Option<()> { + let this = imp::DeckItem::from_instance(self); + + let serial_number = self.serial_number()?; + let device_name = self.device_name()?; + let mut dialog_opt = this.dialog.borrow_mut(); + + let dialog: Dialog = if let Some(dialog) = &*dialog_opt { + dialog.clone() + } else { + let dialog = + crate::dialogs::edit_deck_dialog::build(serial_number.clone(), device_name.clone()); + + dialog.set_transient_for( + self.toplevel() + .and_then(|widget| widget.downcast::().ok()) + .as_ref(), + ); + + *dialog_opt = Some(dialog.clone()); + + dialog.connect_response(glib::clone!(@weak self as obj => move |_, _| { + let this = imp::DeckItem::from_instance(&obj); + let mut dialog_opt = this.dialog.borrow_mut(); + if let Some(dialog) = dialog_opt.take() { + dialog.close(); + } + })); + + dialog + }; + + dialog.present(); + + Some(()) + } } mod imp { use gtk::{prelude::*, subclass::prelude::*}; + use marble::widgets::Dialog; use once_cell::{sync::Lazy, unsync::OnceCell}; use std::{cell::RefCell, rc::Rc}; @@ -33,6 +79,7 @@ mod imp { serial_number: OnceCell, device_name: OnceCell>>, port_name: OnceCell, + pub(super) dialog: RefCell>, } #[glib::object_subclass] @@ -80,6 +127,20 @@ mod imp { obj.add(&grid); obj.show_all(); + edit_button.connect_clicked(glib::clone!(@weak obj => move |_| { + obj.handle_clicked(); + })); + + glib::MainContext::default().spawn_local(glib::clone!(@weak obj => async move { + let mut handle = crate::daemon::Handle::current(); + let this = DeckItem::from_instance(&obj); + let serial_number = this.serial_number.get().unwrap(); + + if let Ok(name) = handle.get_deck_name(serial_number.to_owned()).await { + let _ = obj.set_property("device-name", &name); + } + })); + obj.bind_property("device-name", &row_title, "label") .build(); obj.bind_property("port-name", &row_desc, "label").build(); diff --git a/streamdeck/src/widgets/deck_list.rs b/streamdeck/src/widgets/deck_list.rs index 03c5d92..19d7685 100644 --- a/streamdeck/src/widgets/deck_list.rs +++ b/streamdeck/src/widgets/deck_list.rs @@ -71,6 +71,7 @@ mod imp { pub struct DeckList { pub(super) stack: OnceCell, pub(super) list_box: OnceCell, + pub(super) usb_label: OnceCell, } #[glib::object_subclass] @@ -138,6 +139,7 @@ mod imp { // TODO: daemon decks added callback // TODO: daemon decks removed callback + self.usb_label.set(usb_label).unwrap(); self.list_box.set(list_box).unwrap(); } }