From 4b24340bd2352acade3cf0322078c737ba401d7b Mon Sep 17 00:00:00 2001 From: CodeDoctorDE Date: Sat, 28 Dec 2024 15:12:35 +0100 Subject: [PATCH 1/8] Improve temporary state --- api/lib/src/models/tool.dart | 42 ++++ api/lib/src/models/tool.freezed.dart | 270 ++++++++++++++++++++++ api/lib/src/models/tool.g.dart | 39 ++++ api/lib/src/models/utilities.dart | 9 - api/lib/src/models/utilities.freezed.dart | 116 +--------- api/lib/src/models/utilities.g.dart | 12 - api/lib/src/models/view.dart | 8 +- api/lib/src/models/view.freezed.dart | 126 +--------- api/lib/src/models/view.g.dart | 15 +- app/lib/actions/select.dart | 2 +- app/lib/cubits/current_index.dart | 35 ++- app/lib/cubits/current_index.freezed.dart | 36 +-- app/lib/dialogs/collections.dart | 3 +- app/lib/dialogs/import/add.dart | 27 ++- app/lib/handlers/collection.dart | 2 +- app/lib/handlers/export.dart | 2 +- app/lib/handlers/eye_dropper.dart | 9 + app/lib/handlers/full_screen.dart | 2 +- app/lib/handlers/handler.dart | 6 +- app/lib/handlers/import.dart | 3 - app/lib/handlers/redo.dart | 2 +- app/lib/handlers/select.dart | 6 +- app/lib/handlers/undo.dart | 2 +- app/lib/l10n/app_en.arb | 1 - app/lib/renderers/utilities.dart | 21 +- app/lib/selections/selection.dart | 1 - app/lib/selections/utilities.dart | 99 +------- app/lib/services/import.dart | 2 +- app/lib/views/edit.dart | 3 +- app/lib/views/view.dart | 7 +- app/lib/visualizer/tool.dart | 17 ++ app/lib/widgets/option_button.dart | 18 +- 32 files changed, 497 insertions(+), 446 deletions(-) diff --git a/api/lib/src/models/tool.dart b/api/lib/src/models/tool.dart index 2eb4ba4ca787..b5906b923b09 100644 --- a/api/lib/src/models/tool.dart +++ b/api/lib/src/models/tool.dart @@ -43,6 +43,8 @@ enum SelectMode { rectangle, lasso } enum LaserAnimation { fade, path } +enum ToolCategory { normal, import, surface, action, view } + @Freezed(equal: false) sealed class Tool with _$Tool { Tool._(); @@ -190,10 +192,50 @@ sealed class Tool with _$Tool { @Default(SurfaceTexture.pattern()) SurfaceTexture texture, }) = TextureTool; + factory Tool.ruler({ + @Default('') String name, + @Default('') String displayIcon, + @Default(SRGBColor.black) @ColorJsonConverter() SRGBColor gridColor, + }) = RulerTool; + + factory Tool.grid({ + @Default('') String name, + @Default('') String displayIcon, + @Default(SRGBColor.black) @ColorJsonConverter() SRGBColor color, + @Default(20) double xSize, + @Default(20) double ySize, + }) = GridTool; + factory Tool.eyeDropper({ @Default('') String name, @Default('') String displayIcon, }) = EyeDropperTool; factory Tool.fromJson(Map json) => _$ToolFromJson(json); + + ToolCategory get category => switch (this) { + SelectTool() => ToolCategory.normal, + HandTool() => ToolCategory.normal, + ImportTool() => ToolCategory.import, + UndoTool() => ToolCategory.action, + RedoTool() => ToolCategory.action, + LabelTool() => ToolCategory.normal, + PenTool() => ToolCategory.normal, + EraserTool() => ToolCategory.normal, + PathEraserTool() => ToolCategory.normal, + CollectionTool() => ToolCategory.normal, + AreaTool() => ToolCategory.normal, + LaserTool() => ToolCategory.normal, + ShapeTool() => ToolCategory.surface, + StampTool() => ToolCategory.surface, + PresentationTool() => ToolCategory.normal, + SpacerTool() => ToolCategory.normal, + FullScreenTool() => ToolCategory.action, + AssetTool() => ToolCategory.import, + ExportTool() => ToolCategory.action, + TextureTool() => ToolCategory.surface, + RulerTool() => ToolCategory.view, + GridTool() => ToolCategory.view, + EyeDropperTool() => ToolCategory.action, + }; } diff --git a/api/lib/src/models/tool.freezed.dart b/api/lib/src/models/tool.freezed.dart index f60eba4d63c2..de96650c80d6 100644 --- a/api/lib/src/models/tool.freezed.dart +++ b/api/lib/src/models/tool.freezed.dart @@ -56,6 +56,10 @@ Tool _$ToolFromJson(Map json) { return ExportTool.fromJson(json); case 'texture': return TextureTool.fromJson(json); + case 'ruler': + return RulerTool.fromJson(json); + case 'grid': + return GridTool.fromJson(json); case 'eyeDropper': return EyeDropperTool.fromJson(json); @@ -2785,6 +2789,272 @@ abstract class TextureTool extends Tool { throw _privateConstructorUsedError; } +/// @nodoc +abstract class _$$RulerToolImplCopyWith<$Res> implements $ToolCopyWith<$Res> { + factory _$$RulerToolImplCopyWith( + _$RulerToolImpl value, $Res Function(_$RulerToolImpl) then) = + __$$RulerToolImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String name, + String displayIcon, + @ColorJsonConverter() SRGBColor gridColor}); +} + +/// @nodoc +class __$$RulerToolImplCopyWithImpl<$Res> + extends _$ToolCopyWithImpl<$Res, _$RulerToolImpl> + implements _$$RulerToolImplCopyWith<$Res> { + __$$RulerToolImplCopyWithImpl( + _$RulerToolImpl _value, $Res Function(_$RulerToolImpl) _then) + : super(_value, _then); + + /// Create a copy of Tool + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? name = null, + Object? displayIcon = null, + Object? gridColor = null, + }) { + return _then(_$RulerToolImpl( + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + displayIcon: null == displayIcon + ? _value.displayIcon + : displayIcon // ignore: cast_nullable_to_non_nullable + as String, + gridColor: null == gridColor + ? _value.gridColor + : gridColor // ignore: cast_nullable_to_non_nullable + as SRGBColor, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$RulerToolImpl extends RulerTool { + _$RulerToolImpl( + {this.name = '', + this.displayIcon = '', + @ColorJsonConverter() this.gridColor = SRGBColor.black, + final String? $type}) + : $type = $type ?? 'ruler', + super._(); + + factory _$RulerToolImpl.fromJson(Map json) => + _$$RulerToolImplFromJson(json); + + @override + @JsonKey() + final String name; + @override + @JsonKey() + final String displayIcon; + @override + @JsonKey() + @ColorJsonConverter() + final SRGBColor gridColor; + + @JsonKey(name: 'type') + final String $type; + + @override + String toString() { + return 'Tool.ruler(name: $name, displayIcon: $displayIcon, gridColor: $gridColor)'; + } + + /// Create a copy of Tool + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$RulerToolImplCopyWith<_$RulerToolImpl> get copyWith => + __$$RulerToolImplCopyWithImpl<_$RulerToolImpl>(this, _$identity); + + @override + Map toJson() { + return _$$RulerToolImplToJson( + this, + ); + } +} + +abstract class RulerTool extends Tool { + factory RulerTool( + {final String name, + final String displayIcon, + @ColorJsonConverter() final SRGBColor gridColor}) = _$RulerToolImpl; + RulerTool._() : super._(); + + factory RulerTool.fromJson(Map json) = + _$RulerToolImpl.fromJson; + + @override + String get name; + @override + String get displayIcon; + @ColorJsonConverter() + SRGBColor get gridColor; + + /// Create a copy of Tool + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$RulerToolImplCopyWith<_$RulerToolImpl> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$GridToolImplCopyWith<$Res> implements $ToolCopyWith<$Res> { + factory _$$GridToolImplCopyWith( + _$GridToolImpl value, $Res Function(_$GridToolImpl) then) = + __$$GridToolImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String name, + String displayIcon, + @ColorJsonConverter() SRGBColor color, + double xSize, + double ySize}); +} + +/// @nodoc +class __$$GridToolImplCopyWithImpl<$Res> + extends _$ToolCopyWithImpl<$Res, _$GridToolImpl> + implements _$$GridToolImplCopyWith<$Res> { + __$$GridToolImplCopyWithImpl( + _$GridToolImpl _value, $Res Function(_$GridToolImpl) _then) + : super(_value, _then); + + /// Create a copy of Tool + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? name = null, + Object? displayIcon = null, + Object? color = null, + Object? xSize = null, + Object? ySize = null, + }) { + return _then(_$GridToolImpl( + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + displayIcon: null == displayIcon + ? _value.displayIcon + : displayIcon // ignore: cast_nullable_to_non_nullable + as String, + color: null == color + ? _value.color + : color // ignore: cast_nullable_to_non_nullable + as SRGBColor, + xSize: null == xSize + ? _value.xSize + : xSize // ignore: cast_nullable_to_non_nullable + as double, + ySize: null == ySize + ? _value.ySize + : ySize // ignore: cast_nullable_to_non_nullable + as double, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$GridToolImpl extends GridTool { + _$GridToolImpl( + {this.name = '', + this.displayIcon = '', + @ColorJsonConverter() this.color = SRGBColor.black, + this.xSize = 20, + this.ySize = 20, + final String? $type}) + : $type = $type ?? 'grid', + super._(); + + factory _$GridToolImpl.fromJson(Map json) => + _$$GridToolImplFromJson(json); + + @override + @JsonKey() + final String name; + @override + @JsonKey() + final String displayIcon; + @override + @JsonKey() + @ColorJsonConverter() + final SRGBColor color; + @override + @JsonKey() + final double xSize; + @override + @JsonKey() + final double ySize; + + @JsonKey(name: 'type') + final String $type; + + @override + String toString() { + return 'Tool.grid(name: $name, displayIcon: $displayIcon, color: $color, xSize: $xSize, ySize: $ySize)'; + } + + /// Create a copy of Tool + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$GridToolImplCopyWith<_$GridToolImpl> get copyWith => + __$$GridToolImplCopyWithImpl<_$GridToolImpl>(this, _$identity); + + @override + Map toJson() { + return _$$GridToolImplToJson( + this, + ); + } +} + +abstract class GridTool extends Tool { + factory GridTool( + {final String name, + final String displayIcon, + @ColorJsonConverter() final SRGBColor color, + final double xSize, + final double ySize}) = _$GridToolImpl; + GridTool._() : super._(); + + factory GridTool.fromJson(Map json) = + _$GridToolImpl.fromJson; + + @override + String get name; + @override + String get displayIcon; + @ColorJsonConverter() + SRGBColor get color; + double get xSize; + double get ySize; + + /// Create a copy of Tool + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$GridToolImplCopyWith<_$GridToolImpl> get copyWith => + throw _privateConstructorUsedError; +} + /// @nodoc abstract class _$$EyeDropperToolImplCopyWith<$Res> implements $ToolCopyWith<$Res> { diff --git a/api/lib/src/models/tool.g.dart b/api/lib/src/models/tool.g.dart index b9e66350ce09..460514d1ecf7 100644 --- a/api/lib/src/models/tool.g.dart +++ b/api/lib/src/models/tool.g.dart @@ -424,6 +424,45 @@ Map _$$TextureToolImplToJson(_$TextureToolImpl instance) => 'type': instance.$type, }; +_$RulerToolImpl _$$RulerToolImplFromJson(Map json) => _$RulerToolImpl( + name: json['name'] as String? ?? '', + displayIcon: json['displayIcon'] as String? ?? '', + gridColor: json['gridColor'] == null + ? SRGBColor.black + : const ColorJsonConverter() + .fromJson((json['gridColor'] as num).toInt()), + $type: json['type'] as String?, + ); + +Map _$$RulerToolImplToJson(_$RulerToolImpl instance) => + { + 'name': instance.name, + 'displayIcon': instance.displayIcon, + 'gridColor': const ColorJsonConverter().toJson(instance.gridColor), + 'type': instance.$type, + }; + +_$GridToolImpl _$$GridToolImplFromJson(Map json) => _$GridToolImpl( + name: json['name'] as String? ?? '', + displayIcon: json['displayIcon'] as String? ?? '', + color: json['color'] == null + ? SRGBColor.black + : const ColorJsonConverter().fromJson((json['color'] as num).toInt()), + xSize: (json['xSize'] as num?)?.toDouble() ?? 20, + ySize: (json['ySize'] as num?)?.toDouble() ?? 20, + $type: json['type'] as String?, + ); + +Map _$$GridToolImplToJson(_$GridToolImpl instance) => + { + 'name': instance.name, + 'displayIcon': instance.displayIcon, + 'color': const ColorJsonConverter().toJson(instance.color), + 'xSize': instance.xSize, + 'ySize': instance.ySize, + 'type': instance.$type, + }; + _$EyeDropperToolImpl _$$EyeDropperToolImplFromJson(Map json) => _$EyeDropperToolImpl( name: json['name'] as String? ?? '', diff --git a/api/lib/src/models/utilities.dart b/api/lib/src/models/utilities.dart index 1c69853daca6..20f77ecddea4 100644 --- a/api/lib/src/models/utilities.dart +++ b/api/lib/src/models/utilities.dart @@ -1,6 +1,3 @@ -import 'dart:math'; - -import 'package:butterfly_api/butterfly_api.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'utilities.g.dart'; @@ -13,13 +10,7 @@ sealed class UtilitiesState with _$UtilitiesState { @Default(false) bool lockZoom, @Default(false) bool lockHorizontal, @Default(false) bool lockVertical, - @Default(false) bool rulerEnabled, - @Default(false) bool gridEnabled, @Default(false) bool fullSelection, - @DoublePointJsonConverter() - @Default(Point(0.0, 0.0)) - Point rulerPosition, - @Default(0) double rulerAngle, }) = _UtilitiesState; factory UtilitiesState.fromJson(Map json) => diff --git a/api/lib/src/models/utilities.freezed.dart b/api/lib/src/models/utilities.freezed.dart index f013d77e3d5e..5a80382878a2 100644 --- a/api/lib/src/models/utilities.freezed.dart +++ b/api/lib/src/models/utilities.freezed.dart @@ -24,12 +24,7 @@ mixin _$UtilitiesState { bool get lockZoom => throw _privateConstructorUsedError; bool get lockHorizontal => throw _privateConstructorUsedError; bool get lockVertical => throw _privateConstructorUsedError; - bool get rulerEnabled => throw _privateConstructorUsedError; - bool get gridEnabled => throw _privateConstructorUsedError; bool get fullSelection => throw _privateConstructorUsedError; - @DoublePointJsonConverter() - Point get rulerPosition => throw _privateConstructorUsedError; - double get rulerAngle => throw _privateConstructorUsedError; /// Serializes this UtilitiesState to a JSON map. Map toJson() => throw _privateConstructorUsedError; @@ -52,11 +47,7 @@ abstract class $UtilitiesStateCopyWith<$Res> { bool lockZoom, bool lockHorizontal, bool lockVertical, - bool rulerEnabled, - bool gridEnabled, - bool fullSelection, - @DoublePointJsonConverter() Point rulerPosition, - double rulerAngle}); + bool fullSelection}); } /// @nodoc @@ -78,11 +69,7 @@ class _$UtilitiesStateCopyWithImpl<$Res, $Val extends UtilitiesState> Object? lockZoom = null, Object? lockHorizontal = null, Object? lockVertical = null, - Object? rulerEnabled = null, - Object? gridEnabled = null, Object? fullSelection = null, - Object? rulerPosition = null, - Object? rulerAngle = null, }) { return _then(_value.copyWith( lockCollection: null == lockCollection @@ -101,26 +88,10 @@ class _$UtilitiesStateCopyWithImpl<$Res, $Val extends UtilitiesState> ? _value.lockVertical : lockVertical // ignore: cast_nullable_to_non_nullable as bool, - rulerEnabled: null == rulerEnabled - ? _value.rulerEnabled - : rulerEnabled // ignore: cast_nullable_to_non_nullable - as bool, - gridEnabled: null == gridEnabled - ? _value.gridEnabled - : gridEnabled // ignore: cast_nullable_to_non_nullable - as bool, fullSelection: null == fullSelection ? _value.fullSelection : fullSelection // ignore: cast_nullable_to_non_nullable as bool, - rulerPosition: null == rulerPosition - ? _value.rulerPosition - : rulerPosition // ignore: cast_nullable_to_non_nullable - as Point, - rulerAngle: null == rulerAngle - ? _value.rulerAngle - : rulerAngle // ignore: cast_nullable_to_non_nullable - as double, ) as $Val); } } @@ -138,11 +109,7 @@ abstract class _$$UtilitiesStateImplCopyWith<$Res> bool lockZoom, bool lockHorizontal, bool lockVertical, - bool rulerEnabled, - bool gridEnabled, - bool fullSelection, - @DoublePointJsonConverter() Point rulerPosition, - double rulerAngle}); + bool fullSelection}); } /// @nodoc @@ -162,11 +129,7 @@ class __$$UtilitiesStateImplCopyWithImpl<$Res> Object? lockZoom = null, Object? lockHorizontal = null, Object? lockVertical = null, - Object? rulerEnabled = null, - Object? gridEnabled = null, Object? fullSelection = null, - Object? rulerPosition = null, - Object? rulerAngle = null, }) { return _then(_$UtilitiesStateImpl( lockCollection: null == lockCollection @@ -185,26 +148,10 @@ class __$$UtilitiesStateImplCopyWithImpl<$Res> ? _value.lockVertical : lockVertical // ignore: cast_nullable_to_non_nullable as bool, - rulerEnabled: null == rulerEnabled - ? _value.rulerEnabled - : rulerEnabled // ignore: cast_nullable_to_non_nullable - as bool, - gridEnabled: null == gridEnabled - ? _value.gridEnabled - : gridEnabled // ignore: cast_nullable_to_non_nullable - as bool, fullSelection: null == fullSelection ? _value.fullSelection : fullSelection // ignore: cast_nullable_to_non_nullable as bool, - rulerPosition: null == rulerPosition - ? _value.rulerPosition - : rulerPosition // ignore: cast_nullable_to_non_nullable - as Point, - rulerAngle: null == rulerAngle - ? _value.rulerAngle - : rulerAngle // ignore: cast_nullable_to_non_nullable - as double, )); } } @@ -217,11 +164,7 @@ class _$UtilitiesStateImpl implements _UtilitiesState { this.lockZoom = false, this.lockHorizontal = false, this.lockVertical = false, - this.rulerEnabled = false, - this.gridEnabled = false, - this.fullSelection = false, - @DoublePointJsonConverter() this.rulerPosition = const Point(0.0, 0.0), - this.rulerAngle = 0}); + this.fullSelection = false}); factory _$UtilitiesStateImpl.fromJson(Map json) => _$$UtilitiesStateImplFromJson(json); @@ -240,24 +183,11 @@ class _$UtilitiesStateImpl implements _UtilitiesState { final bool lockVertical; @override @JsonKey() - final bool rulerEnabled; - @override - @JsonKey() - final bool gridEnabled; - @override - @JsonKey() final bool fullSelection; - @override - @JsonKey() - @DoublePointJsonConverter() - final Point rulerPosition; - @override - @JsonKey() - final double rulerAngle; @override String toString() { - return 'UtilitiesState(lockCollection: $lockCollection, lockZoom: $lockZoom, lockHorizontal: $lockHorizontal, lockVertical: $lockVertical, rulerEnabled: $rulerEnabled, gridEnabled: $gridEnabled, fullSelection: $fullSelection, rulerPosition: $rulerPosition, rulerAngle: $rulerAngle)'; + return 'UtilitiesState(lockCollection: $lockCollection, lockZoom: $lockZoom, lockHorizontal: $lockHorizontal, lockVertical: $lockVertical, fullSelection: $fullSelection)'; } @override @@ -273,31 +203,14 @@ class _$UtilitiesStateImpl implements _UtilitiesState { other.lockHorizontal == lockHorizontal) && (identical(other.lockVertical, lockVertical) || other.lockVertical == lockVertical) && - (identical(other.rulerEnabled, rulerEnabled) || - other.rulerEnabled == rulerEnabled) && - (identical(other.gridEnabled, gridEnabled) || - other.gridEnabled == gridEnabled) && (identical(other.fullSelection, fullSelection) || - other.fullSelection == fullSelection) && - (identical(other.rulerPosition, rulerPosition) || - other.rulerPosition == rulerPosition) && - (identical(other.rulerAngle, rulerAngle) || - other.rulerAngle == rulerAngle)); + other.fullSelection == fullSelection)); } @JsonKey(includeFromJson: false, includeToJson: false) @override - int get hashCode => Object.hash( - runtimeType, - lockCollection, - lockZoom, - lockHorizontal, - lockVertical, - rulerEnabled, - gridEnabled, - fullSelection, - rulerPosition, - rulerAngle); + int get hashCode => Object.hash(runtimeType, lockCollection, lockZoom, + lockHorizontal, lockVertical, fullSelection); /// Create a copy of UtilitiesState /// with the given fields replaced by the non-null parameter values. @@ -322,11 +235,7 @@ abstract class _UtilitiesState implements UtilitiesState { final bool lockZoom, final bool lockHorizontal, final bool lockVertical, - final bool rulerEnabled, - final bool gridEnabled, - final bool fullSelection, - @DoublePointJsonConverter() final Point rulerPosition, - final double rulerAngle}) = _$UtilitiesStateImpl; + final bool fullSelection}) = _$UtilitiesStateImpl; factory _UtilitiesState.fromJson(Map json) = _$UtilitiesStateImpl.fromJson; @@ -340,16 +249,7 @@ abstract class _UtilitiesState implements UtilitiesState { @override bool get lockVertical; @override - bool get rulerEnabled; - @override - bool get gridEnabled; - @override bool get fullSelection; - @override - @DoublePointJsonConverter() - Point get rulerPosition; - @override - double get rulerAngle; /// Create a copy of UtilitiesState /// with the given fields replaced by the non-null parameter values. diff --git a/api/lib/src/models/utilities.g.dart b/api/lib/src/models/utilities.g.dart index aab0ef0f57ff..f15c4bfd5692 100644 --- a/api/lib/src/models/utilities.g.dart +++ b/api/lib/src/models/utilities.g.dart @@ -12,14 +12,7 @@ _$UtilitiesStateImpl _$$UtilitiesStateImplFromJson(Map json) => lockZoom: json['lockZoom'] as bool? ?? false, lockHorizontal: json['lockHorizontal'] as bool? ?? false, lockVertical: json['lockVertical'] as bool? ?? false, - rulerEnabled: json['rulerEnabled'] as bool? ?? false, - gridEnabled: json['gridEnabled'] as bool? ?? false, fullSelection: json['fullSelection'] as bool? ?? false, - rulerPosition: json['rulerPosition'] == null - ? const Point(0.0, 0.0) - : const DoublePointJsonConverter() - .fromJson(json['rulerPosition'] as Map), - rulerAngle: (json['rulerAngle'] as num?)?.toDouble() ?? 0, ); Map _$$UtilitiesStateImplToJson( @@ -29,10 +22,5 @@ Map _$$UtilitiesStateImplToJson( 'lockZoom': instance.lockZoom, 'lockHorizontal': instance.lockHorizontal, 'lockVertical': instance.lockVertical, - 'rulerEnabled': instance.rulerEnabled, - 'gridEnabled': instance.gridEnabled, 'fullSelection': instance.fullSelection, - 'rulerPosition': - const DoublePointJsonConverter().toJson(instance.rulerPosition), - 'rulerAngle': instance.rulerAngle, }; diff --git a/api/lib/src/models/view.dart b/api/lib/src/models/view.dart index 856f0f311eb7..9fc6f1cb3edb 100644 --- a/api/lib/src/models/view.dart +++ b/api/lib/src/models/view.dart @@ -1,5 +1,3 @@ -import 'package:butterfly_api/src/converter/color.dart'; -import 'package:dart_leap/dart_leap.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'view.freezed.dart'; @@ -7,11 +5,7 @@ part 'view.g.dart'; @freezed sealed class ViewOption with _$ViewOption { - const factory ViewOption({ - @Default(SRGBColor.black) @ColorJsonConverter() SRGBColor gridColor, - @Default(20) double gridXSize, - @Default(20) double gridYSize, - }) = _ViewOption; + const factory ViewOption() = _ViewOption; factory ViewOption.fromJson(Map json) => _$ViewOptionFromJson(json); diff --git a/api/lib/src/models/view.freezed.dart b/api/lib/src/models/view.freezed.dart index 46e8ee9913b6..294cca6ceb2a 100644 --- a/api/lib/src/models/view.freezed.dart +++ b/api/lib/src/models/view.freezed.dart @@ -20,19 +20,8 @@ ViewOption _$ViewOptionFromJson(Map json) { /// @nodoc mixin _$ViewOption { - @ColorJsonConverter() - SRGBColor get gridColor => throw _privateConstructorUsedError; - double get gridXSize => throw _privateConstructorUsedError; - double get gridYSize => throw _privateConstructorUsedError; - /// Serializes this ViewOption to a JSON map. Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of ViewOption - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $ViewOptionCopyWith get copyWith => - throw _privateConstructorUsedError; } /// @nodoc @@ -40,11 +29,6 @@ abstract class $ViewOptionCopyWith<$Res> { factory $ViewOptionCopyWith( ViewOption value, $Res Function(ViewOption) then) = _$ViewOptionCopyWithImpl<$Res, ViewOption>; - @useResult - $Res call( - {@ColorJsonConverter() SRGBColor gridColor, - double gridXSize, - double gridYSize}); } /// @nodoc @@ -59,42 +43,13 @@ class _$ViewOptionCopyWithImpl<$Res, $Val extends ViewOption> /// Create a copy of ViewOption /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? gridColor = null, - Object? gridXSize = null, - Object? gridYSize = null, - }) { - return _then(_value.copyWith( - gridColor: null == gridColor - ? _value.gridColor - : gridColor // ignore: cast_nullable_to_non_nullable - as SRGBColor, - gridXSize: null == gridXSize - ? _value.gridXSize - : gridXSize // ignore: cast_nullable_to_non_nullable - as double, - gridYSize: null == gridYSize - ? _value.gridYSize - : gridYSize // ignore: cast_nullable_to_non_nullable - as double, - ) as $Val); - } } /// @nodoc -abstract class _$$ViewOptionImplCopyWith<$Res> - implements $ViewOptionCopyWith<$Res> { +abstract class _$$ViewOptionImplCopyWith<$Res> { factory _$$ViewOptionImplCopyWith( _$ViewOptionImpl value, $Res Function(_$ViewOptionImpl) then) = __$$ViewOptionImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {@ColorJsonConverter() SRGBColor gridColor, - double gridXSize, - double gridYSize}); } /// @nodoc @@ -107,81 +62,30 @@ class __$$ViewOptionImplCopyWithImpl<$Res> /// Create a copy of ViewOption /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? gridColor = null, - Object? gridXSize = null, - Object? gridYSize = null, - }) { - return _then(_$ViewOptionImpl( - gridColor: null == gridColor - ? _value.gridColor - : gridColor // ignore: cast_nullable_to_non_nullable - as SRGBColor, - gridXSize: null == gridXSize - ? _value.gridXSize - : gridXSize // ignore: cast_nullable_to_non_nullable - as double, - gridYSize: null == gridYSize - ? _value.gridYSize - : gridYSize // ignore: cast_nullable_to_non_nullable - as double, - )); - } } /// @nodoc @JsonSerializable() class _$ViewOptionImpl implements _ViewOption { - const _$ViewOptionImpl( - {@ColorJsonConverter() this.gridColor = SRGBColor.black, - this.gridXSize = 20, - this.gridYSize = 20}); + const _$ViewOptionImpl(); factory _$ViewOptionImpl.fromJson(Map json) => _$$ViewOptionImplFromJson(json); - @override - @JsonKey() - @ColorJsonConverter() - final SRGBColor gridColor; - @override - @JsonKey() - final double gridXSize; - @override - @JsonKey() - final double gridYSize; - @override String toString() { - return 'ViewOption(gridColor: $gridColor, gridXSize: $gridXSize, gridYSize: $gridYSize)'; + return 'ViewOption()'; } @override bool operator ==(Object other) { return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$ViewOptionImpl && - (identical(other.gridColor, gridColor) || - other.gridColor == gridColor) && - (identical(other.gridXSize, gridXSize) || - other.gridXSize == gridXSize) && - (identical(other.gridYSize, gridYSize) || - other.gridYSize == gridYSize)); + (other.runtimeType == runtimeType && other is _$ViewOptionImpl); } @JsonKey(includeFromJson: false, includeToJson: false) @override - int get hashCode => Object.hash(runtimeType, gridColor, gridXSize, gridYSize); - - /// Create a copy of ViewOption - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$ViewOptionImplCopyWith<_$ViewOptionImpl> get copyWith => - __$$ViewOptionImplCopyWithImpl<_$ViewOptionImpl>(this, _$identity); + int get hashCode => runtimeType.hashCode; @override Map toJson() { @@ -192,26 +96,8 @@ class _$ViewOptionImpl implements _ViewOption { } abstract class _ViewOption implements ViewOption { - const factory _ViewOption( - {@ColorJsonConverter() final SRGBColor gridColor, - final double gridXSize, - final double gridYSize}) = _$ViewOptionImpl; + const factory _ViewOption() = _$ViewOptionImpl; factory _ViewOption.fromJson(Map json) = _$ViewOptionImpl.fromJson; - - @override - @ColorJsonConverter() - SRGBColor get gridColor; - @override - double get gridXSize; - @override - double get gridYSize; - - /// Create a copy of ViewOption - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$ViewOptionImplCopyWith<_$ViewOptionImpl> get copyWith => - throw _privateConstructorUsedError; } diff --git a/api/lib/src/models/view.g.dart b/api/lib/src/models/view.g.dart index fd3cea5707eb..72eb0bd27444 100644 --- a/api/lib/src/models/view.g.dart +++ b/api/lib/src/models/view.g.dart @@ -6,18 +6,7 @@ part of 'view.dart'; // JsonSerializableGenerator // ************************************************************************** -_$ViewOptionImpl _$$ViewOptionImplFromJson(Map json) => _$ViewOptionImpl( - gridColor: json['gridColor'] == null - ? SRGBColor.black - : const ColorJsonConverter() - .fromJson((json['gridColor'] as num).toInt()), - gridXSize: (json['gridXSize'] as num?)?.toDouble() ?? 20, - gridYSize: (json['gridYSize'] as num?)?.toDouble() ?? 20, - ); +_$ViewOptionImpl _$$ViewOptionImplFromJson(Map json) => _$ViewOptionImpl(); Map _$$ViewOptionImplToJson(_$ViewOptionImpl instance) => - { - 'gridColor': const ColorJsonConverter().toJson(instance.gridColor), - 'gridXSize': instance.gridXSize, - 'gridYSize': instance.gridYSize, - }; + {}; diff --git a/app/lib/actions/select.dart b/app/lib/actions/select.dart index 4c3214607fa7..4165eb8ec8e8 100644 --- a/app/lib/actions/select.dart +++ b/app/lib/actions/select.dart @@ -25,7 +25,7 @@ class SelectAllAction extends Action { intent.context, SelectTool(), bloc: bloc, - temporaryClicked: true, + temporaryState: TemporaryState.removeAfterClick, ); if (handler is! SelectHandler) return; handler.selectAll(bloc); diff --git a/app/lib/cubits/current_index.dart b/app/lib/cubits/current_index.dart index 45b696be5d44..bd0e9e794261 100644 --- a/app/lib/cubits/current_index.dart +++ b/app/lib/cubits/current_index.dart @@ -37,6 +37,8 @@ enum HideState { visible, keyboard, touch } enum RendererState { visible, temporary, hidden } +enum TemporaryState { allowClick, removeAfterClick, removeAfterRelease } + @Freezed(equal: false) class CurrentIndex with _$CurrentIndex { const CurrentIndex._(); @@ -57,7 +59,7 @@ class CurrentIndex with _$CurrentIndex { @Default({}) Map> toggleableForegrounds, @Default(MouseCursor.defer) MouseCursor cursor, MouseCursor? temporaryCursor, - @Default(false) bool temporaryClicked, + @Default(TemporaryState.allowClick) TemporaryState temporaryState, Offset? lastPosition, @Default([]) List pointers, int? buttons, @@ -501,7 +503,8 @@ class CurrentIndexCubit extends Cubit { } Future changeTemporaryHandlerIndex(BuildContext context, int index, - {DocumentBloc? bloc, bool temporaryClicked = true}) async { + {DocumentBloc? bloc, + TemporaryState temporaryState = TemporaryState.allowClick}) async { bloc ??= context.read(); final blocState = bloc.state; if (blocState is! DocumentLoadSuccess) return null; @@ -513,12 +516,14 @@ class CurrentIndexCubit extends Cubit { context, tool, bloc: bloc, - temporaryClicked: temporaryClicked, + temporaryState: temporaryState, ); } - Future changeTemporaryHandler(BuildContext context, Tool tool, - {DocumentBloc? bloc, bool temporaryClicked = true}) async { + Future?> changeTemporaryHandler( + BuildContext context, T tool, + {DocumentBloc? bloc, + TemporaryState temporaryState = TemporaryState.allowClick}) async { bloc ??= context.read(); final handler = Handler.fromTool(tool); final blocState = bloc.state; @@ -541,7 +546,7 @@ class CurrentIndexCubit extends Cubit { temporaryToolbar: handler.getToolbar(bloc), temporaryCursor: handler.cursor, temporaryRendererStates: handler.rendererStates, - temporaryClicked: temporaryClicked, + temporaryState: temporaryState, )); } return handler; @@ -552,14 +557,22 @@ class CurrentIndexCubit extends Cubit { if (networking) ...state.networkingForegrounds, ]; + void resetReleaseHandler(DocumentBloc bloc) { + if (state.temporaryState == TemporaryState.removeAfterRelease) { + resetTemporaryHandler(bloc, true); + } + } + void resetTemporaryHandler(DocumentBloc bloc, [bool force = false]) { if (state.temporaryHandler == null) { return; } - if (!force && state.temporaryClicked) { - emit(state.copyWith( - temporaryClicked: false, - )); + if (!force && state.temporaryState != TemporaryState.removeAfterClick) { + if (state.temporaryState == TemporaryState.allowClick) { + emit(state.copyWith( + temporaryState: TemporaryState.removeAfterClick, + )); + } return; } state.temporaryHandler?.dispose(bloc); @@ -570,7 +583,6 @@ class CurrentIndexCubit extends Cubit { temporaryToolbar: null, temporaryCursor: null, temporaryRendererStates: null, - temporaryClicked: false, )); } @@ -985,7 +997,6 @@ class CurrentIndexCubit extends Cubit { emit(state.copyWith( temporaryHandler: HandHandler(), temporaryCursor: null, - temporaryClicked: false, )); } diff --git a/app/lib/cubits/current_index.freezed.dart b/app/lib/cubits/current_index.freezed.dart index 854370bbce85..568a03205c24 100644 --- a/app/lib/cubits/current_index.freezed.dart +++ b/app/lib/cubits/current_index.freezed.dart @@ -36,7 +36,7 @@ mixin _$CurrentIndex { throw _privateConstructorUsedError; MouseCursor get cursor => throw _privateConstructorUsedError; MouseCursor? get temporaryCursor => throw _privateConstructorUsedError; - bool get temporaryClicked => throw _privateConstructorUsedError; + TemporaryState get temporaryState => throw _privateConstructorUsedError; ui.Offset? get lastPosition => throw _privateConstructorUsedError; List get pointers => throw _privateConstructorUsedError; int? get buttons => throw _privateConstructorUsedError; @@ -86,7 +86,7 @@ abstract class $CurrentIndexCopyWith<$Res> { Map> toggleableForegrounds, MouseCursor cursor, MouseCursor? temporaryCursor, - bool temporaryClicked, + TemporaryState temporaryState, ui.Offset? lastPosition, List pointers, int? buttons, @@ -137,7 +137,7 @@ class _$CurrentIndexCopyWithImpl<$Res, $Val extends CurrentIndex> Object? toggleableForegrounds = null, Object? cursor = null, Object? temporaryCursor = freezed, - Object? temporaryClicked = null, + Object? temporaryState = null, Object? lastPosition = freezed, Object? pointers = null, Object? buttons = freezed, @@ -219,10 +219,10 @@ class _$CurrentIndexCopyWithImpl<$Res, $Val extends CurrentIndex> ? _value.temporaryCursor : temporaryCursor // ignore: cast_nullable_to_non_nullable as MouseCursor?, - temporaryClicked: null == temporaryClicked - ? _value.temporaryClicked - : temporaryClicked // ignore: cast_nullable_to_non_nullable - as bool, + temporaryState: null == temporaryState + ? _value.temporaryState + : temporaryState // ignore: cast_nullable_to_non_nullable + as TemporaryState, lastPosition: freezed == lastPosition ? _value.lastPosition : lastPosition // ignore: cast_nullable_to_non_nullable @@ -322,7 +322,7 @@ abstract class _$$CurrentIndexImplCopyWith<$Res> Map> toggleableForegrounds, MouseCursor cursor, MouseCursor? temporaryCursor, - bool temporaryClicked, + TemporaryState temporaryState, ui.Offset? lastPosition, List pointers, int? buttons, @@ -372,7 +372,7 @@ class __$$CurrentIndexImplCopyWithImpl<$Res> Object? toggleableForegrounds = null, Object? cursor = null, Object? temporaryCursor = freezed, - Object? temporaryClicked = null, + Object? temporaryState = null, Object? lastPosition = freezed, Object? pointers = null, Object? buttons = freezed, @@ -454,10 +454,10 @@ class __$$CurrentIndexImplCopyWithImpl<$Res> ? _value.temporaryCursor : temporaryCursor // ignore: cast_nullable_to_non_nullable as MouseCursor?, - temporaryClicked: null == temporaryClicked - ? _value.temporaryClicked - : temporaryClicked // ignore: cast_nullable_to_non_nullable - as bool, + temporaryState: null == temporaryState + ? _value.temporaryState + : temporaryState // ignore: cast_nullable_to_non_nullable + as TemporaryState, lastPosition: freezed == lastPosition ? _value.lastPosition : lastPosition // ignore: cast_nullable_to_non_nullable @@ -537,7 +537,7 @@ class _$CurrentIndexImpl extends _CurrentIndex { final Map> toggleableForegrounds = const {}, this.cursor = MouseCursor.defer, this.temporaryCursor, - this.temporaryClicked = false, + this.temporaryState = TemporaryState.allowClick, this.lastPosition, final List pointers = const [], this.buttons, @@ -639,7 +639,7 @@ class _$CurrentIndexImpl extends _CurrentIndex { final MouseCursor? temporaryCursor; @override @JsonKey() - final bool temporaryClicked; + final TemporaryState temporaryState; @override final ui.Offset? lastPosition; final List _pointers; @@ -704,7 +704,7 @@ class _$CurrentIndexImpl extends _CurrentIndex { @override String toString() { - return 'CurrentIndex(index: $index, handler: $handler, cameraViewport: $cameraViewport, settingsCubit: $settingsCubit, transformCubit: $transformCubit, networkingService: $networkingService, temporaryHandler: $temporaryHandler, foregrounds: $foregrounds, selection: $selection, pinned: $pinned, temporaryForegrounds: $temporaryForegrounds, toggleableHandlers: $toggleableHandlers, networkingForegrounds: $networkingForegrounds, toggleableForegrounds: $toggleableForegrounds, cursor: $cursor, temporaryCursor: $temporaryCursor, temporaryClicked: $temporaryClicked, lastPosition: $lastPosition, pointers: $pointers, buttons: $buttons, location: $location, embedding: $embedding, saved: $saved, toolbar: $toolbar, temporaryToolbar: $temporaryToolbar, rendererStates: $rendererStates, temporaryRendererStates: $temporaryRendererStates, viewOption: $viewOption, hideUi: $hideUi, areaNavigatorCreate: $areaNavigatorCreate, areaNavigatorExact: $areaNavigatorExact, areaNavigatorAsk: $areaNavigatorAsk)'; + return 'CurrentIndex(index: $index, handler: $handler, cameraViewport: $cameraViewport, settingsCubit: $settingsCubit, transformCubit: $transformCubit, networkingService: $networkingService, temporaryHandler: $temporaryHandler, foregrounds: $foregrounds, selection: $selection, pinned: $pinned, temporaryForegrounds: $temporaryForegrounds, toggleableHandlers: $toggleableHandlers, networkingForegrounds: $networkingForegrounds, toggleableForegrounds: $toggleableForegrounds, cursor: $cursor, temporaryCursor: $temporaryCursor, temporaryState: $temporaryState, lastPosition: $lastPosition, pointers: $pointers, buttons: $buttons, location: $location, embedding: $embedding, saved: $saved, toolbar: $toolbar, temporaryToolbar: $temporaryToolbar, rendererStates: $rendererStates, temporaryRendererStates: $temporaryRendererStates, viewOption: $viewOption, hideUi: $hideUi, areaNavigatorCreate: $areaNavigatorCreate, areaNavigatorExact: $areaNavigatorExact, areaNavigatorAsk: $areaNavigatorAsk)'; } /// Create a copy of CurrentIndex @@ -734,7 +734,7 @@ abstract class _CurrentIndex extends CurrentIndex { final Map> toggleableForegrounds, final MouseCursor cursor, final MouseCursor? temporaryCursor, - final bool temporaryClicked, + final TemporaryState temporaryState, final ui.Offset? lastPosition, final List pointers, final int? buttons, @@ -785,7 +785,7 @@ abstract class _CurrentIndex extends CurrentIndex { @override MouseCursor? get temporaryCursor; @override - bool get temporaryClicked; + TemporaryState get temporaryState; @override ui.Offset? get lastPosition; @override diff --git a/app/lib/dialogs/collections.dart b/app/lib/dialogs/collections.dart index 9b194914fa39..c43fe54a7afe 100644 --- a/app/lib/dialogs/collections.dart +++ b/app/lib/dialogs/collections.dart @@ -1,4 +1,5 @@ import 'package:butterfly/bloc/document_bloc.dart'; +import 'package:butterfly/cubits/current_index.dart'; import 'package:butterfly/dialogs/delete.dart'; import 'package:butterfly/handlers/handler.dart'; import 'package:butterfly_api/butterfly_api.dart'; @@ -97,7 +98,7 @@ class _CollectionsDialogState extends State { context, SelectTool(), bloc: bloc, - temporaryClicked: true, + temporaryState: TemporaryState.removeAfterClick, ); if (handler is! SelectHandler) return; handler.selectAll( diff --git a/app/lib/dialogs/import/add.dart b/app/lib/dialogs/import/add.dart index fe09191b9707..c6ab323a375b 100644 --- a/app/lib/dialogs/import/add.dart +++ b/app/lib/dialogs/import/add.dart @@ -4,7 +4,6 @@ import 'package:butterfly/helpers/color.dart'; import 'package:butterfly/services/import.dart'; import 'package:butterfly/visualizer/element.dart'; import 'package:butterfly/visualizer/tool.dart'; -import 'package:butterfly/visualizer/property.dart'; import 'package:butterfly_api/butterfly_api.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -75,7 +74,10 @@ class _AddDialogState extends State { ), trailing: tool.isAction() ? IconButton( - onPressed: () => handler.onSelected(context), + onPressed: () { + Navigator.of(context).pop(); + handler.onSelected(context, false); + }, icon: const PhosphorIcon(PhosphorIconsLight.playCircle), tooltip: AppLocalizations.of(context).play, ) @@ -157,7 +159,6 @@ class _AddDialogState extends State { () => Tool.select(mode: SelectMode.lasso), () => Tool.select(mode: SelectMode.rectangle), Tool.pen, - Tool.stamp, Tool.laser, Tool.pathEraser, Tool.label, @@ -166,7 +167,6 @@ class _AddDialogState extends State { Tool.presentation, () => Tool.spacer(axis: Axis2D.vertical), () => Tool.spacer(axis: Axis2D.horizontal), - Tool.eyeDropper, ] .map((e) => e()) .where((e) => e @@ -174,13 +174,16 @@ class _AddDialogState extends State { .toLowerCase() .contains(search.toLowerCase())) .toList(); - final shapes = [ - PathShape.circle, - PathShape.rectangle, - PathShape.line, - PathShape.triangle, + final shapes = [ + Tool.stamp(), + ...[ + PathShape.circle, + PathShape.rectangle, + PathShape.line, + PathShape.triangle + ].map((e) => + Tool.shape(property: ShapeProperty(shape: e()))), ] - .map((e) => e()) .where((e) => e .getLocalizedName(context) .toLowerCase() @@ -198,6 +201,7 @@ class _AddDialogState extends State { Tool.redo, Tool.fullScreen, Tool.collection, + Tool.eyeDropper, ] .map((e) => e()) .where((e) => e @@ -278,8 +282,7 @@ class _AddDialogState extends State { ), icon: Icon( e.icon(PhosphorIconsStyle.light)), - onTap: () => addTool(ShapeTool( - property: ShapeProperty(shape: e))), + onTap: () => addTool(e), )), ...textures.map((e) => BoxTile( title: Text( diff --git a/app/lib/handlers/collection.dart b/app/lib/handlers/collection.dart index f9f5d54c283b..43596e13d9fa 100644 --- a/app/lib/handlers/collection.dart +++ b/app/lib/handlers/collection.dart @@ -4,7 +4,7 @@ class CollectionHandler extends Handler { CollectionHandler(super.data); @override - bool onSelected(BuildContext context) { + bool onSelected(BuildContext context, [bool wasAdded = true]) { final bloc = context.read(); showDialog( context: context, diff --git a/app/lib/handlers/export.dart b/app/lib/handlers/export.dart index ee4bb408805d..d14cf11524ee 100644 --- a/app/lib/handlers/export.dart +++ b/app/lib/handlers/export.dart @@ -4,7 +4,7 @@ class ExportHandler extends Handler { ExportHandler(super.data); @override - bool onSelected(BuildContext context) { + bool onSelected(BuildContext context, [bool wasAdded = true]) { final bloc = context.read(); showDialog( context: context, diff --git a/app/lib/handlers/eye_dropper.dart b/app/lib/handlers/eye_dropper.dart index a204cab059c5..08b55243e22f 100644 --- a/app/lib/handlers/eye_dropper.dart +++ b/app/lib/handlers/eye_dropper.dart @@ -3,6 +3,15 @@ part of 'handler.dart'; class EyeDropperHandler extends Handler { EyeDropperHandler(super.data); + @override + bool onSelected(BuildContext context, [bool wasAdded = true]) { + if (!wasAdded) { + context.read().changeTemporaryHandler(context, data, + temporaryState: TemporaryState.removeAfterRelease); + } + return super.onSelected(context); + } + @override void onPointerUp(PointerUpEvent event, EventContext context) async { final globalPos = diff --git a/app/lib/handlers/full_screen.dart b/app/lib/handlers/full_screen.dart index 832c41fb4522..a87ecbd06b2b 100644 --- a/app/lib/handlers/full_screen.dart +++ b/app/lib/handlers/full_screen.dart @@ -4,7 +4,7 @@ class FullScreenHandler extends Handler { FullScreenHandler(super.data); @override - bool onSelected(BuildContext context) { + bool onSelected(BuildContext context, [bool wasAdded = true]) { context.read().toggleFullScreen(); return false; } diff --git a/app/lib/handlers/handler.dart b/app/lib/handlers/handler.dart index 35351a553e8e..7ab3294275f8 100644 --- a/app/lib/handlers/handler.dart +++ b/app/lib/handlers/handler.dart @@ -158,7 +158,7 @@ abstract class Handler { const Handler(this.data); - bool onSelected(BuildContext context) => true; + bool onSelected(BuildContext context, [bool wasAdded = true]) => true; List createForegrounds(CurrentIndexCubit currentIndexCubit, NoteData document, DocumentPage page, DocumentInfo info, @@ -177,7 +177,7 @@ abstract class Handler { void onPointerMove(PointerMoveEvent event, EventContext context) {} - void onPointerUp(PointerUpEvent event, EventContext context) {} + FutureOr onPointerUp(PointerUpEvent event, EventContext context) {} void onPointerHover(PointerHoverEvent event, EventContext context) {} @@ -292,7 +292,7 @@ mixin ColoredHandler on Handler { context, EyeDropperTool(), bloc: bloc, - temporaryClicked: true, + temporaryState: TemporaryState.removeAfterRelease, ); }, ) diff --git a/app/lib/handlers/import.dart b/app/lib/handlers/import.dart index 8c527e8acb95..d36a36aa1d03 100644 --- a/app/lib/handlers/import.dart +++ b/app/lib/handlers/import.dart @@ -61,9 +61,6 @@ class ImportHandler extends Handler { .copyWith(id: createUniqueId())) .nonNulls .toList())); - context - .getCurrentIndexCubit() - .resetTemporaryHandler(context.getDocumentBloc()); context.refresh(); context.bake(); } diff --git a/app/lib/handlers/redo.dart b/app/lib/handlers/redo.dart index 527b7098a638..7c6eb72bb6ba 100644 --- a/app/lib/handlers/redo.dart +++ b/app/lib/handlers/redo.dart @@ -4,7 +4,7 @@ class RedoHandler extends Handler { RedoHandler(super.data); @override - bool onSelected(BuildContext context) { + bool onSelected(BuildContext context, [bool wasAdded = true]) { final bloc = context.read(); bloc.redo(); bloc.load().then((value) => bloc.bake().then((value) => bloc.save())); diff --git a/app/lib/handlers/select.dart b/app/lib/handlers/select.dart index 9ba3230bbd6b..750439adedcc 100644 --- a/app/lib/handlers/select.dart +++ b/app/lib/handlers/select.dart @@ -323,7 +323,7 @@ class SelectHandler extends Handler { event.kind == PointerDeviceKind.mouse && event.buttons != kSecondaryMouseButton; - void _handleRuler(ScaleUpdateDetails details, EventContext context) { + /*void _handleRuler(ScaleUpdateDetails details, EventContext context) { final state = context.getState(); if (state == null) return; final viewport = context.getCameraViewport(); @@ -347,7 +347,7 @@ class SelectHandler extends Handler { _rulerRotation = currentRotation; _rulerPosition = currentPos; context.getCurrentIndexCubit().updateUtilities(utilities: utilitiesState); - } + }*/ @override void onScaleUpdate(ScaleUpdateDetails details, EventContext context) { @@ -355,7 +355,7 @@ class SelectHandler extends Handler { final globalPos = context.getCameraTransform().localToGlobal(details.localFocalPoint); if (_rulerRotation != null && _rulerPosition != null) { - _handleRuler(details, context); + //_handleRuler(details, context); return; } if (_selectionManager.isTransforming) { diff --git a/app/lib/handlers/undo.dart b/app/lib/handlers/undo.dart index 6b04dc61aae2..018b52869cf6 100644 --- a/app/lib/handlers/undo.dart +++ b/app/lib/handlers/undo.dart @@ -4,7 +4,7 @@ class UndoHandler extends Handler { UndoHandler(super.data); @override - bool onSelected(BuildContext context) { + bool onSelected(BuildContext context, [bool wasAdded = true]) { final bloc = context.read(); bloc.undo(); bloc.load().then((value) => bloc.bake().then((value) => bloc.save())); diff --git a/app/lib/l10n/app_en.arb b/app/lib/l10n/app_en.arb index 7b7354fc39e6..0d1390c723c3 100644 --- a/app/lib/l10n/app_en.arb +++ b/app/lib/l10n/app_en.arb @@ -28,7 +28,6 @@ "yes": "Yes", "undo": "Undo", "redo": "Redo", - "project": "Project", "general": "General", "copyTitle": "Copied to clipboard", "loading": "Loading...", diff --git a/app/lib/renderers/utilities.dart b/app/lib/renderers/utilities.dart index ceafaa3db622..49aad6c18a08 100644 --- a/app/lib/renderers/utilities.dart +++ b/app/lib/renderers/utilities.dart @@ -18,7 +18,8 @@ class UtilitiesRenderer extends Renderer { } bool hitRuler(Offset position, Size size) { - if (!element.rulerEnabled) return false; + return false; + /*if (!element.rulerEnabled) return false; final rulerRect = getRulerRect(size).translate( size.width / 2 + element.rulerPosition.x, size.height / 2 + element.rulerPosition.y); @@ -26,14 +27,14 @@ class UtilitiesRenderer extends Renderer { element.rulerPosition .toOffset() .translate(size.width / 2, size.height / 2), - -element.rulerAngle * pi / 180)); + -element.rulerAngle * pi / 180));*/ } @override void build(Canvas canvas, Size size, NoteData document, DocumentPage page, DocumentInfo info, CameraTransform transform, [ColorScheme? colorScheme, bool foreground = false]) { - if (element.gridEnabled) { + /*if (element.gridEnabled) { if (option.gridXSize > 0) { double x = 0; while (x < size.width) { @@ -145,12 +146,12 @@ class UtilitiesRenderer extends Renderer { x += steps; } canvas.restore(); - } + }*/ } Offset getPointerPosition(Offset position, CurrentIndexCubit cubit) { - if (!element.rulerEnabled) return position; - final size = cubit.state.cameraViewport.toSize(); + /* if (!element.rulerEnabled) */ return position; + /* final size = cubit.state.cameraViewport.toSize(); final rulerRect = getRulerRect(size).translate( size.width / 2 + element.rulerPosition.x, size.height / 2 + element.rulerPosition.y); @@ -173,17 +174,17 @@ class UtilitiesRenderer extends Renderer { } else if (secondHalfHit) { return Offset(rotatedPosition.dx, rulerRect.bottom).rotate(pivot, angle); } - return position; + return position; */ } Offset getGridPosition(Offset position, DocumentPage page, DocumentInfo info, CurrentIndexCubit cubit) { - if (!element.gridEnabled) return position; - final transform = cubit.state.transformCubit.state; + /* if (!element.gridEnabled) */ return position; + /* final transform = cubit.state.transformCubit.state; final localPosition = transform.globalToLocal(position); final option = info.view; final x = (localPosition.dx ~/ option.gridXSize) * option.gridXSize; final y = (localPosition.dy ~/ option.gridYSize) * option.gridYSize; - return transform.localToGlobal(Offset(x, y)); + return transform.localToGlobal(Offset(x, y)); */ } } diff --git a/app/lib/selections/selection.dart b/app/lib/selections/selection.dart index 221fdb56be34..72b4a480518d 100644 --- a/app/lib/selections/selection.dart +++ b/app/lib/selections/selection.dart @@ -2,7 +2,6 @@ import 'package:butterfly/bloc/document_bloc.dart'; import 'package:butterfly/cubits/current_index.dart'; import 'package:butterfly/dialogs/constraints.dart'; import 'package:butterfly/dialogs/texture.dart'; -import 'package:butterfly/helpers/point.dart'; import 'package:butterfly/visualizer/tool.dart'; import 'package:butterfly/visualizer/preset.dart'; import 'package:butterfly/visualizer/property.dart'; diff --git a/app/lib/selections/utilities.dart b/app/lib/selections/utilities.dart index d1174dd4cebd..93f6ddad8175 100644 --- a/app/lib/selections/utilities.dart +++ b/app/lib/selections/utilities.dart @@ -50,11 +50,10 @@ class _UtilitiesViewState extends State<_UtilitiesView> late final TabController _tabController; final TextEditingController _nameController = TextEditingController(), _descriptionController = TextEditingController(); - int _expandedIndex = -1; @override void initState() { - _tabController = TabController(length: 4, vsync: this); + _tabController = TabController(length: 3, vsync: this); _tabController.addListener(_onTabChange); @@ -97,9 +96,8 @@ class _UtilitiesViewState extends State<_UtilitiesView> controller: _tabController, isScrollable: true, tabs: >[ - [PhosphorIconsLight.file, AppLocalizations.of(context).project], + [PhosphorIconsLight.file, AppLocalizations.of(context).file], [PhosphorIconsLight.book, AppLocalizations.of(context).page], - [PhosphorIconsLight.eye, AppLocalizations.of(context).view], [PhosphorIconsLight.camera, AppLocalizations.of(context).camera], ] .map((e) => @@ -348,99 +346,6 @@ class _UtilitiesViewState extends State<_UtilitiesView> child: Text(AppLocalizations.of(context).background), ), ]), - Column(children: [ - ExpansionPanelList( - expansionCallback: (index, isExpanded) { - setState(() { - _expandedIndex = isExpanded ? index : -1; - }); - }, - children: [ - ExpansionPanel( - canTapOnHeader: true, - isExpanded: _expandedIndex == 0, - headerBuilder: (context, isExpanded) => ListTile( - leading: Checkbox( - value: widget.state.gridEnabled, - onChanged: (value) => widget.onStateChanged( - widget.state - .copyWith(gridEnabled: value ?? false), - ), - ), - title: Text(AppLocalizations.of(context).grid), - ), - body: Column(children: [ - OffsetPropertyView( - value: Offset(widget.option.gridXSize, - widget.option.gridYSize), - title: Text(AppLocalizations.of(context).size), - onChanged: (value) => widget.onToolChanged( - widget.option.copyWith( - gridXSize: value.dx, gridYSize: value.dy), - ), - ), - const SizedBox(height: 8), - ColorField( - title: Text(LeapLocalizations.of(context).color), - value: widget.option.gridColor.withValues(a: 255), - onChanged: (value) => widget.onToolChanged( - widget.option.copyWith( - gridColor: value.withValues( - a: widget.option.gridColor.a)), - ), - ), - const SizedBox(height: 8), - ExactSlider( - header: Text(AppLocalizations.of(context).alpha), - value: widget.option.gridColor.a.toDouble(), - defaultValue: 255, - min: 0, - max: 255, - fractionDigits: 0, - onChangeEnd: (value) => widget.onToolChanged( - widget.option.copyWith( - gridColor: widget.option.gridColor - .withValues(a: value.toInt()), - ), - ), - ), - ]), - ), - ExpansionPanel( - canTapOnHeader: true, - isExpanded: _expandedIndex == 1, - headerBuilder: (context, isExpanded) => ListTile( - leading: Checkbox( - value: widget.state.rulerEnabled, - onChanged: (value) => widget.onStateChanged(widget - .state - .copyWith(rulerEnabled: value ?? false)), - ), - title: Text(AppLocalizations.of(context).ruler), - ), - body: Column(children: [ - OffsetPropertyView( - title: - Text(AppLocalizations.of(context).position), - onChanged: (value) => widget.onStateChanged(widget - .state - .copyWith(rulerPosition: value.toPoint())), - value: widget.state.rulerPosition.toOffset(), - ), - const SizedBox(height: 8), - ExactSlider( - header: Text(AppLocalizations.of(context).angle), - value: widget.state.rulerAngle, - defaultValue: 0, - min: 0, - max: 360, - onChangeEnd: (value) => widget.onStateChanged( - widget.state.copyWith(rulerAngle: value)), - ), - ]), - ), - ]), - ]), Column( children: [ OffsetPropertyView( diff --git a/app/lib/services/import.dart b/app/lib/services/import.dart index 2eb954b26c8e..b8c1d404438a 100644 --- a/app/lib/services/import.dart +++ b/app/lib/services/import.dart @@ -743,7 +743,7 @@ class ImportService { context, ImportTool(elements: elements, areas: areas), bloc: bloc!, - temporaryClicked: true, + temporaryState: TemporaryState.removeAfterRelease, ); } else { bloc diff --git a/app/lib/views/edit.dart b/app/lib/views/edit.dart index ea95380670d2..00ef540de263 100644 --- a/app/lib/views/edit.dart +++ b/app/lib/views/edit.dart @@ -266,8 +266,7 @@ class _EditToolbarState extends State { final selected = i == currentIndex.index; final tool = handler.data; final highlighted = currentIndex.selection?.selected.any( - (element) => - element.hashCode == handler.hashCode) ?? + (element) => element.hashCode == tool.hashCode) ?? false; String tooltip = tool.name.trim(); if (tooltip.isEmpty) { diff --git a/app/lib/views/view.dart b/app/lib/views/view.dart index c521959d9f08..0f0af8af9c87 100644 --- a/app/lib/views/view.dart +++ b/app/lib/views/view.dart @@ -119,12 +119,13 @@ class _MainViewViewportState extends State }); } + final bloc = context.read(); + Future changeTemporaryTool( PointerDeviceKind kind, int buttons) async { int? nextPointerIndex; final config = context.read().state.inputConfiguration; final cubit = context.read(); - final bloc = context.read(); // Mapped to the priority of the buttons switch (kind) { case PointerDeviceKind.touch: @@ -157,7 +158,6 @@ class _MainViewViewportState extends State await cubit.changeTemporaryHandlerIndex( context, nextPointerIndex, - temporaryClicked: false, ); } } @@ -341,11 +341,12 @@ class _MainViewViewportState extends State onPointerUp: (PointerUpEvent event) async { cubit.updateLastPosition(event.localPosition); if (_isScalingDisabled ?? true) { - getHandler() + await getHandler() .onPointerUp(event, getEventContext()); } cubit.removePointer(event.pointer); cubit.removeButtons(); + cubit.resetReleaseHandler(bloc); }, behavior: HitTestBehavior.translucent, onPointerHover: (event) { diff --git a/app/lib/visualizer/tool.dart b/app/lib/visualizer/tool.dart index 2b95d0604e72..ea4fb1ec7cfe 100644 --- a/app/lib/visualizer/tool.dart +++ b/app/lib/visualizer/tool.dart @@ -14,6 +14,16 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:material_leap/material_leap.dart'; import 'package:phosphor_flutter/phosphor_flutter.dart'; +extension ToolCategoryVisualizer on ToolCategory { + IconGetter get icon => switch (this) { + ToolCategory.normal => PhosphorIcons.paintBrush, + ToolCategory.import => PhosphorIcons.arrowSquareIn, + ToolCategory.surface => PhosphorIcons.monitor, + ToolCategory.action => PhosphorIcons.arrowClockwise, + ToolCategory.view => PhosphorIcons.eye, + }; +} + extension ToolVisualizer on Tool { String getDisplay(BuildContext context) { if (name.trim().isEmpty) return getLocalizedName(context); @@ -44,6 +54,8 @@ extension ToolVisualizer on Tool { AssetTool e => e.importType.getLocalizedName(context), EyeDropperTool() => loc.eyeDropper, ExportTool() => loc.export, + GridTool() => loc.grid, + RulerTool() => loc.ruler, }; } @@ -89,6 +101,8 @@ extension ToolVisualizer on Tool { AssetTool tool => tool.importType.icon, EyeDropperTool() => PhosphorIcons.eyedropper, ExportTool() => PhosphorIcons.export, + GridTool() => PhosphorIcons.gridFour, + RulerTool() => PhosphorIcons.ruler, }; List get help { @@ -114,6 +128,8 @@ extension ToolVisualizer on Tool { AssetTool() => null, ExportTool() => null, EyeDropperTool() => 'eye_dropper', + GridTool() => 'grid', + RulerTool() => 'ruler', }; if (page == null) return []; return ['tools', page]; @@ -127,6 +143,7 @@ extension ToolVisualizer on Tool { FullScreenTool() => true, ExportTool() => true, CollectionTool() => true, + EyeDropperTool() => true, _ => false, }; } diff --git a/app/lib/widgets/option_button.dart b/app/lib/widgets/option_button.dart index 3218be5fa6fb..7719801568f9 100644 --- a/app/lib/widgets/option_button.dart +++ b/app/lib/widgets/option_button.dart @@ -3,7 +3,7 @@ import 'package:phosphor_flutter/phosphor_flutter.dart'; class OptionButton extends StatefulWidget { final Widget icon; - final Widget? selectedIcon, bottomIcon; + final Widget? selectedIcon, bottomIcon, leadingIcon; final VoidCallback? onPressed, onSecondaryPressed, onLongPressed; final bool selected, highlighted, focussed, alwaysShowBottom; final String tooltip; @@ -14,6 +14,7 @@ class OptionButton extends StatefulWidget { required this.icon, this.selectedIcon, this.bottomIcon, + this.leadingIcon, this.onPressed, this.onSecondaryPressed, this.onLongPressed, @@ -139,9 +140,18 @@ class _OptionButtonState extends State child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - widget.selected - ? (widget.selectedIcon ?? widget.icon) - : widget.icon, + Stack( + children: [ + if (widget.leadingIcon != null) + Align( + alignment: Alignment.topLeft, + child: widget.leadingIcon!, + ), + widget.selected + ? (widget.selectedIcon ?? widget.icon) + : widget.icon + ], + ), SizeTransition( axisAlignment: -1, axis: Axis.vertical, From 3a491144e868eddae7f6197f0d13c73ed0c6a874 Mon Sep 17 00:00:00 2001 From: CodeDoctorDE Date: Sun, 29 Dec 2024 19:33:29 +0100 Subject: [PATCH 2/8] Add grid and ruler selections --- api/lib/src/models/tool.dart | 2 +- api/lib/src/models/tool.freezed.dart | 23 ++++++++-------- api/lib/src/models/tool.g.dart | 21 ++++++++++---- app/lib/cubits/current_index.dart | 5 ++-- app/lib/dialogs/import/add.dart | 18 ++++++++++++ app/lib/handlers/hand.dart | 4 +++ app/lib/handlers/handler.dart | 4 +-- app/lib/handlers/import.dart | 4 +-- app/lib/l10n/app_en.arb | 5 ++-- app/lib/selections/selection.dart | 2 ++ app/lib/selections/tools/grid.dart | 40 +++++++++++++++++++++++++++ app/lib/selections/tools/ruler.dart | 41 ++++++++++++++++++++++++++++ app/lib/selections/tools/tool.dart | 2 ++ app/lib/services/import.dart | 3 +- app/lib/visualizer/tool.dart | 9 +++++- 15 files changed, 155 insertions(+), 28 deletions(-) create mode 100644 app/lib/selections/tools/grid.dart create mode 100644 app/lib/selections/tools/ruler.dart diff --git a/api/lib/src/models/tool.dart b/api/lib/src/models/tool.dart index b5906b923b09..437ecc2d66eb 100644 --- a/api/lib/src/models/tool.dart +++ b/api/lib/src/models/tool.dart @@ -195,7 +195,7 @@ sealed class Tool with _$Tool { factory Tool.ruler({ @Default('') String name, @Default('') String displayIcon, - @Default(SRGBColor.black) @ColorJsonConverter() SRGBColor gridColor, + @ColorJsonConverter() SRGBColor? color, }) = RulerTool; factory Tool.grid({ diff --git a/api/lib/src/models/tool.freezed.dart b/api/lib/src/models/tool.freezed.dart index de96650c80d6..b7f09f735a68 100644 --- a/api/lib/src/models/tool.freezed.dart +++ b/api/lib/src/models/tool.freezed.dart @@ -2799,7 +2799,7 @@ abstract class _$$RulerToolImplCopyWith<$Res> implements $ToolCopyWith<$Res> { $Res call( {String name, String displayIcon, - @ColorJsonConverter() SRGBColor gridColor}); + @ColorJsonConverter() SRGBColor? color}); } /// @nodoc @@ -2817,7 +2817,7 @@ class __$$RulerToolImplCopyWithImpl<$Res> $Res call({ Object? name = null, Object? displayIcon = null, - Object? gridColor = null, + Object? color = freezed, }) { return _then(_$RulerToolImpl( name: null == name @@ -2828,10 +2828,10 @@ class __$$RulerToolImplCopyWithImpl<$Res> ? _value.displayIcon : displayIcon // ignore: cast_nullable_to_non_nullable as String, - gridColor: null == gridColor - ? _value.gridColor - : gridColor // ignore: cast_nullable_to_non_nullable - as SRGBColor, + color: freezed == color + ? _value.color + : color // ignore: cast_nullable_to_non_nullable + as SRGBColor?, )); } } @@ -2842,7 +2842,7 @@ class _$RulerToolImpl extends RulerTool { _$RulerToolImpl( {this.name = '', this.displayIcon = '', - @ColorJsonConverter() this.gridColor = SRGBColor.black, + @ColorJsonConverter() this.color, final String? $type}) : $type = $type ?? 'ruler', super._(); @@ -2857,16 +2857,15 @@ class _$RulerToolImpl extends RulerTool { @JsonKey() final String displayIcon; @override - @JsonKey() @ColorJsonConverter() - final SRGBColor gridColor; + final SRGBColor? color; @JsonKey(name: 'type') final String $type; @override String toString() { - return 'Tool.ruler(name: $name, displayIcon: $displayIcon, gridColor: $gridColor)'; + return 'Tool.ruler(name: $name, displayIcon: $displayIcon, color: $color)'; } /// Create a copy of Tool @@ -2889,7 +2888,7 @@ abstract class RulerTool extends Tool { factory RulerTool( {final String name, final String displayIcon, - @ColorJsonConverter() final SRGBColor gridColor}) = _$RulerToolImpl; + @ColorJsonConverter() final SRGBColor? color}) = _$RulerToolImpl; RulerTool._() : super._(); factory RulerTool.fromJson(Map json) = @@ -2900,7 +2899,7 @@ abstract class RulerTool extends Tool { @override String get displayIcon; @ColorJsonConverter() - SRGBColor get gridColor; + SRGBColor? get color; /// Create a copy of Tool /// with the given fields replaced by the non-null parameter values. diff --git a/api/lib/src/models/tool.g.dart b/api/lib/src/models/tool.g.dart index 460514d1ecf7..f87f64628ad5 100644 --- a/api/lib/src/models/tool.g.dart +++ b/api/lib/src/models/tool.g.dart @@ -427,10 +427,8 @@ Map _$$TextureToolImplToJson(_$TextureToolImpl instance) => _$RulerToolImpl _$$RulerToolImplFromJson(Map json) => _$RulerToolImpl( name: json['name'] as String? ?? '', displayIcon: json['displayIcon'] as String? ?? '', - gridColor: json['gridColor'] == null - ? SRGBColor.black - : const ColorJsonConverter() - .fromJson((json['gridColor'] as num).toInt()), + color: _$JsonConverterFromJson( + json['color'], const ColorJsonConverter().fromJson), $type: json['type'] as String?, ); @@ -438,10 +436,23 @@ Map _$$RulerToolImplToJson(_$RulerToolImpl instance) => { 'name': instance.name, 'displayIcon': instance.displayIcon, - 'gridColor': const ColorJsonConverter().toJson(instance.gridColor), + 'color': _$JsonConverterToJson( + instance.color, const ColorJsonConverter().toJson), 'type': instance.$type, }; +Value? _$JsonConverterFromJson( + Object? json, + Value? Function(Json json) fromJson, +) => + json == null ? null : fromJson(json as Json); + +Json? _$JsonConverterToJson( + Value? value, + Json? Function(Value value) toJson, +) => + value == null ? null : toJson(value); + _$GridToolImpl _$$GridToolImplFromJson(Map json) => _$GridToolImpl( name: json['name'] as String? ?? '', displayIcon: json['displayIcon'] as String? ?? '', diff --git a/app/lib/cubits/current_index.dart b/app/lib/cubits/current_index.dart index bd0e9e794261..d1991b5abaae 100644 --- a/app/lib/cubits/current_index.dart +++ b/app/lib/cubits/current_index.dart @@ -319,7 +319,7 @@ class CurrentIndexCubit extends Cubit { Handler? handler; bool needsDispose = false; if (state.index == index) { - handler = fetchHandler>(); + handler = fetchHandler>(disableTemporary: true); } else if (state.toggleableHandlers.containsKey(index)) { handler = state.toggleableHandlers[index]; } @@ -327,7 +327,8 @@ class CurrentIndexCubit extends Cubit { List tools = const []; final blocState = bloc.state; if (blocState is DocumentLoaded) tools = blocState.info.tools; - handler = Handler.fromTool(tools.elementAtOrNull(index)); + final tool = tools.elementAtOrNull(index) ?? HandTool(); + handler = Handler.fromTool(tool); needsDispose = true; } final result = callback(handler); diff --git a/app/lib/dialogs/import/add.dart b/app/lib/dialogs/import/add.dart index c6ab323a375b..81ca6d3a7761 100644 --- a/app/lib/dialogs/import/add.dart +++ b/app/lib/dialogs/import/add.dart @@ -209,6 +209,16 @@ class _AddDialogState extends State { .toLowerCase() .contains(search.toLowerCase())) .toList(); + final view = [ + Tool.ruler, + Tool.grid, + ] + .map((e) => e()) + .where((e) => e + .getLocalizedName(context) + .toLowerCase() + .contains(search.toLowerCase())) + .toList(); return ListView( shrinkWrap: true, children: [ @@ -306,6 +316,14 @@ class _AddDialogState extends State { ), const SizedBox(height: 16), ], + if (view.isNotEmpty) ...[ + _ToolsListView( + isMobile: isMobile, + title: AppLocalizations.of(context).view, + children: view.map(buildTool).toList(), + ), + const SizedBox(height: 16), + ], ], ); }), diff --git a/app/lib/handlers/hand.dart b/app/lib/handlers/hand.dart index 3a4365aec4b0..f2f43813a670 100644 --- a/app/lib/handlers/hand.dart +++ b/app/lib/handlers/hand.dart @@ -23,6 +23,10 @@ class GeneralHandHandler extends Handler { _moved ? SystemMouseCursors.grabbing : SystemMouseCursors.grab; } +class FallbackHandler extends GeneralHandHandler { + FallbackHandler(super.data); +} + class HandHandler extends GeneralHandHandler { HandHandler([HandTool? tool]) : super(tool ?? HandTool()); } diff --git a/app/lib/handlers/handler.dart b/app/lib/handlers/handler.dart index 7ab3294275f8..a725c0b94336 100644 --- a/app/lib/handlers/handler.dart +++ b/app/lib/handlers/handler.dart @@ -221,7 +221,7 @@ abstract class Handler { return Handler.fromTool(tool); } - static Handler fromTool(T? tool) { + static Handler fromTool(T tool) { return switch (tool) { HandTool() => HandHandler(tool), SelectTool() => SelectHandler(tool), @@ -244,7 +244,7 @@ abstract class Handler { AssetTool() => AssetHandler(tool), EyeDropperTool() => EyeDropperHandler(tool), ExportTool() => ExportHandler(tool), - _ => GeneralHandHandler(tool), + _ => FallbackHandler(tool), } as Handler; } diff --git a/app/lib/handlers/import.dart b/app/lib/handlers/import.dart index d36a36aa1d03..3540e3dde3d6 100644 --- a/app/lib/handlers/import.dart +++ b/app/lib/handlers/import.dart @@ -61,8 +61,8 @@ class ImportHandler extends Handler { .copyWith(id: createUniqueId())) .nonNulls .toList())); - context.refresh(); - context.bake(); + await context.refresh(); + await context.bake(); } @override diff --git a/app/lib/l10n/app_en.arb b/app/lib/l10n/app_en.arb index 0d1390c723c3..e6428d052459 100644 --- a/app/lib/l10n/app_en.arb +++ b/app/lib/l10n/app_en.arb @@ -626,7 +626,7 @@ "renderResolution": "Render resolution", "translate": "Translate", "unencrypted": "Unencrypted", - "encrypted": "Decrypted", + "encrypted": "Encrypted", "encryptDocumentMessage": "Click to encrypt the document", "unencryptDocumentMessage": "Click to unencrypt the document", "unencrypt": "Unencrypt", @@ -634,5 +634,6 @@ "encryptWarning": "This will encrypt the document. You will need to remember the password to decrypt it.", "unencryptWarning": "This will unencrypt the document. The password will be removed and everyone with access will be able to open it.", "confirmPassword": "Confirm password", - "passwordMismatch": "The passwords do not match" + "passwordMismatch": "The passwords do not match", + "action": "Action" } diff --git a/app/lib/selections/selection.dart b/app/lib/selections/selection.dart index 72b4a480518d..0abf448a3bf3 100644 --- a/app/lib/selections/selection.dart +++ b/app/lib/selections/selection.dart @@ -31,10 +31,12 @@ part 'tools/tool.dart'; part 'tools/hand.dart'; part 'tools/area.dart'; part 'tools/eraser.dart'; +part 'tools/grid.dart'; part 'tools/label.dart'; part 'tools/laser.dart'; part 'tools/path_eraser.dart'; part 'tools/pen.dart'; +part 'tools/ruler.dart'; part 'tools/shape.dart'; part 'tools/stamp.dart'; part 'tools/texture.dart'; diff --git a/app/lib/selections/tools/grid.dart b/app/lib/selections/tools/grid.dart new file mode 100644 index 000000000000..c393b3957594 --- /dev/null +++ b/app/lib/selections/tools/grid.dart @@ -0,0 +1,40 @@ +part of '../selection.dart'; + +class GridToolSelection extends ToolSelection { + GridToolSelection(super.selected); + + @override + List buildProperties(BuildContext context) { + return [ + ...super.buildProperties(context), + const SizedBox(height: 8), + OffsetPropertyView( + title: Text(AppLocalizations.of(context).size), + value: Offset(selected.first.xSize, selected.first.ySize), + onChanged: (value) => update( + context, + selected + .map((e) => e.copyWith(xSize: value.dx, ySize: value.dy)) + .toList(), + ), + ), + const SizedBox(height: 8), + ColorField( + value: selected.first.color, + onChanged: (value) => update( + context, + selected.map((e) => e.copyWith(color: value)).toList(), + ), + title: Text(LeapLocalizations.of(context).color), + ), + ]; + } + + @override + Selection insert(dynamic element) { + if (element is GridTool) { + return GridToolSelection([...selected, element]); + } + return super.insert(element); + } +} diff --git a/app/lib/selections/tools/ruler.dart b/app/lib/selections/tools/ruler.dart new file mode 100644 index 000000000000..dab240fac245 --- /dev/null +++ b/app/lib/selections/tools/ruler.dart @@ -0,0 +1,41 @@ +part of '../selection.dart'; + +class RulerToolSelection extends ToolSelection { + RulerToolSelection(super.selected); + + @override + List buildProperties(BuildContext context) { + return [ + ...super.buildProperties(context), + ColorField( + title: Text(LeapLocalizations.of(context).color), + value: + selected.first.color?.withValues(a: 255) ?? SRGBColor.transparent, + subtitle: selected.first.color == null + ? Text(AppLocalizations.of(context).notSet) + : null, + leading: selected.first.color == null + ? null + : IconButton( + icon: const Icon(PhosphorIconsLight.trash), + onPressed: () => update( + context, + selected.map((e) => e.copyWith(color: null)).toList(), + ), + ), + onChanged: (value) => update( + context, + selected.map((e) => e.copyWith(color: value)).toList(), + ), + ), + ]; + } + + @override + Selection insert(dynamic element) { + if (element is RulerTool) { + return RulerToolSelection([...selected, element]); + } + return super.insert(element); + } +} diff --git a/app/lib/selections/tools/tool.dart b/app/lib/selections/tools/tool.dart index 14aa9662b25e..8e26018f6451 100644 --- a/app/lib/selections/tools/tool.dart +++ b/app/lib/selections/tools/tool.dart @@ -10,7 +10,9 @@ class ToolSelection extends Selection { EraserTool e => EraserToolSelection([e]), PathEraserTool e => PathEraserToolSelection([e]), AreaTool e => AreaToolSelection([e]), + GridTool e => GridToolSelection([e]), LaserTool e => LaserToolSelection([e]), + RulerTool e => RulerToolSelection([e]), ShapeTool e => ShapeToolSelection([e]), StampTool e => StampToolSelection([e]), TextureTool e => TextureToolSelection([e]), diff --git a/app/lib/services/import.dart b/app/lib/services/import.dart index b8c1d404438a..3490da121f0d 100644 --- a/app/lib/services/import.dart +++ b/app/lib/services/import.dart @@ -229,7 +229,8 @@ class ImportService { password = await showDialog( context: context, builder: (context) => NameDialog( - title: AppLocalizations.of(context).password, + title: AppLocalizations.of(context).encrypted, + hint: AppLocalizations.of(context).password, button: AppLocalizations.of(context).open, obscureText: true, ), diff --git a/app/lib/visualizer/tool.dart b/app/lib/visualizer/tool.dart index ea4fb1ec7cfe..f638ac859453 100644 --- a/app/lib/visualizer/tool.dart +++ b/app/lib/visualizer/tool.dart @@ -19,9 +19,16 @@ extension ToolCategoryVisualizer on ToolCategory { ToolCategory.normal => PhosphorIcons.paintBrush, ToolCategory.import => PhosphorIcons.arrowSquareIn, ToolCategory.surface => PhosphorIcons.monitor, - ToolCategory.action => PhosphorIcons.arrowClockwise, + ToolCategory.action => PhosphorIcons.play, ToolCategory.view => PhosphorIcons.eye, }; + String getLocalizedName(BuildContext context) => switch (this) { + ToolCategory.normal => AppLocalizations.of(context).normal, + ToolCategory.import => AppLocalizations.of(context).import, + ToolCategory.surface => AppLocalizations.of(context).surface, + ToolCategory.action => AppLocalizations.of(context).action, + ToolCategory.view => AppLocalizations.of(context).view, + }; } extension ToolVisualizer on Tool { From 417131d017a6fd7ae1246d12b20a88be855875e3 Mon Sep 17 00:00:00 2001 From: CodeDoctorDE Date: Sun, 29 Dec 2024 21:16:00 +0100 Subject: [PATCH 3/8] Add ruler and grid renderer --- api/pubspec.lock | 4 +- api/pubspec.yaml | 2 +- app/lib/bloc/document_bloc.dart | 4 +- app/lib/cubits/current_index.dart | 106 ++++++++++++++++++++-------- app/lib/dialogs/import/add.dart | 1 + app/lib/handlers/collection.dart | 4 +- app/lib/handlers/export.dart | 4 +- app/lib/handlers/eye_dropper.dart | 2 +- app/lib/handlers/full_screen.dart | 4 +- app/lib/handlers/grid.dart | 56 +++++++++++++++ app/lib/handlers/handler.dart | 12 +++- app/lib/handlers/redo.dart | 4 +- app/lib/handlers/ruler.dart | 109 +++++++++++++++++++++++++++++ app/lib/handlers/select.dart | 4 +- app/lib/handlers/undo.dart | 4 +- app/lib/renderers/renderer.dart | 3 +- app/lib/renderers/utilities.dart | 33 +-------- app/lib/views/edit.dart | 39 ++++++----- app/lib/views/view.dart | 4 +- app/lib/widgets/option_button.dart | 7 +- app/pubspec.lock | 12 ++-- app/pubspec.yaml | 4 +- 22 files changed, 308 insertions(+), 114 deletions(-) create mode 100644 app/lib/handlers/grid.dart create mode 100644 app/lib/handlers/ruler.dart diff --git a/api/pubspec.lock b/api/pubspec.lock index 407151a00bef..d47e1a1878d5 100644 --- a/api/pubspec.lock +++ b/api/pubspec.lock @@ -331,8 +331,8 @@ packages: dependency: "direct main" description: path: "packages/lw_file_system_api" - ref: a0752f136913f64a975cb8b20ccb16fb6ce37737 - resolved-ref: a0752f136913f64a975cb8b20ccb16fb6ce37737 + ref: "5ab1b96bea6ef0e0c07629ff4e7152b4437cf8ee" + resolved-ref: "5ab1b96bea6ef0e0c07629ff4e7152b4437cf8ee" url: "https://github.com/LinwoodDev/dart_pkgs" source: git version: "1.0.0" diff --git a/api/pubspec.yaml b/api/pubspec.yaml index f7c905ef61cc..0c8342dc6ea0 100644 --- a/api/pubspec.yaml +++ b/api/pubspec.yaml @@ -19,7 +19,7 @@ dependencies: git: url: https://github.com/LinwoodDev/dart_pkgs path: packages/lw_file_system_api - ref: a0752f136913f64a975cb8b20ccb16fb6ce37737 + ref: 5ab1b96bea6ef0e0c07629ff4e7152b4437cf8ee dart_leap: git: url: https://github.com/LinwoodDev/dart_pkgs diff --git a/app/lib/bloc/document_bloc.dart b/app/lib/bloc/document_bloc.dart index a7cb26e980b4..65c03f0be731 100644 --- a/app/lib/bloc/document_bloc.dart +++ b/app/lib/bloc/document_bloc.dart @@ -406,7 +406,9 @@ class DocumentBloc extends ReplayBloc { }).toList()))); final updatedCurrent = event.tools.entries.firstWhereOrNull((element) => oldTools[element.key] == - current.currentIndexCubit.state.handler.data); + current.currentIndexCubit.state.handler.data || + current.currentIndexCubit.state.toggleableHandlers + .containsKey(element.key)); if (updatedCurrent != null) { current.currentIndexCubit.updateTool(this, updatedCurrent.value); } diff --git a/app/lib/cubits/current_index.dart b/app/lib/cubits/current_index.dart index d1991b5abaae..9f4791a8b6e3 100644 --- a/app/lib/cubits/current_index.dart +++ b/app/lib/cubits/current_index.dart @@ -90,6 +90,12 @@ class CurrentIndex with _$CurrentIndex { ...rendererStates, ...?temporaryRendererStates, }; + + List getAllForegrounds([bool networking = true]) => [ + ...(temporaryForegrounds ?? foregrounds), + ...toggleableForegrounds.values.expand((e) => e), + if (networking) ...networkingForegrounds, + ]; } class CurrentIndexCubit extends Cubit { @@ -129,7 +135,7 @@ class CurrentIndexCubit extends Cubit { BuildContext? context, Handler? handler, }) async { - resetInput(bloc); + await resetInput(bloc); final blocState = bloc.state; if (blocState is! DocumentLoadSuccess) return null; final document = blocState.data; @@ -139,7 +145,11 @@ class CurrentIndexCubit extends Cubit { return null; } handler ??= Handler.fromTool(info.tools[index]); - if (context == null || handler.onSelected(context)) { + var selectState = SelectState.normal; + if (context != null) { + selectState = handler.onSelected(context); + } + if (selectState != SelectState.none) { state.handler.dispose(bloc); state.temporaryHandler?.dispose(bloc); _disposeForegrounds(); @@ -149,19 +159,36 @@ class CurrentIndexCubit extends Cubit { await Future.wait(foregrounds.map((e) async => await e.setup(document, blocState.assetService, blocState.page))); } - emit(state.copyWith( - index: index, - handler: handler, - cursor: handler.cursor ?? MouseCursor.defer, - foregrounds: foregrounds, - toolbar: handler.getToolbar(bloc), - rendererStates: handler.rendererStates, - temporaryForegrounds: null, - temporaryHandler: null, - temporaryToolbar: null, - temporaryCursor: null, - temporaryRendererStates: null, - )); + if (selectState == SelectState.normal) { + emit(state.copyWith( + index: index, + handler: handler, + cursor: handler.cursor ?? MouseCursor.defer, + foregrounds: foregrounds, + toolbar: handler.getToolbar(bloc), + rendererStates: handler.rendererStates, + temporaryForegrounds: null, + temporaryHandler: null, + temporaryToolbar: null, + temporaryCursor: null, + temporaryRendererStates: null, + )); + } else { + if (isHandlerEnabled(index)) { + disableHandler(bloc, index); + } else { + emit(state.copyWith( + toggleableHandlers: { + ...state.toggleableHandlers, + index: handler, + }, + toggleableForegrounds: { + ...state.toggleableForegrounds, + index: foregrounds + }, + )); + } + } } return handler; } @@ -186,7 +213,7 @@ class CurrentIndexCubit extends Cubit { cursor ??= state.lastPosition ?? Offset.zero; state.networkingService.sendUser(NetworkingUser( cursor: state.transformCubit.state.localToGlobal(cursor).toPoint(), - foreground: (foregrounds ?? getForegrounds(false)) + foreground: (foregrounds ?? state.getAllForegrounds(false)) .map((e) => e.element) .whereType() .toList(), @@ -427,9 +454,22 @@ class CurrentIndexCubit extends Cubit { return null; } - void enableHandler(DocumentBloc bloc, int index, Handler handler) { + void toggleHandler(DocumentBloc bloc, int index) { + if (state.toggleableHandlers.containsKey(index)) { + disableHandler(bloc, index); + } else { + enableHandler(bloc, index); + } + } + + Handler? enableHandler(DocumentBloc bloc, int index) { final blocState = bloc.state; - if (blocState is! DocumentLoaded) return; + if (blocState is! DocumentLoaded) return null; + if (index < 0 || index >= blocState.info.tools.length) { + return null; + } + final tool = blocState.info.tools[index]; + final handler = Handler.fromTool(tool); final document = blocState.data; final page = blocState.page; final info = blocState.info; @@ -445,6 +485,7 @@ class CurrentIndexCubit extends Cubit { ..[index] = handler, toggleableForegrounds: Map.from(state.toggleableForegrounds) ..[index] = foregrounds)); + return handler; } bool disableHandler(DocumentBloc bloc, int index) { @@ -453,14 +494,16 @@ class CurrentIndexCubit extends Cubit { return false; } handler.dispose(bloc); - final foregrounds = state.toggleableForegrounds[index]; - for (final r in foregrounds ?? []) { + final foregrounds = + Map>.from(state.toggleableForegrounds); + final current = foregrounds.remove(index); + for (final r in current ?? []) { r.dispose(); } emit(state.copyWith( - toggleableHandlers: Map.from(state.toggleableHandlers)..remove(index), - toggleableForegrounds: Map.from(state.toggleableForegrounds) - ..remove(index))); + toggleableHandlers: Map.from(state.toggleableHandlers)..remove(index), + toggleableForegrounds: foregrounds, + )); return true; } @@ -518,12 +561,14 @@ class CurrentIndexCubit extends Cubit { tool, bloc: bloc, temporaryState: temporaryState, + index: index, ); } Future?> changeTemporaryHandler( BuildContext context, T tool, {DocumentBloc? bloc, + int? index, TemporaryState temporaryState = TemporaryState.allowClick}) async { bloc ??= context.read(); final handler = Handler.fromTool(tool); @@ -533,7 +578,9 @@ class CurrentIndexCubit extends Cubit { final page = blocState.page; final currentArea = blocState.currentArea; state.temporaryHandler?.dispose(bloc); - if (handler.onSelected(context)) { + final selectState = handler.onSelected(context); + + if (selectState == SelectState.normal) { _disposeTemporaryForegrounds(); final temporaryForegrounds = handler.createForegrounds( this, document, page, blocState.info, currentArea); @@ -549,15 +596,12 @@ class CurrentIndexCubit extends Cubit { temporaryRendererStates: handler.rendererStates, temporaryState: temporaryState, )); + } else if (selectState == SelectState.toggle && index != null) { + toggleHandler(bloc, index); } return handler; } - List getForegrounds([bool networking = true]) => [ - ...(state.temporaryForegrounds ?? state.foregrounds), - if (networking) ...state.networkingForegrounds, - ]; - void resetReleaseHandler(DocumentBloc bloc) { if (state.temporaryState == TemporaryState.removeAfterRelease) { resetTemporaryHandler(bloc, true); @@ -989,8 +1033,8 @@ class CurrentIndexCubit extends Cubit { emit(state.copyWith(buttons: null)); } - void resetInput(DocumentBloc bloc) { - state.handler.resetInput(bloc); + Future resetInput(DocumentBloc bloc) async { + await state.handler.resetInput(bloc); emit(state.copyWith(buttons: null, pointers: [])); } diff --git a/app/lib/dialogs/import/add.dart b/app/lib/dialogs/import/add.dart index 81ca6d3a7761..65971d0e0a92 100644 --- a/app/lib/dialogs/import/add.dart +++ b/app/lib/dialogs/import/add.dart @@ -45,6 +45,7 @@ class _AddDialogState extends State { currentIndexCubit.changeTool( bloc, index: state.info.tools.length, + context: context, handler: Handler.fromTool(defaultTool), ); } diff --git a/app/lib/handlers/collection.dart b/app/lib/handlers/collection.dart index 43596e13d9fa..2a506068bb97 100644 --- a/app/lib/handlers/collection.dart +++ b/app/lib/handlers/collection.dart @@ -4,7 +4,7 @@ class CollectionHandler extends Handler { CollectionHandler(super.data); @override - bool onSelected(BuildContext context, [bool wasAdded = true]) { + SelectState onSelected(BuildContext context, [bool wasAdded = true]) { final bloc = context.read(); showDialog( context: context, @@ -13,6 +13,6 @@ class CollectionHandler extends Handler { child: const CollectionsDialog(), ), ); - return false; + return SelectState.normal; } } diff --git a/app/lib/handlers/export.dart b/app/lib/handlers/export.dart index d14cf11524ee..56f3bca4677b 100644 --- a/app/lib/handlers/export.dart +++ b/app/lib/handlers/export.dart @@ -4,7 +4,7 @@ class ExportHandler extends Handler { ExportHandler(super.data); @override - bool onSelected(BuildContext context, [bool wasAdded = true]) { + SelectState onSelected(BuildContext context, [bool wasAdded = true]) { final bloc = context.read(); showDialog( context: context, @@ -14,6 +14,6 @@ class ExportHandler extends Handler { options: data.options, ), )); - return false; + return SelectState.none; } } diff --git a/app/lib/handlers/eye_dropper.dart b/app/lib/handlers/eye_dropper.dart index 08b55243e22f..4c62dbe2feab 100644 --- a/app/lib/handlers/eye_dropper.dart +++ b/app/lib/handlers/eye_dropper.dart @@ -4,7 +4,7 @@ class EyeDropperHandler extends Handler { EyeDropperHandler(super.data); @override - bool onSelected(BuildContext context, [bool wasAdded = true]) { + SelectState onSelected(BuildContext context, [bool wasAdded = true]) { if (!wasAdded) { context.read().changeTemporaryHandler(context, data, temporaryState: TemporaryState.removeAfterRelease); diff --git a/app/lib/handlers/full_screen.dart b/app/lib/handlers/full_screen.dart index a87ecbd06b2b..7130bb1c48b4 100644 --- a/app/lib/handlers/full_screen.dart +++ b/app/lib/handlers/full_screen.dart @@ -4,9 +4,9 @@ class FullScreenHandler extends Handler { FullScreenHandler(super.data); @override - bool onSelected(BuildContext context, [bool wasAdded = true]) { + SelectState onSelected(BuildContext context, [bool wasAdded = true]) { context.read().toggleFullScreen(); - return false; + return SelectState.none; } @override diff --git a/app/lib/handlers/grid.dart b/app/lib/handlers/grid.dart new file mode 100644 index 000000000000..54076e8e424b --- /dev/null +++ b/app/lib/handlers/grid.dart @@ -0,0 +1,56 @@ +part of 'handler.dart'; + +class GridHandler extends Handler { + GridHandler(super.data); + + @override + List createForegrounds(CurrentIndexCubit currentIndexCubit, + NoteData document, DocumentPage page, DocumentInfo info, + [Area? currentArea]) => + [GridRenderer(data)]; + + @override + SelectState onSelected(BuildContext context, [bool wasAdded = true]) { + return SelectState.toggle; + } +} + +class GridRenderer extends Renderer { + GridRenderer(super.element); + + @override + void build(Canvas canvas, Size size, NoteData document, DocumentPage page, + DocumentInfo info, CameraTransform transform, + [ColorScheme? colorScheme, bool foreground = false]) { + if (element.xSize > 0) { + double x = 0; + while (x < size.width) { + final localX = x / transform.size; + canvas.drawLine( + Offset(localX + transform.position.dx, transform.position.dy), + Offset(localX + transform.position.dx, + size.height / transform.size + transform.position.dy), + Paint() + ..strokeWidth = 1 / transform.size + ..color = element.color.toColor(), + ); + x += element.xSize; + } + } + if (element.ySize > 0) { + double y = 0; + while (y < size.height) { + final localY = y / transform.size; + canvas.drawLine( + Offset(transform.position.dx, transform.position.dy + localY), + Offset(transform.position.dx + size.width / transform.size, + transform.position.dy + localY), + Paint() + ..strokeWidth = 1 / transform.size + ..color = element.color.toColor(), + ); + y += element.ySize; + } + } + } +} diff --git a/app/lib/handlers/handler.dart b/app/lib/handlers/handler.dart index a725c0b94336..6e14674fd7ba 100644 --- a/app/lib/handlers/handler.dart +++ b/app/lib/handlers/handler.dart @@ -70,6 +70,8 @@ part 'pen.dart'; part 'eye_dropper.dart'; part 'presentation.dart'; part 'redo.dart'; +part 'ruler.dart'; +part 'grid.dart'; part 'select.dart'; part 'shape.dart'; part 'spacer.dart'; @@ -153,12 +155,15 @@ class EventContext { enum ToolStatus { normal, disabled } +enum SelectState { normal, none, toggle } + abstract class Handler { final T data; const Handler(this.data); - bool onSelected(BuildContext context, [bool wasAdded = true]) => true; + SelectState onSelected(BuildContext context, [bool wasAdded = true]) => + SelectState.normal; List createForegrounds(CurrentIndexCubit currentIndexCubit, NoteData document, DocumentPage page, DocumentInfo info, @@ -210,7 +215,7 @@ abstract class Handler { bool canChange(PointerDownEvent event, EventContext context) => true; - void resetInput(DocumentBloc bloc) {} + FutureOr resetInput(DocumentBloc bloc) {} ToolStatus getStatus(DocumentBloc bloc) => ToolStatus.normal; @@ -244,7 +249,8 @@ abstract class Handler { AssetTool() => AssetHandler(tool), EyeDropperTool() => EyeDropperHandler(tool), ExportTool() => ExportHandler(tool), - _ => FallbackHandler(tool), + GridTool() => GridHandler(tool), + RulerTool() => RulerHandler(tool), } as Handler; } diff --git a/app/lib/handlers/redo.dart b/app/lib/handlers/redo.dart index 7c6eb72bb6ba..d52e1f14379e 100644 --- a/app/lib/handlers/redo.dart +++ b/app/lib/handlers/redo.dart @@ -4,11 +4,11 @@ class RedoHandler extends Handler { RedoHandler(super.data); @override - bool onSelected(BuildContext context, [bool wasAdded = true]) { + SelectState onSelected(BuildContext context, [bool wasAdded = true]) { final bloc = context.read(); bloc.redo(); bloc.load().then((value) => bloc.bake().then((value) => bloc.save())); - return false; + return SelectState.none; } @override diff --git a/app/lib/handlers/ruler.dart b/app/lib/handlers/ruler.dart new file mode 100644 index 000000000000..58ca7c378d7a --- /dev/null +++ b/app/lib/handlers/ruler.dart @@ -0,0 +1,109 @@ +part of 'handler.dart'; + +class RulerHandler extends Handler { + Offset _position = Offset.zero; + double _rotation = 0; + + RulerHandler(super.data); + + @override + List createForegrounds(CurrentIndexCubit currentIndexCubit, + NoteData document, DocumentPage page, DocumentInfo info, + [Area? currentArea]) => + [ + RulerRenderer(data, position: _position, rotation: _rotation), + ]; + + @override + SelectState onSelected(BuildContext context, [bool wasAdded = true]) { + return SelectState.toggle; + } + + void move(Offset offset, EventContext context) { + _position = offset; + context.refresh(); + } + + void rotate(double rotation, EventContext context) { + _rotation = rotation; + context.refresh(); + } +} + +class RulerRenderer extends Renderer { + final Offset position; + @override + final double rotation; + + RulerRenderer( + super.element, { + this.position = Offset.zero, + this.rotation = 0, + }); + + Rect getRulerRect(Size size, CameraTransform transform) { + const rulerSize = 100.0; + return Rect.fromLTWH( + transform.position.dx, + transform.position.dy + + (size.height / 2 + -rulerSize / 2) / transform.size, + size.width / transform.size, + rulerSize / transform.size, + ); + } + + @override + void build(Canvas canvas, Size size, NoteData document, DocumentPage page, + DocumentInfo info, CameraTransform transform, + [ColorScheme? colorScheme, bool foreground = false]) { + final rulerColor = colorScheme?.primary ?? Colors.grey; + final rulerBackgroundColor = element.color?.toColor() ?? + (colorScheme?.primaryContainer ?? Colors.grey).withAlpha(200); + final rulerForegroundColor = colorScheme?.onPrimary ?? Colors.white; + final rulerPaint = Paint() + ..color = rulerColor + ..strokeWidth = 1 / transform.size + ..style = PaintingStyle.stroke + ..strokeJoin = StrokeJoin.round; + final rulerBackgroundPaint = Paint() + ..color = rulerBackgroundColor + ..style = PaintingStyle.fill; + final rulerForegroundPaint = Paint()..color = rulerForegroundColor; + final rulerRect = getRulerRect(size, transform); + + // Calculate steps based on zoom level + var steps = 50; + + // Paint ruler background + canvas.drawRect(rulerRect, rulerBackgroundPaint); + canvas.drawLine( + Offset(rulerRect.left, rulerRect.top), + Offset(rulerRect.right, rulerRect.top), + rulerPaint, + ); + canvas.drawLine( + Offset(rulerRect.left, rulerRect.bottom), + Offset(rulerRect.right, rulerRect.bottom), + rulerPaint, + ); + + // Paint ruler lines + double x = 0; + var even = (transform.position.dx ~/ (steps / transform.size)) % 2 == 0; + while (x <= size.width / transform.size) { + final posX = x - + (transform.position.dx % (steps / transform.size)) + + transform.position.dx; + canvas.drawLine( + Offset(posX, rulerRect.top), + Offset( + posX, + rulerRect.top + + (even ? rulerRect.height / 8 : rulerRect.height / 4)), + rulerForegroundPaint, + ); + even = !even; + x += steps / transform.size; + } + } +} diff --git a/app/lib/handlers/select.dart b/app/lib/handlers/select.dart index 750439adedcc..ceb2ea4395d0 100644 --- a/app/lib/handlers/select.dart +++ b/app/lib/handlers/select.dart @@ -31,12 +31,12 @@ class SelectHandler extends Handler { : {}; @override - void resetInput(DocumentBloc bloc) { + Future resetInput(DocumentBloc bloc) async { _submitTransform(bloc); _rectangleFreeSelection = null; _lassoFreeSelection = null; _selectionManager.reset(); - bloc.refresh(); + await bloc.refresh(); } @override diff --git a/app/lib/handlers/undo.dart b/app/lib/handlers/undo.dart index 018b52869cf6..1474d6c0e902 100644 --- a/app/lib/handlers/undo.dart +++ b/app/lib/handlers/undo.dart @@ -4,11 +4,11 @@ class UndoHandler extends Handler { UndoHandler(super.data); @override - bool onSelected(BuildContext context, [bool wasAdded = true]) { + SelectState onSelected(BuildContext context, [bool wasAdded = true]) { final bloc = context.read(); bloc.undo(); bloc.load().then((value) => bloc.bake().then((value) => bloc.save())); - return false; + return SelectState.none; } @override diff --git a/app/lib/renderers/renderer.dart b/app/lib/renderers/renderer.dart index 31048d671a4e..4be18c784e7c 100644 --- a/app/lib/renderers/renderer.dart +++ b/app/lib/renderers/renderer.dart @@ -171,7 +171,8 @@ abstract class Renderer { element is PadElement ? (element as PadElement).rotation : 0.0; String get id => - (element is PadElement ? (element as PadElement).id : null) ?? ''; + (element is PadElement ? (element as PadElement).id : null) ?? + createUniqueId(); @mustCallSuper FutureOr setup(NoteData document, AssetService assetService, diff --git a/app/lib/renderers/utilities.dart b/app/lib/renderers/utilities.dart index 49aad6c18a08..45e71f241f7f 100644 --- a/app/lib/renderers/utilities.dart +++ b/app/lib/renderers/utilities.dart @@ -34,38 +34,7 @@ class UtilitiesRenderer extends Renderer { void build(Canvas canvas, Size size, NoteData document, DocumentPage page, DocumentInfo info, CameraTransform transform, [ColorScheme? colorScheme, bool foreground = false]) { - /*if (element.gridEnabled) { - if (option.gridXSize > 0) { - double x = 0; - while (x < size.width) { - final localX = x / transform.size; - canvas.drawLine( - Offset(localX + transform.position.dx, transform.position.dy), - Offset(localX + transform.position.dx, - size.height / transform.size + transform.position.dy), - Paint() - ..strokeWidth = 1 / transform.size - ..color = option.gridColor.toColor(), - ); - x += option.gridXSize; - } - } - if (option.gridYSize > 0) { - double y = 0; - while (y < size.height) { - final localY = y / transform.size; - canvas.drawLine( - Offset(transform.position.dx, transform.position.dy + localY), - Offset(transform.position.dx + size.width / transform.size, - transform.position.dy + localY), - Paint() - ..strokeWidth = 1 / transform.size - ..color = option.gridColor.toColor(), - ); - y += option.gridYSize; - } - } - } + /* if (element.rulerEnabled) { final rulerColor = colorScheme?.primary ?? Colors.grey; final rulerBackgroundColor = diff --git a/app/lib/views/edit.dart b/app/lib/views/edit.dart index 00ef540de263..c4d4d382ce76 100644 --- a/app/lib/views/edit.dart +++ b/app/lib/views/edit.dart @@ -90,6 +90,8 @@ class _EditToolbarState extends State { buildWhen: (previous, current) => previous.index != current.index || previous.handler != current.handler || + previous.toggleableHandlers != + current.toggleableHandlers || previous.temporaryHandler != current.temporaryHandler || previous.selection != current.selection, builder: (context, currentIndex) { @@ -295,24 +297,27 @@ class _EditToolbarState extends State { .read() .changeSelection(tool), focussed: shortcuts.contains(i), - selected: selected, - alwaysShowBottom: tool.isAction(), + selected: selected || + currentIndex.toggleableHandlers.containsKey(i), + showBottom: selected || tool.isAction(), highlighted: highlighted, - bottomIcon: PhosphorIcon(tool.isAction() - ? PhosphorIconsLight.playCircle - : isMobile - ? PhosphorIconsLight.caretUp - : switch (settings.toolbarPosition) { - ToolbarPosition.top || - ToolbarPosition.inline => - PhosphorIconsLight.caretDown, - ToolbarPosition.bottom => - PhosphorIconsLight.caretUp, - ToolbarPosition.left => - PhosphorIconsLight.caretRight, - ToolbarPosition.right => - PhosphorIconsLight.caretLeft, - }), + bottomIcon: selected || tool.isAction() + ? PhosphorIcon(tool.isAction() + ? PhosphorIconsLight.playCircle + : isMobile + ? PhosphorIconsLight.caretUp + : switch (settings.toolbarPosition) { + ToolbarPosition.top || + ToolbarPosition.inline => + PhosphorIconsLight.caretDown, + ToolbarPosition.bottom => + PhosphorIconsLight.caretUp, + ToolbarPosition.left => + PhosphorIconsLight.caretRight, + ToolbarPosition.right => + PhosphorIconsLight.caretLeft, + }) + : null, selectedIcon: _buildIcon(icon, size, color), icon: _buildIcon(icon, size, color), onPressed: () { diff --git a/app/lib/views/view.dart b/app/lib/views/view.dart index 0f0af8af9c87..e44e650f8965 100644 --- a/app/lib/views/view.dart +++ b/app/lib/views/view.dart @@ -190,6 +190,8 @@ class _MainViewViewportState extends State previous.foregrounds != current.foregrounds || previous.handler != current.handler || previous.temporaryHandler != current.temporaryHandler || + previous.toggleableForegrounds != + current.toggleableForegrounds || previous.temporaryForegrounds != current.temporaryForegrounds || previous.rendererStates != current.rendererStates || previous.networkingForegrounds != @@ -440,7 +442,7 @@ class _MainViewViewportState extends State CustomPaint( size: Size.infinite, foregroundPainter: ForegroundPainter( - cubit.getForegrounds(), + currentIndex.getAllForegrounds(), state.data, state.page, state.info, diff --git a/app/lib/widgets/option_button.dart b/app/lib/widgets/option_button.dart index 7719801568f9..ec3f2cf22600 100644 --- a/app/lib/widgets/option_button.dart +++ b/app/lib/widgets/option_button.dart @@ -5,7 +5,7 @@ class OptionButton extends StatefulWidget { final Widget icon; final Widget? selectedIcon, bottomIcon, leadingIcon; final VoidCallback? onPressed, onSecondaryPressed, onLongPressed; - final bool selected, highlighted, focussed, alwaysShowBottom; + final bool selected, highlighted, focussed, showBottom; final String tooltip; const OptionButton({ @@ -21,7 +21,7 @@ class OptionButton extends StatefulWidget { this.selected = false, this.highlighted = false, this.focussed = false, - this.alwaysShowBottom = false, + this.showBottom = false, }); @override @@ -54,8 +54,7 @@ class _OptionButtonState extends State _animationController.dispose(); } - double get _nextValue => - widget.alwaysShowBottom || widget.selected || widget.highlighted ? 1 : 0; + double get _nextValue => widget.showBottom ? 1 : 0; @override void didUpdateWidget(covariant OptionButton oldWidget) { diff --git a/app/pubspec.lock b/app/pubspec.lock index c366716fddfb..35e274ab270b 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -782,8 +782,8 @@ packages: dependency: "direct main" description: path: "packages/lw_file_system" - ref: "2325ec71be6691f64725bc78a911cabed68d901b" - resolved-ref: "2325ec71be6691f64725bc78a911cabed68d901b" + ref: "54f7ac141410938babff9539dca190f5d130a0db" + resolved-ref: "54f7ac141410938babff9539dca190f5d130a0db" url: "https://github.com/LinwoodDev/dart_pkgs" source: git version: "1.0.0" @@ -791,8 +791,8 @@ packages: dependency: transitive description: path: "packages/lw_file_system_api" - ref: a0752f136913f64a975cb8b20ccb16fb6ce37737 - resolved-ref: a0752f136913f64a975cb8b20ccb16fb6ce37737 + ref: "5ab1b96bea6ef0e0c07629ff4e7152b4437cf8ee" + resolved-ref: "5ab1b96bea6ef0e0c07629ff4e7152b4437cf8ee" url: "https://github.com/LinwoodDev/dart_pkgs" source: git version: "1.0.0" @@ -841,8 +841,8 @@ packages: dependency: "direct main" description: path: "packages/material_leap" - ref: "307657c0e0b93de9af1e1aa9bd96900e0bb7d27c" - resolved-ref: "307657c0e0b93de9af1e1aa9bd96900e0bb7d27c" + ref: "54f7ac141410938babff9539dca190f5d130a0db" + resolved-ref: "54f7ac141410938babff9539dca190f5d130a0db" url: "https://github.com/LinwoodDev/dart_pkgs" source: git version: "0.0.1" diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 11f93d3afad9..b6fbfbd1abc4 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -79,7 +79,7 @@ dependencies: material_leap: git: url: https://github.com/LinwoodDev/dart_pkgs - ref: 307657c0e0b93de9af1e1aa9bd96900e0bb7d27c + ref: 54f7ac141410938babff9539dca190f5d130a0db path: packages/material_leap lw_sysapi: git: @@ -99,7 +99,7 @@ dependencies: lw_file_system: git: url: https://github.com/LinwoodDev/dart_pkgs - ref: 2325ec71be6691f64725bc78a911cabed68d901b + ref: 54f7ac141410938babff9539dca190f5d130a0db path: packages/lw_file_system flutter_localized_locales: ^2.0.5 dynamic_color: ^1.7.0 From fd9476ae06e134e1b48faf361e803f8805e49174 Mon Sep 17 00:00:00 2001 From: CodeDoctorDE Date: Sun, 29 Dec 2024 21:52:58 +0100 Subject: [PATCH 4/8] Remove utilities renderer, use handler for manipulation --- app/lib/bloc/document_bloc.dart | 10 +- app/lib/cubits/current_index.dart | 45 +---- app/lib/cubits/current_index.freezed.dart | 39 ++++- app/lib/handlers/eraser.dart | 2 +- app/lib/handlers/grid.dart | 10 +- app/lib/handlers/handler.dart | 16 ++ app/lib/handlers/laser.dart | 4 +- app/lib/handlers/path_eraser.dart | 2 +- app/lib/handlers/pen.dart | 4 +- app/lib/handlers/ruler.dart | 69 ++++++-- app/lib/handlers/select.dart | 54 +++--- app/lib/models/viewport.dart | 30 +--- app/lib/renderers/renderer.dart | 6 - app/lib/renderers/utilities.dart | 159 ------------------ .../selections/{utilities.dart => file.dart} | 6 +- app/lib/selections/selection.dart | 6 +- app/lib/view_painter.dart | 8 +- app/lib/views/edit.dart | 5 +- app/lib/views/main.dart | 6 +- app/lib/views/view.dart | 1 - 20 files changed, 160 insertions(+), 322 deletions(-) delete mode 100644 app/lib/renderers/utilities.dart rename app/lib/selections/{utilities.dart => file.dart} (99%) diff --git a/app/lib/bloc/document_bloc.dart b/app/lib/bloc/document_bloc.dart index 65c03f0be731..49bdfe3abe35 100644 --- a/app/lib/bloc/document_bloc.dart +++ b/app/lib/bloc/document_bloc.dart @@ -1079,13 +1079,7 @@ class DocumentBloc extends ReplayBloc { final current = state; if (current is! DocumentLoaded) return; final cubit = current.currentIndexCubit; - final document = current.data; - final page = current.page; - final assetService = current.assetService; cubit.setSaveState(saved: SaveState.saved); - final tool = UtilitiesRenderer(cubit.state.utilitiesState); - await tool.setup(document, assetService, page); - cubit.unbake(tool: tool); cubit.loadElements(current); cubit.init(this); } @@ -1158,7 +1152,7 @@ class DocumentBloc extends ReplayBloc { if (state is! DocumentLoadSuccess) return {}; transform ??= state.currentIndexCubit.state.transformCubit.state; final renderers = state.cameraViewport.visibleElements; - full ??= state.cameraViewport.utilities.element.fullSelection; + full ??= state.currentIndexCubit.state.utilities.fullSelection; return compute( _executeRayCast, _RayCastParams( @@ -1182,7 +1176,7 @@ class DocumentBloc extends ReplayBloc { if (state is! DocumentLoadSuccess) return {}; final renderers = state.cameraViewport.visibleElements; transform ??= state.currentIndexCubit.state.transformCubit.state; - full ??= state.cameraViewport.utilities.element.fullSelection; + full ??= state.currentIndexCubit.state.utilities.fullSelection; return compute( _executeRayCastPolygon, _RayCastPolygonParams( diff --git a/app/lib/cubits/current_index.dart b/app/lib/cubits/current_index.dart index 9f4791a8b6e3..4471588c6931 100644 --- a/app/lib/cubits/current_index.dart +++ b/app/lib/cubits/current_index.dart @@ -49,6 +49,7 @@ class CurrentIndex with _$CurrentIndex { SettingsCubit settingsCubit, TransformCubit transformCubit, NetworkingService networkingService, { + @Default(UtilitiesState()) UtilitiesState utilities, Handler? temporaryHandler, @Default([]) List foregrounds, Selection? selection, @@ -84,8 +85,6 @@ class CurrentIndex with _$CurrentIndex { MouseCursor get currentCursor => temporaryCursor ?? cursor; - UtilitiesState get utilitiesState => cameraViewport.utilities.element; - Map get allRendererStates => { ...rendererStates, ...?temporaryRendererStates, @@ -123,12 +122,6 @@ class CurrentIndexCubit extends Cubit { } } - Offset getGridPosition( - Offset position, DocumentPage page, DocumentInfo info) { - return state.cameraViewport.utilities - .getGridPosition(position, page, info, this); - } - Future changeTool( DocumentBloc bloc, { int? index, @@ -529,7 +522,7 @@ class CurrentIndexCubit extends Cubit { temporaryForegrounds: null, temporaryCursor: null, temporaryRendererStates: null, - cameraViewport: CameraViewport.unbaked(UtilitiesRenderer()), + cameraViewport: CameraViewport.unbaked(), )); } @@ -864,13 +857,10 @@ class CurrentIndexCubit extends Cubit { void unbake( {List>? backgrounds, - UtilitiesRenderer? tool, List>? unbakedElements}) { emit(state.copyWith( cameraViewport: state.cameraViewport.unbake( - unbakedElements: unbakedElements, - utilities: tool, - backgrounds: backgrounds))); + unbakedElements: unbakedElements, backgrounds: backgrounds))); } Future loadElements(DocumentState docState) async { @@ -893,17 +883,10 @@ class CurrentIndexCubit extends Cubit { final backgrounds = page.backgrounds.map(Renderer.fromInstance).toList(); await Future.wait(backgrounds .map((e) async => await e.setup(document, assetService, page))); - final utilities = UtilitiesRenderer(state.settingsCubit.state.utilities); - await utilities.setup( - docState.data, - docState.assetService, - docState.page, - ); emit(state.copyWith( cameraViewport: state.cameraViewport.unbake( unbakedElements: renderers, backgrounds: backgrounds, - utilities: utilities, ))); } @@ -1048,24 +1031,10 @@ class CurrentIndexCubit extends Cubit { Future updateUtilities( {UtilitiesState? utilities, ViewOption? view}) async { var state = this.state; - final renderer = UtilitiesRenderer( - utilities ?? state.utilitiesState, view ?? state.viewOption); - if (utilities != null) { - var newSelection = - state.selection?.remove(state.cameraViewport.utilities.element); - if (newSelection == null && state.selection != null) { - newSelection = Selection.from(utilities); - } else if (newSelection != state.selection) { - newSelection = newSelection?.insert(renderer); - } - state = state.copyWith(selection: newSelection); - } state = state.copyWith( - cameraViewport: state.cameraViewport.withUtilities(renderer), + utilities: utilities ?? state.utilities, + viewOption: view ?? state.viewOption, ); - if (view != null) { - state = state.copyWith(viewOption: view); - } emit(state); if (utilities != null) { return state.settingsCubit.changeUtilities(utilities); @@ -1075,7 +1044,7 @@ class CurrentIndexCubit extends Cubit { void togglePin() => emit(state.copyWith(pinned: !state.pinned)); void move(Offset delta, [bool force = false]) { - final utilitiesState = state.utilitiesState; + final utilitiesState = state.utilities; if (utilitiesState.lockHorizontal && !force) { delta = Offset(0, delta.dy); } @@ -1089,7 +1058,7 @@ class CurrentIndexCubit extends Cubit { } void zoom(double delta, [Offset cursor = Offset.zero, bool force = false]) { - final utilitiesState = state.utilitiesState; + final utilitiesState = state.utilities; if (utilitiesState.lockZoom && !force) { delta = 1; } diff --git a/app/lib/cubits/current_index.freezed.dart b/app/lib/cubits/current_index.freezed.dart index 568a03205c24..8f4bc578a183 100644 --- a/app/lib/cubits/current_index.freezed.dart +++ b/app/lib/cubits/current_index.freezed.dart @@ -22,6 +22,7 @@ mixin _$CurrentIndex { SettingsCubit get settingsCubit => throw _privateConstructorUsedError; TransformCubit get transformCubit => throw _privateConstructorUsedError; NetworkingService get networkingService => throw _privateConstructorUsedError; + UtilitiesState get utilities => throw _privateConstructorUsedError; Handler? get temporaryHandler => throw _privateConstructorUsedError; List get foregrounds => throw _privateConstructorUsedError; Selection? get selection => throw _privateConstructorUsedError; @@ -76,6 +77,7 @@ abstract class $CurrentIndexCopyWith<$Res> { SettingsCubit settingsCubit, TransformCubit transformCubit, NetworkingService networkingService, + UtilitiesState utilities, Handler? temporaryHandler, List foregrounds, Selection? selection, @@ -103,6 +105,7 @@ abstract class $CurrentIndexCopyWith<$Res> { bool areaNavigatorExact, bool areaNavigatorAsk}); + $UtilitiesStateCopyWith<$Res> get utilities; $ViewOptionCopyWith<$Res> get viewOption; } @@ -127,6 +130,7 @@ class _$CurrentIndexCopyWithImpl<$Res, $Val extends CurrentIndex> Object? settingsCubit = null, Object? transformCubit = null, Object? networkingService = null, + Object? utilities = null, Object? temporaryHandler = freezed, Object? foregrounds = null, Object? selection = freezed, @@ -179,6 +183,10 @@ class _$CurrentIndexCopyWithImpl<$Res, $Val extends CurrentIndex> ? _value.networkingService : networkingService // ignore: cast_nullable_to_non_nullable as NetworkingService, + utilities: null == utilities + ? _value.utilities + : utilities // ignore: cast_nullable_to_non_nullable + as UtilitiesState, temporaryHandler: freezed == temporaryHandler ? _value.temporaryHandler : temporaryHandler // ignore: cast_nullable_to_non_nullable @@ -286,6 +294,16 @@ class _$CurrentIndexCopyWithImpl<$Res, $Val extends CurrentIndex> ) as $Val); } + /// Create a copy of CurrentIndex + /// with the given fields replaced by the non-null parameter values. + @override + @pragma('vm:prefer-inline') + $UtilitiesStateCopyWith<$Res> get utilities { + return $UtilitiesStateCopyWith<$Res>(_value.utilities, (value) { + return _then(_value.copyWith(utilities: value) as $Val); + }); + } + /// Create a copy of CurrentIndex /// with the given fields replaced by the non-null parameter values. @override @@ -312,6 +330,7 @@ abstract class _$$CurrentIndexImplCopyWith<$Res> SettingsCubit settingsCubit, TransformCubit transformCubit, NetworkingService networkingService, + UtilitiesState utilities, Handler? temporaryHandler, List foregrounds, Selection? selection, @@ -339,6 +358,8 @@ abstract class _$$CurrentIndexImplCopyWith<$Res> bool areaNavigatorExact, bool areaNavigatorAsk}); + @override + $UtilitiesStateCopyWith<$Res> get utilities; @override $ViewOptionCopyWith<$Res> get viewOption; } @@ -362,6 +383,7 @@ class __$$CurrentIndexImplCopyWithImpl<$Res> Object? settingsCubit = null, Object? transformCubit = null, Object? networkingService = null, + Object? utilities = null, Object? temporaryHandler = freezed, Object? foregrounds = null, Object? selection = freezed, @@ -414,6 +436,10 @@ class __$$CurrentIndexImplCopyWithImpl<$Res> ? _value.networkingService : networkingService // ignore: cast_nullable_to_non_nullable as NetworkingService, + utilities: null == utilities + ? _value.utilities + : utilities // ignore: cast_nullable_to_non_nullable + as UtilitiesState, temporaryHandler: freezed == temporaryHandler ? _value.temporaryHandler : temporaryHandler // ignore: cast_nullable_to_non_nullable @@ -527,7 +553,8 @@ class __$$CurrentIndexImplCopyWithImpl<$Res> class _$CurrentIndexImpl extends _CurrentIndex { const _$CurrentIndexImpl(this.index, this.handler, this.cameraViewport, this.settingsCubit, this.transformCubit, this.networkingService, - {this.temporaryHandler, + {this.utilities = const UtilitiesState(), + this.temporaryHandler, final List foregrounds = const [], this.selection, this.pinned = false, @@ -576,6 +603,9 @@ class _$CurrentIndexImpl extends _CurrentIndex { @override final NetworkingService networkingService; @override + @JsonKey() + final UtilitiesState utilities; + @override final Handler? temporaryHandler; final List _foregrounds; @override @@ -704,7 +734,7 @@ class _$CurrentIndexImpl extends _CurrentIndex { @override String toString() { - return 'CurrentIndex(index: $index, handler: $handler, cameraViewport: $cameraViewport, settingsCubit: $settingsCubit, transformCubit: $transformCubit, networkingService: $networkingService, temporaryHandler: $temporaryHandler, foregrounds: $foregrounds, selection: $selection, pinned: $pinned, temporaryForegrounds: $temporaryForegrounds, toggleableHandlers: $toggleableHandlers, networkingForegrounds: $networkingForegrounds, toggleableForegrounds: $toggleableForegrounds, cursor: $cursor, temporaryCursor: $temporaryCursor, temporaryState: $temporaryState, lastPosition: $lastPosition, pointers: $pointers, buttons: $buttons, location: $location, embedding: $embedding, saved: $saved, toolbar: $toolbar, temporaryToolbar: $temporaryToolbar, rendererStates: $rendererStates, temporaryRendererStates: $temporaryRendererStates, viewOption: $viewOption, hideUi: $hideUi, areaNavigatorCreate: $areaNavigatorCreate, areaNavigatorExact: $areaNavigatorExact, areaNavigatorAsk: $areaNavigatorAsk)'; + return 'CurrentIndex(index: $index, handler: $handler, cameraViewport: $cameraViewport, settingsCubit: $settingsCubit, transformCubit: $transformCubit, networkingService: $networkingService, utilities: $utilities, temporaryHandler: $temporaryHandler, foregrounds: $foregrounds, selection: $selection, pinned: $pinned, temporaryForegrounds: $temporaryForegrounds, toggleableHandlers: $toggleableHandlers, networkingForegrounds: $networkingForegrounds, toggleableForegrounds: $toggleableForegrounds, cursor: $cursor, temporaryCursor: $temporaryCursor, temporaryState: $temporaryState, lastPosition: $lastPosition, pointers: $pointers, buttons: $buttons, location: $location, embedding: $embedding, saved: $saved, toolbar: $toolbar, temporaryToolbar: $temporaryToolbar, rendererStates: $rendererStates, temporaryRendererStates: $temporaryRendererStates, viewOption: $viewOption, hideUi: $hideUi, areaNavigatorCreate: $areaNavigatorCreate, areaNavigatorExact: $areaNavigatorExact, areaNavigatorAsk: $areaNavigatorAsk)'; } /// Create a copy of CurrentIndex @@ -724,7 +754,8 @@ abstract class _CurrentIndex extends CurrentIndex { final SettingsCubit settingsCubit, final TransformCubit transformCubit, final NetworkingService networkingService, - {final Handler? temporaryHandler, + {final UtilitiesState utilities, + final Handler? temporaryHandler, final List foregrounds, final Selection? selection, final bool pinned, @@ -765,6 +796,8 @@ abstract class _CurrentIndex extends CurrentIndex { @override NetworkingService get networkingService; @override + UtilitiesState get utilities; + @override Handler? get temporaryHandler; @override List get foregrounds; diff --git a/app/lib/handlers/eraser.dart b/app/lib/handlers/eraser.dart index 4aa2617df7bc..983a47427fe9 100644 --- a/app/lib/handlers/eraser.dart +++ b/app/lib/handlers/eraser.dart @@ -38,7 +38,7 @@ class EraserHandler extends Handler { Future _changeElement(Offset position, EventContext context) async { final currentIndex = context.getCurrentIndex(); final transform = currentIndex.transformCubit.state; - final utilities = currentIndex.utilitiesState; + final utilities = currentIndex.utilities; final globalPos = transform.localToGlobal(position); final size = data.strokeWidth; final shouldErase = diff --git a/app/lib/handlers/grid.dart b/app/lib/handlers/grid.dart index 54076e8e424b..e0a4e5b028eb 100644 --- a/app/lib/handlers/grid.dart +++ b/app/lib/handlers/grid.dart @@ -1,6 +1,6 @@ part of 'handler.dart'; -class GridHandler extends Handler { +class GridHandler extends Handler with PointerManipulationHandler { GridHandler(super.data); @override @@ -13,6 +13,14 @@ class GridHandler extends Handler { SelectState onSelected(BuildContext context, [bool wasAdded = true]) { return SelectState.toggle; } + + @override + Offset getPointerPosition(Offset position, Size viewportSize) { + return Offset( + (position.dx / data.xSize).round() * data.xSize, + (position.dy / data.ySize).round() * data.ySize, + ); + } } class GridRenderer extends Renderer { diff --git a/app/lib/handlers/handler.dart b/app/lib/handlers/handler.dart index 6e14674fd7ba..c815d6cc5452 100644 --- a/app/lib/handlers/handler.dart +++ b/app/lib/handlers/handler.dart @@ -474,3 +474,19 @@ abstract class PastingHandler extends Handler { bool get currentlyPasting => _firstPos != null && _secondPos != null; } + +mixin PointerManipulationHandler on Handler { + Offset getPointerPosition(Offset position, Size viewportSize) { + return position; + } + + static Offset calculatePointerPosition( + CurrentIndex index, Offset position, Size viewportSize) { + return index.toggleableHandlers.values + .whereType() + .map((e) => e.getPointerPosition(position, viewportSize)) + .where((e) => e != position) + .firstOrNull ?? + position; + } +} diff --git a/app/lib/handlers/laser.dart b/app/lib/handlers/laser.dart index 3df076427f7e..8dca78ea04ab 100644 --- a/app/lib/handlers/laser.dart +++ b/app/lib/handlers/laser.dart @@ -111,8 +111,8 @@ class LaserHandler extends Handler with ColoredHandler { final state = bloc.state as DocumentLoadSuccess; final settings = context.read().state; final penOnlyInput = settings.penOnlyInput; - localPosition = - viewport.utilities.getPointerPosition(localPosition, currentIndexCubit); + localPosition = PointerManipulationHandler.calculatePointerPosition( + currentIndexCubit.state, localPosition, viewport.toSize()); if (penOnlyInput && kind != PointerDeviceKind.stylus) { return; } diff --git a/app/lib/handlers/path_eraser.dart b/app/lib/handlers/path_eraser.dart index 9c4682781f49..0089a017860f 100644 --- a/app/lib/handlers/path_eraser.dart +++ b/app/lib/handlers/path_eraser.dart @@ -43,7 +43,7 @@ class PathEraserHandler extends Handler { _currentPos = event.localPosition; final currentIndex = context.getCurrentIndex(); final transform = currentIndex.transformCubit.state; - final utilities = currentIndex.utilitiesState; + final utilities = currentIndex.utilities; final state = context.getState(); final globalPos = transform.localToGlobal(event.localPosition); final size = data.strokeWidth; diff --git a/app/lib/handlers/pen.dart b/app/lib/handlers/pen.dart index 85b401c0915e..78b9774f5dcd 100644 --- a/app/lib/handlers/pen.dart +++ b/app/lib/handlers/pen.dart @@ -81,8 +81,8 @@ class PenHandler extends Handler with ColoredHandler { final currentIndexCubit = context.read(); final viewport = currentIndexCubit.state.cameraViewport; final transform = context.read().state; - localPos = - viewport.utilities.getPointerPosition(localPos, currentIndexCubit); + localPos = PointerManipulationHandler.calculatePointerPosition( + currentIndexCubit.state, localPos, viewport.toSize()); final globalPos = transform.localToGlobal(localPos); if (!bloc.isInBounds(globalPos)) return; final state = bloc.state as DocumentLoadSuccess; diff --git a/app/lib/handlers/ruler.dart b/app/lib/handlers/ruler.dart index 58ca7c378d7a..8eceaee52f4d 100644 --- a/app/lib/handlers/ruler.dart +++ b/app/lib/handlers/ruler.dart @@ -1,9 +1,23 @@ part of 'handler.dart'; -class RulerHandler extends Handler { +Rect _getRulerRect(Size size, + [CameraTransform transform = const CameraTransform()]) { + const rulerSize = 100.0; + return Rect.fromLTWH( + transform.position.dx, + transform.position.dy + (size.height / 2 + -rulerSize / 2) / transform.size, + size.width / transform.size, + rulerSize / transform.size, + ); +} + +class RulerHandler extends Handler with PointerManipulationHandler { Offset _position = Offset.zero; double _rotation = 0; + Offset get position => _position; + double get rotation => _rotation; + RulerHandler(super.data); @override @@ -19,14 +33,44 @@ class RulerHandler extends Handler { return SelectState.toggle; } - void move(Offset offset, EventContext context) { - _position = offset; + void transform( + EventContext context, { + Offset? position, + double? rotation, + }) { + if (position != null) { + _position = position; + } + if (rotation != null) { + _rotation = rotation; + } context.refresh(); } - void rotate(double rotation, EventContext context) { - _rotation = rotation; - context.refresh(); + bool isPointerInside(Offset position, Size viewportSize) { + final rect = _getRulerRect(viewportSize); + // Check if the position is inside the ruler rect, consider rotation + final rotatedPosition = position.rotate(rect.center, -_rotation); + return rect.contains(rotatedPosition); + } + + @override + Offset getPointerPosition(Offset position, Size viewportSize) { + if (!isPointerInside(position, viewportSize)) { + return _position; + } + final rotatedPosition = position.rotate(_position, _rotation); + return Offset( + _position.dx + (rotatedPosition.dx - _position.dx).roundToDouble(), + _position.dy + (rotatedPosition.dy - _position.dy).roundToDouble(), + ); + } + + static RulerHandler? getFirstRuler( + CurrentIndex index, Offset position, Size viewportSize) { + return index.toggleableHandlers.values + .whereType() + .firstWhereOrNull((e) => e.isPointerInside(position, viewportSize)); } } @@ -41,17 +85,6 @@ class RulerRenderer extends Renderer { this.rotation = 0, }); - Rect getRulerRect(Size size, CameraTransform transform) { - const rulerSize = 100.0; - return Rect.fromLTWH( - transform.position.dx, - transform.position.dy + - (size.height / 2 + -rulerSize / 2) / transform.size, - size.width / transform.size, - rulerSize / transform.size, - ); - } - @override void build(Canvas canvas, Size size, NoteData document, DocumentPage page, DocumentInfo info, CameraTransform transform, @@ -69,7 +102,7 @@ class RulerRenderer extends Renderer { ..color = rulerBackgroundColor ..style = PaintingStyle.fill; final rulerForegroundPaint = Paint()..color = rulerForegroundColor; - final rulerRect = getRulerRect(size, transform); + final rulerRect = _getRulerRect(size, transform); // Calculate steps based on zoom level var steps = 50; diff --git a/app/lib/handlers/select.dart b/app/lib/handlers/select.dart index ceb2ea4395d0..d6ee6d5b0465 100644 --- a/app/lib/handlers/select.dart +++ b/app/lib/handlers/select.dart @@ -182,7 +182,7 @@ class SelectHandler extends Handler { if (_selectionManager.isTransforming) { return; } - final utilities = context.getCurrentIndex().utilitiesState; + final utilities = context.getCurrentIndex().utilities; final transform = context.getCameraTransform(); final globalPos = transform.localToGlobal(localPosition); final selectionRect = getSelectionRect(); @@ -285,19 +285,10 @@ class SelectHandler extends Handler { context.refresh(); } - double? _rulerRotation; - Offset? _rulerPosition; - @override bool onScaleStart(ScaleStartDetails details, EventContext context) { - final viewport = context.getCameraViewport(); - if (viewport.utilities - .hitRuler(details.localFocalPoint, viewport.toSize())) { - _rulerRotation = 0; - _rulerPosition = details.localFocalPoint; - return true; - } final currentIndex = context.getCurrentIndex(); + _ruler = false; if (currentIndex.buttons == kSecondaryMouseButton && currentIndex.temporaryHandler == null) { return false; @@ -323,39 +314,33 @@ class SelectHandler extends Handler { event.kind == PointerDeviceKind.mouse && event.buttons != kSecondaryMouseButton; - /*void _handleRuler(ScaleUpdateDetails details, EventContext context) { + bool _ruler = false; + + bool _handleRuler(ScaleUpdateDetails details, EventContext context) { + _ruler = true; final state = context.getState(); - if (state == null) return; - final viewport = context.getCameraViewport(); - var utilitiesState = viewport.utilities.element; - final currentRotation = details.rotation * 180 / pi * details.scale; - final delta = currentRotation - _rulerRotation!; - var angle = utilitiesState.rulerAngle + delta; + if (state == null) return false; + final ruler = RulerHandler.getFirstRuler(context.getCurrentIndex(), + details.localFocalPoint, context.viewportSize); + if (ruler == null) return false; + //final currentRotation = ruler.rotation * 180 / pi * details.scale; + final delta = details.rotation; + var angle = ruler.rotation + delta; while (angle < 0) { angle += 360; } angle %= 360; final currentPos = details.localFocalPoint; - utilitiesState = utilitiesState.copyWith( - rulerPosition: utilitiesState.rulerPosition + - Point( - currentPos.dx - _rulerPosition!.dx, - currentPos.dy - _rulerPosition!.dy, - ), - rulerAngle: angle, - ); - _rulerRotation = currentRotation; - _rulerPosition = currentPos; - context.getCurrentIndexCubit().updateUtilities(utilities: utilitiesState); - }*/ + ruler.transform(context, position: currentPos, rotation: angle); + return true; + } @override void onScaleUpdate(ScaleUpdateDetails details, EventContext context) { if (details.pointerCount > 1) return; final globalPos = context.getCameraTransform().localToGlobal(details.localFocalPoint); - if (_rulerRotation != null && _rulerPosition != null) { - //_handleRuler(details, context); + if (_handleRuler(details, context)) { return; } if (_selectionManager.isTransforming) { @@ -381,11 +366,10 @@ class SelectHandler extends Handler { @override void onScaleEnd(ScaleEndDetails details, EventContext context) async { - final utilities = context.getCurrentIndex().utilitiesState; + final utilities = context.getCurrentIndex().utilities; final rectangleSelection = _rectangleFreeSelection?.normalized(); final lassoSelection = _lassoFreeSelection; - if (_rulerRotation != null) { - _rulerRotation = null; + if (_ruler) { return; } final transformed = _submitTransform(context.getDocumentBloc()); diff --git a/app/lib/models/viewport.dart b/app/lib/models/viewport.dart index 06ab26f6bccd..801756fd6d33 100644 --- a/app/lib/models/viewport.dart +++ b/app/lib/models/viewport.dart @@ -10,7 +10,6 @@ import 'package:flutter/material.dart'; class CameraViewport extends Equatable { final ui.Image? image, belowLayerImage, aboveLayerImage; final List> backgrounds; - final UtilitiesRenderer utilities; final List> bakedElements, unbakedElements, visibleElements; @@ -19,7 +18,7 @@ class CameraViewport extends Equatable { final double scale; final double x, y; - const CameraViewport.unbaked(this.utilities, + const CameraViewport.unbaked( [this.backgrounds = const [], this.unbakedElements = const [], List>? visibleElements]) @@ -35,7 +34,7 @@ class CameraViewport extends Equatable { y = 0, visibleElements = visibleElements ?? unbakedElements; - const CameraViewport.baked(this.utilities, + const CameraViewport.baked( {this.backgrounds = const [], required this.image, this.belowLayerImage, @@ -80,7 +79,6 @@ class CameraViewport extends Equatable { CameraViewport withUnbaked(List> unbakedElements, [List>? backgrounds]) => CameraViewport.baked( - utilities, backgrounds: backgrounds ?? this.backgrounds, image: image, width: width, @@ -99,12 +97,10 @@ class CameraViewport extends Equatable { CameraViewport unbake({ List>? backgrounds, - UtilitiesRenderer? utilities, List>? unbakedElements, List>? visibleElements, }) => CameraViewport.unbaked( - utilities ?? this.utilities, backgrounds ?? this.backgrounds, unbakedElements ?? (List>.from(this.unbakedElements) @@ -129,7 +125,6 @@ class CameraViewport extends Equatable { double y = 0, }) => CameraViewport.baked( - utilities, backgrounds: backgrounds, image: image, width: width, @@ -146,7 +141,6 @@ class CameraViewport extends Equatable { ); CameraViewport withoutLayers() => CameraViewport.baked( - utilities, backgrounds: backgrounds, image: image, width: width, @@ -164,25 +158,6 @@ class CameraViewport extends Equatable { CameraViewport withBackgrounds(List> backgrounds) => CameraViewport.baked( - utilities, - backgrounds: backgrounds, - pixelRatio: pixelRatio, - image: image, - width: width, - height: height, - scale: scale, - bakedElements: bakedElements, - unbakedElements: unbakedElements, - x: x, - y: y, - visibleElements: visibleElements, - aboveLayerImage: aboveLayerImage, - belowLayerImage: belowLayerImage, - ); - - CameraViewport withUtilities(UtilitiesRenderer utilities) => - CameraViewport.baked( - utilities, backgrounds: backgrounds, pixelRatio: pixelRatio, image: image, @@ -211,7 +186,6 @@ class CameraViewport extends Equatable { y, pixelRatio, visibleElements, - utilities, aboveLayerImage, belowLayerImage, ]; diff --git a/app/lib/renderers/renderer.dart b/app/lib/renderers/renderer.dart index 4be18c784e7c..b85b0b69286d 100644 --- a/app/lib/renderers/renderer.dart +++ b/app/lib/renderers/renderer.dart @@ -22,7 +22,6 @@ import 'package:perfect_freehand/perfect_freehand.dart' as freehand; import 'package:phosphor_flutter/phosphor_flutter.dart'; import 'package:xml/xml.dart'; -import '../cubits/current_index.dart'; import '../cubits/transform.dart'; import '../helpers/xml.dart'; import '../models/label.dart'; @@ -40,7 +39,6 @@ part 'elements/path.dart'; part 'elements/pen.dart'; part 'elements/shape.dart'; part 'elements/svg.dart'; -part 'utilities.dart'; class DefaultHitCalculator extends HitCalculator { final Rect? rect; @@ -160,10 +158,6 @@ abstract class Renderer { } as Renderer; } - if (element is UtilitiesState) { - return UtilitiesRenderer(element) as Renderer; - } - throw Exception('Invalid instance type'); } diff --git a/app/lib/renderers/utilities.dart b/app/lib/renderers/utilities.dart deleted file mode 100644 index 45e71f241f7f..000000000000 --- a/app/lib/renderers/utilities.dart +++ /dev/null @@ -1,159 +0,0 @@ -part of 'renderer.dart'; - -class UtilitiesRenderer extends Renderer { - final ViewOption option; - - UtilitiesRenderer( - [super.element = const UtilitiesState(), - this.option = const ViewOption()]); - - Rect getRulerRect(Size size) { - const rulerSize = 100.0; - return Rect.fromLTWH( - -size.width, - -rulerSize / 2, - size.width * 2, - rulerSize, - ); - } - - bool hitRuler(Offset position, Size size) { - return false; - /*if (!element.rulerEnabled) return false; - final rulerRect = getRulerRect(size).translate( - size.width / 2 + element.rulerPosition.x, - size.height / 2 + element.rulerPosition.y); - return rulerRect.contains(position.rotate( - element.rulerPosition - .toOffset() - .translate(size.width / 2, size.height / 2), - -element.rulerAngle * pi / 180));*/ - } - - @override - void build(Canvas canvas, Size size, NoteData document, DocumentPage page, - DocumentInfo info, CameraTransform transform, - [ColorScheme? colorScheme, bool foreground = false]) { - /* - if (element.rulerEnabled) { - final rulerColor = colorScheme?.primary ?? Colors.grey; - final rulerBackgroundColor = - (colorScheme?.primaryContainer ?? Colors.grey).withAlpha(200); - final rulerForegroundColor = colorScheme?.onPrimary ?? Colors.white; - final rulerPaint = Paint() - ..color = rulerColor - ..strokeWidth = 1 - ..style = PaintingStyle.stroke - ..strokeJoin = StrokeJoin.round; - final rulerBackgroundPaint = Paint() - ..color = rulerBackgroundColor - ..style = PaintingStyle.fill; - final rulerForegroundPaint = Paint()..color = rulerForegroundColor; - final rulerRect = getRulerRect(size); - - // Calculate steps based on zoom level - var steps = 10; - - // Paint ruler background - canvas.save(); - canvas.translate(transform.position.dx, transform.position.dy); - canvas.scale(1 / transform.size, 1 / transform.size); - canvas.translate(size.width / 2 + element.rulerPosition.x, - size.height / 2 + element.rulerPosition.y); - canvas.rotate(element.rulerAngle * pi / 180); - canvas.drawRect(rulerRect, rulerBackgroundPaint); - canvas.drawLine( - Offset(rulerRect.left, rulerRect.top), - Offset(rulerRect.right, rulerRect.top), - rulerPaint, - ); - canvas.drawLine( - Offset(rulerRect.left, rulerRect.bottom), - Offset(rulerRect.right, rulerRect.bottom), - rulerPaint, - ); - - // Paint ruler lines - int x = 0; - var placeTextBottom = false; - while (x < size.width * 2) { - // Disable text for now - //final realX = x - (size.width / 2 ~/ steps) * steps; - final posX = - x + (transform.position.dx * transform.size) % steps - size.width; - canvas.drawLine( - Offset(posX, rulerRect.top), - Offset( - posX, - rulerRect.top + - (placeTextBottom - ? rulerRect.height / 8 - : rulerRect.height / 4)), - rulerForegroundPaint, - ); - /*if (realX >= 0 && realX < size.width) { - final textPainter = TextPainter( - textDirection: TextDirection.ltr, - textAlign: TextAlign.center, - text: TextSpan( - text: realX.toString(), - style: TextStyle(color: colorScheme?.onPrimary ?? Colors.white), - ), - ); - textPainter.layout(); - textPainter.paint( - canvas, - Offset( - posX - textPainter.width / 2, - rulerRect.top + - 10 + - (placeTextBottom - ? rulerRect.height / 8 - : rulerRect.height / 4))); - }*/ - placeTextBottom = !placeTextBottom; - x += steps; - } - canvas.restore(); - }*/ - } - - Offset getPointerPosition(Offset position, CurrentIndexCubit cubit) { - /* if (!element.rulerEnabled) */ return position; - /* final size = cubit.state.cameraViewport.toSize(); - final rulerRect = getRulerRect(size).translate( - size.width / 2 + element.rulerPosition.x, - size.height / 2 + element.rulerPosition.y); - final pivot = element.rulerPosition - .toOffset() - .translate(size.width / 2, size.height / 2); - final angle = element.rulerAngle * pi / 180; - - final rotatedPosition = position.rotate(pivot, -angle); - final firstHalf = - rulerRect.topLeft & Size(rulerRect.width, rulerRect.height / 2); - final secondHalf = firstHalf.translate(0, rulerRect.height / 2); - final firstHalfHit = firstHalf.contains(rotatedPosition); - final secondHalfHit = secondHalf.contains(rotatedPosition); - // If the pointer is in the first half of the ruler, set the y to the top - // If the pointer is in the second half of the ruler, set the y to the bottom - - if (firstHalfHit) { - return Offset(rotatedPosition.dx, rulerRect.top).rotate(pivot, angle); - } else if (secondHalfHit) { - return Offset(rotatedPosition.dx, rulerRect.bottom).rotate(pivot, angle); - } - return position; */ - } - - Offset getGridPosition(Offset position, DocumentPage page, DocumentInfo info, - CurrentIndexCubit cubit) { - /* if (!element.gridEnabled) */ return position; - /* final transform = cubit.state.transformCubit.state; - final localPosition = transform.globalToLocal(position); - final option = info.view; - final x = (localPosition.dx ~/ option.gridXSize) * option.gridXSize; - final y = (localPosition.dy ~/ option.gridYSize) * option.gridYSize; - return transform.localToGlobal(Offset(x, y)); */ - } -} diff --git a/app/lib/selections/utilities.dart b/app/lib/selections/file.dart similarity index 99% rename from app/lib/selections/utilities.dart rename to app/lib/selections/file.dart index 93f6ddad8175..c22fa438f6ed 100644 --- a/app/lib/selections/utilities.dart +++ b/app/lib/selections/file.dart @@ -1,7 +1,7 @@ part of 'selection.dart'; -class UtilitiesSelection extends Selection { - UtilitiesSelection(super.selected); +class FileSelection extends Selection { + FileSelection() : super([null]); @override IconGetter get icon => PhosphorIcons.wrench; @@ -20,7 +20,7 @@ class UtilitiesSelection extends Selection { return [ ...super.buildProperties(context), _UtilitiesView( - state: selected.first, + state: currentIndex.utilities, option: currentIndex.viewOption, onStateChanged: (state) => cubit.updateUtilities(utilities: state), onToolChanged: (option) => cubit.updateUtilities(view: option), diff --git a/app/lib/selections/selection.dart b/app/lib/selections/selection.dart index 0abf448a3bf3..562d4ad5650d 100644 --- a/app/lib/selections/selection.dart +++ b/app/lib/selections/selection.dart @@ -46,7 +46,7 @@ part 'properties/path.dart'; part 'properties/pen.dart'; part 'area.dart'; -part 'utilities.dart'; +part 'file.dart'; abstract class Selection { List _selected; @@ -65,8 +65,8 @@ abstract class Selection { if (selected is Area) { return AreaSelection([selected]) as Selection; } - if (selected is UtilitiesState) { - return UtilitiesSelection([selected]) as Selection; + if (selected == null) { + return FileSelection() as Selection; } throw UnsupportedError('Unsupported selection type: $T'); } diff --git a/app/lib/view_painter.dart b/app/lib/view_painter.dart index 119962f41875..eea11f3948e2 100644 --- a/app/lib/view_painter.dart +++ b/app/lib/view_painter.dart @@ -19,11 +19,10 @@ class ForegroundPainter extends CustomPainter { final List renderers; final CameraTransform transform; final Selection? selection; - final Renderer? tool; ForegroundPainter( this.renderers, this.document, this.page, this.info, this.colorScheme, - [this.transform = const CameraTransform(), this.selection, this.tool]); + [this.transform = const CameraTransform(), this.selection]); @override void paint(Canvas canvas, Size size) { @@ -60,10 +59,6 @@ class ForegroundPainter extends CustomPainter { */ _drawSelection(canvas, selection); } - if (tool != null) { - tool!.build( - canvas, size, document, page, info, transform, colorScheme, true); - } } void _drawSelection(Canvas canvas, ElementSelection selection) { @@ -83,7 +78,6 @@ class ForegroundPainter extends CustomPainter { oldDelegate.renderers != renderers || oldDelegate.transform != transform || oldDelegate.selection != selection || - oldDelegate.tool != tool || oldDelegate.colorScheme != colorScheme; } diff --git a/app/lib/views/edit.dart b/app/lib/views/edit.dart index c4d4d382ce76..5b37119c7c03 100644 --- a/app/lib/views/edit.dart +++ b/app/lib/views/edit.dart @@ -381,8 +381,7 @@ class _EditToolbarState extends State { false, onPressed: () { final cubit = context.read(); - final state = cubit.state.cameraViewport.utilities.element; - cubit.changeSelection(state); + cubit.changeSelection(null); }, ), if (windowState.fullScreen && @@ -396,7 +395,7 @@ class _EditToolbarState extends State { ), BlocBuilder( builder: (context, currentIndex) { - final utilitiesState = currentIndex.utilitiesState; + final utilitiesState = currentIndex.utilities; Widget buildButton( bool selected, UtilitiesState Function() update, diff --git a/app/lib/views/main.dart b/app/lib/views/main.dart index ba7fdd802e17..23a54188c2b6 100644 --- a/app/lib/views/main.dart +++ b/app/lib/views/main.dart @@ -152,7 +152,7 @@ class _ProjectPageState extends State { setState(() { _transformCubit = TransformCubit(); _currentIndexCubit = CurrentIndexCubit(settingsCubit, _transformCubit!, - CameraViewport.unbaked(UtilitiesRenderer()), embedding); + CameraViewport.unbaked(), embedding); _bloc = DocumentBloc( fileSystem, _currentIndexCubit!, @@ -264,7 +264,7 @@ class _ProjectPageState extends State { _currentIndexCubit = CurrentIndexCubit( settingsCubit, _transformCubit!, - CameraViewport.unbaked(UtilitiesRenderer(), backgrounds), + CameraViewport.unbaked(backgrounds), null, networkingService, ); @@ -283,7 +283,7 @@ class _ProjectPageState extends State { _currentIndexCubit = CurrentIndexCubit( settingsCubit, _transformCubit!, - CameraViewport.unbaked(UtilitiesRenderer()), + CameraViewport.unbaked(), null, networkingService, ); diff --git a/app/lib/views/view.dart b/app/lib/views/view.dart index e44e650f8965..145471afdeac 100644 --- a/app/lib/views/view.dart +++ b/app/lib/views/view.dart @@ -449,7 +449,6 @@ class _MainViewViewportState extends State Theme.of(context).colorScheme, frictionTransform, cubit.state.selection, - currentIndex.cameraViewport.utilities, ), painter: ViewPainter( state.data, From f9f46d279ddb8b00facfe105e6cb2744a4ccddb6 Mon Sep 17 00:00:00 2001 From: CodeDoctorDE Date: Mon, 30 Dec 2024 01:07:16 +0100 Subject: [PATCH 5/8] Fix ruler --- app/lib/handlers/laser.dart | 11 ++++---- app/lib/handlers/pen.dart | 11 ++++---- app/lib/handlers/ruler.dart | 54 +++++++++++++++++++++++------------- app/lib/handlers/select.dart | 14 +++++----- app/pubspec.lock | 16 +++++------ 5 files changed, 60 insertions(+), 46 deletions(-) diff --git a/app/lib/handlers/laser.dart b/app/lib/handlers/laser.dart index 8dca78ea04ab..494174bd07c8 100644 --- a/app/lib/handlers/laser.dart +++ b/app/lib/handlers/laser.dart @@ -97,22 +97,21 @@ class LaserHandler extends Handler with ColoredHandler { @override void onPointerUp(PointerUpEvent event, EventContext context) { addPoint(context.buildContext, event.pointer, event.localPosition, - event.pressure, event.kind); + context.viewportSize, event.pressure, event.kind); _submit(context.getDocumentBloc(), [event.pointer]); } void addPoint(BuildContext context, int pointer, Offset localPosition, - double pressure, PointerDeviceKind kind, + Size viewportSize, double pressure, PointerDeviceKind kind, {bool forceCreate = false}) { final bloc = context.read(); final currentIndexCubit = context.read(); - final viewport = currentIndexCubit.state.cameraViewport; final transform = context.read().state; final state = bloc.state as DocumentLoadSuccess; final settings = context.read().state; final penOnlyInput = settings.penOnlyInput; localPosition = PointerManipulationHandler.calculatePointerPosition( - currentIndexCubit.state, localPosition, viewport.toSize()); + currentIndexCubit.state, localPosition, viewportSize); if (penOnlyInput && kind != PointerDeviceKind.stylus) { return; } @@ -146,7 +145,7 @@ class LaserHandler extends Handler with ColoredHandler { return; } addPoint(context.buildContext, event.pointer, event.localPosition, - event.pressure, event.kind, + context.viewportSize, event.pressure, event.kind, forceCreate: true); } @@ -154,7 +153,7 @@ class LaserHandler extends Handler with ColoredHandler { Future onPointerMove( PointerMoveEvent event, EventContext context) async { addPoint(context.buildContext, event.pointer, event.localPosition, - event.pressure, event.kind); + context.viewportSize, event.pressure, event.kind); } @override diff --git a/app/lib/handlers/pen.dart b/app/lib/handlers/pen.dart index 78b9774f5dcd..3298f5ff6f0d 100644 --- a/app/lib/handlers/pen.dart +++ b/app/lib/handlers/pen.dart @@ -50,7 +50,7 @@ class PenHandler extends Handler with ColoredHandler { _positionCheckTimer?.cancel(); _positionCheckTimer = null; addPoint(context.buildContext, event.pointer, event.localPosition, - _getPressure(event), event.kind, + context.viewportSize, _getPressure(event), event.kind, refresh: false); submitElements(context.getDocumentBloc(), [event.pointer]); points.clear(); @@ -75,14 +75,13 @@ class PenHandler extends Handler with ColoredHandler { // Add a point to the element. void addPoint(BuildContext context, int pointer, Offset localPos, - double pressure, PointerDeviceKind kind, + Size viewportSize, double pressure, PointerDeviceKind kind, {bool refresh = true, bool shouldCreate = false}) { final bloc = context.read(); final currentIndexCubit = context.read(); - final viewport = currentIndexCubit.state.cameraViewport; final transform = context.read().state; localPos = PointerManipulationHandler.calculatePointerPosition( - currentIndexCubit.state, localPos, viewport.toSize()); + currentIndexCubit.state, localPos, viewportSize); final globalPos = transform.localToGlobal(localPos); if (!bloc.isInBounds(globalPos)) return; final state = bloc.state as DocumentLoadSuccess; @@ -125,7 +124,7 @@ class PenHandler extends Handler with ColoredHandler { } elements.remove(event.pointer); addPoint(context.buildContext, event.pointer, event.localPosition, - _getPressure(event), event.kind, + context.viewportSize, _getPressure(event), event.kind, shouldCreate: true); } @@ -152,7 +151,7 @@ class PenHandler extends Handler with ColoredHandler { }); // Call the addPoint function to add a point to the current brush stroke. addPoint(context.buildContext, event.pointer, event.localPosition, - _getPressure(event), event.kind); + context.viewportSize, _getPressure(event), event.kind); points.add(event.localPosition); } diff --git a/app/lib/handlers/ruler.dart b/app/lib/handlers/ruler.dart index 8eceaee52f4d..4f08fb06c58d 100644 --- a/app/lib/handlers/ruler.dart +++ b/app/lib/handlers/ruler.dart @@ -1,11 +1,12 @@ part of 'handler.dart'; -Rect _getRulerRect(Size size, +Rect _getRulerRect(Size size, Offset position, [CameraTransform transform = const CameraTransform()]) { const rulerSize = 100.0; return Rect.fromLTWH( - transform.position.dx, - transform.position.dy + (size.height / 2 + -rulerSize / 2) / transform.size, + transform.position.dx + position.dx / transform.size, + transform.position.dy + + (size.height / 2 + -rulerSize / 2 + position.dy) / transform.size, size.width / transform.size, rulerSize / transform.size, ); @@ -39,31 +40,45 @@ class RulerHandler extends Handler with PointerManipulationHandler { double? rotation, }) { if (position != null) { - _position = position; + _position += position; } if (rotation != null) { - _rotation = rotation; + _rotation += rotation; } context.refresh(); } bool isPointerInside(Offset position, Size viewportSize) { - final rect = _getRulerRect(viewportSize); + final rect = _getRulerRect(viewportSize, _position); // Check if the position is inside the ruler rect, consider rotation - final rotatedPosition = position.rotate(rect.center, -_rotation); + final rotatedPosition = position.rotate(rect.center, -_rotation * pi / 180); return rect.contains(rotatedPosition); } @override Offset getPointerPosition(Offset position, Size viewportSize) { if (!isPointerInside(position, viewportSize)) { - return _position; + return position; } - final rotatedPosition = position.rotate(_position, _rotation); - return Offset( - _position.dx + (rotatedPosition.dx - _position.dx).roundToDouble(), - _position.dy + (rotatedPosition.dy - _position.dy).roundToDouble(), - ); + final rulerRect = _getRulerRect(viewportSize, _position); + final pivot = rulerRect.center; + final angle = _rotation * pi / 180; + + final rotatedPosition = position.rotate(pivot, -angle); + final firstHalf = + rulerRect.topLeft & Size(rulerRect.width, rulerRect.height / 2); + final secondHalf = firstHalf.translate(0, rulerRect.height / 2); + final firstHalfHit = firstHalf.contains(rotatedPosition); + final secondHalfHit = secondHalf.contains(rotatedPosition); + // If the pointer is in the first half of the ruler, set the y to the top + // If the pointer is in the second half of the ruler, set the y to the bottom + + if (firstHalfHit) { + return Offset(rotatedPosition.dx, rulerRect.top).rotate(pivot, angle); + } else if (secondHalfHit) { + return Offset(rotatedPosition.dx, rulerRect.bottom).rotate(pivot, angle); + } + return position; } static RulerHandler? getFirstRuler( @@ -102,7 +117,7 @@ class RulerRenderer extends Renderer { ..color = rulerBackgroundColor ..style = PaintingStyle.fill; final rulerForegroundPaint = Paint()..color = rulerForegroundColor; - final rulerRect = _getRulerRect(size, transform); + final rulerRect = _getRulerRect(size, position, transform); // Calculate steps based on zoom level var steps = 50; @@ -121,12 +136,13 @@ class RulerRenderer extends Renderer { ); // Paint ruler lines - double x = 0; + int x = steps; var even = (transform.position.dx ~/ (steps / transform.size)) % 2 == 0; - while (x <= size.width / transform.size) { - final posX = x - + while (x <= size.width) { + final posX = x / transform.size - (transform.position.dx % (steps / transform.size)) + - transform.position.dx; + transform.position.dx + + position.dx / transform.size; canvas.drawLine( Offset(posX, rulerRect.top), Offset( @@ -136,7 +152,7 @@ class RulerRenderer extends Renderer { rulerForegroundPaint, ); even = !even; - x += steps / transform.size; + x += steps; } } } diff --git a/app/lib/handlers/select.dart b/app/lib/handlers/select.dart index d6ee6d5b0465..b6ad33936629 100644 --- a/app/lib/handlers/select.dart +++ b/app/lib/handlers/select.dart @@ -288,7 +288,9 @@ class SelectHandler extends Handler { @override bool onScaleStart(ScaleStartDetails details, EventContext context) { final currentIndex = context.getCurrentIndex(); - _ruler = false; + _ruler = RulerHandler.getFirstRuler(context.getCurrentIndex(), + details.localFocalPoint, context.viewportSize); + if (_ruler == null) return true; if (currentIndex.buttons == kSecondaryMouseButton && currentIndex.temporaryHandler == null) { return false; @@ -314,14 +316,12 @@ class SelectHandler extends Handler { event.kind == PointerDeviceKind.mouse && event.buttons != kSecondaryMouseButton; - bool _ruler = false; + RulerHandler? _ruler; bool _handleRuler(ScaleUpdateDetails details, EventContext context) { - _ruler = true; final state = context.getState(); if (state == null) return false; - final ruler = RulerHandler.getFirstRuler(context.getCurrentIndex(), - details.localFocalPoint, context.viewportSize); + final ruler = _ruler; if (ruler == null) return false; //final currentRotation = ruler.rotation * 180 / pi * details.scale; final delta = details.rotation; @@ -330,7 +330,7 @@ class SelectHandler extends Handler { angle += 360; } angle %= 360; - final currentPos = details.localFocalPoint; + final currentPos = details.focalPointDelta; ruler.transform(context, position: currentPos, rotation: angle); return true; } @@ -369,7 +369,7 @@ class SelectHandler extends Handler { final utilities = context.getCurrentIndex().utilities; final rectangleSelection = _rectangleFreeSelection?.normalized(); final lassoSelection = _lassoFreeSelection; - if (_ruler) { + if (_ruler != null) { return; } final transformed = _submitTransform(context.getDocumentBloc()); diff --git a/app/pubspec.lock b/app/pubspec.lock index 35e274ab270b..9b1d7d09cb92 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -458,18 +458,18 @@ packages: dependency: "direct main" description: name: flex_color_scheme - sha256: "90f4fe67b9561ae8a4af117df65a8ce9988624025667c54e6d304e65cff77d52" + sha256: "09bea5d776f694c5a67f2229f2aa500cc7cce369322dc6500ab01cf9ad1b4e1a" url: "https://pub.dev" source: hosted - version: "8.0.2" + version: "8.1.0" flex_seed_scheme: dependency: transitive description: name: flex_seed_scheme - sha256: "7639d2c86268eff84a909026eb169f008064af0fb3696a651b24b0fa24a40334" + sha256: d3ba3c5c92d2d79d45e94b4c6c71d01fac3c15017da1545880c53864da5dfeb0 url: "https://pub.dev" source: hosted - version: "3.4.1" + version: "3.5.0" flutter: dependency: "direct main" description: flutter @@ -1366,18 +1366,18 @@ packages: dependency: "direct main" description: name: super_clipboard - sha256: "687ef5d4ceb2cb1e0e36a4af37683936609f424f0767b46fee5fc312b0aeb595" + sha256: "5203c881d24033c3e6154c2ae01afd94e7f0a3201280373f28e540f1defa3f40" url: "https://pub.dev" source: hosted - version: "0.9.0-dev.5" + version: "0.9.0-dev.6" super_native_extensions: dependency: transitive description: name: super_native_extensions - sha256: "1cb6baecf529300ae7f59974bdc33a53b947ecc4ce374c00126df064c10e4e51" + sha256: "09ccc40c475e6f91770eaeb2553bf4803812d7beadc3759aa57d643370619c86" url: "https://pub.dev" source: hosted - version: "0.9.0-dev.5" + version: "0.9.0-dev.6" sync_http: dependency: transitive description: From 8aa4ba80b9f77324a9d9780573039991971f98c4 Mon Sep 17 00:00:00 2001 From: CodeDoctorDE Date: Mon, 30 Dec 2024 13:10:55 +0100 Subject: [PATCH 6/8] Reimplement ruler rotation and position change --- app/lib/handlers/ruler.dart | 53 ++++++++++++++++++++---------------- app/lib/handlers/select.dart | 24 ++++++++++++---- 2 files changed, 48 insertions(+), 29 deletions(-) diff --git a/app/lib/handlers/ruler.dart b/app/lib/handlers/ruler.dart index 4f08fb06c58d..745f88a92c05 100644 --- a/app/lib/handlers/ruler.dart +++ b/app/lib/handlers/ruler.dart @@ -14,7 +14,7 @@ Rect _getRulerRect(Size size, Offset position, class RulerHandler extends Handler with PointerManipulationHandler { Offset _position = Offset.zero; - double _rotation = 0; + double _rotation = 20; Offset get position => _position; double get rotation => _rotation; @@ -26,7 +26,7 @@ class RulerHandler extends Handler with PointerManipulationHandler { NoteData document, DocumentPage page, DocumentInfo info, [Area? currentArea]) => [ - RulerRenderer(data, position: _position, rotation: _rotation), + RulerRenderer(data, position: _position, rulerRotation: _rotation), ]; @override @@ -48,8 +48,13 @@ class RulerHandler extends Handler with PointerManipulationHandler { context.refresh(); } + Rect getRect(Size size, + [CameraTransform transform = const CameraTransform()]) { + return _getRulerRect(size, _position, transform); + } + bool isPointerInside(Offset position, Size viewportSize) { - final rect = _getRulerRect(viewportSize, _position); + final rect = getRect(viewportSize); // Check if the position is inside the ruler rect, consider rotation final rotatedPosition = position.rotate(rect.center, -_rotation * pi / 180); return rect.contains(rotatedPosition); @@ -60,7 +65,7 @@ class RulerHandler extends Handler with PointerManipulationHandler { if (!isPointerInside(position, viewportSize)) { return position; } - final rulerRect = _getRulerRect(viewportSize, _position); + final rulerRect = getRect(viewportSize); final pivot = rulerRect.center; final angle = _rotation * pi / 180; @@ -91,58 +96,54 @@ class RulerHandler extends Handler with PointerManipulationHandler { class RulerRenderer extends Renderer { final Offset position; - @override - final double rotation; + final double rulerRotation; RulerRenderer( super.element, { this.position = Offset.zero, - this.rotation = 0, + this.rulerRotation = 0, }); @override void build(Canvas canvas, Size size, NoteData document, DocumentPage page, DocumentInfo info, CameraTransform transform, [ColorScheme? colorScheme, bool foreground = false]) { + canvas.save(); + canvas.translate(transform.position.dx, transform.position.dy); + canvas.scale(1 / transform.size, 1 / transform.size); + var rulerRect = _getRulerRect(size, position); + final rulerCenter = rulerRect.center; + canvas.translate(rulerCenter.dx, rulerCenter.dy); + canvas.rotate(rulerRotation * pi / 180); + rulerRect = rulerRect.translate(-rulerCenter.dx, -rulerCenter.dy); final rulerColor = colorScheme?.primary ?? Colors.grey; final rulerBackgroundColor = element.color?.toColor() ?? (colorScheme?.primaryContainer ?? Colors.grey).withAlpha(200); final rulerForegroundColor = colorScheme?.onPrimary ?? Colors.white; final rulerPaint = Paint() ..color = rulerColor - ..strokeWidth = 1 / transform.size + ..strokeWidth = 1 ..style = PaintingStyle.stroke ..strokeJoin = StrokeJoin.round; final rulerBackgroundPaint = Paint() ..color = rulerBackgroundColor ..style = PaintingStyle.fill; final rulerForegroundPaint = Paint()..color = rulerForegroundColor; - final rulerRect = _getRulerRect(size, position, transform); // Calculate steps based on zoom level var steps = 50; // Paint ruler background canvas.drawRect(rulerRect, rulerBackgroundPaint); - canvas.drawLine( - Offset(rulerRect.left, rulerRect.top), - Offset(rulerRect.right, rulerRect.top), - rulerPaint, - ); - canvas.drawLine( - Offset(rulerRect.left, rulerRect.bottom), - Offset(rulerRect.right, rulerRect.bottom), - rulerPaint, - ); + canvas.drawRect(rulerRect, rulerPaint); // Paint ruler lines int x = steps; var even = (transform.position.dx ~/ (steps / transform.size)) % 2 == 0; - while (x <= size.width) { + while (x <= size.width * transform.size) { final posX = x / transform.size - - (transform.position.dx % (steps / transform.size)) + - transform.position.dx + - position.dx / transform.size; + (transform.position.dx % (steps / transform.size)) - + size.width / 2; canvas.drawLine( Offset(posX, rulerRect.top), Offset( @@ -154,5 +155,11 @@ class RulerRenderer extends Renderer { even = !even; x += steps; } + + canvas.rotate(-rulerRotation * pi / 180); + canvas.translate(-rulerCenter.dx, -rulerCenter.dy); + canvas.scale(transform.size, transform.size); + canvas.translate(-transform.position.dx, -transform.position.dy); + canvas.restore(); } } diff --git a/app/lib/handlers/select.dart b/app/lib/handlers/select.dart index b6ad33936629..e536a351d290 100644 --- a/app/lib/handlers/select.dart +++ b/app/lib/handlers/select.dart @@ -290,7 +290,8 @@ class SelectHandler extends Handler { final currentIndex = context.getCurrentIndex(); _ruler = RulerHandler.getFirstRuler(context.getCurrentIndex(), details.localFocalPoint, context.viewportSize); - if (_ruler == null) return true; + _rulerRotationStart = details.localFocalPoint; + if (_ruler != null) return true; if (currentIndex.buttons == kSecondaryMouseButton && currentIndex.temporaryHandler == null) { return false; @@ -317,32 +318,43 @@ class SelectHandler extends Handler { event.buttons != kSecondaryMouseButton; RulerHandler? _ruler; + Offset? _rulerRotationStart; bool _handleRuler(ScaleUpdateDetails details, EventContext context) { final state = context.getState(); if (state == null) return false; final ruler = _ruler; if (ruler == null) return false; - //final currentRotation = ruler.rotation * 180 / pi * details.scale; - final delta = details.rotation; - var angle = ruler.rotation + delta; + final rightClick = + (context.getCurrentIndex().buttons ?? 0) & kSecondaryMouseButton != 0; + var angle = details.rotation * 180 / pi; + var currentPos = Offset.zero; + if (details.rotation == 0 && rightClick) { + final rulerCenter = ruler.getRect(context.viewportSize).center; + var start = _rulerRotationStart ?? rulerCenter; + final startDelta = (start - rulerCenter).direction; + final currentDelta = (details.localFocalPoint - rulerCenter).direction; + angle = (currentDelta - startDelta) * 180 / pi - ruler.rotation; + } else { + currentPos = details.focalPointDelta; + } while (angle < 0) { angle += 360; } angle %= 360; - final currentPos = details.focalPointDelta; ruler.transform(context, position: currentPos, rotation: angle); return true; } @override void onScaleUpdate(ScaleUpdateDetails details, EventContext context) { - if (details.pointerCount > 1) return; + print("onScaleUpdate"); final globalPos = context.getCameraTransform().localToGlobal(details.localFocalPoint); if (_handleRuler(details, context)) { return; } + if (details.pointerCount > 1) return; if (_selectionManager.isTransforming) { _selectionManager.updateCurrentPosition(globalPos); context.refresh(); From 7546bd70a529793f9139722d54665f0a858ccd75 Mon Sep 17 00:00:00 2001 From: CodeDoctorDE Date: Mon, 30 Dec 2024 13:18:54 +0100 Subject: [PATCH 7/8] Improve multi toggle tool editing --- app/lib/handlers/handler.dart | 8 +++----- app/lib/handlers/ruler.dart | 9 +++++---- app/lib/handlers/select.dart | 1 - 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/app/lib/handlers/handler.dart b/app/lib/handlers/handler.dart index c815d6cc5452..2ba048e6a9c3 100644 --- a/app/lib/handlers/handler.dart +++ b/app/lib/handlers/handler.dart @@ -483,10 +483,8 @@ mixin PointerManipulationHandler on Handler { static Offset calculatePointerPosition( CurrentIndex index, Offset position, Size viewportSize) { return index.toggleableHandlers.values - .whereType() - .map((e) => e.getPointerPosition(position, viewportSize)) - .where((e) => e != position) - .firstOrNull ?? - position; + .whereType() + .fold(position, + (pos, handler) => handler.getPointerPosition(pos, viewportSize)); } } diff --git a/app/lib/handlers/ruler.dart b/app/lib/handlers/ruler.dart index 745f88a92c05..2021b1fdc747 100644 --- a/app/lib/handlers/ruler.dart +++ b/app/lib/handlers/ruler.dart @@ -7,14 +7,14 @@ Rect _getRulerRect(Size size, Offset position, transform.position.dx + position.dx / transform.size, transform.position.dy + (size.height / 2 + -rulerSize / 2 + position.dy) / transform.size, - size.width / transform.size, + size.width * 2 / transform.size, rulerSize / transform.size, ); } class RulerHandler extends Handler with PointerManipulationHandler { Offset _position = Offset.zero; - double _rotation = 20; + double _rotation = 0; Offset get position => _position; double get rotation => _rotation; @@ -140,10 +140,11 @@ class RulerRenderer extends Renderer { // Paint ruler lines int x = steps; var even = (transform.position.dx ~/ (steps / transform.size)) % 2 == 0; - while (x <= size.width * transform.size) { + while (x <= size.width * transform.size * 2) { final posX = x / transform.size - (transform.position.dx % (steps / transform.size)) - - size.width / 2; + size.width / 2 - + rulerRect.width / 4; canvas.drawLine( Offset(posX, rulerRect.top), Offset( diff --git a/app/lib/handlers/select.dart b/app/lib/handlers/select.dart index e536a351d290..f9d4a66ebe08 100644 --- a/app/lib/handlers/select.dart +++ b/app/lib/handlers/select.dart @@ -348,7 +348,6 @@ class SelectHandler extends Handler { @override void onScaleUpdate(ScaleUpdateDetails details, EventContext context) { - print("onScaleUpdate"); final globalPos = context.getCameraTransform().localToGlobal(details.localFocalPoint); if (_handleRuler(details, context)) { From 8dfd63aec1b1873d9d0e07d791bf5c50cff2a396 Mon Sep 17 00:00:00 2001 From: CodeDoctorDE Date: Mon, 30 Dec 2024 13:32:04 +0100 Subject: [PATCH 8/8] Fix selecting document settings --- app/lib/selections/file.dart | 6 +++--- app/lib/selections/selection.dart | 4 ++-- app/lib/views/edit.dart | 4 ++-- app/lib/visualizer/icon.dart | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/lib/selections/file.dart b/app/lib/selections/file.dart index c22fa438f6ed..5841452e65b6 100644 --- a/app/lib/selections/file.dart +++ b/app/lib/selections/file.dart @@ -1,7 +1,7 @@ part of 'selection.dart'; -class FileSelection extends Selection { - FileSelection() : super([null]); +class FileSelection extends Selection { + FileSelection(CurrentIndexCubit cubit) : super([cubit]); @override IconGetter get icon => PhosphorIcons.wrench; @@ -15,7 +15,7 @@ class FileSelection extends Selection { @override List buildProperties(BuildContext context) { - final cubit = context.read(); + final cubit = selected.first; final currentIndex = cubit.state; return [ ...super.buildProperties(context), diff --git a/app/lib/selections/selection.dart b/app/lib/selections/selection.dart index 562d4ad5650d..86573d5fa859 100644 --- a/app/lib/selections/selection.dart +++ b/app/lib/selections/selection.dart @@ -65,8 +65,8 @@ abstract class Selection { if (selected is Area) { return AreaSelection([selected]) as Selection; } - if (selected == null) { - return FileSelection() as Selection; + if (selected is CurrentIndexCubit) { + return FileSelection(selected) as Selection; } throw UnsupportedError('Unsupported selection type: $T'); } diff --git a/app/lib/views/edit.dart b/app/lib/views/edit.dart index 5b37119c7c03..d0b1ee267b76 100644 --- a/app/lib/views/edit.dart +++ b/app/lib/views/edit.dart @@ -377,11 +377,11 @@ class _EditToolbarState extends State { tooltip: AppLocalizations.of(context).tools, selectedIcon: const PhosphorIcon(PhosphorIconsFill.wrench), isSelected: currentIndex.selection?.selected - .any((element) => element is UtilitiesState) ?? + .any((element) => element is CurrentIndexCubit) ?? false, onPressed: () { final cubit = context.read(); - cubit.changeSelection(null); + cubit.changeSelection(cubit); }, ), if (windowState.fullScreen && diff --git a/app/lib/visualizer/icon.dart b/app/lib/visualizer/icon.dart index f148bad30731..253984c570bc 100644 --- a/app/lib/visualizer/icon.dart +++ b/app/lib/visualizer/icon.dart @@ -23,7 +23,7 @@ enum DisplayIcons { return recommended(tool).firstOrNull?.icon ?? PhosphorIcons.question; } - static List recommended(Object tool) { + static List recommended(Object? tool) { return switch (tool) { PenTool _ => [ DisplayIcons.pen,