Commit 45c3fa8
8
files
+278
added
-104
deleted
@@ -89,7 +89,7 @@ libluna/
89
89
libluna-i18n/ # wielojęzyczność: każdy język w pliku i18n/<kod>.lang + przełączanie w runtime
90
90
libluna-render/ # graf sceny + backend software (tiny-skia + fontdue)
91
91
libluna-platform/ # Wayland: xdg-shell (CSD), wl_shm, wejście, pętla zdarzeń, motyw w Frame
92
- libluna-widgets/ # widgety: ViewSwitcher, NavList, Menu, Button, Switch, Slider, DropDown, HeaderBar, TabBar/TabView, BottomSheet, Pager, PathBar, SearchEntry, ItemView, ScrollView, Stepper, CheckButton, RadioGroup
92
+ libluna-widgets/ # widgety: ViewSwitcher, NavList, Menu, Button, Switch, Slider, DropDown, HeaderBar, TabBar/TabView, BottomSheet, Pager, PathBar, SearchEntry, ItemView, ScrollView, Stepper, CheckButton, RadioGroup, Card, ActionRow
93
93
apps/
94
94
luna-demo/ # demo aplikacji środowiska Luna; też render offline do PNG
95
95
templates/
@@ -226,7 +226,7 @@ w `~/.config/luna/theme.conf`.
226
226
Rozmiary i paddingi kontrolek nie są już stałymi w widgetach, lecz **tokenami**
227
227
(`Theme::metrics`) podzielonymi na sekcje per widget: `headerbar`, `button`, `switch`,
228
228
`slider`, `dropdown`, `menu`, `nav`, `segmented`, `tabs`, `sheet`, `pager`, `pathbar`, `entry`,
229
-`itemview`, `scrollview`, `stepper`, `check`, `radio`. Dzięki temu wygląd steruje **także
229
+`itemview`, `scrollview`, `stepper`, `check`, `radio`, `card`, `row`. Dzięki temu wygląd steruje **także
230
230
geometrią**, nie tylko kolorami — np. `theme.metrics.switch.width` albo
231
231
`theme.metrics.headerbar.height`. Widgety czytają te wartości w `paint`/`layout`, więc
232
232
przyszły, instalowany styl może zmienić np. wysokość paska tytułu czy rozmiar przełącznika
@@ -300,10 +300,11 @@ XDG_CONFIG_HOME="$PWD/examples" cargo run -p luna-demo --bin render -- --lang fr
300
300
`PathBar` (okruszki z separatorem `›`, wskaźnikiem `▾` i zwijaniem do `…`), `SearchEntry`
301
301
(pole wyszukiwania: lupka, kursor, czyszczenie), `ItemView` (pliki/katalogi w siatce lub
302
302
liście, z zaznaczaniem i przewijaniem) i `ScrollView` (pionowe przewijanie: rolka + „kciuk”)
303
- oraz `Stepper`, `CheckButton` i `RadioGroup` (krokowiec, pole wyboru, wybór jednokrotny)
304
- oraz wspólny `PaintCtx`.
303
+ oraz `Stepper`, `CheckButton` i `RadioGroup` (krokowiec, pole wyboru, wybór jednokrotny),
304
+ a także `Card` (karta z nagłówkiem) i `ActionRow` (wiersz ustawień) oraz wspólny `PaintCtx`.
305
305
- `luna-demo`: `HeaderBar` jako widget (tytuł + `[—][□][✕]` po prawej + treść: `◧`, `+`,
306
- „Opcje ▾”, `☰`); **popover `☰`**, chowany i rozciągany (drag) panel boczny (`NavList`),
306
+ „Opcje ▾”, `☰`); karty i wiersze ustawień rysowane widgetami (`Card`, `ActionRow`);
307
+ **popover `☰`**, chowany i rozciągany (drag) panel boczny (`NavList`),
307
308
chowany **pasek dolny**, wybór motywu/widoku (`ViewSwitcher`), `Switch`, `Slider`, `DropDown`,
308
309
**zakładki** (`TabBar` + `TabView`) oraz **dolny panel** (`BottomSheet` z karuzelą `Pager`,
309
310
otwierany z menu `☰` → „O aplikacji”), a także kartę **Pliki** (`PathBar` + `SearchEntry` +
@@ -11,14 +11,16 @@ use libluna_core::{
11
11
use libluna_i18n::{tr, Locale};
12
12
use libluna_render::TextEngine;
13
13
use libluna_widgets::{
14
- BottomSheet, Button, CheckButton, ControlsLayout, ControlsSide, DropDown, HeaderBar, Item,
15
- ItemView, Menu, MenuEntry, NavEntry, NavList, Pager, PaintCtx, PathBar, RadioGroup, ScrollView,
16
- SearchEntry, Slider, Stepper, StepperSide, Switch, Tab, TabBar, TabView, ViewMode,
17
- ViewSwitcher, WindowControl,
14
+ ActionRow, BottomSheet, Button, Card, CheckButton, ControlsLayout, ControlsSide, DropDown,
15
+ HeaderBar, Item, ItemView, Menu, MenuEntry, NavEntry, NavList, Pager, PaintCtx, PathBar,
16
+ RadioGroup, ScrollView, SearchEntry, Slider, Stepper, StepperSide, Switch, Tab, TabBar,
17
+ TabView, ViewMode, ViewSwitcher, WindowControl,
18
18
};
19
19
20
20
const STATUS_H: f64 = 28.0;
21
21
const PAD: f64 = 12.0;
22
+/// Wysokość wiersza ustawień w demo.
23
+const ROW_H: f64 = 56.0;
22
24
/// Indeks pozycji „O aplikacji” w menu „☰” (otwiera dolny panel).
23
25
const ABOUT_ENTRY: usize = 4;
24
26
@@ -379,8 +381,8 @@ pub fn layout(size: Size, theme: &Theme, demo: &Demo) -> Layout {
379
381
(area.width() - 2.0 * lg).max(0.0),
380
382
208.0,
381
383
);
382
- let header_h = 40.0;
383
- let row_h = 56.0;
384
+ let header_h = theme.metrics.card.header;
385
+ let row_h = ROW_H;
384
386
let row = |i: usize| {
385
387
Rect::from_xywh(
386
388
card.x(),
@@ -1048,63 +1050,28 @@ fn draw_content(
1048
1050
(area.width() - 2.0 * lg).max(0.0),
1049
1051
208.0,
1050
1052
);
1051
- draw_card(paint, card, &tr("Appearance"));
1053
+ Card::new(tr("Appearance")).paint(paint, card);
1052
1054
1053
- paint.label_left(
1054
- card.x() + md,
1055
- lay.theme_switcher.center().y,
1056
- &tr("Theme"),
1057
- body,
1058
- weight::NORMAL,
1059
- theme.colors.text,
1060
- );
1055
+ draw_row_label(paint, card, lay.theme_switcher, &tr("Theme"));
1061
1056
theme_switcher(preference, demo.theme_hover).paint(paint, lay.theme_switcher);
1062
1057
1063
- paint.label_left(
1064
- card.x() + md,
1065
- lay.accents[0].center().y,
1066
- &tr("Accent color"),
1067
- body,
1068
- weight::NORMAL,
1069
- theme.colors.text,
1070
- );
1058
+ draw_row_label(paint, card, lay.accents[0], &tr("Accent color"));
1071
1059
draw_accent_dots(paint, &lay.accents, accent);
1072
1060
1073
- paint.label_left(
1074
- card.x() + md,
1075
- lay.language.center().y,
1076
- &tr("Language"),
1077
- body,
1078
- weight::NORMAL,
1079
- theme.colors.text,
1080
- );
1061
+ draw_row_label(paint, card, lay.language, &tr("Language"));
1081
1062
let codes = libluna_i18n::languages();
1082
1063
language_dropdown(&codes).paint(paint, lay.language);
1083
1064
1084
1065
// --- Karta 2: Ustawienia ---
1085
1066
let card2 = Rect::from_xywh(card.x(), card.bottom() + lg, card.width(), 320.0);
1086
- draw_card(paint, card2, &tr("Settings"));
1067
+ Card::new(tr("Settings")).paint(paint, card2);
1087
1068
1088
- paint.label_left(
1089
- card2.x() + md,
1090
- lay.option_a.center().y,
1091
- &tr("Option A"),
1092
- body,
1093
- weight::NORMAL,
1094
- theme.colors.text,
1095
- );
1069
+ draw_row_label(paint, card2, lay.option_a, &tr("Option A"));
1096
1070
let mut sw = Switch::new(demo.option_a);
1097
1071
sw.set_hovered(demo.switch_hover);
1098
1072
sw.paint(paint, lay.option_a);
1099
1073
1100
- paint.label_left(
1101
- card2.x() + md,
1102
- lay.volume_track.center().y,
1103
- &tr("Volume level"),
1104
- body,
1105
- weight::NORMAL,
1106
- theme.colors.text,
1107
- );
1074
+ draw_row_label(paint, card2, lay.volume_track, &tr("Volume level"));
1108
1075
Slider::new(demo.volume).paint(paint, lay.volume_track);
1109
1076
paint.label_right(
1110
1077
lay.volume_track.x() - 12.0,
@@ -1115,39 +1082,18 @@ fn draw_content(
1115
1082
theme.colors.text,
1116
1083
);
1117
1084
1118
- paint.label_left(
1119
- card2.x() + md,
1120
- lay.view_switcher.center().y,
1121
- &tr("View"),
1122
- body,
1123
- weight::NORMAL,
1124
- theme.colors.text,
1125
- );
1085
+ draw_row_label(paint, card2, lay.view_switcher, &tr("View"));
1126
1086
view_switcher(demo.view_mode, demo.view_hover).paint(paint, lay.view_switcher);
1127
1087
1128
- paint.label_left(
1129
- card2.x() + md,
1130
- lay.bottom_bar.center().y,
1131
- &tr("Bottom bar"),
1132
- body,
1133
- weight::NORMAL,
1134
- theme.colors.text,
1135
- );
1088
+ draw_row_label(paint, card2, lay.bottom_bar, &tr("Bottom bar"));
1136
1089
Switch::new(demo.show_bottom_bar).paint(paint, lay.bottom_bar);
1137
1090
1138
- paint.label_left(
1139
- card2.x() + md,
1140
- lay.sidebar_toggle.center().y,
1141
- &tr("Sidebar"),
1142
- body,
1143
- weight::NORMAL,
1144
- theme.colors.text,
1145
- );
1091
+ draw_row_label(paint, card2, lay.sidebar_toggle, &tr("Sidebar"));
1146
1092
Switch::new(demo.sidebar_visible).paint(paint, lay.sidebar_toggle);
1147
1093
1148
1094
// --- Karta 3: Zakładki ---
1149
1095
let card3 = Rect::from_xywh(card.x(), card2.bottom() + lg, card.width(), 152.0);
1150
- draw_card(paint, card3, &tr("Tabs"));
1096
+ Card::new(tr("Tabs")).paint(paint, card3);
1151
1097
1152
1098
// Wiersz 0: pasek zakładek (widget TabBar).
1153
1099
tab_bar(demo).paint(paint, lay.tabs);
@@ -1173,7 +1119,7 @@ fn draw_content(
1173
1119
1174
1120
// --- Karta 4: Pliki (PathBar + SearchEntry + ItemView) ---
1175
1121
let card4 = Rect::from_xywh(card.x(), card3.bottom() + lg, card.width(), 196.0);
1176
- draw_card(paint, card4, &tr("Files"));
1122
+ Card::new(tr("Files")).paint(paint, card4);
1177
1123
path_bar(demo).paint(paint, lay.path);
1178
1124
1179
1125
let mut search = demo.search.clone();
@@ -1187,27 +1133,18 @@ fn draw_content(
1187
1133
scrollbar.paint_scrollbar(paint, lay.items);
1188
1134
}
1189
1135
1190
-fn draw_card(paint: &mut PaintCtx, card: Rect, title: &str) {
1136
+/// Rysuje etykietę wiersza ustawień (`ActionRow`) z miejscem na kontrolkę po prawej.
1137
+fn draw_row_label(paint: &mut PaintCtx, container: Rect, control: Rect, title: &str) {
1191
1138
let theme = *paint.theme;
1192
- paint
1193
- .scene
1194
- .fill_rounded_rect(card, theme.radius.large, theme.colors.card_bg);
1195
- paint
1196
- .scene
1197
- .stroke_rounded_rect(card, theme.radius.large, 1.0, theme.colors.border);
1198
- let header_h = 40.0;
1199
- paint.label_left(
1200
- card.x() + theme.spacing.md,
1201
- card.y() + header_h / 2.0,
1202
- title,
1203
- theme.typography.body.size,
1204
- weight::BOLD,
1205
- theme.colors.text,
1206
- );
1207
- paint.scene.fill_rect(
1208
- Rect::from_xywh(card.x(), card.y() + header_h, card.width(), 1.0),
1209
- theme.colors.border,
1139
+ let md = theme.spacing.md;
1140
+ let row = Rect::from_xywh(
1141
+ container.x(),
1142
+ control.center().y - ROW_H / 2.0,
1143
+ container.width(),
1144
+ ROW_H,
1210
1145
);
1146
+ let suffix = (container.width() - (control.x() - container.x()) - md).max(0.0);
1147
+ ActionRow::new(title).paint(paint, row, suffix);
1211
1148
}
1212
1149
1213
1150
fn draw_accent_dots(paint: &mut PaintCtx, dots: &[Rect; 9], accent: Accent) {
@@ -26,8 +26,9 @@ pub use event::{
26
26
pub use geometry::{Insets, Point, Rect, Size};
27
27
pub use scale::Scale;
28
28
pub use style::{
29
- BottomSheetMetrics, ButtonMetrics, CheckMetrics, DropDownMetrics, EntryMetrics,
30
- HeaderBarMetrics, ItemViewMetrics, MenuMetrics, Metrics, NavMetrics, PagerMetrics,
31
- PathBarMetrics, RadioMetrics, Radius, ScrollViewMetrics, SegmentedMetrics, SliderMetrics,
32
- Spacing, StepperMetrics, SwitchMetrics, TabsMetrics, Theme, Typography,
29
+ ActionRowMetrics, BottomSheetMetrics, ButtonMetrics, CardMetrics, CheckMetrics,
30
+ DropDownMetrics, EntryMetrics, HeaderBarMetrics, ItemViewMetrics, MenuMetrics, Metrics,
31
+ NavMetrics, PagerMetrics, PathBarMetrics, RadioMetrics, Radius, ScrollViewMetrics,
32
+ SegmentedMetrics, SliderMetrics, Spacing, StepperMetrics, SwitchMetrics, TabsMetrics, Theme,
33
+ Typography,
33
34
};
@@ -429,6 +429,24 @@ pub struct RadioMetrics {
429
429
pub row_h: f64,
430
430
}
431
431
432
+/// Geometria karty ([`Card`](crate::style)).
433
+#[derive(Debug, Clone, Copy, PartialEq)]
434
+pub struct CardMetrics {
435
+ /// Wysokość nagłówka karty.
436
+ pub header: f64,
437
+ /// Padding wewnętrzny (nagłówek).
438
+ pub pad: f64,
439
+}
440
+
441
+/// Geometria wiersza akcji ([`ActionRow`](crate::style)).
442
+#[derive(Debug, Clone, Copy, PartialEq)]
443
+pub struct ActionRowMetrics {
444
+ /// Padding poziomy (lewy i prawy).
445
+ pub pad_x: f64,
446
+ /// Odstęp między tytułem a podtytułem.
447
+ pub gap: f64,
448
+}
449
+
432
450
/// Geometria kontrolek („component metrics”) — rozmiary i paddingi w pikselach logicznych.
433
451
///
434
452
/// Widgety nie mają już własnych stałych rozmiarów: czytają je z motywu, dzięki czemu
@@ -454,6 +472,8 @@ pub struct Metrics {
454
472
pub stepper: StepperMetrics,
455
473
pub check: CheckMetrics,
456
474
pub radio: RadioMetrics,
475
+ pub card: CardMetrics,
476
+ pub row: ActionRowMetrics,
457
477
}
458
478
459
479
impl Metrics {
@@ -562,6 +582,14 @@ impl Metrics {
562
582
gap: 10.0,
563
583
row_h: 30.0,
564
584
},
585
+ card: CardMetrics {
586
+ header: 40.0,
587
+ pad: 12.0,
588
+ },
589
+ row: ActionRowMetrics {
590
+ pad_x: 12.0,
591
+ gap: 8.0,
592
+ },
565
593
}
566
594
}
567
595
}
568
596
new file mode 100644
@@ -0,0 +1,109 @@
1
+//! `ActionRow` — wiersz ustawień: tytuł (i podtytuł) z lewej, obszar na kontrolkę z prawej.
2
+//!
3
+//! Widget immediate: rysuje tekst wiersza i zwraca [`Rect`] na kontrolkę (`suffix`), którą
4
+//! umieszcza aplikacja. Padding pochodzi z tokenów motywu ([`ActionRowMetrics`](libluna_core::ActionRowMetrics)).
5
+
6
+use libluna_core::style::weight;
7
+use libluna_core::{Rect, Theme};
8
+
9
+use crate::ctx::PaintCtx;
10
+
11
+/// Wiersz akcji (ustawienie).
12
+#[derive(Debug, Clone, Default)]
13
+pub struct ActionRow {
14
+ title: String,
15
+ subtitle: Option<String>,
16
+}
17
+
18
+impl ActionRow {
19
+ pub fn new(title: impl Into<String>) -> Self {
20
+ Self {
21
+ title: title.into(),
22
+ subtitle: None,
23
+ }
24
+ }
25
+
26
+ pub fn subtitle(mut self, subtitle: impl Into<String>) -> Self {
27
+ self.subtitle = Some(subtitle.into());
28
+ self
29
+ }
30
+
31
+ pub fn title(&self) -> &str {
32
+ &self.title
33
+ }
34
+
35
+ pub fn subtitle_text(&self) -> Option<&str> {
36
+ self.subtitle.as_deref()
37
+ }
38
+
39
+ /// Obszar na kontrolkę (prawa strona) o zadanej szerokości.
40
+ pub fn suffix_rect(&self, rect: Rect, theme: &Theme, suffix_width: f64) -> Rect {
41
+ let pad = theme.metrics.row.pad_x;
42
+ Rect::from_xywh(
43
+ rect.right() - pad - suffix_width,
44
+ rect.y(),
45
+ suffix_width,
46
+ rect.height(),
47
+ )
48
+ }
49
+
50
+ /// Rysuje tytuł (i podtytuł); zwraca obszar na kontrolkę.
51
+ pub fn paint(&self, ctx: &mut PaintCtx, rect: Rect, suffix_width: f64) -> Rect {
52
+ let theme = *ctx.theme;
53
+ let pad = theme.metrics.row.pad_x;
54
+ let x = rect.x() + pad;
55
+ match &self.subtitle {
56
+ Some(subtitle) => {
57
+ let gap = theme.metrics.row.gap;
58
+ ctx.label_left(
59
+ x,
60
+ rect.center().y - gap / 2.0 - 2.0,
61
+ &self.title,
62
+ theme.typography.body.size,
63
+ weight::NORMAL,
64
+ theme.colors.text,
65
+ );
66
+ ctx.label_left(
67
+ x,
68
+ rect.center().y + gap / 2.0 + 5.0,
69
+ subtitle,
70
+ theme.typography.caption.size,
71
+ weight::NORMAL,
72
+ theme.colors.dim_text,
73
+ );
74
+ }
75
+ None => ctx.label_left(
76
+ x,
77
+ rect.center().y,
78
+ &self.title,
79
+ theme.typography.body.size,
80
+ weight::NORMAL,
81
+ theme.colors.text,
82
+ ),
83
+ }
84
+ self.suffix_rect(rect, &theme, suffix_width)
85
+ }
86
+}
87
+
88
+#[cfg(test)]
89
+mod tests {
90
+ use super::*;
91
+
92
+ #[test]
93
+ fn suffix_is_right_aligned_with_padding() {
94
+ let theme = Theme::light();
95
+ let row = ActionRow::new("Motyw");
96
+ let rect = Rect::from_xywh(0.0, 0.0, 300.0, 56.0);
97
+ let s = row.suffix_rect(rect, &theme, 120.0);
98
+ assert!((s.right() - (rect.right() - theme.metrics.row.pad_x)).abs() < 1e-9);
99
+ assert!((s.width() - 120.0).abs() < 1e-9);
100
+ assert!((s.height() - rect.height()).abs() < 1e-9);
101
+ }
102
+
103
+ #[test]
104
+ fn subtitle_builder() {
105
+ let row = ActionRow::new("Sieć").subtitle("Włączone");
106
+ assert_eq!(row.title(), "Sieć");
107
+ assert_eq!(row.subtitle_text(), Some("Włączone"));
108
+ }
109
+}
0
110
new file mode 100644
@@ -0,0 +1,91 @@
1
+//! `Card` — karta (powierzchnia podniesiona) z nagłówkiem i obszarem treści.
2
+//!
3
+//! Widget immediate: rysuje tło, obramowanie, nagłówek i separator, a [`Card::paint`] zwraca
4
+//! obszar zawartości, w którym aplikacja umieszcza własne wiersze.
5
+
6
+use libluna_core::style::weight;
7
+use libluna_core::{Rect, Theme};
8
+
9
+use crate::ctx::PaintCtx;
10
+
11
+/// Karta z nagłówkiem.
12
+#[derive(Debug, Clone, Default)]
13
+pub struct Card {
14
+ title: String,
15
+}
16
+
17
+impl Card {
18
+ pub fn new(title: impl Into<String>) -> Self {
19
+ Self {
20
+ title: title.into(),
21
+ }
22
+ }
23
+
24
+ pub fn title(&self) -> &str {
25
+ &self.title
26
+ }
27
+
28
+ /// Wysokość nagłówka (z metryk).
29
+ pub fn header_height(&self, theme: &Theme) -> f64 {
30
+ theme.metrics.card.header
31
+ }
32
+
33
+ /// Obszar zawartości (poniżej nagłówka i separatora).
34
+ pub fn content_rect(&self, rect: Rect, theme: &Theme) -> Rect {
35
+ let h = self.header_height(theme);
36
+ Rect::from_xywh(
37
+ rect.x(),
38
+ rect.y() + h,
39
+ rect.width(),
40
+ (rect.height() - h).max(0.0),
41
+ )
42
+ }
43
+
44
+ /// Rysuje kartę; zwraca obszar zawartości.
45
+ pub fn paint(&self, ctx: &mut PaintCtx, rect: Rect) -> Rect {
46
+ let theme = *ctx.theme;
47
+ ctx.scene
48
+ .fill_rounded_rect(rect, theme.radius.large, theme.colors.card_bg);
49
+ ctx.scene
50
+ .stroke_rounded_rect(rect, theme.radius.large, 1.0, theme.colors.border);
51
+
52
+ let h = self.header_height(&theme);
53
+ ctx.label_left(
54
+ rect.x() + theme.metrics.card.pad,
55
+ rect.y() + h / 2.0,
56
+ &self.title,
57
+ theme.typography.body.size,
58
+ weight::BOLD,
59
+ theme.colors.text,
60
+ );
61
+ ctx.scene.fill_rect(
62
+ Rect::from_xywh(rect.x(), rect.y() + h, rect.width(), 1.0),
63
+ theme.colors.border,
64
+ );
65
+ self.content_rect(rect, &theme)
66
+ }
67
+}
68
+
69
+#[cfg(test)]
70
+mod tests {
71
+ use super::*;
72
+
73
+ #[test]
74
+ fn content_is_below_header() {
75
+ let theme = Theme::light();
76
+ let card = Card::new("Wygląd");
77
+ let rect = Rect::from_xywh(0.0, 0.0, 300.0, 200.0);
78
+ let content = card.content_rect(rect, &theme);
79
+ assert!((content.y() - (rect.y() + theme.metrics.card.header)).abs() < 1e-9);
80
+ assert!((content.height() - (200.0 - theme.metrics.card.header)).abs() < 1e-9);
81
+ }
82
+
83
+ #[test]
84
+ fn header_height_from_metrics() {
85
+ let theme = Theme::light();
86
+ assert_eq!(
87
+ Card::new("X").header_height(&theme),
88
+ theme.metrics.card.header
89
+ );
90
+ }
91
+}
@@ -26,6 +26,8 @@
26
26
//! - [`Stepper`] — krokowiec (`−` wartość `+`).
27
27
//! - [`CheckButton`] — pole wyboru (checkbox) z etykietą.
28
28
//! - [`RadioGroup`] — lista opcji z pojedynczym wyborem.
29
+//! - [`Card`] — karta z nagłówkiem i obszarem treści.
30
+//! - [`ActionRow`] — wiersz ustawień (tytuł/podtytuł + obszar na kontrolkę).
29
31
//!
30
32
//! ```no_run
31
33
//! use libluna_widgets::{PaintCtx, ViewSwitcher};
@@ -35,8 +37,10 @@
35
37
//! # }
36
38
//! ```
37
39
40
+pub mod action_row;
38
41
pub mod bottom_sheet;
39
42
pub mod button;
43
+pub mod card;
40
44
pub mod check_button;
41
45
pub mod ctx;
42
46
pub mod dropdown;
@@ -57,8 +61,10 @@ pub mod tab_bar;
57
61
pub mod tab_view;
58
62
pub mod view_switcher;
59
63
64
+pub use action_row::ActionRow;
60
65
pub use bottom_sheet::BottomSheet;
61
66
pub use button::{Button, ButtonVariant};
67
+pub use card::Card;
62
68
pub use check_button::CheckButton;
63
69
pub use ctx::PaintCtx;
64
70
pub use dropdown::DropDown;
@@ -39,7 +39,8 @@ a na końcu odtworzył realne aplikacje środowiska Luna z makiet.
39
39
(zakładki: pasek + model stron), `BottomSheet` (dolny panel modalny), `Pager` (karuzela stron),
40
40
`PathBar` (okruszki), `SearchEntry` (pole wyszukiwania), `ItemView` (pliki/katalogi: siatka
41
41
lub lista) i `ScrollView` (przewijanie), oraz `Stepper`, `CheckButton` i `RadioGroup`
42
- (krokowiec, checkbox, wybór jednokrotny); wspólny `PaintCtx`; testy hit-testingu bez okna.
42
+ (krokowiec, checkbox, wybór jednokrotny); do tego `Card` i `ActionRow` (karty i wiersze
43
+ ustawień); wspólny `PaintCtx`; testy hit-testingu bez okna.
43
44
- `libluna-platform`: realne akcje okna z `Frame` (`close`/`minimize`/`toggle_maximize`).
44
45
- Demo używa widgetów zamiast ręcznego rysowania (pasek boczny, wybór motywu/widoku, menu `☰`,
45
46
przycisk „Opcje”, przełączniki, suwak, wybór języka, pasek tytułu, zakładki `TabBar`/`TabView`).
@@ -101,7 +102,7 @@ a na końcu odtworzył realne aplikacje środowiska Luna z makiet.
101
102
| SearchEntry / SearchBar | `libluna-widgets` | Files |
102
103
| GridView / ListView | `libluna-widgets` (`ItemView`) | Files |
103
104
| ScrollView | `libluna-widgets` | obie |
104
-| Card + ActionRow | `libluna-adwaita` | Settings |
105
+| Card + ActionRow | `libluna-widgets` | Settings |
105
106
| Switch | `libluna-widgets` | Settings |
106
107
| Slider (+ etykieta %) | `libluna-widgets` | Settings |
107
108
| DropDown | `libluna-widgets` | Settings |