feat: added prop dyn support to more widgets
This commit is contained in:
@@ -321,8 +321,8 @@ impl<B: DisplayBackend> App<B> {
|
|||||||
|
|
||||||
let initiator = WindowInitiator::new(&window_def, window_args)?;
|
let initiator = WindowInitiator::new(&window_def, window_args)?;
|
||||||
|
|
||||||
/// Holds the id and the props of a widget
|
// Holds the id and the props of a widget
|
||||||
/// It is crutual for supporting dynamic updates
|
// It is crutual for supporting dynamic updates
|
||||||
let mut widget_reg_store = WidgetRegistry::new();
|
let mut widget_reg_store = WidgetRegistry::new();
|
||||||
// note for future me: ^ this might need cloning.
|
// note for future me: ^ this might need cloning.
|
||||||
|
|
||||||
|
|||||||
@@ -114,6 +114,15 @@ pub(super) fn build_gtk_box(props: Map, children: Vec<WidgetNode>, widget_regist
|
|||||||
if let Ok(space_evenly) = get_bool_prop(props, "space_evenly", Some(true)) {
|
if let Ok(space_evenly) = get_bool_prop(props, "space_evenly", Some(true)) {
|
||||||
gtk_widget_clone.set_homogeneous(space_evenly);
|
gtk_widget_clone.set_homogeneous(space_evenly);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// now re-apply generic widget attrs
|
||||||
|
if let Err(err) = resolve_rhai_widget_attrs(
|
||||||
|
None,
|
||||||
|
>k_widget_clone.clone().upcast::<gtk::Widget>(),
|
||||||
|
Some(props),
|
||||||
|
) {
|
||||||
|
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let id = hash_props_and_type(&props, "Box");
|
let id = hash_props_and_type(&props, "Box");
|
||||||
@@ -239,6 +248,15 @@ pub(super) fn build_center_box(props: Map, children: Vec<WidgetNode>, widget_reg
|
|||||||
};
|
};
|
||||||
|
|
||||||
gtk_widget_clone.set_orientation(orientation);
|
gtk_widget_clone.set_orientation(orientation);
|
||||||
|
|
||||||
|
// now re-apply generic widget attrs
|
||||||
|
if let Err(err) = resolve_rhai_widget_attrs(
|
||||||
|
None,
|
||||||
|
>k_widget_clone.clone().upcast::<gtk::Widget>(),
|
||||||
|
Some(props),
|
||||||
|
) {
|
||||||
|
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let id = hash_props_and_type(&props, "CenterBox");
|
let id = hash_props_and_type(&props, "CenterBox");
|
||||||
@@ -446,6 +464,15 @@ pub(super) fn build_gtk_event_box(
|
|||||||
let gtk_widget_clone = gtk_widget.clone();
|
let gtk_widget_clone = gtk_widget.clone();
|
||||||
let update_fn: UpdateFn = Box::new(move |props: &Map| {
|
let update_fn: UpdateFn = Box::new(move |props: &Map| {
|
||||||
let _ = apply_props(props, >k_widget_clone);
|
let _ = apply_props(props, >k_widget_clone);
|
||||||
|
|
||||||
|
// now re-apply generic widget attrs
|
||||||
|
if let Err(err) = resolve_rhai_widget_attrs(
|
||||||
|
None,
|
||||||
|
>k_widget_clone.clone().upcast::<gtk::Widget>(),
|
||||||
|
Some(props),
|
||||||
|
) {
|
||||||
|
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let count = children.len();
|
let count = children.len();
|
||||||
@@ -545,172 +572,260 @@ pub(super) fn build_transform(props: Map, widget_registry: &mut WidgetRegistry)
|
|||||||
pub(super) fn build_circular_progress_bar(props: Map, widget_registry: &mut WidgetRegistry) -> Result<CircProg> {
|
pub(super) fn build_circular_progress_bar(props: Map, widget_registry: &mut WidgetRegistry) -> Result<CircProg> {
|
||||||
let widget = CircProg::new();
|
let widget = CircProg::new();
|
||||||
|
|
||||||
if let Ok(value) = get_f64_prop(&props, "value", None) {
|
let apply_props = |props: &Map, widget: &CircProg| -> Result<()> {
|
||||||
widget.set_property("value", value.clamp(0.0, 100.0));
|
if let Ok(value) = get_f64_prop(&props, "value", None) {
|
||||||
}
|
widget.set_property("value", value.clamp(0.0, 100.0));
|
||||||
|
}
|
||||||
|
|
||||||
if let Ok(start_at) = get_f64_prop(&props, "start_at", None) {
|
if let Ok(start_at) = get_f64_prop(&props, "start_at", None) {
|
||||||
widget.set_property("start-at", start_at.clamp(0.0, 100.0));
|
widget.set_property("start-at", start_at.clamp(0.0, 100.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(thickness) = get_f64_prop(&props, "thickness", None) {
|
if let Ok(thickness) = get_f64_prop(&props, "thickness", None) {
|
||||||
widget.set_property("thickness", thickness);
|
widget.set_property("thickness", thickness);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(clockwise) = get_f64_prop(&props, "clockwise", None) {
|
if let Ok(clockwise) = get_f64_prop(&props, "clockwise", None) {
|
||||||
widget.set_property("clockwise", clockwise);
|
widget.set_property("clockwise", clockwise);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
|
|
||||||
|
apply_props(&props, &widget)?;
|
||||||
|
|
||||||
|
let widget_clone = widget.clone();
|
||||||
|
let update_fn: UpdateFn = Box::new(move |props: &Map| {
|
||||||
|
let _ = apply_props(props, &widget_clone);
|
||||||
|
|
||||||
|
// now re-apply generic widget attrs
|
||||||
|
if let Err(err) = resolve_rhai_widget_attrs(
|
||||||
|
None,
|
||||||
|
&widget_clone.clone().upcast::<gtk::Widget>(),
|
||||||
|
Some(props),
|
||||||
|
) {
|
||||||
|
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let id = hash_props_and_type(&props, "CircularProgressBar");
|
let id = hash_props_and_type(&props, "CircularProgressBar");
|
||||||
|
|
||||||
|
widget_registry.widgets.insert(id, WidgetEntry { update_fn });
|
||||||
|
|
||||||
Ok(widget)
|
Ok(widget)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn build_graph(props: Map, widget_registry: &mut WidgetRegistry) -> Result<super::graph::Graph> {
|
pub(super) fn build_graph(props: Map, widget_registry: &mut WidgetRegistry) -> Result<super::graph::Graph> {
|
||||||
let widget = super::graph::Graph::new();
|
let widget = super::graph::Graph::new();
|
||||||
|
|
||||||
if let Ok(value) = get_f64_prop(&props, "value", None) {
|
let apply_props = |props: &Map, widget: &super::graph::Graph| -> Result<()> {
|
||||||
if value.is_nan() || value.is_infinite() {
|
if let Ok(value) = get_f64_prop(&props, "value", None) {
|
||||||
return Err(anyhow!("Graph's value should never be NaN or infinite"));
|
if value.is_nan() || value.is_infinite() {
|
||||||
|
return Err(anyhow!("Graph's value should never be NaN or infinite"));
|
||||||
|
}
|
||||||
|
widget.set_property("value", value);
|
||||||
}
|
}
|
||||||
widget.set_property("value", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Ok(thickness) = get_f64_prop(&props, "thickness", None) {
|
if let Ok(thickness) = get_f64_prop(&props, "thickness", None) {
|
||||||
widget.set_property("thickness", thickness);
|
widget.set_property("thickness", thickness);
|
||||||
}
|
|
||||||
|
|
||||||
if let Ok(time_range) = get_duration_prop(&props, "time_range", None) {
|
|
||||||
widget.set_property("time-range", time_range.as_millis() as u64);
|
|
||||||
}
|
|
||||||
|
|
||||||
let min = get_f64_prop(&props, "min", Some(0.0)).ok();
|
|
||||||
let max = get_f64_prop(&props, "max", Some(100.0)).ok();
|
|
||||||
|
|
||||||
if let (Some(mi), Some(ma)) = (min, max) {
|
|
||||||
if mi > ma {
|
|
||||||
return Err(anyhow!("Graph's min ({mi}) should never be higher than max ({ma})"));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(mi) = min {
|
if let Ok(time_range) = get_duration_prop(&props, "time_range", None) {
|
||||||
widget.set_property("min", mi);
|
widget.set_property("time-range", time_range.as_millis() as u64);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ma) = max {
|
let min = get_f64_prop(&props, "min", Some(0.0)).ok();
|
||||||
widget.set_property("max", ma);
|
let max = get_f64_prop(&props, "max", Some(100.0)).ok();
|
||||||
}
|
|
||||||
|
|
||||||
if let Ok(dynamic) = get_bool_prop(&props, "dynamic", None) {
|
if let (Some(mi), Some(ma)) = (min, max) {
|
||||||
widget.set_property("dynamic", dynamic);
|
if mi > ma {
|
||||||
}
|
return Err(anyhow!("Graph's min ({mi}) should never be higher than max ({ma})"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Ok(line_style) = get_string_prop(&props, "line_style", None) {
|
if let Some(mi) = min {
|
||||||
widget.set_property("line-style", line_style);
|
widget.set_property("min", mi);
|
||||||
}
|
}
|
||||||
|
|
||||||
// flip-x - whether the x axis should go from high to low
|
if let Some(ma) = max {
|
||||||
if let Ok(flip_x) = get_bool_prop(&props, "flip_x", None) {
|
widget.set_property("max", ma);
|
||||||
widget.set_property("flip-x", flip_x);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// flip-y - whether the y axis should go from high to low
|
if let Ok(dynamic) = get_bool_prop(&props, "dynamic", None) {
|
||||||
if let Ok(flip_y) = get_bool_prop(&props, "flip_y", None) {
|
widget.set_property("dynamic", dynamic);
|
||||||
widget.set_property("flip-y", flip_y);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// vertical - if set to true, the x and y axes will be exchanged
|
if let Ok(line_style) = get_string_prop(&props, "line_style", None) {
|
||||||
if let Ok(vertical) = get_bool_prop(&props, "vertical", None) {
|
widget.set_property("line-style", line_style);
|
||||||
widget.set_property("vertical", vertical);
|
}
|
||||||
}
|
|
||||||
|
// flip-x - whether the x axis should go from high to low
|
||||||
|
if let Ok(flip_x) = get_bool_prop(&props, "flip_x", None) {
|
||||||
|
widget.set_property("flip-x", flip_x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// flip-y - whether the y axis should go from high to low
|
||||||
|
if let Ok(flip_y) = get_bool_prop(&props, "flip_y", None) {
|
||||||
|
widget.set_property("flip-y", flip_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// vertical - if set to true, the x and y axes will be exchanged
|
||||||
|
if let Ok(vertical) = get_bool_prop(&props, "vertical", None) {
|
||||||
|
widget.set_property("vertical", vertical);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
|
|
||||||
|
apply_props(&props, &widget)?;
|
||||||
|
|
||||||
|
let widget_clone = widget.clone();
|
||||||
|
let update_fn: UpdateFn = Box::new(move |props: &Map| {
|
||||||
|
let _ = apply_props(props, &widget_clone);
|
||||||
|
|
||||||
|
// now re-apply generic widget attrs
|
||||||
|
if let Err(err) = resolve_rhai_widget_attrs(
|
||||||
|
None,
|
||||||
|
&widget_clone.clone().upcast::<gtk::Widget>(),
|
||||||
|
Some(props),
|
||||||
|
) {
|
||||||
|
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let id = hash_props_and_type(&props, "Graph");
|
let id = hash_props_and_type(&props, "Graph");
|
||||||
|
|
||||||
|
widget_registry.widgets.insert(id, WidgetEntry { update_fn });
|
||||||
|
|
||||||
Ok(widget)
|
Ok(widget)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn build_gtk_progress(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::ProgressBar> {
|
pub(super) fn build_gtk_progress(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::ProgressBar> {
|
||||||
let gtk_widget = gtk::ProgressBar::new();
|
let gtk_widget = gtk::ProgressBar::new();
|
||||||
|
|
||||||
let orientation = props
|
let apply_props = |props: &Map, widget: >k::ProgressBar| -> Result<()> {
|
||||||
.get("orientation")
|
let orientation = props
|
||||||
.and_then(|v| v.clone().try_cast::<String>())
|
.get("orientation")
|
||||||
.map(|s| parse_orientation(&s))
|
.and_then(|v| v.clone().try_cast::<String>())
|
||||||
.transpose()?
|
.map(|s| parse_orientation(&s))
|
||||||
.unwrap_or(gtk::Orientation::Horizontal);
|
.transpose()?
|
||||||
|
.unwrap_or(gtk::Orientation::Horizontal);
|
||||||
|
|
||||||
gtk_widget.set_orientation(orientation);
|
widget.set_orientation(orientation);
|
||||||
|
|
||||||
if let Ok(flipped) = get_bool_prop(&props, "flipped", Some(false)) {
|
if let Ok(flipped) = get_bool_prop(&props, "flipped", Some(false)) {
|
||||||
gtk_widget.set_inverted(flipped)
|
widget.set_inverted(flipped)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(bar_value) = get_f64_prop(&props, "value", None) {
|
if let Ok(bar_value) = get_f64_prop(&props, "value", None) {
|
||||||
gtk_widget.set_fraction(bar_value / 100f64)
|
widget.set_fraction(bar_value / 100f64)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
|
|
||||||
|
apply_props(&props, >k_widget)?;
|
||||||
|
|
||||||
|
let gtk_widget_clone = gtk_widget.clone();
|
||||||
|
let update_fn: UpdateFn = Box::new(move |props: &Map| {
|
||||||
|
let _ = apply_props(props, >k_widget_clone);
|
||||||
|
|
||||||
|
// now re-apply generic widget attrs
|
||||||
|
if let Err(err) = resolve_rhai_widget_attrs(
|
||||||
|
None,
|
||||||
|
>k_widget_clone.clone().upcast::<gtk::Widget>(),
|
||||||
|
Some(props),
|
||||||
|
) {
|
||||||
|
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let id = hash_props_and_type(&props, "Progress");
|
let id = hash_props_and_type(&props, "Progress");
|
||||||
|
|
||||||
|
widget_registry.widgets.insert(id, WidgetEntry { update_fn });
|
||||||
|
|
||||||
Ok(gtk_widget)
|
Ok(gtk_widget)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn build_gtk_image(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::Image> {
|
pub(super) fn build_gtk_image(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::Image> {
|
||||||
let gtk_widget = gtk::Image::new();
|
let gtk_widget = gtk::Image::new();
|
||||||
|
|
||||||
let path = get_string_prop(&props, "path", None)?;
|
let apply_props = |props: &Map, widget: >k::Image| -> Result<()> {
|
||||||
let image_width = get_i32_prop(&props, "image_width", Some(-1))?;
|
let path = get_string_prop(&props, "path", None)?;
|
||||||
let image_height = get_i32_prop(&props, "image_height", Some(-1))?;
|
let image_width = get_i32_prop(&props, "image_width", Some(-1))?;
|
||||||
let preserve_aspect_ratio = get_bool_prop(&props, "preserve_aspect_ratio", Some(true))?;
|
let image_height = get_i32_prop(&props, "image_height", Some(-1))?;
|
||||||
let fill_svg = get_string_prop(&props, "fill_svg", Some(""))?;
|
let preserve_aspect_ratio = get_bool_prop(&props, "preserve_aspect_ratio", Some(true))?;
|
||||||
|
let fill_svg = get_string_prop(&props, "fill_svg", Some(""))?;
|
||||||
|
|
||||||
if !path.ends_with(".svg") && !fill_svg.is_empty() {
|
if !path.ends_with(".svg") && !fill_svg.is_empty() {
|
||||||
log::warn!("Fill attribute ignored, file is not an svg image");
|
log::warn!("Fill attribute ignored, file is not an svg image");
|
||||||
}
|
|
||||||
|
|
||||||
if path.ends_with(".gif") {
|
|
||||||
let pixbuf_animation = gtk::gdk_pixbuf::PixbufAnimation::from_file(std::path::PathBuf::from(path))?;
|
|
||||||
gtk_widget.set_from_animation(&pixbuf_animation);
|
|
||||||
} else {
|
|
||||||
let pixbuf;
|
|
||||||
// populate the pixel buffer
|
|
||||||
if path.ends_with(".svg") && !fill_svg.is_empty() {
|
|
||||||
let svg_data = std::fs::read_to_string(std::path::PathBuf::from(path.clone()))?;
|
|
||||||
// The fastest way to add/change fill color
|
|
||||||
let svg_data = if svg_data.contains("fill=") {
|
|
||||||
let reg = regex::Regex::new(r#"fill="[^"]*""#)?;
|
|
||||||
reg.replace(&svg_data, &format!("fill=\"{}\"", fill_svg))
|
|
||||||
} else {
|
|
||||||
let reg = regex::Regex::new(r"<svg")?;
|
|
||||||
reg.replace(&svg_data, &format!("<svg fill=\"{}\"", fill_svg))
|
|
||||||
};
|
|
||||||
let stream = gtk::gio::MemoryInputStream::from_bytes(>k::glib::Bytes::from(svg_data.as_bytes()));
|
|
||||||
pixbuf = gtk::gdk_pixbuf::Pixbuf::from_stream_at_scale(
|
|
||||||
&stream,
|
|
||||||
image_width,
|
|
||||||
image_height,
|
|
||||||
preserve_aspect_ratio,
|
|
||||||
None::<>k::gio::Cancellable>,
|
|
||||||
)?;
|
|
||||||
stream.close(None::<>k::gio::Cancellable>)?;
|
|
||||||
} else {
|
|
||||||
pixbuf = gtk::gdk_pixbuf::Pixbuf::from_file_at_scale(
|
|
||||||
std::path::PathBuf::from(path),
|
|
||||||
image_width,
|
|
||||||
image_height,
|
|
||||||
preserve_aspect_ratio,
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
gtk_widget.set_from_pixbuf(Some(&pixbuf));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Ok(icon_name) = get_string_prop(&props, "icon", None) {
|
if path.ends_with(".gif") {
|
||||||
let icon_size = get_string_prop(&props, "icon-size", Some("button"))?;
|
let pixbuf_animation = gtk::gdk_pixbuf::PixbufAnimation::from_file(std::path::PathBuf::from(path))?;
|
||||||
gtk_widget.set_from_icon_name(Some(&icon_name), parse_icon_size(&icon_size)?);
|
widget.set_from_animation(&pixbuf_animation);
|
||||||
return Ok(gtk_widget);
|
} else {
|
||||||
}
|
let pixbuf;
|
||||||
|
// populate the pixel buffer
|
||||||
|
if path.ends_with(".svg") && !fill_svg.is_empty() {
|
||||||
|
let svg_data = std::fs::read_to_string(std::path::PathBuf::from(path.clone()))?;
|
||||||
|
// The fastest way to add/change fill color
|
||||||
|
let svg_data = if svg_data.contains("fill=") {
|
||||||
|
let reg = regex::Regex::new(r#"fill="[^"]*""#)?;
|
||||||
|
reg.replace(&svg_data, &format!("fill=\"{}\"", fill_svg))
|
||||||
|
} else {
|
||||||
|
let reg = regex::Regex::new(r"<svg")?;
|
||||||
|
reg.replace(&svg_data, &format!("<svg fill=\"{}\"", fill_svg))
|
||||||
|
};
|
||||||
|
let stream = gtk::gio::MemoryInputStream::from_bytes(>k::glib::Bytes::from(svg_data.as_bytes()));
|
||||||
|
pixbuf = gtk::gdk_pixbuf::Pixbuf::from_stream_at_scale(
|
||||||
|
&stream,
|
||||||
|
image_width,
|
||||||
|
image_height,
|
||||||
|
preserve_aspect_ratio,
|
||||||
|
None::<>k::gio::Cancellable>,
|
||||||
|
)?;
|
||||||
|
stream.close(None::<>k::gio::Cancellable>)?;
|
||||||
|
} else {
|
||||||
|
pixbuf = gtk::gdk_pixbuf::Pixbuf::from_file_at_scale(
|
||||||
|
std::path::PathBuf::from(path),
|
||||||
|
image_width,
|
||||||
|
image_height,
|
||||||
|
preserve_aspect_ratio,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
widget.set_from_pixbuf(Some(&pixbuf));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(icon_name) = get_string_prop(&props, "icon", None) {
|
||||||
|
let icon_size = get_string_prop(&props, "icon-size", Some("button"))?;
|
||||||
|
widget.set_from_icon_name(Some(&icon_name), parse_icon_size(&icon_size)?);
|
||||||
|
// return Ok(widget);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
|
|
||||||
|
apply_props(&props, >k_widget)?;
|
||||||
|
|
||||||
|
let gtk_widget_clone = gtk_widget.clone();
|
||||||
|
let update_fn: UpdateFn = Box::new(move |props: &Map| {
|
||||||
|
let _ = apply_props(props, >k_widget_clone);
|
||||||
|
|
||||||
|
// now re-apply generic widget attrs
|
||||||
|
if let Err(err) = resolve_rhai_widget_attrs(
|
||||||
|
None,
|
||||||
|
>k_widget_clone.clone().upcast::<gtk::Widget>(),
|
||||||
|
Some(props),
|
||||||
|
) {
|
||||||
|
eprintln!("Failed to update widget attrs: {:?}", err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let id = hash_props_and_type(&props, "Image");
|
let id = hash_props_and_type(&props, "Image");
|
||||||
|
|
||||||
|
widget_registry.widgets.insert(id, WidgetEntry { update_fn });
|
||||||
|
|
||||||
Ok(gtk_widget)
|
Ok(gtk_widget)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1332,6 +1447,13 @@ pub(super) fn resolve_rhai_widget_attrs(node: Option<WidgetNode>, gtk_widget: &g
|
|||||||
// Handle classes
|
// Handle classes
|
||||||
if let Ok(class_str) = get_string_prop(&props, "class", None) {
|
if let Ok(class_str) = get_string_prop(&props, "class", None) {
|
||||||
let style_context = gtk_widget.style_context();
|
let style_context = gtk_widget.style_context();
|
||||||
|
|
||||||
|
// remove all classes
|
||||||
|
for class in style_context.list_classes() {
|
||||||
|
style_context.remove_class(&class);
|
||||||
|
}
|
||||||
|
|
||||||
|
// then apply the classes
|
||||||
for class in class_str.split_whitespace() {
|
for class in class_str.split_whitespace() {
|
||||||
style_context.add_class(class);
|
style_context.add_class(class);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,14 +16,23 @@ let object = #{
|
|||||||
|
|
||||||
// Widget: a single animal button
|
// Widget: a single animal button
|
||||||
fn animalButton(emoji, selected) {
|
fn animalButton(emoji, selected) {
|
||||||
|
print!(selected);
|
||||||
|
print!(emoji);
|
||||||
|
|
||||||
|
let class = "animal";
|
||||||
|
|
||||||
|
if selected == emoji {
|
||||||
|
class = "animal selected"
|
||||||
|
}
|
||||||
|
|
||||||
return box(#{ class: "animalLayout" }, [
|
return box(#{ class: "animalLayout" }, [
|
||||||
eventbox(#{
|
eventbox(#{
|
||||||
class: if selected == emoji { "animal selected" } else { "animal" },
|
class: class,
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
onclick: "echo " + emoji + " > /tmp/selected_emoji.txt",
|
onclick: "echo " + emoji + " >> /tmp/selected_emoji.txt",
|
||||||
dyn_id: "dyn_eventbox",
|
dyn_id: "dyn_eventbox_" + emoji, // unique per emoji
|
||||||
}, [
|
}, [
|
||||||
label(#{ text: emoji })
|
label(#{ text: emoji, dyn_id: "dyn_label_" + emoji }) // unique per emoji
|
||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -63,15 +72,15 @@ fn layout(stringArray, object, selected) {
|
|||||||
enter([
|
enter([
|
||||||
// Track the selected emoji
|
// Track the selected emoji
|
||||||
listen("selected", #{
|
listen("selected", #{
|
||||||
cmd: "tail -n 1 -f /tmp/selected_emoji.txt",
|
cmd: "scripts/readlast_and_truncate.sh",
|
||||||
initial: "🦝"
|
initial: "🦝",
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|
||||||
defwindow("data-structures", #{
|
defwindow("data-structures", #{
|
||||||
monitor: 0,
|
monitor: 0,
|
||||||
exclusive: false,
|
exclusive: false,
|
||||||
focusable: "none",
|
focusable: "none",
|
||||||
|
windowtype: "normal",
|
||||||
geometry: #{ anchor: "center", x: "0px", y: "0px", width: "100px", height: "10px" },
|
geometry: #{ anchor: "center", x: "0px", y: "0px", width: "100px", height: "10px" },
|
||||||
}, layout(stringArray, object, selected))
|
}, layout(stringArray, object, selected))
|
||||||
])
|
])
|
||||||
10
examples/data-structures/scripts/readlast_and_truncate.sh
Executable file
10
examples/data-structures/scripts/readlast_and_truncate.sh
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
while true; do
|
||||||
|
# read the last line
|
||||||
|
if [ -s /tmp/selected_emoji.txt ]; then
|
||||||
|
tail -n 1 /tmp/selected_emoji.txt
|
||||||
|
# truncate the file after reading
|
||||||
|
: > /tmp/selected_emoji.txt
|
||||||
|
fi
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
Reference in New Issue
Block a user