diff --git a/api/lib/src/models/utilities.dart b/api/lib/src/models/utilities.dart index cc0761b1bab5..ab917cb647d6 100644 --- a/api/lib/src/models/utilities.dart +++ b/api/lib/src/models/utilities.dart @@ -15,6 +15,7 @@ sealed class UtilitiesState with _$UtilitiesState { @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, diff --git a/api/lib/src/models/utilities.freezed.dart b/api/lib/src/models/utilities.freezed.dart index 6dee06e031f7..04f31f60abde 100644 --- a/api/lib/src/models/utilities.freezed.dart +++ b/api/lib/src/models/utilities.freezed.dart @@ -26,6 +26,7 @@ mixin _$UtilitiesState { 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; @@ -53,6 +54,7 @@ abstract class $UtilitiesStateCopyWith<$Res> { bool lockVertical, bool rulerEnabled, bool gridEnabled, + bool fullSelection, @DoublePointJsonConverter() Point rulerPosition, double rulerAngle}); } @@ -78,6 +80,7 @@ class _$UtilitiesStateCopyWithImpl<$Res, $Val extends UtilitiesState> Object? lockVertical = null, Object? rulerEnabled = null, Object? gridEnabled = null, + Object? fullSelection = null, Object? rulerPosition = null, Object? rulerAngle = null, }) { @@ -106,6 +109,10 @@ class _$UtilitiesStateCopyWithImpl<$Res, $Val extends UtilitiesState> ? _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 @@ -133,6 +140,7 @@ abstract class _$$UtilitiesStateImplCopyWith<$Res> bool lockVertical, bool rulerEnabled, bool gridEnabled, + bool fullSelection, @DoublePointJsonConverter() Point rulerPosition, double rulerAngle}); } @@ -156,6 +164,7 @@ class __$$UtilitiesStateImplCopyWithImpl<$Res> Object? lockVertical = null, Object? rulerEnabled = null, Object? gridEnabled = null, + Object? fullSelection = null, Object? rulerPosition = null, Object? rulerAngle = null, }) { @@ -184,6 +193,10 @@ class __$$UtilitiesStateImplCopyWithImpl<$Res> ? _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 @@ -206,6 +219,7 @@ class _$UtilitiesStateImpl implements _UtilitiesState { this.lockVertical = false, this.rulerEnabled = false, this.gridEnabled = false, + this.fullSelection = false, @DoublePointJsonConverter() this.rulerPosition = const Point(0.0, 0.0), this.rulerAngle = 0}); @@ -232,6 +246,9 @@ class _$UtilitiesStateImpl implements _UtilitiesState { final bool gridEnabled; @override @JsonKey() + final bool fullSelection; + @override + @JsonKey() @DoublePointJsonConverter() final Point rulerPosition; @override @@ -240,7 +257,7 @@ class _$UtilitiesStateImpl implements _UtilitiesState { @override String toString() { - return 'UtilitiesState(lockLayer: $lockLayer, lockZoom: $lockZoom, lockHorizontal: $lockHorizontal, lockVertical: $lockVertical, rulerEnabled: $rulerEnabled, gridEnabled: $gridEnabled, rulerPosition: $rulerPosition, rulerAngle: $rulerAngle)'; + return 'UtilitiesState(lockLayer: $lockLayer, lockZoom: $lockZoom, lockHorizontal: $lockHorizontal, lockVertical: $lockVertical, rulerEnabled: $rulerEnabled, gridEnabled: $gridEnabled, fullSelection: $fullSelection, rulerPosition: $rulerPosition, rulerAngle: $rulerAngle)'; } @override @@ -260,6 +277,8 @@ class _$UtilitiesStateImpl implements _UtilitiesState { 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) || @@ -276,6 +295,7 @@ class _$UtilitiesStateImpl implements _UtilitiesState { lockVertical, rulerEnabled, gridEnabled, + fullSelection, rulerPosition, rulerAngle); @@ -304,6 +324,7 @@ abstract class _UtilitiesState implements UtilitiesState { final bool lockVertical, final bool rulerEnabled, final bool gridEnabled, + final bool fullSelection, @DoublePointJsonConverter() final Point rulerPosition, final double rulerAngle}) = _$UtilitiesStateImpl; @@ -323,6 +344,8 @@ abstract class _UtilitiesState implements UtilitiesState { @override bool get gridEnabled; @override + bool get fullSelection; + @override @DoublePointJsonConverter() Point get rulerPosition; @override diff --git a/api/lib/src/models/utilities.g.dart b/api/lib/src/models/utilities.g.dart index 9f0f0e323325..148308b5d212 100644 --- a/api/lib/src/models/utilities.g.dart +++ b/api/lib/src/models/utilities.g.dart @@ -14,6 +14,7 @@ _$UtilitiesStateImpl _$$UtilitiesStateImplFromJson(Map json) => 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() @@ -30,6 +31,7 @@ Map _$$UtilitiesStateImplToJson( 'lockVertical': instance.lockVertical, 'rulerEnabled': instance.rulerEnabled, 'gridEnabled': instance.gridEnabled, + 'fullSelection': instance.fullSelection, 'rulerPosition': const DoublePointJsonConverter().toJson(instance.rulerPosition), 'rulerAngle': instance.rulerAngle, diff --git a/app/lib/bloc/document_bloc.dart b/app/lib/bloc/document_bloc.dart index 6c1ffcfa8104..b041a5964667 100644 --- a/app/lib/bloc/document_bloc.dart +++ b/app/lib/bloc/document_bloc.dart @@ -269,7 +269,7 @@ class DocumentBloc extends ReplayBloc { } else { final rect = renderer?.rect; if (rect != null) { - final hits = (await rayCastRect(rect, useLayer: false)) + final hits = (await rayCastRect(rect, useLayer: false, full: false)) .map((e) => e.element) .toList(); final hitIndex = hits.indexOf(renderer!.element); @@ -1008,11 +1008,13 @@ class DocumentBloc extends ReplayBloc { Rect rect, { CameraTransform? transform, required bool useLayer, + bool? full, }) async { final state = this.state; if (state is! DocumentLoadSuccess) return {}; transform ??= state.currentIndexCubit.state.transformCubit.state; final renderers = state.cameraViewport.visibleElements; + full ??= state.cameraViewport.utilities.element.fullSelection; return compute( _executeRayCast, _RayCastParams( @@ -1021,6 +1023,7 @@ class DocumentBloc extends ReplayBloc { rect, transform.size, useLayer ? state.currentLayer : null, + full, ), ).then((value) => value.map((e) => renderers[e]).toSet()); } @@ -1029,11 +1032,13 @@ class DocumentBloc extends ReplayBloc { List points, { CameraTransform? transform, required bool useLayer, + bool? full, }) async { final state = this.state; if (state is! DocumentLoadSuccess) return {}; final renderers = state.cameraViewport.visibleElements; transform ??= state.currentIndexCubit.state.transformCubit.state; + full ??= state.cameraViewport.utilities.element.fullSelection; return compute( _executeRayCastPolygon, _RayCastPolygonParams( @@ -1042,6 +1047,7 @@ class DocumentBloc extends ReplayBloc { points, transform.size, useLayer ? state.currentLayer : null, + full, ), ).then((value) => value.map((e) => renderers[e]).toSet()); } @@ -1065,6 +1071,7 @@ class _RayCastParams { final Rect rect; final double size; final String? layer; + final bool full; const _RayCastParams( this.invisibleLayers, @@ -1072,6 +1079,7 @@ class _RayCastParams { this.rect, this.size, this.layer, + this.full, ); } @@ -1082,7 +1090,7 @@ Set _executeRayCast(_RayCastParams params) { .entries .where((e) => !params.invisibleLayers.contains(e.value.element.layer)) .where((e) => - e.value.hitCalc.hit(rect) && + e.value.hitCalc.hit(rect, full: params.full) && (params.layer == null || e.value.element.layer == params.layer)) .map((e) => e.key) .toSet(); @@ -1094,9 +1102,10 @@ class _RayCastPolygonParams { final List polygon; final double size; final String? layer; + final bool full; const _RayCastPolygonParams(this.invisibleLayers, this.renderers, - this.polygon, this.size, this.layer); + this.polygon, this.size, this.layer, this.full); } Set _executeRayCastPolygon(_RayCastPolygonParams params) { @@ -1105,7 +1114,7 @@ Set _executeRayCastPolygon(_RayCastPolygonParams params) { .entries .where((e) => !params.invisibleLayers.contains(e.value.element.layer)) .where((e) => - e.value.hitCalc.hitPolygon(params.polygon) && + e.value.hitCalc.hitPolygon(params.polygon, full: params.full) && (params.layer == null || e.value.element.layer == params.layer)) .map((e) => e.key) .toSet(); diff --git a/app/lib/l10n/app_en.arb b/app/lib/l10n/app_en.arb index 08ac3a9b9f4e..522dea879eac 100644 --- a/app/lib/l10n/app_en.arb +++ b/app/lib/l10n/app_en.arb @@ -613,5 +613,5 @@ "deselect": "Deselect", "changeLayer": "Change layer", "fullSelection": "Full selection", - "fullSelectionDescription": "When enabled, elements must be fully contained within the selection box to be selected. If disabled, elements can be selected by touching the selection box, even if only partially." + "fullSelectionDescription": "Requires elements to be fully within the selection box for selection." } diff --git a/app/lib/selections/utilities.dart b/app/lib/selections/utilities.dart index 530cfc45b73f..0127799fd367 100644 --- a/app/lib/selections/utilities.dart +++ b/app/lib/selections/utilities.dart @@ -315,6 +315,15 @@ class _UtilitiesViewState extends State<_UtilitiesView> context.read().bake(); }, ), + const SizedBox(height: 8), + CheckboxListTile( + value: widget.state.fullSelection, + onChanged: (value) => widget.onStateChanged( + widget.state.copyWith(fullSelection: value ?? false)), + title: Text(AppLocalizations.of(context).fullSelection), + subtitle: Text(AppLocalizations.of(context) + .fullSelectionDescription), + ), ], ), ][_tabController.index]), diff --git a/docs/package.json b/docs/package.json index ae0090452b1b..e1c7d8d08bed 100644 --- a/docs/package.json +++ b/docs/package.json @@ -12,7 +12,7 @@ "dependencies": { "@astrojs/check": "^0.9.3", "@astrojs/react": "^3.6.2", - "@astrojs/starlight": "^0.27.0", + "@astrojs/starlight": "^0.27.1", "@phosphor-icons/react": "^2.1.7", "@types/react": "^18.3.5", "@types/react-dom": "^18.3.0", diff --git a/docs/pnpm-lock.yaml b/docs/pnpm-lock.yaml index b18fac355f90..c923ddba9cb2 100644 --- a/docs/pnpm-lock.yaml +++ b/docs/pnpm-lock.yaml @@ -15,8 +15,8 @@ importers: specifier: ^3.6.2 version: 3.6.2(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vite@5.4.3(sass@1.78.0)(terser@5.31.6)) '@astrojs/starlight': - specifier: ^0.27.0 - version: 0.27.0(astro@4.15.4(rollup@4.21.2)(sass@1.78.0)(terser@5.31.6)(typescript@5.5.4)) + specifier: ^0.27.1 + version: 0.27.1(astro@4.15.4(rollup@4.21.2)(sass@1.78.0)(terser@5.31.6)(typescript@5.5.4)) '@phosphor-icons/react': specifier: ^2.1.7 version: 2.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -107,8 +107,8 @@ packages: '@astrojs/sitemap@3.1.6': resolution: {integrity: sha512-1Qp2NvAzVImqA6y+LubKi1DVhve/hXXgFvB0szxiipzh7BvtuKe4oJJ9dXSqaubaTkt4nMa6dv6RCCAYeB6xaQ==} - '@astrojs/starlight@0.27.0': - resolution: {integrity: sha512-W06VHc4VVohKE6g1CytSF64WQ97nv/hlHHf2+vKGh/+I9nDdom/M7RXqkyLx7FBlLCbgsSvLYZUkh8cQ/mMOzQ==} + '@astrojs/starlight@0.27.1': + resolution: {integrity: sha512-L2hEgN/Tk7tfBDeaqUOgOpey5NcUL78FuQa06iNxyZ6RjyYyuXSniOoFxZYIo5PpY9O1dLdK22PkZyCDpO729g==} peerDependencies: astro: ^4.8.6 @@ -2412,7 +2412,7 @@ snapshots: stream-replace-string: 2.0.0 zod: 3.23.8 - '@astrojs/starlight@0.27.0(astro@4.15.4(rollup@4.21.2)(sass@1.78.0)(terser@5.31.6)(typescript@5.5.4))': + '@astrojs/starlight@0.27.1(astro@4.15.4(rollup@4.21.2)(sass@1.78.0)(terser@5.31.6)(typescript@5.5.4))': dependencies: '@astrojs/mdx': 3.1.5(astro@4.15.4(rollup@4.21.2)(sass@1.78.0)(terser@5.31.6)(typescript@5.5.4)) '@astrojs/sitemap': 3.1.6 diff --git a/metadata/en-US/changelogs/112.txt b/metadata/en-US/changelogs/112.txt index e6a4ef2f24bb..1836899d9d42 100644 --- a/metadata/en-US/changelogs/112.txt +++ b/metadata/en-US/changelogs/112.txt @@ -1,3 +1,4 @@ +* Add full selection mode * Save utilities states after restart * Ignore invalid files with invalid syntax * Fix elements not being selected after moving