OneLauncher-Cracked/packages/oneclient_app/src/components/tab_bar.rs

192 lines
5.5 KiB
Rust

use freya::prelude::*;
use crate::theme::colors;
pub struct TabItem {
label: String,
active: bool,
count: Option<String>,
on_press: Option<Box<dyn FnMut(Event<PressEventData>)>>,
}
impl TabItem {
pub fn new(label: impl Into<String>, active: bool) -> Self {
Self {
label: label.into(),
active,
count: None,
on_press: None,
}
}
#[allow(dead_code)]
pub fn count_text(mut self, text: impl Into<String>) -> Self {
self.count = Some(text.into());
self
}
pub fn on_press(mut self, handler: impl FnMut(Event<PressEventData>) + 'static) -> Self {
self.on_press = Some(Box::new(handler));
self
}
}
pub struct TabBar {
tabs: Vec<TabItem>,
width: Size,
height: Size,
spacing: f32,
font_size: f32,
}
impl Default for TabBar {
fn default() -> Self {
Self::new()
}
}
impl TabBar {
pub fn new() -> Self {
Self {
tabs: Vec::new(),
width: Size::fill(),
height: Size::fill(),
spacing: 24.,
font_size: 12.,
}
}
pub fn tabs(mut self, tabs: impl IntoIterator<Item = TabItem>) -> Self {
self.tabs = tabs.into_iter().collect();
self
}
pub fn width(mut self, width: Size) -> Self {
self.width = width;
self
}
pub fn height(mut self, height: Size) -> Self {
self.height = height;
self
}
pub fn spacing(mut self, spacing: f32) -> Self {
self.spacing = spacing;
self
}
pub fn font_size(mut self, font_size: f32) -> Self {
self.font_size = font_size;
self
}
}
fn tab_label(text: &str, font_size: f32, active: bool) -> impl IntoElement {
const ACTIVE_WEIGHT: FontWeight = FontWeight::MEDIUM;
rect()
.center()
.content(Content::Fit)
.child(
label()
.text(text.to_string())
.font_size(font_size)
.font_weight(ACTIVE_WEIGHT)
.color(Color::TRANSPARENT)
.text_align(TextAlign::Center),
)
.child(
rect()
.position(
Position::new_absolute()
.top(0.)
.left(0.)
.right(0.)
.bottom(0.),
)
.center()
.child(
label()
.text(text.to_string())
.font_size(font_size)
.font_weight(if active {
ACTIVE_WEIGHT
} else {
FontWeight::NORMAL
})
.color(colors::fg_primary())
.text_align(TextAlign::Center),
),
)
}
fn count_pill(count: &str, font_size: f32) -> impl IntoElement {
rect()
.center()
.padding(Gaps::new_symmetric(1., 6.))
.corner_radius(CornerRadius::new_all(10.))
.background(colors::component_bg())
.child(
label()
.text(count.to_string())
.font_size((font_size - 3.).max(8.))
.color(colors::fg_secondary()),
)
}
impl IntoElement for TabBar {
fn into_element(self) -> Element {
let font_size = self.font_size;
rect()
.horizontal()
.width(self.width)
.height(self.height)
.spacing(self.spacing)
.cross_align(Alignment::Center)
.content(Content::Fit)
.children(self.tabs.into_iter().map(|tab| {
let active = tab.active;
let mut el = rect()
.vertical()
.content(Content::Fit)
.on_pointer_enter(|_| Cursor::set(CursorIcon::Pointer))
.on_pointer_leave(|_| Cursor::set(CursorIcon::default()));
if let Some(handler) = tab.on_press {
el = el.on_press(handler);
}
el.child(
rect()
.horizontal()
.cross_align(Alignment::Center)
.spacing(6.)
.content(Content::Fit)
.child(
rect()
.vertical()
.content(Content::Fit)
.child(tab_label(&tab.label, font_size, active))
.child(
rect()
.height(Size::px(1.5))
.width(Size::fill_minimum())
.margin(Gaps::new_symmetric(0., 4.0))
.corner_radius(CornerRadius::new_all(2.))
.background(if active {
colors::fg_primary()
} else {
Color::TRANSPARENT
}),
),
)
.maybe_child(tab.count.map(|c| count_pill(&c, font_size).into_element())),
)
.into_element()
}))
.into_element()
}
}