Provide better error and docs for duration type

This commit is contained in:
elkowar 2023-03-26 12:40:12 +02:00
parent 992ac0b59a
commit 88cd3a29dc
No known key found for this signature in database
GPG key ID: E321AD71B1D1F27F
2 changed files with 22 additions and 16 deletions

View file

@ -244,7 +244,7 @@ pub(super) fn resolve_range_attrs(bargs: &mut BuilderArgs, gtk_widget: &gtk::Ran
prop(min: as_f64) { gtk_widget.adjustment().set_lower(min)}, prop(min: as_f64) { gtk_widget.adjustment().set_lower(min)},
// @prop max - the maximum value // @prop max - the maximum value
prop(max: as_f64) { gtk_widget.adjustment().set_upper(max)}, prop(max: as_f64) { gtk_widget.adjustment().set_upper(max)},
// @prop timeout - timeout of the command // @prop timeout - timeout of the command. Default: "200ms"
// @prop onchange - command executed once the value is changes. The placeholder `{}`, used in the command will be replaced by the new value. // @prop onchange - command executed once the value is changes. The placeholder `{}`, used in the command will be replaced by the new value.
prop(timeout: as_duration = Duration::from_millis(200), onchange: as_string) { prop(timeout: as_duration = Duration::from_millis(200), onchange: as_string) {
gtk_widget.set_sensitive(true); gtk_widget.set_sensitive(true);
@ -285,7 +285,7 @@ fn build_gtk_combo_box_text(bargs: &mut BuilderArgs) -> Result<gtk::ComboBoxText
gtk_widget.append_text(&i); gtk_widget.append_text(&i);
} }
}, },
// @prop timeout - timeout of the command // @prop timeout - timeout of the command: Default: "200ms"
// @prop onchange - runs the code when a item was selected, replacing {} with the item as a string // @prop onchange - runs the code when a item was selected, replacing {} with the item as a string
prop(timeout: as_duration = Duration::from_millis(200), onchange: as_string) { prop(timeout: as_duration = Duration::from_millis(200), onchange: as_string) {
connect_signal_handler!(gtk_widget, gtk_widget.connect_changed(move |gtk_widget| { connect_signal_handler!(gtk_widget, gtk_widget.connect_changed(move |gtk_widget| {
@ -320,7 +320,7 @@ fn build_gtk_revealer(bargs: &mut BuilderArgs) -> Result<gtk::Revealer> {
prop(transition: as_string = "crossfade") { gtk_widget.set_transition_type(parse_transition(&transition)?); }, prop(transition: as_string = "crossfade") { gtk_widget.set_transition_type(parse_transition(&transition)?); },
// @prop reveal - sets if the child is revealed or not // @prop reveal - sets if the child is revealed or not
prop(reveal: as_bool) { gtk_widget.set_reveal_child(reveal); }, prop(reveal: as_bool) { gtk_widget.set_reveal_child(reveal); },
// @prop duration - the duration of the reveal transition // @prop duration - the duration of the reveal transition. Default: "500ms"
prop(duration: as_duration = Duration::from_millis(500)) { gtk_widget.set_transition_duration(duration.as_millis() as u32); }, prop(duration: as_duration = Duration::from_millis(500)) { gtk_widget.set_transition_duration(duration.as_millis() as u32); },
}); });
Ok(gtk_widget) Ok(gtk_widget)
@ -332,7 +332,7 @@ const WIDGET_NAME_CHECKBOX: &str = "checkbox";
fn build_gtk_checkbox(bargs: &mut BuilderArgs) -> Result<gtk::CheckButton> { fn build_gtk_checkbox(bargs: &mut BuilderArgs) -> Result<gtk::CheckButton> {
let gtk_widget = gtk::CheckButton::new(); let gtk_widget = gtk::CheckButton::new();
def_widget!(bargs, _g, gtk_widget, { def_widget!(bargs, _g, gtk_widget, {
// @prop timeout - timeout of the command // @prop timeout - timeout of the command. Default: "200ms"
// @prop onchecked - action (command) to be executed when checked by the user // @prop onchecked - action (command) to be executed when checked by the user
// @prop onunchecked - similar to onchecked but when the widget is unchecked // @prop onunchecked - similar to onchecked but when the widget is unchecked
prop(timeout: as_duration = Duration::from_millis(200), onchecked: as_string = "", onunchecked: as_string = "") { prop(timeout: as_duration = Duration::from_millis(200), onchecked: as_string = "", onunchecked: as_string = "") {
@ -355,7 +355,7 @@ fn build_gtk_color_button(bargs: &mut BuilderArgs) -> Result<gtk::ColorButton> {
prop(use_alpha: as_bool) {gtk_widget.set_use_alpha(use_alpha);}, prop(use_alpha: as_bool) {gtk_widget.set_use_alpha(use_alpha);},
// @prop onchange - runs the code when the color was selected // @prop onchange - runs the code when the color was selected
// @prop timeout - timeout of the command // @prop timeout - timeout of the command. Default: "200ms"
prop(timeout: as_duration = Duration::from_millis(200), onchange: as_string) { prop(timeout: as_duration = Duration::from_millis(200), onchange: as_string) {
connect_signal_handler!(gtk_widget, gtk_widget.connect_color_set(move |gtk_widget| { connect_signal_handler!(gtk_widget, gtk_widget.connect_color_set(move |gtk_widget| {
run_command(timeout, &onchange, &[gtk_widget.rgba()]); run_command(timeout, &onchange, &[gtk_widget.rgba()]);
@ -376,7 +376,7 @@ fn build_gtk_color_chooser(bargs: &mut BuilderArgs) -> Result<gtk::ColorChooserW
prop(use_alpha: as_bool) {gtk_widget.set_use_alpha(use_alpha);}, prop(use_alpha: as_bool) {gtk_widget.set_use_alpha(use_alpha);},
// @prop onchange - runs the code when the color was selected // @prop onchange - runs the code when the color was selected
// @prop timeout - timeout of the command // @prop timeout - timeout of the command. Default: "200ms"
prop(timeout: as_duration = Duration::from_millis(200), onchange: as_string) { prop(timeout: as_duration = Duration::from_millis(200), onchange: as_string) {
connect_signal_handler!(gtk_widget, gtk_widget.connect_color_activated(move |_a, color| { connect_signal_handler!(gtk_widget, gtk_widget.connect_color_activated(move |_a, color| {
run_command(timeout, &onchange, &[*color]); run_command(timeout, &onchange, &[*color]);
@ -446,14 +446,14 @@ fn build_gtk_input(bargs: &mut BuilderArgs) -> Result<gtk::Entry> {
}, },
// @prop onchange - Command to run when the text changes. The placeholder `{}` will be replaced by the value // @prop onchange - Command to run when the text changes. The placeholder `{}` will be replaced by the value
// @prop timeout - timeout of the command // @prop timeout - timeout of the command. Default: "200ms"
prop(timeout: as_duration = Duration::from_millis(200), onchange: as_string) { prop(timeout: as_duration = Duration::from_millis(200), onchange: as_string) {
connect_signal_handler!(gtk_widget, gtk_widget.connect_changed(move |gtk_widget| { connect_signal_handler!(gtk_widget, gtk_widget.connect_changed(move |gtk_widget| {
run_command(timeout, &onchange, &[gtk_widget.text().to_string()]); run_command(timeout, &onchange, &[gtk_widget.text().to_string()]);
})); }));
}, },
// @prop onaccept - Command to run when the user hits return in the input field. The placeholder `{}` will be replaced by the value // @prop onaccept - Command to run when the user hits return in the input field. The placeholder `{}` will be replaced by the value
// @prop timeout - timeout of the command // @prop timeout - timeout of the command. Default: "200ms"
prop(timeout: as_duration = Duration::from_millis(200), onaccept: as_string) { prop(timeout: as_duration = Duration::from_millis(200), onaccept: as_string) {
connect_signal_handler!(gtk_widget, gtk_widget.connect_activate(move |gtk_widget| { connect_signal_handler!(gtk_widget, gtk_widget.connect_activate(move |gtk_widget| {
run_command(timeout, &onaccept, &[gtk_widget.text().to_string()]); run_command(timeout, &onaccept, &[gtk_widget.text().to_string()]);
@ -475,7 +475,7 @@ fn build_gtk_button(bargs: &mut BuilderArgs) -> Result<gtk::Button> {
def_widget!(bargs, _g, gtk_widget, { def_widget!(bargs, _g, gtk_widget, {
prop( prop(
// @prop timeout - timeout of the command // @prop timeout - timeout of the command. Default: "200ms"
timeout: as_duration = Duration::from_millis(200), timeout: as_duration = Duration::from_millis(200),
// @prop onclick - a command that get's run when the button is clicked // @prop onclick - a command that get's run when the button is clicked
onclick: as_string = "", onclick: as_string = "",
@ -675,7 +675,7 @@ fn build_gtk_event_box(bargs: &mut BuilderArgs) -> Result<gtk::EventBox> {
}); });
def_widget!(bargs, _g, gtk_widget, { def_widget!(bargs, _g, gtk_widget, {
// @prop timeout - timeout of the command // @prop timeout - timeout of the command. Default: "200ms"
// @prop onscroll - event to execute when the user scrolls with the mouse over the widget. The placeholder `{}` used in the command will be replaced with either `up` or `down`. // @prop onscroll - event to execute when the user scrolls with the mouse over the widget. The placeholder `{}` used in the command will be replaced with either `up` or `down`.
prop(timeout: as_duration = Duration::from_millis(200), onscroll: as_string) { prop(timeout: as_duration = Duration::from_millis(200), onscroll: as_string) {
gtk_widget.add_events(gdk::EventMask::SCROLL_MASK); gtk_widget.add_events(gdk::EventMask::SCROLL_MASK);
@ -688,7 +688,7 @@ fn build_gtk_event_box(bargs: &mut BuilderArgs) -> Result<gtk::EventBox> {
gtk::Inhibit(false) gtk::Inhibit(false)
})); }));
}, },
// @prop timeout - timeout of the command // @prop timeout - timeout of the command. Default: "200ms"
// @prop onhover - event to execute when the user hovers over the widget // @prop onhover - event to execute when the user hovers over the widget
prop(timeout: as_duration = Duration::from_millis(200), onhover: as_string) { prop(timeout: as_duration = Duration::from_millis(200), onhover: as_string) {
gtk_widget.add_events(gdk::EventMask::ENTER_NOTIFY_MASK); gtk_widget.add_events(gdk::EventMask::ENTER_NOTIFY_MASK);
@ -699,7 +699,7 @@ fn build_gtk_event_box(bargs: &mut BuilderArgs) -> Result<gtk::EventBox> {
gtk::Inhibit(false) gtk::Inhibit(false)
})); }));
}, },
// @prop timeout - timeout of the command // @prop timeout - timeout of the command. Default: "200ms"
// @prop onhoverlost - event to execute when the user losts hovers over the widget // @prop onhoverlost - event to execute when the user losts hovers over the widget
prop(timeout: as_duration = Duration::from_millis(200), onhoverlost: as_string) { prop(timeout: as_duration = Duration::from_millis(200), onhoverlost: as_string) {
gtk_widget.add_events(gdk::EventMask::LEAVE_NOTIFY_MASK); gtk_widget.add_events(gdk::EventMask::LEAVE_NOTIFY_MASK);
@ -735,7 +735,7 @@ fn build_gtk_event_box(bargs: &mut BuilderArgs) -> Result<gtk::EventBox> {
gtk::Inhibit(false) gtk::Inhibit(false)
})); }));
}, },
// @prop timeout - timeout of the command // @prop timeout - timeout of the command. Default: "200ms"
// @prop ondropped - Command to execute when something is dropped on top of this element. The placeholder `{}` used in the command will be replaced with the uri to the dropped thing. // @prop ondropped - Command to execute when something is dropped on top of this element. The placeholder `{}` used in the command will be replaced with the uri to the dropped thing.
prop(timeout: as_duration = Duration::from_millis(200), ondropped: as_string) { prop(timeout: as_duration = Duration::from_millis(200), ondropped: as_string) {
gtk_widget.drag_dest_set( gtk_widget.drag_dest_set(
@ -785,7 +785,7 @@ fn build_gtk_event_box(bargs: &mut BuilderArgs) -> Result<gtk::EventBox> {
// TODO the fact that we have the same code here as for button is ugly, as we want to keep consistency // TODO the fact that we have the same code here as for button is ugly, as we want to keep consistency
prop( prop(
// @prop timeout - timeout of the command // @prop timeout - timeout of the command. Default: "200ms"
timeout: as_duration = Duration::from_millis(200), timeout: as_duration = Duration::from_millis(200),
// @prop onclick - a command that get's run when the button is clicked // @prop onclick - a command that get's run when the button is clicked
onclick: as_string = "", onclick: as_string = "",
@ -948,7 +948,7 @@ fn build_gtk_calendar(bargs: &mut BuilderArgs) -> Result<gtk::Calendar> {
// @prop show-week-numbers - show week numbers // @prop show-week-numbers - show week numbers
prop(show_week_numbers: as_bool) { gtk_widget.set_show_week_numbers(show_week_numbers) }, prop(show_week_numbers: as_bool) { gtk_widget.set_show_week_numbers(show_week_numbers) },
// @prop onclick - command to run when the user selects a date. The `{0}` placeholder will be replaced by the selected day, `{1}` will be replaced by the month, and `{2}` by the year. // @prop onclick - command to run when the user selects a date. The `{0}` placeholder will be replaced by the selected day, `{1}` will be replaced by the month, and `{2}` by the year.
// @prop timeout - timeout of the command // @prop timeout - timeout of the command. Default: "200ms"
prop(timeout: as_duration = Duration::from_millis(200), onclick: as_string) { prop(timeout: as_duration = Duration::from_millis(200), onclick: as_string) {
connect_signal_handler!(gtk_widget, gtk_widget.connect_day_selected(move |w| { connect_signal_handler!(gtk_widget, gtk_widget.connect_day_selected(move |w| {
run_command( run_command(

View file

@ -13,6 +13,10 @@ pub struct ConversionError {
pub source: Option<Box<dyn std::error::Error + Sync + Send + 'static>>, pub source: Option<Box<dyn std::error::Error + Sync + Send + 'static>>,
} }
#[derive(Debug, thiserror::Error)]
#[error("Failed to parse duration. Must be a number of milliseconds, or a string like \"150ms\"")]
pub struct DurationParseError;
impl ConversionError { impl ConversionError {
pub fn new(value: DynVal, target_type: &'static str, source: impl std::error::Error + 'static + Sync + Send) -> Self { pub fn new(value: DynVal, target_type: &'static str, source: impl std::error::Error + 'static + Sync + Send) -> Self {
ConversionError { value, target_type, source: Some(Box::new(source)) } ConversionError { value, target_type, source: Some(Box::new(source)) }
@ -188,8 +192,10 @@ impl DynVal {
} else if s.ends_with('h') { } else if s.ends_with('h') {
let hours = s.trim_end_matches('h').parse::<f64>().map_err(|e| ConversionError::new(self.clone(), "number", e))?; let hours = s.trim_end_matches('h').parse::<f64>().map_err(|e| ConversionError::new(self.clone(), "number", e))?;
Ok(Duration::from_secs(f64::floor(hours * 60f64 * 60f64) as u64)) Ok(Duration::from_secs(f64::floor(hours * 60f64 * 60f64) as u64))
} else if let Ok(millis) = s.parse() {
Ok(Duration::from_millis(millis))
} else { } else {
Err(ConversionError { value: self.clone(), target_type: "duration", source: None }) Err(ConversionError { value: self.clone(), target_type: "duration", source: Some(Box::new(DurationParseError)) })
} }
} }