🔒 Repository is read-only – file editing is disabled.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
//! Galeria nowych widgetów libLuny (cz. 5–12) — render headless.
//!
//! Uruchom: `cargo run -p luna-demo --bin gallery`
//! Efekt: `docs/images/gallery.png`
use std::error::Error;
use libluna_core::style::weight;
use libluna_core::{Color, Rect, Size, Theme};
use libluna_render::{Scene, SoftwareRenderer, TextEngine};
use libluna_widgets::{
Adjustment, Assistant, Calendar, ColorButton, ColorChooser, Column, ColumnView, EmojiChooser,
Expander, FlowBox, Image, ImageIcon, LinkButton, MenuButton, NavigationRail, Orientation,
PaintCtx, Paned, PasswordEntry, Popover, PopoverPlacement, Scrollbar, SpinButton, Statusbar,
ToggleButton,
};
const W: u32 = 960;
const H: u32 = 760;
/// Symbole rysowane przez font interfejsu (emoji kolorowe wymagają osobnego fontu).
const SYMBOLS: [&str; 12] = ["★", "♥", "♪", "☀", "✓", "✗", "✚", "●", "◆", "■", "▲", "☰"];
fn main() -> Result<(), Box<dyn Error>> {
let theme = Theme::light();
let text = TextEngine::system();
let size = Size::new(W as f64, H as f64);
let mut scene = Scene::new();
let mut paint = PaintCtx {
scene: &mut scene,
theme: &theme,
text: &text,
};
paint
.scene
.fill_rect(Rect::from_size(size), theme.colors.window_bg);
// — Wiersz 1: rail, tabela, kalendarz —
caption(
&mut paint,
16.0,
14.0,
"NavigationRail / ColumnView / Calendar",
);
let rail = NavigationRail::new(["Overview", "Network", "System", "About"]);
rail.paint(&mut paint, Rect::from_xywh(16.0, 40.0, 180.0, 200.0));
let mut table = ColumnView::new(vec![
Column::new("Name").weight(2.0),
Column::new("Size"),
Column::new("Date"),
]);
table.set_rows(5);
table.set_selected(Some(1));
let table_rect = Rect::from_xywh(216.0, 40.0, 420.0, 200.0);
table.paint(&mut paint, table_rect);
for (i, name) in [
"Projects",
"Report.pdf",
"Notes.txt",
"Budget.xlsx",
"Backup",
]
.iter()
.enumerate()
{
let cell = table.cell_rect(table_rect, &theme, i, 0);
paint.label_left(
cell.x() + theme.spacing.md,
cell.center().y,
name,
theme.typography.body.size,
weight::NORMAL,
theme.colors.text,
);
}
let calendar = Calendar::new(2024, 1);
let weekdays: Vec<String> = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"]
.iter()
.map(|s| s.to_string())
.collect();
calendar.paint(
&mut paint,
Rect::from_xywh(656.0, 40.0, 288.0, 200.0),
"January 2024",
&weekdays,
);
// — Wiersz 2: przyciski, pola, chooser’y —
caption(&mut paint, 16.0, 256.0, "Buttons / Entries / Choosers");
let toggle = ToggleButton::new("Bold").active(true);
let toggle_rect = Rect::from_xywh(16.0, 282.0, 90.0, toggle.height(&theme));
toggle.paint(&mut paint, toggle_rect);
LinkButton::new("Documentation").paint(&mut paint, Rect::from_xywh(116.0, 282.0, 130.0, 34.0));
ColorButton::new(Color::from_rgba8(0x91, 0x41, 0xac, 0xff))
.paint(&mut paint, Rect::from_xywh(256.0, 286.0, 28.0, 26.0));
MenuButton::burger().paint(&mut paint, Rect::from_xywh(296.0, 282.0, 40.0, 34.0));
MenuButton::new("Options").paint(&mut paint, Rect::from_xywh(344.0, 282.0, 110.0, 34.0));
SpinButton::new(65.0, 0.0, 100.0).paint(&mut paint, Rect::from_xywh(464.0, 282.0, 110.0, 34.0));
let mut password = PasswordEntry::new("Password");
password.insert_str("hunter2");
password.paint(&mut paint, Rect::from_xywh(588.0, 282.0, 160.0, 34.0));
for (i, icon) in [
ImageIcon::Folder,
ImageIcon::File,
ImageIcon::Search,
ImageIcon::Burger,
]
.into_iter()
.enumerate()
{
Image::new(icon).size(28.0).paint(
&mut paint,
Rect::from_xywh(760.0 + i as f64 * 36.0, 282.0, 28.0, 34.0),
);
}
let mut chooser = ColorChooser::new(ColorChooser::default_palette());
chooser.set_columns(4);
chooser.paint(
&mut paint,
Rect::from_xywh(16.0, 332.0, 220.0, chooser.height(220.0, &theme)),
);
let symbols: Vec<String> = SYMBOLS.iter().map(|s| s.to_string()).collect();
let mut emoji = EmojiChooser::new(symbols);
emoji.set_columns(6);
emoji.set_selected(1);
emoji.paint(
&mut paint,
Rect::from_xywh(256.0, 332.0, 400.0, emoji.height(400.0, &theme)),
);
let mut flow = FlowBox::new(56.0);
flow.set_count(8);
flow.set_selected(Some(2));
flow.paint(&mut paint, Rect::from_xywh(676.0, 332.0, 268.0, 120.0));
// — Wiersz 3: expander, paned, assistant, scrollbar, popover —
caption(
&mut paint,
16.0,
486.0,
"Expander / Paned / Assistant / Scrollbar / Popover",
);
let expander = Expander::new("Advanced options").expanded(true);
let exp_rect = Rect::from_xywh(16.0, 512.0, 220.0, 120.0);
expander.paint(&mut paint, exp_rect);
let content = expander.content_rect(exp_rect, &theme);
paint.label_left(
content.x() + theme.spacing.md,
content.y() + 16.0,
"Extra settings…",
theme.typography.caption.size,
weight::NORMAL,
theme.colors.dim_text,
);
let mut paned = Paned::new(Orientation::Horizontal);
paned.set_position(0.4);
let paned_rect = Rect::from_xywh(256.0, 512.0, 300.0, 120.0);
let (a, b) = paned.panes(paned_rect, &theme);
paint.scene.fill_rect(a, theme.colors.shade);
paint.scene.fill_rect(b, theme.colors.card_bg);
paned.paint(&mut paint, paned_rect);
Assistant::new(["Welcome", "Details", "Done"]).paint(
&mut paint,
Rect::from_xywh(576.0, 512.0, 368.0, 120.0),
"Cancel",
"Next",
);
let mut bar = Scrollbar::new(Orientation::Horizontal);
let mut adj = Adjustment::new(0.0, 200.0, 60.0);
adj.set_fraction(0.35);
bar.set_adjustment(adj);
bar.paint(&mut paint, Rect::from_xywh(16.0, 648.0, 300.0, 24.0));
let popover = Popover::new(PopoverPlacement::Above);
let pop_rect = Rect::from_xywh(560.0, 640.0, 200.0, 56.0);
popover.paint(&mut paint, pop_rect);
paint.label_left(
pop_rect.x() + theme.spacing.md,
pop_rect.center().y,
"Popover content",
theme.typography.body.size,
weight::NORMAL,
theme.colors.text,
);
// — Pasek stanu na dole —
let mut status = Statusbar::new();
status.set_message("Ready");
status.set_right("8 items");
let sh = status.height(&theme);
status.paint(
&mut paint,
Rect::from_xywh(0.0, size.height - sh, size.width, sh),
);
let mut renderer = SoftwareRenderer::new(W, H);
renderer.draw(&scene, 1.0, theme.colors.window_bg);
let png = renderer.encode_png().ok_or("failed to encode PNG")?;
std::fs::write("docs/images/gallery.png", png)?;
println!("saved docs/images/gallery.png ({W}x{H})");
Ok(())
}
fn caption(paint: &mut PaintCtx, x: f64, y: f64, text: &str) {
paint.label_left(
x,
y + 8.0,
text,
paint.theme.typography.caption.size,
weight::BOLD,
paint.theme.colors.dim_text,
);
}