diff --git a/editor/src/document/properties_panel_message_handler.rs b/editor/src/document/properties_panel_message_handler.rs index baf75392e9..930f272bb8 100644 --- a/editor/src/document/properties_panel_message_handler.rs +++ b/editor/src/document/properties_panel_message_handler.rs @@ -2,7 +2,7 @@ use super::layer_panel::LayerDataTypeDiscriminant; use crate::document::properties_panel_message::TransformOp; use crate::layout::layout_message::LayoutTarget; use crate::layout::widgets::{ - IconLabel, LayoutRow, NumberInput, PopoverButton, Separator, SeparatorDirection, SeparatorType, TextInput, TextLabel, Widget, WidgetCallback, WidgetHolder, WidgetLayout, + ColorInput, IconLabel, LayoutRow, NumberInput, PopoverButton, Separator, SeparatorDirection, SeparatorType, TextInput, TextLabel, Widget, WidgetCallback, WidgetHolder, WidgetLayout, }; use crate::message_prelude::*; @@ -424,9 +424,9 @@ fn node_section_fill(fill: &Fill) -> Option { separator_type: SeparatorType::Related, direction: SeparatorDirection::Horizontal, })), - WidgetHolder::new(Widget::TextInput(TextInput { + WidgetHolder::new(Widget::ColorInput(ColorInput { value: color.rgba_hex(), - on_update: WidgetCallback::new(|text_input: &TextInput| { + on_update: WidgetCallback::new(|text_input: &ColorInput| { if let Some(color) = Color::from_rgba_str(&text_input.value).or(Color::from_rgb_str(&text_input.value)) { let new_fill = Fill::Solid(color); PropertiesPanelMessage::ModifyFill { fill: new_fill }.into() @@ -455,9 +455,9 @@ fn node_section_fill(fill: &Fill) -> Option { separator_type: SeparatorType::Related, direction: SeparatorDirection::Horizontal, })), - WidgetHolder::new(Widget::TextInput(TextInput { + WidgetHolder::new(Widget::ColorInput(ColorInput { value: gradient_1.positions[0].1.rgba_hex(), - on_update: WidgetCallback::new(move |text_input: &TextInput| { + on_update: WidgetCallback::new(move |text_input: &ColorInput| { if let Some(color) = Color::from_rgba_str(&text_input.value).or(Color::from_rgb_str(&text_input.value)) { let mut new_gradient = (*gradient_1).clone(); new_gradient.positions[0].1 = color; @@ -483,9 +483,9 @@ fn node_section_fill(fill: &Fill) -> Option { separator_type: SeparatorType::Related, direction: SeparatorDirection::Horizontal, })), - WidgetHolder::new(Widget::TextInput(TextInput { + WidgetHolder::new(Widget::ColorInput(ColorInput { value: gradient_2.positions[1].1.rgba_hex(), - on_update: WidgetCallback::new(move |text_input: &TextInput| { + on_update: WidgetCallback::new(move |text_input: &ColorInput| { if let Some(color) = Color::from_rgba_str(&text_input.value).or(Color::from_rgb_str(&text_input.value)) { let mut new_gradient = (*gradient_2).clone(); new_gradient.positions[1].1 = color; @@ -524,9 +524,9 @@ fn node_section_stroke(stroke: &Stroke) -> LayoutRow { separator_type: SeparatorType::Related, direction: SeparatorDirection::Horizontal, })), - WidgetHolder::new(Widget::TextInput(TextInput { + WidgetHolder::new(Widget::ColorInput(ColorInput { value: stroke.color().rgba_hex(), - on_update: WidgetCallback::new(move |text_input: &TextInput| { + on_update: WidgetCallback::new(move |text_input: &ColorInput| { PropertiesPanelMessage::ModifyStroke { color: text_input.value.clone(), weight: weight as f64, diff --git a/editor/src/layout/layout_message_handler.rs b/editor/src/layout/layout_message_handler.rs index 4bedcf66e0..4f15409729 100644 --- a/editor/src/layout/layout_message_handler.rs +++ b/editor/src/layout/layout_message_handler.rs @@ -80,17 +80,23 @@ impl MessageHandler for LayoutMessageHandler { responses.push_back(callback_message); } Widget::RadioInput(radio_input) => { - let update_value = value.as_u64().expect("OptionalInput update was not of type: u64"); + let update_value = value.as_u64().expect("RadioInput update was not of type: u64"); radio_input.selected_index = update_value as u32; let callback_message = (radio_input.entries[update_value as usize].on_update.callback)(&()); responses.push_back(callback_message); } Widget::TextInput(text_input) => { - let update_value = value.as_str().expect("OptionalInput update was not of type: string"); + 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.push_back(callback_message); } + Widget::ColorInput(color_input) => { + let update_value = value.as_str().expect("ColorInput update was not of type: string"); + color_input.value = update_value.into(); + let callback_message = (color_input.on_update.callback)(color_input); + responses.push_back(callback_message); + } Widget::TextLabel(_) => {} }; self.send_layout(layout_target, responses); diff --git a/editor/src/layout/widgets.rs b/editor/src/layout/widgets.rs index f0e7119fd6..b5137caa6c 100644 --- a/editor/src/layout/widgets.rs +++ b/editor/src/layout/widgets.rs @@ -150,6 +150,7 @@ impl Default for WidgetCallback { #[remain::sorted] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum Widget { + ColorInput(ColorInput), IconButton(IconButton), IconLabel(IconLabel), NumberInput(NumberInput), @@ -199,6 +200,15 @@ pub struct TextInput { pub on_update: WidgetCallback, } +#[derive(Clone, Serialize, Deserialize, Derivative)] +#[derivative(Debug, PartialEq, Default)] +pub struct ColorInput { + pub value: String, + #[serde(skip)] + #[derivative(Debug = "ignore", PartialEq = "ignore")] + pub on_update: WidgetCallback, +} + #[derive(Clone, Serialize, Deserialize, Debug, PartialEq)] pub enum NumberInputIncrementBehavior { Add, diff --git a/frontend/src/components/widgets/WidgetRow.vue b/frontend/src/components/widgets/WidgetRow.vue index 98a3eb2704..e1b7480f97 100644 --- a/frontend/src/components/widgets/WidgetRow.vue +++ b/frontend/src/components/widgets/WidgetRow.vue @@ -15,6 +15,7 @@ :incrementCallbackDecrease="() => updateLayout(component.widget_id, 'Decrement')" /> + @@ -41,6 +42,7 @@ import { WidgetRow } from "@/dispatcher/js-messages"; import IconButton from "@/components/widgets/buttons/IconButton.vue"; import PopoverButton from "@/components/widgets/buttons/PopoverButton.vue"; +import ColorInput from "@/components/widgets/inputs/ColorInput.vue"; import NumberInput from "@/components/widgets/inputs/NumberInput.vue"; import OptionalInput from "@/components/widgets/inputs/OptionalInput.vue"; import RadioInput from "@/components/widgets/inputs/RadioInput.vue"; @@ -70,6 +72,7 @@ export default defineComponent({ RadioInput, TextLabel, IconLabel, + ColorInput, }, }); diff --git a/frontend/src/components/widgets/inputs/ColorInput.vue b/frontend/src/components/widgets/inputs/ColorInput.vue new file mode 100644 index 0000000000..ab4ef92b07 --- /dev/null +++ b/frontend/src/components/widgets/inputs/ColorInput.vue @@ -0,0 +1,95 @@ + + + + + diff --git a/frontend/src/dispatcher/js-messages.ts b/frontend/src/dispatcher/js-messages.ts index 2c2325a0ed..03f65ec4a3 100644 --- a/frontend/src/dispatcher/js-messages.ts +++ b/frontend/src/dispatcher/js-messages.ts @@ -412,7 +412,7 @@ export function isWidgetSection(layoutRow: WidgetRow | WidgetSection): layoutRow return Boolean((layoutRow as WidgetSection).layout); } -export type WidgetKind = "NumberInput" | "Separator" | "IconButton" | "PopoverButton" | "OptionalInput" | "RadioInput" | "TextInput" | "TextLabel" | "IconLabel"; +export type WidgetKind = "NumberInput" | "Separator" | "IconButton" | "PopoverButton" | "OptionalInput" | "RadioInput" | "TextInput" | "TextLabel" | "IconLabel" | "ColorInput"; export interface Widget { kind: WidgetKind;