Split widget callbacks into update and commit so only the latter adds a history state (#1584)
* feat: split commit and update layout * feat: add on_commit callback * Code review * fix: refactor --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
f25038067e
commit
9530e55ace
|
|
@ -15,7 +15,12 @@ pub enum LayoutMessage {
|
|||
layout: Layout,
|
||||
layout_target: LayoutTarget,
|
||||
},
|
||||
UpdateLayout {
|
||||
WidgetValueCommit {
|
||||
layout_target: LayoutTarget,
|
||||
widget_id: WidgetId,
|
||||
value: serde_json::Value,
|
||||
},
|
||||
WidgetValueUpdate {
|
||||
layout_target: LayoutTarget,
|
||||
widget_id: WidgetId,
|
||||
value: serde_json::Value,
|
||||
|
|
|
|||
|
|
@ -12,6 +12,11 @@ pub struct LayoutMessageHandler {
|
|||
layouts: [Layout; LayoutTarget::LayoutTargetLength as usize],
|
||||
}
|
||||
|
||||
enum WidgetValueAction {
|
||||
Commit,
|
||||
Update,
|
||||
}
|
||||
|
||||
impl LayoutMessageHandler {
|
||||
/// Get the widget path for the widget with the specified id
|
||||
fn get_widget_path(widget_layout: &WidgetLayout, widget_id: WidgetId) -> Option<(&WidgetHolder, Vec<usize>)> {
|
||||
|
|
@ -40,6 +45,229 @@ impl LayoutMessageHandler {
|
|||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn handle_widget_callback(&mut self, layout_target: LayoutTarget, widget_id: WidgetId, value: Value, action: WidgetValueAction, responses: &mut std::collections::VecDeque<Message>) {
|
||||
let Some(layout) = self.layouts.get_mut(layout_target as usize) else {
|
||||
warn!("handle_widget_callback was called referencing an invalid layout. `widget_id: {widget_id}`, `layout_target: {layout_target:?}`",);
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(widget_holder) = layout.iter_mut().find(|widget| widget.widget_id == widget_id) else {
|
||||
warn!("handle_widget_callback was called referencing an invalid widget ID, although the layout target was valid. `widget_id: {widget_id}`, `layout_target: {layout_target:?}`",);
|
||||
return;
|
||||
};
|
||||
|
||||
match &mut widget_holder.widget {
|
||||
Widget::BreadcrumbTrailButtons(breadcrumb_trail_buttons) => {
|
||||
let callback_message = match action {
|
||||
WidgetValueAction::Commit => (breadcrumb_trail_buttons.on_commit.callback)(&()),
|
||||
WidgetValueAction::Update => {
|
||||
let update_value = value.as_u64().expect("BreadcrumbTrailButtons update was not of type: u64");
|
||||
(breadcrumb_trail_buttons.on_update.callback)(&update_value)
|
||||
}
|
||||
};
|
||||
responses.add(callback_message);
|
||||
}
|
||||
Widget::CheckboxInput(checkbox_input) => {
|
||||
let callback_message = match action {
|
||||
WidgetValueAction::Commit => (checkbox_input.on_commit.callback)(&()),
|
||||
WidgetValueAction::Update => {
|
||||
let update_value = value.as_bool().expect("CheckboxInput update was not of type: bool");
|
||||
checkbox_input.checked = update_value;
|
||||
(checkbox_input.on_update.callback)(checkbox_input)
|
||||
}
|
||||
};
|
||||
responses.add(callback_message);
|
||||
}
|
||||
Widget::ColorButton(color_button) => {
|
||||
let callback_message = match action {
|
||||
WidgetValueAction::Commit => (color_button.on_commit.callback)(&()),
|
||||
WidgetValueAction::Update => {
|
||||
let update_value = value.as_object().expect("ColorButton update was not of type: object");
|
||||
let parsed_color = (|| {
|
||||
let is_none = update_value.get("none")?.as_bool()?;
|
||||
|
||||
if !is_none {
|
||||
Some(Some(Color::from_rgbaf32(
|
||||
update_value.get("red")?.as_f64()? as f32,
|
||||
update_value.get("green")?.as_f64()? as f32,
|
||||
update_value.get("blue")?.as_f64()? as f32,
|
||||
update_value.get("alpha")?.as_f64()? as f32,
|
||||
)?))
|
||||
} else {
|
||||
Some(None)
|
||||
}
|
||||
})()
|
||||
.unwrap_or_else(|| panic!("ColorButton update was not able to be parsed with color data: {color_button:?}"));
|
||||
color_button.value = parsed_color;
|
||||
(color_button.on_update.callback)(color_button)
|
||||
}
|
||||
};
|
||||
|
||||
responses.add(callback_message);
|
||||
}
|
||||
Widget::CurveInput(curve_input) => {
|
||||
let callback_message = match action {
|
||||
WidgetValueAction::Commit => (curve_input.on_commit.callback)(&()),
|
||||
WidgetValueAction::Update => {
|
||||
let curve = serde_json::from_value(value).expect("CurveInput event data could not be deserialized");
|
||||
curve_input.value = curve;
|
||||
(curve_input.on_update.callback)(curve_input)
|
||||
}
|
||||
};
|
||||
|
||||
responses.add(callback_message);
|
||||
}
|
||||
Widget::DropdownInput(dropdown_input) => {
|
||||
let callback_message = match action {
|
||||
WidgetValueAction::Commit => {
|
||||
let update_value = value.as_u64().expect("DropdownInput commit was not of type: u64");
|
||||
(dropdown_input.entries.iter().flatten().nth(update_value as usize).unwrap().on_commit.callback)(&())
|
||||
}
|
||||
WidgetValueAction::Update => {
|
||||
let update_value = value.as_u64().expect("DropdownInput update was not of type: u64");
|
||||
dropdown_input.selected_index = Some(update_value as u32);
|
||||
(dropdown_input.entries.iter().flatten().nth(update_value as usize).unwrap().on_update.callback)(&())
|
||||
}
|
||||
};
|
||||
|
||||
responses.add(callback_message);
|
||||
}
|
||||
Widget::FontInput(font_input) => {
|
||||
let callback_message = match action {
|
||||
WidgetValueAction::Commit => (font_input.on_commit.callback)(&()),
|
||||
WidgetValueAction::Update => {
|
||||
let update_value = value.as_object().expect("FontInput update was not of type: object");
|
||||
let font_family_value = update_value.get("fontFamily").expect("FontInput update does not have a fontFamily");
|
||||
let font_style_value = update_value.get("fontStyle").expect("FontInput update does not have a fontStyle");
|
||||
|
||||
let font_family = font_family_value.as_str().expect("FontInput update fontFamily was not of type: string");
|
||||
let font_style = font_style_value.as_str().expect("FontInput update fontStyle was not of type: string");
|
||||
|
||||
font_input.font_family = font_family.into();
|
||||
font_input.font_style = font_style.into();
|
||||
|
||||
responses.add(PortfolioMessage::LoadFont {
|
||||
font: Font::new(font_family.into(), font_style.into()),
|
||||
is_default: false,
|
||||
});
|
||||
(font_input.on_update.callback)(font_input)
|
||||
}
|
||||
};
|
||||
|
||||
responses.add(callback_message);
|
||||
}
|
||||
Widget::IconButton(icon_button) => {
|
||||
let callback_message = match action {
|
||||
WidgetValueAction::Commit => (icon_button.on_commit.callback)(&()),
|
||||
WidgetValueAction::Update => (icon_button.on_update.callback)(icon_button),
|
||||
};
|
||||
responses.add(callback_message);
|
||||
}
|
||||
Widget::IconLabel(_) => {}
|
||||
Widget::ImageLabel(_) => {}
|
||||
Widget::InvisibleStandinInput(invisible) => {
|
||||
let callback_message = match action {
|
||||
WidgetValueAction::Commit => (invisible.on_commit.callback)(&()),
|
||||
WidgetValueAction::Update => (invisible.on_update.callback)(&()),
|
||||
};
|
||||
|
||||
responses.add(callback_message);
|
||||
}
|
||||
Widget::NumberInput(number_input) => {
|
||||
match action {
|
||||
WidgetValueAction::Commit => {
|
||||
let callback_message = (number_input.on_commit.callback)(&());
|
||||
responses.add(callback_message);
|
||||
}
|
||||
WidgetValueAction::Update => {
|
||||
match value {
|
||||
Value::Number(num) => {
|
||||
let update_value = num.as_f64().unwrap();
|
||||
number_input.value = Some(update_value);
|
||||
let callback_message = (number_input.on_update.callback)(number_input);
|
||||
responses.add(callback_message);
|
||||
}
|
||||
Value::String(str) => match str.as_str() {
|
||||
"Increment" => responses.add((number_input.increment_callback_increase.callback)(number_input)),
|
||||
"Decrement" => responses.add((number_input.increment_callback_decrease.callback)(number_input)),
|
||||
_ => {
|
||||
panic!("Invalid string found when updating `NumberInput`")
|
||||
}
|
||||
},
|
||||
_ => {} // If it's some other type we could just ignore it and leave the value as is
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Widget::ParameterExposeButton(parameter_expose_button) => {
|
||||
let callback_message = match action {
|
||||
WidgetValueAction::Commit => (parameter_expose_button.on_commit.callback)(&()),
|
||||
WidgetValueAction::Update => (parameter_expose_button.on_update.callback)(parameter_expose_button),
|
||||
};
|
||||
|
||||
responses.add(callback_message);
|
||||
}
|
||||
Widget::PivotInput(pivot_input) => {
|
||||
let callback_message = match action {
|
||||
WidgetValueAction::Commit => (pivot_input.on_commit.callback)(&()),
|
||||
WidgetValueAction::Update => {
|
||||
let update_value = value.as_str().expect("PivotInput update was not of type: u64");
|
||||
pivot_input.position = update_value.into();
|
||||
(pivot_input.on_update.callback)(pivot_input)
|
||||
}
|
||||
};
|
||||
|
||||
responses.add(callback_message);
|
||||
}
|
||||
Widget::PopoverButton(_) => {}
|
||||
Widget::RadioInput(radio_input) => {
|
||||
let update_value = value.as_u64().expect("RadioInput update was not of type: u64");
|
||||
radio_input.selected_index = Some(update_value as u32);
|
||||
let callback_message = match action {
|
||||
WidgetValueAction::Commit => (radio_input.entries[update_value as usize].on_commit.callback)(&()),
|
||||
WidgetValueAction::Update => (radio_input.entries[update_value as usize].on_update.callback)(&()),
|
||||
};
|
||||
|
||||
responses.add(callback_message);
|
||||
}
|
||||
Widget::Separator(_) => {}
|
||||
Widget::TextAreaInput(text_area_input) => {
|
||||
let callback_message = match action {
|
||||
WidgetValueAction::Commit => (text_area_input.on_commit.callback)(&()),
|
||||
WidgetValueAction::Update => {
|
||||
let update_value = value.as_str().expect("TextAreaInput update was not of type: string");
|
||||
text_area_input.value = update_value.into();
|
||||
(text_area_input.on_update.callback)(text_area_input)
|
||||
}
|
||||
};
|
||||
|
||||
responses.add(callback_message);
|
||||
}
|
||||
Widget::TextButton(text_button) => {
|
||||
let callback_message = match action {
|
||||
WidgetValueAction::Commit => (text_button.on_commit.callback)(&()),
|
||||
WidgetValueAction::Update => (text_button.on_update.callback)(text_button),
|
||||
};
|
||||
|
||||
responses.add(callback_message);
|
||||
}
|
||||
Widget::TextInput(text_input) => {
|
||||
let callback_message = match action {
|
||||
WidgetValueAction::Commit => (text_input.on_commit.callback)(&()),
|
||||
WidgetValueAction::Update => {
|
||||
let update_value = value.as_str().expect("TextInput update was not of type: string");
|
||||
text_input.value = update_value.into();
|
||||
(text_input.on_update.callback)(text_input)
|
||||
}
|
||||
};
|
||||
|
||||
responses.add(callback_message);
|
||||
}
|
||||
Widget::TextLabel(_) => {}
|
||||
Widget::WorkingColorsInput(_) => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Fn(&MessageDiscriminant) -> Vec<KeysGroup>> MessageHandler<LayoutMessage, F> for LayoutMessageHandler {
|
||||
|
|
@ -64,150 +292,11 @@ impl<F: Fn(&MessageDiscriminant) -> Vec<KeysGroup>> MessageHandler<LayoutMessage
|
|||
self.send_diff(vec![diff], layout_target, responses, &action_input_mapping);
|
||||
}
|
||||
SendLayout { layout, layout_target } => self.diff_and_send_layout_to_frontend(layout_target, layout, responses, &action_input_mapping),
|
||||
UpdateLayout { layout_target, widget_id, value } => {
|
||||
// Look up the layout
|
||||
let layout = if let Some(layout) = self.layouts.get_mut(layout_target as usize) {
|
||||
layout
|
||||
} else {
|
||||
warn!("UpdateLayout was called referencing an invalid layout. `widget_id: {widget_id}`, `layout_target: {layout_target:?}`",);
|
||||
return;
|
||||
};
|
||||
|
||||
let widget_holder = if let Some(widget_holder) = layout.iter_mut().find(|widget| widget.widget_id == widget_id) {
|
||||
widget_holder
|
||||
} else {
|
||||
warn!("UpdateLayout was called referencing an invalid widget ID, although the layout target was valid. `widget_id: {widget_id}`, `layout_target: {layout_target:?}`",);
|
||||
return;
|
||||
};
|
||||
|
||||
#[remain::sorted]
|
||||
match &mut widget_holder.widget {
|
||||
Widget::BreadcrumbTrailButtons(breadcrumb_trail_buttons) => {
|
||||
let update_value = value.as_u64().expect("BreadcrumbTrailButtons update was not of type: u64");
|
||||
|
||||
let callback_message = (breadcrumb_trail_buttons.on_update.callback)(&update_value);
|
||||
responses.add(callback_message);
|
||||
}
|
||||
Widget::CheckboxInput(checkbox_input) => {
|
||||
let update_value = value.as_bool().expect("CheckboxInput update was not of type: bool");
|
||||
checkbox_input.checked = update_value;
|
||||
let callback_message = (checkbox_input.on_update.callback)(checkbox_input);
|
||||
responses.add(callback_message);
|
||||
}
|
||||
Widget::ColorButton(color_button) => {
|
||||
let update_value = value.as_object().expect("ColorButton update was not of type: object");
|
||||
let parsed_color = (|| {
|
||||
let is_none = update_value.get("none")?.as_bool()?;
|
||||
|
||||
if !is_none {
|
||||
Some(Some(Color::from_rgbaf32(
|
||||
update_value.get("red")?.as_f64()? as f32,
|
||||
update_value.get("green")?.as_f64()? as f32,
|
||||
update_value.get("blue")?.as_f64()? as f32,
|
||||
update_value.get("alpha")?.as_f64()? as f32,
|
||||
)?))
|
||||
} else {
|
||||
Some(None)
|
||||
}
|
||||
})()
|
||||
.unwrap_or_else(|| panic!("ColorButton update was not able to be parsed with color data: {color_button:?}"));
|
||||
color_button.value = parsed_color;
|
||||
let callback_message = (color_button.on_update.callback)(color_button);
|
||||
responses.add(callback_message);
|
||||
}
|
||||
Widget::CurveInput(curve_input) => {
|
||||
let curve = serde_json::from_value(value).expect("CurveInput event data could not be deserialized");
|
||||
curve_input.value = curve;
|
||||
let callback_message = (curve_input.on_update.callback)(curve_input);
|
||||
responses.add(callback_message);
|
||||
}
|
||||
Widget::DropdownInput(dropdown_input) => {
|
||||
let update_value = value.as_u64().expect("DropdownInput update was not of type: u64");
|
||||
dropdown_input.selected_index = Some(update_value as u32);
|
||||
let callback_message = (dropdown_input.entries.iter().flatten().nth(update_value as usize).unwrap().on_update.callback)(&());
|
||||
responses.add(callback_message);
|
||||
}
|
||||
Widget::FontInput(font_input) => {
|
||||
let update_value = value.as_object().expect("FontInput update was not of type: object");
|
||||
let font_family_value = update_value.get("fontFamily").expect("FontInput update does not have a fontFamily");
|
||||
let font_style_value = update_value.get("fontStyle").expect("FontInput update does not have a fontStyle");
|
||||
|
||||
let font_family = font_family_value.as_str().expect("FontInput update fontFamily was not of type: string");
|
||||
let font_style = font_style_value.as_str().expect("FontInput update fontStyle was not of type: string");
|
||||
|
||||
font_input.font_family = font_family.into();
|
||||
font_input.font_style = font_style.into();
|
||||
|
||||
responses.add(PortfolioMessage::LoadFont {
|
||||
font: Font::new(font_family.into(), font_style.into()),
|
||||
is_default: false,
|
||||
});
|
||||
let callback_message = (font_input.on_update.callback)(font_input);
|
||||
responses.add(callback_message);
|
||||
}
|
||||
Widget::IconButton(icon_button) => {
|
||||
let callback_message = (icon_button.on_update.callback)(icon_button);
|
||||
responses.add(callback_message);
|
||||
}
|
||||
Widget::IconLabel(_) => {}
|
||||
Widget::ImageLabel(_) => {}
|
||||
Widget::InvisibleStandinInput(invisible) => {
|
||||
let callback_message = (invisible.on_update.callback)(&());
|
||||
responses.add(callback_message);
|
||||
}
|
||||
Widget::NumberInput(number_input) => match value {
|
||||
Value::Number(num) => {
|
||||
let update_value = num.as_f64().unwrap();
|
||||
number_input.value = Some(update_value);
|
||||
let callback_message = (number_input.on_update.callback)(number_input);
|
||||
responses.add(callback_message);
|
||||
}
|
||||
Value::String(str) => match str.as_str() {
|
||||
"Increment" => responses.add((number_input.increment_callback_increase.callback)(number_input)),
|
||||
"Decrement" => responses.add((number_input.increment_callback_decrease.callback)(number_input)),
|
||||
_ => {
|
||||
panic!("Invalid string found when updating `NumberInput`")
|
||||
}
|
||||
},
|
||||
_ => {} // If it's some other type we could just ignore it and leave the value as is
|
||||
},
|
||||
Widget::ParameterExposeButton(parameter_expose_button) => {
|
||||
let callback_message = (parameter_expose_button.on_update.callback)(parameter_expose_button);
|
||||
responses.add(callback_message);
|
||||
}
|
||||
Widget::PivotInput(pivot_input) => {
|
||||
let update_value = value.as_str().expect("PivotInput update was not of type: u64");
|
||||
pivot_input.position = update_value.into();
|
||||
let callback_message = (pivot_input.on_update.callback)(pivot_input);
|
||||
responses.add(callback_message);
|
||||
}
|
||||
Widget::PopoverButton(_) => {}
|
||||
Widget::RadioInput(radio_input) => {
|
||||
let update_value = value.as_u64().expect("RadioInput update was not of type: u64");
|
||||
radio_input.selected_index = Some(update_value as u32);
|
||||
let callback_message = (radio_input.entries[update_value as usize].on_update.callback)(&());
|
||||
responses.add(callback_message);
|
||||
}
|
||||
Widget::Separator(_) => {}
|
||||
Widget::TextAreaInput(text_area_input) => {
|
||||
let update_value = value.as_str().expect("TextAreaInput update was not of type: string");
|
||||
text_area_input.value = update_value.into();
|
||||
let callback_message = (text_area_input.on_update.callback)(text_area_input);
|
||||
responses.add(callback_message);
|
||||
}
|
||||
Widget::TextButton(text_button) => {
|
||||
let callback_message = (text_button.on_update.callback)(text_button);
|
||||
responses.add(callback_message);
|
||||
}
|
||||
Widget::TextInput(text_input) => {
|
||||
let update_value = value.as_str().expect("TextInput update was not of type: string");
|
||||
text_input.value = update_value.into();
|
||||
let callback_message = (text_input.on_update.callback)(text_input);
|
||||
responses.add(callback_message);
|
||||
}
|
||||
Widget::TextLabel(_) => {}
|
||||
Widget::WorkingColorsInput(_) => {}
|
||||
};
|
||||
WidgetValueCommit { layout_target, widget_id, value } => {
|
||||
self.handle_widget_callback(layout_target, widget_id, value, WidgetValueAction::Commit, responses);
|
||||
}
|
||||
WidgetValueUpdate { layout_target, widget_id, value } => {
|
||||
self.handle_widget_callback(layout_target, widget_id, value, WidgetValueAction::Update, responses);
|
||||
responses.add(ResendActiveWidget { layout_target, widget_id: widget_id });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,10 @@ pub struct IconButton {
|
|||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub on_update: WidgetCallback<IconButton>,
|
||||
|
||||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub on_commit: WidgetCallback<()>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Derivative, WidgetBuilder, specta::Type)]
|
||||
|
|
@ -79,6 +83,10 @@ pub struct ParameterExposeButton {
|
|||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub on_update: WidgetCallback<ParameterExposeButton>,
|
||||
|
||||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub on_commit: WidgetCallback<()>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Derivative, Default, WidgetBuilder, specta::Type)]
|
||||
|
|
@ -111,6 +119,10 @@ pub struct TextButton {
|
|||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub on_update: WidgetCallback<TextButton>,
|
||||
|
||||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub on_commit: WidgetCallback<()>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Derivative, Serialize, Deserialize, WidgetBuilder, specta::Type)]
|
||||
|
|
@ -140,6 +152,10 @@ pub struct ColorButton {
|
|||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub on_update: WidgetCallback<ColorButton>,
|
||||
|
||||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub on_commit: WidgetCallback<()>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Derivative, Default, WidgetBuilder, specta::Type)]
|
||||
|
|
@ -160,4 +176,8 @@ pub struct BreadcrumbTrailButtons {
|
|||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub on_update: WidgetCallback<u64>,
|
||||
|
||||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub on_commit: WidgetCallback<()>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,10 @@ pub struct CheckboxInput {
|
|||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub on_update: WidgetCallback<CheckboxInput>,
|
||||
|
||||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub on_commit: WidgetCallback<()>,
|
||||
}
|
||||
|
||||
impl Default for CheckboxInput {
|
||||
|
|
@ -38,6 +42,7 @@ impl Default for CheckboxInput {
|
|||
tooltip: Default::default(),
|
||||
tooltip_shortcut: Default::default(),
|
||||
on_update: Default::default(),
|
||||
on_commit: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -95,6 +100,10 @@ pub struct MenuListEntry {
|
|||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub on_update: WidgetCallback<()>,
|
||||
|
||||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub on_commit: WidgetCallback<()>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Derivative, WidgetBuilder, specta::Type)]
|
||||
|
|
@ -122,6 +131,10 @@ pub struct FontInput {
|
|||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub on_update: WidgetCallback<FontInput>,
|
||||
|
||||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub on_commit: WidgetCallback<()>,
|
||||
}
|
||||
|
||||
/// This widget allows for the flexible use of the layout system.
|
||||
|
|
@ -133,6 +146,10 @@ pub struct InvisibleStandinInput {
|
|||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub on_update: WidgetCallback<()>,
|
||||
|
||||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub on_commit: WidgetCallback<()>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Derivative, WidgetBuilder, specta::Type)]
|
||||
|
|
@ -204,6 +221,10 @@ pub struct NumberInput {
|
|||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub on_update: WidgetCallback<NumberInput>,
|
||||
|
||||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub on_commit: WidgetCallback<()>,
|
||||
}
|
||||
|
||||
impl NumberInput {
|
||||
|
|
@ -289,6 +310,10 @@ pub struct RadioEntryData {
|
|||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub on_update: WidgetCallback<()>,
|
||||
|
||||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub on_commit: WidgetCallback<()>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Derivative, WidgetBuilder, specta::Type)]
|
||||
|
|
@ -317,6 +342,10 @@ pub struct TextAreaInput {
|
|||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub on_update: WidgetCallback<TextAreaInput>,
|
||||
|
||||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub on_commit: WidgetCallback<()>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Derivative, WidgetBuilder, specta::Type)]
|
||||
|
|
@ -340,6 +369,10 @@ pub struct TextInput {
|
|||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub on_update: WidgetCallback<TextInput>,
|
||||
|
||||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub on_commit: WidgetCallback<()>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Derivative, WidgetBuilder, specta::Type)]
|
||||
|
|
@ -356,6 +389,10 @@ pub struct CurveInput {
|
|||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub on_update: WidgetCallback<CurveInput>,
|
||||
|
||||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub on_commit: WidgetCallback<()>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Derivative, Serialize, Deserialize, WidgetBuilder, specta::Type)]
|
||||
|
|
@ -370,6 +407,10 @@ pub struct PivotInput {
|
|||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub on_update: WidgetCallback<PivotInput>,
|
||||
|
||||
#[serde(skip)]
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||
pub on_commit: WidgetCallback<()>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Serialize, Deserialize, Debug, Default, PartialEq, Eq, specta::Type)]
|
||||
|
|
|
|||
|
|
@ -499,6 +499,8 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
|
|||
// outside of this range can get rounded in f64
|
||||
let random_bits = generate_uuid();
|
||||
let random_value = ((random_bits >> 11) as f64).copysign(f64::from_bits(random_bits & (1 << 63)));
|
||||
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
// Set a random seed input
|
||||
responses.add(NodeGraphMessage::SetInputValue {
|
||||
node_id: *imaginate_node.last().unwrap(),
|
||||
|
|
|
|||
|
|
@ -844,8 +844,6 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
|||
NodeGraphMessage::SetInputValue { node_id, input_index, value } => {
|
||||
if let Some(network) = document_network.nested_network(&self.network) {
|
||||
if let Some(node) = network.nodes.get(&node_id) {
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
|
||||
let input = NodeInput::Value { tagged_value: value, exposed: false };
|
||||
responses.add(NodeGraphMessage::SetNodeInput { node_id, input_index, input });
|
||||
responses.add(PropertiesPanelMessage::Refresh);
|
||||
|
|
|
|||
|
|
@ -36,6 +36,10 @@ fn update_value<T>(value: impl Fn(&T) -> TaggedValue + 'static + Send + Sync, no
|
|||
optionally_update_value(move |v| Some(value(v)), node_id, input_index)
|
||||
}
|
||||
|
||||
fn commit_value<T>(_: &T) -> Message {
|
||||
DocumentMessage::StartTransaction.into()
|
||||
}
|
||||
|
||||
fn expose_widget(node_id: NodeId, index: usize, data_type: FrontendGraphDataType, exposed: bool) -> WidgetHolder {
|
||||
ParameterExposeButton::new()
|
||||
.exposed(exposed)
|
||||
|
|
@ -84,6 +88,7 @@ fn text_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name
|
|||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
TextInput::new(x.clone())
|
||||
.on_update(update_value(|x: &TextInput| TaggedValue::String(x.value.clone()), node_id, index))
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
])
|
||||
}
|
||||
|
|
@ -102,6 +107,7 @@ fn text_area_widget(document_node: &DocumentNode, node_id: NodeId, index: usize,
|
|||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
TextAreaInput::new(x.clone())
|
||||
.on_update(update_value(|x: &TextAreaInput| TaggedValue::String(x.value.clone()), node_id, index))
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
])
|
||||
}
|
||||
|
|
@ -120,6 +126,7 @@ fn bool_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name
|
|||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
CheckboxInput::new(*x)
|
||||
.on_update(update_value(|x: &CheckboxInput| TaggedValue::Bool(x.checked), node_id, index))
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
])
|
||||
}
|
||||
|
|
@ -144,6 +151,7 @@ fn vec2_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name
|
|||
.min(min.unwrap_or(-((1_u64 << std::f64::MANTISSA_DIGITS) as f64)))
|
||||
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
|
||||
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(input.value.unwrap(), dvec2.y)), node_id, index))
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
Separator::new(SeparatorType::Related).widget_holder(),
|
||||
NumberInput::new(Some(dvec2.y))
|
||||
|
|
@ -152,6 +160,7 @@ fn vec2_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name
|
|||
.min(min.unwrap_or(-((1_u64 << std::f64::MANTISSA_DIGITS) as f64)))
|
||||
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
|
||||
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(dvec2.x, input.value.unwrap())), node_id, index))
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
]);
|
||||
} else if let NodeInput::Value {
|
||||
|
|
@ -170,6 +179,7 @@ fn vec2_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name
|
|||
.min(min.unwrap_or(-((1_u64 << std::f64::MANTISSA_DIGITS) as f64)))
|
||||
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
|
||||
.on_update(update_value(update_x, node_id, index))
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
Separator::new(SeparatorType::Related).widget_holder(),
|
||||
NumberInput::new(Some(ivec2.y as f64))
|
||||
|
|
@ -179,6 +189,7 @@ fn vec2_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name
|
|||
.min(min.unwrap_or(-((1_u64 << std::f64::MANTISSA_DIGITS) as f64)))
|
||||
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
|
||||
.on_update(update_value(update_y, node_id, index))
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
]);
|
||||
} else if let NodeInput::Value {
|
||||
|
|
@ -197,6 +208,7 @@ fn vec2_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name
|
|||
.min(min.unwrap_or(0.))
|
||||
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
|
||||
.on_update(update_value(update_x, node_id, index))
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
Separator::new(SeparatorType::Related).widget_holder(),
|
||||
NumberInput::new(Some(uvec2.y as f64))
|
||||
|
|
@ -206,6 +218,7 @@ fn vec2_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name
|
|||
.min(min.unwrap_or(0.))
|
||||
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
|
||||
.on_update(update_value(update_y, node_id, index))
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
]);
|
||||
}
|
||||
|
|
@ -286,6 +299,7 @@ fn font_inputs(document_node: &DocumentNode, node_id: NodeId, index: usize, name
|
|||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
FontInput::new(font.font_family.clone(), font.font_style.clone())
|
||||
.on_update(update_value(from_font_input, node_id, index))
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
]);
|
||||
|
||||
|
|
@ -296,6 +310,7 @@ fn font_inputs(document_node: &DocumentNode, node_id: NodeId, index: usize, name
|
|||
FontInput::new(font.font_family.clone(), font.font_style.clone())
|
||||
.is_style_picker(true)
|
||||
.on_update(update_value(from_font_input, node_id, index))
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
]);
|
||||
second_widgets = Some(second_row);
|
||||
|
|
@ -324,6 +339,7 @@ fn number_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, na
|
|||
number_props
|
||||
.value(Some(x))
|
||||
.on_update(update_value(move |x: &NumberInput| TaggedValue::F64(x.value.unwrap()), node_id, index))
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
])
|
||||
} else if let NodeInput::Value {
|
||||
|
|
@ -336,6 +352,7 @@ fn number_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, na
|
|||
number_props
|
||||
.value(Some(x as f64))
|
||||
.on_update(update_value(move |x: &NumberInput| TaggedValue::U32((x.value.unwrap()) as u32), node_id, index))
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
])
|
||||
} else if let NodeInput::Value {
|
||||
|
|
@ -348,6 +365,7 @@ fn number_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, na
|
|||
number_props
|
||||
.value(Some(x as f64))
|
||||
.on_update(update_value(move |x: &NumberInput| TaggedValue::F32((x.value.unwrap()) as f32), node_id, index))
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
])
|
||||
}
|
||||
|
|
@ -365,7 +383,11 @@ fn color_channel(document_node: &DocumentNode, node_id: NodeId, index: usize, na
|
|||
let calculation_modes = [RedGreenBlue::Red, RedGreenBlue::Green, RedGreenBlue::Blue];
|
||||
let mut entries = Vec::with_capacity(calculation_modes.len());
|
||||
for method in calculation_modes {
|
||||
entries.push(MenuListEntry::new(method.to_string()).on_update(update_value(move |_| TaggedValue::RedGreenBlue(method), node_id, index)));
|
||||
entries.push(
|
||||
MenuListEntry::new(method.to_string())
|
||||
.on_update(update_value(move |_| TaggedValue::RedGreenBlue(method), node_id, index))
|
||||
.on_commit(commit_value),
|
||||
);
|
||||
}
|
||||
let entries = vec![entries];
|
||||
|
||||
|
|
@ -387,7 +409,11 @@ fn noise_type(document_node: &DocumentNode, node_id: NodeId, index: usize, name:
|
|||
{
|
||||
let entries = NoiseType::list()
|
||||
.iter()
|
||||
.map(|noise_type| MenuListEntry::new(noise_type.to_string()).on_update(update_value(move |_| TaggedValue::NoiseType(*noise_type), node_id, index)))
|
||||
.map(|noise_type| {
|
||||
MenuListEntry::new(noise_type.to_string())
|
||||
.on_update(update_value(move |_| TaggedValue::NoiseType(*noise_type), node_id, index))
|
||||
.on_commit(commit_value)
|
||||
})
|
||||
.collect();
|
||||
|
||||
widgets.extend_from_slice(&[
|
||||
|
|
@ -408,7 +434,11 @@ fn fractal_type(document_node: &DocumentNode, node_id: NodeId, index: usize, nam
|
|||
{
|
||||
let entries = FractalType::list()
|
||||
.iter()
|
||||
.map(|fractal_type| MenuListEntry::new(fractal_type.to_string()).on_update(update_value(move |_| TaggedValue::FractalType(*fractal_type), node_id, index)))
|
||||
.map(|fractal_type| {
|
||||
MenuListEntry::new(fractal_type.to_string())
|
||||
.on_update(update_value(move |_| TaggedValue::FractalType(*fractal_type), node_id, index))
|
||||
.on_commit(commit_value)
|
||||
})
|
||||
.collect();
|
||||
|
||||
widgets.extend_from_slice(&[
|
||||
|
|
@ -430,7 +460,9 @@ fn cellular_distance_function(document_node: &DocumentNode, node_id: NodeId, ind
|
|||
let entries = CellularDistanceFunction::list()
|
||||
.iter()
|
||||
.map(|cellular_distance_function| {
|
||||
MenuListEntry::new(cellular_distance_function.to_string()).on_update(update_value(move |_| TaggedValue::CellularDistanceFunction(*cellular_distance_function), node_id, index))
|
||||
MenuListEntry::new(cellular_distance_function.to_string())
|
||||
.on_update(update_value(move |_| TaggedValue::CellularDistanceFunction(*cellular_distance_function), node_id, index))
|
||||
.on_commit(commit_value)
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
|
@ -455,7 +487,11 @@ fn cellular_return_type(document_node: &DocumentNode, node_id: NodeId, index: us
|
|||
{
|
||||
let entries = CellularReturnType::list()
|
||||
.iter()
|
||||
.map(|cellular_return_type| MenuListEntry::new(cellular_return_type.to_string()).on_update(update_value(move |_| TaggedValue::CellularReturnType(*cellular_return_type), node_id, index)))
|
||||
.map(|cellular_return_type| {
|
||||
MenuListEntry::new(cellular_return_type.to_string())
|
||||
.on_update(update_value(move |_| TaggedValue::CellularReturnType(*cellular_return_type), node_id, index))
|
||||
.on_commit(commit_value)
|
||||
})
|
||||
.collect();
|
||||
|
||||
widgets.extend_from_slice(&[
|
||||
|
|
@ -476,7 +512,11 @@ fn domain_warp_type(document_node: &DocumentNode, node_id: NodeId, index: usize,
|
|||
{
|
||||
let entries = DomainWarpType::list()
|
||||
.iter()
|
||||
.map(|domain_warp_type| MenuListEntry::new(domain_warp_type.to_string()).on_update(update_value(move |_| TaggedValue::DomainWarpType(*domain_warp_type), node_id, index)))
|
||||
.map(|domain_warp_type| {
|
||||
MenuListEntry::new(domain_warp_type.to_string())
|
||||
.on_update(update_value(move |_| TaggedValue::DomainWarpType(*domain_warp_type), node_id, index))
|
||||
.on_commit(commit_value)
|
||||
})
|
||||
.collect();
|
||||
|
||||
widgets.extend_from_slice(&[
|
||||
|
|
@ -500,7 +540,11 @@ fn blend_mode(document_node: &DocumentNode, node_id: NodeId, index: usize, name:
|
|||
.map(|category| {
|
||||
category
|
||||
.iter()
|
||||
.map(|blend_mode| MenuListEntry::new(blend_mode.to_string()).on_update(update_value(move |_| TaggedValue::BlendMode(*blend_mode), node_id, index)))
|
||||
.map(|blend_mode| {
|
||||
MenuListEntry::new(blend_mode.to_string())
|
||||
.on_update(update_value(move |_| TaggedValue::BlendMode(*blend_mode), node_id, index))
|
||||
.on_commit(commit_value)
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
|
|
@ -526,7 +570,11 @@ fn luminance_calculation(document_node: &DocumentNode, node_id: NodeId, index: u
|
|||
let calculation_modes = LuminanceCalculation::list();
|
||||
let mut entries = Vec::with_capacity(calculation_modes.len());
|
||||
for method in calculation_modes {
|
||||
entries.push(MenuListEntry::new(method.to_string()).on_update(update_value(move |_| TaggedValue::LuminanceCalculation(method), node_id, index)));
|
||||
entries.push(
|
||||
MenuListEntry::new(method.to_string())
|
||||
.on_update(update_value(move |_| TaggedValue::LuminanceCalculation(method), node_id, index))
|
||||
.on_commit(commit_value),
|
||||
);
|
||||
}
|
||||
let entries = vec![entries];
|
||||
|
||||
|
|
@ -547,7 +595,11 @@ fn line_cap_widget(document_node: &DocumentNode, node_id: NodeId, index: usize,
|
|||
{
|
||||
let entries = [("Butt", LineCap::Butt), ("Round", LineCap::Round), ("Square", LineCap::Square)]
|
||||
.into_iter()
|
||||
.map(|(name, val)| RadioEntryData::new(name).on_update(update_value(move |_| TaggedValue::LineCap(val), node_id, index)))
|
||||
.map(|(name, val)| {
|
||||
RadioEntryData::new(name)
|
||||
.on_update(update_value(move |_| TaggedValue::LineCap(val), node_id, index))
|
||||
.on_commit(commit_value)
|
||||
})
|
||||
.collect();
|
||||
|
||||
widgets.extend_from_slice(&[
|
||||
|
|
@ -567,7 +619,11 @@ fn line_join_widget(document_node: &DocumentNode, node_id: NodeId, index: usize,
|
|||
{
|
||||
let entries = [("Miter", LineJoin::Miter), ("Bevel", LineJoin::Bevel), ("Round", LineJoin::Round)]
|
||||
.into_iter()
|
||||
.map(|(name, val)| RadioEntryData::new(name).on_update(update_value(move |_| TaggedValue::LineJoin(val), node_id, index)))
|
||||
.map(|(name, val)| {
|
||||
RadioEntryData::new(name)
|
||||
.on_update(update_value(move |_| TaggedValue::LineJoin(val), node_id, index))
|
||||
.on_commit(commit_value)
|
||||
})
|
||||
.collect();
|
||||
|
||||
widgets.extend_from_slice(&[
|
||||
|
|
@ -586,8 +642,12 @@ fn fill_type_widget(document_node: &DocumentNode, node_id: NodeId, index: usize)
|
|||
} = &document_node.inputs[index]
|
||||
{
|
||||
let entries = vec![
|
||||
RadioEntryData::new("Solid").on_update(update_value(move |_| TaggedValue::FillType(FillType::Solid), node_id, index)),
|
||||
RadioEntryData::new("Gradient").on_update(update_value(move |_| TaggedValue::FillType(FillType::Gradient), node_id, index)),
|
||||
RadioEntryData::new("Solid")
|
||||
.on_update(update_value(move |_| TaggedValue::FillType(FillType::Solid), node_id, index))
|
||||
.on_commit(commit_value),
|
||||
RadioEntryData::new("Gradient")
|
||||
.on_update(update_value(move |_| TaggedValue::FillType(FillType::Gradient), node_id, index))
|
||||
.on_commit(commit_value),
|
||||
];
|
||||
|
||||
widgets.extend_from_slice(&[
|
||||
|
|
@ -611,8 +671,12 @@ fn gradient_type_widget(document_node: &DocumentNode, node_id: NodeId, index: us
|
|||
} = &document_node.inputs[index]
|
||||
{
|
||||
let entries = vec![
|
||||
RadioEntryData::new("Linear").on_update(update_value(move |_| TaggedValue::GradientType(GradientType::Linear), node_id, index)),
|
||||
RadioEntryData::new("Radial").on_update(update_value(move |_| TaggedValue::GradientType(GradientType::Radial), node_id, index)),
|
||||
RadioEntryData::new("Linear")
|
||||
.on_update(update_value(move |_| TaggedValue::GradientType(GradientType::Linear), node_id, index))
|
||||
.on_commit(commit_value),
|
||||
RadioEntryData::new("Radial")
|
||||
.on_update(update_value(move |_| TaggedValue::GradientType(GradientType::Radial), node_id, index))
|
||||
.on_commit(commit_value),
|
||||
];
|
||||
|
||||
widgets.extend_from_slice(&[
|
||||
|
|
@ -634,7 +698,10 @@ fn gradient_row(row: &mut Vec<WidgetHolder>, positions: &Vec<(f64, Color)>, inde
|
|||
TaggedValue::GradientPositions(new_positions)
|
||||
}
|
||||
};
|
||||
let color = ColorButton::new(Some(positions[index].1)).on_update(update_value(on_update, node_id, input_index)).allow_none(false);
|
||||
let color = ColorButton::new(Some(positions[index].1))
|
||||
.on_update(update_value(on_update, node_id, input_index))
|
||||
.on_commit(commit_value)
|
||||
.allow_none(false);
|
||||
add_blank_assist(row);
|
||||
row.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||
row.push(color.widget_holder());
|
||||
|
|
@ -657,6 +724,7 @@ fn gradient_row(row: &mut Vec<WidgetHolder>, positions: &Vec<(f64, Color)>, inde
|
|||
IconButton::new("Remove", 16)
|
||||
.tooltip("Remove this gradient stop")
|
||||
.on_update(update_value(on_update, node_id, input_index))
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
);
|
||||
}
|
||||
|
|
@ -689,6 +757,7 @@ fn gradient_row(row: &mut Vec<WidgetHolder>, positions: &Vec<(f64, Color)>, inde
|
|||
IconButton::new("Add", 16)
|
||||
.tooltip("Add a gradient stop after this")
|
||||
.on_update(update_value(on_update, node_id, input_index))
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
);
|
||||
}
|
||||
|
|
@ -720,6 +789,7 @@ fn gradient_positions(rows: &mut Vec<LayoutGroup>, document_node: &DocumentNode,
|
|||
.icon(Some("Swap".into()))
|
||||
.tooltip("Reverse the order of each color stop")
|
||||
.on_update(update_value(on_update, node_id, input_index))
|
||||
.on_commit(commit_value)
|
||||
.widget_holder();
|
||||
|
||||
if widgets.is_empty() {
|
||||
|
|
@ -746,6 +816,7 @@ fn color_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, nam
|
|||
color_props
|
||||
.value(Some(x as Color))
|
||||
.on_update(update_value(|x: &ColorButton| TaggedValue::Color(x.value.unwrap()), node_id, index))
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
])
|
||||
} else if let &TaggedValue::OptionalColor(x) = tagged_value {
|
||||
|
|
@ -754,6 +825,7 @@ fn color_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, nam
|
|||
color_props
|
||||
.value(x)
|
||||
.on_update(update_value(|x: &ColorButton| TaggedValue::OptionalColor(x.value), node_id, index))
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
])
|
||||
}
|
||||
|
|
@ -773,6 +845,7 @@ fn curves_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, na
|
|||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||
CurveInput::new(curve.clone())
|
||||
.on_update(update_value(|x: &CurveInput| TaggedValue::Curve(x.value.clone()), node_id, index))
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
])
|
||||
}
|
||||
|
|
@ -1114,9 +1187,15 @@ pub fn adjust_channel_mixer_properties(document_node: &DocumentNode, node_id: No
|
|||
} = &document_node.inputs[output_channel_index]
|
||||
{
|
||||
let entries = vec![
|
||||
RadioEntryData::new(RedGreenBlue::Red.to_string()).on_update(update_value(|_| TaggedValue::RedGreenBlue(RedGreenBlue::Red), node_id, output_channel_index)),
|
||||
RadioEntryData::new(RedGreenBlue::Green.to_string()).on_update(update_value(|_| TaggedValue::RedGreenBlue(RedGreenBlue::Green), node_id, output_channel_index)),
|
||||
RadioEntryData::new(RedGreenBlue::Blue.to_string()).on_update(update_value(|_| TaggedValue::RedGreenBlue(RedGreenBlue::Blue), node_id, output_channel_index)),
|
||||
RadioEntryData::new(RedGreenBlue::Red.to_string())
|
||||
.on_update(update_value(|_| TaggedValue::RedGreenBlue(RedGreenBlue::Red), node_id, output_channel_index))
|
||||
.on_commit(commit_value),
|
||||
RadioEntryData::new(RedGreenBlue::Green.to_string())
|
||||
.on_update(update_value(|_| TaggedValue::RedGreenBlue(RedGreenBlue::Green), node_id, output_channel_index))
|
||||
.on_commit(commit_value),
|
||||
RadioEntryData::new(RedGreenBlue::Blue.to_string())
|
||||
.on_update(update_value(|_| TaggedValue::RedGreenBlue(RedGreenBlue::Blue), node_id, output_channel_index))
|
||||
.on_commit(commit_value),
|
||||
];
|
||||
output_channel.extend([RadioInput::new(entries).selected_index(Some(choice as u32)).widget_holder()]);
|
||||
};
|
||||
|
|
@ -1203,7 +1282,11 @@ pub fn adjust_selective_color_properties(document_node: &DocumentNode, node_id:
|
|||
.map(|section| {
|
||||
section
|
||||
.iter()
|
||||
.map(|choice| MenuListEntry::new(choice.to_string()).on_update(update_value(move |_| TaggedValue::SelectiveColorChoice(*choice), node_id, colors_index)))
|
||||
.map(|choice| {
|
||||
MenuListEntry::new(choice.to_string())
|
||||
.on_update(update_value(move |_| TaggedValue::SelectiveColorChoice(*choice), node_id, colors_index))
|
||||
.on_commit(commit_value)
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
|
|
@ -1247,8 +1330,12 @@ pub fn adjust_selective_color_properties(document_node: &DocumentNode, node_id:
|
|||
} = &document_node.inputs[mode_index]
|
||||
{
|
||||
let entries = vec![
|
||||
RadioEntryData::new("Relative").on_update(update_value(|_| TaggedValue::RelativeAbsolute(RelativeAbsolute::Relative), node_id, mode_index)),
|
||||
RadioEntryData::new("Absolute").on_update(update_value(|_| TaggedValue::RelativeAbsolute(RelativeAbsolute::Absolute), node_id, mode_index)),
|
||||
RadioEntryData::new("Relative")
|
||||
.on_update(update_value(|_| TaggedValue::RelativeAbsolute(RelativeAbsolute::Relative), node_id, mode_index))
|
||||
.on_commit(commit_value),
|
||||
RadioEntryData::new("Absolute")
|
||||
.on_update(update_value(|_| TaggedValue::RelativeAbsolute(RelativeAbsolute::Absolute), node_id, mode_index))
|
||||
.on_commit(commit_value),
|
||||
];
|
||||
mode.push(RadioInput::new(entries).selected_index(Some(relative_or_absolute as u32)).widget_holder());
|
||||
};
|
||||
|
|
@ -1439,6 +1526,7 @@ pub fn transform_properties(document_node: &DocumentNode, node_id: NodeId, _cont
|
|||
widgets.push(
|
||||
PivotInput::new(pivot.into())
|
||||
.on_update(update_value(|pivot: &PivotInput| TaggedValue::DVec2(Into::<Option<DVec2>>::into(pivot.position).unwrap()), node_id, 5))
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
);
|
||||
} else {
|
||||
|
|
@ -1469,6 +1557,7 @@ pub fn transform_properties(document_node: &DocumentNode, node_id: NodeId, _cont
|
|||
node_id,
|
||||
index,
|
||||
))
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
]);
|
||||
}
|
||||
|
|
@ -1695,6 +1784,7 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
|||
.min(-((1_u64 << f64::MANTISSA_DIGITS) as f64))
|
||||
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
||||
.on_update(update_value(move |input: &NumberInput| TaggedValue::F64(input.value.unwrap()), node_id, seed_index))
|
||||
.on_commit(commit_value)
|
||||
.mode(NumberInputMode::Increment)
|
||||
.widget_holder(),
|
||||
])
|
||||
|
|
@ -1762,6 +1852,7 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
|||
node_id,
|
||||
resolution_index,
|
||||
))
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
Separator::new(SeparatorType::Related).widget_holder(),
|
||||
NumberInput::new(Some(vec2.x))
|
||||
|
|
@ -1775,6 +1866,7 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
|||
node_id,
|
||||
resolution_index,
|
||||
))
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
Separator::new(SeparatorType::Related).widget_holder(),
|
||||
NumberInput::new(Some(vec2.y))
|
||||
|
|
@ -1788,6 +1880,7 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
|||
node_id,
|
||||
resolution_index,
|
||||
))
|
||||
.on_commit(commit_value)
|
||||
.widget_holder(),
|
||||
])
|
||||
}
|
||||
|
|
@ -1815,7 +1908,11 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
|||
let sampling_methods = ImaginateSamplingMethod::list();
|
||||
let mut entries = Vec::with_capacity(sampling_methods.len());
|
||||
for method in sampling_methods {
|
||||
entries.push(MenuListEntry::new(method.to_string()).on_update(update_value(move |_| TaggedValue::ImaginateSamplingMethod(method), node_id, sampling_method_index)));
|
||||
entries.push(
|
||||
MenuListEntry::new(method.to_string())
|
||||
.on_update(update_value(move |_| TaggedValue::ImaginateSamplingMethod(method), node_id, sampling_method_index))
|
||||
.on_commit(commit_value),
|
||||
);
|
||||
}
|
||||
let entries = vec![entries];
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
|
||||
const editor = getContext<Editor>("editor");
|
||||
|
||||
const dispatch = createEventDispatcher<{ color: Color }>();
|
||||
const dispatch = createEventDispatcher<{ color: Color; start: undefined }>();
|
||||
|
||||
export let color: Color;
|
||||
export let allowNone = false;
|
||||
|
|
@ -149,6 +149,8 @@
|
|||
function addEvents() {
|
||||
document.addEventListener("pointermove", onPointerMove);
|
||||
document.addEventListener("pointerup", onPointerUp);
|
||||
|
||||
dispatch("start");
|
||||
}
|
||||
|
||||
function removeEvents() {
|
||||
|
|
|
|||
|
|
@ -57,8 +57,16 @@
|
|||
return widgets;
|
||||
}
|
||||
|
||||
function updateLayout(index: number, value: unknown) {
|
||||
editor.instance.updateLayout(layoutTarget, widgets[index].widgetId, value);
|
||||
function widgetValueCommit(index: number, value: unknown) {
|
||||
editor.instance.widgetValueCommit(layoutTarget, widgets[index].widgetId, value);
|
||||
}
|
||||
|
||||
function widgetValueUpdate(index: number, value: unknown) {
|
||||
editor.instance.widgetValueUpdate(layoutTarget, widgets[index].widgetId, value);
|
||||
}
|
||||
|
||||
function widgetValueCommitAndUpdate(index: number, value: unknown) {
|
||||
editor.instance.widgetValueCommitAndUpdate(layoutTarget, widgets[index].widgetId, value);
|
||||
}
|
||||
|
||||
// TODO: This seems to work, but verify the correctness and terseness of this, it's adapted from https://stackoverflow.com/a/67434028/775283
|
||||
|
|
@ -76,31 +84,31 @@
|
|||
{#each widgets as component, index}
|
||||
{@const checkboxInput = narrowWidgetProps(component.props, "CheckboxInput")}
|
||||
{#if checkboxInput}
|
||||
<CheckboxInput {...exclude(checkboxInput)} on:checked={({ detail }) => updateLayout(index, detail)} />
|
||||
<CheckboxInput {...exclude(checkboxInput)} on:checked={({ detail }) => widgetValueCommitAndUpdate(index, detail)} />
|
||||
{/if}
|
||||
{@const colorInput = narrowWidgetProps(component.props, "ColorButton")}
|
||||
{#if colorInput}
|
||||
<ColorButton {...exclude(colorInput)} on:value={({ detail }) => updateLayout(index, detail)} />
|
||||
<ColorButton {...exclude(colorInput)} on:value={({ detail }) => widgetValueUpdate(index, detail)} on:startHistoryTransaction={() => widgetValueCommit(index, colorInput.value)} />
|
||||
{/if}
|
||||
{@const curvesInput = narrowWidgetProps(component.props, "CurveInput")}
|
||||
{#if curvesInput}
|
||||
<CurveInput {...exclude(curvesInput)} on:value={({ detail }) => debouncer((value) => updateLayout(index, value), { debounceTime: 120 }).debounceUpdateValue(detail)} />
|
||||
<CurveInput {...exclude(curvesInput)} on:value={({ detail }) => debouncer((value) => widgetValueCommitAndUpdate(index, value), { debounceTime: 120 }).debounceUpdateValue(detail)} />
|
||||
{/if}
|
||||
{@const dropdownInput = narrowWidgetProps(component.props, "DropdownInput")}
|
||||
{#if dropdownInput}
|
||||
<DropdownInput {...exclude(dropdownInput)} on:selectedIndex={({ detail }) => updateLayout(index, detail)} />
|
||||
<DropdownInput {...exclude(dropdownInput)} on:selectedIndex={({ detail }) => widgetValueCommitAndUpdate(index, detail)} />
|
||||
{/if}
|
||||
{@const fontInput = narrowWidgetProps(component.props, "FontInput")}
|
||||
{#if fontInput}
|
||||
<FontInput {...exclude(fontInput)} on:changeFont={({ detail }) => updateLayout(index, detail)} />
|
||||
<FontInput {...exclude(fontInput)} on:changeFont={({ detail }) => widgetValueCommitAndUpdate(index, detail)} />
|
||||
{/if}
|
||||
{@const parameterExposeButton = narrowWidgetProps(component.props, "ParameterExposeButton")}
|
||||
{#if parameterExposeButton}
|
||||
<ParameterExposeButton {...exclude(parameterExposeButton)} action={() => updateLayout(index, undefined)} />
|
||||
<ParameterExposeButton {...exclude(parameterExposeButton)} action={() => widgetValueCommitAndUpdate(index, undefined)} />
|
||||
{/if}
|
||||
{@const iconButton = narrowWidgetProps(component.props, "IconButton")}
|
||||
{#if iconButton}
|
||||
<IconButton {...exclude(iconButton)} action={() => updateLayout(index, undefined)} />
|
||||
<IconButton {...exclude(iconButton)} action={() => widgetValueCommitAndUpdate(index, undefined)} />
|
||||
{/if}
|
||||
{@const iconLabel = narrowWidgetProps(component.props, "IconLabel")}
|
||||
{#if iconLabel}
|
||||
|
|
@ -114,14 +122,15 @@
|
|||
{#if numberInput}
|
||||
<NumberInput
|
||||
{...exclude(numberInput)}
|
||||
on:value={({ detail }) => debouncer((value) => updateLayout(index, value)).debounceUpdateValue(detail)}
|
||||
incrementCallbackIncrease={() => updateLayout(index, "Increment")}
|
||||
incrementCallbackDecrease={() => updateLayout(index, "Decrement")}
|
||||
on:value={({ detail }) => debouncer((value) => widgetValueUpdate(index, value)).debounceUpdateValue(detail)}
|
||||
on:startHistoryTransaction={() => widgetValueCommit(index, numberInput.value)}
|
||||
incrementCallbackIncrease={() => widgetValueCommitAndUpdate(index, "Increment")}
|
||||
incrementCallbackDecrease={() => widgetValueCommitAndUpdate(index, "Decrement")}
|
||||
/>
|
||||
{/if}
|
||||
{@const pivotInput = narrowWidgetProps(component.props, "PivotInput")}
|
||||
{#if pivotInput}
|
||||
<PivotInput {...exclude(pivotInput)} on:position={({ detail }) => updateLayout(index, detail)} />
|
||||
<PivotInput {...exclude(pivotInput)} on:position={({ detail }) => widgetValueCommitAndUpdate(index, detail)} />
|
||||
{/if}
|
||||
{@const popoverButton = narrowWidgetProps(component.props, "PopoverButton")}
|
||||
{#if popoverButton}
|
||||
|
|
@ -136,7 +145,7 @@
|
|||
{/if}
|
||||
{@const radioInput = narrowWidgetProps(component.props, "RadioInput")}
|
||||
{#if radioInput}
|
||||
<RadioInput {...exclude(radioInput)} on:selectedIndex={({ detail }) => updateLayout(index, detail)} />
|
||||
<RadioInput {...exclude(radioInput)} on:selectedIndex={({ detail }) => widgetValueCommitAndUpdate(index, detail)} />
|
||||
{/if}
|
||||
{@const separator = narrowWidgetProps(component.props, "Separator")}
|
||||
{#if separator}
|
||||
|
|
@ -148,19 +157,19 @@
|
|||
{/if}
|
||||
{@const textAreaInput = narrowWidgetProps(component.props, "TextAreaInput")}
|
||||
{#if textAreaInput}
|
||||
<TextAreaInput {...exclude(textAreaInput)} on:commitText={({ detail }) => updateLayout(index, detail)} />
|
||||
<TextAreaInput {...exclude(textAreaInput)} on:commitText={({ detail }) => widgetValueCommitAndUpdate(index, detail)} />
|
||||
{/if}
|
||||
{@const textButton = narrowWidgetProps(component.props, "TextButton")}
|
||||
{#if textButton}
|
||||
<TextButton {...exclude(textButton)} action={() => updateLayout(index, undefined)} />
|
||||
<TextButton {...exclude(textButton)} action={() => widgetValueCommitAndUpdate(index, undefined)} />
|
||||
{/if}
|
||||
{@const breadcrumbTrailButtons = narrowWidgetProps(component.props, "BreadcrumbTrailButtons")}
|
||||
{#if breadcrumbTrailButtons}
|
||||
<BreadcrumbTrailButtons {...exclude(breadcrumbTrailButtons)} action={(index) => updateLayout(index, index)} />
|
||||
<BreadcrumbTrailButtons {...exclude(breadcrumbTrailButtons)} action={(index) => widgetValueCommitAndUpdate(index, index)} />
|
||||
{/if}
|
||||
{@const textInput = narrowWidgetProps(component.props, "TextInput")}
|
||||
{#if textInput}
|
||||
<TextInput {...exclude(textInput)} on:commitText={({ detail }) => updateLayout(index, detail)} />
|
||||
<TextInput {...exclude(textInput)} on:commitText={({ detail }) => widgetValueCommitAndUpdate(index, detail)} />
|
||||
{/if}
|
||||
{@const textLabel = narrowWidgetProps(component.props, "TextLabel")}
|
||||
{#if textLabel}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
import LayoutCol from "@graphite/components/layout/LayoutCol.svelte";
|
||||
import TextLabel from "@graphite/components/widgets/labels/TextLabel.svelte";
|
||||
|
||||
const dispatch = createEventDispatcher<{ value: Color }>();
|
||||
const dispatch = createEventDispatcher<{ value: Color; startHistoryTransaction: undefined }>();
|
||||
|
||||
let open = false;
|
||||
|
||||
|
|
@ -31,6 +31,11 @@
|
|||
value = detail;
|
||||
dispatch("value", detail);
|
||||
}}
|
||||
on:startHistoryTransaction={() => {
|
||||
// This event is sent to the backend so it knows to start a transaction for the history system. See discussion for some explanation:
|
||||
// <https://github.com/GraphiteEditor/Graphite/pull/1584#discussion_r1477592483>
|
||||
dispatch("startHistoryTransaction");
|
||||
}}
|
||||
{allowNone}
|
||||
/>
|
||||
</LayoutCol>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
const BUTTON_LEFT = 0;
|
||||
const BUTTON_RIGHT = 2;
|
||||
|
||||
const dispatch = createEventDispatcher<{ value: number | undefined }>();
|
||||
const dispatch = createEventDispatcher<{ value: number | undefined; startHistoryTransaction: undefined }>();
|
||||
|
||||
// Label
|
||||
export let label: string | undefined = undefined;
|
||||
|
|
@ -293,6 +293,9 @@
|
|||
initialValueBeforeDragging = value;
|
||||
cumulativeDragDelta = 0;
|
||||
|
||||
// Tell the backend that we are beginning a transaction for the history system
|
||||
startDragging();
|
||||
|
||||
// We ignore the first event invocation's `e.movementX` value because it's unreliable.
|
||||
// In both Chrome and Firefox (tested on Windows 10), the first `e.movementX` value is occasionally a very large number
|
||||
// (around positive 1000, even if movement was in the negative direction). This seems to happen more often if the movement is rapid.
|
||||
|
|
@ -430,6 +433,9 @@
|
|||
// We're dragging now, so that's the new state.
|
||||
rangeSliderClickDragState = "Dragging";
|
||||
|
||||
// Tell the backend that we are beginning a transaction for the history system
|
||||
startDragging();
|
||||
|
||||
// We want to begin watching for an abort while dragging the slider.
|
||||
addEventListener("pointermove", sliderAbortFromDragging);
|
||||
addEventListener("keydown", sliderAbortFromDragging);
|
||||
|
|
@ -474,6 +480,12 @@
|
|||
removeEventListener("keydown", sliderAbortFromDragging);
|
||||
}
|
||||
|
||||
function startDragging() {
|
||||
// This event is sent to the backend so it knows to start a transaction for the history system. See discussion for some explanation:
|
||||
// <https://github.com/GraphiteEditor/Graphite/pull/1584#discussion_r1477592483>
|
||||
dispatch("startHistoryTransaction");
|
||||
}
|
||||
|
||||
// We want to let the user abort while dragging the slider by right clicking or pressing Escape.
|
||||
// This function also helps recover and clean up if the window loses focus while dragging the slider.
|
||||
// Since we reuse the function for both the "pointermove" and "keydown" events, it is split into parts that only run for a `PointerEvent` or `KeyboardEvent`.
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@
|
|||
...entry,
|
||||
|
||||
// Shared names with fields that need to be converted from the type used in `MenuBarEntry` to that of `MenuListEntry`
|
||||
action: () => editor.instance.updateLayout(updateMenuBarLayout.layoutTarget, entry.action.widgetId, undefined),
|
||||
action: () => editor.instance.widgetValueCommitAndUpdate(updateMenuBarLayout.layoutTarget, entry.action.widgetId, undefined),
|
||||
children: entry.children ? entry.children.map((entries) => entries.map((entry) => menuBarEntryToMenuListEntry(entry))) : undefined,
|
||||
|
||||
// New fields in `MenuListEntry`
|
||||
|
|
|
|||
|
|
@ -249,13 +249,13 @@ impl JsEditorHandle {
|
|||
FILE_SAVE_SUFFIX.into()
|
||||
}
|
||||
|
||||
/// Update layout of a given UI
|
||||
#[wasm_bindgen(js_name = updateLayout)]
|
||||
pub fn update_layout(&self, layout_target: JsValue, widget_id: u64, value: JsValue) -> Result<(), JsValue> {
|
||||
/// Update the value of a given UI widget, but don't commit it to the history (unless `commit_layout()` is called, which handles that)
|
||||
#[wasm_bindgen(js_name = widgetValueUpdate)]
|
||||
pub fn widget_value_update(&self, layout_target: JsValue, widget_id: u64, value: JsValue) -> Result<(), JsValue> {
|
||||
let widget_id = WidgetId(widget_id);
|
||||
match (from_value(layout_target), from_value(value)) {
|
||||
(Ok(layout_target), Ok(value)) => {
|
||||
let message = LayoutMessage::UpdateLayout { layout_target, widget_id, value };
|
||||
let message = LayoutMessage::WidgetValueUpdate { layout_target, widget_id, value };
|
||||
self.dispatch(message);
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -263,6 +263,28 @@ impl JsEditorHandle {
|
|||
}
|
||||
}
|
||||
|
||||
/// Commit the value of a given UI widget to the history
|
||||
#[wasm_bindgen(js_name = widgetValueCommit)]
|
||||
pub fn widget_value_commit(&self, layout_target: JsValue, widget_id: u64, value: JsValue) -> Result<(), JsValue> {
|
||||
let widget_id = WidgetId(widget_id);
|
||||
match (from_value(layout_target), from_value(value)) {
|
||||
(Ok(layout_target), Ok(value)) => {
|
||||
let message = LayoutMessage::WidgetValueCommit { layout_target, widget_id, value };
|
||||
self.dispatch(message);
|
||||
Ok(())
|
||||
}
|
||||
(target, val) => Err(Error::new(&format!("Could not commit UI\nDetails:\nTarget: {target:?}\nValue: {val:?}")).into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the value of a given UI widget, and commit it to the history
|
||||
#[wasm_bindgen(js_name = widgetValueCommitAndUpdate)]
|
||||
pub fn widget_value_commit_and_update(&self, layout_target: JsValue, widget_id: u64, value: JsValue) -> Result<(), JsValue> {
|
||||
self.widget_value_commit(layout_target.clone(), widget_id, value.clone())?;
|
||||
self.widget_value_update(layout_target, widget_id, value)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = loadPreferences)]
|
||||
pub fn load_preferences(&self, preferences: String) {
|
||||
let message = PreferencesMessage::Load { preferences };
|
||||
|
|
|
|||
Loading…
Reference in New Issue