cleanup widget definition code
This commit is contained in:
parent
988b9b5085
commit
ada307aac2
9 changed files with 290 additions and 271 deletions
4
rustfmt.toml
Normal file
4
rustfmt.toml
Normal file
|
@ -0,0 +1,4 @@
|
|||
unstable_features=true
|
||||
fn_single_line=false
|
||||
max_width=130
|
||||
|
|
@ -98,10 +98,9 @@ impl WidgetUse {
|
|||
}
|
||||
|
||||
pub fn get_attr(&self, key: &str) -> Result<&AttrValue> {
|
||||
self.attrs.get(key).context(format!(
|
||||
"attribute '{}' missing from widgetuse of '{}'",
|
||||
key, &self.name
|
||||
))
|
||||
self.attrs
|
||||
.get(key)
|
||||
.context(format!("attribute '{}' missing from widgetuse of '{}'", key, &self.name))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,14 +144,7 @@ mod test {
|
|||
attrs: hashmap! { "value".to_string() => AttrValue::Concrete(PrimitiveValue::String("test".to_string()))},
|
||||
};
|
||||
assert_eq!(
|
||||
WidgetUse::parse_hocon_hash(
|
||||
parse_hocon(input_complex)
|
||||
.unwrap()
|
||||
.as_hash()
|
||||
.unwrap()
|
||||
.clone()
|
||||
)
|
||||
.unwrap(),
|
||||
WidgetUse::parse_hocon_hash(parse_hocon(input_complex).unwrap().as_hash().unwrap().clone()).unwrap(),
|
||||
expected
|
||||
);
|
||||
}
|
||||
|
@ -168,11 +160,7 @@ mod test {
|
|||
size: None,
|
||||
};
|
||||
assert_eq!(
|
||||
WidgetDefinition::parse_hocon(
|
||||
"widget_name".to_string(),
|
||||
&parse_hocon(input_complex).unwrap()
|
||||
)
|
||||
.unwrap(),
|
||||
WidgetDefinition::parse_hocon("widget_name".to_string(), &parse_hocon(input_complex).unwrap()).unwrap(),
|
||||
expected
|
||||
);
|
||||
}
|
||||
|
|
|
@ -83,9 +83,7 @@ pub struct EwwWindowDefinition {
|
|||
|
||||
impl EwwWindowDefinition {
|
||||
pub fn from_hocon(hocon: &Hocon) -> Result<EwwWindowDefinition> {
|
||||
let data = hocon
|
||||
.as_hash()
|
||||
.context("window config has to be a map structure")?;
|
||||
let data = hocon.as_hash().context("window config has to be a map structure")?;
|
||||
let position: Option<_> = try {
|
||||
(
|
||||
data.get("pos")?.as_hash().ok()?.get("x")?.as_i64()? as i32,
|
||||
|
@ -99,8 +97,7 @@ impl EwwWindowDefinition {
|
|||
)
|
||||
};
|
||||
|
||||
let element =
|
||||
ElementUse::parse_hocon(data.get("widget").context("no widget use given")?.clone())?;
|
||||
let element = ElementUse::parse_hocon(data.get("widget").context("no widget use given")?.clone())?;
|
||||
|
||||
Ok(EwwWindowDefinition {
|
||||
position: position.context("pos.x and pos.y need to be set")?,
|
||||
|
|
|
@ -34,6 +34,7 @@ impl EwwState {
|
|||
) -> bool {
|
||||
match value {
|
||||
AttrValue::VarRef(name) => {
|
||||
// get value from globals
|
||||
if let Some(value) = self.state.get(name).cloned() {
|
||||
self.on_change_handlers
|
||||
.entry(name.to_string())
|
||||
|
@ -41,6 +42,7 @@ impl EwwState {
|
|||
.push(Box::new(set_value.clone()));
|
||||
self.resolve(local_env, &value.into(), set_value)
|
||||
} else if let Some(value) = local_env.get(name).cloned() {
|
||||
// get value from local
|
||||
self.resolve(local_env, &value, set_value)
|
||||
} else {
|
||||
false
|
||||
|
@ -53,11 +55,7 @@ impl EwwState {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn resolve_into<
|
||||
TE: std::fmt::Debug,
|
||||
V: TryFrom<PrimitiveValue, Error = TE>,
|
||||
F: Fn(V) + 'static + Clone,
|
||||
>(
|
||||
pub fn resolve_into<TE: std::fmt::Debug, V: TryFrom<PrimitiveValue, Error = TE>, F: Fn(V) + 'static + Clone>(
|
||||
&mut self,
|
||||
local_env: &HashMap<String, AttrValue>,
|
||||
value: &AttrValue,
|
||||
|
|
19
src/main.rs
19
src/main.rs
|
@ -33,13 +33,23 @@ const EXAMPLE_CONFIG: &str = r#"{
|
|||
structure: {
|
||||
layout: {
|
||||
class: "container",
|
||||
children: [
|
||||
{ layout: {
|
||||
orientation: "v"
|
||||
children: [
|
||||
"fancy button"
|
||||
{ button: { children: "reeee" } }
|
||||
]
|
||||
} }
|
||||
{ layout: {
|
||||
children: [
|
||||
"hi",
|
||||
{ button: { children: "click me you" } }
|
||||
{ slider: { value: "$$some_value", min: 0, max: 100, onchange: "notify-send 'changed' {}" } }
|
||||
{ slider: { value: "$$some_value", orientation: "vertical" } }
|
||||
"hu"
|
||||
{ image: { path: "/home/leon/Bilder/ElCoward.png" } }
|
||||
]
|
||||
} }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -103,12 +113,7 @@ fn try_main() -> Result<()> {
|
|||
.get_display()
|
||||
.get_default_screen()
|
||||
.get_rgba_visual()
|
||||
.or_else(|| {
|
||||
app_window
|
||||
.get_display()
|
||||
.get_default_screen()
|
||||
.get_system_visual()
|
||||
})
|
||||
.or_else(|| app_window.get_display().get_default_screen().get_system_visual())
|
||||
.as_ref(),
|
||||
);
|
||||
|
||||
|
|
10
src/value.rs
10
src/value.rs
|
@ -20,6 +20,7 @@ impl TryFrom<PrimitiveValue> for String {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<PrimitiveValue> for f64 {
|
||||
type Error = anyhow::Error;
|
||||
fn try_from(x: PrimitiveValue) -> Result<Self> {
|
||||
|
@ -65,10 +66,7 @@ impl std::convert::TryFrom<&Hocon> for PrimitiveValue {
|
|||
fn try_from(value: &Hocon) -> Result<Self> {
|
||||
Ok(match value {
|
||||
Hocon::String(s) if s.starts_with("$$") => {
|
||||
return Err(anyhow!(
|
||||
"Tried to use variable reference {} as primitive value",
|
||||
s
|
||||
))
|
||||
return Err(anyhow!("Tried to use variable reference {} as primitive value", s))
|
||||
}
|
||||
Hocon::String(s) => PrimitiveValue::String(s.to_string()),
|
||||
Hocon::Integer(n) => PrimitiveValue::Number(*n as f64),
|
||||
|
@ -116,9 +114,7 @@ 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) 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)),
|
||||
|
|
225
src/widgets.rs
225
src/widgets.rs
|
@ -1,225 +0,0 @@
|
|||
use crate::config::element;
|
||||
use crate::eww_state::*;
|
||||
use crate::value::{AttrValue, PrimitiveValue};
|
||||
use anyhow::*;
|
||||
use gtk::prelude::*;
|
||||
use gtk::ImageExt;
|
||||
use std::path::Path;
|
||||
use std::{collections::HashMap, process::Command};
|
||||
|
||||
const CMD_STRING_PLACEHODLER: &str = "{}";
|
||||
|
||||
macro_rules! log_errors {
|
||||
($body:expr) => {{
|
||||
let result = try { $body };
|
||||
if let Err(e) = result {
|
||||
eprintln!("WARN: {}", e);
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! resolve {
|
||||
($args:ident, $gtk_widget:ident, {
|
||||
$(
|
||||
$func:ident => $attr:literal $( = $default:literal)? $( = req $(@$required:tt)?)? => |$arg:ident| $body:expr
|
||||
),+ $(,)?
|
||||
}) => {
|
||||
$(
|
||||
resolve!($args, $gtk_widget, $func => $attr $( [ $default ] )* $($($required)*)* => |$arg| $body);
|
||||
)+
|
||||
};
|
||||
|
||||
($args:ident, $gtk_widget:ident, {
|
||||
$($func:ident => {
|
||||
$($attr:literal $(= $default:literal)? $(= req $(@$required:tt)?)? => |$arg:ident| $body:expr),+ $(,)?
|
||||
}),+ $(,)?
|
||||
}) => {
|
||||
$($(
|
||||
resolve!($args, $gtk_widget, $func => $attr $( [ $default ] )* $($($required)*)* => |$arg| $body);
|
||||
)+)+
|
||||
};
|
||||
|
||||
// optional
|
||||
($args:ident, $gtk_widget:ident, $func:ident => $attr:literal => |$arg:ident| $body:expr) => {
|
||||
if let Some(attr_value) = $args.widget.attrs.get($attr) {
|
||||
$args.eww_state.$func($args.local_env, attr_value, {
|
||||
let $gtk_widget = $gtk_widget.clone();
|
||||
move |$arg| { $body; }
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// required
|
||||
($args:ident, $gtk_widget:ident, $func:ident => $attr:literal req => |$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; }
|
||||
});
|
||||
};
|
||||
|
||||
// with default
|
||||
($args:ident, $gtk_widget:ident, $func:ident => $attr:literal [$default:expr] => |$arg:ident| $body:expr) => {
|
||||
$args.eww_state.$func($args.local_env, $args.widget.attrs.get($attr).unwrap_or(&AttrValue::Concrete(PrimitiveValue::from($default))), {
|
||||
let $gtk_widget = $gtk_widget.clone();
|
||||
move |$arg| { $body; }
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn element_to_gtk_thing(
|
||||
widget_definitions: &HashMap<String, element::WidgetDefinition>,
|
||||
eww_state: &mut EwwState,
|
||||
local_env: &HashMap<String, AttrValue>,
|
||||
element: &element::ElementUse,
|
||||
) -> Result<gtk::Widget> {
|
||||
match element {
|
||||
element::ElementUse::Text(text) => Ok(gtk::Label::new(Some(&text)).upcast()),
|
||||
element::ElementUse::Widget(widget) => {
|
||||
let gtk_container =
|
||||
build_gtk_widget_or_container(widget_definitions, eww_state, local_env, widget)?;
|
||||
|
||||
let gtk_widget = if let Some(gtk_container) = gtk_container {
|
||||
gtk_container
|
||||
} else if let Some(def) = widget_definitions.get(widget.name.as_str()) {
|
||||
let mut local_env = local_env.clone();
|
||||
local_env.extend(widget.attrs.clone());
|
||||
element_to_gtk_thing(widget_definitions, eww_state, &local_env, &def.structure)?
|
||||
} else {
|
||||
return Err(anyhow!("unknown widget: '{}'", &widget.name));
|
||||
};
|
||||
|
||||
if let Ok(css_class) = widget
|
||||
.get_attr("class")
|
||||
.and_then(|x| AttrValue::as_string(x))
|
||||
{
|
||||
gtk_widget.get_style_context().add_class(css_class);
|
||||
}
|
||||
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct BuilderArgs<'a, 'b, 'c> {
|
||||
eww_state: &'a mut EwwState,
|
||||
local_env: &'b HashMap<String, AttrValue>,
|
||||
widget: &'c element::WidgetUse,
|
||||
}
|
||||
|
||||
pub fn build_gtk_widget_or_container(
|
||||
widget_definitions: &HashMap<String, element::WidgetDefinition>,
|
||||
eww_state: &mut EwwState,
|
||||
local_env: &HashMap<String, AttrValue>,
|
||||
widget: &element::WidgetUse,
|
||||
) -> Result<Option<gtk::Widget>> {
|
||||
let mut builder_args = BuilderArgs {
|
||||
eww_state,
|
||||
local_env: &local_env,
|
||||
widget: &widget,
|
||||
};
|
||||
if let Some(gtk_widget) = build_gtk_container(&mut builder_args)? {
|
||||
for child in &widget.children {
|
||||
let child_widget =
|
||||
&element_to_gtk_thing(widget_definitions, eww_state, local_env, child)
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"error while building child '{:?}' of '{}'",
|
||||
&child,
|
||||
>k_widget.get_widget_name()
|
||||
)
|
||||
})?;
|
||||
gtk_widget.add(child_widget);
|
||||
}
|
||||
Ok(Some(gtk_widget.upcast()))
|
||||
} else {
|
||||
build_gtk_widget(&mut builder_args).context("error building gtk widget")
|
||||
}
|
||||
}
|
||||
|
||||
// widget definitions
|
||||
|
||||
fn build_gtk_widget(builder_args: &mut BuilderArgs) -> Result<Option<gtk::Widget>> {
|
||||
let gtk_widget = match builder_args.widget.name.as_str() {
|
||||
"slider" => build_gtk_scale(builder_args)?.upcast(),
|
||||
"image" => build_gtk_image(builder_args)?.upcast(),
|
||||
_ => return Ok(None),
|
||||
};
|
||||
Ok(Some(gtk_widget))
|
||||
}
|
||||
|
||||
fn build_gtk_container(builder_args: &mut BuilderArgs) -> Result<Option<gtk::Container>> {
|
||||
let gtk_widget = match builder_args.widget.name.as_str() {
|
||||
"layout" => build_gtk_layout(builder_args)?.upcast(),
|
||||
"button" => build_gtk_button(builder_args)?.upcast(),
|
||||
_ => return Ok(None),
|
||||
};
|
||||
Ok(Some(gtk_widget))
|
||||
}
|
||||
|
||||
// concrete widgets
|
||||
|
||||
fn build_gtk_scale(builder_args: &mut 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" = req => |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_str => {
|
||||
"orientation" => |v| gtk_widget.set_orientation(parse_orientation(&v)),
|
||||
"onchange" => |cmd| {
|
||||
gtk_widget.connect_value_changed(move |gtk_widget| {
|
||||
run_command(&cmd, gtk_widget.get_value());
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
fn build_gtk_button(builder_args: &mut BuilderArgs) -> Result<gtk::Button> {
|
||||
let gtk_widget = gtk::Button::new();
|
||||
resolve!(builder_args, gtk_widget, {
|
||||
resolve_bool => "active" = true => |v| gtk_widget.set_sensitive(v),
|
||||
resolve_str => "onclick" => |v| gtk_widget.connect_clicked(move |_| run_command(&v, ""))
|
||||
|
||||
});
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
fn build_gtk_image(builder_args: &mut BuilderArgs) -> Result<gtk::Image> {
|
||||
let gtk_widget = gtk::Image::new();
|
||||
resolve!(builder_args, gtk_widget, {
|
||||
resolve_str => "path" = req => |v| gtk_widget.set_from_file(Path::new(&v))
|
||||
});
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
fn build_gtk_layout(builder_args: &mut BuilderArgs) -> Result<gtk::Box> {
|
||||
let gtk_widget = gtk::Box::new(gtk::Orientation::Horizontal, 0);
|
||||
resolve!(builder_args, gtk_widget, {
|
||||
resolve_f64 => "spacing" = 10.0 => |v| gtk_widget.set_spacing(v as i32),
|
||||
resolve_str => "orientation" => |v| gtk_widget.set_orientation(parse_orientation(&v)),
|
||||
|
||||
});
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
fn parse_orientation(o: &str) -> gtk::Orientation {
|
||||
match o {
|
||||
"vertical" => gtk::Orientation::Vertical,
|
||||
_ => gtk::Orientation::Horizontal,
|
||||
}
|
||||
}
|
148
src/widgets/mod.rs
Normal file
148
src/widgets/mod.rs
Normal file
|
@ -0,0 +1,148 @@
|
|||
use crate::config::element;
|
||||
use crate::eww_state::*;
|
||||
use crate::value::AttrValue;
|
||||
use anyhow::*;
|
||||
use gtk::prelude::*;
|
||||
use std::{collections::HashMap, process::Command};
|
||||
use widget_definitions::*;
|
||||
|
||||
pub mod widget_definitions;
|
||||
|
||||
const CMD_STRING_PLACEHODLER: &str = "{}";
|
||||
|
||||
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() {
|
||||
eprintln!("{}", e);
|
||||
}
|
||||
}
|
||||
|
||||
struct BuilderArgs<'a, 'b, 'c> {
|
||||
eww_state: &'a mut EwwState,
|
||||
local_env: &'b HashMap<String, AttrValue>,
|
||||
widget: &'c element::WidgetUse,
|
||||
}
|
||||
|
||||
pub fn element_to_gtk_thing(
|
||||
widget_definitions: &HashMap<String, element::WidgetDefinition>,
|
||||
eww_state: &mut EwwState,
|
||||
local_env: &HashMap<String, AttrValue>,
|
||||
element: &element::ElementUse,
|
||||
) -> Result<gtk::Widget> {
|
||||
match element {
|
||||
element::ElementUse::Text(text) => Ok(gtk::Label::new(Some(&text)).upcast()),
|
||||
element::ElementUse::Widget(widget) => {
|
||||
let gtk_container = build_gtk_widget(widget_definitions, eww_state, local_env, widget)?;
|
||||
|
||||
let gtk_widget = if let Some(gtk_container) = gtk_container {
|
||||
gtk_container
|
||||
} else if let Some(def) = widget_definitions.get(widget.name.as_str()) {
|
||||
let mut local_env = local_env.clone();
|
||||
local_env.extend(widget.attrs.clone());
|
||||
element_to_gtk_thing(widget_definitions, eww_state, &local_env, &def.structure)?
|
||||
} else {
|
||||
return Err(anyhow!("unknown widget: '{}'", &widget.name));
|
||||
};
|
||||
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_gtk_widget(
|
||||
widget_definitions: &HashMap<String, element::WidgetDefinition>,
|
||||
eww_state: &mut EwwState,
|
||||
local_env: &HashMap<String, AttrValue>,
|
||||
widget: &element::WidgetUse,
|
||||
) -> Result<Option<gtk::Widget>> {
|
||||
let mut bargs = BuilderArgs {
|
||||
eww_state,
|
||||
local_env,
|
||||
widget,
|
||||
};
|
||||
let gtk_widget = match widget_to_gtk_widget(&mut bargs) {
|
||||
Ok(Some(gtk_widget)) => gtk_widget,
|
||||
result => return result,
|
||||
};
|
||||
|
||||
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 = child_widget.with_context(|| {
|
||||
format!(
|
||||
"error while building child '{:?}' of '{}'",
|
||||
&child,
|
||||
>k_widget.get_widget_name()
|
||||
)
|
||||
})?;
|
||||
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_orientable_attrs(&mut bargs, &w));
|
||||
resolve_widget_attrs(&mut bargs, >k_widget);
|
||||
|
||||
Ok(Some(gtk_widget))
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! resolve {
|
||||
($args:ident, $gtk_widget:ident, {
|
||||
$(
|
||||
$func:ident => $attr:literal $( = $default:literal)? $( = req $(@$required:tt)?)? => |$arg:ident| $body:expr
|
||||
),+ $(,)?
|
||||
}) => {
|
||||
$(
|
||||
resolve!($args, $gtk_widget, $func => $attr $( [ $default ] )* $($($required)*)* => |$arg| $body);
|
||||
)+
|
||||
};
|
||||
|
||||
($args:ident, $gtk_widget:ident, {
|
||||
$($func:ident => {
|
||||
$($attr:literal $(= $default:literal)? $(= req $(@$required:tt)?)? => |$arg:ident| $body:expr),+ $(,)?
|
||||
}),+ $(,)?
|
||||
}) => {
|
||||
$($(
|
||||
resolve!($args, $gtk_widget, $func => $attr $( [ $default ] )* $($($required)*)* => |$arg| $body);
|
||||
)+)+
|
||||
};
|
||||
|
||||
// optional
|
||||
($args:ident, $gtk_widget:ident, $func:ident => $attr:literal => |$arg:ident| $body:expr) => {
|
||||
if let Some(attr_value) = $args.widget.attrs.get($attr) {
|
||||
$args.eww_state.$func($args.local_env, attr_value, {
|
||||
let $gtk_widget = $gtk_widget.clone();
|
||||
move |$arg| { $body; }
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// required
|
||||
($args:ident, $gtk_widget:ident, $func:ident => $attr:literal req => |$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; }
|
||||
});
|
||||
};
|
||||
|
||||
// with default
|
||||
($args:ident, $gtk_widget:ident, $func:ident => $attr:literal [$default:expr] => |$arg:ident| $body:expr) => {
|
||||
$args.eww_state.$func($args.local_env, $args.widget.attrs.get($attr).unwrap_or(&AttrValue::Concrete(PrimitiveValue::from($default))), {
|
||||
let $gtk_widget = $gtk_widget.clone();
|
||||
move |$arg| { $body; }
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
macro_rules! log_errors {
|
||||
($body:expr) => {{
|
||||
let result = try { $body };
|
||||
if let Err(e) = result {
|
||||
eprintln!("WARN: {}", e);
|
||||
}
|
||||
}};
|
||||
}
|
108
src/widgets/widget_definitions.rs
Normal file
108
src/widgets/widget_definitions.rs
Normal file
|
@ -0,0 +1,108 @@
|
|||
use super::{run_command, BuilderArgs};
|
||||
use crate::resolve;
|
||||
use crate::value::{AttrValue, PrimitiveValue};
|
||||
use anyhow::*;
|
||||
use gtk::prelude::*;
|
||||
use gtk::ImageExt;
|
||||
use std::path::Path;
|
||||
|
||||
// TODO figure out how to
|
||||
// https://developer.gnome.org/gtk3/stable/GtkFixed.html
|
||||
|
||||
// general attributes
|
||||
|
||||
/// attributes that apply to all widgets
|
||||
pub(super) fn resolve_widget_attrs(bargs: &mut BuilderArgs, gtk_widget: >k::Widget) {
|
||||
resolve!(bargs, gtk_widget, {
|
||||
resolve_str => "class" => |v| gtk_widget.get_style_context().add_class(&v),
|
||||
resolve_bool => "active" = true => |v| gtk_widget.set_sensitive(v),
|
||||
});
|
||||
}
|
||||
|
||||
/// attributes that apply to all container widgets
|
||||
pub(super) fn resolve_container_attrs(bargs: &mut BuilderArgs, gtk_widget: >k::Container) {
|
||||
resolve!(bargs, gtk_widget, {
|
||||
resolve_bool => "vexpand" = true => |v| gtk_widget.set_vexpand(v),
|
||||
resolve_bool => "hexpand" = true => |v| gtk_widget.set_hexpand(v),
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) fn resolve_range_attrs(bargs: &mut BuilderArgs, gtk_widget: >k::Range) {
|
||||
resolve!(bargs, gtk_widget, {
|
||||
resolve_f64 => "value" = req => |v| gtk_widget.set_value(v),
|
||||
resolve_f64 => "min" => |v| gtk_widget.get_adjustment().set_lower(v),
|
||||
resolve_f64 => "max" => |v| gtk_widget.get_adjustment().set_upper(v),
|
||||
resolve_str => "orientation" => |v| gtk_widget.set_orientation(parse_orientation(&v)),
|
||||
resolve_str => "onchange" => |cmd| {
|
||||
gtk_widget.connect_value_changed(move |gtk_widget| {
|
||||
run_command(&cmd, gtk_widget.get_value());
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) fn resolve_orientable_attrs(bargs: &mut BuilderArgs, gtk_widget: >k::Range) {
|
||||
resolve!(bargs, gtk_widget, {
|
||||
resolve_str => "orientation" => |v| gtk_widget.set_orientation(parse_orientation(&v)),
|
||||
});
|
||||
}
|
||||
|
||||
// widget definitions
|
||||
|
||||
pub(super) fn widget_to_gtk_widget(bargs: &mut BuilderArgs) -> Result<Option<gtk::Widget>> {
|
||||
let gtk_widget = match bargs.widget.name.as_str() {
|
||||
"slider" => build_gtk_scale(bargs)?.upcast(),
|
||||
"image" => build_gtk_image(bargs)?.upcast(),
|
||||
"layout" => build_gtk_layout(bargs)?.upcast(),
|
||||
"button" => build_gtk_button(bargs)?.upcast(),
|
||||
_ => return Ok(None),
|
||||
};
|
||||
Ok(Some(gtk_widget))
|
||||
}
|
||||
|
||||
// concrete widgets
|
||||
|
||||
fn build_gtk_scale(bargs: &mut 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!(bargs, gtk_widget, {
|
||||
resolve_bool => "flipped" => |v| gtk_widget.set_inverted(v)
|
||||
});
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
fn build_gtk_button(bargs: &mut BuilderArgs) -> Result<gtk::Button> {
|
||||
let gtk_widget = gtk::Button::new();
|
||||
resolve!(bargs, gtk_widget, {
|
||||
resolve_str => "onclick" => |v| gtk_widget.connect_clicked(move |_| run_command(&v, ""))
|
||||
|
||||
});
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
fn build_gtk_image(bargs: &mut BuilderArgs) -> Result<gtk::Image> {
|
||||
let gtk_widget = gtk::Image::new();
|
||||
resolve!(bargs, gtk_widget, {
|
||||
resolve_str => "path" = req => |v| gtk_widget.set_from_file(Path::new(&v))
|
||||
});
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
fn build_gtk_layout(bargs: &mut BuilderArgs) -> Result<gtk::Box> {
|
||||
let gtk_widget = gtk::Box::new(gtk::Orientation::Horizontal, 0);
|
||||
resolve!(bargs, gtk_widget, {
|
||||
resolve_f64 => "spacing" = 10.0 => |v| gtk_widget.set_spacing(v as i32),
|
||||
resolve_str => "orientation" => |v| gtk_widget.set_orientation(parse_orientation(&v)),
|
||||
|
||||
});
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
fn parse_orientation(o: &str) -> gtk::Orientation {
|
||||
match o {
|
||||
"vertical" | "v" => gtk::Orientation::Vertical,
|
||||
_ => gtk::Orientation::Horizontal,
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue