🔒 Repository is read-only – file editing is disabled.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
//! `Statusbar` — pasek stanu (GTK4): komunikat po lewej, informacje po prawej.
//!
//! Widget immediate: trzyma dwa napisy (lewy komunikat i prawy opis), liczy geometrię
//! i rysuje tło z górną krawędzią. Aplikacja wpisuje tam np. „Gotowy” i liczbę elementów.
use libluna_core::style::weight;
use libluna_core::{Rect, Theme};
use crate::ctx::PaintCtx;
/// Pasek stanu.
#[derive(Debug, Clone, Default)]
pub struct Statusbar {
message: String,
right: String,
}
impl Statusbar {
pub fn new() -> Self {
Self::default()
}
pub fn message(&self) -> &str {
&self.message
}
pub fn right(&self) -> &str {
&self.right
}
pub fn set_message(&mut self, message: impl Into<String>) {
self.message = message.into();
}
pub fn set_right(&mut self, right: impl Into<String>) {
self.right = right.into();
}
pub fn height(&self, theme: &Theme) -> f64 {
theme.typography.caption.line_height + theme.spacing.xs * 2.0
}
/// Prostokąty: `[lewy komunikat, prawy opis]` wewnątrz `rect`.
pub fn parts(&self, rect: Rect, theme: &Theme) -> [Rect; 2] {
let pad = theme.spacing.md;
let h = rect.height();
let w = (rect.width() - 2.0 * pad).max(0.0) / 2.0;
[
Rect::from_xywh(rect.x() + pad, rect.y(), w, h),
Rect::from_xywh(rect.right() - pad - w, rect.y(), w, h),
]
}
pub fn paint(&self, ctx: &mut PaintCtx, rect: Rect) {
let theme = *ctx.theme;
ctx.scene.fill_rect(rect, theme.colors.window_bg);
let line = Rect::from_xywh(rect.x(), rect.y(), rect.width(), 1.0);
ctx.scene.fill_rect(line, theme.colors.border);
let [left, right] = self.parts(rect, &theme);
let size = theme.typography.caption.size;
ctx.label_left(
left.x(),
left.center().y,
&self.message,
size,
weight::NORMAL,
theme.colors.dim_text,
);
ctx.label_right(
right.right(),
right.center().y,
&self.right,
size,
weight::NORMAL,
theme.colors.dim_text,
);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn height_from_caption_metrics() {
let theme = Theme::light();
let s = Statusbar::new();
assert!(
(s.height(&theme) - (theme.typography.caption.line_height + theme.spacing.xs * 2.0))
.abs()
< 1e-9
);
}
#[test]
fn parts_are_padded_start_and_end() {
let theme = Theme::light();
let rect = Rect::from_xywh(0.0, 0.0, 400.0, 24.0);
let mut s = Statusbar::new();
s.set_message("Gotowy");
s.set_right("4 elementy");
assert_eq!(s.message(), "Gotowy");
assert_eq!(s.right(), "4 elementy");
let [l, r] = s.parts(rect, &theme);
assert_eq!(l.x(), theme.spacing.md);
assert!((r.right() - (400.0 - theme.spacing.md)).abs() < 1e-9);
}
}