Files
imajviewer/lib/services/window_persistence.dart
T
Alhan 2cd9e6c357
Build & Release / build-linux (push) Has been cancelled
Build & Release / build-windows (push) Has been cancelled
Build & Release / create-release (push) Has been cancelled
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).
2026-10-12 22:33:43 +03:00

137 lines
4.2 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:screen_retriever/screen_retriever.dart';
/// Persists and restores window position and size.
class WindowPersistence {
static File get _configFile {
String dir;
if (Platform.isWindows) {
dir = '${Platform.environment['APPDATA'] ?? Platform.environment['USERPROFILE'] ?? '.'}/imajviewer';
} else {
dir = '${Platform.environment['HOME'] ?? '/tmp'}/.config/imajviewer';
}
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).
static void saveSync({
required Offset position,
required Size size,
}) {
_write({
'x': position.dx.toInt(),
'y': position.dy.toInt(),
'width': size.width.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.
static ({Offset position, Size size})? restore() {
final data = _read();
if (data == null) return null;
try {
final x = (data['x'] as num?)?.toDouble() ?? 0;
final y = (data['y'] as num?)?.toDouble() ?? 0;
final w = (data['width'] as num?)?.toDouble() ?? 1280;
final h = (data['height'] as num?)?.toDouble() ?? 800;
if (w < 200 || h < 200 || x < -1000 || y < -1000) return null;
return (
position: Offset(x, y),
size: Size(w, h),
);
} catch (_) {
return null;
}
}
/// Cascade: place new window to the right of the previous one.
/// Falls to the next row (row start) when it would overflow the screen,
/// and wraps to the top-left if the next row also overflows.
static Future<Offset> cascadePosition({
required Offset previousPosition,
required Size previousSize,
}) async {
try {
final displays = await screenRetriever.getAllDisplays();
// Önceki pencerenin bulunduğu monitörü bul (çoklu monitör desteği)
Display display = await screenRetriever.getPrimaryDisplay();
for (final d in displays) {
final pos = d.visiblePosition;
final size = d.visibleSize ?? d.size;
if (pos != null &&
previousPosition.dx >= pos.dx &&
previousPosition.dx < pos.dx + size.width &&
previousPosition.dy >= pos.dy &&
previousPosition.dy < pos.dy + size.height) {
display = d;
break;
}
}
final workLeft = display.visiblePosition?.dx ?? 0;
final workTop = display.visiblePosition?.dy ?? 0;
final workWidth = display.visibleSize?.width ?? display.size.width;
final workHeight = display.visibleSize?.height ?? display.size.height;
var x = previousPosition.dx + previousSize.width;
var y = previousPosition.dy;
if (x + previousSize.width > workLeft + workWidth) {
x = workLeft;
y = previousPosition.dy + previousSize.height;
}
if (y + previousSize.height > workTop + workHeight) {
x = workLeft;
y = workTop;
}
return Offset(x, y);
} catch (_) {
// Fallback: keep previous position if screen info is unavailable.
return previousPosition;
}
}
}