use fluidattacks_designs_domain::icon::Style as IconStyle;
use fluidattacks_designs_domain::table::{
offered_sizes, PAGE_MENU_OFFSET, PAGE_MENU_RADIUS, PAGE_MENU_SHADOW, PAGE_OPTION_CHOSEN_BG,
PAGE_OPTION_CHOSEN_INK, PAGE_OPTION_WIDTH, PAGE_SIZE_BG, PAGE_SIZE_BORDER,
PAGE_SIZE_CLOSED_ICON, PAGE_SIZE_GLYPH, PAGE_SIZE_HOVER_BG, PAGE_SIZE_ICON, PAGE_SIZE_INK,
PAGE_SIZE_MIN_HEIGHT, PAGE_SIZE_OPEN_ICON, PAGE_SIZE_PADDING, PAGE_SIZE_RADIUS, PAGE_SIZE_TEXT,
};
use fluidattacks_designs_domain::typography::FONT_PRIMARY;
use gpui::prelude::FluentBuilder as _;
use gpui::{
anchored, deferred, div, point, px, rgb, Anchor, App, Div, Entity, InteractiveElement as _,
MouseButton, ParentElement as _, SharedString, Stateful, StatefulInteractiveElement as _,
Styled as _,
};
use crate::icon::Icon;
use crate::table::state::{PageHandler, TableState};
use crate::theme::{ActiveTheme as _, Theme};
use crate::widget::elevation;
pub fn page_size(state: &Entity<TableState>, on_page: &PageHandler, cx: &App) -> Div {
let read = state.read(cx);
let open = read.sizes_open();
let closing = state.clone();
div()
.relative()
.flex_none()
.child(size_box(state, read.page().size, open, cx.theme()))
.when(open, |el| {
el.child(deferred(hung(menu(state, on_page, cx))))
})
.on_mouse_down_out(move |_, _win, app| {
closing.update(app, TableState::close_sizes);
})
}
fn size_box(state: &Entity<TableState>, size: usize, open: bool, theme: &Theme) -> Div {
let toggling = state.clone();
let hover_bg = theme.shade(PAGE_SIZE_HOVER_BG);
div()
.flex()
.items_center()
.min_h(px(PAGE_SIZE_MIN_HEIGHT))
.px(px(PAGE_SIZE_PADDING))
.bg(rgb(theme.shade(PAGE_SIZE_BG)))
.border_1()
.border_color(rgb(theme.shade(PAGE_SIZE_BORDER)))
.rounded(px(PAGE_SIZE_RADIUS))
.child(number(size, PAGE_SIZE_INK, theme).pr(px(PAGE_SIZE_PADDING)))
.child(
div()
.id(crate::table::part(state, "page-size-toggle"))
.flex()
.p(px(PAGE_SIZE_PADDING))
.rounded(px(PAGE_SIZE_RADIUS))
.cursor_pointer()
.hover(move |hovered| hovered.bg(rgb(hover_bg)))
.on_click(move |_, _win, app| {
toggling.update(app, TableState::toggle_sizes);
})
.child(
Icon::new(if open {
PAGE_SIZE_OPEN_ICON
} else {
PAGE_SIZE_CLOSED_ICON
})
.style(IconStyle::Light)
.size(PAGE_SIZE_GLYPH)
.color(theme.shade(PAGE_SIZE_ICON)),
),
)
}
fn hung(menu: Div) -> Div {
div().absolute().top_0().right_0().size_0().child(
anchored()
.anchor(Anchor::BottomRight)
.offset(point(px(0.0), px(-PAGE_MENU_OFFSET)))
.child(menu),
)
}
fn menu(state: &Entity<TableState>, on_page: &PageHandler, cx: &App) -> Div {
let read = state.read(cx);
let theme = cx.theme();
let current = read.page().size;
div()
.flex()
.flex_col()
.p(px(PAGE_SIZE_PADDING))
.bg(rgb(theme.shade(PAGE_SIZE_BG)))
.border_1()
.border_color(rgb(theme.shade(PAGE_SIZE_BORDER)))
.rounded(px(PAGE_MENU_RADIUS))
.shadow(elevation(PAGE_MENU_SHADOW))
.occlude()
.children(
offered_sizes(read.page_counts(), current)
.map(|size| option(state, on_page, size, size == current, theme)),
)
}
fn option(
state: &Entity<TableState>,
on_page: &PageHandler,
size: usize,
chosen: bool,
theme: &Theme,
) -> Stateful<Div> {
let choosing = state.clone();
let handler = on_page.clone();
let (fill, ink) = if chosen {
(Some(PAGE_OPTION_CHOSEN_BG), PAGE_OPTION_CHOSEN_INK)
} else {
(None, PAGE_SIZE_INK)
};
let hover_bg = theme.shade(fill.unwrap_or(PAGE_SIZE_HOVER_BG));
div()
.id(crate::table::part(state, format!("page-size-{size}")))
.flex()
.justify_center()
.w(px(PAGE_OPTION_WIDTH))
.p(px(PAGE_SIZE_PADDING))
.rounded(px(PAGE_SIZE_RADIUS))
.cursor_pointer()
.when_some(fill, |el, color| el.bg(rgb(theme.shade(color))))
.hover(move |hovered| hovered.bg(rgb(hover_bg)))
.child(number(size, ink, theme))
.on_mouse_down(MouseButton::Left, move |_, _win, app| {
let page = choosing.update(app, |table, inner| {
table.close_sizes(inner);
table.set_page_size(size, inner);
table.page()
});
handler(page, app);
})
}
fn number(size: usize, ink: u32, theme: &Theme) -> Div {
div()
.font_family(FONT_PRIMARY)
.text_size(px(PAGE_SIZE_TEXT.px()))
.line_height(px(PAGE_SIZE_TEXT.line_height_px()))
.text_color(rgb(theme.shade(ink)))
.child(SharedString::from(size.to_string()))
}