From 7af0cb2489ee0dc3eb5fa6db8fd86d799eeb0ea5 Mon Sep 17 00:00:00 2001 From: elkowar <5300871+elkowar@users.noreply.github.com> Date: Sun, 20 Sep 2020 23:13:20 +0200 Subject: [PATCH] fancy macro makes things gud --- src/config/element.rs | 8 ++ src/config/mod.rs | 50 ------------ src/eww_state.rs | 93 ++++++++++++++++++++++ src/main.rs | 177 +++++++----------------------------------- src/value.rs | 49 ++++++++++++ src/widgets.rs | 89 +++++++++++++++++++++ src/widgets/mod.rs | 0 7 files changed, 267 insertions(+), 199 deletions(-) create mode 100644 src/eww_state.rs create mode 100644 src/widgets.rs delete mode 100644 src/widgets/mod.rs diff --git a/src/config/element.rs b/src/config/element.rs index 9c71d6c..8fe04ae 100644 --- a/src/config/element.rs +++ b/src/config/element.rs @@ -1,5 +1,6 @@ use super::*; +use crate::value::AttrValue; use hocon_ext::HoconExt; use std::collections::HashMap; use std::convert::TryFrom; @@ -95,6 +96,13 @@ impl WidgetUse { attrs, }) } + + pub fn get_attr(&self, key: &str) -> Result<&AttrValue> { + self.attrs.get(key).context(format!( + "attribute {} missing from widgetuse of {}", + key, &self.name + )) + } } impl From for ElementUse { diff --git a/src/config/mod.rs b/src/config/mod.rs index 8b63569..8c6270b 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -5,7 +5,6 @@ use hocon::*; use hocon_ext::HoconExt; use std::collections::HashMap; use std::convert::TryFrom; -use try_match::try_match; pub mod element; pub mod hocon_ext; @@ -111,55 +110,6 @@ impl EwwWindowDefinition { } } -#[derive(Clone, Debug, PartialEq)] -pub enum AttrValue { - Concrete(PrimitiveValue), - VarRef(String), -} - -impl AttrValue { - pub fn as_string(&self) -> Result<&String> { - try_match!(AttrValue::Concrete(x) = self) - .map_err(|e| anyhow!("{:?} is not a string", e))? - .as_string() - } - pub fn as_f64(&self) -> Result { - try_match!(AttrValue::Concrete(x) = self) - .map_err(|e| anyhow!("{:?} is not an f64", e))? - .as_f64() - } - pub fn as_bool(&self) -> Result { - try_match!(AttrValue::Concrete(x) = self) - .map_err(|e| anyhow!("{:?} is not a bool", e))? - .as_bool() - } - pub fn as_var_ref(&self) -> Result<&String> { - try_match!(AttrValue::VarRef(x) = self).map_err(|e| anyhow!("{:?} is not a VarRef", e)) - } -} - -impl From for AttrValue { - fn from(value: PrimitiveValue) -> Self { - AttrValue::Concrete(value) - } -} - -impl std::convert::TryFrom<&Hocon> for AttrValue { - type Error = anyhow::Error; - fn try_from(value: &Hocon) -> Result { - Ok(match value { - Hocon::String(s) if s.starts_with("$$") => { - AttrValue::VarRef(s.trim_start_matches("$$").to_string()) - } - Hocon::String(s) => AttrValue::Concrete(PrimitiveValue::String(s.clone())), - Hocon::Integer(n) => AttrValue::Concrete(PrimitiveValue::Number(*n as f64)), - Hocon::Real(n) => AttrValue::Concrete(PrimitiveValue::Number(*n as f64)), - Hocon::Boolean(b) => AttrValue::Concrete(PrimitiveValue::Boolean(*b)), - _ => return Err(anyhow!("cannot convert {:?} to config::AttrValue", &value)), - }) - } -} - pub fn parse_hocon(s: &str) -> Result { Ok(HoconLoader::new().load_str(s)?.hocon()?) } diff --git a/src/eww_state.rs b/src/eww_state.rs new file mode 100644 index 0000000..b7575ee --- /dev/null +++ b/src/eww_state.rs @@ -0,0 +1,93 @@ +use std::collections::HashMap; + +use crate::value::{AttrValue, PrimitiveValue}; + +#[derive(Default)] +pub struct EwwState { + on_change_handlers: HashMap>>, + state: HashMap, +} + +impl EwwState { + pub fn from_default_vars(defaults: HashMap) -> Self { + EwwState { + state: defaults, + ..EwwState::default() + } + } + pub fn update_value(&mut self, key: String, value: PrimitiveValue) { + if let Some(handlers) = self.on_change_handlers.get(&key) { + for on_change in handlers { + on_change(value.clone()); + } + } + self.state.insert(key, value); + } + + pub fn resolve( + &mut self, + local_env: &HashMap, + value: &AttrValue, + set_value: F, + ) -> bool { + dbg!("resolve: ", value); + match value { + AttrValue::VarRef(name) => { + if let Some(value) = self.state.get(name).cloned() { + self.on_change_handlers + .entry(name.to_string()) + .or_insert_with(Vec::new) + .push(Box::new(set_value.clone())); + self.resolve(local_env, &value.into(), set_value) + } else if let Some(value) = local_env.get(name).cloned() { + self.resolve(local_env, &value, set_value) + } else { + false + } + } + AttrValue::Concrete(value) => { + set_value(value.clone()); + true + } + } + } + + pub fn resolve_f64( + &mut self, + local_env: &HashMap, + value: &AttrValue, + set_value: F, + ) -> bool { + self.resolve(local_env, value, move |x| { + if let Err(e) = x.as_f64().map(|v| set_value(v)) { + eprintln!("error while resolving value: {}", e); + }; + }) + } + + #[allow(dead_code)] + pub fn resolve_bool( + &mut self, + local_env: &HashMap, + value: &AttrValue, + set_value: F, + ) -> bool { + self.resolve(local_env, value, move |x| { + if let Err(e) = x.as_bool().map(|v| set_value(v)) { + eprintln!("error while resolving value: {}", e); + }; + }) + } + pub fn resolve_string( + &mut self, + local_env: &HashMap, + value: &AttrValue, + set_value: F, + ) -> bool { + self.resolve(local_env, value, move |x| { + if let Err(e) = x.as_string().map(|v| set_value(v.clone())) { + eprintln!("error while resolving value: {}", e); + }; + }) + } +} diff --git a/src/main.rs b/src/main.rs index 8985232..a461fa4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,22 +2,29 @@ extern crate gio; extern crate gtk; -use anyhow::{self, Result}; +use anyhow::*; use gdk::*; use gio::prelude::*; use gtk::prelude::*; use gtk::{Application, ApplicationWindow}; -use std::{collections::HashMap, process::Command}; +use std::collections::HashMap; pub mod config; +pub mod eww_state; pub mod value; pub mod widgets; use config::element; -use config::AttrValue; -use value::PrimitiveValue; +use eww_state::*; +use value::{AttrValue, PrimitiveValue}; -const CMD_STRING_PLACEHODLER: &str = "{}"; +macro_rules! build { + ($var_name:ident = $value:expr ; $code:block) => {{ + let mut $var_name = $value; + $code; + $var_name + }}; +} const EXAMPLE_CONFIG: &str = r#"{ widgets: { @@ -61,14 +68,6 @@ const EXAMPLE_CONFIG: &str = r#"{ }, }"#; -macro_rules! build { - ($var_name:ident = $value:expr ; $code:block) => {{ - let mut $var_name = $value; - $code; - $var_name - }}; -} - #[derive(Debug)] enum MuhhMsg { UpdateValue(String, PrimitiveValue), @@ -170,9 +169,9 @@ fn element_to_gtk_thing( eww_state: &mut EwwState, local_environment: &HashMap, element: &element::ElementUse, -) -> Option { +) -> Result { match element { - element::ElementUse::Text(text) => Some(gtk::Label::new(Some(&text)).upcast()), + element::ElementUse::Text(text) => Ok(gtk::Label::new(Some(&text)).upcast()), element::ElementUse::Widget(widget) => { widget_use_to_gtk_thing(widget_definitions, eww_state, local_environment, widget) } @@ -184,7 +183,7 @@ fn widget_use_to_gtk_thing( eww_state: &mut EwwState, local_environment: &HashMap, widget: &element::WidgetUse, -) -> Option { +) -> Result { let gtk_widget = widget_use_to_gtk_container(widget_definitions, eww_state, &local_environment, &widget) .or(widget_use_to_gtk_widget( @@ -201,7 +200,7 @@ fn widget_use_to_gtk_thing( gtk_widget.get_style_context().add_class(css_class); } - Some(gtk_widget) + Ok(gtk_widget) } fn widget_use_to_gtk_container( @@ -209,11 +208,11 @@ fn widget_use_to_gtk_container( eww_state: &mut EwwState, local_environment: &HashMap, widget: &element::WidgetUse, -) -> Option { +) -> Result { let container_widget: gtk::Container = match widget.name.as_str() { "layout_horizontal" => gtk::Box::new(gtk::Orientation::Horizontal, 0).upcast(), "button" => gtk::Button::new().upcast(), - _ => return None, + _ => return Err(anyhow!("{} is not a known container widget", widget.name)), }; for child in &widget.children { @@ -224,7 +223,7 @@ fn widget_use_to_gtk_container( child, )?); } - Some(container_widget.upcast()) + Ok(container_widget.upcast()) } fn widget_use_to_gtk_widget( @@ -232,37 +231,14 @@ fn widget_use_to_gtk_widget( eww_state: &mut EwwState, local_env: &HashMap, widget: &element::WidgetUse, -) -> Option { +) -> Result { + let builder_args = widgets::BuilderArgs { + eww_state, + local_env: &local_env, + widget: &widget, + }; let new_widget: gtk::Widget = match widget.name.as_str() { - "slider" => { - let scale = gtk::Scale::new( - gtk::Orientation::Horizontal, - Some(>k::Adjustment::new(0.0, 0.0, 100.0, 1.0, 1.0, 1.0)), - ); - eww_state.resolve_f64(local_env, widget.attrs.get("value")?, { - let scale = scale.clone(); - move |value| scale.set_value(value) - }); - eww_state.resolve_f64(local_env, widget.attrs.get("min")?, { - let scale = scale.clone(); - move |value| scale.get_adjustment().set_lower(value) - }); - eww_state.resolve_f64(local_env, widget.attrs.get("max")?, { - let scale = scale.clone(); - move |value| scale.get_adjustment().set_upper(value) - }); - eww_state.resolve_string(local_env, widget.attrs.get("onchange")?, { - let scale = scale.clone(); - move |on_change| { - scale.connect_value_changed(move |scale| { - run_command(&on_change, scale.get_value()); - }); - } - }); - - //scale.set_property("draw-value", &false.to_value()).ok()?; - scale.upcast() - } + "slider" => widgets::build_gtk_scale(builder_args)?.upcast(), name if widget_definitions.contains_key(name) => { let def = &widget_definitions[name]; @@ -277,104 +253,7 @@ fn widget_use_to_gtk_widget( &def.structure, )? } - _ => return None, + _ => return Err(anyhow!("unknown widget {}", &widget.name)), }; - Some(new_widget) -} - -#[derive(Default)] -struct EwwState { - on_change_handlers: HashMap>>, - state: HashMap, -} - -impl EwwState { - pub fn from_default_vars(defaults: HashMap) -> Self { - EwwState { - state: defaults, - ..EwwState::default() - } - } - pub fn update_value(&mut self, key: String, value: PrimitiveValue) { - if let Some(handlers) = self.on_change_handlers.get(&key) { - for on_change in handlers { - on_change(value.clone()); - } - } - self.state.insert(key, value); - } - - pub fn resolve( - &mut self, - local_env: &HashMap, - value: &AttrValue, - set_value: F, - ) -> bool { - dbg!("resolve: ", value); - match value { - AttrValue::VarRef(name) => { - if let Some(value) = self.state.get(name).cloned() { - self.on_change_handlers - .entry(name.to_string()) - .or_insert_with(Vec::new) - .push(Box::new(set_value.clone())); - self.resolve(local_env, &value.into(), set_value) - } else if let Some(value) = local_env.get(name).cloned() { - self.resolve(local_env, &value, set_value) - } else { - false - } - } - AttrValue::Concrete(value) => { - set_value(value.clone()); - true - } - } - } - - pub fn resolve_f64( - &mut self, - local_env: &HashMap, - value: &AttrValue, - set_value: F, - ) -> bool { - self.resolve(local_env, value, move |x| { - if let Err(e) = x.as_f64().map(|v| set_value(v)) { - eprintln!("error while resolving value: {}", e); - }; - }) - } - - #[allow(dead_code)] - pub fn resolve_bool( - &mut self, - local_env: &HashMap, - value: &AttrValue, - set_value: F, - ) -> bool { - self.resolve(local_env, value, move |x| { - if let Err(e) = x.as_bool().map(|v| set_value(v)) { - eprintln!("error while resolving value: {}", e); - }; - }) - } - pub fn resolve_string( - &mut self, - local_env: &HashMap, - value: &AttrValue, - set_value: F, - ) -> bool { - self.resolve(local_env, value, move |x| { - if let Err(e) = x.as_string().map(|v| set_value(v.clone())) { - eprintln!("error while resolving value: {}", e); - }; - }) - } -} - -fn run_command(cmd: &str, arg: T) { - let cmd = cmd.replace(CMD_STRING_PLACEHODLER, &format!("{}", arg)); - if let Err(e) = Command::new("bash").arg("-c").arg(cmd).output() { - eprintln!("{}", e); - } + Ok(new_widget) } diff --git a/src/value.rs b/src/value.rs index ed22fb0..f941be1 100644 --- a/src/value.rs +++ b/src/value.rs @@ -44,3 +44,52 @@ impl std::convert::TryFrom<&Hocon> for PrimitiveValue { }) } } + +#[derive(Clone, Debug, PartialEq)] +pub enum AttrValue { + Concrete(PrimitiveValue), + VarRef(String), +} + +impl AttrValue { + pub fn as_string(&self) -> Result<&String> { + try_match!(AttrValue::Concrete(x) = self) + .map_err(|e| anyhow!("{:?} is not a string", e))? + .as_string() + } + pub fn as_f64(&self) -> Result { + try_match!(AttrValue::Concrete(x) = self) + .map_err(|e| anyhow!("{:?} is not an f64", e))? + .as_f64() + } + pub fn as_bool(&self) -> Result { + try_match!(AttrValue::Concrete(x) = self) + .map_err(|e| anyhow!("{:?} is not a bool", e))? + .as_bool() + } + pub fn as_var_ref(&self) -> Result<&String> { + try_match!(AttrValue::VarRef(x) = self).map_err(|e| anyhow!("{:?} is not a VarRef", e)) + } +} + +impl From for AttrValue { + fn from(value: PrimitiveValue) -> Self { + AttrValue::Concrete(value) + } +} + +impl std::convert::TryFrom<&Hocon> for AttrValue { + type Error = anyhow::Error; + fn try_from(value: &Hocon) -> Result { + Ok(match value { + Hocon::String(s) if s.starts_with("$$") => { + AttrValue::VarRef(s.trim_start_matches("$$").to_string()) + } + Hocon::String(s) => AttrValue::Concrete(PrimitiveValue::String(s.clone())), + Hocon::Integer(n) => AttrValue::Concrete(PrimitiveValue::Number(*n as f64)), + Hocon::Real(n) => AttrValue::Concrete(PrimitiveValue::Number(*n as f64)), + Hocon::Boolean(b) => AttrValue::Concrete(PrimitiveValue::Boolean(*b)), + _ => return Err(anyhow!("cannot convert {:?} to config::AttrValue", &value)), + }) + } +} diff --git a/src/widgets.rs b/src/widgets.rs new file mode 100644 index 0000000..88c896c --- /dev/null +++ b/src/widgets.rs @@ -0,0 +1,89 @@ +use crate::config::element; +use crate::eww_state::*; +use crate::value::AttrValue; +use anyhow::*; +use gtk::prelude::*; +use std::{collections::HashMap, process::Command}; + +const CMD_STRING_PLACEHODLER: &str = "{}"; + +macro_rules! resolve { + ($args:ident, $gtk_widget:ident, { + $($func:ident => + { + $($attr:literal => |$arg:ident| $body:expr),+ + } + ),+ + }) => { + $( + $( + $args.eww_state.$func($args.local_env, $args.widget.get_attr($attr)?, { + let $gtk_widget = $gtk_widget.clone(); + move |$arg| { $body; } + }); + )+ + )+ + } +} + +pub struct BuilderArgs<'a, 'b, 'c> { + pub eww_state: &'a mut EwwState, + pub local_env: &'b HashMap, + pub widget: &'c element::WidgetUse, +} + +pub fn build_gtk_scale(builder_args: BuilderArgs) -> Result { + let gtk_widget = gtk::Scale::new( + gtk::Orientation::Horizontal, + Some(>k::Adjustment::new(0.0, 0.0, 100.0, 1.0, 1.0, 1.0)), + ); + + resolve!(builder_args, gtk_widget, { + resolve_f64 => { + "value" => |v| gtk_widget.set_value(v), + "min" => |v| gtk_widget.get_adjustment().set_lower(v), + "max" => |v| gtk_widget.get_adjustment().set_upper(v) + }, + resolve_string => { + "onchange" => |cmd| { + gtk_widget.connect_value_changed(move |gtk_widget| { + run_command(&cmd, gtk_widget.get_value()); + }); + } + } + }); + Ok(gtk_widget) +} + +pub fn build_gtk_button(builder_args: BuilderArgs) -> Result { + let gtk_widget = gtk::Button::new(); + resolve!(builder_args, gtk_widget, { + resolve_bool => { + "active" => |v| gtk_widget.set_sensitive(v) + }, + resolve_string => { + "onclick" => |cmd| gtk_widget.connect_clicked(move |_| run_command(&cmd, "")) + } + }); + Ok(gtk_widget) +} + +pub fn build_gtk_layout(builder_args: BuilderArgs) -> Result { + let gtk_widget = gtk::Box::new(gtk::Orientation::Horizontal, 0); + resolve!(builder_args, gtk_widget, { + resolve_f64 => { + "spacing" => |v| gtk_widget.set_spacing(v as i32) + } + }); + + Ok(gtk_widget) +} + +//"layout_horizontal" => gtk::Box::new(gtk::Orientation::Horizontal, 0).upcast(), + +fn run_command(cmd: &str, arg: T) { + let cmd = cmd.replace(CMD_STRING_PLACEHODLER, &format!("{}", arg)); + if let Err(e) = Command::new("bash").arg("-c").arg(cmd).output() { + eprintln!("{}", e); + } +} diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs deleted file mode 100644 index e69de29..0000000