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 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!(
|
||||||
|
|
|
@ -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")?,
|
||||||
|
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>,
|
||||||
|
|
Loading…
Add table
Reference in a new issue