Simplify PrimitiveValue, add comments for doc-gen

This commit is contained in:
elkowar 2020-10-18 12:48:21 +02:00
parent de83f1fb69
commit c11cfaa26a
7 changed files with 173 additions and 119 deletions

View file

@ -33,11 +33,18 @@ pub enum EwwCommand {
PrintState(crossbeam_channel::Sender<String>), PrintState(crossbeam_channel::Sender<String>),
} }
#[derive(Debug, Clone, PartialEq)]
pub struct EwwWindow {
pub name: config::WindowName,
pub definition: config::EwwWindowDefinition,
pub gtk_window: gtk::Window,
}
#[derive(DebugStub)] #[derive(DebugStub)]
pub struct App { pub struct App {
pub eww_state: eww_state::EwwState, pub eww_state: eww_state::EwwState,
pub eww_config: config::EwwConfig, pub eww_config: config::EwwConfig,
pub windows: HashMap<config::WindowName, gtk::Window>, pub windows: HashMap<config::WindowName, EwwWindow>,
pub css_provider: gtk::CssProvider, pub css_provider: gtk::CssProvider,
pub app_evt_send: glib::Sender<EwwCommand>, pub app_evt_send: glib::Sender<EwwCommand>,
#[debug_stub = "ScriptVarHandler(...)"] #[debug_stub = "ScriptVarHandler(...)"]
@ -80,7 +87,7 @@ impl App {
.windows .windows
.remove(window_name) .remove(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.gtk_window.close();
self.eww_state.clear_window_state(window_name); self.eww_state.clear_window_state(window_name);
Ok(()) Ok(())
@ -146,7 +153,13 @@ impl App {
window.set_keep_below(true); window.set_keep_below(true);
} }
self.windows.insert(window_name.clone(), window); let eww_window = EwwWindow {
definition: window_def,
gtk_window: window,
name: window_name.clone(),
};
self.windows.insert(window_name.clone(), eww_window);
Ok(()) Ok(())
} }
@ -163,9 +176,9 @@ impl App {
let windows = self.windows.clone(); let windows = self.windows.clone();
for (window_name, window) in windows { for (window_name, window) in windows {
let old_pos = window.get_position(); let old_pos = window.definition.position;
let old_size = window.get_size(); let old_size = window.definition.size;
window.close(); window.gtk_window.close();
self.open_window(&window_name, Some(old_pos.into()), Some(old_size.into()))?; self.open_window(&window_name, Some(old_pos.into()), Some(old_size.into()))?;
} }
Ok(()) Ok(())

View file

@ -80,7 +80,7 @@ impl WidgetUse {
XmlNode::Text(text) if PATTERN.is_match(&text.text()) => WidgetUse::from_text_with_var_refs(&text.text()), XmlNode::Text(text) if PATTERN.is_match(&text.text()) => WidgetUse::from_text_with_var_refs(&text.text()),
XmlNode::Text(text) => WidgetUse::simple_text(AttrValue::parse_string(text.text())), XmlNode::Text(text) => WidgetUse::simple_text(AttrValue::parse_string(text.text())),
XmlNode::Element(elem) => WidgetUse { XmlNode::Element(elem) => WidgetUse {
name: elem.tag_name().to_string(), name: elem.tag_name().to_owned(),
children: with_text_pos_context! { elem => elem.children().map(WidgetUse::from_xml_node).collect::<Result<_>>()?}?, children: with_text_pos_context! { elem => elem.children().map(WidgetUse::from_xml_node).collect::<Result<_>>()?}?,
attrs: elem attrs: elem
.attributes() .attributes()
@ -98,7 +98,7 @@ impl WidgetUse {
WidgetUse { WidgetUse {
name: "label".to_owned(), name: "label".to_owned(),
children: vec![], children: vec![],
attrs: hashmap! { "text".to_string() => text }, // TODO this hardcoded "text" is dumdum attrs: hashmap! { "text".to_owned() => text }, // TODO this hardcoded "text" is dumdum
..WidgetUse::default() ..WidgetUse::default()
} }
} }
@ -136,7 +136,7 @@ enum StringOrVarRef {
impl StringOrVarRef { impl StringOrVarRef {
fn to_attr_value(self) -> AttrValue { fn to_attr_value(self) -> AttrValue {
match self { match self {
StringOrVarRef::String(x) => AttrValue::Concrete(PrimitiveValue::parse_string(&x)), StringOrVarRef::String(x) => AttrValue::Concrete(PrimitiveValue::from_string(x)),
StringOrVarRef::VarRef(x) => AttrValue::VarRef(VarName(x)), StringOrVarRef::VarRef(x) => AttrValue::VarRef(VarName(x)),
} }
} }
@ -190,12 +190,12 @@ mod test {
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
fn mk_attr_str(s: &str) -> AttrValue { fn mk_attr_str(s: &str) -> AttrValue {
AttrValue::Concrete(PrimitiveValue::String(s.to_owned())) AttrValue::Concrete(PrimitiveValue::from_string(s.to_owned()))
} }
#[test] #[test]
fn test_simple_text() { fn test_simple_text() {
let expected_attr_value = AttrValue::Concrete(PrimitiveValue::String("my text".to_owned())); let expected_attr_value = AttrValue::Concrete(PrimitiveValue::from_string("my text".to_owned()));
let widget = WidgetUse::simple_text(expected_attr_value.clone()); let widget = WidgetUse::simple_text(expected_attr_value.clone());
assert_eq!( assert_eq!(
widget, widget,
@ -244,12 +244,12 @@ mod test {
let expected = WidgetUse { let expected = WidgetUse {
name: "widget_name".to_owned(), name: "widget_name".to_owned(),
attrs: hashmap! { attrs: hashmap! {
"attr1".to_owned() => AttrValue::Concrete(PrimitiveValue::String("hi".to_owned())), "attr1".to_owned() => AttrValue::Concrete(PrimitiveValue::from_string("hi".to_owned())),
"attr2".to_owned() => AttrValue::Concrete(PrimitiveValue::Number(12f64)), "attr2".to_owned() => AttrValue::Concrete(PrimitiveValue::from_string("12".to_owned())),
}, },
children: vec![ children: vec![
WidgetUse::new("child_widget".to_owned(), Vec::new()), WidgetUse::new("child_widget".to_owned(), Vec::new()),
WidgetUse::simple_text(AttrValue::Concrete(PrimitiveValue::String("foo".to_owned()))), WidgetUse::simple_text(AttrValue::Concrete(PrimitiveValue::from_string("foo".to_owned()))),
], ],
..WidgetUse::default() ..WidgetUse::default()
}; };
@ -271,7 +271,7 @@ mod test {
size: Some((12, 20)), size: Some((12, 20)),
structure: WidgetUse { structure: WidgetUse {
name: "layout".to_owned(), name: "layout".to_owned(),
children: vec![WidgetUse::simple_text(AttrValue::Concrete(PrimitiveValue::String( children: vec![WidgetUse::simple_text(AttrValue::Concrete(PrimitiveValue::from_string(
"test".to_owned(), "test".to_owned(),
)))], )))],
attrs: HashMap::new(), attrs: HashMap::new(),

View file

@ -58,7 +58,7 @@ impl ScriptVar {
pub fn initial_value(&self) -> Result<PrimitiveValue> { pub fn initial_value(&self) -> Result<PrimitiveValue> {
match self { match self {
ScriptVar::Poll(x) => Ok(crate::run_command(&x.command)?), ScriptVar::Poll(x) => Ok(crate::run_command(&x.command)?),
ScriptVar::Tail(_) => Ok(PrimitiveValue::String(String::new())), ScriptVar::Tail(_) => Ok(PrimitiveValue::from_string(String::new())),
} }
} }
@ -126,9 +126,8 @@ impl EwwConfig {
"var" => { "var" => {
initial_variables.insert( initial_variables.insert(
VarName(node.attr("name")?.to_owned()), VarName(node.attr("name")?.to_owned()),
PrimitiveValue::parse_string( PrimitiveValue::from_string(
&node node.only_child()
.only_child()
.map(|c| c.as_text_or_sourcecode()) .map(|c| c.as_text_or_sourcecode())
.unwrap_or_else(|_| String::new()), .unwrap_or_else(|_| String::new()),
), ),

View file

@ -115,7 +115,7 @@ impl ScriptVarHandler {
handle.read_line(&mut buffer)?; handle.read_line(&mut buffer)?;
evt_send.send(app::EwwCommand::UpdateVar( evt_send.send(app::EwwCommand::UpdateVar(
var_name.clone(), var_name.clone(),
PrimitiveValue::parse_string(&buffer), PrimitiveValue::from_string(buffer),
))?; ))?;
} else if event.hangup { } else if event.hangup {
command_out_readers.remove(var_name); command_out_readers.remove(var_name);

View file

@ -5,6 +5,23 @@ use itertools::Itertools;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{fmt, path::Path}; use std::{fmt, path::Path};
#[macro_export]
macro_rules! impl_many {
($trait:ident<$typ:ty> $fn_name:ident{
$(
for $for:ty => |$var:ident| $code:expr
);*;
}) => {
$(impl $trait<$typ> for $for {
type Error = anyhow::Error;
fn $fn_name($var: $typ) -> Result<Self> {
$code
}
})*
}
}
/// read an scss file, replace all environment variable references within it and /// read an scss file, replace all environment variable references within it and
/// then parse it into css. /// then parse it into css.
pub fn parse_scss_from_file<P: AsRef<Path>>(path: P) -> Result<String> { pub fn parse_scss_from_file<P: AsRef<Path>>(path: P) -> Result<String> {

View file

@ -6,20 +6,14 @@ use regex::Regex;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{convert::TryFrom, fmt}; use std::{convert::TryFrom, fmt};
use crate::impl_many;
#[derive(Clone, PartialEq, Deserialize, Serialize, derive_more::From)] #[derive(Clone, PartialEq, Deserialize, Serialize, derive_more::From)]
pub enum PrimitiveValue { pub struct PrimitiveValue(String);
String(String),
Number(f64),
Boolean(bool),
}
impl fmt::Display for PrimitiveValue { impl fmt::Display for PrimitiveValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { write!(f, "\"{}\"", self.0)
PrimitiveValue::String(s) => write!(f, "\"{}\"", s),
PrimitiveValue::Number(n) => write!(f, "{}", n),
PrimitiveValue::Boolean(b) => write!(f, "{}", b),
}
} }
} }
impl fmt::Debug for PrimitiveValue { impl fmt::Debug for PrimitiveValue {
@ -34,80 +28,60 @@ impl std::str::FromStr for PrimitiveValue {
/// parses the value, trying to turn it into a number and a boolean first, /// parses the value, trying to turn it into a number and a boolean first,
/// before deciding that it is a string. /// before deciding that it is a string.
fn from_str(s: &str) -> Result<Self> { fn from_str(s: &str) -> Result<Self> {
Ok(PrimitiveValue::parse_string(s)) Ok(PrimitiveValue::from_string(s.to_string()))
} }
} }
fn remove_surrounding(s: &str, surround: char) -> &str { impl_many!(TryFrom<PrimitiveValue> try_from {
s.strip_prefix(surround).unwrap_or(s).strip_suffix(surround).unwrap_or(s) for String => |x| x.as_string();
} for f64 => |x| x.as_f64();
for bool => |x| x.as_bool();
});
impl TryFrom<PrimitiveValue> for String { impl From<i32> for PrimitiveValue {
type Error = anyhow::Error; fn from(x: i32) -> Self {
PrimitiveValue(format!("{}", x))
fn try_from(x: PrimitiveValue) -> Result<Self> {
x.as_string()
} }
} }
impl TryFrom<PrimitiveValue> for f64 { impl From<bool> for PrimitiveValue {
type Error = anyhow::Error; fn from(x: bool) -> Self {
PrimitiveValue(format!("{}", x))
fn try_from(x: PrimitiveValue) -> Result<Self> {
x.as_f64()
}
}
impl TryFrom<PrimitiveValue> for bool {
type Error = anyhow::Error;
fn try_from(x: PrimitiveValue) -> Result<Self> {
x.as_bool()
} }
} }
impl From<&str> for PrimitiveValue { impl From<&str> for PrimitiveValue {
fn from(s: &str) -> Self { fn from(s: &str) -> Self {
PrimitiveValue::String(s.to_string()) PrimitiveValue(s.to_string())
} }
} }
impl PrimitiveValue { impl PrimitiveValue {
/// parses the value, trying to turn it into a number and a boolean first, pub fn from_string(s: String) -> Self {
/// before deciding that it is a string. PrimitiveValue(s.to_string())
pub fn parse_string(s: &str) -> Self {
s.parse()
.map(PrimitiveValue::Number)
.or_else(|_| s.parse().map(PrimitiveValue::Boolean))
.unwrap_or_else(|_| PrimitiveValue::String(remove_surrounding(s, '\'').to_string()))
} }
/// This will never fail
pub fn as_string(&self) -> Result<String> { pub fn as_string(&self) -> Result<String> {
match self { Ok(self.0.to_owned())
PrimitiveValue::String(x) => Ok(x.clone()),
PrimitiveValue::Number(x) => Ok(format!("{}", x)),
PrimitiveValue::Boolean(x) => Ok(format!("{}", x)),
}
} }
pub fn as_f64(&self) -> Result<f64> { pub fn as_f64(&self) -> Result<f64> {
match self { self.0
PrimitiveValue::Number(x) => Ok(*x),
PrimitiveValue::String(x) => x
.parse() .parse()
.map_err(|e| anyhow!("couldn't convert string {:?} to f64: {}", &self, e)), .map_err(|e| anyhow!("couldn't convert {:?} to f64: {}", &self, e))
_ => Err(anyhow!("{:?} is not an f64", &self)),
} }
pub fn as_i32(&self) -> Result<i32> {
self.0
.parse()
.map_err(|e| anyhow!("couldn't convert {:?} to i32: {}", &self, e))
} }
pub fn as_bool(&self) -> Result<bool> { pub fn as_bool(&self) -> Result<bool> {
match self { self.0
PrimitiveValue::Boolean(x) => Ok(*x),
PrimitiveValue::String(x) => x
.parse() .parse()
.map_err(|e| anyhow!("couldn't convert string {:?} to bool: {}", &self, e)), .map_err(|e| anyhow!("couldn't convert {:?} to bool: {}", &self, e))
_ => Err(anyhow!("{:?} is not a string", &self)),
}
} }
} }
@ -148,21 +122,28 @@ pub enum AttrValue {
impl AttrValue { impl AttrValue {
pub fn as_string(&self) -> Result<String> { pub fn as_string(&self) -> Result<String> {
match self { match self {
AttrValue::Concrete(x) => Ok(x.as_string()?), AttrValue::Concrete(x) => x.as_string(),
_ => Err(anyhow!("{:?} is not a string", self)), _ => Err(anyhow!("{:?} is not a string", self)),
} }
} }
pub fn as_f64(&self) -> Result<f64> { pub fn as_f64(&self) -> Result<f64> {
match self { match self {
AttrValue::Concrete(x) => Ok(x.as_f64()?), AttrValue::Concrete(x) => x.as_f64(),
_ => Err(anyhow!("{:?} is not an f64", self)), _ => Err(anyhow!("{:?} is not an f64", self)),
} }
} }
pub fn as_i32(&self) -> Result<i32> {
match self {
AttrValue::Concrete(x) => x.as_i32(),
_ => Err(anyhow!("{:?} is not an i32", self)),
}
}
pub fn as_bool(&self) -> Result<bool> { pub fn as_bool(&self) -> Result<bool> {
match self { match self {
AttrValue::Concrete(x) => Ok(x.as_bool()?), AttrValue::Concrete(x) => x.as_bool(),
_ => Err(anyhow!("{:?} is not a bool", self)), _ => Err(anyhow!("{:?} is not a bool", self)),
} }
} }
@ -184,7 +165,7 @@ impl AttrValue {
if let Some(ref_name) = PATTERN.captures(&s).and_then(|cap| cap.get(1)).map(|x| x.as_str()) { if let Some(ref_name) = PATTERN.captures(&s).and_then(|cap| cap.get(1)).map(|x| x.as_str()) {
AttrValue::VarRef(VarName(ref_name.to_owned())) AttrValue::VarRef(VarName(ref_name.to_owned()))
} else { } else {
AttrValue::Concrete(PrimitiveValue::String(s)) AttrValue::Concrete(PrimitiveValue::from_string(s))
} }
} }
} }

View file

@ -5,7 +5,7 @@ use crate::{
}; };
use anyhow::*; use anyhow::*;
use gtk::{prelude::*, ImageExt}; use gtk::{prelude::*, ImageExt};
use std::{cell::RefCell, path::Path, rc::Rc}; use std::{cell::RefCell, rc::Rc};
use gdk_pixbuf; use gdk_pixbuf;
@ -17,12 +17,11 @@ use gdk_pixbuf;
pub(super) fn widget_to_gtk_widget(bargs: &mut BuilderArgs) -> Result<Option<gtk::Widget>> { pub(super) fn widget_to_gtk_widget(bargs: &mut BuilderArgs) -> Result<Option<gtk::Widget>> {
let gtk_widget = match bargs.widget.name.as_str() { let gtk_widget = match bargs.widget.name.as_str() {
"box" => build_gtk_box(bargs)?.upcast(), "box" => build_gtk_box(bargs)?.upcast(),
"slider" => build_gtk_scale(bargs)?.upcast(), "scale" => build_gtk_scale(bargs)?.upcast(),
"image" => build_gtk_image(bargs)?.upcast(), "image" => build_gtk_image(bargs)?.upcast(),
"button" => build_gtk_button(bargs)?.upcast(), "button" => build_gtk_button(bargs)?.upcast(),
"label" => build_gtk_label(bargs)?.upcast(), "label" => build_gtk_label(bargs)?.upcast(),
"text" => build_gtk_text(bargs)?.upcast(), "text" => build_gtk_text(bargs)?.upcast(),
"aspect" => build_gtk_aspect_frame(bargs)?.upcast(),
"literal" => build_gtk_literal(bargs)?.upcast(), "literal" => build_gtk_literal(bargs)?.upcast(),
"input" => build_gtk_input(bargs)?.upcast(), "input" => build_gtk_input(bargs)?.upcast(),
"calendar" => build_gtk_calendar(bargs)?.upcast(), "calendar" => build_gtk_calendar(bargs)?.upcast(),
@ -32,6 +31,7 @@ pub(super) fn widget_to_gtk_widget(bargs: &mut BuilderArgs) -> Result<Option<gtk
} }
/// attributes that apply to all widgets /// attributes that apply to all widgets
/// @widget !widget
pub(super) fn resolve_widget_attrs(bargs: &mut BuilderArgs, gtk_widget: &gtk::Widget) { pub(super) fn resolve_widget_attrs(bargs: &mut BuilderArgs, gtk_widget: &gtk::Widget) {
let css_provider = gtk::CssProvider::new(); let css_provider = gtk::CssProvider::new();
@ -53,21 +53,30 @@ pub(super) fn resolve_widget_attrs(bargs: &mut BuilderArgs, gtk_widget: &gtk::Wi
let on_hover_handler_id: Rc<RefCell<Option<glib::SignalHandlerId>>> = Rc::new(RefCell::new(None)); let on_hover_handler_id: Rc<RefCell<Option<glib::SignalHandlerId>>> = Rc::new(RefCell::new(None));
resolve_block!(bargs, gtk_widget, { resolve_block!(bargs, gtk_widget, {
// @prop class - css class name
prop(class: as_string) { gtk_widget.get_style_context().add_class(&class) }, prop(class: as_string) { gtk_widget.get_style_context().add_class(&class) },
// @prop valign - how to align this vertically. possible values: $alignment
prop(valign: as_string) { gtk_widget.set_valign(parse_align(&valign)?) }, prop(valign: as_string) { gtk_widget.set_valign(parse_align(&valign)?) },
// @prop halign - how to align this horizontally. possible values: $alignment
prop(halign: as_string) { gtk_widget.set_halign(parse_align(&halign)?) }, prop(halign: as_string) { gtk_widget.set_halign(parse_align(&halign)?) },
// @prop width - width of this element. note that this can not restrict the size if the contents stretch it
prop(width: as_f64) { gtk_widget.set_size_request(width as i32, gtk_widget.get_allocated_height()) }, prop(width: as_f64) { gtk_widget.set_size_request(width as i32, gtk_widget.get_allocated_height()) },
// @prop height - height of this element. note that this can not restrict the size if the contents stretch it
prop(height: as_f64) { gtk_widget.set_size_request(gtk_widget.get_allocated_width(), height as i32) }, prop(height: as_f64) { gtk_widget.set_size_request(gtk_widget.get_allocated_width(), height as i32) },
// @prop active - If this widget can be interacted with
prop(active: as_bool = true) { gtk_widget.set_sensitive(active) }, prop(active: as_bool = true) { gtk_widget.set_sensitive(active) },
// @prop visible - visibility of the widget
prop(visible: as_bool = true) { prop(visible: as_bool = true) {
// TODO how do i call this only after the widget has been mapped? this is actually an issue,.... // TODO how do i call this only after the widget has been mapped? this is actually an issue,....
if visible { gtk_widget.show(); } else { gtk_widget.hide(); } if visible { gtk_widget.show(); } else { gtk_widget.hide(); }
}, },
// @prop style - inline css style applied to the widget
prop(style: as_string) { prop(style: as_string) {
gtk_widget.reset_style(); gtk_widget.reset_style();
css_provider.load_from_data(format!("* {{ {} }}", style).as_bytes())?; css_provider.load_from_data(format!("* {{ {} }}", style).as_bytes())?;
gtk_widget.get_style_context().add_provider(&css_provider, gtk::STYLE_PROVIDER_PRIORITY_APPLICATION) gtk_widget.get_style_context().add_provider(&css_provider, gtk::STYLE_PROVIDER_PRIORITY_APPLICATION)
}, },
// @prop onscroll - event to execute when the user scrolls with the mouse over the widget
prop(onscroll: as_string) { prop(onscroll: as_string) {
gtk_widget.add_events(gdk::EventMask::SCROLL_MASK); gtk_widget.add_events(gdk::EventMask::SCROLL_MASK);
gtk_widget.add_events(gdk::EventMask::SMOOTH_SCROLL_MASK); gtk_widget.add_events(gdk::EventMask::SMOOTH_SCROLL_MASK);
@ -79,6 +88,7 @@ pub(super) fn resolve_widget_attrs(bargs: &mut BuilderArgs, gtk_widget: &gtk::Wi
)); ));
old_id.map(|id| gtk_widget.disconnect(id)); old_id.map(|id| gtk_widget.disconnect(id));
}, },
// @prop onhover - event to execute when the user hovers over the widget
prop(onhover: as_string) { prop(onhover: as_string) {
gtk_widget.add_events(gdk::EventMask::ENTER_NOTIFY_MASK); gtk_widget.add_events(gdk::EventMask::ENTER_NOTIFY_MASK);
let old_id = on_hover_handler_id.replace(Some( let old_id = on_hover_handler_id.replace(Some(
@ -92,22 +102,28 @@ pub(super) fn resolve_widget_attrs(bargs: &mut BuilderArgs, gtk_widget: &gtk::Wi
}); });
} }
/// attributes that apply to all container widgets /// @widget !container
pub(super) fn resolve_container_attrs(bargs: &mut BuilderArgs, gtk_widget: &gtk::Container) { pub(super) fn resolve_container_attrs(bargs: &mut BuilderArgs, gtk_widget: &gtk::Container) {
resolve_block!(bargs, gtk_widget, { resolve_block!(bargs, gtk_widget, {
// @prop vexpand - should this container expand vertically
prop(vexpand: as_bool = false) { gtk_widget.set_vexpand(vexpand) }, prop(vexpand: as_bool = false) { gtk_widget.set_vexpand(vexpand) },
// @prop hexpand - should this container expand horizontally
prop(hexpand: as_bool = false) { gtk_widget.set_hexpand(hexpand) }, prop(hexpand: as_bool = false) { gtk_widget.set_hexpand(hexpand) },
}); });
} }
/// @widget !range
pub(super) fn resolve_range_attrs(bargs: &mut BuilderArgs, gtk_widget: &gtk::Range) { pub(super) fn resolve_range_attrs(bargs: &mut BuilderArgs, gtk_widget: &gtk::Range) {
let on_change_handler_id: Rc<RefCell<Option<glib::SignalHandlerId>>> = Rc::new(RefCell::new(None)); let on_change_handler_id: Rc<RefCell<Option<glib::SignalHandlerId>>> = Rc::new(RefCell::new(None));
gtk_widget.set_sensitive(false); gtk_widget.set_sensitive(false);
resolve_block!(bargs, gtk_widget, { resolve_block!(bargs, gtk_widget, {
// @prop value - the value
prop(value: as_f64) { gtk_widget.set_value(value)}, prop(value: as_f64) { gtk_widget.set_value(value)},
// @prop min - the minimum value
prop(min: as_f64) { gtk_widget.get_adjustment().set_lower(min)}, prop(min: as_f64) { gtk_widget.get_adjustment().set_lower(min)},
// @prop max - the maximum value
prop(max: as_f64) { gtk_widget.get_adjustment().set_upper(max)}, prop(max: as_f64) { gtk_widget.get_adjustment().set_upper(max)},
prop(orientation : as_string) { gtk_widget.set_orientation(parse_orientation(&orientation)?) }, // @prop onchange - command executed once the value is changes. The placeholder `{}`, used in the command will be replaced by the new value.
prop(onchange: as_string) { prop(onchange: as_string) {
gtk_widget.set_sensitive(true); gtk_widget.set_sensitive(true);
gtk_widget.add_events(gdk::EventMask::ENTER_NOTIFY_MASK); gtk_widget.add_events(gdk::EventMask::ENTER_NOTIFY_MASK);
@ -122,26 +138,35 @@ pub(super) fn resolve_range_attrs(bargs: &mut BuilderArgs, gtk_widget: &gtk::Ran
}); });
} }
/// @widget !orientable
pub(super) fn resolve_orientable_attrs(bargs: &mut BuilderArgs, gtk_widget: &gtk::Range) { pub(super) fn resolve_orientable_attrs(bargs: &mut BuilderArgs, gtk_widget: &gtk::Range) {
resolve_block!(bargs, gtk_widget, { resolve_block!(bargs, gtk_widget, {
// @prop orientation - orientation of the widget. Possible values: $orientation
prop(orientation: as_string) { gtk_widget.set_orientation(parse_orientation(&orientation)?) }, prop(orientation: as_string) { gtk_widget.set_orientation(parse_orientation(&orientation)?) },
}); });
} }
// concrete widgets // concrete widgets
/// @widget scale extends range
/// @desc a slider.
fn build_gtk_scale(bargs: &mut BuilderArgs) -> Result<gtk::Scale> { fn build_gtk_scale(bargs: &mut BuilderArgs) -> Result<gtk::Scale> {
let gtk_widget = gtk::Scale::new( let gtk_widget = gtk::Scale::new(
gtk::Orientation::Horizontal, gtk::Orientation::Horizontal,
Some(&gtk::Adjustment::new(0.0, 0.0, 100.0, 1.0, 1.0, 1.0)), Some(&gtk::Adjustment::new(0.0, 0.0, 100.0, 1.0, 1.0, 1.0)),
); );
resolve_block!(bargs, gtk_widget, { resolve_block!(bargs, gtk_widget, {
// @prop flipped - flip the direction
prop(flipped: as_bool) { gtk_widget.set_inverted(flipped) }, prop(flipped: as_bool) { gtk_widget.set_inverted(flipped) },
// @prop draw-value - draw the value of the property
prop(draw_value: as_bool = false) { gtk_widget.set_draw_value(draw_value) }, prop(draw_value: as_bool = false) { gtk_widget.set_draw_value(draw_value) },
}); });
Ok(gtk_widget) Ok(gtk_widget)
} }
/// @widget input
/// @desc an input field that doesn't yet really work
fn build_gtk_input(bargs: &mut BuilderArgs) -> Result<gtk::Entry> { fn build_gtk_input(bargs: &mut BuilderArgs) -> Result<gtk::Entry> {
let gtk_widget = gtk::Entry::new(); let gtk_widget = gtk::Entry::new();
let on_change_handler_id: Rc<RefCell<Option<glib::SignalHandlerId>>> = Rc::new(RefCell::new(None)); let on_change_handler_id: Rc<RefCell<Option<glib::SignalHandlerId>>> = Rc::new(RefCell::new(None));
@ -162,10 +187,12 @@ fn build_gtk_input(bargs: &mut BuilderArgs) -> Result<gtk::Entry> {
Ok(gtk_widget) Ok(gtk_widget)
} }
/// @widget button extends container
fn build_gtk_button(bargs: &mut BuilderArgs) -> Result<gtk::Button> { fn build_gtk_button(bargs: &mut BuilderArgs) -> Result<gtk::Button> {
let gtk_widget = gtk::Button::new(); let gtk_widget = gtk::Button::new();
let on_click_handler_id: Rc<RefCell<Option<glib::SignalHandlerId>>> = Rc::new(RefCell::new(None)); let on_click_handler_id: Rc<RefCell<Option<glib::SignalHandlerId>>> = Rc::new(RefCell::new(None));
resolve_block!(bargs, gtk_widget, { resolve_block!(bargs, gtk_widget, {
// @prop onclick - a command that get's run when the button is clicked
prop(onclick: as_string) { prop(onclick: as_string) {
gtk_widget.add_events(gdk::EventMask::ENTER_NOTIFY_MASK); gtk_widget.add_events(gdk::EventMask::ENTER_NOTIFY_MASK);
let old_id = on_click_handler_id.replace(Some( let old_id = on_click_handler_id.replace(Some(
@ -178,38 +205,47 @@ fn build_gtk_button(bargs: &mut BuilderArgs) -> Result<gtk::Button> {
Ok(gtk_widget) Ok(gtk_widget)
} }
/// @widget image
fn build_gtk_image(bargs: &mut BuilderArgs) -> Result<gtk::Image> { fn build_gtk_image(bargs: &mut BuilderArgs) -> Result<gtk::Image> {
let gtk_widget = gtk::Image::new(); let gtk_widget = gtk::Image::new();
resolve_block!(bargs, gtk_widget, { resolve_block!(bargs, gtk_widget, {
prop(path: as_string) { // @prop path - path to the image file
gtk_widget.set_from_file(Path::new(&path)); // @prop width - width of the image
}, // @prop height - height of the image
prop(path: as_string, width: as_f64, height: as_f64) { prop(path: as_string, width: as_i32 = 10000, height: as_i32 = 10000) {
let pixbuf = gdk_pixbuf::Pixbuf::from_file_at_size(std::path::PathBuf::from(path), width as i32, height as i32)?; let pixbuf = gdk_pixbuf::Pixbuf::from_file_at_size(std::path::PathBuf::from(path), width, height)?;
gtk_widget.set_from_pixbuf(Some(&pixbuf)); gtk_widget.set_from_pixbuf(Some(&pixbuf));
} }
}); });
Ok(gtk_widget) Ok(gtk_widget)
} }
/// @widget box extends container
/// @desc the main layout container
fn build_gtk_box(bargs: &mut BuilderArgs) -> Result<gtk::Box> { fn build_gtk_box(bargs: &mut BuilderArgs) -> Result<gtk::Box> {
let gtk_widget = gtk::Box::new(gtk::Orientation::Horizontal, 0); let gtk_widget = gtk::Box::new(gtk::Orientation::Horizontal, 0);
resolve_block!(bargs, gtk_widget, { resolve_block!(bargs, gtk_widget, {
prop(spacing: as_f64 = 0.0) { gtk_widget.set_spacing(spacing as i32) }, // @prop spacing - spacing between elements
prop(spacing: as_i32 = 0) { gtk_widget.set_spacing(spacing) },
// @prop orientation - orientation of the box. possible values: $orientation
prop(orientation: as_string) { gtk_widget.set_orientation(parse_orientation(&orientation)?) }, prop(orientation: as_string) { gtk_widget.set_orientation(parse_orientation(&orientation)?) },
// @prop space-evenly - space the widgets evenly.
prop(space_evenly: as_bool = true) { gtk_widget.set_homogeneous(space_evenly) }, prop(space_evenly: as_bool = true) { gtk_widget.set_homogeneous(space_evenly) },
}); });
Ok(gtk_widget) Ok(gtk_widget)
} }
/// @widget label
fn build_gtk_label(bargs: &mut BuilderArgs) -> Result<gtk::Label> { fn build_gtk_label(bargs: &mut BuilderArgs) -> Result<gtk::Label> {
let gtk_widget = gtk::Label::new(None); let gtk_widget = gtk::Label::new(None);
resolve_block!(bargs, gtk_widget, { resolve_block!(bargs, gtk_widget, {
// @prop - the text to display
prop(text: as_string) { gtk_widget.set_text(&text) }, prop(text: as_string) { gtk_widget.set_text(&text) },
}); });
Ok(gtk_widget) Ok(gtk_widget)
} }
/// @widget text
fn build_gtk_text(_bargs: &mut BuilderArgs) -> Result<gtk::Box> { fn build_gtk_text(_bargs: &mut BuilderArgs) -> Result<gtk::Box> {
let gtk_widget = gtk::Box::new(gtk::Orientation::Horizontal, 0); let gtk_widget = gtk::Box::new(gtk::Orientation::Horizontal, 0);
gtk_widget.set_halign(gtk::Align::Center); gtk_widget.set_halign(gtk::Align::Center);
@ -217,12 +253,15 @@ fn build_gtk_text(_bargs: &mut BuilderArgs) -> Result<gtk::Box> {
Ok(gtk_widget) Ok(gtk_widget)
} }
/// @widget literal
/// @desc a tag that allows you to render arbitrary XML.
fn build_gtk_literal(bargs: &mut BuilderArgs) -> Result<gtk::Frame> { fn build_gtk_literal(bargs: &mut BuilderArgs) -> Result<gtk::Frame> {
let gtk_widget = gtk::Frame::new(None); let gtk_widget = gtk::Frame::new(None);
// TODO these clones here are dumdum // TODO these clones here are dumdum
let window_name = bargs.window_name.clone(); let window_name = bargs.window_name.clone();
let widget_definitions = bargs.widget_definitions.clone(); let widget_definitions = bargs.widget_definitions.clone();
resolve_block!(bargs, gtk_widget, { resolve_block!(bargs, gtk_widget, {
// @prop - inline Eww XML that will be rendered as a widget.
prop(content: as_string) { prop(content: as_string) {
gtk_widget.get_children().iter().for_each(|w| gtk_widget.remove(w)); gtk_widget.get_children().iter().for_each(|w| gtk_widget.remove(w));
if !content.is_empty() { if !content.is_empty() {
@ -243,18 +282,26 @@ fn build_gtk_literal(bargs: &mut BuilderArgs) -> Result<gtk::Frame> {
Ok(gtk_widget) Ok(gtk_widget)
} }
/// @widget calendar
fn build_gtk_calendar(bargs: &mut BuilderArgs) -> Result<gtk::Calendar> { fn build_gtk_calendar(bargs: &mut BuilderArgs) -> Result<gtk::Calendar> {
let gtk_widget = gtk::Calendar::new(); let gtk_widget = gtk::Calendar::new();
let on_click_handler_id: Rc<RefCell<Option<glib::SignalHandlerId>>> = Rc::new(RefCell::new(None)); let on_click_handler_id: Rc<RefCell<Option<glib::SignalHandlerId>>> = Rc::new(RefCell::new(None));
resolve_block!(bargs, gtk_widget, { resolve_block!(bargs, gtk_widget, {
// @prop day - the selected day
prop(day: as_f64) { gtk_widget.set_property_day(day as i32) }, prop(day: as_f64) { gtk_widget.set_property_day(day as i32) },
// @prop month - the selected month
prop(month: as_f64) { gtk_widget.set_property_day(month as i32) }, prop(month: as_f64) { gtk_widget.set_property_day(month as i32) },
// @prop year - the selected year
prop(year: as_f64) { gtk_widget.set_property_day(year as i32) }, prop(year: as_f64) { gtk_widget.set_property_day(year as i32) },
// @prop show-details - show details
prop(show_details: as_bool) { gtk_widget.set_property_show_details(show_details) }, prop(show_details: as_bool) { gtk_widget.set_property_show_details(show_details) },
prop(show_heading: as_bool) { gtk_widget.set_property_show_details(show_heading) }, // @prop show-heading - show heading line
prop(show_day_names: as_bool) { gtk_widget.set_property_show_day_names(show_day_names) },
prop(show_week_numbers: as_bool) { gtk_widget.set_property_show_week_numbers(show_week_numbers) },
prop(show_heading: as_bool) { gtk_widget.set_property_show_heading(show_heading) }, prop(show_heading: as_bool) { gtk_widget.set_property_show_heading(show_heading) },
// @prop show-day-names - show names of days
prop(show_day_names: as_bool) { gtk_widget.set_property_show_day_names(show_day_names) },
// @prop show-week-numbers - show week numbers
prop(show_week_numbers: as_bool) { gtk_widget.set_property_show_week_numbers(show_week_numbers) },
// @prop onclick - command to run when the user selects a date. The `{}` placeholder will be replaced by the selected date.
prop(onclick: as_string) { prop(onclick: as_string) {
let old_id = on_click_handler_id.replace(Some( let old_id = on_click_handler_id.replace(Some(
gtk_widget.connect_day_selected(move |w| { gtk_widget.connect_day_selected(move |w| {
@ -272,11 +319,7 @@ fn build_gtk_calendar(bargs: &mut BuilderArgs) -> Result<gtk::Calendar> {
Ok(gtk_widget) Ok(gtk_widget)
} }
fn build_gtk_aspect_frame(_bargs: &mut BuilderArgs) -> Result<gtk::AspectFrame> { /// @var orientation - "vertical", "v", "horizontal", "h"
let gtk_widget = gtk::AspectFrame::new(None, 0.5, 0.5, 1.0, true);
Ok(gtk_widget)
}
fn parse_orientation(o: &str) -> Result<gtk::Orientation> { fn parse_orientation(o: &str) -> Result<gtk::Orientation> {
Ok(match o { Ok(match o {
"vertical" | "v" => gtk::Orientation::Vertical, "vertical" | "v" => gtk::Orientation::Vertical,
@ -285,6 +328,7 @@ fn parse_orientation(o: &str) -> Result<gtk::Orientation> {
}) })
} }
/// @var align - "fill", "baseline", "center", "start", "end"
fn parse_align(o: &str) -> Result<gtk::Align> { fn parse_align(o: &str) -> Result<gtk::Align> {
Ok(match o { Ok(match o {
"fill" => gtk::Align::Fill, "fill" => gtk::Align::Fill,