cleanup error handling and improve widget configuration options

This commit is contained in:
elkowar 2020-09-27 13:49:20 +02:00
parent aefa9016f1
commit fd6a49c5e7
4 changed files with 81 additions and 80 deletions

View file

@ -2,13 +2,14 @@ use super::*;
use crate::value::AttrValue; use crate::value::AttrValue;
use hocon_ext::HoconExt; use hocon_ext::HoconExt;
use maplit::hashmap;
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::TryFrom; use std::convert::TryFrom;
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct WidgetDefinition { pub struct WidgetDefinition {
pub name: String, pub name: String,
pub structure: ElementUse, pub structure: WidgetUse,
pub size: Option<(i32, i32)>, pub size: Option<(i32, i32)>,
} }
@ -19,7 +20,7 @@ impl WidgetDefinition {
.get("structure") .get("structure")
.cloned() .cloned()
.context("structure must be set in widget definition") .context("structure must be set in widget definition")
.and_then(ElementUse::parse_hocon)?; .and_then(WidgetUse::parse_hocon)?;
Ok(WidgetDefinition { Ok(WidgetDefinition {
name, 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<Self> {
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)] #[derive(Debug, Clone, PartialEq, Default)]
pub struct WidgetUse { pub struct WidgetUse {
pub name: String, pub name: String,
pub children: Vec<ElementUse>, pub children: Vec<WidgetUse>,
pub attrs: HashMap<String, AttrValue>, pub attrs: HashMap<String, AttrValue>,
} }
impl WidgetUse { impl WidgetUse {
pub fn new(name: String, children: Vec<ElementUse>) -> Self { pub fn new(name: String, children: Vec<WidgetUse>) -> Self {
WidgetUse { WidgetUse {
name, name,
children, children,
@ -66,28 +51,40 @@ impl WidgetUse {
} }
} }
pub fn parse_hocon_hash(data: HashMap<String, Hocon>) -> Result<WidgetUse> { pub fn parse_hocon(data: Hocon) -> Result<Self> {
let (widget_name, widget_config) = data.into_iter().next().unwrap(); match data {
let widget_config = widget_config.as_hash().unwrap(); Hocon::Hash(data) => {
let (widget_name, widget_config) = data.into_iter().next().context("tried to parse empty hash as widget use")?;
// TODO allow for `layout_horizontal: [ elements ]` shorthand match widget_config {
Hocon::Hash(widget_config) => WidgetUse::from_hash_definition(widget_name.clone(), widget_config),
let children = match &widget_config.get("children") { direct_childen => Ok(WidgetUse::new(
Some(Hocon::String(text)) => Ok(vec![ElementUse::Text(AttrValue::from_string(text.to_string()))]), widget_name.clone(),
Some(Hocon::Array(children)) => children parse_widget_use_children(direct_childen)?,
.clone()
.into_iter()
.map(ElementUse::parse_hocon)
.collect::<Result<Vec<_>>>(),
None => Ok(Vec::new()),
_ => Err(anyhow!(
"children must be either a list of elements or a string, but was '{:?}'"
)), )),
}?; }
}
primitive => Ok(WidgetUse::simple_text(AttrValue::try_from(&primitive)?)),
}
}
/// generate a WidgetUse from an array-style definition
/// i.e.: { layout: [ "hi", "ho" ] }
pub fn from_array_definition(widget_name: String, children: Vec<Hocon>) -> Result<Self> {
let children = children.into_iter().map(WidgetUse::parse_hocon).collect::<Result<_>>()?;
Ok(WidgetUse::new(widget_name, children))
}
/// 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<String, Hocon>) -> Result<Self> {
let children = widget_config
.remove("children")
.map(parse_widget_use_children)
.unwrap_or(Ok(Vec::new()))?;
let attrs = widget_config let attrs = widget_config
.into_iter() .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(); .collect();
Ok(WidgetUse { 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> { pub fn get_attr(&self, key: &str) -> Result<&AttrValue> {
self.attrs self.attrs
.get(key) .get(key)
@ -104,9 +109,17 @@ impl WidgetUse {
} }
} }
impl From<WidgetUse> for ElementUse { pub fn parse_widget_use_children(children: Hocon) -> Result<Vec<WidgetUse>> {
fn from(other: WidgetUse) -> ElementUse { match children {
ElementUse::Widget(other) 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::<Result<Vec<_>>>(),
primitive => Ok(vec![WidgetUse::simple_text(AttrValue::try_from(&primitive)?)]),
} }
} }
@ -116,14 +129,6 @@ mod test {
use maplit::hashmap; use maplit::hashmap;
use pretty_assertions::assert_eq; 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] #[test]
fn test_parse_widget_use() { fn test_parse_widget_use() {
let input_complex = r#"{ let input_complex = r#"{
@ -131,20 +136,25 @@ mod test {
value: "test" value: "test"
children: [ children: [
{ child: {} } { child: {} }
{ child: {} } { child: { children: ["hi"] } }
] ]
} }
}"#; }"#;
let expected = WidgetUse { let expected = WidgetUse {
name: "widget_name".to_string(), name: "widget_name".to_string(),
children: vec![ children: vec![
ElementUse::Widget(WidgetUse::new("child".to_string(), vec![])), WidgetUse::new("child".to_string(), vec![]),
ElementUse::Widget(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()))}, attrs: hashmap! { "value".to_string() => AttrValue::Concrete(PrimitiveValue::String("test".to_string()))},
}; };
assert_eq!( 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 expected
); );
} }
@ -156,7 +166,7 @@ mod test {
}"#; }"#;
let expected = WidgetDefinition { let expected = WidgetDefinition {
name: "widget_name".to_string(), name: "widget_name".to_string(),
structure: ElementUse::Widget(WidgetUse::new("foo".to_string(), vec![])), structure: WidgetUse::new("foo".to_string(), vec![]),
size: None, size: None,
}; };
assert_eq!( assert_eq!(

View file

@ -78,7 +78,7 @@ impl EwwConfig {
pub struct EwwWindowDefinition { pub struct EwwWindowDefinition {
pub position: (i32, i32), pub position: (i32, i32),
pub size: (i32, i32), pub size: (i32, i32),
pub widget: ElementUse, pub widget: WidgetUse,
} }
impl EwwWindowDefinition { 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 { Ok(EwwWindowDefinition {
position: position.context("pos.x and pos.y need to be set")?, position: position.context("pos.x and pos.y need to be set")?,

View file

@ -252,7 +252,7 @@ impl App {
window.show_all(); 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.set_override_redirect(true);
gdk_window.move_(window_def.position.0, window_def.position.1); gdk_window.move_(window_def.position.0, window_def.position.1);
gdk_window.show(); gdk_window.show();
@ -266,7 +266,7 @@ impl App {
fn reload_all_windows(&mut self, config: config::EwwConfig) -> Result<()> { fn reload_all_windows(&mut self, config: config::EwwConfig) -> Result<()> {
self.eww_config = config; 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(); let windows = self.windows.clone();
for (window_name, window) in windows { for (window_name, window) in windows {
window.close(); window.close();
@ -277,10 +277,6 @@ impl App {
fn load_css(&mut self, css: &str) -> Result<()> { fn load_css(&mut self, css: &str) -> Result<()> {
self.css_provider.load_from_data(css.as_bytes())?; 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(()) Ok(())
} }

View file

@ -27,11 +27,8 @@ pub fn element_to_gtk_thing(
widget_definitions: &HashMap<String, element::WidgetDefinition>, widget_definitions: &HashMap<String, element::WidgetDefinition>,
eww_state: &mut EwwState, eww_state: &mut EwwState,
local_env: &HashMap<String, AttrValue>, local_env: &HashMap<String, AttrValue>,
element: &element::ElementUse, widget: &element::WidgetUse,
) -> Result<gtk::Widget> { ) -> Result<gtk::Widget> {
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 { let gtk_widget = if let Some(gtk_container) = gtk_container {
@ -43,13 +40,11 @@ pub fn element_to_gtk_thing(
custom_widget.get_style_context().add_class(widget.name.as_str()); custom_widget.get_style_context().add_class(widget.name.as_str());
custom_widget custom_widget
} else { } else {
return Err(anyhow!("unknown widget: '{}'", &widget.name)); bail!("unknown widget: '{}'", &widget.name);
}; };
Ok(gtk_widget) Ok(gtk_widget)
} }
}
}
pub fn build_gtk_widget( pub fn build_gtk_widget(
widget_definitions: &HashMap<String, element::WidgetDefinition>, widget_definitions: &HashMap<String, element::WidgetDefinition>,