cleanup widget definition code

This commit is contained in:
elkowar 2020-09-22 20:59:56 +02:00
parent 988b9b5085
commit ada307aac2
9 changed files with 290 additions and 271 deletions

4
rustfmt.toml Normal file
View file

@ -0,0 +1,4 @@
unstable_features=true
fn_single_line=false
max_width=130

View file

@ -98,10 +98,9 @@ impl WidgetUse {
} }
pub fn get_attr(&self, key: &str) -> Result<&AttrValue> { pub fn get_attr(&self, key: &str) -> Result<&AttrValue> {
self.attrs.get(key).context(format!( self.attrs
"attribute '{}' missing from widgetuse of '{}'", .get(key)
key, &self.name .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()))}, attrs: hashmap! { "value".to_string() => AttrValue::Concrete(PrimitiveValue::String("test".to_string()))},
}; };
assert_eq!( assert_eq!(
WidgetUse::parse_hocon_hash( WidgetUse::parse_hocon_hash(parse_hocon(input_complex).unwrap().as_hash().unwrap().clone()).unwrap(),
parse_hocon(input_complex)
.unwrap()
.as_hash()
.unwrap()
.clone()
)
.unwrap(),
expected expected
); );
} }
@ -168,11 +160,7 @@ mod test {
size: None, size: None,
}; };
assert_eq!( assert_eq!(
WidgetDefinition::parse_hocon( WidgetDefinition::parse_hocon("widget_name".to_string(), &parse_hocon(input_complex).unwrap()).unwrap(),
"widget_name".to_string(),
&parse_hocon(input_complex).unwrap()
)
.unwrap(),
expected expected
); );
} }

View file

@ -83,9 +83,7 @@ pub struct EwwWindowDefinition {
impl EwwWindowDefinition { impl EwwWindowDefinition {
pub fn from_hocon(hocon: &Hocon) -> Result<EwwWindowDefinition> { pub fn from_hocon(hocon: &Hocon) -> Result<EwwWindowDefinition> {
let data = hocon let data = hocon.as_hash().context("window config has to be a map structure")?;
.as_hash()
.context("window config has to be a map structure")?;
let position: Option<_> = try { let position: Option<_> = try {
( (
data.get("pos")?.as_hash().ok()?.get("x")?.as_i64()? as i32, data.get("pos")?.as_hash().ok()?.get("x")?.as_i64()? as i32,
@ -99,8 +97,7 @@ impl EwwWindowDefinition {
) )
}; };
let element = let element = ElementUse::parse_hocon(data.get("widget").context("no widget use given")?.clone())?;
ElementUse::parse_hocon(data.get("widget").context("no widget use given")?.clone())?;
Ok(EwwWindowDefinition { Ok(EwwWindowDefinition {
position: position.context("pos.x and pos.y need to be set")?, position: position.context("pos.x and pos.y need to be set")?,

View file

@ -34,6 +34,7 @@ impl EwwState {
) -> bool { ) -> bool {
match value { match value {
AttrValue::VarRef(name) => { AttrValue::VarRef(name) => {
// get value from globals
if let Some(value) = self.state.get(name).cloned() { if let Some(value) = self.state.get(name).cloned() {
self.on_change_handlers self.on_change_handlers
.entry(name.to_string()) .entry(name.to_string())
@ -41,6 +42,7 @@ impl EwwState {
.push(Box::new(set_value.clone())); .push(Box::new(set_value.clone()));
self.resolve(local_env, &value.into(), set_value) self.resolve(local_env, &value.into(), set_value)
} else if let Some(value) = local_env.get(name).cloned() { } else if let Some(value) = local_env.get(name).cloned() {
// get value from local
self.resolve(local_env, &value, set_value) self.resolve(local_env, &value, set_value)
} else { } else {
false false
@ -53,11 +55,7 @@ impl EwwState {
} }
} }
pub fn resolve_into< pub fn resolve_into<TE: std::fmt::Debug, V: TryFrom<PrimitiveValue, Error = TE>, F: Fn(V) + 'static + Clone>(
TE: std::fmt::Debug,
V: TryFrom<PrimitiveValue, Error = TE>,
F: Fn(V) + 'static + Clone,
>(
&mut self, &mut self,
local_env: &HashMap<String, AttrValue>, local_env: &HashMap<String, AttrValue>,
value: &AttrValue, value: &AttrValue,

View file

@ -34,12 +34,22 @@ const EXAMPLE_CONFIG: &str = r#"{
layout: { layout: {
class: "container", class: "container",
children: [ children: [
"hi", { layout: {
{ button: { children: "click me you" } } orientation: "v"
{ slider: { value: "$$some_value", min: 0, max: 100, onchange: "notify-send 'changed' {}" } } children: [
{ slider: { value: "$$some_value", orientation: "vertical" } } "fancy button"
"hu" { button: { children: "reeee" } }
{ image: { path: "/home/leon/Bilder/ElCoward.png" } } ]
} }
{ 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"
]
} }
] ]
} }
} }
@ -103,12 +113,7 @@ fn try_main() -> Result<()> {
.get_display() .get_display()
.get_default_screen() .get_default_screen()
.get_rgba_visual() .get_rgba_visual()
.or_else(|| { .or_else(|| app_window.get_display().get_default_screen().get_system_visual())
app_window
.get_display()
.get_default_screen()
.get_system_visual()
})
.as_ref(), .as_ref(),
); );

View file

@ -20,6 +20,7 @@ impl TryFrom<PrimitiveValue> for String {
} }
} }
} }
impl TryFrom<PrimitiveValue> for f64 { impl TryFrom<PrimitiveValue> for f64 {
type Error = anyhow::Error; type Error = anyhow::Error;
fn try_from(x: PrimitiveValue) -> Result<Self> { 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> { fn try_from(value: &Hocon) -> Result<Self> {
Ok(match value { Ok(match value {
Hocon::String(s) if s.starts_with("$$") => { Hocon::String(s) if s.starts_with("$$") => {
return Err(anyhow!( return Err(anyhow!("Tried to use variable reference {} as primitive value", s))
"Tried to use variable reference {} as primitive value",
s
))
} }
Hocon::String(s) => PrimitiveValue::String(s.to_string()), Hocon::String(s) => PrimitiveValue::String(s.to_string()),
Hocon::Integer(n) => PrimitiveValue::Number(*n as f64), Hocon::Integer(n) => PrimitiveValue::Number(*n as f64),
@ -116,9 +114,7 @@ impl std::convert::TryFrom<&Hocon> for AttrValue {
type Error = anyhow::Error; type Error = anyhow::Error;
fn try_from(value: &Hocon) -> Result<Self> { fn try_from(value: &Hocon) -> Result<Self> {
Ok(match value { Ok(match value {
Hocon::String(s) if s.starts_with("$$") => { Hocon::String(s) if s.starts_with("$$") => AttrValue::VarRef(s.trim_start_matches("$$").to_string()),
AttrValue::VarRef(s.trim_start_matches("$$").to_string())
}
Hocon::String(s) => AttrValue::Concrete(PrimitiveValue::String(s.clone())), Hocon::String(s) => AttrValue::Concrete(PrimitiveValue::String(s.clone())),
Hocon::Integer(n) => AttrValue::Concrete(PrimitiveValue::Number(*n as f64)), Hocon::Integer(n) => AttrValue::Concrete(PrimitiveValue::Number(*n as f64)),
Hocon::Real(n) => AttrValue::Concrete(PrimitiveValue::Number(*n as f64)), Hocon::Real(n) => AttrValue::Concrete(PrimitiveValue::Number(*n as f64)),

View file

@ -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,
&gtk_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(&gtk::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
View 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, &gtk_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,
&gtk_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, &gtk_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);
}
}};
}

View 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: &gtk::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: &gtk::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: &gtk::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: &gtk::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(&gtk::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,
}
}