diff --git a/Cargo.lock b/Cargo.lock index a884896..f9ac40f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,6 +15,12 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" +[[package]] +name = "ahash" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" + [[package]] name = "aho-corasick" version = "0.7.13" @@ -96,6 +102,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "beef" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "474a626a67200bd107d44179bb3d4fc61891172d11696609264589be6a0e6a43" + [[package]] name = "bincode" version = "1.3.1" @@ -171,6 +183,12 @@ dependencies = [ "vec_map", ] +[[package]] +name = "codemap" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e769b5c8c8283982a987c6e948e540254f1058d5a74b8794914d4ef5fc2a24" + [[package]] name = "crossbeam-channel" version = "0.4.4" @@ -299,10 +317,12 @@ dependencies = [ "gdk", "gio", "glib", + "grass", "gtk", "hocon", "ipc-channel", "maplit", + "notify", "pretty_assertions", "regex", "serde", @@ -344,12 +364,43 @@ dependencies = [ "synstructure", ] +[[package]] +name = "filetime" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed85775dcc68644b5c950ac06a2b23768d3bc9390464151aaf27136998dcf9e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "winapi 0.3.9", +] + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "fsevent" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ab7d1bd1bd33cc98b0889831b72da23c0aa4df9cec7e0702f46ecea04b35db6" +dependencies = [ + "bitflags", + "fsevent-sys", +] + +[[package]] +name = "fsevent-sys" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f41b048a94555da0f42f1d632e2e19510084fb8e303b0daa2816e733fb3644a0" +dependencies = [ + "libc", +] + [[package]] name = "fuchsia-zircon" version = "0.3.3" @@ -633,6 +684,26 @@ dependencies = [ "system-deps", ] +[[package]] +name = "grass" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de2cf11276bb4d38b76d9666f9d87423522d57a519aefb9df744c1f6cf574e4f" +dependencies = [ + "beef", + "clap", + "codemap", + "indexmap", + "lasso", + "num-bigint", + "num-rational", + "num-traits", + "once_cell", + "peekmore", + "phf", + "rand", +] + [[package]] name = "gtk" version = "0.9.2" @@ -679,6 +750,22 @@ dependencies = [ "system-deps", ] +[[package]] +name = "hashbrown" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b62f79061a0bc2e046024cb7ba44b08419ed238ecbd9adbd787434b9e8c25" +dependencies = [ + "ahash", + "autocfg", +] + +[[package]] +name = "hashbrown" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00d63df3d41950fb462ed38308eea019113ad1508da725bbedcd0fa5a85ef5f7" + [[package]] name = "heck" version = "0.3.1" @@ -711,6 +798,36 @@ dependencies = [ "uuid", ] +[[package]] +name = "indexmap" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2" +dependencies = [ + "autocfg", + "hashbrown 0.9.0", +] + +[[package]] +name = "inotify" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4816c66d2c8ae673df83366c18341538f234a26d65a9ecea5c348b453ac1d02f" +dependencies = [ + "bitflags", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e74a1aa87c59aeff6ef2cc2fa62d41bc43f54952f55652656b18a02fd5e356c0" +dependencies = [ + "libc", +] + [[package]] name = "iovec" version = "0.1.4" @@ -767,12 +884,27 @@ dependencies = [ "winapi-build", ] +[[package]] +name = "lasso" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf1a626ea51398f5acf36666c8046ff4bfd048aab88e92db676d2a6eac8805d0" +dependencies = [ + "hashbrown 0.8.2", +] + [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.77" @@ -835,6 +967,18 @@ dependencies = [ "winapi 0.2.8", ] +[[package]] +name = "mio-extras" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" +dependencies = [ + "lazycell", + "log", + "mio", + "slab", +] + [[package]] name = "miow" version = "0.2.1" @@ -868,6 +1012,66 @@ dependencies = [ "version_check 0.1.5", ] +[[package]] +name = "notify" +version = "4.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80ae4a7688d1fab81c5bf19c64fc8db920be8d519ce6336ed4e7efe024724dbd" +dependencies = [ + "bitflags", + "filetime", + "fsevent", + "fsevent-sys", + "inotify", + "libc", + "mio", + "mio-extras", + "walkdir", + "winapi 0.3.9", +] + +[[package]] +name = "num-bigint" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f3fc75e3697059fb1bc465e3d8cca6cf92f56854f201158b3f9c77d5a3cfa0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5b4d7360f362cfb50dde8143501e6940b22f644be75a4cc90b2d81968908138" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" +dependencies = [ + "autocfg", +] + [[package]] name = "object" version = "0.20.0" @@ -916,6 +1120,59 @@ dependencies = [ "system-deps", ] +[[package]] +name = "peekmore" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e029e89e27328ecf6a2e0820b5767ce6deb10872f2f18fb9e98d2e8ab7aef29f" +dependencies = [ + "smallvec", +] + +[[package]] +name = "phf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +dependencies = [ + "phf_macros", + "phf_shared", + "proc-macro-hack", +] + +[[package]] +name = "phf_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project" version = "0.4.23" @@ -1040,6 +1297,7 @@ dependencies = [ "rand_chacha", "rand_core", "rand_hc", + "rand_pcg", ] [[package]] @@ -1070,6 +1328,15 @@ dependencies = [ "rand_core", ] +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core", +] + [[package]] name = "redox_syscall" version = "0.1.57" @@ -1109,6 +1376,15 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "serde" version = "1.0.116" @@ -1129,12 +1405,24 @@ dependencies = [ "syn", ] +[[package]] +name = "siphasher" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa8f3741c7372e75519bd9346068370c9cdaabcc1f9599cbcf2a2719352286b7" + [[package]] name = "slab" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" +[[package]] +name = "smallvec" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" + [[package]] name = "strsim" version = "0.8.0" @@ -1356,6 +1644,17 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" +[[package]] +name = "walkdir" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" +dependencies = [ + "same-file", + "winapi 0.3.9", + "winapi-util", +] + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" @@ -1390,6 +1689,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 2ea92af..3dd375b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,8 @@ structopt = "0.3" ipc-channel="0.14.1" serde = {version = "1.0", features = ["derive"]} extend = "0.3.0" +grass = "0.10" +notify = "4.0" #thiserror = "1.0" diff --git a/src/main.rs b/src/main.rs index eeea5ec..4c6315b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,10 +6,13 @@ extern crate gtk; use anyhow::*; use gdk::*; use gio::prelude::*; +use grass; use gtk::prelude::*; use ipc_channel::ipc; +use notify::{self, Watcher}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; +use std::path; use structopt::StructOpt; pub mod config; @@ -29,61 +32,6 @@ macro_rules! build { }}; } -const EXAMPLE_CONFIG: &str = r#"{ - widgets: { - some_widget: { - structure: { - layout: { - class: "container", - children: [ - { layout: { - orientation: "v" - children: [ - "fancy button" - { button: { children: "reeee" } } - ] - } } - { layout: { - children: [ - "date" // TODO FIX!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - { label: { text: "$$date" } } - { button: { children: "click me you" } } - { slider: { value: "$$some_value", min: 0, max: 100, onchange: "notify-send 'changed' {}" } } - { slider: { value: "$$some_value", orientation: "vertical" } } - "hu" - ] - } } - ] - } - } - }, - test: { - structure: { - some_widget: { - some_value: "$$ooph" - } - } - } - }, - default_vars: { - ree: 12 - date: "never" - } - windows: { - main_window: { - pos.x: 200 - pos.y: 1550 - size.x: 500 - size.y: 50 - widget: { - test: { - ooph: "$$ree" - } - } - } - }, -}"#; - fn main() { if let Err(e) = try_main() { eprintln!("{:?}", e); @@ -92,27 +40,29 @@ fn main() { #[derive(StructOpt, Debug, Serialize, Deserialize)] struct Opt { + #[structopt(short = "-c", parse(from_os_str))] + config_file: Option, + #[structopt(subcommand)] action: OptAction, } #[derive(StructOpt, Debug, Serialize, Deserialize)] enum OptAction { #[structopt(name = "update")] - Update(OptActionUpdate), + Update { fieldname: String, value: PrimitiveValue }, #[structopt(name = "open")] - OpenWindow(OptActionOpen), + OpenWindow { window_name: String }, + + #[structopt(name = "close")] + CloseWindow { window_name: String }, } -#[derive(StructOpt, Debug, Serialize, Deserialize)] -struct OptActionUpdate { - fieldname: String, - value: PrimitiveValue, -} - -#[derive(StructOpt, Debug, Serialize, Deserialize)] -struct OptActionOpen { - window_name: String, +#[derive(Debug)] +enum EwwEvent { + UserCommand(Opt), + ReloadConfig(config::EwwConfig), + ReloadCss(String), } fn try_main() -> Result<()> { @@ -130,21 +80,102 @@ fn find_server_process() -> Result> { Ok(ipc::IpcSender::connect(instance_path)?) } +fn get_config_file_path() -> path::PathBuf { + std::env::var("XDG_CONFIG_HOME") + .map(|v| path::PathBuf::from(v)) + .unwrap_or_else(|_| path::PathBuf::from(std::env::var("HOME").unwrap()).join(".config")) + .join("eww") + .join("eww.conf") +} + fn initialize_server(opts: Opt) -> Result<()> { - let eww_config = config::EwwConfig::from_hocon(&config::parse_hocon(EXAMPLE_CONFIG)?)?; + let config_file_path = opts.config_file.clone().unwrap_or_else(get_config_file_path); + let config_dir = config_file_path + .clone() + .parent() + .context("config file did not have a parent?!")? + .to_owned() + .to_path_buf(); + let scss_file_path = config_dir.join("eww.scss"); + + let (watcher_tx, watcher_rx) = std::sync::mpsc::channel(); + + let mut file_watcher = notify::watcher(watcher_tx, std::time::Duration::from_millis(100))?; + file_watcher.watch(config_file_path.clone(), notify::RecursiveMode::NonRecursive)?; + if let Err(e) = file_watcher.watch(scss_file_path.clone(), notify::RecursiveMode::NonRecursive) { + eprintln!("WARN: error while loading CSS file for hot-reloading: \n{}", e) + } + + let config_content = std::fs::read_to_string(config_file_path.clone())?; + let scss_content = std::fs::read_to_string(scss_file_path.clone()).unwrap_or_default(); + + let eww_config = config::EwwConfig::from_hocon(&config::parse_hocon(&config_content)?)?; + let eww_css = + grass::from_string(scss_content, &grass::Options::default()).map_err(|err| anyhow!("SCSS parsing error: {:?}", err))?; let mut app = App { eww_state: EwwState::from_default_vars(eww_config.get_default_vars().clone()), eww_config, + eww_css: eww_css.clone(), + windows: HashMap::new(), }; - - let (send, recv) = glib::MainContext::channel(glib::PRIORITY_DEFAULT); - gtk::init()?; + let css_provider = gtk::CssProvider::new(); + css_provider.load_from_data(eww_css.as_bytes())?; + gdk::Screen::get_default().map(|screen| { + gtk::StyleContext::add_provider_for_screen(&screen, &css_provider, gtk::STYLE_PROVIDER_PRIORITY_APPLICATION); + }); + app.handle_user_command(opts)?; - std::thread::spawn(move || run_ipc_server(send)); + let (send, recv) = glib::MainContext::channel(glib::PRIORITY_DEFAULT); + std::thread::spawn({ + let send = send.clone(); + { + move || { + let result: Result<_> = try { + loop { + let (ipc_server, instance_path): (ipc::IpcOneShotServer, _) = ipc::IpcOneShotServer::new()?; + std::fs::write("/tmp/eww-instance-path", instance_path)?; + let (_, initial) = ipc_server.accept()?; + send.send(EwwEvent::UserCommand(initial))?; + } + }; + if let Err(err) = result { + eprintln!("error in server thread: {}", err); + std::process::exit(1); + } + } + } + }); + std::thread::spawn(move || { + while let Ok(event) = watcher_rx.recv() { + let result: Result<_> = try { + dbg!(&event); + match event { + notify::DebouncedEvent::Write(updated_path) | notify::DebouncedEvent::NoticeWrite(updated_path) + if updated_path == config_file_path => + { + let new_eww_config = config::EwwConfig::from_hocon(&config::parse_hocon(&config_content)?)?; + send.send(EwwEvent::ReloadConfig(new_eww_config))?; + } + notify::DebouncedEvent::Write(updated_path) if updated_path == scss_file_path => { + let scss_content = std::fs::read_to_string(scss_file_path.clone()).unwrap_or_default(); + let eww_css = grass::from_string(scss_content, &grass::Options::default()) + .map_err(|err| anyhow!("SCSS parsing error: {:?}", err))?; + send.send(EwwEvent::ReloadCss(eww_css))?; + } + _ => {} + } + }; + if let Err(err) = result { + eprintln!("error in server thread: {}", err); + std::process::exit(1); + } + } + }); + recv.attach(None, move |msg| { app.handle_event(msg); glib::Continue(true) @@ -155,63 +186,62 @@ fn initialize_server(opts: Opt) -> Result<()> { Ok(()) } -fn run_ipc_server(send: glib::Sender) -> Result<()> { - loop { - let (ipc_server, instance_path): (ipc::IpcOneShotServer, _) = ipc::IpcOneShotServer::new()?; - std::fs::write("/tmp/eww-instance-path", instance_path)?; - let (receiver, initial) = ipc_server.accept()?; - send.send(EwwEvent::UserCommand(initial))?; - } -} - #[derive(Debug)] struct App { eww_state: EwwState, eww_config: config::EwwConfig, + eww_css: String, + windows: HashMap, } impl App { fn handle_user_command(&mut self, opts: Opt) -> Result<()> { match opts.action { - OptAction::Update(update) => self.update_state(update), - OptAction::OpenWindow(update) => self.open_window(update)?, + OptAction::Update { fieldname, value } => self.update_state(fieldname, value), + OptAction::OpenWindow { window_name } => self.open_window(&window_name)?, + OptAction::CloseWindow { window_name } => self.close_window(&window_name)?, } Ok(()) } - fn update_state(&mut self, update: OptActionUpdate) { - self.eww_state.update_value(update.fieldname, update.value); + fn update_state(&mut self, fieldname: String, value: PrimitiveValue) { + self.eww_state.update_value(fieldname, value); } - fn open_window(&mut self, open_window: OptActionOpen) -> Result<()> { - let window_def = self.eww_config.get_windows()[&open_window.window_name].clone(); + fn close_window(&mut self, window_name: &str) -> Result<()> { + let window = self + .windows + .get(window_name) + .context(format!("No window with name '{}' is running.", window_name))?; + window.close(); + Ok(()) + } - let window = gtk::Window::new(gtk::WindowType::Toplevel); + fn open_window(&mut self, window_name: &str) -> Result<()> { + let window_def = self + .eww_config + .get_windows() + .get(window_name) + .context(format!("No window named '{}' defined", window_name))? + .clone(); + + let window = gtk::Window::new(gtk::WindowType::Popup); window.set_title("Eww"); window.set_wmclass("noswallow", "noswallow"); window.set_type_hint(gdk::WindowTypeHint::Dock); window.set_position(gtk::WindowPosition::Center); - window.set_keep_above(true); window.set_default_size(window_def.size.0, window_def.size.1); - window.set_visual( - window - .get_display() - .get_default_screen() - .get_rgba_visual() - .or_else(|| window.get_display().get_default_screen().get_system_visual()) - .as_ref(), - ); - - window.fullscreen(); + window.set_decorated(false); let empty_local_state = HashMap::new(); - - window.add(&widgets::element_to_gtk_thing( + let root_widget = &widgets::element_to_gtk_thing( &self.eww_config.get_widgets(), &mut self.eww_state, &empty_local_state, &window_def.widget, - )?); + )?; + root_widget.get_style_context().add_class(window_name); + window.add(root_widget); window.show_all(); @@ -220,7 +250,27 @@ impl App { gdk_window.move_(window_def.position.0, window_def.position.1); gdk_window.show(); gdk_window.raise(); + window.set_keep_above(true); + self.windows.insert(window_name.to_string(), window); + + Ok(()) + } + + fn reload_all_windows(&mut self, config: config::EwwConfig) -> Result<()> { + self.eww_config = config; + let windows = self.windows.clone(); + for (window_name, window) in windows { + dbg!(&window_name); + window.close(); + window.hide(); + self.open_window(&window_name)?; + } + Ok(()) + } + + fn reload_css(&mut self, css: String) -> Result<()> { + for window in self.windows.values() {} Ok(()) } @@ -228,6 +278,8 @@ impl App { let result: Result<_> = try { match event { EwwEvent::UserCommand(command) => self.handle_user_command(command)?, + EwwEvent::ReloadConfig(config) => self.reload_all_windows(config)?, + EwwEvent::ReloadCss(css) => self.reload_css(css)?, } }; if let Err(err) = result { @@ -235,23 +287,3 @@ impl App { } } } - -#[derive(Debug)] -enum EwwEvent { - UserCommand(Opt), -} - -fn event_loop(sender: glib::Sender) { - let mut x = 0; - loop { - x += 1; - std::thread::sleep(std::time::Duration::from_millis(1000)); - let event_opt = Opt { - action: OptAction::Update(OptActionUpdate { - fieldname: "ree".to_string(), - value: PrimitiveValue::Number(x as f64 * 10.0), - }), - }; - sender.send(EwwEvent::UserCommand(event_opt)).unwrap(); - } -} diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index faf2fdb..1cb7d3f 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -39,7 +39,9 @@ pub fn element_to_gtk_thing( } else if let Some(def) = widget_definitions.get(widget.name.as_str()) { let mut local_env = local_env.clone(); local_env.extend(widget.attrs.clone()); - element_to_gtk_thing(widget_definitions, eww_state, &local_env, &def.structure)? + let custom_widget = element_to_gtk_thing(widget_definitions, eww_state, &local_env, &def.structure)?; + custom_widget.get_style_context().add_class(widget.name.as_str()); + custom_widget } else { return Err(anyhow!("unknown widget: '{}'", &widget.name)); };