Files
imajviewer/lib/services/window_persistence.dart
T

172 lines
5.4 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});
/// Tüm arayüzdeki hover vurgu rengi (ARGB). Anahtar yoksa varsayılan kırmızı.
static int get hoverColor => (_read()?['hoverColor'] as num?)?.toInt() ?? 0xFFF44336;
static void saveHoverColor(int value) => _write({'hoverColor': 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;
}
}
/// Work area (taskbar excluded) of the display containing the window,
/// in logical px. Returns null if screen info is unavailable.
static Future<Size?> workAreaSize({
required Offset windowPosition,
}) async {
try {
final displays = await screenRetriever.getAllDisplays();
// 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 &&
windowPosition.dx >= pos.dx &&
windowPosition.dx < pos.dx + size.width &&
windowPosition.dy >= pos.dy &&
windowPosition.dy < pos.dy + size.height) {
display = d;
break;
}
}
return display.visibleSize ?? display.size;
} catch (_) {
// Fallback: screen info unavailable → caller silently no-ops.
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;
}
}
}