feat: zoom percentage badge at bottom center (v1.7.1)
Build & Release / build-linux (push) Canceled after 0s
Build & Release / build-windows (push) Canceled after 0s
Build & Release / create-release (push) Canceled after 0s

- Percentage shown between the prev/next buttons, together with the hover
  controls, on a translucent background.
- Value is the on-screen scale relative to the image's real pixel size
  (100% = 1:1); updated on wheel zoom, fill/fit toggle, image change and
  window resize.
- Pixel-size header parsing moved to lib/services/image_header.dart, shared
  with the settings file-properties panel.
- dist: 1.7.1 .deb replaces the tracked 1.7.0 package.
This commit is contained in:
Alhan committed 2026-10-25 18:53:59 +03:00
1 parent abbde04e50
commit 32fb12c210
7 files changed
+185 -71

No files matched your search

+53 -2
View File
@@ -6,11 +6,15 @@ import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import '../l10n/app_localizations.dart';
import '../services/crop_save_service.dart';
import '../services/image_header.dart';
import '../services/window_persistence.dart';
class ImageCanvas extends StatefulWidget {
final String filePath;
const ImageCanvas({super.key, required this.filePath});
/// Görüntünün gerçek piksel boyutuna göre ölçek yüzdesi (100% = 1:1).
final ValueNotifier<double>? zoomPercent;
const ImageCanvas({super.key, required this.filePath, this.zoomPercent});
@override
State<ImageCanvas> createState() => ImageCanvasState();
}
@@ -47,6 +51,44 @@ class ImageCanvasState extends State<ImageCanvas> {
Size _imageSize = Size.zero;
static const int _downsampleTarget = 2048;
// ── Gerçek piksel boyutu + zoom yüzdesi ──
Size? _origSize;
bool _zoomSyncScheduled = false;
/// Yüzdeyi bir sonraki kare sonunda yayınla (build sırasında setState tetiklememek için).
void _syncZoom() {
if (_zoomSyncScheduled) return;
_zoomSyncScheduled = true;
WidgetsBinding.instance.addPostFrameCallback((_) {
_zoomSyncScheduled = false;
if (!mounted) return;
final n = widget.zoomPercent;
if (n == null) return;
final v = _zoomPercentValue();
if (n.value != v) n.value = v;
});
}
/// Ekrandaki ölçeğin görüntünün gerçek piksel boyutuna oranı (100% = 1:1).
double _zoomPercentValue() {
final o = _origSize;
if (o == null || o.isEmpty || _vpSize.isEmpty) return _sc * 100;
final double ratio;
if (_isFilled) {
// cover: kutu viewport, boya ölçeği max oran
ratio = math.max(_vpSize.width / o.width, _vpSize.height / o.height);
} else {
// contain: yerleşmiş görüntü kutusu (downsample sınırı dahil) → gerçek ölçek
final box = _imageKey.currentContext?.findRenderObject() as RenderBox?;
if (box != null && box.hasSize && box.size.width > 0) {
ratio = box.size.width / o.width;
} else {
ratio = math.min(_vpSize.width / o.width, _vpSize.height / o.height);
}
}
return _sc * ratio * 100;
}
// ── Kare hızına eşitlenmiş render tick ──
final ValueNotifier<int> _renderTick = ValueNotifier(0);
bool _frameScheduled = false;
@@ -128,8 +170,10 @@ class ImageCanvasState extends State<ImageCanvas> {
@override
void initState() {
super.initState();
_origSize = pixelSizeOfFile(widget.filePath);
HardwareKeyboard.instance.addHandler(_onHardwareKey);
_startWarmup();
_syncZoom();
}
/// İlk kullanım shader derlemesini açılışa çek: gizli katmanda sırayla
@@ -189,6 +233,7 @@ class ImageCanvasState extends State<ImageCanvas> {
void didUpdateWidget(ImageCanvas old) {
super.didUpdateWidget(old);
if (old.filePath != widget.filePath) {
_origSize = pixelSizeOfFile(widget.filePath);
_cachedFileImage?.evict();
_cachedProvider?.evict();
_cachedFileImage = null;
@@ -207,6 +252,7 @@ class ImageCanvasState extends State<ImageCanvas> {
_usingFullRes = false;
_imageSize = Size.zero;
_markRenderDirty();
_syncZoom();
}
void _toggleFillFit() {
@@ -215,6 +261,7 @@ class ImageCanvasState extends State<ImageCanvas> {
_flipH = false;
WindowPersistence.saveFilled(_isFilled);
_markRenderDirty();
_syncZoom();
}
/// Ctrl+R: sağ çift tık gibi efektleri sıfırlar ve yatay aynalar; tekrar basınca geri alır.
@@ -250,6 +297,7 @@ class ImageCanvasState extends State<ImageCanvas> {
_checkFullResSwap();
_clamp();
_markRenderDirty();
_syncZoom();
}
void _onPanUpdate(DragUpdateDetails d) {
@@ -502,7 +550,10 @@ class ImageCanvasState extends State<ImageCanvas> {
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
_vpSize = constraints.biggest;
if (constraints.biggest != _vpSize) {
_vpSize = constraints.biggest;
_syncZoom();
}
_clamp();
return RepaintBoundary(
child: ValueListenableBuilder<int>(