Most stuff works

This commit is contained in:
elkowar 2020-10-07 00:56:53 +02:00
parent 1f86c72ef6
commit 2df0984ac3
5 changed files with 182 additions and 133 deletions

View file

@ -26,7 +26,7 @@ pub struct App {
impl App {
pub fn handle_user_command(&mut self, opts: &Opt) -> Result<()> {
match &opts.action {
OptAction::Update { fieldname, value } => self.update_state(fieldname.clone(), value.clone()),
OptAction::Update { fieldname, value } => self.update_state(fieldname.clone(), value.clone())?,
OptAction::OpenWindow { window_name } => self.open_window(&window_name)?,
OptAction::CloseWindow { window_name } => self.close_window(&window_name)?,
OptAction::KillServer => {
@ -43,7 +43,7 @@ impl App {
let result: Result<_> = try {
match event {
EwwEvent::UserCommand(command) => self.handle_user_command(&command)?,
EwwEvent::UpdateVar(key, value) => self.update_state(key, value),
EwwEvent::UpdateVar(key, value) => self.update_state(key, value)?,
EwwEvent::ReloadConfig(config) => self.reload_all_windows(config)?,
EwwEvent::ReloadCss(css) => self.load_css(&css)?,
}
@ -53,8 +53,8 @@ impl App {
}
}
fn update_state(&mut self, fieldname: VarName, value: PrimitiveValue) {
self.eww_state.update_value(fieldname, value);
fn update_state(&mut self, fieldname: VarName, value: PrimitiveValue) -> Result<()> {
self.eww_state.update_value(fieldname, value)
}
fn close_window(&mut self, window_name: &str) -> Result<()> {

View file

@ -7,24 +7,56 @@ use std::sync::Arc;
use crate::value::{AttrValue, PrimitiveValue};
//pub struct StateChangeHandler(Box<dyn Fn(HashMap<String, PrimitiveValue>) + 'static>);
pub struct StateChangeHandler {
func: Box<dyn Fn(HashMap<String, PrimitiveValue>) -> Result<()> + 'static>,
constant_values: HashMap<String, PrimitiveValue>,
unresolved_attrs: HashMap<String, VarName>,
}
impl StateChangeHandler {
fn run_with_state(&self, state: &HashMap<VarName, PrimitiveValue>) -> Result<()> {
let mut all_resolved_attrs = self.constant_values.clone();
for (attr_name, var_ref) in self.unresolved_attrs.iter() {
let resolved = state
.get(var_ref)
// TODO provide context here, including line numbers
.with_context(|| format!("Unknown variable '{}' was referenced", var_ref))?;
all_resolved_attrs.insert(attr_name.to_owned(), resolved.clone());
}
let result: Result<_> = (self.func)(all_resolved_attrs);
if let Err(err) = result {
eprintln!("WARN: Error while resolving attributes: {}", err);
}
Ok(())
}
}
pub struct StateChangeHandlers {
handlers: HashMap<VarName, Vec<Arc<dyn Fn(HashMap<String, PrimitiveValue>) + 'static>>>,
handlers: HashMap<VarName, Vec<Arc<StateChangeHandler>>>,
}
impl StateChangeHandlers {
fn put_handler(&mut self, var_names: Vec<VarName>, handler: Arc<dyn Fn(HashMap<String, PrimitiveValue>) + 'static>) {
for var_name in var_names {
let entry: &mut Vec<Arc<dyn Fn(HashMap<String, PrimitiveValue>) + 'static>> =
self.handlers.entry(var_name).or_insert_with(Vec::new);
entry.push(handler);
fn put_handler(&mut self, handler: StateChangeHandler) {
let handler = Arc::new(handler);
for var_name in handler.unresolved_attrs.values() {
let entry: &mut Vec<Arc<StateChangeHandler>> = self.handlers.entry(var_name.clone()).or_insert_with(Vec::new);
entry.push(handler.clone());
}
}
fn get(&self, key: &VarName) -> Option<&Vec<Arc<StateChangeHandler>>> {
self.handlers.get(key)
}
fn clear(&mut self) {
self.handlers.clear();
}
}
pub struct EwwState {
state_change_handlers: StateChangeHandlers,
//on_change_handlers: HashMap<VarName, Vec<StateChangeHandler>>,
state: HashMap<VarName, PrimitiveValue>,
}
@ -54,97 +86,65 @@ impl EwwState {
}
pub fn clear_callbacks(&mut self) {
self.on_change_handlers.clear();
self.state_change_handlers.clear();
}
pub fn update_value(&mut self, key: VarName, value: PrimitiveValue) {
if let Some(handlers) = self.on_change_handlers.get(&key) {
for on_change in handlers {
on_change(value.clone());
pub fn update_value(&mut self, key: VarName, value: PrimitiveValue) -> Result<()> {
if let Some(handlers) = self.state_change_handlers.get(&key) {
self.state.insert(key.clone(), value);
for handler in handlers {
handler
.run_with_state(&self.state)
.with_context(|| format!("When updating value of {}", &key))?;
}
}
self.state.insert(key, value);
Ok(())
}
pub fn resolve<F: Fn(PrimitiveValue) + 'static + Clone>(
pub fn resolve<F: Fn(HashMap<String, PrimitiveValue>) -> Result<()> + 'static + Clone>(
&mut self,
local_env: &HashMap<VarName, AttrValue>,
value: &AttrValue,
mut needed_attributes: HashMap<String, AttrValue>,
set_value: F,
) -> bool {
match value {
AttrValue::VarRef(name) => {
// get value from globals
if let Some(value) = self.state.get(&name).cloned() {
self.on_change_handlers
.entry(name.clone())
.or_insert_with(Vec::new)
.push(Box::new(set_value.clone()));
self.resolve(local_env, &value.into(), set_value)
} else if let Some(value) = local_env.get(&name).cloned() {
// get value from local
self.resolve(local_env, &value, set_value)
} else {
eprintln!("WARN: unknown variable '{}' was referenced", name);
false
) {
let mut resolved_attrs = HashMap::new();
let mut unresolved_attrs: HashMap<String, VarName> = HashMap::new();
needed_attributes
.drain()
.for_each(|(attr_name, attr_value)| match attr_value {
AttrValue::Concrete(primitive) => {
resolved_attrs.insert(attr_name, primitive);
}
AttrValue::VarRef(var_name) => match local_env.get(&var_name) {
Some(AttrValue::VarRef(var_ref_from_local)) => {
unresolved_attrs.insert(attr_name, var_ref_from_local.clone());
}
Some(AttrValue::Concrete(concrete_from_local)) => {
resolved_attrs.insert(attr_name, concrete_from_local.clone());
}
None => {
unresolved_attrs.insert(attr_name, var_name);
}
},
});
let result: Result<_> = try {
if unresolved_attrs.is_empty() {
set_value(resolved_attrs)?;
} else {
let handler = StateChangeHandler {
func: Box::new(set_value.clone()),
constant_values: resolved_attrs,
unresolved_attrs,
};
handler.run_with_state(&self.state)?;
self.state_change_handlers.put_handler(handler);
}
AttrValue::Concrete(value) => {
set_value(value.clone());
true
}
};
if let Err(e) = result {
eprintln!("{}", e);
}
}
//pub fn resolve_attrs<F: Fn(HashMap<String, PrimitiveValue>) + 'static + Clone>(
//&mut self,
//local_env: &HashMap<VarName, AttrValue>,
//unresolved_attrs: HashMap<String, AttrValue>,
//state_update_handler: F,
//) {
//let var_names = values.iter().filter_map(|value| value.as_var_ref().ok()).collect();
//self.state_change_handlers
//.put_handler(var_names, Arc::new(state_update_handler))
//}
pub fn resolve_f64<F: Fn(f64) + 'static + Clone>(
&mut self,
local_env: &HashMap<VarName, AttrValue>,
value: &AttrValue,
set_value: F,
) -> bool {
self.resolve(local_env, value, move |x| {
if let Err(e) = x.as_f64().map(|v| set_value(v)) {
eprintln!("error while resolving value: {}", e);
};
})
}
#[allow(dead_code)]
pub fn resolve_bool<F: Fn(bool) + 'static + Clone>(
&mut self,
local_env: &HashMap<VarName, AttrValue>,
value: &AttrValue,
set_value: F,
) -> bool {
self.resolve(local_env, value, move |x| {
if let Err(e) = x.as_bool().map(|v| set_value(v)) {
eprintln!("error while resolving value: {}", e);
};
})
}
pub fn resolve_str<F: Fn(String) + 'static + Clone>(
&mut self,
local_env: &HashMap<VarName, AttrValue>,
value: &AttrValue,
set_value: F,
) -> bool {
self.resolve(local_env, value, move |x| {
if let Err(e) = x.as_string().map(|v| set_value(v.clone())) {
eprintln!("error while resolving value: {}", e);
};
})
}
}
pub fn run_command(cmd: &str) -> Result<PrimitiveValue> {
@ -152,3 +152,11 @@ pub fn run_command(cmd: &str) -> Result<PrimitiveValue> {
let output = output.trim_matches('\n');
Ok(PrimitiveValue::from(output))
}
pub fn recursive_lookup<'a>(data: &'a HashMap<VarName, AttrValue>, key: &VarName) -> Result<&'a PrimitiveValue> {
match data.get(key) {
Some(AttrValue::Concrete(x)) => Ok(x),
Some(AttrValue::VarRef(x)) => recursive_lookup(data, x),
None => Err(anyhow!("No value found for key '{}'", key)),
}
}

View file

@ -158,7 +158,7 @@ impl AttrValue {
}
}
pub fn as_var_ref(&self) -> Result<VarName> {
pub fn as_var_ref(&self) -> Result<&VarName> {
match self {
AttrValue::VarRef(x) => Ok(x),
_ => Err(anyhow!("{:?} is not a variable reference", self)),

View file

@ -37,6 +37,7 @@ pub fn element_to_gtk_thing(
let gtk_widget = if let Some(gtk_container) = gtk_container {
gtk_container
} else if let Some(def) = widget_definitions.get(widget.name.as_str()) {
// TODO widget cleanup phase, where widget arguments are resolved as far as possible beforehand?
let mut local_env = local_env.clone();
local_env.extend(widget.attrs.clone().into_iter().map(|(k, v)| (VarName(k), v)));
let custom_widget = element_to_gtk_thing(widget_definitions, eww_state, &local_env, &def.structure)?;

View file

@ -4,58 +4,101 @@ use crate::value::{AttrValue, PrimitiveValue, VarName};
use anyhow::*;
use gtk::prelude::*;
use gtk::ImageExt;
use maplit::hashmap;
use std::path::Path;
// TODO figure out how to
// TODO https://developer.gnome.org/gtk3/stable/GtkFixed.html
// general attributes
#[macro_export]
macro_rules! resolve_block {
($args:ident, $gtk_widget:ident, {
$(
prop( $( $attr_name:ident : $typecast_func:ident ),*) $code:block
),+ $(,)?
}) => {
$({
$(
$args.unhandled_attrs.retain(|a| a != &::std::stringify!($attr_name).replace('_', "-"));
)*
// TODO reimplement unused warnings
let attr_map: Result<_> = try {
::maplit::hashmap! {
$(
::std::stringify!($attr_name).to_owned() => $args.widget.get_attr(&::std::stringify!($attr_name).replace('_', "-"))?.clone()
),*
}
};
if let Ok(attr_map) = attr_map {
$args.eww_state.resolve(
$args.local_env,
attr_map,
::glib::clone!(@strong $gtk_widget => move |attrs| {
$(
let $attr_name = attrs.get( ::std::stringify!($attr_name) ).context("REEE")?.$typecast_func()?;
)*
$code
Ok(())
})
);
}
})+
};
// required
//($args:ident, $gtk_widget:ident, $func:ident => $attr:literal req => |$arg:ident| $body:expr) => {
//$args.unhandled_attrs.retain(|a| a != &$attr);
//$args.eww_state.$func($args.local_env, $args.widget.get_attr($attr)?, {
//let $gtk_widget = $gtk_widget.clone();
//move |$arg| { $body; }
//});
//};
}
/// attributes that apply to all widgets
pub(super) fn resolve_widget_attrs(bargs: &mut BuilderArgs, gtk_widget: &gtk::Widget) {
resolve!(bargs, gtk_widget, {
resolve_str => "class" => |v| gtk_widget.get_style_context().add_class(&v),
resolve_bool => "active" = true => |v| gtk_widget.set_sensitive(v),
resolve_str => "valign" => |v| gtk_widget.set_valign(parse_align(&v)),
resolve_str => "halign" => |v| gtk_widget.set_halign(parse_align(&v)),
resolve_f64 => "width" => |v| gtk_widget.set_size_request(v as i32, gtk_widget.get_allocated_height()),
resolve_f64 => "height" => |v| gtk_widget.set_size_request(gtk_widget.get_allocated_width(), v as i32),
resolve_bool => "visible" => |v| {
resolve_block!(bargs, gtk_widget, {
prop(class: as_string) { gtk_widget.get_style_context().add_class(&class) },
prop(valign: as_string) { gtk_widget.set_valign(parse_align(&valign)) },
prop(halign: as_string) { gtk_widget.set_halign(parse_align(&halign)) },
prop(width: as_f64 ) { gtk_widget.set_size_request(width as i32, gtk_widget.get_allocated_height()) },
prop(height: as_f64 ) { gtk_widget.set_size_request(gtk_widget.get_allocated_width(), height as i32) },
prop(active: as_bool ) { gtk_widget.set_sensitive(active) },
prop(visible: as_bool ) {
// TODO how do i call this only after the widget has been mapped? this is actually an issue,....
if v { gtk_widget.show(); } else { gtk_widget.hide(); }
}
if visible { gtk_widget.show(); } else { gtk_widget.hide(); }
},
});
}
/// attributes that apply to all container widgets
pub(super) fn resolve_container_attrs(bargs: &mut BuilderArgs, gtk_widget: &gtk::Container) {
resolve!(bargs, gtk_widget, {
resolve_bool => "vexpand" = false => |v| gtk_widget.set_vexpand(v),
resolve_bool => "hexpand" = false => |v| gtk_widget.set_hexpand(v),
resolve_block!(bargs, gtk_widget, {
prop(vexpand: as_bool) { gtk_widget.set_vexpand(vexpand) },
prop(hexpand: as_bool) { gtk_widget.set_hexpand(hexpand) },
});
}
pub(super) fn resolve_range_attrs(bargs: &mut BuilderArgs, gtk_widget: &gtk::Range) {
resolve!(bargs, gtk_widget, {
resolve_f64 => "value" = req => |v| gtk_widget.set_value(v),
resolve_f64 => "min" => |v| gtk_widget.get_adjustment().set_lower(v),
resolve_f64 => "max" => |v| gtk_widget.get_adjustment().set_upper(v),
resolve_str => "orientation" => |v| gtk_widget.set_orientation(parse_orientation(&v)),
resolve_str => "onchange" => |cmd| {
resolve_block!(bargs, gtk_widget, {
prop(value : as_f64) { gtk_widget.set_value(value)},
prop(min : as_f64) { gtk_widget.get_adjustment().set_lower(min)},
prop(max : as_f64) { gtk_widget.get_adjustment().set_upper(max)},
prop(orientation : as_string) { gtk_widget.set_orientation(parse_orientation(&orientation)) },
prop(onchange : as_string) {
gtk_widget.connect_value_changed(move |gtk_widget| {
run_command(&cmd, gtk_widget.get_value());
run_command(&onchange, gtk_widget.get_value());
});
}
});
}
pub(super) fn resolve_orientable_attrs(bargs: &mut BuilderArgs, gtk_widget: &gtk::Range) {
resolve!(bargs, gtk_widget, {
resolve_str => "orientation" => |v| gtk_widget.set_orientation(parse_orientation(&v)),
resolve_block!(bargs, gtk_widget, {
prop(orientation: as_string) { gtk_widget.set_orientation(parse_orientation(&orientation)) },
});
}
// widget definitions
//// widget definitions
pub(super) fn widget_to_gtk_widget(bargs: &mut BuilderArgs) -> Result<Option<gtk::Widget>> {
let gtk_widget = match bargs.widget.name.as_str() {
@ -78,45 +121,43 @@ fn build_gtk_scale(bargs: &mut BuilderArgs) -> Result<gtk::Scale> {
gtk::Orientation::Horizontal,
Some(&gtk::Adjustment::new(0.0, 0.0, 100.0, 1.0, 1.0, 1.0)),
);
resolve!(bargs, gtk_widget, {
resolve_bool => "flipped" => |v| gtk_widget.set_inverted(v),
resolve_bool => "draw-value" = false => |v| gtk_widget.set_draw_value(v),
resolve_block!(bargs, gtk_widget, {
prop(flipped: as_bool) { gtk_widget.set_inverted(flipped) },
prop(draw_value: as_bool) { gtk_widget.set_draw_value(draw_value) },
});
Ok(gtk_widget)
}
fn build_gtk_button(bargs: &mut BuilderArgs) -> Result<gtk::Button> {
let gtk_widget = gtk::Button::new();
resolve!(bargs, gtk_widget, {
resolve_str => "onclick" => |v| gtk_widget.connect_clicked(move |_| run_command(&v, ""))
resolve_block!(bargs, gtk_widget, {
prop(onclick: as_string) { gtk_widget.connect_clicked(move |_| run_command(&onclick, "")); }
});
Ok(gtk_widget)
}
fn build_gtk_image(bargs: &mut BuilderArgs) -> Result<gtk::Image> {
let gtk_widget = gtk::Image::new();
resolve!(bargs, gtk_widget, {
resolve_str => "path" = req => |v| {
gtk_widget.set_from_file(Path::new(&v));
}
resolve_block!(bargs, gtk_widget, {
prop(path: as_string) { gtk_widget.set_from_file(Path::new(&path)); }
});
Ok(gtk_widget)
}
fn build_gtk_layout(bargs: &mut BuilderArgs) -> Result<gtk::Box> {
let gtk_widget = gtk::Box::new(gtk::Orientation::Horizontal, 0);
resolve!(bargs, gtk_widget, {
resolve_f64 => "spacing" = 0.0 => |v| gtk_widget.set_spacing(v as i32),
resolve_str => "orientation" => |v| gtk_widget.set_orientation(parse_orientation(&v)),
resolve_bool => "space-evenly" = true => |v| gtk_widget.set_homogeneous(v),
resolve_block!(bargs, gtk_widget, {
prop(spacing: as_f64 ) { gtk_widget.set_spacing(spacing as i32) },
prop(orientation: as_string ) { gtk_widget.set_orientation(parse_orientation(&orientation)) },
prop(space_evenly: as_bool) { gtk_widget.set_homogeneous(space_evenly) },
});
Ok(gtk_widget)
}
fn build_gtk_label(bargs: &mut BuilderArgs) -> Result<gtk::Label> {
let gtk_widget = gtk::Label::new(None);
resolve!(bargs, gtk_widget, {
resolve_str => "text" => |v| gtk_widget.set_text(&v),
resolve_block!(bargs, gtk_widget, {
prop(text: as_string) { gtk_widget.set_text(&text) },
});
Ok(gtk_widget)
}
@ -130,17 +171,16 @@ fn build_gtk_text(bargs: &mut BuilderArgs) -> Result<gtk::Label> {
.context("text node must contain exactly one child")?
.get_attr("text")?;
let gtk_widget = gtk::Label::new(None);
bargs.eww_state.resolve_str(
bargs.eww_state.resolve(
bargs.local_env,
text,
glib::clone!(@strong gtk_widget => move |v| gtk_widget.set_text(&v)),
hashmap! {"text".to_owned() => text.clone() },
glib::clone!(@strong gtk_widget => move |v| { gtk_widget.set_text(&v.get("text").unwrap().as_string().unwrap()); Ok(())}),
);
Ok(gtk_widget)
}
fn build_gtk_aspect_frame(bargs: &mut BuilderArgs) -> Result<gtk::AspectFrame> {
let gtk_widget = gtk::AspectFrame::new(None, 0.5, 0.5, 1.0, true);
//resolve!(bargs, gtk_widget, {});
Ok(gtk_widget)
}