210 lines
7.8 KiB
Markdown
210 lines
7.8 KiB
Markdown
# Implementation — imajViewer
|
||
|
||
> Teknik uygulama dokümanı
|
||
> Versiyon: 1.0
|
||
|
||
---
|
||
|
||
## 1. Mimari Genel Bakış
|
||
|
||
```
|
||
┌──────────────────────────────────────────────────┐
|
||
│ Flutter App │
|
||
│ ┌────────────┐ ┌────────────────────────────┐ │
|
||
│ │ window_mgr │ │ ViewerScreen │ │
|
||
│ │ (frameless)│ │ ┌──────────────────────┐ │ │
|
||
│ └────────────┘ │ │ CustomTitleBar │ │ │
|
||
│ │ │ (minimize, close) │ │ │
|
||
│ │ ├──────────────────────┤ │ │
|
||
│ │ │ ImageCanvas │ │ │
|
||
│ │ │ (InteractiveViewer) │ │ │
|
||
│ │ │ - Scroll → Zoom │ │ │
|
||
│ │ │ - Drag → Pan │ │ │
|
||
│ │ └──────────────────────┘ │ │
|
||
│ └────────────────────────────┘ │
|
||
│ ┌────────────────────────────────────────────┐ │
|
||
│ │ ImageCacheManager │ │
|
||
│ │ - PrecacheImage │ │
|
||
│ │ - LRU eviction (> N images) │ │
|
||
│ │ - File → Memory → GPU Texture │ │
|
||
│ └────────────────────────────────────────────┘ │
|
||
└──────────────────────────────────────────────────┘
|
||
```
|
||
|
||
## 2. Bileşen Ağacı (Widget Tree)
|
||
|
||
```
|
||
MaterialApp (dark theme)
|
||
└── ViewerScreen (StatefulWidget)
|
||
├── Stack
|
||
│ ├── InteractiveViewer (zoom + pan)
|
||
│ │ └── RawImage (resim)
|
||
│ ├── CustomTitleBar (Positioned: top-right)
|
||
│ │ ├── IconButton (minimize)
|
||
│ │ └── IconButton (close)
|
||
│ └── (koyu arka plan)
|
||
└── DragTarget (sürükle-bırak dosya alanı)
|
||
```
|
||
|
||
## 3. Data Flow
|
||
|
||
```
|
||
Kullanıcı dosya bırakır / açar
|
||
│
|
||
▼
|
||
ViewerScreen.dragResult / openFileDialog
|
||
│
|
||
▼
|
||
ImageList (List<String> filePaths)
|
||
│
|
||
▼
|
||
preloadImages() → ImageCache'e ekle
|
||
│
|
||
▼
|
||
Indexed, currentIndex değişince
|
||
│
|
||
▼
|
||
InteractiveViewer.builder → Image.file(path)
|
||
│
|
||
▼
|
||
Flutter ImageCache → GPU Texture → Render
|
||
```
|
||
|
||
## 4. Adım Adım Uygulama
|
||
|
||
### Adım 1: Proje İskeleti
|
||
- `flutter create imajViewer`
|
||
- `--platforms=linux` ile oluştur
|
||
- Temele dark tema, frameless window ekle
|
||
|
||
**Dosyalar:** `main.dart`, `app.dart`
|
||
|
||
### Adım 2: window_manager Kurulumu
|
||
- pubspec.yaml: `window_manager: ^0.4.3`
|
||
- `main()` içinde `windowManager.ensureInitialized()`
|
||
- `waitUntilReadyToShow` → frameless ayarları:
|
||
- `setTitleBarStyle(TitleBarStyle.hidden)`
|
||
- `setSize(Size(1280, 800))`
|
||
- `setMinimumSize(Size(800, 600))`
|
||
- `setBackgroundColor(Colors.transparent)`
|
||
- Linux'ta `linux/my_app.cc`'de window_manager için ek ayar gerekebilir
|
||
|
||
**Dosyalar:** `lib/main.dart`, `pubspec.yaml`
|
||
|
||
### Adım 3: CustomTitleBar Widget
|
||
- Sağ üst köşede, sabit konumda
|
||
- Siyah yarı saydam arka plan
|
||
- minimize (—) ve kapat (X) butonları
|
||
- **Püf nokta:** Butonların üzerine gelince InteractiveViewer zoom yapmamalı
|
||
- `AbsorbPointer` veya `IgnorePointer` ile InteractiveViewer etkileşimini engelle
|
||
- Pencere boyutlandırma ve sürükleme kenarlıkları (window_manager'in `setResizable` özelliği)
|
||
|
||
**Dosya:** `lib/widgets/custom_title_bar.dart`
|
||
|
||
### Adım 4: ImageCanvas (Ana Görüntüleme)
|
||
- `InteractiveViewer` widget'ı
|
||
- `boundaryMargin: EdgeInsets.all(double.infinity)` → sınırsız kaydırma
|
||
- `minScale: 0.1`, `maxScale: 10.0`
|
||
- `onInteractionUpdate` → zoom oranını göstermek için (ops.)
|
||
- `transformationController` ile programatik zoom kontrolü
|
||
- Mouse wheel event: `Listener` ile `onPointerSignal` yakala
|
||
- `PointerScrollEvent` → transformationController ile zoom
|
||
- Fare imlecinin resim üzerindeki konumuna göre zoom odağı
|
||
|
||
**Püf nokta:**
|
||
`InteractiveViewer`'ın default scroll-to-zoom desteği vardır ama imleç odağında zoom
|
||
için `TransformationController`'a manuel matrix hesaplaması yapmak gerekir.
|
||
|
||
**Dosya:** `lib/widgets/image_canvas.dart`
|
||
|
||
### Adım 5: Resim Yönetimi (ImageManager)
|
||
- `ChangeNotifier` ile state yönetimi
|
||
- Dışarıdan dosya eklendiğinde `notifyListeners()` + `precacheImage()`
|
||
- `ImageCache.maximumSize` ve `maximumSizeBytes` ayarı
|
||
- **Performans stratejisi:**
|
||
- Sadece görünen resim bellekte tutulur
|
||
- 12+ resim için `ImageCache.maximumSizeBytes` = 512MB
|
||
- Resim değiştirince önceki resmin cache'ini düşürmeye gerek yok (Flutter LRU)
|
||
- Sürükle-bırak: `DragTarget<List<File>>` widget'ı
|
||
|
||
**Dosya:** `lib/services/image_manager.dart`
|
||
|
||
### Adım 6: Dosya Açma (Dialog + DragDrop)
|
||
- `file_picker` paketi ile `Ctrl+O` kısayolu
|
||
- `DragTarget` ile sürükle-bırak
|
||
- Desteklenen format filtreleri: PNG, JPEG, WebP, BMP, GIF
|
||
- **Dikkat:** Flutter Linux'ta DragTarget çalışıyor — ayrıca `window_manager`'dan `onDragFile` event'i de var
|
||
|
||
**Dosya:** `lib/services/file_handler.dart`
|
||
|
||
### Adım 7: Klavye Kısayolları
|
||
- Flutter'daki `Shortcuts` + `Actions` sistemi
|
||
- `← →` → resim değiştir
|
||
- `Ctrl+O` → dosya aç
|
||
- `F11` → tam ekran (fullscreen toggle)
|
||
- `Esc` → tam ekrandan çık
|
||
- `Ctrl+Q` → çıkış
|
||
|
||
**Dosya:** `lib/shortcuts/app_shortcuts.dart`
|
||
|
||
### Adım 8: Birleştirme ve Test
|
||
- Ana ekranı `ViewerScreen`'de topla
|
||
- `Listener(onPointerSignal)` + `InteractiveViewer` entegrasyonu
|
||
- `flutter run -d linux` ile test
|
||
- Performance profiling: `flutter run --profile`
|
||
|
||
### Adım 9: Derleme
|
||
- `flutter build linux --release`
|
||
- Binary: `build/linux/x64/release/bundle/imajViewer`
|
||
|
||
## 5. pubspec.yaml Bağımlılıkları
|
||
|
||
```yaml
|
||
dependencies:
|
||
flutter:
|
||
sdk: flutter
|
||
window_manager: ^0.4.3
|
||
file_picker: ^8.0.0
|
||
path: ^1.9.0
|
||
```
|
||
|
||
## 6. Önemli Dikkat Noktaları
|
||
|
||
### Zoom Odağı (Cursor-based Zoom)
|
||
InteractiveViewer varsayılan zoom odağı merkezdir. İmleç odağında zoom için
|
||
`TransformationController` ile manuel matrix çarpımı:
|
||
|
||
```
|
||
1. Scroll event yakala (PointerScrollEvent)
|
||
2. İmlecin resim üzerindeki koordinatını hesapla
|
||
3. Matrix4.translationValues(-txt) * Matrix4.diagonal3(values(scale)) * Matrix4.translationValues(txt)
|
||
```
|
||
|
||
Detaylı implementasyon `image_canvas.dart`'da yapılacak.
|
||
|
||
### Frameless Window Sürükleme
|
||
Window_manager zaten varsayılan olarak fare ile pencerenin herhangi bir
|
||
yerinden sürüklenmesine izin vermez. Bunun için:
|
||
- CustomTitleBar alanını `DragToMoveArea` ile sarmak
|
||
- Veya `windowManager.startDragging()` çağırmak
|
||
|
||
Biz `DragToMoveArea` widget'ını kullanacağız.
|
||
|
||
### Performans
|
||
- Flutter'ın `ImageCache`'i varsayılan olarak 50 MB ve 1000 entry ile sınırlıdır
|
||
- 12 adet 4K resim için `ImageCache.maximumSizeBytes` 1 GB'a çıkarılmalı
|
||
- `RepaintBoundary` ile sadece değişen bölgelerin yeniden çizilmesi sağlanır
|
||
- `Texture` bazlı render ile GPU'da işleme devam eder
|
||
|
||
## 7. Timeline
|
||
|
||
| Adım | İş | Tahmini Süre |
|
||
|---|---|---|
|
||
| 1-2 | Proje iskeleti + window_manager | 15 dk |
|
||
| 3 | CustomTitleBar | 15 dk |
|
||
| 4 | ImageCanvas + zoom | 30 dk |
|
||
| 5-6 | Resim yönetimi + dosya açma | 30 dk |
|
||
| 7 | Kısayollar | 10 dk |
|
||
| 8-9 | Birleştirme + build | 20 dk |
|
||
| **Toplam** | | **~2 saat** |
|