174 lines
5.3 KiB
Rust
174 lines
5.3 KiB
Rust
use crate::build;
|
|
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; }
|
|
});
|
|
)+
|
|
)+
|
|
}
|
|
}
|
|
|
|
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 = gtk_container.or_else(|_| {
|
|
if let Some(def) = widget_definitions.get(widget.name.as_str()) {
|
|
let local_environment = build!(env = local_env.clone(); {
|
|
env.extend(widget.attrs.clone());
|
|
});
|
|
|
|
element_to_gtk_thing(
|
|
widget_definitions,
|
|
eww_state,
|
|
&local_environment,
|
|
&def.structure,
|
|
)
|
|
} else {
|
|
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<gtk::Widget> {
|
|
let mut builder_args = BuilderArgs {
|
|
eww_state,
|
|
local_env: &local_env,
|
|
widget: &widget,
|
|
};
|
|
let gtk_widget: Option<gtk::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)?;
|
|
gtk_widget.add(child_widget);
|
|
}
|
|
Some(gtk_widget.upcast())
|
|
} else {
|
|
build_gtk_widget(&mut builder_args)?
|
|
};
|
|
gtk_widget.context(format!("unknown widget {:?}", 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(),
|
|
_ => 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" => |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)
|
|
}
|
|
|
|
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" => |v| gtk_widget.set_sensitive(v)
|
|
},
|
|
resolve_string => {
|
|
"onclick" => |cmd| gtk_widget.connect_clicked(move |_| run_command(&cmd, ""))
|
|
}
|
|
});
|
|
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" => |v| gtk_widget.set_spacing(v as i32)
|
|
}
|
|
});
|
|
|
|
Ok(gtk_widget)
|
|
}
|