From 52a6c7609c950819acc394adcfd14a0f77ef5020 Mon Sep 17 00:00:00 2001 From: hm21 Date: Thu, 12 Feb 2026 11:54:40 +0100 Subject: [PATCH 1/2] feat(arrow): scale arrow size based on strokeWidth for consistent proportions --- .../models/path_builder/path_builder_arrow.dart | 7 ++++--- .../path_builder/path_builder_freestyle.dart | 14 ++++++++------ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/features/paint_editor/models/path_builder/path_builder_arrow.dart b/lib/features/paint_editor/models/path_builder/path_builder_arrow.dart index e7580178..a149e30c 100644 --- a/lib/features/paint_editor/models/path_builder/path_builder_arrow.dart +++ b/lib/features/paint_editor/models/path_builder/path_builder_arrow.dart @@ -19,11 +19,12 @@ class PathBuilderArrow extends PathBuilderBase { ..lineTo(end.dx, end.dy); // Define the arrowhead (before transformation) - final pathOffset = 1.0 * scale; // this can be adjusted based on strokeWidth + // Scale arrow size based on strokeWidth for consistent proportions + final strokeFactor = painter.strokeWidth / 2; final arrowHead = Path() ..moveTo(0, 0) - ..lineTo(-15 * pathOffset, 10 * pathOffset) - ..lineTo(-15 * pathOffset, -10 * pathOffset) + ..lineTo(-3 * strokeFactor, 2 * strokeFactor) + ..lineTo(-3 * strokeFactor, -2 * strokeFactor) ..close(); // Create transform to rotate + translate the arrowhead to the end point diff --git a/lib/features/paint_editor/models/path_builder/path_builder_freestyle.dart b/lib/features/paint_editor/models/path_builder/path_builder_freestyle.dart index cc36e5c7..ff9801a3 100644 --- a/lib/features/paint_editor/models/path_builder/path_builder_freestyle.dart +++ b/lib/features/paint_editor/models/path_builder/path_builder_freestyle.dart @@ -47,7 +47,8 @@ class PathBuilderFreestyle extends PathBuilderBase { // Add arrowheads if needed if (hasArrowStart || hasArrowEnd) { - final pathOffset = 1.0 * scale; + // Scale arrow size based on strokeWidth for consistent proportions + final strokeFactor = painter.strokeWidth / 2; // Minimum distance for stable direction calculation final minDistance = 20.0 * scale; @@ -55,7 +56,7 @@ class PathBuilderFreestyle extends PathBuilderBase { final (startPoint, directionPoint) = _findPointsWithMinDistance(scaled, minDistance, fromStart: true); if (startPoint != null && directionPoint != null) { - _addArrowHead(startPoint, directionPoint, pathOffset); + _addArrowHead(startPoint, directionPoint, strokeFactor); } } @@ -63,7 +64,7 @@ class PathBuilderFreestyle extends PathBuilderBase { final (endPoint, directionPoint) = _findPointsWithMinDistance(scaled, minDistance, fromStart: false); if (endPoint != null && directionPoint != null) { - _addArrowHead(endPoint, directionPoint, pathOffset); + _addArrowHead(endPoint, directionPoint, strokeFactor); } } } @@ -132,12 +133,13 @@ class PathBuilderFreestyle extends PathBuilderBase { /// Adds an arrowhead at [anchorPoint] pointing away from [directionPoint]. void _addArrowHead( - Offset anchorPoint, Offset directionPoint, double pathOffset) { + Offset anchorPoint, Offset directionPoint, double strokeFactor) { // Open arrowhead (two lines instead of closed triangle) + // Size is proportional to strokeWidth for consistent look final arrowHead = Path() - ..moveTo(-20 * pathOffset, 20 * pathOffset) + ..moveTo(-4 * strokeFactor, 4 * strokeFactor) ..lineTo(0, 0) - ..lineTo(-20 * pathOffset, -20 * pathOffset); + ..lineTo(-4 * strokeFactor, -4 * strokeFactor); // Direction points from directionPoint to anchorPoint final direction = (anchorPoint - directionPoint).direction; From 929f02441dd9288e7a1388c869b15e5c6e92038c Mon Sep 17 00:00:00 2001 From: hm21 Date: Thu, 12 Feb 2026 12:15:31 +0100 Subject: [PATCH 2/2] feat(paint-editor): add custom paint modes and path builders; improve arrow scaling --- CHANGELOG.md | 5 + .../core/constants/example_list_constant.dart | 7 + .../features/custom_path_builder_example.dart | 309 ++++++++++++++++++ .../paint_editor/paint_editor_configs.dart | 31 ++ lib/core/models/i18n/i18n_paint_editor.dart | 18 + lib/core/models/icons/paint_editor_icons.dart | 18 + .../paint_editor/enums/paint_editor_enum.dart | 17 +- .../path_builder/custom_path_builder.dart | 37 +++ .../path_builder/path_builder_base.dart | 23 +- lib/features/paint_editor/paint_editor.dart | 30 ++ .../services/paint_item_hit_test_manager.dart | 2 - .../paint_editor/widgets/draw_paint_item.dart | 2 - .../paint_editor/widgets/paint_canvas.dart | 1 - pubspec.yaml | 2 +- 14 files changed, 493 insertions(+), 9 deletions(-) create mode 100644 example/lib/features/custom_path_builder_example.dart create mode 100644 lib/features/paint_editor/models/path_builder/custom_path_builder.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index b996edd3..76576ecf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 11.23.0 +- **FEAT**(paint-editor): Add `customPathBuilders` to `PaintEditorConfigs`, allowing users to register custom `PathBuilderBase` implementations for any paint mode. +- **FEAT**(paint-editor): Add `custom1`, `custom2`, `custom3` paint modes that require custom path builders to be registered. These can be fully customized with custom icons and i18n labels. +- **FIX**(paint-editor): Arrow head size now scales proportionally with stroke width for consistent appearance. + ## 11.22.2 - **FEAT**(helper-lines): Helper line stroke width is now configurable via the configs. - **FIX**(paint-editor): Fix drawing latency with the Apple Pencil. diff --git a/example/lib/core/constants/example_list_constant.dart b/example/lib/core/constants/example_list_constant.dart index 550b0cf0..2d2d6e6e 100644 --- a/example/lib/core/constants/example_list_constant.dart +++ b/example/lib/core/constants/example_list_constant.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import '/features/ai/ai_group_page.dart'; import '/features/crop_to_main_editor.dart'; +import '/features/custom_path_builder_example.dart'; import '/features/custom_widgets_example.dart'; import '/features/default_example.dart'; import '/features/design_examples/design_example.dart'; @@ -78,6 +79,12 @@ List kImageEditorExamples = const [ icon: Icons.draw_outlined, page: SignatureDrawingExample(), ), + Example( + path: '/custom-path-builder', + name: 'Custom Path Builder', + icon: Icons.brush_outlined, + page: CustomPathBuilderExample(), + ), Example( path: '/stickers', name: 'Stickers', diff --git a/example/lib/features/custom_path_builder_example.dart b/example/lib/features/custom_path_builder_example.dart new file mode 100644 index 00000000..040d6967 --- /dev/null +++ b/example/lib/features/custom_path_builder_example.dart @@ -0,0 +1,309 @@ +// Flutter imports: +import 'dart:math'; + +import 'package:flutter/material.dart'; + +// Package imports: +import 'package:pro_image_editor/pro_image_editor.dart'; + +// Project imports: +import '/core/constants/example_constants.dart'; +import '/core/mixin/example_helper.dart'; + +/// A widget that demonstrates how to create and use custom paint mode +/// path builders. +/// +/// The [CustomPathBuilderExample] widget shows how users can provide their +/// own [PathBuilderBase] implementations to customize the rendering of +/// paint modes like arrows, lines, or completely custom shapes. +/// +/// Example usage: +/// ```dart +/// CustomPathBuilderExample(); +/// ``` +class CustomPathBuilderExample extends StatefulWidget { + /// Creates a new [CustomPathBuilderExample] widget. + const CustomPathBuilderExample({super.key}); + + @override + State createState() => + _CustomPathBuilderExampleState(); +} + +class _CustomPathBuilderExampleState extends State + with ExampleHelperState { + late final _configs = ProImageEditorConfigs( + designMode: platformDesignMode, + // Custom i18n labels for the custom paint modes + i18n: const I18n( + paintEditor: I18nPaintEditor( + custom1: 'Double Arrow', + custom2: 'Wavy Line', + custom3: 'Star', + ), + ), + mainEditor: MainEditorConfigs( + enableCloseButton: !isDesktopMode(context), + ), + paintEditor: PaintEditorConfigs( + initialPaintMode: PaintMode.custom1, + // Custom icons for the custom tools + icons: const PaintEditorIcons( + custom1: Icons.swap_horiz, + custom2: Icons.waves, + custom3: Icons.star_outline, + ), + // Only show our 3 custom tools + tools: const [ + PaintMode.custom1, // Double Arrow + PaintMode.custom2, // Wavy Line + PaintMode.custom3, // Star + ], + // Register custom path builders for the custom paint modes + customPathBuilders: { + // Custom1: Double-headed arrow + PaintMode.custom1: ({ + required item, + required scale, + required paintEditorConfigs, + }) => + DoubleArrowPathBuilder( + item: item, + scale: scale, + paintEditorConfigs: paintEditorConfigs, + ), + // Custom2: Wavy line + PaintMode.custom2: ({ + required item, + required scale, + required paintEditorConfigs, + }) => + WavyLinePathBuilder( + item: item, + scale: scale, + paintEditorConfigs: paintEditorConfigs, + ), + // Custom3: Star shape + PaintMode.custom3: ({ + required item, + required scale, + required paintEditorConfigs, + }) => + StarPathBuilder( + item: item, + scale: scale, + paintEditorConfigs: paintEditorConfigs, + ), + }, + ), + ); + + late final _callbacks = ProImageEditorCallbacks( + onImageEditingStarted: onImageEditingStarted, + onImageEditingComplete: onImageEditingComplete, + onCloseEditor: (editorMode) => onCloseEditor( + editorMode: editorMode, + enablePop: !isDesktopMode(context), + ), + mainEditorCallbacks: MainEditorCallbacks( + helperLines: HelperLinesCallbacks(onLineHit: vibrateLineHit), + ), + ); + + @override + void initState() { + super.initState(); + preCacheImage(assetPath: kImageEditorExampleAssetPath); + } + + @override + Widget build(BuildContext context) { + if (!isPreCached) return const PrepareImageWidget(); + + return ProImageEditor.asset( + kImageEditorExampleAssetPath, + key: editorKey, + callbacks: _callbacks, + configs: _configs, + ); + } +} + +// ============================================================================= +// Custom Path Builders +// ============================================================================= + +/// A custom path builder that creates a double-headed arrow (arrows at both +/// ends of the line). +/// +/// This demonstrates how to override the default arrow behavior by extending +/// [PathBuilderBase] and implementing custom rendering logic. +class DoubleArrowPathBuilder extends PathBuilderBase { + /// Creates a double arrow path builder. + DoubleArrowPathBuilder({ + required super.item, + required super.scale, + required super.paintEditorConfigs, + }); + + @override + Path build() { + // Draw the main line + path + ..moveTo(start.dx, start.dy) + ..lineTo(end.dx, end.dy); + + // Scale arrow size based on strokeWidth for consistent proportions + final strokeFactor = painter.strokeWidth / 2; + + // Create arrowhead path (pointing right by default) + Path createArrowHead() { + return Path() + ..moveTo(0, 0) + ..lineTo(-3 * strokeFactor, 2 * strokeFactor) + ..lineTo(-3 * strokeFactor, -2 * strokeFactor) + ..close(); + } + + // Direction from start to end + final directionForward = (end - start).direction; + // Direction from end to start (opposite) + final directionBackward = directionForward + pi; + + // Arrow at the end point (pointing forward) + final endTransform = Matrix4.identity() + ..translateByDouble(end.dx, end.dy, 0.0, 1.0) + ..rotateZ(directionForward); + path.addPath( + createArrowHead().transform(endTransform.storage), + Offset.zero, + ); + + // Arrow at the start point (pointing backward) + final startTransform = Matrix4.identity() + ..translateByDouble(start.dx, start.dy, 0.0, 1.0) + ..rotateZ(directionBackward); + path.addPath( + createArrowHead().transform(startTransform.storage), + Offset.zero, + ); + + return path; + } + + @override + bool hitTest(Offset position) { + return super.hitTestLine(position); + } +} + +/// A custom path builder that creates a wavy/sine-wave line between two +/// points. +/// +/// This demonstrates how to create completely custom drawing effects +/// by extending [PathBuilderBase]. +class WavyLinePathBuilder extends PathBuilderBase { + /// Creates a wavy line path builder. + WavyLinePathBuilder({ + required super.item, + required super.scale, + required super.paintEditorConfigs, + }); + + @override + Path build() { + final distance = (end - start).distance; + final direction = (end - start).direction; + + // Wave parameters - scale with stroke width for consistency + final amplitude = painter.strokeWidth * 2; + final wavelength = painter.strokeWidth * 4; + + // Number of wave segments + final segments = (distance / 2).ceil(); + + path.moveTo(start.dx, start.dy); + + for (int i = 1; i <= segments; i++) { + final t = i / segments; + final x = start.dx + (end.dx - start.dx) * t; + final y = start.dy + (end.dy - start.dy) * t; + + // Calculate perpendicular offset for the wave + final waveOffset = sin(t * distance / wavelength * 2 * pi) * amplitude; + + // Perpendicular direction (90 degrees rotated) + final perpX = -sin(direction) * waveOffset; + final perpY = cos(direction) * waveOffset; + + path.lineTo(x + perpX, y + perpY); + } + + return path; + } + + @override + bool hitTest(Offset position) { + return hitTestWithStroke(position); + } +} + +/// A custom path builder that creates a star shape. +/// +/// This demonstrates how to override shape-based paint modes (like rect) +/// with completely different shapes by extending [PathBuilderBase]. +class StarPathBuilder extends PathBuilderBase { + /// Creates a star path builder. + StarPathBuilder({ + required super.item, + required super.scale, + required super.paintEditorConfigs, + }); + + @override + Path build() { + // Calculate the center and radius from the bounding box + final centerX = (start.dx + end.dx) / 2; + final centerY = (start.dy + end.dy) / 2; + final radiusX = (end.dx - start.dx).abs() / 2; + final radiusY = (end.dy - start.dy).abs() / 2; + final outerRadius = min(radiusX, radiusY); + final innerRadius = outerRadius * 0.4; + + // 5-pointed star + const points = 5; + const angleOffset = -pi / 2; // Start from the top + + path.moveTo( + centerX + outerRadius * cos(angleOffset), + centerY + outerRadius * sin(angleOffset), + ); + + for (int i = 1; i <= points * 2; i++) { + final radius = i.isOdd ? innerRadius : outerRadius; + final angle = angleOffset + (i * pi / points); + path.lineTo( + centerX + radius * cos(angle), + centerY + radius * sin(angle), + ); + } + + path.close(); + + return path; + } + + @override + Path? buildSecond() { + // Return the same path for fill if item.fill is true + if (item.fill) { + return build(); + } + return null; + } + + @override + bool hitTest(Offset position) { + return hitTestFillableObject(position); + } +} diff --git a/lib/core/models/editor_configs/paint_editor/paint_editor_configs.dart b/lib/core/models/editor_configs/paint_editor/paint_editor_configs.dart index bf0773c2..9d4be58b 100644 --- a/lib/core/models/editor_configs/paint_editor/paint_editor_configs.dart +++ b/lib/core/models/editor_configs/paint_editor/paint_editor_configs.dart @@ -3,6 +3,7 @@ import 'package:flutter/widgets.dart'; import '/features/paint_editor/enums/paint_editor_enum.dart'; +import '/features/paint_editor/models/path_builder/custom_path_builder.dart'; import '../../custom_widgets/paint_editor_widgets.dart'; import '../../icons/paint_editor_icons.dart'; import '../../styles/paint_editor_style.dart'; @@ -12,6 +13,8 @@ import '../utils/editor_safe_area.dart'; import '../utils/zoom_configs.dart'; import 'censor_configs.dart'; +export '/features/paint_editor/models/path_builder/custom_path_builder.dart'; +export '/features/paint_editor/models/path_builder/path_builder_base.dart'; export '../../custom_widgets/paint_editor_widgets.dart'; export '../../icons/paint_editor_icons.dart'; export '../../styles/paint_editor_style.dart'; @@ -108,6 +111,7 @@ class PaintEditorConfigs extends ZoomConfigs this.style = const PaintEditorStyle(), this.icons = const PaintEditorIcons(), this.widgets = const PaintEditorWidgets(), + this.customPathBuilders = const {}, }) : assert(maxScale >= minScale, 'maxScale must be greater than or equal to minScale'), assert(editorMaxScale > editorMinScale, @@ -321,6 +325,31 @@ class PaintEditorConfigs extends ZoomConfigs /// Widgets associated with the paint editor. final PaintEditorWidgets widgets; + /// A map of custom path builders for specific paint modes. + /// + /// Users can provide their own [PathBuilderBase] implementations to + /// override the default behavior of existing paint modes or to create + /// custom drawing tools with unique rendering logic. + /// + /// Example: + /// ```dart + /// PaintEditorConfigs( + /// customPathBuilders: { + /// PaintMode.arrow: ({ + /// required item, + /// required scale, + /// required paintEditorConfigs, + /// }) => + /// MyCustomArrowBuilder( + /// item: item, + /// scale: scale, + /// paintEditorConfigs: paintEditorConfigs, + /// ), + /// }, + /// ) + /// ``` + final Map customPathBuilders; + /// Creates a copy of this `PaintEditorConfigs` object with the given fields /// replaced with new values. /// @@ -389,6 +418,7 @@ class PaintEditorConfigs extends ZoomConfigs double? dashLineWidthFactor, double? dashDotLineSpacingFactor, double? dashDotLineWidthFactor, + Map? customPathBuilders, }) { return PaintEditorConfigs( layerFractionalOffset: @@ -452,6 +482,7 @@ class PaintEditorConfigs extends ZoomConfigs dashDotLineSpacingFactor ?? this.dashDotLineSpacingFactor, dashDotLineWidthFactor: dashDotLineWidthFactor ?? this.dashDotLineWidthFactor, + customPathBuilders: customPathBuilders ?? this.customPathBuilders, ); } } diff --git a/lib/core/models/i18n/i18n_paint_editor.dart b/lib/core/models/i18n/i18n_paint_editor.dart index afe78313..58201a12 100644 --- a/lib/core/models/i18n/i18n_paint_editor.dart +++ b/lib/core/models/i18n/i18n_paint_editor.dart @@ -24,6 +24,9 @@ class I18nPaintEditor { this.polygon = 'Polygon', this.blur = 'Blur', this.pixelate = 'Pixelate', + this.custom1 = 'Custom 1', + this.custom2 = 'Custom 2', + this.custom3 = 'Custom 3', this.lineWidth = 'Line width', this.eraser = 'Eraser', this.toggleFill = 'Toggle fill', @@ -88,6 +91,15 @@ class I18nPaintEditor { /// Text for the "Pixelate" paint mode. final String pixelate; + /// Text for the "Custom 1" paint mode. + final String custom1; + + /// Text for the "Custom 2" paint mode. + final String custom2; + + /// Text for the "Custom 3" paint mode. + final String custom3; + /// Text for the "Eraser" paint mode. final String eraser; @@ -153,6 +165,9 @@ class I18nPaintEditor { String? polygon, String? blur, String? pixelate, + String? custom1, + String? custom2, + String? custom3, String? eraser, String? lineWidth, String? toggleFill, @@ -187,6 +202,9 @@ class I18nPaintEditor { polygon: polygon ?? this.polygon, blur: blur ?? this.blur, pixelate: pixelate ?? this.pixelate, + custom1: custom1 ?? this.custom1, + custom2: custom2 ?? this.custom2, + custom3: custom3 ?? this.custom3, eraser: eraser ?? this.eraser, lineWidth: lineWidth ?? this.lineWidth, toggleFill: toggleFill ?? this.toggleFill, diff --git a/lib/core/models/icons/paint_editor_icons.dart b/lib/core/models/icons/paint_editor_icons.dart index 1e41aaf6..bb814c8a 100644 --- a/lib/core/models/icons/paint_editor_icons.dart +++ b/lib/core/models/icons/paint_editor_icons.dart @@ -28,6 +28,9 @@ class PaintEditorIcons { this.hexagon = Icons.hexagon_outlined, this.polygon = Icons.pentagon_outlined, this.pixelate = Icons.grid_on, + this.custom1 = Icons.bug_report_outlined, + this.custom2 = Icons.bug_report_outlined, + this.custom3 = Icons.bug_report_outlined, this.blur = Icons.blur_on_rounded, this.applyChanges = Icons.done, this.backButton = Icons.arrow_back, @@ -103,6 +106,15 @@ class PaintEditorIcons { /// The icon for the pixelate drawing tool. final IconData pixelate; + /// The icon for the custom1 drawing tool. + final IconData custom1; + + /// The icon for the custom2 drawing tool. + final IconData custom2; + + /// The icon for the custom3 drawing tool. + final IconData custom3; + /// The icon for the eraser tool. final IconData eraser; @@ -140,6 +152,9 @@ class PaintEditorIcons { IconData? polygon, IconData? blur, IconData? pixelate, + IconData? custom1, + IconData? custom2, + IconData? custom3, IconData? eraser, IconData? backButton, IconData? applyChanges, @@ -168,6 +183,9 @@ class PaintEditorIcons { polygon: polygon ?? this.polygon, blur: blur ?? this.blur, pixelate: pixelate ?? this.pixelate, + custom1: custom1 ?? this.custom1, + custom2: custom2 ?? this.custom2, + custom3: custom3 ?? this.custom3, eraser: eraser ?? this.eraser, backButton: backButton ?? this.backButton, applyChanges: applyChanges ?? this.applyChanges, diff --git a/lib/features/paint_editor/enums/paint_editor_enum.dart b/lib/features/paint_editor/enums/paint_editor_enum.dart index e088687a..a59d86ca 100644 --- a/lib/features/paint_editor/enums/paint_editor_enum.dart +++ b/lib/features/paint_editor/enums/paint_editor_enum.dart @@ -47,7 +47,22 @@ enum PaintMode { blur, /// Creates an area that will pixelate the background. - pixelate; + pixelate, + + /// Custom paint mode 1 - requires a custom path builder to be registered. + /// + /// Use [PaintEditorConfigs.customPathBuilders] to provide the implementation. + custom1, + + /// Custom paint mode 2 - requires a custom path builder to be registered. + /// + /// Use [PaintEditorConfigs.customPathBuilders] to provide the implementation. + custom2, + + /// Custom paint mode 3 - requires a custom path builder to be registered. + /// + /// Use [PaintEditorConfigs.customPathBuilders] to provide the implementation. + custom3; /// Returns `true` if this mode is any of the freehand drawing modes. /// diff --git a/lib/features/paint_editor/models/path_builder/custom_path_builder.dart b/lib/features/paint_editor/models/path_builder/custom_path_builder.dart new file mode 100644 index 00000000..5839f45c --- /dev/null +++ b/lib/features/paint_editor/models/path_builder/custom_path_builder.dart @@ -0,0 +1,37 @@ +import '/core/models/editor_configs/paint_editor/paint_editor_configs.dart'; + +export '../painted_model.dart'; + +/// A factory function for creating custom [PathBuilderBase] instances. +/// +/// This typedef defines the signature for a custom path builder factory, +/// allowing users to provide their own path builders for specific paint modes +/// or custom drawing tools. +/// +/// Parameters: +/// - [item]: The painted model containing all drawing information. +/// - [scale]: The scale factor for rendering. +/// - [paintEditorConfigs]: The paint editor configuration. +/// +/// Returns a [PathBuilderBase] instance that will be used to build and +/// draw the path. +/// +/// Example: +/// ```dart +/// PathBuilderBase myCustomBuilder({ +/// required PaintedModel item, +/// required double scale, +/// required PaintEditorConfigs paintEditorConfigs, +/// }) { +/// return MyCustomPathBuilder( +/// item: item, +/// scale: scale, +/// paintEditorConfigs: paintEditorConfigs, +/// ); +/// } +/// ``` +typedef CustomPathBuilderFactory = PathBuilderBase Function({ + required PaintedModel item, + required double scale, + required PaintEditorConfigs paintEditorConfigs, +}); diff --git a/lib/features/paint_editor/models/path_builder/path_builder_base.dart b/lib/features/paint_editor/models/path_builder/path_builder_base.dart index e6adfb1b..71ace7ad 100644 --- a/lib/features/paint_editor/models/path_builder/path_builder_base.dart +++ b/lib/features/paint_editor/models/path_builder/path_builder_base.dart @@ -2,7 +2,6 @@ import 'package:flutter/widgets.dart'; import '/core/models/editor_configs/paint_editor/paint_editor_configs.dart'; import '../../enums/paint_editor_enum.dart'; -import '../painted_model.dart'; import 'path_builder_arrow.dart'; import 'path_builder_circle.dart'; import 'path_builder_dash_dot_line.dart'; @@ -25,12 +24,25 @@ abstract class PathBuilderBase { ..style = item.paint.style ..strokeWidth = item.paint.strokeWidth * scale; - /// Factory that returns the appropriate PathBuilder for a given PaintMode + /// Factory that returns the appropriate PathBuilder for a given PaintMode. + /// + /// If a custom path builder is registered in [paintEditorConfigs] for the + /// given mode, it will be used instead of the default implementation. factory PathBuilderBase.fromMode({ required PaintedModel item, required double scale, required PaintEditorConfigs paintEditorConfigs, }) { + // Check for custom path builder first + final customBuilder = paintEditorConfigs.customPathBuilders[item.mode]; + if (customBuilder != null) { + return customBuilder( + item: item, + scale: scale, + paintEditorConfigs: paintEditorConfigs, + ); + } + switch (item.mode) { case PaintMode.line: return PathBuilderLine( @@ -96,6 +108,13 @@ abstract class PathBuilderBase { case PaintMode.blur: case PaintMode.pixelate: throw ArgumentError('${item.mode} is not a valid PaintMode'); + case PaintMode.custom1: + case PaintMode.custom2: + case PaintMode.custom3: + throw ArgumentError( + '${item.mode} requires a custom path builder. ' + 'Register one via PaintEditorConfigs.customPathBuilders.', + ); } } diff --git a/lib/features/paint_editor/paint_editor.dart b/lib/features/paint_editor/paint_editor.dart index 4e888be3..4130c906 100644 --- a/lib/features/paint_editor/paint_editor.dart +++ b/lib/features/paint_editor/paint_editor.dart @@ -465,6 +465,36 @@ class PaintEditorState extends State icon: paintEditorConfigs.icons.moveAndZoom, label: i18n.paintEditor.moveAndZoom, ); + + case PaintMode.custom1: + if (!paintEditorConfigs.customPathBuilders + .containsKey(PaintMode.custom1)) { + return null; + } + return PaintModeHelper( + icon: paintEditorConfigs.icons.custom1, + label: i18n.paintEditor.custom1, + ); + + case PaintMode.custom2: + if (!paintEditorConfigs.customPathBuilders + .containsKey(PaintMode.custom2)) { + return null; + } + return PaintModeHelper( + icon: paintEditorConfigs.icons.custom2, + label: i18n.paintEditor.custom2, + ); + + case PaintMode.custom3: + if (!paintEditorConfigs.customPathBuilders + .containsKey(PaintMode.custom3)) { + return null; + } + return PaintModeHelper( + icon: paintEditorConfigs.icons.custom3, + label: i18n.paintEditor.custom3, + ); } } diff --git a/lib/features/paint_editor/services/paint_item_hit_test_manager.dart b/lib/features/paint_editor/services/paint_item_hit_test_manager.dart index fa485185..07ce0594 100644 --- a/lib/features/paint_editor/services/paint_item_hit_test_manager.dart +++ b/lib/features/paint_editor/services/paint_item_hit_test_manager.dart @@ -2,8 +2,6 @@ import 'dart:ui'; import '/core/models/editor_configs/paint_editor/paint_editor_configs.dart'; import '../enums/paint_editor_enum.dart'; -import '../models/painted_model.dart'; -import '../models/path_builder/path_builder_base.dart'; /// A manager class responsible for handling hit testing of paint items /// within the paint editor feature. This class provides functionality diff --git a/lib/features/paint_editor/widgets/draw_paint_item.dart b/lib/features/paint_editor/widgets/draw_paint_item.dart index 18e68d9d..bda461a0 100644 --- a/lib/features/paint_editor/widgets/draw_paint_item.dart +++ b/lib/features/paint_editor/widgets/draw_paint_item.dart @@ -2,8 +2,6 @@ import 'package:flutter/material.dart'; import '/core/models/editor_configs/paint_editor/paint_editor_configs.dart'; -import '../models/painted_model.dart'; -import '../models/path_builder/path_builder_base.dart'; import '../services/paint_item_hit_test_manager.dart'; /// Handles the paint ongoing on the canvas. diff --git a/lib/features/paint_editor/widgets/paint_canvas.dart b/lib/features/paint_editor/widgets/paint_canvas.dart index 4e4dbb00..c9e0c56f 100644 --- a/lib/features/paint_editor/widgets/paint_canvas.dart +++ b/lib/features/paint_editor/widgets/paint_canvas.dart @@ -12,7 +12,6 @@ import '/shared/widgets/censor/pixelate_area_item.dart'; import '../controllers/paint_controller.dart'; import '../enums/paint_editor_enum.dart'; import '../models/eraser_model.dart'; -import '../models/painted_model.dart'; import '../services/paint_item_hit_test_manager.dart'; import 'draw_paint_item.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index 856aa98b..3eef4802 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: pro_image_editor description: "A Flutter image editor: Seamlessly enhance your images with user-friendly editing features." -version: 11.22.2 +version: 11.23.0 homepage: https://github.com/hm21/pro_image_editor/ repository: https://github.com/hm21/pro_image_editor/ documentation: https://github.com/hm21/pro_image_editor/