- file_handler/image_manager: jfif, jpe, wbmp uzantilari - .desktop + install.sh MimeType: image/vnd.wap.wbmp eklendi - main.dart: yeni pencere oncekinin saginda acilir (screen_retriever ile ekran tespiti) - viewer_screen: 2sn polling kaldirildi, konum kaydi tek akisa oturtuldu (acilis + move/resize debounce + close senkron) - dist/rebuild-deb.sh: .desktop kaynaktan kopyalanir (MimeType/StartupWMClass guncel kalir) - .deb guncellendi, docs: session-2026-08-03 + plan dokumanlari
90 lines
2.8 KiB
Dart
90 lines
2.8 KiB
Dart
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');
|
|
}
|
|
|
|
/// Save window rect synchronously (safe to call before destroy).
|
|
static void saveSync({
|
|
required Offset position,
|
|
required Size size,
|
|
}) {
|
|
final file = _configFile;
|
|
file.parent.createSync(recursive: true);
|
|
file.writeAsStringSync(jsonEncode({
|
|
'x': position.dx.toInt(),
|
|
'y': position.dy.toInt(),
|
|
'width': size.width.toInt(),
|
|
'height': size.height.toInt(),
|
|
}));
|
|
}
|
|
|
|
/// Restore saved window rect. Returns null if no saved config.
|
|
static ({Offset position, Size size})? restore() {
|
|
final file = _configFile;
|
|
if (!file.existsSync()) return null;
|
|
|
|
try {
|
|
final data = jsonDecode(file.readAsStringSync()) as Map<String, dynamic>;
|
|
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 display = await screenRetriever.getPrimaryDisplay();
|
|
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;
|
|
}
|
|
}
|
|
}
|