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 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 {
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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),
|
||||||
|
|
119
src/eww_state.rs
119
src/eww_state.rs
|
@ -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)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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() };
|
||||||
|
|
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))
|
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::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, >k_widget);
|
resolve_container_attrs(&mut bargs, >k_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| {
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue