146 lines
5.3 KiB
Rust
146 lines
5.3 KiB
Rust
use crate::{
|
|
config::window_definition::WindowName,
|
|
value::{AttrName, AttrValElement, VarName},
|
|
};
|
|
use anyhow::*;
|
|
use std::{collections::HashMap, sync::Arc};
|
|
|
|
use crate::value::{AttrVal, PrimVal};
|
|
|
|
/// Handler that gets 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 {
|
|
func: Box<dyn Fn(HashMap<AttrName, PrimVal>) -> Result<()> + 'static>,
|
|
unresolved_values: HashMap<AttrName, AttrVal>,
|
|
}
|
|
|
|
impl StateChangeHandler {
|
|
fn used_variables(&self) -> impl Iterator<Item = &VarName> {
|
|
self.unresolved_values.iter().flat_map(|(_, value)| value.var_refs())
|
|
}
|
|
|
|
/// Run the StateChangeHandler.
|
|
/// [`state`] should be the global [EwwState::state].
|
|
fn run_with_state(&self, state: &HashMap<VarName, PrimVal>) {
|
|
let resolved_attrs = self
|
|
.unresolved_values
|
|
.clone()
|
|
.into_iter()
|
|
.map(|(attr_name, value)| Ok((attr_name, value.resolve_fully(state)?)))
|
|
.collect::<Result<_>>();
|
|
|
|
match resolved_attrs {
|
|
Ok(resolved_attrs) => {
|
|
crate::print_result_err!("while updating UI based after state change", &(self.func)(resolved_attrs))
|
|
}
|
|
Err(err) => log::error!("Error while resolving attributes: {:?}", err),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Collection of [StateChangeHandler]s
|
|
/// 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 EwwWindowState {
|
|
/// register a new [`StateChangeHandler`]
|
|
fn put_handler(&mut self, handler: StateChangeHandler) {
|
|
let handler = Arc::new(handler);
|
|
for var_name in handler.used_variables() {
|
|
self.state_change_handlers.entry(var_name.clone()).or_insert_with(Vec::new).push(handler.clone());
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Stores the actual state of eww, including the variable state and the
|
|
/// window-specific state-change handlers.
|
|
#[derive(Default)]
|
|
pub struct EwwState {
|
|
windows: HashMap<WindowName, EwwWindowState>,
|
|
variables_state: HashMap<VarName, PrimVal>,
|
|
}
|
|
|
|
impl std::fmt::Debug for EwwState {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "EwwState {{ state: {:?} }}", self.variables_state)
|
|
}
|
|
}
|
|
|
|
impl EwwState {
|
|
pub fn from_default_vars(defaults: HashMap<VarName, PrimVal>) -> Self {
|
|
EwwState { variables_state: defaults, ..EwwState::default() }
|
|
}
|
|
|
|
pub fn get_variables(&self) -> &HashMap<VarName, PrimVal> {
|
|
&self.variables_state
|
|
}
|
|
|
|
/// remove all state stored specific to one window
|
|
pub fn clear_window_state(&mut self, window_name: &WindowName) {
|
|
self.windows.remove(window_name);
|
|
}
|
|
|
|
/// remove all state that is specific to any window
|
|
pub fn clear_all_window_states(&mut self) {
|
|
self.windows.clear();
|
|
}
|
|
|
|
/// Update the value of a variable, running all registered
|
|
/// [StateChangeHandler]s.
|
|
pub fn update_variable(&mut self, key: VarName, value: PrimVal) {
|
|
self.variables_state.insert(key.clone(), value);
|
|
|
|
// run all of the handlers
|
|
self.windows
|
|
.values()
|
|
.filter_map(|window_state| window_state.state_change_handlers.get(&key))
|
|
.flatten()
|
|
.for_each(|handler| handler.run_with_state(&self.variables_state));
|
|
}
|
|
|
|
/// Look up a single variable in the eww state, returning an `Err` when the value is not found.
|
|
pub fn lookup(&self, var_name: &VarName) -> Result<&PrimVal> {
|
|
self.variables_state.get(var_name).with_context(|| format!("Unknown variable '{}' referenced", var_name))
|
|
}
|
|
|
|
/// resolves a value if possible, using the current eww_state.
|
|
pub fn resolve_once<'a>(&'a self, value: &'a AttrVal) -> Result<PrimVal> {
|
|
value
|
|
.iter()
|
|
.map(|element| match element {
|
|
AttrValElement::Primitive(primitive) => Ok(primitive.clone()),
|
|
AttrValElement::Expr(expr) => expr.clone().eval(&self.variables_state),
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
/// Resolve takes a function that applies a set of fully resolved attribute
|
|
/// values to it's gtk widget.
|
|
pub fn resolve<F: Fn(HashMap<AttrName, PrimVal>) -> Result<()> + 'static + Clone>(
|
|
&mut self,
|
|
window_name: &WindowName,
|
|
required_attributes: HashMap<AttrName, AttrVal>,
|
|
set_value: F,
|
|
) {
|
|
let handler = StateChangeHandler { func: Box::new(set_value), unresolved_values: required_attributes };
|
|
|
|
handler.run_with_state(&self.variables_state);
|
|
|
|
// only store the handler if at least one variable is being used
|
|
if handler.used_variables().next().is_some() {
|
|
self.windows.entry(window_name.clone()).or_insert_with(EwwWindowState::default).put_handler(handler);
|
|
}
|
|
}
|
|
|
|
pub fn referenced_vars(&self) -> impl Iterator<Item = &VarName> {
|
|
self.windows.values().flat_map(|w| w.state_change_handlers.keys())
|
|
}
|
|
|
|
pub fn vars_referenced_in(&self, window_name: &WindowName) -> std::collections::HashSet<&VarName> {
|
|
self.windows.get(window_name).map(|window| window.state_change_handlers.keys().collect()).unwrap_or_default()
|
|
}
|
|
}
|