Add documentation
This commit is contained in:
parent
11139ad4dc
commit
600f104552
8 changed files with 209 additions and 133 deletions
19
src/app.rs
19
src/app.rs
|
@ -16,7 +16,7 @@ pub enum EwwEvent {
|
|||
pub struct App {
|
||||
pub eww_state: EwwState,
|
||||
pub eww_config: config::EwwConfig,
|
||||
pub windows: HashMap<String, gtk::Window>,
|
||||
pub windows: HashMap<config::WindowName, gtk::Window>,
|
||||
pub css_provider: gtk::CssProvider,
|
||||
pub app_evt_send: glib::Sender<EwwEvent>,
|
||||
#[debug_stub = "ScriptVarHandler(...)"]
|
||||
|
@ -54,19 +54,21 @@ impl App {
|
|||
}
|
||||
|
||||
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
|
||||
.windows
|
||||
.get(window_name)
|
||||
.context(format!("No window with name '{}' is running.", window_name))?;
|
||||
window.close();
|
||||
self.eww_state.clear_window_state(window_name);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn open_window(&mut self, window_name: &str) -> Result<()> {
|
||||
fn open_window(&mut self, window_name: &config::WindowName) -> Result<()> {
|
||||
let window_def = self
|
||||
.eww_config
|
||||
.get_windows()
|
||||
|
@ -89,13 +91,14 @@ impl App {
|
|||
window.connect_screen_changed(on_screen_changed);
|
||||
|
||||
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(),
|
||||
&mut self.eww_state,
|
||||
window_name,
|
||||
&empty_local_state,
|
||||
&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.show_all();
|
||||
|
@ -107,7 +110,7 @@ impl App {
|
|||
gdk_window.raise();
|
||||
window.set_keep_above(true);
|
||||
|
||||
self.windows.insert(window_name.to_string(), window);
|
||||
self.windows.insert(window_name.clone(), window);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -119,7 +122,7 @@ impl App {
|
|||
}
|
||||
|
||||
self.eww_config = config;
|
||||
self.eww_state.clear_callbacks();
|
||||
self.eww_state.clear_all_window_states();
|
||||
|
||||
let windows = self.windows.clone();
|
||||
for (window_name, window) in windows {
|
||||
|
|
|
@ -21,7 +21,8 @@ impl WidgetDefinition {
|
|||
with_text_pos_context! { xml =>
|
||||
if xml.tag_name() != "def" {
|
||||
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()
|
||||
);
|
||||
}
|
||||
|
@ -89,7 +90,7 @@ impl WidgetUse {
|
|||
.collect::<HashMap<_, _>>(),
|
||||
..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))
|
||||
}
|
||||
|
|
|
@ -3,24 +3,14 @@ use crate::value::PrimitiveValue;
|
|||
use crate::value::VarName;
|
||||
use anyhow::*;
|
||||
use element::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use xml_ext::*;
|
||||
|
||||
pub mod element;
|
||||
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_rules! ensure_xml_tag_is {
|
||||
($element:ident, $name:literal) => {
|
||||
|
@ -57,8 +47,8 @@ impl ScriptVar {
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct EwwConfig {
|
||||
widgets: HashMap<String, WidgetDefinition>,
|
||||
windows: HashMap<String, EwwWindowDefinition>,
|
||||
initial_variables: HashMap<String, PrimitiveValue>,
|
||||
windows: HashMap<WindowName, EwwWindowDefinition>,
|
||||
initial_variables: HashMap<VarName, PrimitiveValue>,
|
||||
script_vars: Vec<ScriptVar>,
|
||||
}
|
||||
|
||||
|
@ -88,7 +78,12 @@ impl EwwConfig {
|
|||
let windows = xml
|
||||
.child("windows")?
|
||||
.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<_, _>>>()
|
||||
.context("error parsing window definitions")?;
|
||||
|
||||
|
@ -101,7 +96,7 @@ impl EwwConfig {
|
|||
match node.tag_name() {
|
||||
"var" => {
|
||||
initial_variables.insert(
|
||||
node.attr("name")?.to_owned(),
|
||||
VarName(node.attr("name")?.to_owned()),
|
||||
PrimitiveValue::parse_string(&node.only_child()?.as_text()?.text()),
|
||||
);
|
||||
}
|
||||
|
@ -128,21 +123,17 @@ impl EwwConfig {
|
|||
.iter()
|
||||
.map(|var| Ok((var.name.clone(), crate::eww_state::run_command(&var.command)?)))
|
||||
.collect::<Result<HashMap<_, _>>>()?;
|
||||
vars.extend(
|
||||
self.get_default_vars()
|
||||
.into_iter()
|
||||
.map(|(k, v)| (VarName(k.clone()), v.clone())),
|
||||
);
|
||||
vars.extend(self.get_default_vars().clone());
|
||||
Ok(vars)
|
||||
}
|
||||
|
||||
pub fn get_widgets(&self) -> &HashMap<String, WidgetDefinition> {
|
||||
&self.widgets
|
||||
}
|
||||
pub fn get_windows(&self) -> &HashMap<String, EwwWindowDefinition> {
|
||||
pub fn get_windows(&self) -> &HashMap<WindowName, EwwWindowDefinition> {
|
||||
&self.windows
|
||||
}
|
||||
pub fn get_default_vars(&self) -> &HashMap<String, PrimitiveValue> {
|
||||
pub fn get_default_vars(&self) -> &HashMap<VarName, PrimitiveValue> {
|
||||
&self.initial_variables
|
||||
}
|
||||
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)]
|
||||
pub struct EwwWindowDefinition {
|
||||
pub position: (i32, i32),
|
||||
|
|
157
src/eww_state.rs
157
src/eww_state.rs
|
@ -1,3 +1,4 @@
|
|||
use crate::config::WindowName;
|
||||
use crate::value::VarName;
|
||||
use anyhow::*;
|
||||
use std::collections::HashMap;
|
||||
|
@ -6,7 +7,8 @@ use std::sync::Arc;
|
|||
|
||||
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 {
|
||||
func: Box<dyn Fn(HashMap<String, PrimitiveValue>) -> Result<()> + 'static>,
|
||||
constant_values: HashMap<String, PrimitiveValue>,
|
||||
|
@ -14,6 +16,8 @@ pub struct StateChangeHandler {
|
|||
}
|
||||
|
||||
impl StateChangeHandler {
|
||||
/// Run the StateChangeHandler.
|
||||
/// [`state`] should be the global [EwwState::state].
|
||||
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() {
|
||||
|
@ -33,130 +37,145 @@ impl StateChangeHandler {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct StateChangeHandlers {
|
||||
handlers: HashMap<VarName, Vec<Arc<StateChangeHandler>>>,
|
||||
/// 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 StateChangeHandlers {
|
||||
impl EwwWindowState {
|
||||
/// register a new [StateChangeHandler]
|
||||
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);
|
||||
let entry: &mut Vec<Arc<StateChangeHandler>> =
|
||||
self.state_change_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();
|
||||
}
|
||||
}
|
||||
|
||||
/// Stores the actual state of eww, including the variable state and the window-specific state-change handlers.
|
||||
#[derive(Default)]
|
||||
pub struct EwwState {
|
||||
state_change_handlers: StateChangeHandlers,
|
||||
state: HashMap<VarName, PrimitiveValue>,
|
||||
}
|
||||
|
||||
impl Default for EwwState {
|
||||
fn default() -> Self {
|
||||
EwwState {
|
||||
state_change_handlers: StateChangeHandlers {
|
||||
handlers: HashMap::new(),
|
||||
},
|
||||
state: HashMap::new(),
|
||||
}
|
||||
}
|
||||
windows: HashMap<WindowName, EwwWindowState>,
|
||||
variables_state: HashMap<VarName, PrimitiveValue>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for EwwState {
|
||||
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 {
|
||||
pub fn from_default_vars(defaults: HashMap<VarName, PrimitiveValue>) -> Self {
|
||||
EwwState {
|
||||
state: defaults,
|
||||
variables_state: defaults,
|
||||
..EwwState::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear_callbacks(&mut self) {
|
||||
self.state_change_handlers.clear();
|
||||
/// remove all state stored specific to one window
|
||||
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<()> {
|
||||
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))?;
|
||||
}
|
||||
/// 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: 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 {
|
||||
handler
|
||||
.run_with_state(&self.variables_state)
|
||||
.with_context(|| format!("When updating value of {}", &key))?;
|
||||
}
|
||||
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>(
|
||||
&mut self,
|
||||
window_name: &WindowName,
|
||||
local_env: &HashMap<VarName, AttrValue>,
|
||||
mut needed_attributes: HashMap<String, AttrValue>,
|
||||
set_value: F,
|
||||
) {
|
||||
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);
|
||||
}
|
||||
},
|
||||
});
|
||||
// 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 unresolved_attrs: HashMap<String, VarName> = HashMap::new();
|
||||
needed_attributes
|
||||
.drain()
|
||||
.for_each(|(attr_name, attr_value)| match attr_value {
|
||||
// directly resolve primitive values
|
||||
AttrValue::Concrete(primitive) => {
|
||||
resolved_attrs.insert(attr_name, primitive);
|
||||
}
|
||||
|
||||
AttrValue::VarRef(var_name) => match local_env.get(&var_name) {
|
||||
// look up if variables are found in the local env, and resolve as far as possible
|
||||
Some(AttrValue::Concrete(concrete_from_local)) => {
|
||||
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 => {
|
||||
// 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);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if unresolved_attrs.is_empty() {
|
||||
// if there are no unresolved variables, we can set the value directly
|
||||
set_value(resolved_attrs)?;
|
||||
} else {
|
||||
// otherwise register and execute the handler
|
||||
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);
|
||||
handler.run_with_state(&self.variables_state)?;
|
||||
window_state.put_handler(handler);
|
||||
}
|
||||
};
|
||||
if let Err(e) = result {
|
||||
eprintln!("{}", e);
|
||||
eprintln!("{:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Run a command and get the output
|
||||
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 = 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)),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,10 +70,10 @@ pub enum OptAction {
|
|||
Update { fieldname: VarName, value: PrimitiveValue },
|
||||
|
||||
#[structopt(name = "open")]
|
||||
OpenWindow { window_name: String },
|
||||
OpenWindow { window_name: config::WindowName },
|
||||
|
||||
#[structopt(name = "close")]
|
||||
CloseWindow { window_name: String },
|
||||
CloseWindow { window_name: config::WindowName },
|
||||
|
||||
#[structopt(name = "kill")]
|
||||
KillServer,
|
||||
|
@ -211,6 +211,7 @@ fn run_filewatch_thread<P: AsRef<Path>>(
|
|||
Ok(hotwatch)
|
||||
}
|
||||
|
||||
/// detach the process from the terminal, also closing stdout and redirecting stderr into /dev/null
|
||||
fn do_detach() {
|
||||
// detach from terminal
|
||||
let pid = unsafe { libc::fork() };
|
||||
|
|
14
src/util.rs
14
src/util.rs
|
@ -37,17 +37,3 @@ pub fn parse_duration(s: &str) -> Result<std::time::Duration> {
|
|||
Err(anyhow!("unrecognized time format: {}", s))
|
||||
}
|
||||
}
|
||||
|
||||
//#[macro_export]
|
||||
//macro_rules! check_that {
|
||||
//($(
|
||||
//$cond:expr => $err:expr
|
||||
//),*$(,)?) => {
|
||||
//$(
|
||||
//if !($cond) {
|
||||
//return Err($err);
|
||||
//}
|
||||
//)*
|
||||
//}
|
||||
|
||||
//}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use crate::config::element;
|
||||
use crate::config::WindowName;
|
||||
use crate::eww_state::*;
|
||||
use crate::value::{AttrValue, VarName};
|
||||
use anyhow::*;
|
||||
use gtk::prelude::*;
|
||||
use itertools::Itertools;
|
||||
use ref_cast::RefCast;
|
||||
|
||||
use std::{collections::HashMap, process::Command};
|
||||
use widget_definitions::*;
|
||||
|
||||
|
@ -12,6 +12,8 @@ pub mod widget_definitions;
|
|||
|
||||
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) {
|
||||
let cmd = cmd.replace(CMD_STRING_PLACEHODLER, &format!("{}", arg));
|
||||
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,
|
||||
local_env: &'b HashMap<VarName, AttrValue>,
|
||||
widget: &'c element::WidgetUse,
|
||||
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>,
|
||||
eww_state: &mut EwwState,
|
||||
window_name: &WindowName,
|
||||
local_env: &HashMap<VarName, AttrValue>,
|
||||
widget: &element::WidgetUse,
|
||||
) -> 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 {
|
||||
gtk_container
|
||||
let gtk_widget = if let Some(builtin_gtk_widget) = builtin_gtk_widget {
|
||||
builtin_gtk_widget
|
||||
} 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)?;
|
||||
//let mut local_env = local_env.clone();
|
||||
|
||||
// the attributes that are set on the widget need to be resolved as far as possible.
|
||||
// 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
|
||||
} else {
|
||||
|
@ -50,9 +93,17 @@ pub fn element_to_gtk_thing(
|
|||
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>,
|
||||
eww_state: &mut EwwState,
|
||||
window_name: &WindowName,
|
||||
local_env: &HashMap<VarName, AttrValue>,
|
||||
widget: &element::WidgetUse,
|
||||
) -> Result<Option<gtk::Widget>> {
|
||||
|
@ -60,6 +111,7 @@ pub fn build_gtk_widget(
|
|||
eww_state,
|
||||
local_env,
|
||||
widget,
|
||||
window_name,
|
||||
unhandled_attrs: widget.attrs.keys().map(|x| x.as_ref()).collect(),
|
||||
};
|
||||
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>() {
|
||||
resolve_container_attrs(&mut bargs, >k_widget);
|
||||
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(|| {
|
||||
format!(
|
||||
"{}error while building child '{:#?}' of '{}'",
|
||||
|
@ -90,6 +144,7 @@ pub fn build_gtk_widget(
|
|||
gtk_widget.add(&child_widget);
|
||||
}
|
||||
}
|
||||
|
||||
gtk_widget.dynamic_cast_ref().map(|w| resolve_range_attrs(&mut bargs, &w));
|
||||
gtk_widget
|
||||
.dynamic_cast_ref()
|
||||
|
@ -129,6 +184,7 @@ macro_rules! resolve_block {
|
|||
};
|
||||
if let Ok(attr_map) = attr_map {
|
||||
$args.eww_state.resolve(
|
||||
$args.window_name,
|
||||
$args.local_env,
|
||||
attr_map,
|
||||
::glib::clone!(@strong $gtk_widget => move |attrs| {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::{run_command, BuilderArgs};
|
||||
use crate::resolve_block;
|
||||
use crate::value::{AttrValue, PrimitiveValue, VarName};
|
||||
use crate::value::{AttrValue, PrimitiveValue};
|
||||
use anyhow::*;
|
||||
use gtk::prelude::*;
|
||||
use gtk::ImageExt;
|
||||
|
@ -128,6 +128,7 @@ fn build_gtk_text(bargs: &mut BuilderArgs) -> Result<gtk::Label> {
|
|||
.get_attr("text")?;
|
||||
let gtk_widget = gtk::Label::new(None);
|
||||
bargs.eww_state.resolve(
|
||||
bargs.window_name,
|
||||
bargs.local_env,
|
||||
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(())}),
|
||||
|
@ -135,7 +136,7 @@ fn build_gtk_text(bargs: &mut BuilderArgs) -> Result<gtk::Label> {
|
|||
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);
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue