cleanup error handling and improve widget configuration options
This commit is contained in:
parent
aefa9016f1
commit
fd6a49c5e7
4 changed files with 81 additions and 80 deletions
|
@ -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!(
|
||||
|
|
|
@ -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")?,
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Add table
Reference in a new issue