diff --git a/src/config/element.rs b/src/config/element.rs index d709096..8a45280 100644 --- a/src/config/element.rs +++ b/src/config/element.rs @@ -2,13 +2,14 @@ use super::*; use crate::value::AttrValue; use hocon_ext::HoconExt; +use maplit::hashmap; use std::collections::HashMap; use std::convert::TryFrom; #[derive(Debug, Clone, PartialEq)] pub struct WidgetDefinition { pub name: String, - pub structure: ElementUse, + pub structure: WidgetUse, pub size: Option<(i32, i32)>, } @@ -19,7 +20,7 @@ impl WidgetDefinition { .get("structure") .cloned() .context("structure must be set in widget definition") - .and_then(ElementUse::parse_hocon)?; + .and_then(WidgetUse::parse_hocon)?; Ok(WidgetDefinition { name, @@ -34,31 +35,15 @@ impl WidgetDefinition { } } -#[derive(Debug, Clone, PartialEq)] -pub enum ElementUse { - Widget(WidgetUse), - Text(AttrValue), -} - -impl ElementUse { - pub fn parse_hocon(hocon: Hocon) -> Result { - match hocon { - Hocon::String(s) => Ok(ElementUse::Text(AttrValue::from_string(s))), - Hocon::Hash(hash) => WidgetUse::parse_hocon_hash(hash).map(ElementUse::Widget), - _ => Err(anyhow!("'{:?}' is not a valid element", hocon)), - } - } -} - #[derive(Debug, Clone, PartialEq, Default)] pub struct WidgetUse { pub name: String, - pub children: Vec, + pub children: Vec, pub attrs: HashMap, } impl WidgetUse { - pub fn new(name: String, children: Vec) -> Self { + pub fn new(name: String, children: Vec) -> Self { WidgetUse { name, children, @@ -66,28 +51,40 @@ impl WidgetUse { } } - pub fn parse_hocon_hash(data: HashMap) -> Result { - let (widget_name, widget_config) = data.into_iter().next().unwrap(); - let widget_config = widget_config.as_hash().unwrap(); + pub fn parse_hocon(data: Hocon) -> Result { + match data { + Hocon::Hash(data) => { + let (widget_name, widget_config) = data.into_iter().next().context("tried to parse empty hash as widget use")?; + match widget_config { + Hocon::Hash(widget_config) => WidgetUse::from_hash_definition(widget_name.clone(), widget_config), + direct_childen => Ok(WidgetUse::new( + widget_name.clone(), + parse_widget_use_children(direct_childen)?, + )), + } + } + primitive => Ok(WidgetUse::simple_text(AttrValue::try_from(&primitive)?)), + } + } - // TODO allow for `layout_horizontal: [ elements ]` shorthand + /// generate a WidgetUse from an array-style definition + /// i.e.: { layout: [ "hi", "ho" ] } + pub fn from_array_definition(widget_name: String, children: Vec) -> Result { + let children = children.into_iter().map(WidgetUse::parse_hocon).collect::>()?; + Ok(WidgetUse::new(widget_name, children)) + } - let children = match &widget_config.get("children") { - Some(Hocon::String(text)) => Ok(vec![ElementUse::Text(AttrValue::from_string(text.to_string()))]), - Some(Hocon::Array(children)) => children - .clone() - .into_iter() - .map(ElementUse::parse_hocon) - .collect::>>(), - None => Ok(Vec::new()), - _ => Err(anyhow!( - "children must be either a list of elements or a string, but was '{:?}'" - )), - }?; + /// generate a WidgetUse from a hash-style definition + /// i.e.: { layout: { orientation: "v", children: ["hi", "Ho"] } } + pub fn from_hash_definition(widget_name: String, mut widget_config: HashMap) -> Result { + let children = widget_config + .remove("children") + .map(parse_widget_use_children) + .unwrap_or(Ok(Vec::new()))?; let attrs = widget_config .into_iter() - .filter_map(|(key, value)| Some((key.to_lowercase(), AttrValue::try_from(value).ok()?))) + .filter_map(|(key, value)| Some((key.to_lowercase(), AttrValue::try_from(&value).ok()?))) .collect(); Ok(WidgetUse { @@ -97,6 +94,14 @@ impl WidgetUse { }) } + pub fn simple_text(text: AttrValue) -> Self { + WidgetUse { + name: "label".to_owned(), + children: vec![], + attrs: hashmap! { "text".to_string() => text }, // TODO this hardcoded "text" is dumdum + } + } + pub fn get_attr(&self, key: &str) -> Result<&AttrValue> { self.attrs .get(key) @@ -104,9 +109,17 @@ impl WidgetUse { } } -impl From for ElementUse { - fn from(other: WidgetUse) -> ElementUse { - ElementUse::Widget(other) +pub fn parse_widget_use_children(children: Hocon) -> Result> { + match children { + Hocon::Hash(_) => bail!( + "children of a widget must either be a list of widgets or a primitive value, but got hash: {:?}", + children + ), + Hocon::Array(widget_children) => widget_children + .into_iter() + .map(WidgetUse::parse_hocon) + .collect::>>(), + primitive => Ok(vec![WidgetUse::simple_text(AttrValue::try_from(&primitive)?)]), } } @@ -116,14 +129,6 @@ mod test { use maplit::hashmap; use pretty_assertions::assert_eq; - #[test] - fn test_parse_text() { - assert_eq!( - ElementUse::parse_hocon(Hocon::String("hi".to_string())).unwrap(), - ElementUse::Text(AttrValue::Concrete(PrimitiveValue::String("hi".to_string()))) - ); - } - #[test] fn test_parse_widget_use() { let input_complex = r#"{ @@ -131,20 +136,25 @@ mod test { value: "test" children: [ { child: {} } - { child: {} } + { child: { children: ["hi"] } } ] } }"#; let expected = WidgetUse { name: "widget_name".to_string(), children: vec![ - ElementUse::Widget(WidgetUse::new("child".to_string(), vec![])), - ElementUse::Widget(WidgetUse::new("child".to_string(), vec![])), + WidgetUse::new("child".to_string(), vec![]), + WidgetUse::new( + "child".to_string(), + vec![WidgetUse::simple_text(AttrValue::Concrete(PrimitiveValue::String( + "hi".to_string(), + )))], + ), ], attrs: hashmap! { "value".to_string() => AttrValue::Concrete(PrimitiveValue::String("test".to_string()))}, }; assert_eq!( - WidgetUse::parse_hocon_hash(parse_hocon(input_complex).unwrap().as_hash().unwrap().clone()).unwrap(), + WidgetUse::parse_hocon(parse_hocon(input_complex).unwrap().clone()).unwrap(), expected ); } @@ -156,7 +166,7 @@ mod test { }"#; let expected = WidgetDefinition { name: "widget_name".to_string(), - structure: ElementUse::Widget(WidgetUse::new("foo".to_string(), vec![])), + structure: WidgetUse::new("foo".to_string(), vec![]), size: None, }; assert_eq!( diff --git a/src/config/mod.rs b/src/config/mod.rs index 64d9392..ac21320 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -78,7 +78,7 @@ impl EwwConfig { pub struct EwwWindowDefinition { pub position: (i32, i32), pub size: (i32, i32), - pub widget: ElementUse, + pub widget: WidgetUse, } impl EwwWindowDefinition { @@ -97,7 +97,7 @@ impl EwwWindowDefinition { ) }; - let element = ElementUse::parse_hocon(data.get("widget").context("no widget use given")?.clone())?; + let element = WidgetUse::parse_hocon(data.get("widget").context("no widget use given")?.clone())?; Ok(EwwWindowDefinition { position: position.context("pos.x and pos.y need to be set")?, diff --git a/src/main.rs b/src/main.rs index 5c58fcb..119354e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -252,7 +252,7 @@ impl App { window.show_all(); - let gdk_window = window.get_window().unwrap(); + let gdk_window = window.get_window().context("couldn't get gdk window from gtk window")?; gdk_window.set_override_redirect(true); gdk_window.move_(window_def.position.0, window_def.position.1); gdk_window.show(); @@ -266,7 +266,7 @@ impl App { fn reload_all_windows(&mut self, config: config::EwwConfig) -> Result<()> { self.eww_config = config; - + // TODO this needs to handle removing the callbacks to the old gtk windows, as otherwise this might by horribly fucked. let windows = self.windows.clone(); for (window_name, window) in windows { window.close(); @@ -277,10 +277,6 @@ impl App { fn load_css(&mut self, css: &str) -> Result<()> { self.css_provider.load_from_data(css.as_bytes())?; - //self.css_provider.load_from_data(eww_css.as_bytes())?; - //gdk::Screen::get_default().map(|screen| { - //gtk::StyleContext::add_provider_for_screen(&screen, &self.css_provider, gtk::STYLE_PROVIDER_PRIORITY_APPLICATION); - //}); Ok(()) } diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index 6f2d4ca..3b590a3 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -27,28 +27,23 @@ pub fn element_to_gtk_thing( widget_definitions: &HashMap, eww_state: &mut EwwState, local_env: &HashMap, - element: &element::ElementUse, + widget: &element::WidgetUse, ) -> Result { - match element { - element::ElementUse::Text(text) => Ok(gtk::Label::new(Some(&text.as_string()?)).upcast()), // TODO this should use resolve - element::ElementUse::Widget(widget) => { - let gtk_container = build_gtk_widget(widget_definitions, eww_state, local_env, widget)?; + let gtk_container = build_gtk_widget(widget_definitions, eww_state, local_env, widget)?; - 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()); - 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)); - }; + 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()); + 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 { + bail!("unknown widget: '{}'", &widget.name); + }; - Ok(gtk_widget) - } - } + Ok(gtk_widget) } pub fn build_gtk_widget(