diff --git a/editor/src/messages/dialog/export_dialog/export_dialog_message_handler.rs b/editor/src/messages/dialog/export_dialog/export_dialog_message_handler.rs index 5c4d6b49..1bcf4b70 100644 --- a/editor/src/messages/dialog/export_dialog/export_dialog_message_handler.rs +++ b/editor/src/messages/dialog/export_dialog/export_dialog_message_handler.rs @@ -143,12 +143,14 @@ impl LayoutHolder for ExportDialogMessageHandler { DropdownInput::new(entries).selected_index(Some(index as u32)).widget_holder(), ]; + let mut checkbox_id = CheckboxId::default(); let transparent_background = vec![ - TextLabel::new("Transparency").table_align(true).min_width(100).widget_holder(), + TextLabel::new("Transparency").table_align(true).min_width(100).for_checkbox(&mut checkbox_id).widget_holder(), Separator::new(SeparatorType::Unrelated).widget_holder(), CheckboxInput::new(self.transparent_background) .disabled(self.file_type == FileType::Jpg) .on_update(move |value: &CheckboxInput| ExportDialogMessage::TransparentBackground(value.checked).into()) + .for_label(checkbox_id.clone()) .widget_holder(), ]; diff --git a/editor/src/messages/dialog/new_document_dialog/new_document_dialog_message_handler.rs b/editor/src/messages/dialog/new_document_dialog/new_document_dialog_message_handler.rs index fd095df9..ba30adb0 100644 --- a/editor/src/messages/dialog/new_document_dialog/new_document_dialog_message_handler.rs +++ b/editor/src/messages/dialog/new_document_dialog/new_document_dialog_message_handler.rs @@ -77,11 +77,13 @@ impl LayoutHolder for NewDocumentDialogMessageHandler { .widget_holder(), ]; + let mut checkbox_id = CheckboxId::default(); let infinite = vec![ - TextLabel::new("Infinite Canvas").table_align(true).min_width(90).widget_holder(), + TextLabel::new("Infinite Canvas").table_align(true).min_width(90).for_checkbox(&mut checkbox_id).widget_holder(), Separator::new(SeparatorType::Unrelated).widget_holder(), CheckboxInput::new(self.infinite) .on_update(|checkbox_input: &CheckboxInput| NewDocumentDialogMessage::Infinite(checkbox_input.checked).into()) + .for_label(checkbox_id.clone()) .widget_holder(), ]; diff --git a/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message_handler.rs b/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message_handler.rs index 5c4aa75f..ab46077e 100644 --- a/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message_handler.rs +++ b/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message_handler.rs @@ -66,6 +66,7 @@ impl PreferencesDialogMessageHandler { .widget_holder(), ]; + let mut checkbox_id = CheckboxId::default(); let zoom_with_scroll_tooltip = "Use the scroll wheel for zooming instead of vertically panning (not recommended for trackpads)"; let zoom_with_scroll = vec![ Separator::new(SeparatorType::Unrelated).widget_holder(), @@ -78,8 +79,13 @@ impl PreferencesDialogMessageHandler { } .into() }) + .for_label(checkbox_id.clone()) + .widget_holder(), + TextLabel::new("Zoom with Scroll") + .table_align(true) + .tooltip(zoom_with_scroll_tooltip) + .for_checkbox(&mut checkbox_id) .widget_holder(), - TextLabel::new("Zoom with Scroll").table_align(true).tooltip(zoom_with_scroll_tooltip).widget_holder(), ]; // ======= @@ -161,6 +167,7 @@ impl PreferencesDialogMessageHandler { graph_wire_style, ]; + let mut checkbox_id = CheckboxId::default(); let vello_tooltip = "Use the experimental Vello renderer (your browser must support WebGPU)"; let use_vello = vec![ Separator::new(SeparatorType::Unrelated).widget_holder(), @@ -169,14 +176,17 @@ impl PreferencesDialogMessageHandler { .tooltip(vello_tooltip) .disabled(!preferences.supports_wgpu()) .on_update(|checkbox_input: &CheckboxInput| PreferencesMessage::UseVello { use_vello: checkbox_input.checked }.into()) + .for_label(checkbox_id.clone()) .widget_holder(), TextLabel::new("Vello Renderer") .table_align(true) .tooltip(vello_tooltip) .disabled(!preferences.supports_wgpu()) + .for_checkbox(&mut checkbox_id) .widget_holder(), ]; + let mut checkbox_id = CheckboxId::default(); let vector_mesh_tooltip = "Allow tools to produce vector meshes, where more than two segments can connect to an anchor point.\n\nCurrently this does not properly handle line joins and fills."; let vector_meshes = vec![ Separator::new(SeparatorType::Unrelated).widget_holder(), @@ -184,8 +194,13 @@ impl PreferencesDialogMessageHandler { CheckboxInput::new(preferences.vector_meshes) .tooltip(vector_mesh_tooltip) .on_update(|checkbox_input: &CheckboxInput| PreferencesMessage::VectorMeshes { enabled: checkbox_input.checked }.into()) + .for_label(checkbox_id.clone()) + .widget_holder(), + TextLabel::new("Vector Meshes") + .table_align(true) + .tooltip(vector_mesh_tooltip) + .for_checkbox(&mut checkbox_id) .widget_holder(), - TextLabel::new("Vector Meshes").table_align(true).tooltip(vector_mesh_tooltip).widget_holder(), ]; // TODO: Reenable when Imaginate is restored diff --git a/editor/src/messages/layout/utility_types/widgets/input_widgets.rs b/editor/src/messages/layout/utility_types/widgets/input_widgets.rs index fe917b11..fea9c201 100644 --- a/editor/src/messages/layout/utility_types/widgets/input_widgets.rs +++ b/editor/src/messages/layout/utility_types/widgets/input_widgets.rs @@ -5,6 +5,8 @@ use graphene_core::Color; use graphene_core::raster::curve::Curve; use graphene_std::transform::ReferencePoint; use graphite_proc_macros::WidgetBuilder; +use once_cell::sync::OnceCell; +use std::sync::Arc; #[derive(Clone, Derivative, serde::Serialize, serde::Deserialize, WidgetBuilder, specta::Type)] #[derivative(Debug, PartialEq)] @@ -18,6 +20,9 @@ pub struct CheckboxInput { pub tooltip: String, + #[serde(rename = "forLabel", skip_serializing_if = "checkbox_id_is_empty")] + pub for_label: CheckboxId, + #[serde(skip)] pub tooltip_shortcut: Option, @@ -39,12 +44,51 @@ impl Default for CheckboxInput { icon: "Checkmark".into(), tooltip: Default::default(), tooltip_shortcut: Default::default(), + for_label: CheckboxId::default(), on_update: Default::default(), on_commit: Default::default(), } } } +#[derive(Clone, Default, Debug, Eq, PartialEq)] +pub struct CheckboxId(Arc>); + +impl CheckboxId { + pub fn fill(&mut self) { + let _ = self.0.set(graphene_core::uuid::generate_uuid()); + } +} +impl specta::Type for CheckboxId { + fn inline(_type_map: &mut specta::TypeCollection, _generics: specta::Generics) -> specta::datatype::DataType { + // TODO: This might not be right, but it works for now. We just need the type `bigint | undefined`. + specta::datatype::DataType::Primitive(specta::datatype::PrimitiveType::u64) + } +} +impl serde::Serialize for CheckboxId { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.0.get().copied().serialize(serializer) + } +} +impl<'a> serde::Deserialize<'a> for CheckboxId { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'a>, + { + let id = u64::deserialize(deserializer)?; + let checkbox_id = CheckboxId(OnceCell::new().into()); + checkbox_id.0.set(id).map_err(serde::de::Error::custom)?; + Ok(checkbox_id) + } +} + +fn checkbox_id_is_empty(id: &CheckboxId) -> bool { + id.0.get().is_none() +} + #[derive(Clone, serde::Serialize, serde::Deserialize, Derivative, WidgetBuilder, specta::Type)] #[derivative(Debug, PartialEq, Default)] pub struct DropdownInput { diff --git a/editor/src/messages/layout/utility_types/widgets/label_widgets.rs b/editor/src/messages/layout/utility_types/widgets/label_widgets.rs index 548552b0..caeca5a4 100644 --- a/editor/src/messages/layout/utility_types/widgets/label_widgets.rs +++ b/editor/src/messages/layout/utility_types/widgets/label_widgets.rs @@ -1,3 +1,4 @@ +use super::input_widgets::CheckboxId; use derivative::*; use graphite_proc_macros::WidgetBuilder; @@ -56,9 +57,21 @@ pub struct TextLabel { pub tooltip: String, + #[serde(rename = "checkboxId")] + #[widget_builder(skip)] + pub checkbox_id: CheckboxId, + // Body #[widget_builder(constructor)] pub value: String, } +impl TextLabel { + pub fn for_checkbox(mut self, id: &mut CheckboxId) -> Self { + id.fill(); + self.checkbox_id = id.clone(); + self + } +} + // TODO: Add UserInputLabel diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index b4d50dbd..8d5734ea 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -2141,165 +2141,212 @@ impl DocumentMessageHandler { widgets: vec![TextLabel::new("General").widget_holder()], }, LayoutGroup::Row { - widgets: vec![ - CheckboxInput::new(self.overlays_visibility_settings.artboard_name) - .on_update(|optional_input: &CheckboxInput| { - DocumentMessage::SetOverlaysVisibility { - visible: optional_input.checked, - overlays_type: Some(OverlaysType::ArtboardName), - } - .into() - }) - .widget_holder(), - TextLabel::new("Artboard Name".to_string()).widget_holder(), - ], + widgets: { + let mut checkbox_id = CheckboxId::default(); + vec![ + CheckboxInput::new(self.overlays_visibility_settings.artboard_name) + .on_update(|optional_input: &CheckboxInput| { + DocumentMessage::SetOverlaysVisibility { + visible: optional_input.checked, + overlays_type: Some(OverlaysType::ArtboardName), + } + .into() + }) + .for_label(checkbox_id.clone()) + .widget_holder(), + TextLabel::new("Artboard Name".to_string()).for_checkbox(&mut checkbox_id).widget_holder(), + ] + }, }, LayoutGroup::Row { - widgets: vec![ - CheckboxInput::new(self.overlays_visibility_settings.transform_measurement) - .on_update(|optional_input: &CheckboxInput| { - DocumentMessage::SetOverlaysVisibility { - visible: optional_input.checked, - overlays_type: Some(OverlaysType::TransformMeasurement), - } - .into() - }) - .widget_holder(), - TextLabel::new("G/R/S Measurement".to_string()).widget_holder(), - ], + widgets: { + let mut checkbox_id = CheckboxId::default(); + vec![ + CheckboxInput::new(self.overlays_visibility_settings.transform_measurement) + .on_update(|optional_input: &CheckboxInput| { + DocumentMessage::SetOverlaysVisibility { + visible: optional_input.checked, + overlays_type: Some(OverlaysType::TransformMeasurement), + } + .into() + }) + .for_label(checkbox_id.clone()) + .widget_holder(), + TextLabel::new("G/R/S Measurement".to_string()).for_checkbox(&mut checkbox_id).widget_holder(), + ] + }, }, LayoutGroup::Row { widgets: vec![TextLabel::new("Select Tool").widget_holder()], }, LayoutGroup::Row { - widgets: vec![ - CheckboxInput::new(self.overlays_visibility_settings.quick_measurement) - .on_update(|optional_input: &CheckboxInput| { - DocumentMessage::SetOverlaysVisibility { - visible: optional_input.checked, - overlays_type: Some(OverlaysType::QuickMeasurement), - } - .into() - }) - .widget_holder(), - TextLabel::new("Quick Measurement".to_string()).widget_holder(), - ], + widgets: { + let mut checkbox_id = CheckboxId::default(); + vec![ + CheckboxInput::new(self.overlays_visibility_settings.quick_measurement) + .on_update(|optional_input: &CheckboxInput| { + DocumentMessage::SetOverlaysVisibility { + visible: optional_input.checked, + overlays_type: Some(OverlaysType::QuickMeasurement), + } + .into() + }) + .for_label(checkbox_id.clone()) + .widget_holder(), + TextLabel::new("Quick Measurement".to_string()).for_checkbox(&mut checkbox_id).widget_holder(), + ] + }, }, LayoutGroup::Row { - widgets: vec![ - CheckboxInput::new(self.overlays_visibility_settings.transform_cage) - .on_update(|optional_input: &CheckboxInput| { - DocumentMessage::SetOverlaysVisibility { - visible: optional_input.checked, - overlays_type: Some(OverlaysType::TransformCage), - } - .into() - }) - .widget_holder(), - TextLabel::new("Transform Cage".to_string()).widget_holder(), - ], + widgets: { + let mut checkbox_id = CheckboxId::default(); + vec![ + CheckboxInput::new(self.overlays_visibility_settings.transform_cage) + .on_update(|optional_input: &CheckboxInput| { + DocumentMessage::SetOverlaysVisibility { + visible: optional_input.checked, + overlays_type: Some(OverlaysType::TransformCage), + } + .into() + }) + .for_label(checkbox_id.clone()) + .widget_holder(), + TextLabel::new("Transform Cage".to_string()).for_checkbox(&mut checkbox_id).widget_holder(), + ] + }, }, LayoutGroup::Row { - widgets: vec![ - CheckboxInput::new(self.overlays_visibility_settings.compass_rose) - .on_update(|optional_input: &CheckboxInput| { - DocumentMessage::SetOverlaysVisibility { - visible: optional_input.checked, - overlays_type: Some(OverlaysType::CompassRose), - } - .into() - }) - .widget_holder(), - TextLabel::new("Transform Dial".to_string()).widget_holder(), - ], + widgets: { + let mut checkbox_id = CheckboxId::default(); + vec![ + CheckboxInput::new(self.overlays_visibility_settings.compass_rose) + .on_update(|optional_input: &CheckboxInput| { + DocumentMessage::SetOverlaysVisibility { + visible: optional_input.checked, + overlays_type: Some(OverlaysType::CompassRose), + } + .into() + }) + .for_label(checkbox_id.clone()) + .widget_holder(), + TextLabel::new("Transform Dial".to_string()).for_checkbox(&mut checkbox_id).widget_holder(), + ] + }, }, LayoutGroup::Row { - widgets: vec![ - CheckboxInput::new(self.overlays_visibility_settings.pivot) - .on_update(|optional_input: &CheckboxInput| { - DocumentMessage::SetOverlaysVisibility { - visible: optional_input.checked, - overlays_type: Some(OverlaysType::Pivot), - } - .into() - }) - .widget_holder(), - TextLabel::new("Transform Pivot".to_string()).widget_holder(), - ], + widgets: { + let mut checkbox_id = CheckboxId::default(); + vec![ + CheckboxInput::new(self.overlays_visibility_settings.pivot) + .on_update(|optional_input: &CheckboxInput| { + DocumentMessage::SetOverlaysVisibility { + visible: optional_input.checked, + overlays_type: Some(OverlaysType::Pivot), + } + .into() + }) + .for_label(checkbox_id.clone()) + .widget_holder(), + TextLabel::new("Transform Pivot".to_string()).for_checkbox(&mut checkbox_id).widget_holder(), + ] + }, }, LayoutGroup::Row { - widgets: vec![ - CheckboxInput::new(self.overlays_visibility_settings.hover_outline) - .on_update(|optional_input: &CheckboxInput| { - DocumentMessage::SetOverlaysVisibility { - visible: optional_input.checked, - overlays_type: Some(OverlaysType::HoverOutline), - } - .into() - }) - .widget_holder(), - TextLabel::new("Hover Outline".to_string()).widget_holder(), - ], + widgets: { + let mut checkbox_id = CheckboxId::default(); + vec![ + CheckboxInput::new(self.overlays_visibility_settings.hover_outline) + .on_update(|optional_input: &CheckboxInput| { + DocumentMessage::SetOverlaysVisibility { + visible: optional_input.checked, + overlays_type: Some(OverlaysType::HoverOutline), + } + .into() + }) + .for_label(checkbox_id.clone()) + .widget_holder(), + TextLabel::new("Hover Outline".to_string()).for_checkbox(&mut checkbox_id).widget_holder(), + ] + }, }, LayoutGroup::Row { - widgets: vec![ - CheckboxInput::new(self.overlays_visibility_settings.selection_outline) - .on_update(|optional_input: &CheckboxInput| { - DocumentMessage::SetOverlaysVisibility { - visible: optional_input.checked, - overlays_type: Some(OverlaysType::SelectionOutline), - } - .into() - }) - .widget_holder(), - TextLabel::new("Selection Outline".to_string()).widget_holder(), - ], + widgets: { + let mut checkbox_id = CheckboxId::default(); + vec![ + CheckboxInput::new(self.overlays_visibility_settings.selection_outline) + .on_update(|optional_input: &CheckboxInput| { + DocumentMessage::SetOverlaysVisibility { + visible: optional_input.checked, + overlays_type: Some(OverlaysType::SelectionOutline), + } + .into() + }) + .for_label(checkbox_id.clone()) + .widget_holder(), + TextLabel::new("Selection Outline".to_string()).for_checkbox(&mut checkbox_id).widget_holder(), + ] + }, }, LayoutGroup::Row { widgets: vec![TextLabel::new("Pen & Path Tools").widget_holder()], }, LayoutGroup::Row { - widgets: vec![ - CheckboxInput::new(self.overlays_visibility_settings.path) - .on_update(|optional_input: &CheckboxInput| { - DocumentMessage::SetOverlaysVisibility { - visible: optional_input.checked, - overlays_type: Some(OverlaysType::Path), - } - .into() - }) - .widget_holder(), - TextLabel::new("Path".to_string()).widget_holder(), - ], + widgets: { + let mut checkbox_id = CheckboxId::default(); + vec![ + CheckboxInput::new(self.overlays_visibility_settings.path) + .on_update(|optional_input: &CheckboxInput| { + DocumentMessage::SetOverlaysVisibility { + visible: optional_input.checked, + overlays_type: Some(OverlaysType::Path), + } + .into() + }) + .for_label(checkbox_id.clone()) + .widget_holder(), + TextLabel::new("Path".to_string()).for_checkbox(&mut checkbox_id).widget_holder(), + ] + }, }, LayoutGroup::Row { - widgets: vec![ - CheckboxInput::new(self.overlays_visibility_settings.anchors) - .on_update(|optional_input: &CheckboxInput| { - DocumentMessage::SetOverlaysVisibility { - visible: optional_input.checked, - overlays_type: Some(OverlaysType::Anchors), - } - .into() - }) - .widget_holder(), - TextLabel::new("Anchors".to_string()).widget_holder(), - ], + widgets: { + let mut checkbox_id = CheckboxId::default(); + vec![ + CheckboxInput::new(self.overlays_visibility_settings.anchors) + .on_update(|optional_input: &CheckboxInput| { + DocumentMessage::SetOverlaysVisibility { + visible: optional_input.checked, + overlays_type: Some(OverlaysType::Anchors), + } + .into() + }) + .for_label(checkbox_id.clone()) + .widget_holder(), + TextLabel::new("Anchors".to_string()).for_checkbox(&mut checkbox_id).widget_holder(), + ] + }, }, LayoutGroup::Row { - widgets: vec![ - CheckboxInput::new(self.overlays_visibility_settings.handles) - .disabled(!self.overlays_visibility_settings.anchors) - .on_update(|optional_input: &CheckboxInput| { - DocumentMessage::SetOverlaysVisibility { - visible: optional_input.checked, - overlays_type: Some(OverlaysType::Handles), - } - .into() - }) - .widget_holder(), - TextLabel::new("Handles".to_string()).disabled(!self.overlays_visibility_settings.anchors).widget_holder(), - ], + widgets: { + let mut checkbox_id = CheckboxId::default(); + vec![ + CheckboxInput::new(self.overlays_visibility_settings.handles) + .disabled(!self.overlays_visibility_settings.anchors) + .on_update(|optional_input: &CheckboxInput| { + DocumentMessage::SetOverlaysVisibility { + visible: optional_input.checked, + overlays_type: Some(OverlaysType::Handles), + } + .into() + }) + .for_label(checkbox_id.clone()) + .widget_holder(), + TextLabel::new("Handles".to_string()) + .disabled(!self.overlays_visibility_settings.anchors) + .for_checkbox(&mut checkbox_id) + .widget_holder(), + ] + }, }, ]) .widget_holder(), @@ -2328,25 +2375,45 @@ impl DocumentMessageHandler { ] .into_iter() .chain(SNAP_FUNCTIONS_FOR_BOUNDING_BOXES.into_iter().map(|(name, closure, tooltip)| LayoutGroup::Row { - widgets: vec![ - CheckboxInput::new(*closure(&mut snapping_state)) - .on_update(move |input: &CheckboxInput| DocumentMessage::SetSnapping { closure: Some(closure), snapping_state: input.checked }.into()) - .tooltip(tooltip) - .widget_holder(), - TextLabel::new(name).tooltip(tooltip).widget_holder(), - ], + widgets: { + let mut checkbox_id = CheckboxId::default(); + vec![ + CheckboxInput::new(*closure(&mut snapping_state)) + .on_update(move |input: &CheckboxInput| { + DocumentMessage::SetSnapping { + closure: Some(closure), + snapping_state: input.checked, + } + .into() + }) + .tooltip(tooltip) + .for_label(checkbox_id.clone()) + .widget_holder(), + TextLabel::new(name).tooltip(tooltip).for_checkbox(&mut checkbox_id).widget_holder(), + ] + }, })) .chain([LayoutGroup::Row { widgets: vec![TextLabel::new(SnappingOptions::Paths.to_string()).widget_holder()], }]) .chain(SNAP_FUNCTIONS_FOR_PATHS.into_iter().map(|(name, closure, tooltip)| LayoutGroup::Row { - widgets: vec![ - CheckboxInput::new(*closure(&mut snapping_state2)) - .on_update(move |input: &CheckboxInput| DocumentMessage::SetSnapping { closure: Some(closure), snapping_state: input.checked }.into()) - .tooltip(tooltip) - .widget_holder(), - TextLabel::new(name).tooltip(tooltip).widget_holder(), - ], + widgets: { + let mut checkbox_id = CheckboxId::default(); + vec![ + CheckboxInput::new(*closure(&mut snapping_state2)) + .on_update(move |input: &CheckboxInput| { + DocumentMessage::SetSnapping { + closure: Some(closure), + snapping_state: input.checked, + } + .into() + }) + .tooltip(tooltip) + .for_label(checkbox_id.clone()) + .widget_holder(), + TextLabel::new(name).tooltip(tooltip).for_checkbox(&mut checkbox_id).widget_holder(), + ] + }, })) .collect(), ) diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs index cb490069..e480016f 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs @@ -1851,11 +1851,6 @@ impl NodeGraphMessageHandler { // Separator::new(SeparatorType::Unrelated).widget_holder(), // - IconButton::new("NewLayer", 24) - .tooltip("New Layer") - .tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::CreateEmptyFolder)) - .on_update(|_| DocumentMessage::CreateEmptyFolder.into()) - .widget_holder(), IconButton::new("Folder", 24) .tooltip("Group Selected") .tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::GroupSelectedLayers)) @@ -1865,6 +1860,11 @@ impl NodeGraphMessageHandler { }) .disabled(!has_selection) .widget_holder(), + IconButton::new("NewLayer", 24) + .tooltip("New Layer") + .tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::CreateEmptyFolder)) + .on_update(|_| DocumentMessage::CreateEmptyFolder.into()) + .widget_holder(), IconButton::new("Trash", 24) .tooltip("Delete Selected") .tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::DeleteSelectedLayers)) diff --git a/editor/src/messages/portfolio/document/node_graph/node_properties_imaginate.rs b/editor/src/messages/portfolio/document/node_graph/node_properties_imaginate.rs index 118760a3..7034e4af 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_properties_imaginate.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_properties_imaginate.rs @@ -339,6 +339,7 @@ // resolution_index, // )) // .on_commit(commit_value) +// .for_label(checkbox_id.clone()) // .widget_holder(), // Separator::new(SeparatorType::Related).widget_holder(), // NumberInput::new(Some(vec2.x)) @@ -438,7 +439,7 @@ // LayoutGroup::Row { widgets }.with_tooltip("A negative text prompt can be used to list things like objects or colors to avoid") // }; // let base_image = { -// let widgets = bool_widget(document_node, node_id, base_img_index, "Adapt Input Image", CheckboxInput::default(), true); +// let widgets = bool_widget(document_node, node_id, base_img_index, "Adapt Input Image", CheckboxInput::default().for_label(checkbox_id.clone()), true); // LayoutGroup::Row { widgets }.with_tooltip("Generate an image based upon the bitmap data plugged into this node") // }; // let image_creativity = { @@ -529,7 +530,7 @@ // // } // let improve_faces = { -// let widgets = bool_widget(document_node, node_id, faces_index, "Improve Faces", CheckboxInput::default(), true); +// let widgets = bool_widget(document_node, node_id, faces_index, "Improve Faces", CheckboxInput::default().for_label(checkbox_id.clone()), true); // LayoutGroup::Row { widgets }.with_tooltip( // "Postprocess human (or human-like) faces to look subtly less distorted.\n\ // \n\ @@ -537,7 +538,7 @@ // ) // }; // let tiling = { -// let widgets = bool_widget(document_node, node_id, tiling_index, "Tiling", CheckboxInput::default(), true); +// let widgets = bool_widget(document_node, node_id, tiling_index, "Tiling", CheckboxInput::default().for_label(checkbox_id.clone()), true); // LayoutGroup::Row { widgets }.with_tooltip("Generate the image so its edges loop seamlessly to make repeatable patterns or textures") // }; // layout.extend_from_slice(&[improve_faces, tiling]); diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index d08778db..ade55f3f 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -181,6 +181,7 @@ impl LayoutHolder for PathTool { }) // TODO: Remove `unwrap_or_default` once checkboxes are capable of displaying a mixed state .unwrap_or_default(); + let mut checkbox_id = CheckboxId::default(); let colinear_handle_checkbox = CheckboxInput::new(colinear_handles_state) .disabled(!self.tool_data.can_toggle_colinearity) .on_update(|&CheckboxInput { checked, .. }| { @@ -191,10 +192,12 @@ impl LayoutHolder for PathTool { } }) .tooltip(colinear_handles_tooltip) + .for_label(checkbox_id.clone()) .widget_holder(); let colinear_handles_label = TextLabel::new("Colinear Handles") .disabled(!self.tool_data.can_toggle_colinearity) .tooltip(colinear_handles_tooltip) + .for_checkbox(&mut checkbox_id) .widget_holder(); let path_overlay_mode_widget = RadioInput::new(vec![ diff --git a/frontend/src/components/panels/Layers.svelte b/frontend/src/components/panels/Layers.svelte index 23b12e56..cc9ea37c 100644 --- a/frontend/src/components/panels/Layers.svelte +++ b/frontend/src/components/panels/Layers.svelte @@ -549,8 +549,10 @@ // Layer hierarchy .list-area { - margin: 4px 0; position: relative; + margin-top: 4px; + // Combine with the bottom bar to avoid a double border + margin-bottom: -1px; .layer { flex: 0 0 auto; diff --git a/frontend/src/components/widgets/inputs/CheckboxInput.svelte b/frontend/src/components/widgets/inputs/CheckboxInput.svelte index 2abf1315..f8e53eea 100644 --- a/frontend/src/components/widgets/inputs/CheckboxInput.svelte +++ b/frontend/src/components/widgets/inputs/CheckboxInput.svelte @@ -12,11 +12,13 @@ export let disabled = false; export let icon: IconName = "Checkmark"; export let tooltip: string | undefined = undefined; + export let forLabel: bigint | undefined = undefined; let inputElement: HTMLInputElement | undefined; - let id = String(Math.random()).substring(2); + const backupId = String(Math.random()).substring(2); + $: id = forLabel !== undefined ? String(forLabel) : backupId; $: displayIcon = (!checked && icon === "Checkmark" ? "Empty12px" : icon) as IconName; export function isChecked() { diff --git a/frontend/src/components/widgets/labels/TextLabel.svelte b/frontend/src/components/widgets/labels/TextLabel.svelte index 5619269c..540aa164 100644 --- a/frontend/src/components/widgets/labels/TextLabel.svelte +++ b/frontend/src/components/widgets/labels/TextLabel.svelte @@ -13,6 +13,7 @@ export let minWidth = 0; export let multiline = false; export let tooltip: string | undefined = undefined; + export let checkboxId: bigint | undefined = undefined; $: extraClasses = Object.entries(classes) .flatMap(([className, stateName]) => (stateName ? [className] : [])) @@ -22,7 +23,7 @@ .join(" "); - 0 ? `${minWidth}px` : undefined} style={`${styleName} ${extraStyles}`.trim() || undefined} title={tooltip} + for={checkboxId !== undefined ? `checkbox-input-${checkboxId}` : undefined} > - +