diff --git a/CHANGELOG.md b/CHANGELOG.md index 60bb099..33a500d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,20 @@ All notable changes to imajViewer are documented in this file. ## [Unreleased] +## [1.9.0] - 2026-10-31 + +### Added +- Taskbar/alt-tab title now shows the opened image's file name (falls back to + "imajViewer" when no image is open). +- Full hicolor icon size set (16–256 px) instead of only 64 px, so small-icon + contexts (Thunar "Open With", Xfce app menu) keep the symbol visible. + +### Changed +- `install.sh` generates the full icon size set and rewrites the hicolor + index; uninstall hint updated. +- Settings size grid is now a fixed 4-column layout (the wide/narrow + transposition introduced in this release was removed). + ## [1.8.0] - 2026-10-31 ### Added diff --git a/deb/usr/share/icons/hicolor/128x128/apps/imajviewer.png b/deb/usr/share/icons/hicolor/128x128/apps/imajviewer.png new file mode 100644 index 0000000..4c05c9f Binary files /dev/null and b/deb/usr/share/icons/hicolor/128x128/apps/imajviewer.png differ diff --git a/deb/usr/share/icons/hicolor/16x16/apps/imajviewer.png b/deb/usr/share/icons/hicolor/16x16/apps/imajviewer.png new file mode 100644 index 0000000..5ab653f Binary files /dev/null and b/deb/usr/share/icons/hicolor/16x16/apps/imajviewer.png differ diff --git a/deb/usr/share/icons/hicolor/22x22/apps/imajviewer.png b/deb/usr/share/icons/hicolor/22x22/apps/imajviewer.png new file mode 100644 index 0000000..f64b683 Binary files /dev/null and b/deb/usr/share/icons/hicolor/22x22/apps/imajviewer.png differ diff --git a/deb/usr/share/icons/hicolor/24x24/apps/imajviewer.png b/deb/usr/share/icons/hicolor/24x24/apps/imajviewer.png new file mode 100644 index 0000000..ac18074 Binary files /dev/null and b/deb/usr/share/icons/hicolor/24x24/apps/imajviewer.png differ diff --git a/deb/usr/share/icons/hicolor/256x256/apps/imajviewer.png b/deb/usr/share/icons/hicolor/256x256/apps/imajviewer.png new file mode 100644 index 0000000..777088c Binary files /dev/null and b/deb/usr/share/icons/hicolor/256x256/apps/imajviewer.png differ diff --git a/deb/usr/share/icons/hicolor/32x32/apps/imajviewer.png b/deb/usr/share/icons/hicolor/32x32/apps/imajviewer.png new file mode 100644 index 0000000..b252e57 Binary files /dev/null and b/deb/usr/share/icons/hicolor/32x32/apps/imajviewer.png differ diff --git a/deb/usr/share/icons/hicolor/48x48/apps/imajviewer.png b/deb/usr/share/icons/hicolor/48x48/apps/imajviewer.png new file mode 100644 index 0000000..bb60fcd Binary files /dev/null and b/deb/usr/share/icons/hicolor/48x48/apps/imajviewer.png differ diff --git a/deb/usr/share/icons/hicolor/64x64/apps/imajviewer.png b/deb/usr/share/icons/hicolor/64x64/apps/imajviewer.png index 2da3e46..68877ad 100644 Binary files a/deb/usr/share/icons/hicolor/64x64/apps/imajviewer.png and b/deb/usr/share/icons/hicolor/64x64/apps/imajviewer.png differ diff --git a/install.sh b/install.sh index 80a1048..a5034d8 100755 --- a/install.sh +++ b/install.sh @@ -11,8 +11,8 @@ fi INSTALL_DIR="${HOME}/.local/lib/${APP}" BIN_LINK="${HOME}/.local/bin/${APP}" DESKTOP_FILE="${HOME}/.local/share/applications/${APP}.desktop" -ICON_DIR="${HOME}/.local/share/icons/hicolor/64x64/apps" -ICON_FILE="${ICON_DIR}/${APP}.png" +ICON_BASE="${HOME}/.local/share/icons/hicolor" +ICON_SIZES="16 22 24 32 48 64 128 256" # ── Renkler ── RED='\033[0;31m'; GREEN='\033[0;32m'; CYAN='\033[0;36m'; NC='\033[0m' @@ -36,7 +36,7 @@ if [ ! -f "$BUNDLE_DIR/${APP}" ]; then fi # ── Kurulum dizinleri ── -mkdir -p "$INSTALL_DIR" "$(dirname "$BIN_LINK")" "$(dirname "$DESKTOP_FILE")" "$ICON_DIR" +mkdir -p "$INSTALL_DIR" "$(dirname "$BIN_LINK")" "$(dirname "$DESKTOP_FILE")" # ── Binary + kütüphaneler ── info "Binary ve kütüphaneler kopyalanıyor…" @@ -47,37 +47,39 @@ chmod +x "$INSTALL_DIR/${APP}" ln -sf "$INSTALL_DIR/${APP}" "$BIN_LINK" ok "Symlink: $BIN_LINK → $INSTALL_DIR/${APP}" -# ── İkon (64×64 PNG) ── -if [ ! -f "$ICON_FILE" ]; then - if [ -f "$SCRIPT_DIR/imajviewericon.png" ]; then - info "İkon kopyalanıyor (64x64)…" +# ── İkon (hicolor boyut seti: 16–256) ── +if [ -f "$SCRIPT_DIR/imajviewericon.png" ]; then + info "İkon boyutları üretiliyor (16–256)…" + for s in $ICON_SIZES; do + d="${ICON_BASE}/${s}x${s}/apps" + mkdir -p "$d" python3 -c " from PIL import Image im = Image.open('$SCRIPT_DIR/imajviewericon.png') -im.resize((64,64), Image.LANCZOS).save('$ICON_FILE') -" && ok "İkon: $ICON_FILE" || err "İkon dönüştürme başarısız!" - else - err "imajviewericon.png bulunamadı!" - fi +(im if $s == 256 else im.resize(($s, $s), Image.LANCZOS)).save('$d/${APP}.png') +" || err "İkon dönüştürme başarısız! ($s)" + done + ok "İkon: ${ICON_BASE} (${ICON_SIZES} px)" else - ok "İkon zaten mevcut" + err "imajviewericon.png bulunamadı!" fi # ── Hicolor tema indeksi (GTK ikon temasını tanısın) ── -if [ ! -f "${HOME}/.local/share/icons/hicolor/index.theme" ]; then - mkdir -p "${HOME}/.local/share/icons/hicolor/64x64/apps" - cat > "${HOME}/.local/share/icons/hicolor/index.theme" << 'EOF' -[Icon Theme] -Name=Hicolor -Comment=Fallback icon theme -Directories=64x64/apps - -[64x64/apps] -Size=64 -Context=Applications -Type=Fixed -EOF -fi +{ + echo "[Icon Theme]" + echo "Name=Hicolor" + echo "Comment=Fallback icon theme" + dirs="" + for s in $ICON_SIZES; do dirs="${dirs:+$dirs,}${s}x${s}/apps"; done + echo "Directories=$dirs" + for s in $ICON_SIZES; do + echo "" + echo "[${s}x${s}/apps]" + echo "Size=$s" + echo "Context=Applications" + echo "Type=Fixed" + done +} > "${ICON_BASE}/index.theme" # ── .desktop dosyası ── info ".desktop dosyası oluşturuluyor…" @@ -113,4 +115,4 @@ echo -e "${GREEN} Bir PNG/JPG/WebP dosyasına çift tıklayın${NC}" echo -e "${GREEN} veya 'xdg-open ' ile test edin${NC}" echo -e "${GREEN}══════════════════════════════════════════${NC}" echo "" -echo "Kaldırmak için: rm -rf ${INSTALL_DIR} ${BIN_LINK} ${DESKTOP_FILE} ${ICON_FILE}" +echo "Kaldırmak için: rm -rf \"$INSTALL_DIR\" \"$BIN_LINK\" \"$DESKTOP_FILE\" && rm -f ${ICON_BASE}/*x*/apps/${APP}.png" diff --git a/lib/screens/viewer_screen.dart b/lib/screens/viewer_screen.dart index 507da09..2c783b6 100644 --- a/lib/screens/viewer_screen.dart +++ b/lib/screens/viewer_screen.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:path/path.dart' as p; import 'package:window_manager/window_manager.dart'; import 'package:desktop_drop/desktop_drop.dart'; import 'package:pdf/pdf.dart'; @@ -18,7 +19,7 @@ import '../shortcuts/app_shortcuts.dart'; import '../services/window_persistence.dart'; import '../l10n/app_localizations.dart'; -const appVersion = '1.8.0'; +const appVersion = '1.9.0'; class ViewerScreen extends StatefulWidget { final List initialFiles; @@ -45,7 +46,10 @@ class _ViewerScreenState extends State with WindowListener { if (widget.initialFiles.isNotEmpty) { WidgetsBinding.instance.addPostFrameCallback((_) { FileHandler.loadImagesFromPaths(widget.initialFiles); + _updateTitle(); }); + } else { + WidgetsBinding.instance.addPostFrameCallback((_) => _updateTitle()); } } @@ -85,6 +89,18 @@ class _ViewerScreenState extends State with WindowListener { void _onImageManagerChanged() { setState(() => _hasImages = ImageManager.instance.images.isNotEmpty); + _updateTitle(); + } + + /// Taskbar/alt-tab başlığı = açılan dosyanın adı (görsel yoksa uygulama adı). + Future _updateTitle() async { + final image = ImageManager.instance.currentImagePath; + final name = image.isEmpty ? 'imajViewer' : p.basename(image); + try { + await windowManager.setTitle(name); + } catch (_) { + // Başlık güncellenemezse sessizce geç. + } } Future _doSave() async { @@ -111,6 +127,36 @@ class _ViewerScreenState extends State with WindowListener { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ + if (_hasImages) ...[ + Text(AppLocalizations.of(context)!.fileProperties, + style: const TextStyle(color: Colors.white70, fontSize: 13)), + const SizedBox(height: 4), + FutureBuilder>( + future: _fileProperties(ImageManager.instance.currentImagePath), + builder: (context, snap) { + if (!snap.hasData) return const SizedBox(height: 20); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: snap.data!.map((r) => Padding( + padding: const EdgeInsets.only(bottom: 2), + child: Text.rich( + TextSpan(children: [ + TextSpan(text: '${r.$1}: ', style: const TextStyle(color: Colors.white70, fontSize: 13)), + TextSpan(text: r.$2, style: const TextStyle(color: Colors.white, fontSize: 13)), + ]), + ), + )).toList(), + ); + }, + ), + const SizedBox(height: 12), + const Divider(color: Colors.white24, height: 24), + ], + WindowSizeSelector( + workArea: _workArea, + onPick: _applyWindowSize, + ), + const Divider(color: Colors.white24, height: 24), Text(AppLocalizations.of(context)!.sortBy, style: const TextStyle(color: Colors.white70, fontSize: 13)), RadioListTile( @@ -137,36 +183,6 @@ class _ViewerScreenState extends State with WindowListener { setDialogState(() {}); }, ), - const Divider(color: Colors.white24, height: 24), - WindowSizeSelector( - workArea: _workArea, - onPick: _applyWindowSize, - ), - const Divider(color: Colors.white24, height: 24), - if (_hasImages) ...[ - Text(AppLocalizations.of(context)!.fileProperties, - style: const TextStyle(color: Colors.white70, fontSize: 13)), - const SizedBox(height: 4), - FutureBuilder>( - future: _fileProperties(ImageManager.instance.currentImagePath), - builder: (context, snap) { - if (!snap.hasData) return const SizedBox(height: 20); - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: snap.data!.map((r) => Padding( - padding: const EdgeInsets.only(bottom: 2), - child: Text.rich( - TextSpan(children: [ - TextSpan(text: '${r.$1}: ', style: const TextStyle(color: Colors.white70, fontSize: 13)), - TextSpan(text: r.$2, style: const TextStyle(color: Colors.white, fontSize: 13)), - ]), - ), - )).toList(), - ); - }, - ), - const SizedBox(height: 12), - ], Text('imajViewer $appVersion', style: const TextStyle(color: Colors.white, fontSize: 15)), const SizedBox(height: 4), diff --git a/lib/widgets/window_size_selector.dart b/lib/widgets/window_size_selector.dart index ca1acf6..a077e91 100644 --- a/lib/widgets/window_size_selector.dart +++ b/lib/widgets/window_size_selector.dart @@ -2,11 +2,12 @@ import 'package:flutter/material.dart'; import '../l10n/app_localizations.dart'; -/// 8 sütun × 4 satır ekran oranı buton ızgarası. +/// Ekran oranı buton ızgarası: 31 buton `a:b` (a dikeyi, b yatayı böler; +/// hedef boyut `W/b × H/a`). Sol-üst hücre (1:1) boş placeholder — 1:1 yok +/// (title bar çift tık zaten tam ekran). /// -/// Buton (sütun `b`, satır `a`) etiketi `"$a:$b"` → hedef boyut -/// `W/b × H/a` (a dikeyi, b yatayı böler). Sol-üst hücre (1:1) boş -/// placeholder — 1:1 yok (title bar çift tık zaten tam ekran). +/// Yerleşim: sabit 8 satır × 4 sütun (31-10: geniş/dar transpozesi +/// kaldırıldı — kullanıcı kararı). class WindowSizeSelector extends StatelessWidget { final Future Function() workArea; final void Function(int rows, int cols, Size work) onPick; @@ -30,43 +31,47 @@ class WindowSizeSelector extends StatelessWidget { scrollDirection: Axis.horizontal, child: Column( mainAxisSize: MainAxisSize.min, - children: [ - for (var a = 1; a <= 4; a++) - Row( - mainAxisSize: MainAxisSize.min, - children: [ - for (var b = 1; b <= 8; b++) - Padding( - padding: const EdgeInsets.only(right: 4, bottom: 4), - child: a == 1 && b == 1 - ? const SizedBox(width: 46, height: 26) - : GestureDetector( - onTap: () async { - final work = await workArea(); - if (work != null) onPick(a, b, work); - }, - child: Container( - width: 46, - height: 26, - decoration: BoxDecoration( - color: Colors.white10, - borderRadius: BorderRadius.circular(4), - ), - alignment: Alignment.center, - child: Text( - '$a:$b', - style: const TextStyle( - color: Colors.white, fontSize: 11), - ), - ), - ), - ), - ], - ), - ], + children: [for (var b = 1; b <= 8; b++) _row(b)], ), ), ], ); } + + /// Satır = `b` (1..8, yatay bölün); satır içinde `a` (1..4, dikey bölün). + Widget _row(int b) { + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + for (var a = 1; a <= 4; a++) _cell(a, b), + ], + ); + } + + Widget _cell(int a, int b) { + return Padding( + padding: const EdgeInsets.only(right: 4, bottom: 4), + child: a == 1 && b == 1 + ? const SizedBox(width: 46, height: 26) + : GestureDetector( + onTap: () async { + final work = await workArea(); + if (work != null) onPick(a, b, work); + }, + child: Container( + width: 46, + height: 26, + decoration: BoxDecoration( + color: Colors.white10, + borderRadius: BorderRadius.circular(4), + ), + alignment: Alignment.center, + child: Text( + '$a:$b', + style: const TextStyle(color: Colors.white, fontSize: 11), + ), + ), + ), + ); + } } diff --git a/pubspec.yaml b/pubspec.yaml index 383aebf..1c07bb4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: imajviewer description: "Ultra-lightweight image viewer for Linux and Windows." publish_to: 'none' -version: 1.8.0+19 +version: 1.9.0+20 msix_config: display_name: ImajViewer