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 35e27cfba..74034200a 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 @@ -89,7 +89,6 @@ impl PreferencesDialogMessageHandler { .tooltip_label("Zoom with Scroll") .tooltip_description(zoom_with_scroll_description) .for_checkbox(checkbox_id) - .table_align(true) .widget_instance(), ]; @@ -102,7 +101,10 @@ impl PreferencesDialogMessageHandler { let selection_label = vec![ Separator::new(SeparatorType::Unrelated).widget_instance(), Separator::new(SeparatorType::Unrelated).widget_instance(), - TextLabel::new("Selection").widget_instance(), + TextLabel::new("Selection") + .tooltip_label("Selection") + .tooltip_description("Choose how targets are selected within dragged rectangular and lasso areas.") + .widget_instance(), ]; let selection_mode = RadioInput::new(vec![ @@ -151,7 +153,7 @@ impl PreferencesDialogMessageHandler { let experimental_header = vec![TextLabel::new("Experimental").italic(true).widget_instance()]; - let node_graph_section_description = "Appearance of the wires running between node connections in the graph."; + let node_graph_section_description = "Configure the appearance of the wires running between node connections in the graph."; let node_graph_wires_label = vec![ Separator::new(SeparatorType::Unrelated).widget_instance(), Separator::new(SeparatorType::Unrelated).widget_instance(), @@ -181,13 +183,18 @@ impl PreferencesDialogMessageHandler { ]; let checkbox_id = CheckboxId::new(); - let vello_description = "Use the experimental Vello renderer. (Your browser must support WebGPU)."; + let vello_description = "Use the experimental Vello renderer instead of SVG-based rendering.".to_string(); + #[cfg(target_family = "wasm")] + let mut vello_description = vello_description; + #[cfg(target_family = "wasm")] + vello_description.push_str("\n\n(Your browser must support WebGPU.)"); + let use_vello = vec![ Separator::new(SeparatorType::Unrelated).widget_instance(), Separator::new(SeparatorType::Unrelated).widget_instance(), CheckboxInput::new(preferences.use_vello && preferences.supports_wgpu()) .tooltip_label("Vello Renderer") - .tooltip_description(vello_description) + .tooltip_description(vello_description.clone()) .disabled(!preferences.supports_wgpu()) .on_update(|checkbox_input: &CheckboxInput| PreferencesMessage::UseVello { use_vello: checkbox_input.checked }.into()) .for_label(checkbox_id) @@ -197,14 +204,14 @@ impl PreferencesDialogMessageHandler { .tooltip_description(vello_description) .disabled(!preferences.supports_wgpu()) .for_checkbox(checkbox_id) - .table_align(true) .widget_instance(), ]; let checkbox_id = CheckboxId::new(); let vector_mesh_description = " - Allow tools to produce vector meshes, where more than two segments can connect to an anchor point.\n\ - Currently this does not properly handle stroke joins and fills. + Allow the Pen tool to produce branching geometry, where more than two segments may be connected to one anchor point.\n\ + \n\ + Currently, vector meshes do not properly render strokes (branching joins) and fills (multiple regions). " .trim(); let vector_meshes = vec![ @@ -220,23 +227,58 @@ impl PreferencesDialogMessageHandler { .tooltip_label("Vector Meshes") .tooltip_description(vector_mesh_description) .for_checkbox(checkbox_id) - .table_align(true) + .widget_instance(), + ]; + + let checkbox_id = CheckboxId::new(); + let brush_tool_description = " + Enable the Brush tool to support basic raster-based layer painting.\n\ + \n\ + This legacy tool has performance and quality limitations and is slated for replacement in future versions of Graphite that will focus on raster graphics editing. + " + .trim(); + let brush_tool = vec![ + Separator::new(SeparatorType::Unrelated).widget_instance(), + Separator::new(SeparatorType::Unrelated).widget_instance(), + CheckboxInput::new(preferences.brush_tool) + .tooltip_label("Brush Tool") + .tooltip_description(brush_tool_description) + .on_update(|checkbox_input: &CheckboxInput| PreferencesMessage::BrushTool { enabled: checkbox_input.checked }.into()) + .for_label(checkbox_id) + .widget_instance(), + TextLabel::new("Brush Tool") + .tooltip_label("Brush Tool") + .tooltip_description(brush_tool_description) + .for_checkbox(checkbox_id) .widget_instance(), ]; Layout(vec![ + // NAVIGATION LayoutGroup::Row { widgets: navigation_header }, + // Navigation: Zoom Rate LayoutGroup::Row { widgets: zoom_rate_label }, LayoutGroup::Row { widgets: zoom_rate }, + // Navigation: Zoom with Scroll LayoutGroup::Row { widgets: zoom_with_scroll }, + // + // EDITING LayoutGroup::Row { widgets: editing_header }, + // Editing: Selection LayoutGroup::Row { widgets: selection_label }, LayoutGroup::Row { widgets: selection_mode }, + // + // EXPERIMENTAL LayoutGroup::Row { widgets: experimental_header }, + // Experimental: Node Graph Wires LayoutGroup::Row { widgets: node_graph_wires_label }, LayoutGroup::Row { widgets: graph_wire_style }, + // Experimental: Vello Renderer LayoutGroup::Row { widgets: use_vello }, + // Experimental: Vector Meshes LayoutGroup::Row { widgets: vector_meshes }, + // Experimental: Brush Tool + LayoutGroup::Row { widgets: brush_tool }, ]) } diff --git a/editor/src/messages/preferences/preferences_message.rs b/editor/src/messages/preferences/preferences_message.rs index c0dc72382..0b58e8c1f 100644 --- a/editor/src/messages/preferences/preferences_message.rs +++ b/editor/src/messages/preferences/preferences_message.rs @@ -13,6 +13,7 @@ pub enum PreferencesMessage { UseVello { use_vello: bool }, SelectionMode { selection_mode: SelectionMode }, VectorMeshes { enabled: bool }, + BrushTool { enabled: bool }, ModifyLayout { zoom_with_scroll: bool }, GraphWireStyle { style: GraphWireStyle }, ViewportZoomWheelRate { rate: f64 }, diff --git a/editor/src/messages/preferences/preferences_message_handler.rs b/editor/src/messages/preferences/preferences_message_handler.rs index 8c61fa78a..06080b23c 100644 --- a/editor/src/messages/preferences/preferences_message_handler.rs +++ b/editor/src/messages/preferences/preferences_message_handler.rs @@ -11,6 +11,7 @@ pub struct PreferencesMessageHandler { pub zoom_with_scroll: bool, pub use_vello: bool, pub vector_meshes: bool, + pub brush_tool: bool, pub graph_wire_style: GraphWireStyle, pub viewport_zoom_wheel_rate: f64, } @@ -38,6 +39,7 @@ impl Default for PreferencesMessageHandler { zoom_with_scroll: matches!(MappingVariant::default(), MappingVariant::ZoomWithScroll), use_vello: EditorPreferences::default().use_vello, vector_meshes: false, + brush_tool: false, graph_wire_style: GraphWireStyle::default(), viewport_zoom_wheel_rate: VIEWPORT_ZOOM_WHEEL_RATE, } @@ -76,6 +78,10 @@ impl MessageHandler for PreferencesMessageHandler { PreferencesMessage::VectorMeshes { enabled } => { self.vector_meshes = enabled; } + PreferencesMessage::BrushTool { enabled } => { + self.brush_tool = enabled; + responses.add(ToolMessage::RefreshToolShelf); + } PreferencesMessage::ModifyLayout { zoom_with_scroll } => { self.zoom_with_scroll = zoom_with_scroll; diff --git a/editor/src/messages/tool/tool_message.rs b/editor/src/messages/tool/tool_message.rs index 70668a26d..02f28e019 100644 --- a/editor/src/messages/tool/tool_message.rs +++ b/editor/src/messages/tool/tool_message.rs @@ -79,6 +79,7 @@ pub enum ToolMessage { PreUndo, Redo, RefreshToolOptions, + RefreshToolShelf, ResetColors, SelectWorkingColor { color: Color, diff --git a/editor/src/messages/tool/tool_message_handler.rs b/editor/src/messages/tool/tool_message_handler.rs index f9516d933..65edd2045 100644 --- a/editor/src/messages/tool/tool_message_handler.rs +++ b/editor/src/messages/tool/tool_message_handler.rs @@ -101,7 +101,7 @@ impl MessageHandler> for ToolMessageHandler let tool_type = tool_type.get_tool(); responses.add(ToolMessage::RefreshToolOptions); - tool_data.send_layout(responses, LayoutTarget::ToolShelf); + responses.add(ToolMessage::RefreshToolShelf); // Do nothing if switching to the same tool if self.tool_is_active && tool_type == old_tool { @@ -176,7 +176,7 @@ impl MessageHandler> for ToolMessageHandler responses.add(ToolMessage::RefreshToolOptions); // Notify the frontend about the new active tool to be displayed - tool_data.send_layout(responses, LayoutTarget::ToolShelf); + responses.add(ToolMessage::RefreshToolShelf); } ToolMessage::DeactivateTools => { let tool_data = &mut self.tool_state.tool_data; @@ -220,7 +220,7 @@ impl MessageHandler> for ToolMessageHandler tool_data.tools.get(active_tool).unwrap().send_layout(responses, LayoutTarget::ToolOptions); // Notify the frontend about the initial active tool - tool_data.send_layout(responses, LayoutTarget::ToolShelf); + tool_data.send_layout(responses, LayoutTarget::ToolShelf, preferences.brush_tool); // Notify the frontend about the initial working colors document_data.update_working_colors(responses); @@ -259,6 +259,10 @@ impl MessageHandler> for ToolMessageHandler let tool_data = &mut self.tool_state.tool_data; tool_data.tools.get(&tool_data.active_tool_type).unwrap().send_layout(responses, LayoutTarget::ToolOptions); } + ToolMessage::RefreshToolShelf => { + let tool_data = &mut self.tool_state.tool_data; + tool_data.send_layout(responses, LayoutTarget::ToolShelf, preferences.brush_tool); + } ToolMessage::ResetColors => { let document_data = &mut self.tool_state.document_tool_data; diff --git a/editor/src/messages/tool/utility_types.rs b/editor/src/messages/tool/utility_types.rs index 7f4298d70..e9e7a1700 100644 --- a/editor/src/messages/tool/utility_types.rs +++ b/editor/src/messages/tool/utility_types.rs @@ -231,8 +231,15 @@ impl ToolData { } } -impl LayoutHolder for ToolData { - fn layout(&self) -> Layout { +impl ToolData { + pub fn send_layout(&self, responses: &mut VecDeque, layout_target: LayoutTarget, brush_tool: bool) { + responses.add(LayoutMessage::SendLayout { + layout: self.layout(brush_tool), + layout_target, + }); + } + + fn layout(&self, brush_tool: bool) -> Layout { let active_tool = self.active_shape_type.unwrap_or(self.active_tool_type); let tool_groups_layout = list_tools_in_groups() @@ -240,22 +247,26 @@ impl LayoutHolder for ToolData { .map(|tool_group| tool_group .iter() - .map(|tool_availability| { - match tool_availability { - ToolAvailability::Available(tool) => + .filter_map(|tool_availability| { + if !brush_tool && let ToolRole::Normal(tool) = tool_availability && tool.tool_type() == ToolType::Brush { + return None; + } + + Some(match tool_availability { + ToolRole::Normal(tool) => ToolEntry::new(tool.tool_type(), tool.icon_name()) .tooltip_label(tool.tooltip_label()) .tooltip_shortcut(action_shortcut!(tool_type_to_activate_tool_message(tool.tool_type()))), - ToolAvailability::AvailableAsShape(shape) => + ToolRole::Shape(shape) => ToolEntry::new(shape.tool_type(), shape.icon_name()) .tooltip_label(shape.tooltip_label()) .tooltip_description(shape.tooltip_description()) .tooltip_shortcut(action_shortcut!(tool_type_to_activate_tool_message(shape.tool_type()))), - // ToolAvailability::ComingSoon(tool) => tool.clone(), - } + }) }) .collect::>() ) + .filter(|group| !group.is_empty()) .flat_map(|group| { let separator = std::iter::once(Separator::new(SeparatorType::Section).direction(SeparatorDirection::Vertical).widget_instance()); let buttons = group.into_iter().map(|ToolEntry { tooltip_label, tooltip_description, tooltip_shortcut, tool_type, icon_name }| { @@ -319,9 +330,8 @@ impl Default for ToolFsmState { .into_iter() .flatten() .filter_map(|tool| match tool { - ToolAvailability::Available(tool) => Some((tool.tool_type(), tool)), - ToolAvailability::AvailableAsShape(_) => None, - // ToolAvailability::ComingSoon(_) => None, + ToolRole::Normal(tool) => Some((tool.tool_type(), tool)), + ToolRole::Shape(_) => None, }) .collect(), }, @@ -369,7 +379,6 @@ pub enum ToolType { Patch, Detail, Relight, - Frame, } impl ToolType { @@ -385,58 +394,57 @@ impl ToolType { } } -enum ToolAvailability { - Available(Box), - AvailableAsShape(ShapeType), - // ComingSoon(ToolEntry), +enum ToolRole { + Normal(Box), + Shape(ShapeType), } /// List of all the tools in their conventional ordering and grouping. -fn list_tools_in_groups() -> Vec> { +fn list_tools_in_groups() -> Vec> { vec![ vec![ // General tool group - ToolAvailability::Available(Box::::default()), - ToolAvailability::Available(Box::::default()), - ToolAvailability::Available(Box::::default()), - ToolAvailability::Available(Box::::default()), - ToolAvailability::Available(Box::::default()), - ToolAvailability::Available(Box::::default()), + ToolRole::Normal(Box::::default()), + ToolRole::Normal(Box::::default()), + ToolRole::Normal(Box::::default()), + ToolRole::Normal(Box::::default()), + ToolRole::Normal(Box::::default()), + ToolRole::Normal(Box::::default()), ], vec![ // Vector tool group - ToolAvailability::Available(Box::::default()), - ToolAvailability::Available(Box::::default()), - ToolAvailability::Available(Box::::default()), - ToolAvailability::Available(Box::::default()), - ToolAvailability::AvailableAsShape(ShapeType::Line), - ToolAvailability::AvailableAsShape(ShapeType::Rectangle), - ToolAvailability::AvailableAsShape(ShapeType::Ellipse), - ToolAvailability::Available(Box::::default()), - ToolAvailability::Available(Box::::default()), + ToolRole::Normal(Box::::default()), + ToolRole::Normal(Box::::default()), + ToolRole::Normal(Box::::default()), + ToolRole::Normal(Box::::default()), + ToolRole::Shape(ShapeType::Line), + ToolRole::Shape(ShapeType::Rectangle), + ToolRole::Shape(ShapeType::Ellipse), + ToolRole::Normal(Box::::default()), + ToolRole::Normal(Box::::default()), ], vec![ // Raster tool group - ToolAvailability::Available(Box::::default()), - // ToolAvailability::ComingSoon( + ToolRole::Normal(Box::::default()), + // ToolRole::Normal( // ToolEntry::new(ToolType::Heal, "RasterHealTool") // .tooltip_label("Heal Tool") // .tooltip_shortcut(action_shortcut_manual!(Key::KeyJ)), // ), - // ToolAvailability::ComingSoon( + // ToolRole::Normal( // ToolEntry::new(ToolType::Clone, "RasterCloneTool") // .tooltip_label("Clone Tool") // .tooltip_shortcut(action_shortcut_manual!(Key::KeyC)), // ), - // ToolAvailability::ComingSoon(ToolEntry::new(ToolType::Patch, "RasterPatchTool") + // ToolRole::Normal(ToolEntry::new(ToolType::Patch, "RasterPatchTool") // .tooltip_label("Patch Tool"), // ), - // ToolAvailability::ComingSoon( + // ToolRole::Normal( // ToolEntry::new(ToolType::Detail, "RasterDetailTool") // .tooltip_label("Detail Tool") // .tooltip_shortcut(action_shortcut_manual!(Key::KeyD)), // ), - // ToolAvailability::ComingSoon( + // ToolRole::Normal( // ToolEntry::new(ToolType::Relight, "RasterRelightTool") // .tooltip_label("Relight Tool") // .tooltip_shortcut(action_shortcut_manual!(Key::KeyO)), diff --git a/frontend/src/components/panels/Document.svelte b/frontend/src/components/panels/Document.svelte index a27f59397..f149cb3fd 100644 --- a/frontend/src/components/panels/Document.svelte +++ b/frontend/src/components/panels/Document.svelte @@ -304,14 +304,8 @@ if (cursor === "custom-rotate") { const svg = ` - - - - + + ` .split("\n")