feat: jfif/jpe/wbmp format destegi, pencere cascade yerlesimi, konum kaydi duzeltmesi

- 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
This commit is contained in:
Alhan
2026-08-03 06:44:57 +03:00
parent d09351feef
commit a65bdc152c
17 changed files with 272 additions and 38 deletions

View File

@@ -0,0 +1,59 @@
# Ek Görüntü Formatları Destek Planı
> Tarih: 2026-08-03 · Değerlendirme + implementasyon planı
> Not: TIFF değerlendirildi ve **vazgeçildi** (kullanıcı kararı). Bu plan yalnızca mevcut mimariye sıfır maliyetle eklenebilen formatları kapsar.
## 1. Hedef
imajViewer'ın desteklediği format listesini, **ek decode katmanı gerektirmeden** (Flutter/Skia'nın zaten açabildiği) formatlarla genişletmek.
## 2. Mevcut Durum
### Desteklenen uzantılar (kod içi, 3 yerde)
| Dosya | Satır | Liste |
|---|---|---|
| `lib/services/file_handler.dart` | 9 | png, jpg, jpeg, webp, bmp, gif |
| `lib/services/image_manager.dart` | 24 | png, jpg, jpeg, webp, bmp, gif |
| `imajviewer.desktop` + `deb/.../imajviewer.desktop` | 9 | image/png;image/jpeg;image/webp;image/bmp;image/gif;image/jpg |
| `install.sh` | 77 | aynı MimeType listesi |
### Flutter'ın yerleşik codec'leri (Skia)
Resmi dokümantasyona göre Flutter açabilir: **JPEG, PNG, GIF, WebP, BMP, WBMP**
Yani Skia zaten açabiliyor ama listemizde olmayanlar:
- `.jfif` — JPEG/JFIF içerik (aslında JPEG, magic-byte aynı)
- `.jpe` — JPEG alternatif uzantısı
- `.wbmp` — Skia'da WBMP codec var
- `.avif`**YOK**: Flutter issue #61229ık, native plugin (`flutter_avif`/libavif) gerekir → kapsam dışı
- `.tif/.tiff`**YOK**: image paketi gerekir → vazgeçildi
## 3. Eklenecekler
| Uzantı | MIME | Neden açılır | Maliyet |
|---|---|---|---|
| `.jfif` | image/jpeg | JPEG codec, aynı magic byte | sadece filtre |
| `.jpe` | image/jpeg | JPEG alternatif uzantısı | sadece filtre |
| `.wbmp` | image/vnd.wap.wbmp | Skia WBMP codec | sadece filtre |
> `.avif` bilinçli olarak eklenmiyor: Flutter'ın yerleşik decoder'ı yok; native libavif bağımlılığı "ultra hafif" felsefeyi bozar. İstenirse ileride ayrı plan yapılır.
## 4. Yapılacaklar (sıralı)
1. [ ] `file_handler.dart` → allowedExtensions: `'jfif', 'jpe', 'wbmp'` ekle
2. [ ] `image_manager.dart` → supported: `'jfif', 'jpe', 'wbmp'` ekle
3. [ ] `imajviewer.desktop` → MimeType'a `image/jpeg` zaten var (jfif/jpe kapsar), `image/vnd.wap.wbmp;` ekle
4. [ ] `deb/usr/share/applications/imajviewer.desktop` → aynı
5. [ ] `install.sh` → aynı MimeType güncellemesi
6. [ ] Windows installer (`.iss`) dosya ilişkilendirme listesi kontrolü — varsa `image/jpeg` jfif/jpe'yi kapsar
## 5. Riskler / Açık Sorular
- **JFIF varyantı:** Bazı .jfif dosyaları CMYK-JPEG olabilir; Skia çoğunu açıyor ama tamamını değil → açılmayanlar `errorBuilder` ekranı gösterir (mevcut davranış, çökme yok)
- **WBMP nadir:** Çoğu kullanıcı hiç karşılaşmaz; eklemesi zararsız
- **Windows ilişkilendirme:** Inno Setup `.iss` dosyası `image/jpeg` kayıtlıysa .jfif/.jpe otomatik kapsanır; `.wbmp` için ayrı registry gerekebilir (opsiyonel)
## 6. Kabul Kriterleri
- [ ] `.jfif`, `.jpe`, `.wbmp` dosyaları dosya seçicide görünür
- [ ] JFIF dosyası pan/zoom/filtre ile görüntülenir
- [ ] Mevcut PNG/JPEG/WebP/GIF/BMP davranışı değişmez

View File

@@ -2,6 +2,9 @@
| Tarih | Dosya | Konu |
|---|---|---|
| 2026-08-03 | [session-2026-08-03.md](session-2026-08-03.md) | **Session özeti** — jfif/jpe/wbmp, pencere cascade, konum kaydı düzeltmesi |
| 2026-08-03 | [format-destegi-ek-formatlar.md](format-destegi-ek-formatlar.md) | **Plan** — jfif/jpe/wbmp desteği (TIFF vazgeçildi) |
| 2026-08-03 | [pencere-cascade-yerlesim.md](pencere-cascade-yerlesim.md) | **Plan** — yeni pencereyi yan yana / alt satıra açma (screen_retriever) |
| 2026-07-27 | [session-windows-ci.md](session-windows-ci.md) | **Windows setup.exe yeniden derlendi, Gitea CI eklendi** |
| 2026-07-27 | [session-2026-07-27.md](session-2026-07-27.md) | **Session özeti** — rotate aktif, StartupWMClass ile taskbar gruplama |
| 2026-07-27 | [image-canvas-interactions.md](image-canvas-interactions.md) | Detaylı analiz: contrast, saturation, rotate implementasyon planı |

View File

@@ -0,0 +1,111 @@
# 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**ı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)

View File

@@ -0,0 +1,33 @@
# Session Özeti — 2026-08-03
## Yapılanlar
### 1. Ek Görüntü Formatları (jfif / jpe / wbmp)
- TIFF değerlendirildi, **vazgeçildi** (kullanıcı kararı — image paketi/decode katmanı istenmedi)
- AVIF değerlendirildi, **eklenmedi** (Flutter'da yerleşik decoder yok — native libavif gerekir, "ultra hafif" felsefeye aykırı)
- Eklendi: `.jfif`, `.jpe` (JPEG içerikli, Skia zaten açıyor), `.wbmp` (Skia WBMP codec)
- Değişen yerler: file_handler (seçici), image_manager (filtre), 2× .desktop + install.sh (MimeType)
### 2. Pencere Cascade Yerleşimi
- Yeni pencere artık öncekinin **x + width** kadar sağında açılıyor
- Ekrandan taşarsa → alt satıra, satır başından
- O da taşarsa → ekran sol-üst köşesine
- Ekran bilgisi: `screen_retriever` paketi (window_manager'ın zaten transitive bağımlısıydı, doğrudan bağımlılık yapıldı)
- Yeni: `WindowPersistence.cascadePosition()` — getPrimaryDisplay → visibleSize/visiblePosition
### 3. Pencere Konum Kaydı Düzeltmesi (kök neden)
- **Sorun:** Konum kaydedilemiyordu → çözüm olarak 5 yere kayıt koyulmuştu (initState + 2sn polling + move/resize debounce + close + main). Birden fazla pencere açıkken polling konum yarışı yaratıyordu → yeni pencere eski konuma göre hesaplanıp üstüne düşüyordu.
- **Çözüm:** Polling kaldırıldı. Tek akış:
1. `main.dart` — pencere açılışında konum anında saveSync (sıradaki pencere güncel referans okur)
2. `viewer_screen` move/resize — son bilinen bounds hemen `_lastBounds`'a, 300ms debounce ile saveSync
3. `viewer_screen` close — `_lastBounds` **senkron** saveSync (async çağrı kapanışta tamamlanmıyordu)
## Doğrulama
- `flutter analyze` — 18 info uyarı (mevcut kod stil/deprecated), hata yok
- `flutter build linux --release`
- Kurulum: release bundle `~/.local/lib/imajviewer/` (install.sh debug bundle'ı önceliklendirdiği için elle kopyalandı)
- Kullanıcı manuel test: WebP açılıyor ✓, cascade düzgün çalışıyor ✓
## Not
- `docs/session-windows-ci.md` bozuk encoding (mojibake) — içerik git log'dan biliniyor, düzeltilmedi
- Windows setup.exe **yeniden derlenmedi** — sadece Linux build + .deb güncellendi (Windows CI zaten Gitea Actions'ta)