Cleanup resolve! macro

This commit is contained in:
elkowar 2020-09-21 22:50:56 +02:00
parent 60c6f1b28f
commit 8ed820787a
5 changed files with 137 additions and 114 deletions

View file

@ -45,7 +45,7 @@ impl ElementUse {
match hocon { match hocon {
Hocon::String(s) => Ok(ElementUse::Text(s)), Hocon::String(s) => Ok(ElementUse::Text(s)),
Hocon::Hash(hash) => WidgetUse::parse_hocon_hash(hash).map(ElementUse::Widget), Hocon::Hash(hash) => WidgetUse::parse_hocon_hash(hash).map(ElementUse::Widget),
_ => Err(anyhow!("{:?} is not a valid element", hocon)), _ => Err(anyhow!("'{:?}' is not a valid element", hocon)),
} }
} }
} }
@ -81,7 +81,7 @@ impl WidgetUse {
.collect::<Result<Vec<_>>>(), .collect::<Result<Vec<_>>>(),
None => Ok(Vec::new()), None => Ok(Vec::new()),
_ => Err(anyhow!( _ => Err(anyhow!(
"children must be either a list of elements or a string, but was {:?}" "children must be either a list of elements or a string, but was '{:?}'"
)), )),
}?; }?;
@ -99,7 +99,7 @@ impl WidgetUse {
pub fn get_attr(&self, key: &str) -> Result<&AttrValue> { pub fn get_attr(&self, key: &str) -> Result<&AttrValue> {
self.attrs.get(key).context(format!( self.attrs.get(key).context(format!(
"attribute {} missing from widgetuse of {}", "attribute '{}' missing from widgetuse of '{}'",
key, &self.name key, &self.name
)) ))
} }

View file

@ -30,7 +30,6 @@ impl EwwState {
value: &AttrValue, value: &AttrValue,
set_value: F, set_value: F,
) -> bool { ) -> bool {
dbg!("resolve: ", value);
match value { match value {
AttrValue::VarRef(name) => { AttrValue::VarRef(name) => {
if let Some(value) = self.state.get(name).cloned() { if let Some(value) = self.state.get(name).cloned() {

View file

@ -1,3 +1,4 @@
#![feature(trace_macros)]
#![feature(try_blocks)] #![feature(try_blocks)]
extern crate gio; extern crate gio;
extern crate gtk; extern crate gtk;
@ -14,9 +15,8 @@ pub mod eww_state;
pub mod value; pub mod value;
pub mod widgets; pub mod widgets;
use config::element;
use eww_state::*; use eww_state::*;
use value::{AttrValue, PrimitiveValue}; use value::PrimitiveValue;
#[macro_export] #[macro_export]
macro_rules! build { macro_rules! build {
@ -82,7 +82,6 @@ fn main() {
fn try_main() -> Result<()> { fn try_main() -> Result<()> {
let eww_config = config::EwwConfig::from_hocon(&config::parse_hocon(EXAMPLE_CONFIG)?)?; let eww_config = config::EwwConfig::from_hocon(&config::parse_hocon(EXAMPLE_CONFIG)?)?;
dbg!(&eww_config);
let application = Application::new(Some("de.elkowar.eww"), gio::ApplicationFlags::FLAGS_NONE) let application = Application::new(Some("de.elkowar.eww"), gio::ApplicationFlags::FLAGS_NONE)
.expect("failed to initialize GTK application "); .expect("failed to initialize GTK application ");
@ -90,60 +89,63 @@ fn try_main() -> Result<()> {
let window_def = eww_config.get_windows()["main_window"].clone(); let window_def = eww_config.get_windows()["main_window"].clone();
application.connect_activate(move |app| { application.connect_activate(move |app| {
let app_window = ApplicationWindow::new(app); let result: Result<()> = try {
app_window.set_title("Eww"); let app_window = ApplicationWindow::new(app);
app_window.set_wmclass("noswallow", "noswallow"); app_window.set_title("Eww");
app_window.set_type_hint(gdk::WindowTypeHint::Dock); app_window.set_wmclass("noswallow", "noswallow");
app_window.set_position(gtk::WindowPosition::Center); app_window.set_type_hint(gdk::WindowTypeHint::Dock);
app_window.set_keep_above(true); app_window.set_position(gtk::WindowPosition::Center);
app_window.set_default_size(window_def.size.0, window_def.size.1); app_window.set_keep_above(true);
app_window.set_visual( app_window.set_default_size(window_def.size.0, window_def.size.1);
app_window app_window.set_visual(
.get_display() app_window
.get_default_screen() .get_display()
.get_rgba_visual() .get_default_screen()
.or_else(|| { .get_rgba_visual()
app_window .or_else(|| {
.get_display() app_window
.get_default_screen() .get_display()
.get_system_visual() .get_default_screen()
}) .get_system_visual()
.as_ref(), })
); .as_ref(),
);
app_window.fullscreen(); app_window.fullscreen();
let mut eww_state = EwwState::from_default_vars(eww_config.get_default_vars().clone()); let mut eww_state = EwwState::from_default_vars(eww_config.get_default_vars().clone());
let empty_local_state = HashMap::new(); let empty_local_state = HashMap::new();
app_window.add( app_window.add(&widgets::element_to_gtk_thing(
&widgets::element_to_gtk_thing(
&eww_config.get_widgets(), &eww_config.get_widgets(),
&mut eww_state, &mut eww_state,
&empty_local_state, &empty_local_state,
&window_def.widget, &window_def.widget,
) )?);
.unwrap(),
);
app_window.show_all(); app_window.show_all();
let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT); let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
std::thread::spawn(move || event_loop(tx)); std::thread::spawn(move || event_loop(tx));
rx.attach(None, move |msg| { rx.attach(None, move |msg| {
match msg { match msg {
MuhhMsg::UpdateValue(key, value) => eww_state.update_value(key, value), MuhhMsg::UpdateValue(key, value) => eww_state.update_value(key, value),
} }
glib::Continue(true) glib::Continue(true)
}); });
let window = app_window.get_window().unwrap(); let window = app_window.get_window().unwrap();
window.set_override_redirect(true); window.set_override_redirect(true);
window.move_(window_def.position.0, window_def.position.1); window.move_(window_def.position.0, window_def.position.1);
window.show(); window.show();
window.raise(); window.raise();
};
if let Err(err) = result {
eprintln!("{:?}", err);
std::process::exit(1);
}
}); });
application.run(&[]); application.run(&[]);

View file

@ -1,5 +1,4 @@
use anyhow::*; use anyhow::*;
use derive_more::From;
use derive_more::*; use derive_more::*;
use hocon::Hocon; use hocon::Hocon;
use try_match::try_match; use try_match::try_match;
@ -13,6 +12,12 @@ pub enum PrimitiveValue {
Boolean(bool), Boolean(bool),
} }
impl From<&str> for PrimitiveValue {
fn from(s: &str) -> Self {
PrimitiveValue::String(s.to_string())
}
}
impl PrimitiveValue { impl PrimitiveValue {
pub fn as_string(&self) -> Result<&String> { pub fn as_string(&self) -> Result<&String> {
try_match!(PrimitiveValue::String(x) = self).map_err(|x| anyhow!("{:?} is not a string", x)) try_match!(PrimitiveValue::String(x) = self).map_err(|x| anyhow!("{:?} is not a string", x))

View file

@ -1,7 +1,6 @@
use crate::build;
use crate::config::element; use crate::config::element;
use crate::eww_state::*; use crate::eww_state::*;
use crate::value::AttrValue; use crate::value::{AttrValue, PrimitiveValue};
use anyhow::*; use anyhow::*;
use gtk::prelude::*; use gtk::prelude::*;
use std::{collections::HashMap, process::Command}; use std::{collections::HashMap, process::Command};
@ -10,21 +9,41 @@ const CMD_STRING_PLACEHODLER: &str = "{}";
macro_rules! resolve { macro_rules! resolve {
($args:ident, $gtk_widget:ident, { ($args:ident, $gtk_widget:ident, {
$($func:ident => $($func:ident => {
{ $($attr:literal $([$default:literal])? $(req $(@$required:tt)?)? => |$arg:ident| $body:expr),+ $(,)?
$($attr:literal => |$arg:ident| $body:expr),+ }),+ $(,)?
}
),+
}) => { }) => {
$( $($(
$( resolve!($args, $gtk_widget, $func => $attr $( [ $default ] )* $($($required)*)* => |$arg| $body);
$args.eww_state.$func($args.local_env, $args.widget.get_attr($attr)?, { )+)+
};
// optional
($args:ident, $gtk_widget:ident, $func:ident => $attr:literal => |$arg:ident| $body:expr) => {
if let Some(attr_value) = $args.widget.attrs.get($attr) {
$args.eww_state.$func($args.local_env, attr_value, {
let $gtk_widget = $gtk_widget.clone();
move |$arg| { $body; }
});
}
};
// required
($args:ident, $gtk_widget:ident, $func:ident => $attr:literal req => |$arg:ident| $body:expr) => {
$args.eww_state.$func($args.local_env, $args.widget.get_attr($attr)?, {
let $gtk_widget = $gtk_widget.clone(); let $gtk_widget = $gtk_widget.clone();
move |$arg| { $body; } move |$arg| { $body; }
}); });
)+ };
)+
} // with default
($args:ident, $gtk_widget:ident, $func:ident => $attr:literal [$default:expr] => |$arg:ident| $body:expr) => {
$args.eww_state.$func($args.local_env, $args.widget.attrs.get($attr).unwrap_or(&AttrValue::Concrete(PrimitiveValue::from($default))), {
let $gtk_widget = $gtk_widget.clone();
move |$arg| { $body; }
});
};
} }
fn run_command<T: std::fmt::Display>(cmd: &str, arg: T) { fn run_command<T: std::fmt::Display>(cmd: &str, arg: T) {
@ -44,23 +63,17 @@ pub fn element_to_gtk_thing(
element::ElementUse::Text(text) => Ok(gtk::Label::new(Some(&text)).upcast()), element::ElementUse::Text(text) => Ok(gtk::Label::new(Some(&text)).upcast()),
element::ElementUse::Widget(widget) => { element::ElementUse::Widget(widget) => {
let gtk_container = let gtk_container =
build_gtk_widget_or_container(widget_definitions, eww_state, local_env, widget); build_gtk_widget_or_container(widget_definitions, eww_state, local_env, widget)?;
let gtk_widget = gtk_container.or_else(|_| {
if let Some(def) = widget_definitions.get(widget.name.as_str()) {
let local_environment = build!(env = local_env.clone(); {
env.extend(widget.attrs.clone());
});
element_to_gtk_thing( let gtk_widget = if let Some(gtk_container) = gtk_container {
widget_definitions, gtk_container
eww_state, } else if let Some(def) = widget_definitions.get(widget.name.as_str()) {
&local_environment, let mut local_env = local_env.clone();
&def.structure, local_env.extend(widget.attrs.clone());
) element_to_gtk_thing(widget_definitions, eww_state, &local_env, &def.structure)?
} else { } else {
Err(anyhow!("unknown widget {}", &widget.name)) return Err(anyhow!("unknown widget: '{}'", &widget.name));
} };
})?;
if let Ok(css_class) = widget if let Ok(css_class) = widget
.get_attr("class") .get_attr("class")
@ -85,24 +98,29 @@ pub fn build_gtk_widget_or_container(
eww_state: &mut EwwState, eww_state: &mut EwwState,
local_env: &HashMap<String, AttrValue>, local_env: &HashMap<String, AttrValue>,
widget: &element::WidgetUse, widget: &element::WidgetUse,
) -> Result<gtk::Widget> { ) -> Result<Option<gtk::Widget>> {
let mut builder_args = BuilderArgs { let mut builder_args = BuilderArgs {
eww_state, eww_state,
local_env: &local_env, local_env: &local_env,
widget: &widget, widget: &widget,
}; };
let gtk_widget: Option<gtk::Widget> = if let Some(gtk_widget) = build_gtk_container(&mut builder_args)? {
if let Some(gtk_widget) = build_gtk_container(&mut builder_args)? { for child in &widget.children {
for child in &widget.children { let child_widget =
let child_widget = &element_to_gtk_thing(widget_definitions, eww_state, local_env, child)
&element_to_gtk_thing(widget_definitions, eww_state, local_env, child)?; .with_context(|| {
gtk_widget.add(child_widget); format!(
} "error while building child '{:?}' of '{:?}'",
Some(gtk_widget.upcast()) &child,
} else { &gtk_widget.get_widget_name()
build_gtk_widget(&mut builder_args)? )
}; })?;
gtk_widget.context(format!("unknown widget {:?}", widget)) gtk_widget.add(child_widget);
}
Ok(Some(gtk_widget.upcast()))
} else {
build_gtk_widget(&mut builder_args).context("error building gtk widget")
}
} }
// widget definitions // widget definitions
@ -133,18 +151,18 @@ fn build_gtk_scale(builder_args: &mut BuilderArgs) -> Result<gtk::Scale> {
); );
resolve!(builder_args, gtk_widget, { resolve!(builder_args, gtk_widget, {
resolve_f64 => { resolve_f64 => {
"value" => |v| gtk_widget.set_value(v), "value" req => |v| gtk_widget.set_value(v),
"min" => |v| gtk_widget.get_adjustment().set_lower(v), "min" => |v| gtk_widget.get_adjustment().set_lower(v),
"max" => |v| gtk_widget.get_adjustment().set_upper(v) "max" => |v| gtk_widget.get_adjustment().set_upper(v)
}, },
resolve_string => { resolve_string => {
"onchange" => |cmd| { "onchange" => |cmd| {
gtk_widget.connect_value_changed(move |gtk_widget| { gtk_widget.connect_value_changed(move |gtk_widget| {
run_command(&cmd, gtk_widget.get_value()); run_command(&cmd, gtk_widget.get_value());
}); });
}
} }
}
}); });
Ok(gtk_widget) Ok(gtk_widget)
} }
@ -152,12 +170,12 @@ fn build_gtk_scale(builder_args: &mut BuilderArgs) -> Result<gtk::Scale> {
fn build_gtk_button(builder_args: &mut BuilderArgs) -> Result<gtk::Button> { fn build_gtk_button(builder_args: &mut BuilderArgs) -> Result<gtk::Button> {
let gtk_widget = gtk::Button::new(); let gtk_widget = gtk::Button::new();
resolve!(builder_args, gtk_widget, { resolve!(builder_args, gtk_widget, {
resolve_bool => { resolve_bool => {
"active" => |v| gtk_widget.set_sensitive(v) "active" [true] => |v| gtk_widget.set_sensitive(v)
}, },
resolve_string => { resolve_string => {
"onclick" => |cmd| gtk_widget.connect_clicked(move |_| run_command(&cmd, "")) "onclick" => |cmd| gtk_widget.connect_clicked(move |_| run_command(&cmd, ""))
} }
}); });
Ok(gtk_widget) Ok(gtk_widget)
} }
@ -165,10 +183,9 @@ fn build_gtk_button(builder_args: &mut BuilderArgs) -> Result<gtk::Button> {
fn build_gtk_layout(builder_args: &mut BuilderArgs) -> Result<gtk::Box> { fn build_gtk_layout(builder_args: &mut BuilderArgs) -> Result<gtk::Box> {
let gtk_widget = gtk::Box::new(gtk::Orientation::Horizontal, 0); let gtk_widget = gtk::Box::new(gtk::Orientation::Horizontal, 0);
resolve!(builder_args, gtk_widget, { resolve!(builder_args, gtk_widget, {
resolve_f64 => { resolve_f64 => {
"spacing" => |v| gtk_widget.set_spacing(v as i32) "spacing" [10.0] => |v| gtk_widget.set_spacing(v as i32)
} }
}); });
Ok(gtk_widget) Ok(gtk_widget)
} }