Commit 3a3cdab
4
files
+98
added
-6
deleted
@@ -269,10 +269,11 @@ cargo run -p luna-demo --bin render -- --theme examples/luna/themes/nord.theme d
269
269
cargo run -p luna-demo --bin layout -- --theme examples/luna/themes/nord.theme --out docs/images/layout-nord.png
270
270
```
271
271
272
-Do zrobienia pozostaje **okno podglądu na żywo** przy wyborze (miniatura). Sam wybór stylu
273
-działa już w demo: **menu `☰` → „Styl”** otwiera listę („Domyślny” + style z plików),
274
-a wybór jest natychmiast stosowany i zapisywany w `theme.conf`. W headless demo/testach katalog
275
-ze stylami wskazuje `LUNA_THEMES_DIR`.
272
+Do zrobienia pozostaje **okno podglądu na żywo** wewnątrz aplikacji (miniatura aktualizowana przy
273
+przewijaniu listy). Sam wybór stylu działa już w demo: **menu `☰` → „Styl”** otwiera listę
274
+(„Domyślny” + style z plików), a wybór jest natychmiast stosowany i zapisywany w `theme.conf`.
275
+Headlessowy podgląd (montaż motywów) to `cargo run -p luna-demo --bin styles` →
276
+`docs/images/styles.png`. W headless demo/testach katalog ze stylami wskazuje `LUNA_THEMES_DIR`.
276
277
277
278
### Co jeszcze decyduje o „looku GTK4”
278
279
@@ -384,6 +385,11 @@ Style instalowane z plików (`examples/luna/themes/nord.theme`, `paper.theme`)
384
385

385
386

386
387
388
+Podgląd kilku motywów (headless, `cargo run -p luna-demo --bin styles`) — ten sam realny
389
+interfejs w każdym stylu:
390
+
391
+
392
+
387
393
Przebieg interakcji (headless `simulate`) — m.in. menu `☰`, dolny panel, powiadomienie (`Toast`)
388
394
karta 12 i ekran stanu („Brak wyników”) klatka 14:
389
395
390
396
new file mode 100644
@@ -0,0 +1,84 @@
1
+//! Podgląd stylów — renderuje ten sam (realny) interfejs w kilku motywach i składa
2
+//! montaż. Headlessowy odpowiednik „okna podglądu” przy wyborze stylu.
3
+//!
4
+//! Uruchom: `cargo run -p luna-demo --bin styles`
5
+//! Efekt: `docs/images/styles.png`
6
+
7
+use std::error::Error;
8
+use std::path::Path;
9
+
10
+use libluna_core::{Accent, Size, ThemePreference};
11
+use libluna_render::{Scene, SoftwareRenderer, TextEngine};
12
+use luna_demo::ui::{self, Demo};
13
+use tiny_skia::{FilterQuality, IntSize, Pixmap, PixmapPaint, Transform};
14
+
15
+const W: u32 = 900;
16
+const H: u32 = 1028;
17
+const COLS: u32 = 2;
18
+
19
+/// (etykieta, preferencja, ścieżka stylu) — kolejność = kolejność kafli.
20
+fn specs<'a>() -> [(&'a str, ThemePreference, Option<&'a str>); 4] {
21
+ [
22
+ ("Luna Light", ThemePreference::Light, None),
23
+ ("Luna Dark", ThemePreference::Dark, None),
24
+ ("Nord", ThemePreference::Dark, Some("nord.theme")),
25
+ ("Paper", ThemePreference::Light, Some("paper.theme")),
26
+ ]
27
+}
28
+
29
+fn themes_dir() -> std::path::PathBuf {
30
+ Path::new(env!("CARGO_MANIFEST_DIR")).join("../../examples/luna/themes")
31
+}
32
+
33
+fn main() -> Result<(), Box<dyn Error>> {
34
+ let text = TextEngine::system();
35
+ let size = Size::new(W as f64, H as f64);
36
+ let dir = themes_dir();
37
+
38
+ // Kafle: render interfejsu w danym motywie.
39
+ let mut tiles: Vec<Vec<u8>> = Vec::new();
40
+ for (label, pref, style) in specs() {
41
+ let mut theme = pref.resolve(false, Accent::Blue);
42
+ if let Some(file) = style {
43
+ match libluna_core::theme_file::load_spec(&dir.join(file).to_string_lossy()) {
44
+ Some(patch) => theme = patch.build(theme),
45
+ None => eprintln!("{label}: style not found: {file}"),
46
+ }
47
+ }
48
+ let mut scene = Scene::new();
49
+ ui::build_scene(
50
+ &mut scene,
51
+ &theme,
52
+ &text,
53
+ size,
54
+ &Demo::default(),
55
+ pref,
56
+ Accent::Blue,
57
+ );
58
+ let mut renderer = SoftwareRenderer::new(W, H);
59
+ renderer.draw(&scene, 1.0, theme.colors.window_bg);
60
+ tiles.push(renderer.data().to_vec());
61
+ }
62
+
63
+ // Montaż `COLS` x `rows` (skalowanie + przesunięcie w jednej transformacji).
64
+ let rows = (tiles.len() as u32).div_ceil(COLS);
65
+ let cell_w = W / COLS;
66
+ let cell_h = (H as f64 * cell_w as f64 / W as f64).round() as u32;
67
+ let mut montage = Pixmap::new(cell_w * COLS, cell_h * rows).unwrap();
68
+ let paint = PixmapPaint {
69
+ quality: FilterQuality::Bilinear,
70
+ ..Default::default()
71
+ };
72
+ for (i, pixels) in tiles.iter().enumerate() {
73
+ let src = Pixmap::from_vec(pixels.clone(), IntSize::from_wh(W, H).unwrap()).unwrap();
74
+ let t = Transform::from_scale(cell_w as f32 / W as f32, cell_h as f32 / H as f32)
75
+ .post_translate(
76
+ (i as u32 % COLS) as f32 * cell_w as f32,
77
+ (i as u32 / COLS) as f32 * cell_h as f32,
78
+ );
79
+ montage.draw_pixmap(0, 0, src.as_ref(), &paint, t, None);
80
+ }
81
+ montage.save_png("docs/images/styles.png")?;
82
+ println!("saved docs/images/styles.png ({} styles)", tiles.len());
83
+ Ok(())
84
+}
0
85
new file mode 100644
1
86
Binary files /dev/null and b/docs/images/styles.png differ
@@ -57,8 +57,10 @@ a na końcu odtworzył realne aplikacje środowiska Luna z makiet.
57
57
przykłady `examples/luna/themes/*.theme`, podgląd przez `render/layout --theme`.
58
58
- Wybór stylu w UI: menu `☰` → „Styl” (lista `available_themes()`, „Domyślny” + style z plików),
59
59
stosowany na żywo i zapisywany w `theme.conf`; `LUNA_THEMES_DIR` dla dev/testów.
60
-- Dalej: **okno podglądu** przy wyborze (miniatura z realnych widgetów), scroll/overflow
61
- w `TabBar`, migracja `luna-demo` na drzewo układu.
60
+- Podgląd motywów (headless): `bin/styles` renderuje realny interfejs w kilku stylach i składa
61
+ montaż (`docs/images/styles.png`) — odpowiednik „okna podglądu”.
62
+- Dalej: **okno podglądu na żywo w aplikacji**, scroll/overflow w `TabBar`,
63
+ migracja `luna-demo` na drzewo układu.
62
64
63
65
## M2 — Układ + widgety bazowe
64
66
- Dwuprzebiegowy silnik measure/allocate — ✅ **częściowo** (silnik + kontenery `VBox`/`HBox`,