- 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
163 lines
5.4 KiB
Dart
163 lines
5.4 KiB
Dart
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<String> initialFiles;
|
||
const ViewerScreen({super.key, this.initialFiles = const []});
|
||
@override
|
||
State<ViewerScreen> createState() => _ViewerScreenState();
|
||
}
|
||
|
||
class _ViewerScreenState extends State<ViewerScreen> with WindowListener {
|
||
bool _hasImages = false;
|
||
bool _isDragging = false;
|
||
Timer? _saveDebounce;
|
||
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);
|
||
});
|
||
}
|
||
}
|
||
|
||
@override
|
||
void dispose() {
|
||
windowManager.removeListener(this);
|
||
ImageManager.instance.removeListener(_onImageManagerChanged);
|
||
_saveDebounce?.cancel();
|
||
super.dispose();
|
||
}
|
||
|
||
/// Universal event handler
|
||
@override
|
||
void onWindowEvent(String eventName) {
|
||
switch (eventName) {
|
||
case 'close':
|
||
// Senkron kayıt — async çağrı kapanışta tamamlanmayabilir
|
||
if (_lastBounds != Rect.zero) {
|
||
WindowPersistence.saveSync(
|
||
position: _lastBounds.topLeft,
|
||
size: _lastBounds.size,
|
||
);
|
||
}
|
||
break;
|
||
case 'resize':
|
||
case 'move':
|
||
// Son bilinen konumu hemen güncelle (kapanış senkron kaydı için)
|
||
windowManager.getBounds().then((b) => _lastBounds = b).catchError((_) {});
|
||
_saveDebounce?.cancel();
|
||
_saveDebounce = Timer(const Duration(milliseconds: 300), () {
|
||
_doSave();
|
||
});
|
||
break;
|
||
}
|
||
}
|
||
|
||
void _onImageManagerChanged() {
|
||
setState(() => _hasImages = ImageManager.instance.images.isNotEmpty);
|
||
}
|
||
|
||
Future<void> _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: 0,
|
||
left: 0,
|
||
right: 0,
|
||
bottom: 0,
|
||
child: Container(color: const Color(0xFF1a1a1a)),
|
||
),
|
||
Positioned(
|
||
top: 0,
|
||
left: 0,
|
||
right: 0,
|
||
bottom: 0,
|
||
child: 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)),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
}
|