diff --git a/src/config/element.rs b/src/config/element.rs index 8fe04ae..80c032e 100644 --- a/src/config/element.rs +++ b/src/config/element.rs @@ -45,7 +45,7 @@ impl ElementUse { match hocon { Hocon::String(s) => Ok(ElementUse::Text(s)), Hocon::Hash(hash) => WidgetUse::parse_hocon_hash(hash).map(ElementUse::Widget), - _ => Err(anyhow!("{:?} is not a valid element", hocon)), + _ => Err(anyhow!("'{:?}' is not a valid element", hocon)), } } } @@ -81,7 +81,7 @@ impl WidgetUse { .collect::>>(), None => Ok(Vec::new()), _ => Err(anyhow!( - "children must be either a list of elements or a string, but was {:?}" + "children must be either a list of elements or a string, but was '{:?}'" )), }?; @@ -99,7 +99,7 @@ impl WidgetUse { pub fn get_attr(&self, key: &str) -> Result<&AttrValue> { self.attrs.get(key).context(format!( - "attribute {} missing from widgetuse of {}", + "attribute '{}' missing from widgetuse of '{}'", key, &self.name )) } diff --git a/src/eww_state.rs b/src/eww_state.rs index b7575ee..5233f3c 100644 --- a/src/eww_state.rs +++ b/src/eww_state.rs @@ -30,7 +30,6 @@ impl EwwState { value: &AttrValue, set_value: F, ) -> bool { - dbg!("resolve: ", value); match value { AttrValue::VarRef(name) => { if let Some(value) = self.state.get(name).cloned() { diff --git a/src/main.rs b/src/main.rs index 5f66708..30689ef 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +#![feature(trace_macros)] #![feature(try_blocks)] extern crate gio; extern crate gtk; @@ -14,9 +15,8 @@ pub mod eww_state; pub mod value; pub mod widgets; -use config::element; use eww_state::*; -use value::{AttrValue, PrimitiveValue}; +use value::PrimitiveValue; #[macro_export] macro_rules! build { @@ -82,7 +82,6 @@ fn main() { fn try_main() -> Result<()> { let eww_config = config::EwwConfig::from_hocon(&config::parse_hocon(EXAMPLE_CONFIG)?)?; - dbg!(&eww_config); let application = Application::new(Some("de.elkowar.eww"), gio::ApplicationFlags::FLAGS_NONE) .expect("failed to initialize GTK application "); @@ -90,60 +89,63 @@ fn try_main() -> Result<()> { let window_def = eww_config.get_windows()["main_window"].clone(); application.connect_activate(move |app| { - let app_window = ApplicationWindow::new(app); - app_window.set_title("Eww"); - app_window.set_wmclass("noswallow", "noswallow"); - app_window.set_type_hint(gdk::WindowTypeHint::Dock); - app_window.set_position(gtk::WindowPosition::Center); - app_window.set_keep_above(true); - app_window.set_default_size(window_def.size.0, window_def.size.1); - app_window.set_visual( - app_window - .get_display() - .get_default_screen() - .get_rgba_visual() - .or_else(|| { - app_window - .get_display() - .get_default_screen() - .get_system_visual() - }) - .as_ref(), - ); + let result: Result<()> = try { + let app_window = ApplicationWindow::new(app); + app_window.set_title("Eww"); + app_window.set_wmclass("noswallow", "noswallow"); + app_window.set_type_hint(gdk::WindowTypeHint::Dock); + app_window.set_position(gtk::WindowPosition::Center); + app_window.set_keep_above(true); + app_window.set_default_size(window_def.size.0, window_def.size.1); + app_window.set_visual( + app_window + .get_display() + .get_default_screen() + .get_rgba_visual() + .or_else(|| { + app_window + .get_display() + .get_default_screen() + .get_system_visual() + }) + .as_ref(), + ); - app_window.fullscreen(); + app_window.fullscreen(); - let mut eww_state = EwwState::from_default_vars(eww_config.get_default_vars().clone()); - let empty_local_state = HashMap::new(); + let mut eww_state = EwwState::from_default_vars(eww_config.get_default_vars().clone()); + let empty_local_state = HashMap::new(); - app_window.add( - &widgets::element_to_gtk_thing( + app_window.add(&widgets::element_to_gtk_thing( &eww_config.get_widgets(), &mut eww_state, &empty_local_state, &window_def.widget, - ) - .unwrap(), - ); + )?); - app_window.show_all(); + app_window.show_all(); - let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT); - std::thread::spawn(move || event_loop(tx)); + let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT); + std::thread::spawn(move || event_loop(tx)); - rx.attach(None, move |msg| { - match msg { - MuhhMsg::UpdateValue(key, value) => eww_state.update_value(key, value), - } + rx.attach(None, move |msg| { + match msg { + MuhhMsg::UpdateValue(key, value) => eww_state.update_value(key, value), + } - glib::Continue(true) - }); + glib::Continue(true) + }); - let window = app_window.get_window().unwrap(); - window.set_override_redirect(true); - window.move_(window_def.position.0, window_def.position.1); - window.show(); - window.raise(); + let window = app_window.get_window().unwrap(); + window.set_override_redirect(true); + window.move_(window_def.position.0, window_def.position.1); + window.show(); + window.raise(); + }; + if let Err(err) = result { + eprintln!("{:?}", err); + std::process::exit(1); + } }); application.run(&[]); diff --git a/src/value.rs b/src/value.rs index b0420a2..a52c33e 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,5 +1,4 @@ use anyhow::*; -use derive_more::From; use derive_more::*; use hocon::Hocon; use try_match::try_match; @@ -13,6 +12,12 @@ pub enum PrimitiveValue { Boolean(bool), } +impl From<&str> for PrimitiveValue { + fn from(s: &str) -> Self { + PrimitiveValue::String(s.to_string()) + } +} + impl PrimitiveValue { pub fn as_string(&self) -> Result<&String> { try_match!(PrimitiveValue::String(x) = self).map_err(|x| anyhow!("{:?} is not a string", x)) diff --git a/src/widgets.rs b/src/widgets.rs index 814386f..8501d92 100644 --- a/src/widgets.rs +++ b/src/widgets.rs @@ -1,7 +1,6 @@ -use crate::build; use crate::config::element; use crate::eww_state::*; -use crate::value::AttrValue; +use crate::value::{AttrValue, PrimitiveValue}; use anyhow::*; use gtk::prelude::*; use std::{collections::HashMap, process::Command}; @@ -10,21 +9,41 @@ const CMD_STRING_PLACEHODLER: &str = "{}"; macro_rules! resolve { ($args:ident, $gtk_widget:ident, { - $($func:ident => - { - $($attr:literal => |$arg:ident| $body:expr),+ - } - ),+ + $($func:ident => { + $($attr:literal $([$default:literal])? $(req $(@$required:tt)?)? => |$arg:ident| $body:expr),+ $(,)? + }),+ $(,)? }) => { - $( - $( - $args.eww_state.$func($args.local_env, $args.widget.get_attr($attr)?, { + $($( + resolve!($args, $gtk_widget, $func => $attr $( [ $default ] )* $($($required)*)* => |$arg| $body); + )+)+ + }; + + // optional + ($args:ident, $gtk_widget:ident, $func:ident => $attr:literal => |$arg:ident| $body:expr) => { + if let Some(attr_value) = $args.widget.attrs.get($attr) { + $args.eww_state.$func($args.local_env, attr_value, { + let $gtk_widget = $gtk_widget.clone(); + move |$arg| { $body; } + }); + } + }; + + // required + ($args:ident, $gtk_widget:ident, $func:ident => $attr:literal req => |$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; } - }); - )+ - )+ - } + }); + }; + + // with default + ($args:ident, $gtk_widget:ident, $func:ident => $attr:literal [$default:expr] => |$arg:ident| $body:expr) => { + $args.eww_state.$func($args.local_env, $args.widget.attrs.get($attr).unwrap_or(&AttrValue::Concrete(PrimitiveValue::from($default))), { + let $gtk_widget = $gtk_widget.clone(); + move |$arg| { $body; } + }); + }; + } fn run_command(cmd: &str, arg: T) { @@ -44,23 +63,17 @@ pub fn element_to_gtk_thing( element::ElementUse::Text(text) => Ok(gtk::Label::new(Some(&text)).upcast()), element::ElementUse::Widget(widget) => { let gtk_container = - build_gtk_widget_or_container(widget_definitions, eww_state, local_env, widget); - let gtk_widget = gtk_container.or_else(|_| { - if let Some(def) = widget_definitions.get(widget.name.as_str()) { - let local_environment = build!(env = local_env.clone(); { - env.extend(widget.attrs.clone()); - }); + build_gtk_widget_or_container(widget_definitions, eww_state, local_env, widget)?; - element_to_gtk_thing( - widget_definitions, - eww_state, - &local_environment, - &def.structure, - ) - } else { - Err(anyhow!("unknown widget {}", &widget.name)) - } - })?; + let gtk_widget = if let Some(gtk_container) = gtk_container { + gtk_container + } 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)? + } else { + return Err(anyhow!("unknown widget: '{}'", &widget.name)); + }; if let Ok(css_class) = widget .get_attr("class") @@ -85,24 +98,29 @@ pub fn build_gtk_widget_or_container( eww_state: &mut EwwState, local_env: &HashMap, widget: &element::WidgetUse, -) -> Result { +) -> Result> { let mut builder_args = BuilderArgs { eww_state, local_env: &local_env, widget: &widget, }; - let gtk_widget: Option = - if let Some(gtk_widget) = build_gtk_container(&mut builder_args)? { - for child in &widget.children { - let child_widget = - &element_to_gtk_thing(widget_definitions, eww_state, local_env, child)?; - gtk_widget.add(child_widget); - } - Some(gtk_widget.upcast()) - } else { - build_gtk_widget(&mut builder_args)? - }; - gtk_widget.context(format!("unknown widget {:?}", widget)) + if let Some(gtk_widget) = build_gtk_container(&mut builder_args)? { + for child in &widget.children { + let child_widget = + &element_to_gtk_thing(widget_definitions, eww_state, local_env, child) + .with_context(|| { + format!( + "error while building child '{:?}' of '{:?}'", + &child, + >k_widget.get_widget_name() + ) + })?; + gtk_widget.add(child_widget); + } + Ok(Some(gtk_widget.upcast())) + } else { + build_gtk_widget(&mut builder_args).context("error building gtk widget") + } } // widget definitions @@ -133,18 +151,18 @@ fn build_gtk_scale(builder_args: &mut BuilderArgs) -> Result { ); 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()); - }); + resolve_f64 => { + "value" req => |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) } @@ -152,12 +170,12 @@ fn build_gtk_scale(builder_args: &mut BuilderArgs) -> Result { fn build_gtk_button(builder_args: &mut 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, "")) - } + resolve_bool => { + "active" [true] => |v| gtk_widget.set_sensitive(v) + }, + resolve_string => { + "onclick" => |cmd| gtk_widget.connect_clicked(move |_| run_command(&cmd, "")) + } }); Ok(gtk_widget) } @@ -165,10 +183,9 @@ fn build_gtk_button(builder_args: &mut BuilderArgs) -> Result { fn build_gtk_layout(builder_args: &mut 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) - } + resolve_f64 => { + "spacing" [10.0] => |v| gtk_widget.set_spacing(v as i32) + } }); - Ok(gtk_widget) }