feat: persist fill/fit view mode in window.json (v1.5.0)
The double-click fill/fit mode was hardcoded (image_canvas: _isFilled = true) and reset on every image change, so it never survived a restart. It is now stored as "filled" in window.json and restored on launch and on image switch. WindowPersistence.saveSync() now merges with the existing file, otherwise the startup/move/resize geometry writes would drop the mode key. Also: version 1.5.0, CHANGELOG entry, dist/ ships imajviewer_1.5.0_amd64.deb (replaces 1.4.2).
This commit is contained in:
1 parent
b3bd540549
commit
2cd9e6c357
6 files changed
+50
-11
No files matched your search
+1
-1
@@ -49,7 +49,7 @@ app.*.map.json
|
|||||||
*.deb
|
*.deb
|
||||||
*.tar.gz
|
*.tar.gz
|
||||||
*.exe
|
*.exe
|
||||||
!dist/imajviewer_1.4.2_amd64.deb
|
!dist/imajviewer_1.5.0_amd64.deb
|
||||||
!dist/imajviewer_setup_1.1.0.exe
|
!dist/imajviewer_setup_1.1.0.exe
|
||||||
deb/usr/lib/imajviewer/
|
deb/usr/lib/imajviewer/
|
||||||
release/
|
release/
|
||||||
|
|||||||
@@ -4,6 +4,12 @@ All notable changes to imajViewer are documented in this file.
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [1.5.0] - 2026-10-12
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Fill/fit mode (double-click) is now persisted in `window.json` and restored on the next launch
|
||||||
|
and when switching images, instead of always resetting to fill.
|
||||||
|
|
||||||
## [1.4.2] - 2026-10-12
|
## [1.4.2] - 2026-10-12
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
Binary file not shown.
@@ -15,28 +15,59 @@ class WindowPersistence {
|
|||||||
return File('$dir/window.json');
|
return File('$dir/window.json');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Görüntü ekran modu: true = pencereyi kapla (cover), false = sığdır (contain).
|
||||||
|
/// Anahtar yoksa/bozuksa true (eski davranış).
|
||||||
|
static bool get filled {
|
||||||
|
final v = _read()?['filled'];
|
||||||
|
return v is bool ? v : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sadece modu günceller; x/y/w/h korunur.
|
||||||
|
static void saveFilled(bool value) => _write({'filled': value});
|
||||||
|
|
||||||
/// Save window rect synchronously (safe to call before destroy).
|
/// Save window rect synchronously (safe to call before destroy).
|
||||||
static void saveSync({
|
static void saveSync({
|
||||||
required Offset position,
|
required Offset position,
|
||||||
required Size size,
|
required Size size,
|
||||||
}) {
|
}) {
|
||||||
final file = _configFile;
|
_write({
|
||||||
file.parent.createSync(recursive: true);
|
|
||||||
file.writeAsStringSync(jsonEncode({
|
|
||||||
'x': position.dx.toInt(),
|
'x': position.dx.toInt(),
|
||||||
'y': position.dy.toInt(),
|
'y': position.dy.toInt(),
|
||||||
'width': size.width.toInt(),
|
'width': size.width.toInt(),
|
||||||
'height': size.height.toInt(),
|
'height': size.height.toInt(),
|
||||||
}));
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dosyayı oku (yok/bozuksa null).
|
||||||
|
static Map<String, dynamic>? _read() {
|
||||||
|
final file = _configFile;
|
||||||
|
if (!file.existsSync()) return null;
|
||||||
|
try {
|
||||||
|
return jsonDecode(file.readAsStringSync()) as Map<String, dynamic>;
|
||||||
|
} catch (_) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verilen anahtarları yaz; dosyadaki diğer anahtarlar korunur.
|
||||||
|
static void _write(Map<String, dynamic> values) {
|
||||||
|
try {
|
||||||
|
final file = _configFile;
|
||||||
|
file.parent.createSync(recursive: true);
|
||||||
|
final data = _read() ?? <String, dynamic>{};
|
||||||
|
data.addAll(values);
|
||||||
|
file.writeAsStringSync(jsonEncode(data));
|
||||||
|
} catch (_) {
|
||||||
|
// Disk hatası uygulamayı durdurmasın.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Restore saved window rect. Returns null if no saved config.
|
/// Restore saved window rect. Returns null if no saved config.
|
||||||
static ({Offset position, Size size})? restore() {
|
static ({Offset position, Size size})? restore() {
|
||||||
final file = _configFile;
|
final data = _read();
|
||||||
if (!file.existsSync()) return null;
|
if (data == null) return null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final data = jsonDecode(file.readAsStringSync()) as Map<String, dynamic>;
|
|
||||||
final x = (data['x'] as num?)?.toDouble() ?? 0;
|
final x = (data['x'] as num?)?.toDouble() ?? 0;
|
||||||
final y = (data['y'] as num?)?.toDouble() ?? 0;
|
final y = (data['y'] as num?)?.toDouble() ?? 0;
|
||||||
final w = (data['width'] as num?)?.toDouble() ?? 1280;
|
final w = (data['width'] as num?)?.toDouble() ?? 1280;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:flutter/scheduler.dart';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import '../l10n/app_localizations.dart';
|
import '../l10n/app_localizations.dart';
|
||||||
import '../services/crop_save_service.dart';
|
import '../services/crop_save_service.dart';
|
||||||
|
import '../services/window_persistence.dart';
|
||||||
|
|
||||||
class ImageCanvas extends StatefulWidget {
|
class ImageCanvas extends StatefulWidget {
|
||||||
final String filePath;
|
final String filePath;
|
||||||
@@ -15,7 +16,7 @@ class ImageCanvas extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ImageCanvasState extends State<ImageCanvas> {
|
class ImageCanvasState extends State<ImageCanvas> {
|
||||||
bool _isFilled = true;
|
bool _isFilled = WindowPersistence.filled;
|
||||||
final GlobalKey _imageKey = GlobalKey();
|
final GlobalKey _imageKey = GlobalKey();
|
||||||
double _tx = 0, _ty = 0, _sc = 1.0;
|
double _tx = 0, _ty = 0, _sc = 1.0;
|
||||||
Size _vpSize = Size.zero;
|
Size _vpSize = Size.zero;
|
||||||
@@ -199,7 +200,7 @@ class ImageCanvasState extends State<ImageCanvas> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _resetView() {
|
void _resetView() {
|
||||||
_tx = 0; _ty = 0; _sc = 1.0; _isFilled = true;
|
_tx = 0; _ty = 0; _sc = 1.0; _isFilled = WindowPersistence.filled;
|
||||||
_angle = 0; _contrast = 1.0; _saturation = 1.0; _brightness = 0;
|
_angle = 0; _contrast = 1.0; _saturation = 1.0; _brightness = 0;
|
||||||
_usingFullRes = false;
|
_usingFullRes = false;
|
||||||
_imageSize = Size.zero;
|
_imageSize = Size.zero;
|
||||||
@@ -209,6 +210,7 @@ class ImageCanvasState extends State<ImageCanvas> {
|
|||||||
void _toggleFillFit() {
|
void _toggleFillFit() {
|
||||||
_isFilled = !_isFilled; _tx = 0; _ty = 0; _sc = 1.0;
|
_isFilled = !_isFilled; _tx = 0; _ty = 0; _sc = 1.0;
|
||||||
_angle = 0; _contrast = 1.0; _saturation = 1.0; _brightness = 0;
|
_angle = 0; _contrast = 1.0; _saturation = 1.0; _brightness = 0;
|
||||||
|
WindowPersistence.saveFilled(_isFilled);
|
||||||
_markRenderDirty();
|
_markRenderDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
name: imajviewer
|
name: imajviewer
|
||||||
description: "Ultra-lightweight image viewer for Linux and Windows."
|
description: "Ultra-lightweight image viewer for Linux and Windows."
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
version: 1.4.2+10
|
version: 1.5.0+11
|
||||||
|
|
||||||
msix_config:
|
msix_config:
|
||||||
display_name: ImajViewer
|
display_name: ImajViewer
|
||||||
|
|||||||
Reference in new issue
Block a user