Combo box and as_vec() (#41)

Add `as_vec` for PrimitiveValues and implement the `combo-box` widget
This commit is contained in:
ay9thqi3tbqiwbegqsg a[soiaosshasdg 2020-10-25 09:56:45 +00:00 committed by GitHub
parent 4b86f84479
commit 24e3a36c52
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 90 additions and 0 deletions

View file

@ -40,6 +40,7 @@ impl_try_from!(PrimitiveValue {
for String => |x| x.as_string();
for f64 => |x| x.as_f64();
for bool => |x| x.as_bool();
for Vec<String> => |x| x.as_vec();
});
impl From<bool> for PrimitiveValue {
@ -91,4 +92,68 @@ impl PrimitiveValue {
.parse()
.map_err(|e| anyhow!("couldn't convert {:?} to bool: {}", &self, e))
}
pub fn as_vec(&self) -> Result<Vec<String>> {
parse_vec(self.0.to_owned()).map_err(|e| anyhow!("Couldn't convert {:#?} to a vec: {}", &self, e))
}
}
fn parse_vec(a: String) -> Result<Vec<String>> {
match a.strip_prefix('[').and_then(|x| x.strip_suffix(']')) {
Some(content) => {
let mut items: Vec<String> = content.split(',').map(|x: &str| x.to_string()).collect();
let mut removed = 0;
for times_ran in 0..items.len() {
// escapes `,` if there's a `\` before em
if items[times_ran - removed].ends_with("\\") {
items[times_ran - removed].pop();
let it = items.remove((times_ran + 1) - removed);
items[times_ran - removed] += ",";
items[times_ran - removed] += &it;
removed += 1;
}
}
Ok(items)
}
None => Err(anyhow!("Is your array built like this: '[these,are,items]'?")),
}
}
#[cfg(test)]
mod test {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn test_parse_vec() {
assert_eq!(
vec![""],
parse_vec("[]".to_string()).unwrap(),
"should be able to parse empty lists"
);
assert_eq!(
vec!["hi"],
parse_vec("[hi]".to_string()).unwrap(),
"should be able to parse single element list"
);
assert_eq!(
vec!["hi", "ho", "hu"],
parse_vec("[hi,ho,hu]".to_string()).unwrap(),
"should be able to parse three element list"
);
assert_eq!(
vec!["hi,ho"],
parse_vec("[hi\\,ho]".to_string()).unwrap(),
"should be able to parse list with escaped comma"
);
assert_eq!(
vec!["hi,ho", "hu"],
parse_vec("[hi\\,ho,hu]".to_string()).unwrap(),
"should be able to parse two element list with escaped comma"
);
assert!(parse_vec("".to_string()).is_err(), "Should fail when parsing empty string");
assert!(
parse_vec("[a,b".to_string()).is_err(),
"Should fail when parsing unclosed list"
);
assert!(parse_vec("a]".to_string()).is_err(), "Should fail when parsing unopened list");
}
}

View file

@ -23,6 +23,7 @@ pub(super) fn widget_to_gtk_widget(bargs: &mut BuilderArgs) -> Result<Option<gtk
"color-button" => build_gtk_color_button(bargs)?.upcast(),
"expander" => build_gtk_expander(bargs)?.upcast(),
"color-chooser" => build_gtk_color_chooser(bargs)?.upcast(),
"combo-box" => build_gtk_combo_box(bargs)?.upcast(),
_ => return Ok(None),
};
Ok(Some(gtk_widget))
@ -146,6 +147,30 @@ pub(super) fn resolve_orientable_attrs(bargs: &mut BuilderArgs, gtk_widget: &gtk
// concrete widgets
/// @widget combo-box
fn build_gtk_combo_box(bargs: &mut BuilderArgs) -> Result<gtk::ComboBoxText> {
let gtk_widget = gtk::ComboBoxText::new();
let on_change_handler_id: Rc<RefCell<Option<glib::SignalHandlerId>>> = Rc::new(RefCell::new(None));
resolve_block!(bargs, gtk_widget, {
// @prop items
prop(items: as_vec) {
gtk_widget.remove_all();
for i in items {
gtk_widget.append_text(&i);
}
},
// @prop onchange - runs the code when a item was selected, replacing {} with the item as a string
prop(onchange: as_string) {
let old_id = on_change_handler_id.replace(Some(
gtk_widget.connect_changed(move |gtk_widget| {
run_command(&onchange, gtk_widget.get_active_text().unwrap_or("".into()));
})
));
old_id.map(|id| gtk_widget.disconnect(id));
},
});
Ok(gtk_widget)
}
/// @widget expander widget
fn build_gtk_expander(bargs: &mut BuilderArgs) -> Result<gtk::Expander> {
let gtk_widget = gtk::Expander::new(None);