- 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
112 lines
5.2 KiB
Markdown
112 lines
5.2 KiB
Markdown
# Pencere Cascade Yerleşimi Planı
|
||
|
||
> Tarih: 2026-08-03 · Değerlendirme + implementasyon planı (henüz kod değişikliği yapılmadı)
|
||
|
||
## 1. Sorun
|
||
|
||
imajViewer'a yeni bir resim açıldığında pencere **hep aynı koordinat ve boyutta** açılıyor (önceki pencerenin üstüne biniyor). İstenen davranış:
|
||
|
||
- Yeni pencere, önceki pencerenin **x + width** kadar sağına açılsın (yan yana)
|
||
- Ekrandan **taşacaksa** alt satıra, satır başından (x baştan) açılsın
|
||
|
||
Bu, Windows'un klasik "cascade windows" davranışının basit hali.
|
||
|
||
## 2. Mevcut Durum Analizi
|
||
|
||
### Pencere açılış akışı (`lib/main.dart`)
|
||
```
|
||
main(args)
|
||
→ WindowPersistence.restore() // window.json'dan son konum/boyut
|
||
→ windowOptions = WindowOptions(size: saved ?? 1280x800)
|
||
→ waitUntilReadyToShow:
|
||
saved varsa → setPosition(saved.position) + setSize(saved.size)
|
||
saved yoksa → center()
|
||
→ runApp(ImajViewerApp(initialFiles: args))
|
||
```
|
||
|
||
### Konum kaydetme akışı (`lib/screens/viewer_screen.dart` + `lib/services/window_persistence.dart`)
|
||
- `_startPolling()`: 2 saniyede bir `windowManager.getBounds()` → `WindowPersistence.saveSync()` → `~/.config/imajviewer/window.json` (Linux) / `%APPDATA%/imajviewer/window.json` (Windows)
|
||
- `onWindowEvent` (move/resize/close) → debounce'lu `_doSave()`
|
||
|
||
### Sorunun kökü
|
||
- Her yeni pencere `window.json`'daki **son kaydedilen** konumu okuyor → aynı yere açılıyor
|
||
- Cascade mantığı yok; ekran genişliği bilgisi hiç sorgulanmıyor
|
||
|
||
## 3. Teknik Araştırma (doğrulandı)
|
||
|
||
### window_manager 0.4.3 (mevcut bağımlılık)
|
||
- `setPosition`, `setSize`, `getBounds`, `getPosition`, `getSize`, `center()` → var
|
||
- **Ekran/display API'si → YOK** (getScreen/getPrimaryDisplay yok)
|
||
|
||
### window_manager 0.5.2 (güncel)
|
||
- Ekran API'si yine yok — `screen_retriever`'ı bağımlılık olarak içeriyor ama expose etmiyor
|
||
|
||
### screen_retriever 0.2.2 (pub.dev, window_manager'ın zaten bağımlısı)
|
||
- `ScreenRetriever.instance.getPrimaryDisplay()` → `Display`
|
||
- `getAllDisplays()` → `List<Display>`
|
||
- `Display` alanları:
|
||
- `size` (ekran tam boyutu, logical px)
|
||
- `visiblePosition` (Offset — çalışma alanı başlangıcı)
|
||
- `visibleSize` (Size — görev çubuğu hariç çalışma alanı)
|
||
- `scaleFactor`
|
||
- Linux (X11) + Windows desteği var
|
||
|
||
**Karar: `screen_retriever` doğrudan bağımlılık olarak eklenir** (window_manager sürümü değişmez).
|
||
|
||
## 4. Önerilen Algoritma
|
||
|
||
`main.dart`'ta, `windowOptions` oluşturulmadan önce:
|
||
|
||
```
|
||
1. saved = WindowPersistence.restore()
|
||
2. display = await ScreenRetriever.instance.getPrimaryDisplay()
|
||
workArea = display.visiblePosition + display.visibleSize (yoksa display.size)
|
||
3. Eğer saved != null:
|
||
newX = saved.x + saved.width // önceki pencerenin sağına
|
||
newY = saved.y
|
||
|
||
// Yatay taşma → alt satıra, satır başına
|
||
if (newX + pencereGenişliği > workArea.right):
|
||
newX = workArea.left
|
||
newY = saved.y + saved.height
|
||
|
||
// Dikey taşma da varsa → en başa sar (0,0)
|
||
if (newY + pencereYüksekliği > workArea.bottom):
|
||
newX = workArea.left
|
||
newY = workArea.top
|
||
|
||
position = Offset(newX, newY)
|
||
4. saved yoksa → center() (mevcut davranış)
|
||
```
|
||
|
||
### Örnek
|
||
- Ekran 1920×1080, pencere 1280×800, önceki pencere (100, 50)
|
||
- 1. yeni: (1380, 50) — yan yana ✓
|
||
- 2. yeni: (100+1280*2 = hesaplanır) → 1380+1280=2660 > 1920 → (0, 850) — alt satır başı ✓
|
||
|
||
> Not: `saved` her 2 saniyede bir güncellendiği için "önceki pencere" = en son kaydedilmiş konum. Bu, en son açılan pencerenin konumu olur — istenen davranışla uyumlu.
|
||
|
||
## 5. Yapılacaklar (sıralı)
|
||
|
||
1. [ ] `pubspec.yaml` → `screen_retriever: ^0.2.2` ekle, `flutter pub get`
|
||
2. [ ] `lib/main.dart` → cascade hesabı ekle (yeni bir servis fonksiyonuna taşınabilir)
|
||
3. [ ] `lib/services/window_persistence.dart` → `restore()` dışında, cascade hesabı için yardımcı fonksiyon:
|
||
- `static Future<Offset?> cascadePosition({required Size windowSize})` (opsiyonel, temizlik için)
|
||
4. [ ] Ekran boyutu alınamazsa (hata / çoklu monitör) → mevcut davranışa düş (saved veya center)
|
||
5. [ ] Test: ardışık 3-4 resim aç → yan yana dizilim + taşınca alt satır
|
||
|
||
## 6. Riskler / Açık Sorular
|
||
|
||
- **Çoklu monitör:** `getPrimaryDisplay` birincil ekranı verir. İmleç hangi ekrandaysa oraya açılsın mı? (başlangıç: primary)
|
||
- **Pencere kapatılıp yenisi açılırsa:** `window.json` son kapanan pencerenin konumunu tutar; yeni pencere onun yanına açılır — boşluk oluşabilir, kabul edilebilir
|
||
- **Tam ekran / maximize durumu:** saved boyut maximize ise cascade hesabı yine de eski boyutla yapılır; `window.json` sadece normal durumda yazıldığı için risk düşük
|
||
- **Pencere ekrandan tamamen dışarıdaysa:** `restore()` zaten `x < -1000 || y < -1000` guard'ı var (window_persistence.dart:44) — yine de sağlamlaştırılabilir
|
||
|
||
## 7. Kabul Kriterleri
|
||
|
||
- [ ] Ardışık 2 pencere: ikincisi birincinin hemen sağında
|
||
- [ ] Yatay taşma: yeni pencere alt satıra, satır başından başlar
|
||
- [ ] İlk açılışta (kayıt yok) pencere yine ortalanır
|
||
- [ ] Pencere boyutu/konumu kaydetme davranışı bozulmaz
|
||
- [ ] Linux ve Windows'ta çalışır (screen_retriever her ikisini destekliyor)
|