Add tool skeletons
This commit is contained in:
parent
37ac9b6abe
commit
2c9d8c1589
|
|
@ -11,9 +11,10 @@ use vello::kurbo::Point;
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub enum Tool {
|
pub enum Tool {
|
||||||
|
// ── Vector / shared tools ──────────────────────────────────────────────
|
||||||
/// Selection tool - select and move objects
|
/// Selection tool - select and move objects
|
||||||
Select,
|
Select,
|
||||||
/// Draw/Pen tool - freehand drawing
|
/// Draw/Brush tool - freehand drawing (vector) / paintbrush (raster)
|
||||||
Draw,
|
Draw,
|
||||||
/// Transform tool - scale, rotate, skew
|
/// Transform tool - scale, rotate, skew
|
||||||
Transform,
|
Transform,
|
||||||
|
|
@ -37,12 +38,48 @@ pub enum Tool {
|
||||||
RegionSelect,
|
RegionSelect,
|
||||||
/// Split tool - split audio/video clips at a point
|
/// Split tool - split audio/video clips at a point
|
||||||
Split,
|
Split,
|
||||||
|
// ── Raster brush tools ────────────────────────────────────────────────
|
||||||
|
/// Pencil tool - hard-edged raster brush
|
||||||
|
Pencil,
|
||||||
|
/// Pen tool - pressure-sensitive raster pen
|
||||||
|
Pen,
|
||||||
|
/// Airbrush tool - soft spray raster brush
|
||||||
|
Airbrush,
|
||||||
/// Erase tool - erase raster pixels
|
/// Erase tool - erase raster pixels
|
||||||
Erase,
|
Erase,
|
||||||
/// Smudge tool - smudge/blend raster pixels
|
/// Smudge tool - smudge/blend raster pixels
|
||||||
Smudge,
|
Smudge,
|
||||||
/// Lasso select tool - freehand selection on raster layers
|
/// Clone Stamp - copy pixels from a source point
|
||||||
|
CloneStamp,
|
||||||
|
/// Healing Brush - content-aware pixel repair
|
||||||
|
HealingBrush,
|
||||||
|
/// Pattern Stamp - paint with a repeating pattern
|
||||||
|
PatternStamp,
|
||||||
|
/// Dodge/Burn - lighten or darken pixels
|
||||||
|
DodgeBurn,
|
||||||
|
/// Sponge - saturate or desaturate pixels
|
||||||
|
Sponge,
|
||||||
|
/// Blur/Sharpen - blur or sharpen pixel regions
|
||||||
|
BlurSharpen,
|
||||||
|
// ── Raster fill / shape ───────────────────────────────────────────────
|
||||||
|
/// Gradient tool - fill with a gradient
|
||||||
|
Gradient,
|
||||||
|
/// Custom Shape tool - draw from a shape library
|
||||||
|
CustomShape,
|
||||||
|
// ── Raster selection tools ────────────────────────────────────────────
|
||||||
|
/// Elliptical marquee selection
|
||||||
|
SelectEllipse,
|
||||||
|
/// Lasso select tool - freehand / polygonal / magnetic selection
|
||||||
SelectLasso,
|
SelectLasso,
|
||||||
|
/// Magic Wand - select by colour similarity
|
||||||
|
MagicWand,
|
||||||
|
/// Quick Select - brush-based smart selection
|
||||||
|
QuickSelect,
|
||||||
|
// ── Raster transform tools ────────────────────────────────────────────
|
||||||
|
/// Warp / perspective transform
|
||||||
|
Warp,
|
||||||
|
/// Liquify - freeform pixel warping
|
||||||
|
Liquify,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Region select mode
|
/// Region select mode
|
||||||
|
|
@ -60,6 +97,23 @@ impl Default for RegionSelectMode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Lasso selection sub-mode
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
|
pub enum LassoMode {
|
||||||
|
/// Freehand lasso (existing, implemented)
|
||||||
|
Freehand,
|
||||||
|
/// Click-to-place polygonal lasso
|
||||||
|
Polygonal,
|
||||||
|
/// Magnetically snaps to edges
|
||||||
|
Magnetic,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for LassoMode {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Freehand
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Tool state tracking for interactive operations
|
/// Tool state tracking for interactive operations
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum ToolState {
|
pub enum ToolState {
|
||||||
|
|
@ -229,44 +283,77 @@ impl Tool {
|
||||||
/// Get display name for the tool
|
/// Get display name for the tool
|
||||||
pub fn display_name(self) -> &'static str {
|
pub fn display_name(self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Tool::Select => "Select",
|
Tool::Select => "Select",
|
||||||
Tool::Draw => "Draw",
|
Tool::Draw => "Brush",
|
||||||
Tool::Transform => "Transform",
|
Tool::Transform => "Transform",
|
||||||
Tool::Rectangle => "Rectangle",
|
Tool::Rectangle => "Rectangle",
|
||||||
Tool::Ellipse => "Ellipse",
|
Tool::Ellipse => "Ellipse",
|
||||||
Tool::PaintBucket => "Paint Bucket",
|
Tool::PaintBucket => "Paint Bucket",
|
||||||
Tool::Eyedropper => "Eyedropper",
|
Tool::Eyedropper => "Eyedropper",
|
||||||
Tool::Line => "Line",
|
Tool::Line => "Line",
|
||||||
Tool::Polygon => "Polygon",
|
Tool::Polygon => "Polygon",
|
||||||
Tool::BezierEdit => "Bezier Edit",
|
Tool::BezierEdit => "Bezier Edit",
|
||||||
Tool::Text => "Text",
|
Tool::Text => "Text",
|
||||||
Tool::RegionSelect => "Region Select",
|
Tool::RegionSelect => "Region Select",
|
||||||
Tool::Split => "Split",
|
Tool::Split => "Split",
|
||||||
Tool::Erase => "Erase",
|
Tool::Pencil => "Pencil",
|
||||||
Tool::Smudge => "Smudge",
|
Tool::Pen => "Pen",
|
||||||
Tool::SelectLasso => "Lasso Select",
|
Tool::Airbrush => "Airbrush",
|
||||||
|
Tool::Erase => "Eraser",
|
||||||
|
Tool::Smudge => "Smudge",
|
||||||
|
Tool::CloneStamp => "Clone Stamp",
|
||||||
|
Tool::HealingBrush => "Healing Brush",
|
||||||
|
Tool::PatternStamp => "Pattern Stamp",
|
||||||
|
Tool::DodgeBurn => "Dodge / Burn",
|
||||||
|
Tool::Sponge => "Sponge",
|
||||||
|
Tool::BlurSharpen => "Blur / Sharpen",
|
||||||
|
Tool::Gradient => "Gradient",
|
||||||
|
Tool::CustomShape => "Custom Shape",
|
||||||
|
Tool::SelectEllipse => "Elliptical Select",
|
||||||
|
Tool::SelectLasso => "Lasso Select",
|
||||||
|
Tool::MagicWand => "Magic Wand",
|
||||||
|
Tool::QuickSelect => "Quick Select",
|
||||||
|
Tool::Warp => "Warp",
|
||||||
|
Tool::Liquify => "Liquify",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get SVG icon file name for the tool
|
/// Get SVG icon file name for the tool
|
||||||
pub fn icon_file(self) -> &'static str {
|
pub fn icon_file(self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Tool::Select => "select.svg",
|
Tool::Select => "select.svg",
|
||||||
Tool::Draw => "draw.svg",
|
Tool::Draw => "draw.svg",
|
||||||
Tool::Transform => "transform.svg",
|
Tool::Transform => "transform.svg",
|
||||||
Tool::Rectangle => "rectangle.svg",
|
Tool::Rectangle => "rectangle.svg",
|
||||||
Tool::Ellipse => "ellipse.svg",
|
Tool::Ellipse => "ellipse.svg",
|
||||||
Tool::PaintBucket => "paint_bucket.svg",
|
Tool::PaintBucket => "paint_bucket.svg",
|
||||||
Tool::Eyedropper => "eyedropper.svg",
|
Tool::Eyedropper => "eyedropper.svg",
|
||||||
Tool::Line => "line.svg",
|
Tool::Line => "line.svg",
|
||||||
Tool::Polygon => "polygon.svg",
|
Tool::Polygon => "polygon.svg",
|
||||||
Tool::BezierEdit => "bezier_edit.svg",
|
Tool::BezierEdit => "bezier_edit.svg",
|
||||||
Tool::Text => "text.svg",
|
Tool::Text => "text.svg",
|
||||||
Tool::RegionSelect => "region_select.svg",
|
Tool::RegionSelect => "region_select.svg",
|
||||||
Tool::Split => "split.svg",
|
Tool::Split => "split.svg",
|
||||||
Tool::Erase => "erase.svg",
|
Tool::Erase => "erase.svg",
|
||||||
Tool::Smudge => "smudge.svg",
|
Tool::Smudge => "smudge.svg",
|
||||||
Tool::SelectLasso => "lasso.svg",
|
Tool::SelectLasso => "lasso.svg",
|
||||||
|
// Not yet implemented — use the placeholder icon
|
||||||
|
Tool::Pencil
|
||||||
|
| Tool::Pen
|
||||||
|
| Tool::Airbrush
|
||||||
|
| Tool::CloneStamp
|
||||||
|
| Tool::HealingBrush
|
||||||
|
| Tool::PatternStamp
|
||||||
|
| Tool::DodgeBurn
|
||||||
|
| Tool::Sponge
|
||||||
|
| Tool::BlurSharpen
|
||||||
|
| Tool::Gradient
|
||||||
|
| Tool::CustomShape
|
||||||
|
| Tool::SelectEllipse
|
||||||
|
| Tool::MagicWand
|
||||||
|
| Tool::QuickSelect
|
||||||
|
| Tool::Warp
|
||||||
|
| Tool::Liquify => "todo.svg",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -294,7 +381,23 @@ impl Tool {
|
||||||
match layer_type {
|
match layer_type {
|
||||||
None | Some(LayerType::Vector) => Tool::all(),
|
None | Some(LayerType::Vector) => Tool::all(),
|
||||||
Some(LayerType::Audio) | Some(LayerType::Video) => &[Tool::Select, Tool::Split],
|
Some(LayerType::Audio) | Some(LayerType::Video) => &[Tool::Select, Tool::Split],
|
||||||
Some(LayerType::Raster) => &[Tool::Select, Tool::SelectLasso, Tool::Draw, Tool::Erase, Tool::Smudge, Tool::Eyedropper],
|
Some(LayerType::Raster) => &[
|
||||||
|
// Brush tools
|
||||||
|
Tool::Draw, Tool::Pencil, Tool::Pen, Tool::Airbrush,
|
||||||
|
Tool::Erase, Tool::Smudge,
|
||||||
|
Tool::CloneStamp, Tool::HealingBrush, Tool::PatternStamp,
|
||||||
|
Tool::DodgeBurn, Tool::Sponge, Tool::BlurSharpen,
|
||||||
|
// Fill / shape
|
||||||
|
Tool::PaintBucket, Tool::Gradient,
|
||||||
|
Tool::Rectangle, Tool::Ellipse, Tool::Polygon, Tool::Line, Tool::CustomShape,
|
||||||
|
// Selection
|
||||||
|
Tool::Select, Tool::SelectEllipse, Tool::SelectLasso,
|
||||||
|
Tool::MagicWand, Tool::QuickSelect,
|
||||||
|
// Transform
|
||||||
|
Tool::Transform, Tool::Warp, Tool::Liquify,
|
||||||
|
// Utility
|
||||||
|
Tool::Eyedropper,
|
||||||
|
],
|
||||||
_ => &[Tool::Select],
|
_ => &[Tool::Select],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,7 @@
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"type": "vertical-grid",
|
"type": "vertical-grid",
|
||||||
"percent": 30,
|
"percent": 67,
|
||||||
"children": [
|
"children": [
|
||||||
{ "type": "pane", "name": "toolbar" },
|
{ "type": "pane", "name": "toolbar" },
|
||||||
{ "type": "pane", "name": "infopanel" }
|
{ "type": "pane", "name": "infopanel" }
|
||||||
|
|
|
||||||
|
|
@ -32,22 +32,41 @@ impl CustomCursor {
|
||||||
/// Convert a Tool enum to the corresponding custom cursor
|
/// Convert a Tool enum to the corresponding custom cursor
|
||||||
pub fn from_tool(tool: Tool) -> Self {
|
pub fn from_tool(tool: Tool) -> Self {
|
||||||
match tool {
|
match tool {
|
||||||
Tool::Select => CustomCursor::Select,
|
Tool::Select => CustomCursor::Select,
|
||||||
Tool::Draw => CustomCursor::Draw,
|
Tool::Draw => CustomCursor::Draw,
|
||||||
Tool::Transform => CustomCursor::Transform,
|
Tool::Transform => CustomCursor::Transform,
|
||||||
Tool::Rectangle => CustomCursor::Rectangle,
|
Tool::Rectangle => CustomCursor::Rectangle,
|
||||||
Tool::Ellipse => CustomCursor::Ellipse,
|
Tool::Ellipse => CustomCursor::Ellipse,
|
||||||
Tool::PaintBucket => CustomCursor::PaintBucket,
|
Tool::PaintBucket => CustomCursor::PaintBucket,
|
||||||
Tool::Eyedropper => CustomCursor::Eyedropper,
|
Tool::Eyedropper => CustomCursor::Eyedropper,
|
||||||
Tool::Line => CustomCursor::Line,
|
Tool::Line => CustomCursor::Line,
|
||||||
Tool::Polygon => CustomCursor::Polygon,
|
Tool::Polygon => CustomCursor::Polygon,
|
||||||
Tool::BezierEdit => CustomCursor::BezierEdit,
|
Tool::BezierEdit => CustomCursor::BezierEdit,
|
||||||
Tool::Text => CustomCursor::Text,
|
Tool::Text => CustomCursor::Text,
|
||||||
Tool::RegionSelect => CustomCursor::Select, // Reuse select cursor for now
|
Tool::RegionSelect => CustomCursor::Select,
|
||||||
Tool::Split => CustomCursor::Select, // Reuse select cursor for now
|
Tool::Split => CustomCursor::Select,
|
||||||
Tool::Erase => CustomCursor::Draw, // Reuse draw cursor for raster erase
|
Tool::Erase => CustomCursor::Draw,
|
||||||
Tool::Smudge => CustomCursor::Draw, // Reuse draw cursor for raster smudge
|
Tool::Smudge => CustomCursor::Draw,
|
||||||
Tool::SelectLasso => CustomCursor::Select, // Reuse select cursor for lasso
|
Tool::SelectLasso => CustomCursor::Select,
|
||||||
|
// Raster brush tools — use draw cursor until implemented
|
||||||
|
Tool::Pencil
|
||||||
|
| Tool::Pen
|
||||||
|
| Tool::Airbrush
|
||||||
|
| Tool::CloneStamp
|
||||||
|
| Tool::HealingBrush
|
||||||
|
| Tool::PatternStamp
|
||||||
|
| Tool::DodgeBurn
|
||||||
|
| Tool::Sponge
|
||||||
|
| Tool::BlurSharpen => CustomCursor::Draw,
|
||||||
|
// Selection tools — use select cursor until implemented
|
||||||
|
Tool::SelectEllipse
|
||||||
|
| Tool::MagicWand
|
||||||
|
| Tool::QuickSelect => CustomCursor::Select,
|
||||||
|
// Other tools — use select cursor until implemented
|
||||||
|
Tool::Gradient
|
||||||
|
| Tool::CustomShape
|
||||||
|
| Tool::Warp
|
||||||
|
| Tool::Liquify => CustomCursor::Select,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -429,24 +429,26 @@ impl AppAction {
|
||||||
/// `Tool::Split` has no tool-shortcut action (it's triggered via the menu).
|
/// `Tool::Split` has no tool-shortcut action (it's triggered via the menu).
|
||||||
pub fn tool_app_action(tool: lightningbeam_core::tool::Tool) -> Option<AppAction> {
|
pub fn tool_app_action(tool: lightningbeam_core::tool::Tool) -> Option<AppAction> {
|
||||||
use lightningbeam_core::tool::Tool;
|
use lightningbeam_core::tool::Tool;
|
||||||
Some(match tool {
|
match tool {
|
||||||
Tool::Select => AppAction::ToolSelect,
|
Tool::Select => Some(AppAction::ToolSelect),
|
||||||
Tool::Draw => AppAction::ToolDraw,
|
Tool::Draw => Some(AppAction::ToolDraw),
|
||||||
Tool::Transform => AppAction::ToolTransform,
|
Tool::Transform => Some(AppAction::ToolTransform),
|
||||||
Tool::Rectangle => AppAction::ToolRectangle,
|
Tool::Rectangle => Some(AppAction::ToolRectangle),
|
||||||
Tool::Ellipse => AppAction::ToolEllipse,
|
Tool::Ellipse => Some(AppAction::ToolEllipse),
|
||||||
Tool::PaintBucket => AppAction::ToolPaintBucket,
|
Tool::PaintBucket => Some(AppAction::ToolPaintBucket),
|
||||||
Tool::Eyedropper => AppAction::ToolEyedropper,
|
Tool::Eyedropper => Some(AppAction::ToolEyedropper),
|
||||||
Tool::Line => AppAction::ToolLine,
|
Tool::Line => Some(AppAction::ToolLine),
|
||||||
Tool::Polygon => AppAction::ToolPolygon,
|
Tool::Polygon => Some(AppAction::ToolPolygon),
|
||||||
Tool::BezierEdit => AppAction::ToolBezierEdit,
|
Tool::BezierEdit => Some(AppAction::ToolBezierEdit),
|
||||||
Tool::Text => AppAction::ToolText,
|
Tool::Text => Some(AppAction::ToolText),
|
||||||
Tool::RegionSelect => AppAction::ToolRegionSelect,
|
Tool::RegionSelect => Some(AppAction::ToolRegionSelect),
|
||||||
Tool::Erase => AppAction::ToolErase,
|
Tool::Erase => Some(AppAction::ToolErase),
|
||||||
Tool::Smudge => AppAction::ToolSmudge,
|
Tool::Smudge => Some(AppAction::ToolSmudge),
|
||||||
Tool::SelectLasso => AppAction::ToolSelectLasso,
|
Tool::SelectLasso => Some(AppAction::ToolSelectLasso),
|
||||||
Tool::Split => AppAction::ToolSplit,
|
Tool::Split => Some(AppAction::ToolSplit),
|
||||||
})
|
// New tools have no keybinding yet
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// === Default bindings ===
|
// === Default bindings ===
|
||||||
|
|
|
||||||
|
|
@ -332,6 +332,7 @@ mod tool_icons {
|
||||||
pub static ERASE: &[u8] = include_bytes!("../../../src/assets/erase.svg");
|
pub static ERASE: &[u8] = include_bytes!("../../../src/assets/erase.svg");
|
||||||
pub static SMUDGE: &[u8] = include_bytes!("../../../src/assets/smudge.svg");
|
pub static SMUDGE: &[u8] = include_bytes!("../../../src/assets/smudge.svg");
|
||||||
pub static LASSO: &[u8] = include_bytes!("../../../src/assets/lasso.svg");
|
pub static LASSO: &[u8] = include_bytes!("../../../src/assets/lasso.svg");
|
||||||
|
pub static TODO: &[u8] = include_bytes!("../../../src/assets/todo.svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Embedded focus icon SVGs
|
/// Embedded focus icon SVGs
|
||||||
|
|
@ -399,11 +400,28 @@ impl ToolIconCache {
|
||||||
Tool::Polygon => tool_icons::POLYGON,
|
Tool::Polygon => tool_icons::POLYGON,
|
||||||
Tool::BezierEdit => tool_icons::BEZIER_EDIT,
|
Tool::BezierEdit => tool_icons::BEZIER_EDIT,
|
||||||
Tool::Text => tool_icons::TEXT,
|
Tool::Text => tool_icons::TEXT,
|
||||||
Tool::RegionSelect => tool_icons::SELECT, // Reuse select icon for now
|
Tool::RegionSelect => tool_icons::SELECT,
|
||||||
Tool::Split => tool_icons::SPLIT,
|
Tool::Split => tool_icons::SPLIT,
|
||||||
Tool::Erase => tool_icons::ERASE,
|
Tool::Erase => tool_icons::ERASE,
|
||||||
Tool::Smudge => tool_icons::SMUDGE,
|
Tool::Smudge => tool_icons::SMUDGE,
|
||||||
Tool::SelectLasso => tool_icons::LASSO,
|
Tool::SelectLasso => tool_icons::LASSO,
|
||||||
|
// Not yet implemented — use placeholder icon
|
||||||
|
Tool::Pencil
|
||||||
|
| Tool::Pen
|
||||||
|
| Tool::Airbrush
|
||||||
|
| Tool::CloneStamp
|
||||||
|
| Tool::HealingBrush
|
||||||
|
| Tool::PatternStamp
|
||||||
|
| Tool::DodgeBurn
|
||||||
|
| Tool::Sponge
|
||||||
|
| Tool::BlurSharpen
|
||||||
|
| Tool::Gradient
|
||||||
|
| Tool::CustomShape
|
||||||
|
| Tool::SelectEllipse
|
||||||
|
| Tool::MagicWand
|
||||||
|
| Tool::QuickSelect
|
||||||
|
| Tool::Warp
|
||||||
|
| Tool::Liquify => tool_icons::TODO,
|
||||||
};
|
};
|
||||||
if let Some(texture) = rasterize_svg(svg_data, tool.icon_file(), 180, ctx) {
|
if let Some(texture) = rasterize_svg(svg_data, tool.icon_file(), 180, ctx) {
|
||||||
self.icons.insert(tool, texture);
|
self.icons.insert(tool, texture);
|
||||||
|
|
@ -856,6 +874,7 @@ struct EditorApp {
|
||||||
// Region select state
|
// Region select state
|
||||||
region_selection: Option<lightningbeam_core::selection::RegionSelection>,
|
region_selection: Option<lightningbeam_core::selection::RegionSelection>,
|
||||||
region_select_mode: lightningbeam_core::tool::RegionSelectMode,
|
region_select_mode: lightningbeam_core::tool::RegionSelectMode,
|
||||||
|
lasso_mode: lightningbeam_core::tool::LassoMode,
|
||||||
|
|
||||||
// VU meter levels
|
// VU meter levels
|
||||||
input_level: f32,
|
input_level: f32,
|
||||||
|
|
@ -1127,6 +1146,7 @@ impl EditorApp {
|
||||||
polygon_sides: 5, // Default to pentagon
|
polygon_sides: 5, // Default to pentagon
|
||||||
region_selection: None,
|
region_selection: None,
|
||||||
region_select_mode: lightningbeam_core::tool::RegionSelectMode::default(),
|
region_select_mode: lightningbeam_core::tool::RegionSelectMode::default(),
|
||||||
|
lasso_mode: lightningbeam_core::tool::LassoMode::default(),
|
||||||
input_level: 0.0,
|
input_level: 0.0,
|
||||||
output_level: (0.0, 0.0),
|
output_level: (0.0, 0.0),
|
||||||
track_levels: HashMap::new(),
|
track_levels: HashMap::new(),
|
||||||
|
|
@ -5590,6 +5610,7 @@ impl eframe::App for EditorApp {
|
||||||
script_saved: &mut self.script_saved,
|
script_saved: &mut self.script_saved,
|
||||||
region_selection: &mut self.region_selection,
|
region_selection: &mut self.region_selection,
|
||||||
region_select_mode: &mut self.region_select_mode,
|
region_select_mode: &mut self.region_select_mode,
|
||||||
|
lasso_mode: &mut self.lasso_mode,
|
||||||
pending_graph_loads: &self.pending_graph_loads,
|
pending_graph_loads: &self.pending_graph_loads,
|
||||||
clipboard_consumed: &mut clipboard_consumed,
|
clipboard_consumed: &mut clipboard_consumed,
|
||||||
keymap: &self.keymap,
|
keymap: &self.keymap,
|
||||||
|
|
|
||||||
|
|
@ -169,7 +169,10 @@ impl InfopanelPane {
|
||||||
.and_then(|id| shared.action_executor.document().get_layer(&id))
|
.and_then(|id| shared.action_executor.document().get_layer(&id))
|
||||||
.map_or(false, |l| matches!(l, AnyLayer::Raster(_)));
|
.map_or(false, |l| matches!(l, AnyLayer::Raster(_)));
|
||||||
|
|
||||||
let is_raster_paint_tool = active_is_raster && matches!(tool, Tool::Draw | Tool::Erase | Tool::Smudge);
|
let is_raster_paint_tool = active_is_raster && matches!(
|
||||||
|
tool,
|
||||||
|
Tool::Draw | Tool::Pencil | Tool::Pen | Tool::Airbrush | Tool::Erase | Tool::Smudge
|
||||||
|
);
|
||||||
|
|
||||||
// Only show tool options for tools that have options
|
// Only show tool options for tools that have options
|
||||||
let is_vector_tool = !active_is_raster && matches!(
|
let is_vector_tool = !active_is_raster && matches!(
|
||||||
|
|
@ -319,7 +322,7 @@ impl InfopanelPane {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Raster paint tools
|
// Raster paint tools
|
||||||
Tool::Draw | Tool::Erase if is_raster_paint_tool => {
|
Tool::Draw | Tool::Pencil | Tool::Pen | Tool::Airbrush | Tool::Erase if is_raster_paint_tool => {
|
||||||
self.render_raster_tool_options(ui, shared, matches!(tool, Tool::Erase));
|
self.render_raster_tool_options(ui, shared, matches!(tool, Tool::Erase));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -513,6 +516,11 @@ impl InfopanelPane {
|
||||||
*shared.brush_hardness = s.hardness.clamp(0.0, 1.0);
|
*shared.brush_hardness = s.hardness.clamp(0.0, 1.0);
|
||||||
*shared.brush_spacing = s.dabs_per_radius;
|
*shared.brush_spacing = s.dabs_per_radius;
|
||||||
*shared.active_brush_settings = s.clone();
|
*shared.active_brush_settings = s.clone();
|
||||||
|
// If the user was on a preset-backed tool (Pencil/Pen/Airbrush)
|
||||||
|
// and manually picked a different brush, revert to the generic tool.
|
||||||
|
if matches!(*shared.selected_tool, Tool::Pencil | Tool::Pen | Tool::Airbrush) {
|
||||||
|
*shared.selected_tool = Tool::Draw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -283,6 +283,8 @@ pub struct SharedPaneState<'a> {
|
||||||
pub region_selection: &'a mut Option<lightningbeam_core::selection::RegionSelection>,
|
pub region_selection: &'a mut Option<lightningbeam_core::selection::RegionSelection>,
|
||||||
/// Region select mode (Rectangle or Lasso)
|
/// Region select mode (Rectangle or Lasso)
|
||||||
pub region_select_mode: &'a mut lightningbeam_core::tool::RegionSelectMode,
|
pub region_select_mode: &'a mut lightningbeam_core::tool::RegionSelectMode,
|
||||||
|
/// Lasso select sub-mode (Freehand / Polygonal / Magnetic)
|
||||||
|
pub lasso_mode: &'a mut lightningbeam_core::tool::LassoMode,
|
||||||
/// Counter for in-flight graph preset loads — increment when sending a
|
/// Counter for in-flight graph preset loads — increment when sending a
|
||||||
/// GraphLoadPreset command so the repaint loop stays alive until the
|
/// GraphLoadPreset command so the repaint loop stays alive until the
|
||||||
/// audio thread sends GraphPresetLoaded back
|
/// audio thread sends GraphPresetLoaded back
|
||||||
|
|
|
||||||
|
|
@ -7518,6 +7518,9 @@ impl StagePane {
|
||||||
self.handle_draw_tool(ui, &response, world_pos, shared);
|
self.handle_draw_tool(ui, &response, world_pos, shared);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Tool::Pencil | Tool::Pen | Tool::Airbrush => {
|
||||||
|
self.handle_raster_stroke_tool(ui, &response, world_pos, lightningbeam_core::raster_layer::RasterBlendMode::Normal, shared);
|
||||||
|
}
|
||||||
Tool::Erase => {
|
Tool::Erase => {
|
||||||
self.handle_raster_stroke_tool(ui, &response, world_pos, lightningbeam_core::raster_layer::RasterBlendMode::Erase, shared);
|
self.handle_raster_stroke_tool(ui, &response, world_pos, lightningbeam_core::raster_layer::RasterBlendMode::Erase, shared);
|
||||||
}
|
}
|
||||||
|
|
@ -8001,8 +8004,11 @@ impl StagePane {
|
||||||
|
|
||||||
// Compute semi-axes (world pixels) and dab rotation angle.
|
// Compute semi-axes (world pixels) and dab rotation angle.
|
||||||
let (a_world, b_world, dab_angle_rad) = match *shared.selected_tool {
|
let (a_world, b_world, dab_angle_rad) = match *shared.selected_tool {
|
||||||
Tool::Erase => (*shared.eraser_radius, *shared.eraser_radius, 0.0_f32),
|
Tool::Erase => (*shared.eraser_radius, *shared.eraser_radius, 0.0_f32),
|
||||||
Tool::Smudge => (*shared.smudge_radius, *shared.smudge_radius, 0.0_f32),
|
Tool::Smudge
|
||||||
|
| Tool::BlurSharpen
|
||||||
|
| Tool::DodgeBurn
|
||||||
|
| Tool::Sponge => (*shared.smudge_radius, *shared.smudge_radius, 0.0_f32),
|
||||||
_ => {
|
_ => {
|
||||||
let bs = &shared.active_brush_settings;
|
let bs = &shared.active_brush_settings;
|
||||||
let r = *shared.brush_radius;
|
let r = *shared.brush_radius;
|
||||||
|
|
@ -8637,7 +8643,10 @@ impl PaneRenderer for StagePane {
|
||||||
use lightningbeam_core::tool::Tool;
|
use lightningbeam_core::tool::Tool;
|
||||||
let is_raster_paint = matches!(
|
let is_raster_paint = matches!(
|
||||||
*shared.selected_tool,
|
*shared.selected_tool,
|
||||||
Tool::Draw | Tool::Erase | Tool::Smudge
|
Tool::Draw | Tool::Pencil | Tool::Pen | Tool::Airbrush
|
||||||
|
| Tool::Erase | Tool::Smudge
|
||||||
|
| Tool::CloneStamp | Tool::HealingBrush | Tool::PatternStamp
|
||||||
|
| Tool::DodgeBurn | Tool::Sponge | Tool::BlurSharpen
|
||||||
) && shared.active_layer_id.and_then(|id| {
|
) && shared.active_layer_id.and_then(|id| {
|
||||||
shared.action_executor.document().get_layer(&id)
|
shared.action_executor.document().get_layer(&id)
|
||||||
}).map_or(false, |l| matches!(l, lightningbeam_core::layer::AnyLayer::Raster(_)));
|
}).map_or(false, |l| matches!(l, lightningbeam_core::layer::AnyLayer::Raster(_)));
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@
|
||||||
|
|
||||||
use eframe::egui;
|
use eframe::egui;
|
||||||
use lightningbeam_core::layer::{AnyLayer, LayerType};
|
use lightningbeam_core::layer::{AnyLayer, LayerType};
|
||||||
use lightningbeam_core::tool::{Tool, RegionSelectMode};
|
use lightningbeam_core::tool::{Tool, RegionSelectMode, LassoMode};
|
||||||
|
use lightningbeam_core::brush_settings::bundled_brushes;
|
||||||
use crate::keymap::tool_app_action;
|
use crate::keymap::tool_app_action;
|
||||||
use super::{NodePath, PaneRenderer, SharedPaneState};
|
use super::{NodePath, PaneRenderer, SharedPaneState};
|
||||||
|
|
||||||
|
|
@ -101,7 +102,7 @@ impl PaneRenderer for ToolbarPane {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw sub-tool arrow indicator for tools with modes
|
// Draw sub-tool arrow indicator for tools with modes
|
||||||
let has_sub_tools = matches!(tool, Tool::RegionSelect);
|
let has_sub_tools = matches!(tool, Tool::RegionSelect | Tool::SelectLasso);
|
||||||
if has_sub_tools {
|
if has_sub_tools {
|
||||||
let arrow_size = 6.0;
|
let arrow_size = 6.0;
|
||||||
let margin = 4.0;
|
let margin = 4.0;
|
||||||
|
|
@ -125,6 +126,22 @@ impl PaneRenderer for ToolbarPane {
|
||||||
// Check for click first
|
// Check for click first
|
||||||
if response.clicked() {
|
if response.clicked() {
|
||||||
*shared.selected_tool = *tool;
|
*shared.selected_tool = *tool;
|
||||||
|
// Preset-backed tools: auto-select the matching bundled brush.
|
||||||
|
let preset_name = match tool {
|
||||||
|
Tool::Pencil => Some("Pencil"),
|
||||||
|
Tool::Pen => Some("Pen"),
|
||||||
|
Tool::Airbrush => Some("Airbrush"),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
if let Some(name) = preset_name {
|
||||||
|
if let Some(preset) = bundled_brushes().iter().find(|p| p.name == name) {
|
||||||
|
let s = &preset.settings;
|
||||||
|
*shared.brush_opacity = s.opaque.clamp(0.0, 1.0);
|
||||||
|
*shared.brush_hardness = s.hardness.clamp(0.0, 1.0);
|
||||||
|
*shared.brush_spacing = s.dabs_per_radius;
|
||||||
|
*shared.active_brush_settings = s.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Right-click context menu for tools with sub-options
|
// Right-click context menu for tools with sub-options
|
||||||
|
|
@ -150,6 +167,33 @@ impl PaneRenderer for ToolbarPane {
|
||||||
ui.close();
|
ui.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Tool::SelectLasso => {
|
||||||
|
ui.set_min_width(130.0);
|
||||||
|
if ui.selectable_label(
|
||||||
|
*shared.lasso_mode == LassoMode::Freehand,
|
||||||
|
"Freehand",
|
||||||
|
).clicked() {
|
||||||
|
*shared.lasso_mode = LassoMode::Freehand;
|
||||||
|
*shared.selected_tool = Tool::SelectLasso;
|
||||||
|
ui.close();
|
||||||
|
}
|
||||||
|
if ui.selectable_label(
|
||||||
|
*shared.lasso_mode == LassoMode::Polygonal,
|
||||||
|
"Polygonal",
|
||||||
|
).clicked() {
|
||||||
|
*shared.lasso_mode = LassoMode::Polygonal;
|
||||||
|
*shared.selected_tool = Tool::SelectLasso;
|
||||||
|
ui.close();
|
||||||
|
}
|
||||||
|
if ui.selectable_label(
|
||||||
|
*shared.lasso_mode == LassoMode::Magnetic,
|
||||||
|
"Magnetic",
|
||||||
|
).clicked() {
|
||||||
|
*shared.lasso_mode = LassoMode::Magnetic;
|
||||||
|
*shared.selected_tool = Tool::SelectLasso;
|
||||||
|
ui.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -176,6 +220,13 @@ impl PaneRenderer for ToolbarPane {
|
||||||
RegionSelectMode::Lasso => "Lasso",
|
RegionSelectMode::Lasso => "Lasso",
|
||||||
};
|
};
|
||||||
format!("{} - {}{}\nRight-click for options", tool.display_name(), mode, hint)
|
format!("{} - {}{}\nRight-click for options", tool.display_name(), mode, hint)
|
||||||
|
} else if *tool == Tool::SelectLasso {
|
||||||
|
let mode = match *shared.lasso_mode {
|
||||||
|
LassoMode::Freehand => "Freehand",
|
||||||
|
LassoMode::Polygonal => "Polygonal",
|
||||||
|
LassoMode::Magnetic => "Magnetic",
|
||||||
|
};
|
||||||
|
format!("{} - {}{}\nRight-click for options", tool.display_name(), mode, hint)
|
||||||
} else {
|
} else {
|
||||||
format!("{}{}", tool.display_name(), hint)
|
format!("{}{}", tool.display_name(), hint)
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<circle cx="12" cy="12" r="10"/>
|
||||||
|
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/>
|
||||||
|
<line x1="12" y1="17" x2="12.01" y2="17"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 297 B |
Loading…
Reference in New Issue