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? _read() { final file = _configFile; if (!file.existsSync()) return null; try { return jsonDecode(file.readAsStringSync()) as Map; } catch (_) { return null; } } /// Verilen anahtarları yaz; dosyadaki diğer anahtarlar korunur. static void _write(Map values) { try { final file = _configFile; file.parent.createSync(recursive: true); final data = _read() ?? {}; 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 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 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; } } }