fancy macro makes things gud
This commit is contained in:
parent
1969b6eb56
commit
7af0cb2489
7 changed files with 267 additions and 199 deletions
|
@ -1,5 +1,6 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
use crate::value::AttrValue;
|
||||||
use hocon_ext::HoconExt;
|
use hocon_ext::HoconExt;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
@ -95,6 +96,13 @@ impl WidgetUse {
|
||||||
attrs,
|
attrs,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_attr(&self, key: &str) -> Result<&AttrValue> {
|
||||||
|
self.attrs.get(key).context(format!(
|
||||||
|
"attribute {} missing from widgetuse of {}",
|
||||||
|
key, &self.name
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<WidgetUse> for ElementUse {
|
impl From<WidgetUse> for ElementUse {
|
||||||
|
|
|
@ -5,7 +5,6 @@ use hocon::*;
|
||||||
use hocon_ext::HoconExt;
|
use hocon_ext::HoconExt;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use try_match::try_match;
|
|
||||||
|
|
||||||
pub mod element;
|
pub mod element;
|
||||||
pub mod hocon_ext;
|
pub mod hocon_ext;
|
||||||
|
@ -111,55 +110,6 @@ impl EwwWindowDefinition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub enum AttrValue {
|
|
||||||
Concrete(PrimitiveValue),
|
|
||||||
VarRef(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AttrValue {
|
|
||||||
pub fn as_string(&self) -> Result<&String> {
|
|
||||||
try_match!(AttrValue::Concrete(x) = self)
|
|
||||||
.map_err(|e| anyhow!("{:?} is not a string", e))?
|
|
||||||
.as_string()
|
|
||||||
}
|
|
||||||
pub fn as_f64(&self) -> Result<f64> {
|
|
||||||
try_match!(AttrValue::Concrete(x) = self)
|
|
||||||
.map_err(|e| anyhow!("{:?} is not an f64", e))?
|
|
||||||
.as_f64()
|
|
||||||
}
|
|
||||||
pub fn as_bool(&self) -> Result<bool> {
|
|
||||||
try_match!(AttrValue::Concrete(x) = self)
|
|
||||||
.map_err(|e| anyhow!("{:?} is not a bool", e))?
|
|
||||||
.as_bool()
|
|
||||||
}
|
|
||||||
pub fn as_var_ref(&self) -> Result<&String> {
|
|
||||||
try_match!(AttrValue::VarRef(x) = self).map_err(|e| anyhow!("{:?} is not a VarRef", e))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<PrimitiveValue> for AttrValue {
|
|
||||||
fn from(value: PrimitiveValue) -> Self {
|
|
||||||
AttrValue::Concrete(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::convert::TryFrom<&Hocon> for AttrValue {
|
|
||||||
type Error = anyhow::Error;
|
|
||||||
fn try_from(value: &Hocon) -> Result<Self> {
|
|
||||||
Ok(match value {
|
|
||||||
Hocon::String(s) if s.starts_with("$$") => {
|
|
||||||
AttrValue::VarRef(s.trim_start_matches("$$").to_string())
|
|
||||||
}
|
|
||||||
Hocon::String(s) => AttrValue::Concrete(PrimitiveValue::String(s.clone())),
|
|
||||||
Hocon::Integer(n) => AttrValue::Concrete(PrimitiveValue::Number(*n as f64)),
|
|
||||||
Hocon::Real(n) => AttrValue::Concrete(PrimitiveValue::Number(*n as f64)),
|
|
||||||
Hocon::Boolean(b) => AttrValue::Concrete(PrimitiveValue::Boolean(*b)),
|
|
||||||
_ => return Err(anyhow!("cannot convert {:?} to config::AttrValue", &value)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_hocon(s: &str) -> Result<Hocon> {
|
pub fn parse_hocon(s: &str) -> Result<Hocon> {
|
||||||
Ok(HoconLoader::new().load_str(s)?.hocon()?)
|
Ok(HoconLoader::new().load_str(s)?.hocon()?)
|
||||||
}
|
}
|
||||||
|
|
93
src/eww_state.rs
Normal file
93
src/eww_state.rs
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::value::{AttrValue, PrimitiveValue};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct EwwState {
|
||||||
|
on_change_handlers: HashMap<String, Vec<Box<dyn Fn(PrimitiveValue) + 'static>>>,
|
||||||
|
state: HashMap<String, PrimitiveValue>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EwwState {
|
||||||
|
pub fn from_default_vars(defaults: HashMap<String, PrimitiveValue>) -> Self {
|
||||||
|
EwwState {
|
||||||
|
state: defaults,
|
||||||
|
..EwwState::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn update_value(&mut self, key: String, value: PrimitiveValue) {
|
||||||
|
if let Some(handlers) = self.on_change_handlers.get(&key) {
|
||||||
|
for on_change in handlers {
|
||||||
|
on_change(value.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.state.insert(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve<F: Fn(PrimitiveValue) + 'static + Clone>(
|
||||||
|
&mut self,
|
||||||
|
local_env: &HashMap<String, AttrValue>,
|
||||||
|
value: &AttrValue,
|
||||||
|
set_value: F,
|
||||||
|
) -> bool {
|
||||||
|
dbg!("resolve: ", value);
|
||||||
|
match value {
|
||||||
|
AttrValue::VarRef(name) => {
|
||||||
|
if let Some(value) = self.state.get(name).cloned() {
|
||||||
|
self.on_change_handlers
|
||||||
|
.entry(name.to_string())
|
||||||
|
.or_insert_with(Vec::new)
|
||||||
|
.push(Box::new(set_value.clone()));
|
||||||
|
self.resolve(local_env, &value.into(), set_value)
|
||||||
|
} else if let Some(value) = local_env.get(name).cloned() {
|
||||||
|
self.resolve(local_env, &value, set_value)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AttrValue::Concrete(value) => {
|
||||||
|
set_value(value.clone());
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_f64<F: Fn(f64) + 'static + Clone>(
|
||||||
|
&mut self,
|
||||||
|
local_env: &HashMap<String, AttrValue>,
|
||||||
|
value: &AttrValue,
|
||||||
|
set_value: F,
|
||||||
|
) -> bool {
|
||||||
|
self.resolve(local_env, value, move |x| {
|
||||||
|
if let Err(e) = x.as_f64().map(|v| set_value(v)) {
|
||||||
|
eprintln!("error while resolving value: {}", e);
|
||||||
|
};
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn resolve_bool<F: Fn(bool) + 'static + Clone>(
|
||||||
|
&mut self,
|
||||||
|
local_env: &HashMap<String, AttrValue>,
|
||||||
|
value: &AttrValue,
|
||||||
|
set_value: F,
|
||||||
|
) -> bool {
|
||||||
|
self.resolve(local_env, value, move |x| {
|
||||||
|
if let Err(e) = x.as_bool().map(|v| set_value(v)) {
|
||||||
|
eprintln!("error while resolving value: {}", e);
|
||||||
|
};
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn resolve_string<F: Fn(String) + 'static + Clone>(
|
||||||
|
&mut self,
|
||||||
|
local_env: &HashMap<String, AttrValue>,
|
||||||
|
value: &AttrValue,
|
||||||
|
set_value: F,
|
||||||
|
) -> bool {
|
||||||
|
self.resolve(local_env, value, move |x| {
|
||||||
|
if let Err(e) = x.as_string().map(|v| set_value(v.clone())) {
|
||||||
|
eprintln!("error while resolving value: {}", e);
|
||||||
|
};
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
177
src/main.rs
177
src/main.rs
|
@ -2,22 +2,29 @@
|
||||||
extern crate gio;
|
extern crate gio;
|
||||||
extern crate gtk;
|
extern crate gtk;
|
||||||
|
|
||||||
use anyhow::{self, Result};
|
use anyhow::*;
|
||||||
use gdk::*;
|
use gdk::*;
|
||||||
use gio::prelude::*;
|
use gio::prelude::*;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use gtk::{Application, ApplicationWindow};
|
use gtk::{Application, ApplicationWindow};
|
||||||
use std::{collections::HashMap, process::Command};
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub mod config;
|
pub mod config;
|
||||||
|
pub mod eww_state;
|
||||||
pub mod value;
|
pub mod value;
|
||||||
pub mod widgets;
|
pub mod widgets;
|
||||||
|
|
||||||
use config::element;
|
use config::element;
|
||||||
use config::AttrValue;
|
use eww_state::*;
|
||||||
use value::PrimitiveValue;
|
use value::{AttrValue, PrimitiveValue};
|
||||||
|
|
||||||
const CMD_STRING_PLACEHODLER: &str = "{}";
|
macro_rules! build {
|
||||||
|
($var_name:ident = $value:expr ; $code:block) => {{
|
||||||
|
let mut $var_name = $value;
|
||||||
|
$code;
|
||||||
|
$var_name
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
const EXAMPLE_CONFIG: &str = r#"{
|
const EXAMPLE_CONFIG: &str = r#"{
|
||||||
widgets: {
|
widgets: {
|
||||||
|
@ -61,14 +68,6 @@ const EXAMPLE_CONFIG: &str = r#"{
|
||||||
},
|
},
|
||||||
}"#;
|
}"#;
|
||||||
|
|
||||||
macro_rules! build {
|
|
||||||
($var_name:ident = $value:expr ; $code:block) => {{
|
|
||||||
let mut $var_name = $value;
|
|
||||||
$code;
|
|
||||||
$var_name
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum MuhhMsg {
|
enum MuhhMsg {
|
||||||
UpdateValue(String, PrimitiveValue),
|
UpdateValue(String, PrimitiveValue),
|
||||||
|
@ -170,9 +169,9 @@ fn element_to_gtk_thing(
|
||||||
eww_state: &mut EwwState,
|
eww_state: &mut EwwState,
|
||||||
local_environment: &HashMap<String, AttrValue>,
|
local_environment: &HashMap<String, AttrValue>,
|
||||||
element: &element::ElementUse,
|
element: &element::ElementUse,
|
||||||
) -> Option<gtk::Widget> {
|
) -> Result<gtk::Widget> {
|
||||||
match element {
|
match element {
|
||||||
element::ElementUse::Text(text) => Some(gtk::Label::new(Some(&text)).upcast()),
|
element::ElementUse::Text(text) => Ok(gtk::Label::new(Some(&text)).upcast()),
|
||||||
element::ElementUse::Widget(widget) => {
|
element::ElementUse::Widget(widget) => {
|
||||||
widget_use_to_gtk_thing(widget_definitions, eww_state, local_environment, widget)
|
widget_use_to_gtk_thing(widget_definitions, eww_state, local_environment, widget)
|
||||||
}
|
}
|
||||||
|
@ -184,7 +183,7 @@ fn widget_use_to_gtk_thing(
|
||||||
eww_state: &mut EwwState,
|
eww_state: &mut EwwState,
|
||||||
local_environment: &HashMap<String, AttrValue>,
|
local_environment: &HashMap<String, AttrValue>,
|
||||||
widget: &element::WidgetUse,
|
widget: &element::WidgetUse,
|
||||||
) -> Option<gtk::Widget> {
|
) -> Result<gtk::Widget> {
|
||||||
let gtk_widget =
|
let gtk_widget =
|
||||||
widget_use_to_gtk_container(widget_definitions, eww_state, &local_environment, &widget)
|
widget_use_to_gtk_container(widget_definitions, eww_state, &local_environment, &widget)
|
||||||
.or(widget_use_to_gtk_widget(
|
.or(widget_use_to_gtk_widget(
|
||||||
|
@ -201,7 +200,7 @@ fn widget_use_to_gtk_thing(
|
||||||
gtk_widget.get_style_context().add_class(css_class);
|
gtk_widget.get_style_context().add_class(css_class);
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(gtk_widget)
|
Ok(gtk_widget)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn widget_use_to_gtk_container(
|
fn widget_use_to_gtk_container(
|
||||||
|
@ -209,11 +208,11 @@ fn widget_use_to_gtk_container(
|
||||||
eww_state: &mut EwwState,
|
eww_state: &mut EwwState,
|
||||||
local_environment: &HashMap<String, AttrValue>,
|
local_environment: &HashMap<String, AttrValue>,
|
||||||
widget: &element::WidgetUse,
|
widget: &element::WidgetUse,
|
||||||
) -> Option<gtk::Widget> {
|
) -> Result<gtk::Widget> {
|
||||||
let container_widget: gtk::Container = match widget.name.as_str() {
|
let container_widget: gtk::Container = match widget.name.as_str() {
|
||||||
"layout_horizontal" => gtk::Box::new(gtk::Orientation::Horizontal, 0).upcast(),
|
"layout_horizontal" => gtk::Box::new(gtk::Orientation::Horizontal, 0).upcast(),
|
||||||
"button" => gtk::Button::new().upcast(),
|
"button" => gtk::Button::new().upcast(),
|
||||||
_ => return None,
|
_ => return Err(anyhow!("{} is not a known container widget", widget.name)),
|
||||||
};
|
};
|
||||||
|
|
||||||
for child in &widget.children {
|
for child in &widget.children {
|
||||||
|
@ -224,7 +223,7 @@ fn widget_use_to_gtk_container(
|
||||||
child,
|
child,
|
||||||
)?);
|
)?);
|
||||||
}
|
}
|
||||||
Some(container_widget.upcast())
|
Ok(container_widget.upcast())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn widget_use_to_gtk_widget(
|
fn widget_use_to_gtk_widget(
|
||||||
|
@ -232,37 +231,14 @@ fn widget_use_to_gtk_widget(
|
||||||
eww_state: &mut EwwState,
|
eww_state: &mut EwwState,
|
||||||
local_env: &HashMap<String, AttrValue>,
|
local_env: &HashMap<String, AttrValue>,
|
||||||
widget: &element::WidgetUse,
|
widget: &element::WidgetUse,
|
||||||
) -> Option<gtk::Widget> {
|
) -> Result<gtk::Widget> {
|
||||||
|
let builder_args = widgets::BuilderArgs {
|
||||||
|
eww_state,
|
||||||
|
local_env: &local_env,
|
||||||
|
widget: &widget,
|
||||||
|
};
|
||||||
let new_widget: gtk::Widget = match widget.name.as_str() {
|
let new_widget: gtk::Widget = match widget.name.as_str() {
|
||||||
"slider" => {
|
"slider" => widgets::build_gtk_scale(builder_args)?.upcast(),
|
||||||
let scale = gtk::Scale::new(
|
|
||||||
gtk::Orientation::Horizontal,
|
|
||||||
Some(>k::Adjustment::new(0.0, 0.0, 100.0, 1.0, 1.0, 1.0)),
|
|
||||||
);
|
|
||||||
eww_state.resolve_f64(local_env, widget.attrs.get("value")?, {
|
|
||||||
let scale = scale.clone();
|
|
||||||
move |value| scale.set_value(value)
|
|
||||||
});
|
|
||||||
eww_state.resolve_f64(local_env, widget.attrs.get("min")?, {
|
|
||||||
let scale = scale.clone();
|
|
||||||
move |value| scale.get_adjustment().set_lower(value)
|
|
||||||
});
|
|
||||||
eww_state.resolve_f64(local_env, widget.attrs.get("max")?, {
|
|
||||||
let scale = scale.clone();
|
|
||||||
move |value| scale.get_adjustment().set_upper(value)
|
|
||||||
});
|
|
||||||
eww_state.resolve_string(local_env, widget.attrs.get("onchange")?, {
|
|
||||||
let scale = scale.clone();
|
|
||||||
move |on_change| {
|
|
||||||
scale.connect_value_changed(move |scale| {
|
|
||||||
run_command(&on_change, scale.get_value());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//scale.set_property("draw-value", &false.to_value()).ok()?;
|
|
||||||
scale.upcast()
|
|
||||||
}
|
|
||||||
|
|
||||||
name if widget_definitions.contains_key(name) => {
|
name if widget_definitions.contains_key(name) => {
|
||||||
let def = &widget_definitions[name];
|
let def = &widget_definitions[name];
|
||||||
|
@ -277,104 +253,7 @@ fn widget_use_to_gtk_widget(
|
||||||
&def.structure,
|
&def.structure,
|
||||||
)?
|
)?
|
||||||
}
|
}
|
||||||
_ => return None,
|
_ => return Err(anyhow!("unknown widget {}", &widget.name)),
|
||||||
};
|
};
|
||||||
Some(new_widget)
|
Ok(new_widget)
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct EwwState {
|
|
||||||
on_change_handlers: HashMap<String, Vec<Box<dyn Fn(PrimitiveValue) + 'static>>>,
|
|
||||||
state: HashMap<String, PrimitiveValue>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EwwState {
|
|
||||||
pub fn from_default_vars(defaults: HashMap<String, PrimitiveValue>) -> Self {
|
|
||||||
EwwState {
|
|
||||||
state: defaults,
|
|
||||||
..EwwState::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn update_value(&mut self, key: String, value: PrimitiveValue) {
|
|
||||||
if let Some(handlers) = self.on_change_handlers.get(&key) {
|
|
||||||
for on_change in handlers {
|
|
||||||
on_change(value.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.state.insert(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resolve<F: Fn(PrimitiveValue) + 'static + Clone>(
|
|
||||||
&mut self,
|
|
||||||
local_env: &HashMap<String, AttrValue>,
|
|
||||||
value: &AttrValue,
|
|
||||||
set_value: F,
|
|
||||||
) -> bool {
|
|
||||||
dbg!("resolve: ", value);
|
|
||||||
match value {
|
|
||||||
AttrValue::VarRef(name) => {
|
|
||||||
if let Some(value) = self.state.get(name).cloned() {
|
|
||||||
self.on_change_handlers
|
|
||||||
.entry(name.to_string())
|
|
||||||
.or_insert_with(Vec::new)
|
|
||||||
.push(Box::new(set_value.clone()));
|
|
||||||
self.resolve(local_env, &value.into(), set_value)
|
|
||||||
} else if let Some(value) = local_env.get(name).cloned() {
|
|
||||||
self.resolve(local_env, &value, set_value)
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AttrValue::Concrete(value) => {
|
|
||||||
set_value(value.clone());
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resolve_f64<F: Fn(f64) + 'static + Clone>(
|
|
||||||
&mut self,
|
|
||||||
local_env: &HashMap<String, AttrValue>,
|
|
||||||
value: &AttrValue,
|
|
||||||
set_value: F,
|
|
||||||
) -> bool {
|
|
||||||
self.resolve(local_env, value, move |x| {
|
|
||||||
if let Err(e) = x.as_f64().map(|v| set_value(v)) {
|
|
||||||
eprintln!("error while resolving value: {}", e);
|
|
||||||
};
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn resolve_bool<F: Fn(bool) + 'static + Clone>(
|
|
||||||
&mut self,
|
|
||||||
local_env: &HashMap<String, AttrValue>,
|
|
||||||
value: &AttrValue,
|
|
||||||
set_value: F,
|
|
||||||
) -> bool {
|
|
||||||
self.resolve(local_env, value, move |x| {
|
|
||||||
if let Err(e) = x.as_bool().map(|v| set_value(v)) {
|
|
||||||
eprintln!("error while resolving value: {}", e);
|
|
||||||
};
|
|
||||||
})
|
|
||||||
}
|
|
||||||
pub fn resolve_string<F: Fn(String) + 'static + Clone>(
|
|
||||||
&mut self,
|
|
||||||
local_env: &HashMap<String, AttrValue>,
|
|
||||||
value: &AttrValue,
|
|
||||||
set_value: F,
|
|
||||||
) -> bool {
|
|
||||||
self.resolve(local_env, value, move |x| {
|
|
||||||
if let Err(e) = x.as_string().map(|v| set_value(v.clone())) {
|
|
||||||
eprintln!("error while resolving value: {}", e);
|
|
||||||
};
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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() {
|
|
||||||
eprintln!("{}", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
49
src/value.rs
49
src/value.rs
|
@ -44,3 +44,52 @@ impl std::convert::TryFrom<&Hocon> for PrimitiveValue {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum AttrValue {
|
||||||
|
Concrete(PrimitiveValue),
|
||||||
|
VarRef(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AttrValue {
|
||||||
|
pub fn as_string(&self) -> Result<&String> {
|
||||||
|
try_match!(AttrValue::Concrete(x) = self)
|
||||||
|
.map_err(|e| anyhow!("{:?} is not a string", e))?
|
||||||
|
.as_string()
|
||||||
|
}
|
||||||
|
pub fn as_f64(&self) -> Result<f64> {
|
||||||
|
try_match!(AttrValue::Concrete(x) = self)
|
||||||
|
.map_err(|e| anyhow!("{:?} is not an f64", e))?
|
||||||
|
.as_f64()
|
||||||
|
}
|
||||||
|
pub fn as_bool(&self) -> Result<bool> {
|
||||||
|
try_match!(AttrValue::Concrete(x) = self)
|
||||||
|
.map_err(|e| anyhow!("{:?} is not a bool", e))?
|
||||||
|
.as_bool()
|
||||||
|
}
|
||||||
|
pub fn as_var_ref(&self) -> Result<&String> {
|
||||||
|
try_match!(AttrValue::VarRef(x) = self).map_err(|e| anyhow!("{:?} is not a VarRef", e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PrimitiveValue> for AttrValue {
|
||||||
|
fn from(value: PrimitiveValue) -> Self {
|
||||||
|
AttrValue::Concrete(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::convert::TryFrom<&Hocon> for AttrValue {
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
fn try_from(value: &Hocon) -> Result<Self> {
|
||||||
|
Ok(match value {
|
||||||
|
Hocon::String(s) if s.starts_with("$$") => {
|
||||||
|
AttrValue::VarRef(s.trim_start_matches("$$").to_string())
|
||||||
|
}
|
||||||
|
Hocon::String(s) => AttrValue::Concrete(PrimitiveValue::String(s.clone())),
|
||||||
|
Hocon::Integer(n) => AttrValue::Concrete(PrimitiveValue::Number(*n as f64)),
|
||||||
|
Hocon::Real(n) => AttrValue::Concrete(PrimitiveValue::Number(*n as f64)),
|
||||||
|
Hocon::Boolean(b) => AttrValue::Concrete(PrimitiveValue::Boolean(*b)),
|
||||||
|
_ => return Err(anyhow!("cannot convert {:?} to config::AttrValue", &value)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
89
src/widgets.rs
Normal file
89
src/widgets.rs
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
use crate::config::element;
|
||||||
|
use crate::eww_state::*;
|
||||||
|
use crate::value::AttrValue;
|
||||||
|
use anyhow::*;
|
||||||
|
use gtk::prelude::*;
|
||||||
|
use std::{collections::HashMap, process::Command};
|
||||||
|
|
||||||
|
const CMD_STRING_PLACEHODLER: &str = "{}";
|
||||||
|
|
||||||
|
macro_rules! resolve {
|
||||||
|
($args:ident, $gtk_widget:ident, {
|
||||||
|
$($func:ident =>
|
||||||
|
{
|
||||||
|
$($attr:literal => |$arg:ident| $body:expr),+
|
||||||
|
}
|
||||||
|
),+
|
||||||
|
}) => {
|
||||||
|
$(
|
||||||
|
$(
|
||||||
|
$args.eww_state.$func($args.local_env, $args.widget.get_attr($attr)?, {
|
||||||
|
let $gtk_widget = $gtk_widget.clone();
|
||||||
|
move |$arg| { $body; }
|
||||||
|
});
|
||||||
|
)+
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BuilderArgs<'a, 'b, 'c> {
|
||||||
|
pub eww_state: &'a mut EwwState,
|
||||||
|
pub local_env: &'b HashMap<String, AttrValue>,
|
||||||
|
pub widget: &'c element::WidgetUse,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_gtk_scale(builder_args: BuilderArgs) -> Result<gtk::Scale> {
|
||||||
|
let gtk_widget = gtk::Scale::new(
|
||||||
|
gtk::Orientation::Horizontal,
|
||||||
|
Some(>k::Adjustment::new(0.0, 0.0, 100.0, 1.0, 1.0, 1.0)),
|
||||||
|
);
|
||||||
|
|
||||||
|
resolve!(builder_args, gtk_widget, {
|
||||||
|
resolve_f64 => {
|
||||||
|
"value" => |v| gtk_widget.set_value(v),
|
||||||
|
"min" => |v| gtk_widget.get_adjustment().set_lower(v),
|
||||||
|
"max" => |v| gtk_widget.get_adjustment().set_upper(v)
|
||||||
|
},
|
||||||
|
resolve_string => {
|
||||||
|
"onchange" => |cmd| {
|
||||||
|
gtk_widget.connect_value_changed(move |gtk_widget| {
|
||||||
|
run_command(&cmd, gtk_widget.get_value());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Ok(gtk_widget)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_gtk_button(builder_args: BuilderArgs) -> Result<gtk::Button> {
|
||||||
|
let gtk_widget = gtk::Button::new();
|
||||||
|
resolve!(builder_args, gtk_widget, {
|
||||||
|
resolve_bool => {
|
||||||
|
"active" => |v| gtk_widget.set_sensitive(v)
|
||||||
|
},
|
||||||
|
resolve_string => {
|
||||||
|
"onclick" => |cmd| gtk_widget.connect_clicked(move |_| run_command(&cmd, ""))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Ok(gtk_widget)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_gtk_layout(builder_args: BuilderArgs) -> Result<gtk::Box> {
|
||||||
|
let gtk_widget = gtk::Box::new(gtk::Orientation::Horizontal, 0);
|
||||||
|
resolve!(builder_args, gtk_widget, {
|
||||||
|
resolve_f64 => {
|
||||||
|
"spacing" => |v| gtk_widget.set_spacing(v as i32)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(gtk_widget)
|
||||||
|
}
|
||||||
|
|
||||||
|
//"layout_horizontal" => gtk::Box::new(gtk::Orientation::Horizontal, 0).upcast(),
|
||||||
|
|
||||||
|
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() {
|
||||||
|
eprintln!("{}", e);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue