feat: gezinme oklari (hover), ayarlar penceresi (zaman/isme gore siralama), yazdirma (printing paketi), title hover saydamligi; .deb guncellendi
This commit is contained in:
1 parent
185653f34e
commit
f3a5aeb604
12 files changed
+431
-38
No files matched your search
+207
-34
@@ -1,7 +1,11 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
import 'package:desktop_drop/desktop_drop.dart';
|
||||
import 'package:pdf/pdf.dart';
|
||||
import 'package:pdf/widgets.dart' as pw;
|
||||
import 'package:printing/printing.dart';
|
||||
import '../services/image_manager.dart';
|
||||
import '../services/file_handler.dart';
|
||||
import '../widgets/custom_title_bar.dart';
|
||||
@@ -20,6 +24,7 @@ class ViewerScreen extends StatefulWidget {
|
||||
class _ViewerScreenState extends State<ViewerScreen> with WindowListener {
|
||||
bool _hasImages = false;
|
||||
bool _isDragging = false;
|
||||
bool _controlsVisible = false;
|
||||
Timer? _saveDebounce;
|
||||
Rect _lastBounds = Rect.zero;
|
||||
|
||||
@@ -84,6 +89,112 @@ class _ViewerScreenState extends State<ViewerScreen> with WindowListener {
|
||||
}
|
||||
}
|
||||
|
||||
void _showSettings() {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => StatefulBuilder(
|
||||
builder: (context, setDialogState) => AlertDialog(
|
||||
backgroundColor: const Color(0xFF2a2a2a),
|
||||
title: const Text('Ayarlar',
|
||||
style: TextStyle(color: Colors.white, fontSize: 18)),
|
||||
content: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text('Sıralama',
|
||||
style: TextStyle(color: Colors.white70, fontSize: 13)),
|
||||
RadioListTile<bool>(
|
||||
value: false,
|
||||
groupValue: ImageManager.instance.sortByName,
|
||||
dense: true,
|
||||
activeColor: Colors.blueAccent,
|
||||
title: const Text('Zamana göre',
|
||||
style: TextStyle(color: Colors.white, fontSize: 14)),
|
||||
onChanged: (v) {
|
||||
ImageManager.instance.setSortByName(v!);
|
||||
setDialogState(() {});
|
||||
},
|
||||
),
|
||||
RadioListTile<bool>(
|
||||
value: true,
|
||||
groupValue: ImageManager.instance.sortByName,
|
||||
dense: true,
|
||||
activeColor: Colors.blueAccent,
|
||||
title: const Text('İsme göre',
|
||||
style: TextStyle(color: Colors.white, fontSize: 14)),
|
||||
onChanged: (v) {
|
||||
ImageManager.instance.setSortByName(v!);
|
||||
setDialogState(() {});
|
||||
},
|
||||
),
|
||||
const Divider(color: Colors.white24, height: 24),
|
||||
const Text('imajViewer', style: TextStyle(color: Colors.white, fontSize: 15)),
|
||||
const SizedBox(height: 4),
|
||||
const Text(
|
||||
'Ultra hafif ve hızlı resim görüntüleyici. '
|
||||
'Görselleri açın, gezinti için okları veya klavye tuşlarını kullanın.',
|
||||
style: TextStyle(color: Colors.white70, fontSize: 13, height: 1.5),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
const Text('Kısayollar',
|
||||
style: TextStyle(color: Colors.white70, fontSize: 13)),
|
||||
const SizedBox(height: 4),
|
||||
const Text(
|
||||
'• Sol/Sağ ok — önceki/sonraki resim\n'
|
||||
'• Ctrl+O — dosya aç\n'
|
||||
'• Ctrl+Q — pencereyi kapat\n'
|
||||
'• Çift tık — doldur/sığdır geçişi\n'
|
||||
'• Sağ tık sürükle — kontrast\n'
|
||||
'• Shift+sağ tık — döndür\n'
|
||||
'• Ctrl+sağ tık — doygunluk\n'
|
||||
'• Ctrl+Shift+sağ tık — parlaklık',
|
||||
style: TextStyle(color: Colors.white70, fontSize: 13, height: 1.6),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton.icon(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
_printImage();
|
||||
},
|
||||
icon: const Icon(Icons.print, color: Colors.white70, size: 18),
|
||||
label: const Text('Yazdır',
|
||||
style: TextStyle(color: Colors.white70)),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Kapat', style: TextStyle(color: Colors.white70)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _printImage() async {
|
||||
final path = ImageManager.instance.currentImagePath;
|
||||
if (path.isEmpty) return;
|
||||
final bytes = await File(path).readAsBytes();
|
||||
final image = pw.MemoryImage(bytes);
|
||||
await Printing.layoutPdf(
|
||||
onLayout: (format) async {
|
||||
final doc = pw.Document();
|
||||
doc.addPage(
|
||||
pw.Page(
|
||||
pageFormat: PdfPageFormat.a4,
|
||||
build: (context) => pw.Center(
|
||||
child: pw.Image(image, fit: pw.BoxFit.contain),
|
||||
),
|
||||
),
|
||||
);
|
||||
return doc.save();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppShortcuts(
|
||||
@@ -98,41 +209,65 @@ class _ViewerScreenState extends State<ViewerScreen> with WindowListener {
|
||||
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)),
|
||||
],
|
||||
),
|
||||
),
|
||||
child: MouseRegion(
|
||||
onEnter: (_) => setState(() => _controlsVisible = true),
|
||||
onExit: (_) => setState(() => _controlsVisible = false),
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
child: Container(color: const Color(0xFF1a1a1a)),
|
||||
),
|
||||
),
|
||||
const CustomTitleBar(),
|
||||
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)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
CustomTitleBar(
|
||||
onSettings: _showSettings,
|
||||
visible: _controlsVisible,
|
||||
),
|
||||
if (_hasImages && _controlsVisible) ...[
|
||||
Positioned(
|
||||
left: 8,
|
||||
bottom: 8,
|
||||
child: _NavButton(
|
||||
icon: Icons.chevron_left,
|
||||
onTap: () => ImageManager.instance.previousImage(),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
right: 8,
|
||||
bottom: 8,
|
||||
child: _NavButton(
|
||||
icon: Icons.chevron_right,
|
||||
onTap: () => ImageManager.instance.nextImage(),
|
||||
),
|
||||
),
|
||||
],
|
||||
if (_isDragging)
|
||||
Positioned.fill(
|
||||
child: Container(
|
||||
@@ -152,6 +287,7 @@ class _ViewerScreenState extends State<ViewerScreen> with WindowListener {
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -159,3 +295,40 @@ class _ViewerScreenState extends State<ViewerScreen> with WindowListener {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _NavButton extends StatefulWidget {
|
||||
final IconData icon;
|
||||
final VoidCallback onTap;
|
||||
|
||||
const _NavButton({required this.icon, required this.onTap});
|
||||
|
||||
@override
|
||||
State<_NavButton> createState() => _NavButtonState();
|
||||
}
|
||||
|
||||
class _NavButtonState extends State<_NavButton> {
|
||||
bool _hovered = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: widget.onTap,
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
onEnter: (_) => setState(() => _hovered = true),
|
||||
onExit: (_) => setState(() => _hovered = false),
|
||||
child: Container(
|
||||
width: 36,
|
||||
height: 36,
|
||||
decoration: BoxDecoration(
|
||||
color: _hovered
|
||||
? Colors.red.withValues(alpha: 0.8)
|
||||
: Colors.black.withValues(alpha: 0.35),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(widget.icon, color: Colors.white, size: 22),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user