Skip to content

Commit 20c0181

Browse files
committed
Liść na wietrze
1 parent cd61b77 commit 20c0181

7 files changed

Lines changed: 75 additions & 247 deletions

File tree

cedinia/AGENTS.md

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,16 +69,48 @@ cedinia/src/
6969
├── colors.slint # CediniaColors theme
7070
├── settings_screen.slint # Settings screen layout
7171
├── settings_components.slint # ToggleRow, SegmentRow, DropdownRow, TextInputRow
72-
├── results_list.slint # Scrollable results list (MomentumScrollView)
7372
├── similar_images_gallery.slint
7473
├── directories_screen.slint # Add/remove directory paths
75-
├── momentum_scroll.slint # Touch momentum scroll (custom component)
7674
├── components.slint # Shared components
7775
└── …
7876
```
7977

8078
---
8179

80+
## Slint Performance: ListView vs. for-in / ScrollView
81+
82+
When displaying more than a few dozen items, **always use `ListView`** (from
83+
`std-widgets.slint`)
84+
rather than a bare `ScrollView` / `for` loop inside a `VerticalLayout`.
85+
86+
```slint
87+
// SLOW – instantiates every item upfront, O(n) per frame
88+
ScrollView {
89+
VerticalLayout {
90+
for item in model : Row { ... }
91+
}
92+
}
93+
94+
// SLOW - same as above
95+
VerticalLayout {
96+
for item in model : Row { ... }
97+
}
98+
99+
// FAST – virtual scroll via Flickable's Repeater optimization
100+
ListView {
101+
for item in model : Row { ... }
102+
}
103+
```
104+
105+
`ListView` uses Slint's Repeater-inside-Flickable
106+
optimization: only visible rows (plus a small buffer) are instantiated at any
107+
given time. With a plain `ScrollView` + `VerticalLayout`, all N items are
108+
created and visited on every frame.
109+
110+
Reference: Slint issue [#11021](https://github.com/slint-ui/slint/issues/11021).
111+
112+
---
113+
82114
## Scan Data Flow
83115

84116
```
@@ -112,13 +144,6 @@ Custom settings row with a scrollable popup:
112144
- Content wrapped in `ScrollView` with `viewport-height = options.length * 48px`,
113145
allowing the 27-language list to scroll.
114146

115-
### MomentumScrollView (`ui/momentum_scroll.slint`)
116-
117-
Custom touch-scroll component used for the results list and image gallery.
118-
Provides momentum / deceleration on swipe. Not available in krokiet.
119-
120-
---
121-
122147
## Translation System
123148

124149
Same workaround as krokiet:
@@ -188,7 +213,6 @@ and used by `czkawka_core`'s config path logic.
188213
| Video tools | No | Yes |
189214
| Video optimizer | No | Yes |
190215
| Bad names tool | Yes | No |
191-
| Touch scroll | MomentumScrollView | Standard |
192216
| File picker | JNI (Android) / rfd (desktop) | rfd |
193217
| Settings | JSON, no presets | JSON, 11 presets |
194218
| Column indices | `common.rs` (own set) | `common.rs` (own set) |

cedinia/src/app.rs

Lines changed: 0 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -238,8 +238,6 @@ impl ScanResultHandler for GuiHandler {
238238
win.set_similar_images_model(make_file_model(items));
239239
win.set_similar_images_groups(ModelRc::new(VecModel::from(groups)));
240240

241-
win.global::<AppState>().set_gallery_scroll_y(0.0);
242-
243241
let mut cancel_guard = thumb_cancel.lock().unwrap();
244242
cancel_guard.store(true, Ordering::Relaxed);
245243
let new_cancel = Arc::new(AtomicBool::new(false));
@@ -427,64 +425,6 @@ fn run_app_inner(
427425
wire_licenses_popup(&window);
428426
wire_save_settings_now(&window, included_dirs.clone(), excluded_dirs.clone(), referenced_dirs.clone());
429427

430-
let gallery_momentum: Rc<std::cell::RefCell<f32>> = Rc::new(std::cell::RefCell::new(0.0));
431-
432-
{
433-
let weak_g = window.as_weak();
434-
let mom = Rc::clone(&gallery_momentum);
435-
window.global::<AppState>().on_gallery_swiped(move |total_delta, vel_px| {
436-
if let Some(win) = weak_g.upgrade() {
437-
let current = win.global::<AppState>().get_gallery_scroll_y();
438-
let max_scroll = win.global::<AppState>().get_gallery_max_scroll_f();
439-
440-
let new_y = (current + total_delta).clamp(-max_scroll, 0.0);
441-
win.global::<AppState>().set_gallery_scroll_y(new_y);
442-
443-
const VEL_THRESHOLD: f32 = 4.0;
444-
*mom.borrow_mut() = if vel_px.abs() < VEL_THRESHOLD { 0.0 } else { vel_px * 1.5 };
445-
}
446-
});
447-
}
448-
449-
{
450-
let mom = Rc::clone(&gallery_momentum);
451-
window.global::<AppState>().on_gallery_stop_momentum(move || {
452-
*mom.borrow_mut() = 0.0;
453-
});
454-
}
455-
456-
let scroll_timer = Timer::default();
457-
{
458-
let weak_sc = window.as_weak();
459-
let mom_sc = Rc::clone(&gallery_momentum);
460-
scroll_timer.start(TimerMode::Repeated, std::time::Duration::from_millis(16), move || {
461-
// ── Gallery fling ─────────────────────────────────────────────
462-
let mut mom = mom_sc.borrow_mut();
463-
if mom.abs() > 0.3 {
464-
if let Some(win) = weak_sc.upgrade() {
465-
let current = win.global::<AppState>().get_gallery_scroll_y();
466-
let max_scroll = win.global::<AppState>().get_gallery_max_scroll_f();
467-
let new_y = (current + *mom).clamp(-max_scroll, 0.0);
468-
win.global::<AppState>().set_gallery_scroll_y(new_y);
469-
470-
*mom *= 0.95;
471-
472-
if new_y >= 0.0 || new_y <= -max_scroll {
473-
*mom = 0.0;
474-
}
475-
}
476-
} else if let Some(win) = weak_sc.upgrade() {
477-
let max_s = win.global::<AppState>().get_gallery_max_scroll_f();
478-
let cur = win.global::<AppState>().get_gallery_scroll_y();
479-
if max_s > 0.0 && cur < -max_s {
480-
win.global::<AppState>().set_gallery_scroll_y(-max_s);
481-
} else if cur > 0.0 {
482-
win.global::<AppState>().set_gallery_scroll_y(0.0);
483-
}
484-
}
485-
});
486-
}
487-
488428
let weak = window.as_weak();
489429
let thumb_rx = Rc::new(std::cell::RefCell::new(thumb_rx));
490430
let scan_gen_poll = scan_gen;

cedinia/src/settings/gui_settings_values.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -133,12 +133,7 @@ impl StringComboBoxItems {
133133
("smallest", SearchMode::SmallestFiles, DisplaySpec::Translatable("option_search_mode_smallest")),
134134
]);
135135

136-
let big_files_count = Self::convert(&[
137-
("10", "10", 10usize),
138-
("100", "100", 100),
139-
("1000", "1000", 1000),
140-
("10000", "10000", 10000) // Too slowefor now
141-
]);
136+
let big_files_count = Self::convert(&[("10", "10", 10usize), ("100", "100", 100), ("1000", "1000", 1000), ("10000", "10000", 10000)]);
142137

143138
let similarity_preset = Self::convert_i18n(&[
144139
("very_high", SimilarityPreset::VeryHigh, DisplaySpec::Translatable("option_similarity_very_high")),

cedinia/ui/app_state.slint

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -193,12 +193,6 @@ export global AppState {
193193

194194
in-out property <bool> similar_images_gallery_mode: true;
195195

196-
in-out property <length> gallery_scroll_y: 0px;
197-
in-out property <float> gallery_max_scroll_f: 0.0;
198-
// total_delta: full swipe displacement (px) vel: EMA velocity at release (px per touch-event)
199-
callback gallery_swiped(float, float);
200-
callback gallery_stop_momentum();
201-
202196

203197
in-out property <string> diag_thumbnails_size: "-";
204198
in-out property <string> diag_app_cache_size: "-";

cedinia/ui/momentum_scroll.slint

Lines changed: 0 additions & 149 deletions
This file was deleted.

cedinia/ui/similar_images_gallery.slint

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { ScanState, SimilarGroupCard } from "common.slint";
33
import { AppState, SimilarImagesSettings } from "app_state.slint";
44
import { StatusChip, TouchButton } from "components.slint";
55
import { Translations } from "translations.slint";
6-
import { MomentumScrollView } from "momentum_scroll.slint";
6+
import { ListView } from "std-widgets.slint";
77

88
component GalleryImageCell {
99
in property <image> thumbnail;
@@ -271,11 +271,9 @@ export component SimilarImagesGallery {
271271
property <string> ctx_open_folder: "";
272272
property <string> ctx_item_name: "";
273273

274-
// Set to true while any per-row horizontal SGR is active so the vertical
275-
// MomentumScrollView does not steal the gesture during reversals.
276-
// Cleared only in the horizontal sgr.swiped handler (on finger-up).
277274
property <bool> _h_locked: false;
278275

276+
279277
VerticalLayout {
280278
spacing: 0px;
281279

@@ -303,15 +301,8 @@ export component SimilarImagesGallery {
303301
// Inline the scroller directly (like ResultsList does) so that
304302
// vertical-stretch: 1.0 is applied to a real element in the layout,
305303
// not wrapped inside an intermediate component.
306-
if groups.length > 0 : gallery_scroll := MomentumScrollView {
304+
if groups.length > 0 : gallery_scroll := ListView {
307305
vertical-stretch: 1.0;
308-
scroll_y <=> AppState.gallery_scroll_y;
309-
lock_vertical: root._h_locked;
310-
swiped(d, v, m) => {
311-
AppState.gallery_max_scroll_f = m;
312-
AppState.gallery_swiped(d, v);
313-
}
314-
stop_momentum => { AppState.gallery_stop_momentum(); }
315306

316307
for group in root.groups : VerticalLayout {
317308
padding-left: 8px;
@@ -345,7 +336,7 @@ export component SimilarImagesGallery {
345336
height: 244px;
346337
swiped => {
347338
// Horizontal gesture finished — release the lock so the
348-
// outer MomentumScrollView can handle verticals again.
339+
// outer ListView can handle verticals again.
349340
root._h_locked = false;
350341
self.committed_x = self.committed_x + self.current-position.x - self.pressed-position.x;
351342
if self.committed_x > 0px { self.committed_x = 0px; }
@@ -377,8 +368,7 @@ export component SimilarImagesGallery {
377368
checked: img.checked;
378369
is_reference: img.is_reference;
379370
// Cancel long-press when either the horizontal or the
380-
// vertical (MomentumScrollView) SGR claims the gesture.
381-
any_sgr_swiping: sgr.swiping || gallery_scroll.is_swiping;
371+
any_sgr_swiping: sgr.swiping;
382372
cell_long_pressed(f, d, n) => {
383373
root.ctx_open_file = f;
384374
root.ctx_open_folder = d;
@@ -390,7 +380,7 @@ export component SimilarImagesGallery {
390380
}
391381
}
392382

393-
// When the horizontal swipe is confirmed, lock the vertical SGR.
383+
// Track horizontal swipe state.
394384
// Safe: depends only on sgr.swiping — no layout properties.
395385
property <bool> _row_swiping: sgr.swiping;
396386
changed _row_swiping => {

0 commit comments

Comments
 (0)