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 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<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)]
pub struct WidgetUse {
pub name: String,
pub children: Vec<ElementUse>,
pub children: Vec<WidgetUse>,
pub attrs: HashMap<String, AttrValue>,
}
impl WidgetUse {
pub fn new(name: String, children: Vec<ElementUse>) -> Self {
pub fn new(name: String, children: Vec<WidgetUse>) -> Self {
WidgetUse {
name,
children,
@ -66,28 +51,40 @@ impl WidgetUse {
}
}
pub fn parse_hocon_hash(data: HashMap<String, Hocon>) -> Result<WidgetUse> {
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<Self> {
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<Hocon>) -> Result<Self> {
let children = children.into_iter().map(WidgetUse::parse_hocon).collect::<Result<_>>()?;
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::<Result<Vec<_>>>(),
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<String, Hocon>) -> Result<Self> {
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<WidgetUse> for ElementUse {
fn from(other: WidgetUse) -> ElementUse {
ElementUse::Widget(other)
pub fn parse_widget_use_children(children: Hocon) -> Result<Vec<WidgetUse>> {
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::<Result<Vec<_>>>(),
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!(

View file

@ -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")?,

View file

@ -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(())
}

View file

@ -27,28 +27,23 @@ pub fn element_to_gtk_thing(
widget_definitions: &HashMap<String, element::WidgetDefinition>,
eww_state: &mut EwwState,
local_env: &HashMap<String, AttrValue>,
element: &element::ElementUse,
widget: &element::WidgetUse,
) -> 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 {
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(