diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c125f729..89c02e39d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. ### Added +- optional recycle bin to keep deleted items for 30 days - Viewer: actions to copy/move to album - Indonesian translation (thanks MeFinity) @@ -13,6 +14,8 @@ All notable changes to this project will be documented in this file. - Viewer: action menu reorganization - Viewer: `Export` action renamed to `Convert` +- Viewer: actual size zoom level respects device pixel ratio +- Viewer: allow zooming out small items to actual size - Collection: improved performance for sort/group by name - load previous top items on startup - upgraded Flutter to stable v2.10.1 diff --git a/lib/l10n/app_id.arb b/lib/l10n/app_id.arb index 655a29114..f25c952eb 100644 --- a/lib/l10n/app_id.arb +++ b/lib/l10n/app_id.arb @@ -378,7 +378,7 @@ "appExportSettings": "Pengaturan", "settingsSectionNavigation": "Navigasi", - "settingsHome": "Home", + "settingsHome": "Beranda", "settingsKeepScreenOnTile": "Biarkan layarnya menyala", "settingsKeepScreenOnTitle": "Biarkan Layarnya Menyala", "settingsDoubleBackExit": "Ketuk “kembali” dua kali untuk keluar", diff --git a/lib/widgets/common/magnifier/controller/controller.dart b/lib/widgets/common/magnifier/controller/controller.dart index 0e9d41e15..cdec420f7 100644 --- a/lib/widgets/common/magnifier/controller/controller.dart +++ b/lib/widgets/common/magnifier/controller/controller.dart @@ -71,6 +71,7 @@ class MagnifierController { position = position ?? this.position; scale = scale ?? this.scale; if (this.position == position && this.scale == scale) return; + assert((scale ?? 0) >= 0); previousState = currentState; _setState(MagnifierState( @@ -127,7 +128,7 @@ class MagnifierController { case ScaleState.covering: return _clamp(ScaleLevel.scaleForCovering(scaleBoundaries.viewportSize, scaleBoundaries.childSize)); case ScaleState.originalSize: - return _clamp(1.0); + return _clamp(scaleBoundaries.originalScale); default: return null; } diff --git a/lib/widgets/common/magnifier/core/core.dart b/lib/widgets/common/magnifier/core/core.dart index 214cb5089..da4ad3c5e 100644 --- a/lib/widgets/common/magnifier/core/core.dart +++ b/lib/widgets/common/magnifier/core/core.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'package:aves/widgets/common/magnifier/controller/controller.dart'; import 'package:aves/widgets/common/magnifier/controller/controller_delegate.dart'; import 'package:aves/widgets/common/magnifier/controller/state.dart'; @@ -132,7 +134,7 @@ class _MagnifierCoreState extends State with TickerProviderStateM updateScaleStateFromNewScale(newScale, ChangeSource.gesture); updateMultiple( - scale: newScale, + scale: max(0, newScale), position: newPosition, source: ChangeSource.gesture, ); diff --git a/lib/widgets/common/magnifier/scale/scale_boundaries.dart b/lib/widgets/common/magnifier/scale/scale_boundaries.dart index 85e54b0a8..986fc82ec 100644 --- a/lib/widgets/common/magnifier/scale/scale_boundaries.dart +++ b/lib/widgets/common/magnifier/scale/scale_boundaries.dart @@ -1,3 +1,4 @@ +import 'dart:math'; import 'dart:ui'; import 'package:aves/widgets/common/magnifier/controller/controller.dart'; @@ -41,11 +42,13 @@ class ScaleBoundaries extends Equatable { } } - double get minScale => _scaleForLevel(_minScale); + double get originalScale => 1.0 / window.devicePixelRatio; - double get maxScale => _scaleForLevel(_maxScale).clamp(minScale, double.infinity); + double get minScale => {_scaleForLevel(_minScale), originalScale, initialScale}.fold(double.infinity, min); - double get initialScale => _scaleForLevel(_initialScale).clamp(minScale, maxScale); + double get maxScale => {_scaleForLevel(_maxScale), originalScale, initialScale}.fold(0, max); + + double get initialScale => _scaleForLevel(_initialScale); Offset get _viewportCenter => viewportSize.center(Offset.zero); diff --git a/lib/widgets/common/thumbnail/decorated.dart b/lib/widgets/common/thumbnail/decorated.dart index f3a955b91..5c0b65f39 100644 --- a/lib/widgets/common/thumbnail/decorated.dart +++ b/lib/widgets/common/thumbnail/decorated.dart @@ -35,7 +35,7 @@ class DecoratedThumbnail extends StatelessWidget { ); child = Stack( - alignment: AlignmentDirectional.bottomStart, + fit: StackFit.passthrough, children: [ child, ThumbnailEntryOverlay(entry: entry), diff --git a/lib/widgets/common/thumbnail/overlay.dart b/lib/widgets/common/thumbnail/overlay.dart index ad3a74d44..39a4f60b9 100644 --- a/lib/widgets/common/thumbnail/overlay.dart +++ b/lib/widgets/common/thumbnail/overlay.dart @@ -38,11 +38,13 @@ class ThumbnailEntryOverlay extends StatelessWidget { if (entry.trashed && context.select((t) => t.showTrash)) TrashIcon(trashDaysLeft: entry.trashDaysLeft), ]; if (children.isEmpty) return const SizedBox(); - if (children.length == 1) return children.first; - return Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: children, + return Align( + alignment: AlignmentDirectional.bottomStart, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: children, + ), ); } } diff --git a/lib/widgets/viewer/overlay/minimap.dart b/lib/widgets/viewer/overlay/minimap.dart index 3a30626ff..42a5617ab 100644 --- a/lib/widgets/viewer/overlay/minimap.dart +++ b/lib/widgets/viewer/overlay/minimap.dart @@ -78,6 +78,8 @@ class MinimapPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { + if (entrySize.width <= 0 || entrySize.height <= 0) return; + final viewSize = entrySize * viewScale; if (viewSize.isEmpty) return; diff --git a/lib/widgets/viewer/visual/state.dart b/lib/widgets/viewer/visual/state.dart index efae82e5e..416400db2 100644 --- a/lib/widgets/viewer/visual/state.dart +++ b/lib/widgets/viewer/visual/state.dart @@ -1,15 +1,16 @@ -import 'package:flutter/foundation.dart'; +import 'package:equatable/equatable.dart'; import 'package:flutter/widgets.dart'; -class ViewState { +@immutable +class ViewState extends Equatable { final Offset position; final double? scale; final Size? viewportSize; static const ViewState zero = ViewState(Offset.zero, 0, null); - const ViewState(this.position, this.scale, this.viewportSize); - @override - String toString() => '$runtimeType#${shortHash(this)}{position=$position, scale=$scale, viewportSize=$viewportSize}'; + List get props => [position, scale, viewportSize]; + + const ViewState(this.position, this.scale, this.viewportSize); }