Files
imajviewer/docs/Implementation.md

7.8 KiB
Raw Permalink Blame History

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ı

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