import 'dart:async'; import 'package:flutter/material.dart'; import 'package:window_manager/window_manager.dart'; import 'package:desktop_drop/desktop_drop.dart'; import '../services/image_manager.dart'; import '../services/file_handler.dart'; import '../widgets/custom_title_bar.dart'; import '../widgets/image_canvas.dart'; import '../widgets/window_resize_zones.dart'; import '../shortcuts/app_shortcuts.dart'; import '../services/window_persistence.dart'; class ViewerScreen extends StatefulWidget { final List initialFiles; const ViewerScreen({super.key, this.initialFiles = const []}); @override State createState() => _ViewerScreenState(); } class _ViewerScreenState extends State with WindowListener { bool _hasImages = false; bool _isDragging = false; Timer? _saveDebounce; Timer? _pollTimer; Rect _lastBounds = Rect.zero; @override void initState() { super.initState(); windowManager.addListener(this); ImageManager.instance.addListener(_onImageManagerChanged); if (widget.initialFiles.isNotEmpty) { WidgetsBinding.instance.addPostFrameCallback((_) { FileHandler.loadImagesFromPaths(widget.initialFiles); }); } // İlk kayıt + periyodik polling başlat WidgetsBinding.instance.addPostFrameCallback((_) async { await Future.delayed(const Duration(milliseconds: 200)); await _doSave(); _startPolling(); }); } void _startPolling() { _pollTimer?.cancel(); _pollTimer = Timer.periodic(const Duration(seconds: 2), (_) async { try { final bounds = await windowManager.getBounds(); if (bounds != _lastBounds) { _lastBounds = bounds; WindowPersistence.saveSync( position: bounds.topLeft, size: bounds.size, ); } } catch (_) {} }); } @override void dispose() { windowManager.removeListener(this); ImageManager.instance.removeListener(_onImageManagerChanged); _saveDebounce?.cancel(); _pollTimer?.cancel(); super.dispose(); } /// Universal event handler @override void onWindowEvent(String eventName) { switch (eventName) { case 'close': _doSave(); break; case 'resize': case 'move': _saveDebounce?.cancel(); _saveDebounce = Timer(const Duration(milliseconds: 300), () { _doSave(); }); break; } } void _onImageManagerChanged() { setState(() => _hasImages = ImageManager.instance.images.isNotEmpty); } Future _doSave() async { try { final pos = await windowManager.getPosition(); final size = await windowManager.getSize(); _lastBounds = Rect.fromLTWH(pos.dx, pos.dy, size.width, size.height); WindowPersistence.saveSync(position: pos, size: size); } catch (_) { // Silently ignore save errors } } @override Widget build(BuildContext context) { ImageManager.instance.buildContext = context; return AppShortcuts( child: Scaffold( backgroundColor: Colors.transparent, body: WindowResizeZones( child: DropTarget( onDragEntered: (_) => setState(() => _isDragging = true), onDragExited: (_) => setState(() => _isDragging = false), onDragDone: (details) { setState(() => _isDragging = false); FileHandler.loadImagesFromPaths( details.files.map((f) => f.path).toList()); }, child: Stack( children: [ Positioned( top: 28, left: 0, right: 0, bottom: 0, child: Container(color: const Color(0xFF1a1a1a)), ), Container( color: Colors.transparent, child: _hasImages ? ImageCanvas(filePath: ImageManager.instance.currentImagePath) : const Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.image, size: 64, color: Colors.white24), SizedBox(height: 16), Text('Resimleri buraya sürükleyin\nveya Ctrl+O ile açın', textAlign: TextAlign.center, style: TextStyle(color: Colors.white38, fontSize: 16)), ], ), ), ), const CustomTitleBar(), if (_isDragging) Positioned.fill( child: Container( color: Colors.blue.withValues(alpha: 0.1), child: Center( child: Container( padding: const EdgeInsets.all(32), decoration: BoxDecoration( color: Colors.black87, borderRadius: BorderRadius.circular(16), border: Border.all(color: Colors.blueAccent, width: 2), ), child: const Text('Bırakın', style: TextStyle(color: Colors.white, fontSize: 24)), ), ), ), ), ], ), ), ), ), ); } }