Add documentation

This commit is contained in:
elkowar 2020-10-08 01:03:19 +02:00
parent 11139ad4dc
commit 600f104552
8 changed files with 209 additions and 133 deletions

View file

@ -16,7 +16,7 @@ pub enum EwwEvent {
pub struct App { pub struct App {
pub eww_state: EwwState, pub eww_state: EwwState,
pub eww_config: config::EwwConfig, pub eww_config: config::EwwConfig,
pub windows: HashMap<String, gtk::Window>, pub windows: HashMap<config::WindowName, gtk::Window>,
pub css_provider: gtk::CssProvider, pub css_provider: gtk::CssProvider,
pub app_evt_send: glib::Sender<EwwEvent>, pub app_evt_send: glib::Sender<EwwEvent>,
#[debug_stub = "ScriptVarHandler(...)"] #[debug_stub = "ScriptVarHandler(...)"]
@ -54,19 +54,21 @@ impl App {
} }
fn update_state(&mut self, fieldname: VarName, value: PrimitiveValue) -> Result<()> { fn update_state(&mut self, fieldname: VarName, value: PrimitiveValue) -> Result<()> {
self.eww_state.update_value(fieldname, value) self.eww_state.update_variable(fieldname, value)
} }
fn close_window(&mut self, window_name: &str) -> Result<()> { fn close_window(&mut self, window_name: &config::WindowName) -> Result<()> {
let window = self let window = self
.windows .windows
.get(window_name) .get(window_name)
.context(format!("No window with name '{}' is running.", window_name))?; .context(format!("No window with name '{}' is running.", window_name))?;
window.close(); window.close();
self.eww_state.clear_window_state(window_name);
Ok(()) Ok(())
} }
fn open_window(&mut self, window_name: &str) -> Result<()> { fn open_window(&mut self, window_name: &config::WindowName) -> Result<()> {
let window_def = self let window_def = self
.eww_config .eww_config
.get_windows() .get_windows()
@ -89,13 +91,14 @@ impl App {
window.connect_screen_changed(on_screen_changed); window.connect_screen_changed(on_screen_changed);
let empty_local_state = HashMap::new(); let empty_local_state = HashMap::new();
let root_widget = &widgets::element_to_gtk_thing( let root_widget = &widgets::widget_use_to_gtk_widget(
&self.eww_config.get_widgets(), &self.eww_config.get_widgets(),
&mut self.eww_state, &mut self.eww_state,
window_name,
&empty_local_state, &empty_local_state,
&window_def.widget, &window_def.widget,
)?; )?;
root_widget.get_style_context().add_class(window_name); root_widget.get_style_context().add_class(&window_name.to_string());
window.add(root_widget); window.add(root_widget);
window.show_all(); window.show_all();
@ -107,7 +110,7 @@ impl App {
gdk_window.raise(); gdk_window.raise();
window.set_keep_above(true); window.set_keep_above(true);
self.windows.insert(window_name.to_string(), window); self.windows.insert(window_name.clone(), window);
Ok(()) Ok(())
} }
@ -119,7 +122,7 @@ impl App {
} }
self.eww_config = config; self.eww_config = config;
self.eww_state.clear_callbacks(); self.eww_state.clear_all_window_states();
let windows = self.windows.clone(); let windows = self.windows.clone();
for (window_name, window) in windows { for (window_name, window) in windows {

View file

@ -21,7 +21,8 @@ impl WidgetDefinition {
with_text_pos_context! { xml => with_text_pos_context! { xml =>
if xml.tag_name() != "def" { if xml.tag_name() != "def" {
bail!( bail!(
"Illegal element: only <def> may be used in definition block, but found '{}'", "{} | Illegal element: only <def> may be used in definition block, but found '{}'",
xml.text_pos(),
xml.as_tag_string() xml.as_tag_string()
); );
} }
@ -89,7 +90,7 @@ impl WidgetUse {
.collect::<HashMap<_, _>>(), .collect::<HashMap<_, _>>(),
..WidgetUse::default() ..WidgetUse::default()
}, },
XmlNode::Ignored(_) => Err(anyhow!("Failed to parse node {:?} as widget use", xml))?, XmlNode::Ignored(_) => bail!("{} | Failed to parse node {:?} as widget use", xml.text_pos(), xml),
}; };
Ok(widget_use.at_pos(text_pos)) Ok(widget_use.at_pos(text_pos))
} }

View file

@ -3,24 +3,14 @@ use crate::value::PrimitiveValue;
use crate::value::VarName; use crate::value::VarName;
use anyhow::*; use anyhow::*;
use element::*; use element::*;
use serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt;
use xml_ext::*; use xml_ext::*;
pub mod element; pub mod element;
pub mod xml_ext; pub mod xml_ext;
#[allow(unused)]
macro_rules! try_type {
($typ:ty; $code:expr) => {{
let x: $typ = try { $code };
x
}};
($typ:ty; $code:block) => {{
let x: $typ = try { $code };
x
}};
}
#[macro_export] #[macro_export]
macro_rules! ensure_xml_tag_is { macro_rules! ensure_xml_tag_is {
($element:ident, $name:literal) => { ($element:ident, $name:literal) => {
@ -57,8 +47,8 @@ impl ScriptVar {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct EwwConfig { pub struct EwwConfig {
widgets: HashMap<String, WidgetDefinition>, widgets: HashMap<String, WidgetDefinition>,
windows: HashMap<String, EwwWindowDefinition>, windows: HashMap<WindowName, EwwWindowDefinition>,
initial_variables: HashMap<String, PrimitiveValue>, initial_variables: HashMap<VarName, PrimitiveValue>,
script_vars: Vec<ScriptVar>, script_vars: Vec<ScriptVar>,
} }
@ -88,7 +78,12 @@ impl EwwConfig {
let windows = xml let windows = xml
.child("windows")? .child("windows")?
.child_elements() .child_elements()
.map(|child| Ok((child.attr("name")?.to_owned(), EwwWindowDefinition::from_xml_element(child)?))) .map(|child| {
Ok((
WindowName(child.attr("name")?.to_owned()),
EwwWindowDefinition::from_xml_element(child)?,
))
})
.collect::<Result<HashMap<_, _>>>() .collect::<Result<HashMap<_, _>>>()
.context("error parsing window definitions")?; .context("error parsing window definitions")?;
@ -101,7 +96,7 @@ impl EwwConfig {
match node.tag_name() { match node.tag_name() {
"var" => { "var" => {
initial_variables.insert( initial_variables.insert(
node.attr("name")?.to_owned(), VarName(node.attr("name")?.to_owned()),
PrimitiveValue::parse_string(&node.only_child()?.as_text()?.text()), PrimitiveValue::parse_string(&node.only_child()?.as_text()?.text()),
); );
} }
@ -128,21 +123,17 @@ impl EwwConfig {
.iter() .iter()
.map(|var| Ok((var.name.clone(), crate::eww_state::run_command(&var.command)?))) .map(|var| Ok((var.name.clone(), crate::eww_state::run_command(&var.command)?)))
.collect::<Result<HashMap<_, _>>>()?; .collect::<Result<HashMap<_, _>>>()?;
vars.extend( vars.extend(self.get_default_vars().clone());
self.get_default_vars()
.into_iter()
.map(|(k, v)| (VarName(k.clone()), v.clone())),
);
Ok(vars) Ok(vars)
} }
pub fn get_widgets(&self) -> &HashMap<String, WidgetDefinition> { pub fn get_widgets(&self) -> &HashMap<String, WidgetDefinition> {
&self.widgets &self.widgets
} }
pub fn get_windows(&self) -> &HashMap<String, EwwWindowDefinition> { pub fn get_windows(&self) -> &HashMap<WindowName, EwwWindowDefinition> {
&self.windows &self.windows
} }
pub fn get_default_vars(&self) -> &HashMap<String, PrimitiveValue> { pub fn get_default_vars(&self) -> &HashMap<VarName, PrimitiveValue> {
&self.initial_variables &self.initial_variables
} }
pub fn get_script_vars(&self) -> &Vec<ScriptVar> { pub fn get_script_vars(&self) -> &Vec<ScriptVar> {
@ -150,6 +141,24 @@ impl EwwConfig {
} }
} }
#[repr(transparent)]
#[derive(
Debug, Clone, Hash, PartialEq, Eq, derive_more::AsRef, derive_more::From, derive_more::FromStr, Serialize, Deserialize,
)]
pub struct WindowName(String);
impl std::borrow::Borrow<str> for WindowName {
fn borrow(&self) -> &str {
&self.0
}
}
impl fmt::Display for WindowName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct EwwWindowDefinition { pub struct EwwWindowDefinition {
pub position: (i32, i32), pub position: (i32, i32),

View file

@ -1,3 +1,4 @@
use crate::config::WindowName;
use crate::value::VarName; use crate::value::VarName;
use anyhow::*; use anyhow::*;
use std::collections::HashMap; use std::collections::HashMap;
@ -6,7 +7,8 @@ use std::sync::Arc;
use crate::value::{AttrValue, PrimitiveValue}; use crate::value::{AttrValue, PrimitiveValue};
//pub struct StateChangeHandler(Box<dyn Fn(HashMap<String, PrimitiveValue>) + 'static>); /// Handler that get's executed to apply the necessary parts of the eww state to a gtk widget.
/// These are created and initialized in EwwState::resolve.
pub struct StateChangeHandler { pub struct StateChangeHandler {
func: Box<dyn Fn(HashMap<String, PrimitiveValue>) -> Result<()> + 'static>, func: Box<dyn Fn(HashMap<String, PrimitiveValue>) -> Result<()> + 'static>,
constant_values: HashMap<String, PrimitiveValue>, constant_values: HashMap<String, PrimitiveValue>,
@ -14,6 +16,8 @@ pub struct StateChangeHandler {
} }
impl StateChangeHandler { impl StateChangeHandler {
/// Run the StateChangeHandler.
/// [`state`] should be the global [EwwState::state].
fn run_with_state(&self, state: &HashMap<VarName, PrimitiveValue>) -> Result<()> { fn run_with_state(&self, state: &HashMap<VarName, PrimitiveValue>) -> Result<()> {
let mut all_resolved_attrs = self.constant_values.clone(); let mut all_resolved_attrs = self.constant_values.clone();
for (attr_name, var_ref) in self.unresolved_attrs.iter() { for (attr_name, var_ref) in self.unresolved_attrs.iter() {
@ -33,130 +37,145 @@ impl StateChangeHandler {
} }
} }
pub struct StateChangeHandlers { /// Collection of [StateChangeHandler]s
handlers: HashMap<VarName, Vec<Arc<StateChangeHandler>>>, /// State specific to one window.
/// stores the state_change handlers that are used for that window.
#[derive(Default)]
pub struct EwwWindowState {
state_change_handlers: HashMap<VarName, Vec<Arc<StateChangeHandler>>>,
} }
impl StateChangeHandlers { impl EwwWindowState {
/// register a new [StateChangeHandler]
fn put_handler(&mut self, handler: StateChangeHandler) { fn put_handler(&mut self, handler: StateChangeHandler) {
let handler = Arc::new(handler); let handler = Arc::new(handler);
for var_name in handler.unresolved_attrs.values() { 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); let entry: &mut Vec<Arc<StateChangeHandler>> =
self.state_change_handlers.entry(var_name.clone()).or_insert_with(Vec::new);
entry.push(handler.clone()); entry.push(handler.clone());
} }
} }
fn get(&self, key: &VarName) -> Option<&Vec<Arc<StateChangeHandler>>> {
self.handlers.get(key)
}
fn clear(&mut self) {
self.handlers.clear();
}
} }
/// Stores the actual state of eww, including the variable state and the window-specific state-change handlers.
#[derive(Default)]
pub struct EwwState { pub struct EwwState {
state_change_handlers: StateChangeHandlers, windows: HashMap<WindowName, EwwWindowState>,
state: HashMap<VarName, PrimitiveValue>, variables_state: HashMap<VarName, PrimitiveValue>,
}
impl Default for EwwState {
fn default() -> Self {
EwwState {
state_change_handlers: StateChangeHandlers {
handlers: HashMap::new(),
},
state: HashMap::new(),
}
}
} }
impl std::fmt::Debug for EwwState { impl std::fmt::Debug for EwwState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "EwwState {{ state: {:?} }}", self.state) write!(f, "EwwState {{ state: {:?} }}", self.variables_state)
} }
} }
impl EwwState { impl EwwState {
pub fn from_default_vars(defaults: HashMap<VarName, PrimitiveValue>) -> Self { pub fn from_default_vars(defaults: HashMap<VarName, PrimitiveValue>) -> Self {
EwwState { EwwState {
state: defaults, variables_state: defaults,
..EwwState::default() ..EwwState::default()
} }
} }
pub fn clear_callbacks(&mut self) { /// remove all state stored specific to one window
self.state_change_handlers.clear(); pub fn clear_window_state(&mut self, window_name: &WindowName) {
self.windows.remove(window_name);
} }
pub fn update_value(&mut self, key: VarName, value: PrimitiveValue) -> Result<()> { /// remove all state that is specific to any window
if let Some(handlers) = self.state_change_handlers.get(&key) { pub fn clear_all_window_states(&mut self) {
self.state.insert(key.clone(), value); self.windows.clear();
}
/// Update the value of a variable, running all registered [StateChangeHandler]s.
pub fn update_variable(&mut self, key: VarName, value: PrimitiveValue) -> Result<()> {
if !self.variables_state.contains_key(&key) {
bail!("Tried to set unknown variable '{}'", key);
}
self.variables_state.insert(key.clone(), value);
let handlers = self
.windows
.values()
.filter_map(|window_state| window_state.state_change_handlers.get(&key))
.flatten();
for handler in handlers { for handler in handlers {
handler handler
.run_with_state(&self.state) .run_with_state(&self.variables_state)
.with_context(|| format!("When updating value of {}", &key))?; .with_context(|| format!("When updating value of {}", &key))?;
} }
}
Ok(()) Ok(())
} }
/// Resolve takes a function that applies a set of fully resolved attribute values to it's gtk widget.
pub fn resolve<F: Fn(HashMap<String, PrimitiveValue>) -> Result<()> + 'static + Clone>( pub fn resolve<F: Fn(HashMap<String, PrimitiveValue>) -> Result<()> + 'static + Clone>(
&mut self, &mut self,
window_name: &WindowName,
local_env: &HashMap<VarName, AttrValue>, local_env: &HashMap<VarName, AttrValue>,
mut needed_attributes: HashMap<String, AttrValue>, mut needed_attributes: HashMap<String, AttrValue>,
set_value: F, set_value: F,
) { ) {
// Resolve first collects all variable references and creates a set of unresolved attribute -> VarName pairs.
// additionally, all constant values are looked up and collected, including the values from the local environment
// These are then used to generate a StateChangeHandler, which is then executed and registered in the windows state.
let result: Result<_> = try {
let window_state = self
.windows
.entry(window_name.clone())
.or_insert_with(EwwWindowState::default);
let mut resolved_attrs = HashMap::new(); let mut resolved_attrs = HashMap::new();
let mut unresolved_attrs: HashMap<String, VarName> = HashMap::new(); let mut unresolved_attrs: HashMap<String, VarName> = HashMap::new();
needed_attributes needed_attributes
.drain() .drain()
.for_each(|(attr_name, attr_value)| match attr_value { .for_each(|(attr_name, attr_value)| match attr_value {
// directly resolve primitive values
AttrValue::Concrete(primitive) => { AttrValue::Concrete(primitive) => {
resolved_attrs.insert(attr_name, primitive); resolved_attrs.insert(attr_name, primitive);
} }
AttrValue::VarRef(var_name) => match local_env.get(&var_name) { AttrValue::VarRef(var_name) => match local_env.get(&var_name) {
Some(AttrValue::VarRef(var_ref_from_local)) => { // look up if variables are found in the local env, and resolve as far as possible
unresolved_attrs.insert(attr_name, var_ref_from_local.clone());
}
Some(AttrValue::Concrete(concrete_from_local)) => { Some(AttrValue::Concrete(concrete_from_local)) => {
resolved_attrs.insert(attr_name, concrete_from_local.clone()); resolved_attrs.insert(attr_name, concrete_from_local.clone());
} }
Some(AttrValue::VarRef(var_ref_from_local)) => {
unresolved_attrs.insert(attr_name, var_ref_from_local.clone());
}
None => { None => {
// if it's not in the local env, it must reference the global state,
// and should thus directly be inserted into the unresolved attrs.
unresolved_attrs.insert(attr_name, var_name); unresolved_attrs.insert(attr_name, var_name);
} }
}, },
}); });
let result: Result<_> = try {
if unresolved_attrs.is_empty() { if unresolved_attrs.is_empty() {
// if there are no unresolved variables, we can set the value directly
set_value(resolved_attrs)?; set_value(resolved_attrs)?;
} else { } else {
// otherwise register and execute the handler
let handler = StateChangeHandler { let handler = StateChangeHandler {
func: Box::new(set_value.clone()), func: Box::new(set_value.clone()),
constant_values: resolved_attrs, constant_values: resolved_attrs,
unresolved_attrs, unresolved_attrs,
}; };
handler.run_with_state(&self.state)?; handler.run_with_state(&self.variables_state)?;
self.state_change_handlers.put_handler(handler); window_state.put_handler(handler);
} }
}; };
if let Err(e) = result { if let Err(e) = result {
eprintln!("{}", e); eprintln!("{:?}", e);
} }
} }
} }
/// Run a command and get the output
pub fn run_command(cmd: &str) -> Result<PrimitiveValue> { pub fn run_command(cmd: &str) -> Result<PrimitiveValue> {
let output = String::from_utf8(Command::new("/bin/bash").arg("-c").arg(cmd).output()?.stdout)?; let output = String::from_utf8(Command::new("/bin/bash").arg("-c").arg(cmd).output()?.stdout)?;
let output = output.trim_matches('\n'); let output = output.trim_matches('\n');
Ok(PrimitiveValue::from(output)) 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

@ -70,10 +70,10 @@ pub enum OptAction {
Update { fieldname: VarName, value: PrimitiveValue }, Update { fieldname: VarName, value: PrimitiveValue },
#[structopt(name = "open")] #[structopt(name = "open")]
OpenWindow { window_name: String }, OpenWindow { window_name: config::WindowName },
#[structopt(name = "close")] #[structopt(name = "close")]
CloseWindow { window_name: String }, CloseWindow { window_name: config::WindowName },
#[structopt(name = "kill")] #[structopt(name = "kill")]
KillServer, KillServer,
@ -211,6 +211,7 @@ fn run_filewatch_thread<P: AsRef<Path>>(
Ok(hotwatch) Ok(hotwatch)
} }
/// detach the process from the terminal, also closing stdout and redirecting stderr into /dev/null
fn do_detach() { fn do_detach() {
// detach from terminal // detach from terminal
let pid = unsafe { libc::fork() }; let pid = unsafe { libc::fork() };

View file

@ -37,17 +37,3 @@ pub fn parse_duration(s: &str) -> Result<std::time::Duration> {
Err(anyhow!("unrecognized time format: {}", s)) Err(anyhow!("unrecognized time format: {}", s))
} }
} }
//#[macro_export]
//macro_rules! check_that {
//($(
//$cond:expr => $err:expr
//),*$(,)?) => {
//$(
//if !($cond) {
//return Err($err);
//}
//)*
//}
//}

View file

@ -1,10 +1,10 @@
use crate::config::element; use crate::config::element;
use crate::config::WindowName;
use crate::eww_state::*; use crate::eww_state::*;
use crate::value::{AttrValue, VarName}; use crate::value::{AttrValue, VarName};
use anyhow::*; use anyhow::*;
use gtk::prelude::*; use gtk::prelude::*;
use itertools::Itertools;
use ref_cast::RefCast;
use std::{collections::HashMap, process::Command}; use std::{collections::HashMap, process::Command};
use widget_definitions::*; use widget_definitions::*;
@ -12,6 +12,8 @@ pub mod widget_definitions;
const CMD_STRING_PLACEHODLER: &str = "{}"; const CMD_STRING_PLACEHODLER: &str = "{}";
/// Run a command that was provided as an attribute. This command may use a placeholder ('{}')
/// which will be replaced by the value provided as [arg]
pub fn run_command<T: std::fmt::Display>(cmd: &str, arg: T) { pub fn run_command<T: std::fmt::Display>(cmd: &str, arg: T) {
let cmd = cmd.replace(CMD_STRING_PLACEHODLER, &format!("{}", arg)); let cmd = cmd.replace(CMD_STRING_PLACEHODLER, &format!("{}", arg));
if let Err(e) = Command::new("bash").arg("-c").arg(cmd).output() { if let Err(e) = Command::new("bash").arg("-c").arg(cmd).output() {
@ -19,28 +21,69 @@ pub fn run_command<T: std::fmt::Display>(cmd: &str, arg: T) {
} }
} }
struct BuilderArgs<'a, 'b, 'c> { struct BuilderArgs<'a, 'b, 'c, 'd> {
eww_state: &'a mut EwwState, eww_state: &'a mut EwwState,
local_env: &'b HashMap<VarName, AttrValue>, local_env: &'b HashMap<VarName, AttrValue>,
widget: &'c element::WidgetUse, widget: &'c element::WidgetUse,
unhandled_attrs: Vec<&'c str>, unhandled_attrs: Vec<&'c str>,
window_name: &'d WindowName,
} }
pub fn element_to_gtk_thing( /// Generate a [gtk::Widget] from a [element::WidgetUse].
/// The widget_use may be using a builtin widget, or a custom [element::WidgetDefinition].
///
/// Also registers all the necessary state-change handlers in the eww_state.
///
/// This may return `Err` in case there was an actual error while parsing or resolving the widget,
/// Or `Ok(None)` if the widget_use just didn't match any widget name.
pub fn widget_use_to_gtk_widget(
widget_definitions: &HashMap<String, element::WidgetDefinition>, widget_definitions: &HashMap<String, element::WidgetDefinition>,
eww_state: &mut EwwState, eww_state: &mut EwwState,
window_name: &WindowName,
local_env: &HashMap<VarName, AttrValue>, local_env: &HashMap<VarName, AttrValue>,
widget: &element::WidgetUse, widget: &element::WidgetUse,
) -> Result<gtk::Widget> { ) -> Result<gtk::Widget> {
let gtk_container = build_gtk_widget(widget_definitions, eww_state, local_env, widget)?; let builtin_gtk_widget = build_gtk_widget(widget_definitions, eww_state, window_name, local_env, widget)?;
let gtk_widget = if let Some(gtk_container) = gtk_container { let gtk_widget = if let Some(builtin_gtk_widget) = builtin_gtk_widget {
gtk_container builtin_gtk_widget
} else if let Some(def) = widget_definitions.get(widget.name.as_str()) { } 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();
let mut local_env = local_env.clone();
local_env.extend(widget.attrs.clone().into_iter().map(|(k, v)| (VarName(k), v))); // the attributes that are set on the widget need to be resolved as far as possible.
let custom_widget = element_to_gtk_thing(widget_definitions, eww_state, &local_env, &def.structure)?; // If an attribute is a variable reference, it must either reference a variable in the current local_env, or in the global state.
// As we are building widgets from the outer most to the most nested, we can resolve attributes at every step.
// This way, any definition that is affected by changes in the eww_state will be directly linked to the eww_state's value.
// Example:
// foo="{{in_eww_state}}" => attr_in_child="{{foo}}" => attr_in_nested_child="{{attr_in_child}}"
// will be resolved step by step. This code will first resolve attr_in_child to directly be attr_in_child={{in_eww_state}}.
// then, in the widget_use_to_gtk_widget call of that child element,
// attr_in_nested_child will again be resolved to point to the value of attr_in_child,
// and thus: attr_in_nested_child="{{in_eww_state}}"
let resolved_widget_attr_env = widget
.attrs
.clone()
.into_iter()
.map(|(attr_name, attr_value)| {
(
VarName(attr_name),
match attr_value {
AttrValue::VarRef(var_ref) => {
local_env.get(&var_ref).cloned().unwrap_or_else(|| AttrValue::VarRef(var_ref))
}
AttrValue::Concrete(value) => AttrValue::Concrete(value),
},
)
})
.collect();
let custom_widget = widget_use_to_gtk_widget(
widget_definitions,
eww_state,
window_name,
&resolved_widget_attr_env,
&def.structure,
)?;
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 {
@ -50,9 +93,17 @@ pub fn element_to_gtk_thing(
Ok(gtk_widget) Ok(gtk_widget)
} }
pub fn build_gtk_widget( /// build a [gtk::Widget] out of a [element::WidgetUse] that uses a **builtin widget**.
/// User defined widgets are handled by [widget_use_to_gtk_widget].
///
/// Also registers all the necessary handlers in the `eww_state`.
///
/// This may return `Err` in case there was an actual error while parsing or resolving the widget,
/// Or `Ok(None)` if the widget_use just didn't match any widget name.
fn build_gtk_widget(
widget_definitions: &HashMap<String, element::WidgetDefinition>, widget_definitions: &HashMap<String, element::WidgetDefinition>,
eww_state: &mut EwwState, eww_state: &mut EwwState,
window_name: &WindowName,
local_env: &HashMap<VarName, AttrValue>, local_env: &HashMap<VarName, AttrValue>,
widget: &element::WidgetUse, widget: &element::WidgetUse,
) -> Result<Option<gtk::Widget>> { ) -> Result<Option<gtk::Widget>> {
@ -60,6 +111,7 @@ pub fn build_gtk_widget(
eww_state, eww_state,
local_env, local_env,
widget, widget,
window_name,
unhandled_attrs: widget.attrs.keys().map(|x| x.as_ref()).collect(), unhandled_attrs: widget.attrs.keys().map(|x| x.as_ref()).collect(),
}; };
let gtk_widget = match widget_to_gtk_widget(&mut bargs) { let gtk_widget = match widget_to_gtk_widget(&mut bargs) {
@ -75,10 +127,12 @@ pub fn build_gtk_widget(
} }
}; };
// run resolve functions for superclasses such as range, orientable, and widget
if let Some(gtk_widget) = gtk_widget.dynamic_cast_ref::<gtk::Container>() { if let Some(gtk_widget) = gtk_widget.dynamic_cast_ref::<gtk::Container>() {
resolve_container_attrs(&mut bargs, &gtk_widget); resolve_container_attrs(&mut bargs, &gtk_widget);
for child in &widget.children { for child in &widget.children {
let child_widget = element_to_gtk_thing(widget_definitions, bargs.eww_state, local_env, child); let child_widget = widget_use_to_gtk_widget(widget_definitions, bargs.eww_state, window_name, local_env, child);
let child_widget = child_widget.with_context(|| { let child_widget = child_widget.with_context(|| {
format!( format!(
"{}error while building child '{:#?}' of '{}'", "{}error while building child '{:#?}' of '{}'",
@ -90,6 +144,7 @@ pub fn build_gtk_widget(
gtk_widget.add(&child_widget); gtk_widget.add(&child_widget);
} }
} }
gtk_widget.dynamic_cast_ref().map(|w| resolve_range_attrs(&mut bargs, &w)); gtk_widget.dynamic_cast_ref().map(|w| resolve_range_attrs(&mut bargs, &w));
gtk_widget gtk_widget
.dynamic_cast_ref() .dynamic_cast_ref()
@ -129,6 +184,7 @@ macro_rules! resolve_block {
}; };
if let Ok(attr_map) = attr_map { if let Ok(attr_map) = attr_map {
$args.eww_state.resolve( $args.eww_state.resolve(
$args.window_name,
$args.local_env, $args.local_env,
attr_map, attr_map,
::glib::clone!(@strong $gtk_widget => move |attrs| { ::glib::clone!(@strong $gtk_widget => move |attrs| {

View file

@ -1,6 +1,6 @@
use super::{run_command, BuilderArgs}; use super::{run_command, BuilderArgs};
use crate::resolve_block; use crate::resolve_block;
use crate::value::{AttrValue, PrimitiveValue, VarName}; use crate::value::{AttrValue, PrimitiveValue};
use anyhow::*; use anyhow::*;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::ImageExt; use gtk::ImageExt;
@ -128,6 +128,7 @@ fn build_gtk_text(bargs: &mut BuilderArgs) -> Result<gtk::Label> {
.get_attr("text")?; .get_attr("text")?;
let gtk_widget = gtk::Label::new(None); let gtk_widget = gtk::Label::new(None);
bargs.eww_state.resolve( bargs.eww_state.resolve(
bargs.window_name,
bargs.local_env, bargs.local_env,
hashmap! {"text".to_owned() => text.clone() }, 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(())}), glib::clone!(@strong gtk_widget => move |v| { gtk_widget.set_text(&v.get("text").unwrap().as_string().unwrap()); Ok(())}),
@ -135,7 +136,7 @@ fn build_gtk_text(bargs: &mut BuilderArgs) -> Result<gtk::Label> {
Ok(gtk_widget) Ok(gtk_widget)
} }
fn build_gtk_aspect_frame(bargs: &mut BuilderArgs) -> Result<gtk::AspectFrame> { 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); let gtk_widget = gtk::AspectFrame::new(None, 0.5, 0.5, 1.0, true);
Ok(gtk_widget) Ok(gtk_widget)
} }