use crate::input::core::coordinator::LayerId;
use crate::input::{InputCoordinator, Sense, WidgetKind};
use crate::render::RenderContext;
use crate::types::{Rect, WidgetId, CompositeId};
use super::settings::PopupSettings;
use super::state::PopupState;
use super::style::BackgroundFill;
use super::types::{BackdropKind, PopupRenderKind, PopupView, PopupViewKind};
pub fn register_input_coordinator_popup(
coord: &mut InputCoordinator,
id: impl Into<WidgetId>,
rect: Rect,
state: &mut PopupState,
view: &PopupView<'_>,
_settings: &PopupSettings,
kind: PopupRenderKind,
layer: &LayerId,
) -> CompositeId {
let popup_id = coord.register_composite(id, WidgetKind::Popup, rect, Sense::CLICK, layer);
if matches!(kind, PopupRenderKind::Custom) {
return popup_id;
}
let frame = resolve_frame(rect, state, view);
let body = body_rect(frame, _settings);
register_popup_body_overflow(coord, &popup_id, body, view, state);
popup_id
}
fn register_popup_body_overflow(
coord: &mut InputCoordinator,
popup_id: &CompositeId,
body: Rect,
view: &PopupView<'_>,
state: &mut PopupState,
) {
if body.width <= 0.0 || body.height <= 0.0 { return; }
state.body_viewport_h = body.height;
match view.overflow {
crate::types::OverflowMode::Scrollbar => {
let track_w = 8.0;
let track = Rect::new(body.x + body.width - track_w, body.y, track_w, body.height);
state.body_scroll_track = Some(track);
coord.register_child(popup_id, format!("{}:scrollbar_track", popup_id.0.0),
WidgetKind::ScrollbarTrack, track, Sense::CLICK);
coord.register_child(popup_id, format!("{}:scrollbar_handle", popup_id.0.0),
WidgetKind::ScrollbarHandle, track, Sense::DRAG | Sense::HOVER);
}
crate::types::OverflowMode::Chevrons => {
let strip = 16.0;
let up = Rect::new(body.x, body.y, body.width, strip);
let dn = Rect::new(body.x, body.y + body.height - strip, body.width, strip);
coord.register_child(popup_id, format!("{}:chevron_up", popup_id.0.0),
WidgetKind::Button, up, Sense::CLICK | Sense::HOVER);
coord.register_child(popup_id, format!("{}:chevron_down", popup_id.0.0),
WidgetKind::Button, dn, Sense::CLICK | Sense::HOVER);
}
_ => {}
}
}
pub fn register_context_manager_popup(
ctx_mgr: &mut crate::app_context::ContextManager,
render: &mut dyn RenderContext,
id: impl Into<WidgetId>,
rect: Rect,
state: &mut PopupState,
view: &mut PopupView<'_>,
settings: &PopupSettings,
kind: PopupRenderKind,
layer: &LayerId,
) -> CompositeId {
let coord = &mut ctx_mgr.input;
let popup_id =
register_input_coordinator_popup(coord, id, rect, state, view, settings, kind, layer);
draw_popup(render, rect, state, view, settings, kind);
popup_id
}
fn draw_popup(
ctx: &mut dyn RenderContext,
rect: Rect,
state: &mut PopupState,
view: &mut PopupView<'_>,
settings: &PopupSettings,
kind: PopupRenderKind,
) {
if let PopupViewKind::Custom { ref draw } = view.kind {
draw(ctx, rect, state, settings);
return;
}
if !matches!(kind, PopupRenderKind::Plain) {
return;
}
let theme = settings.theme.as_ref();
let style = settings.style.as_ref();
let frame = resolve_frame(rect, state, view);
if matches!(view.backdrop, BackdropKind::Dim) {
ctx.set_fill_color(theme.backdrop_dim());
ctx.fill_rect(0.0, 0.0, 99_999.0, 99_999.0);
}
let (sx, sy) = style.shadow_offset();
ctx.set_fill_color(theme.shadow());
ctx.fill_rounded_rect(
frame.x + sx, frame.y + sy,
frame.width, frame.height,
style.radius(),
);
draw_frame(ctx, frame, settings);
if matches!(view.overflow, crate::types::OverflowMode::Scrollbar) {
if let Some(track) = state.body_scroll_track {
use crate::ui::widgets::atomic::scrollbar::{
render::{draw_scrollbar, ScrollbarView, ScrollbarVisualState},
style::StandardScrollbarStyle,
theme::DefaultScrollbarTheme,
};
let sb_style = StandardScrollbarStyle::default();
let sb_theme = DefaultScrollbarTheme::default();
let visual_state = if state.scroll.is_dragging {
ScrollbarVisualState::Dragging
} else {
ScrollbarVisualState::Active
};
let sv = ScrollbarView {
content_height: state.body_content_h,
viewport_height: state.body_viewport_h,
scroll_offset: state.scroll.offset,
state: visual_state,
drag_pos_y: None,
style: &sb_style,
theme: &sb_theme,
};
let _ = draw_scrollbar(ctx, track, &sv);
}
}
}
fn draw_frame(ctx: &mut dyn RenderContext, frame: Rect, settings: &PopupSettings) {
let theme = settings.theme.as_ref();
let style = settings.style.as_ref();
match style.background_fill() {
BackgroundFill::Solid => {
ctx.set_fill_color(theme.bg());
ctx.fill_rounded_rect(frame.x, frame.y, frame.width, frame.height, style.radius());
}
BackgroundFill::Glass { blur_radius: _ } => {
ctx.draw_blur_background(frame.x, frame.y, frame.width, frame.height);
ctx.set_fill_color(theme.bg());
ctx.fill_rounded_rect(frame.x, frame.y, frame.width, frame.height, style.radius());
}
BackgroundFill::Texture { asset_id } => {
let _ = asset_id;
ctx.set_fill_color(theme.bg());
ctx.fill_rounded_rect(frame.x, frame.y, frame.width, frame.height, style.radius());
}
}
ctx.set_stroke_color(theme.border());
ctx.set_stroke_width(style.border_width());
ctx.set_line_dash(&[]);
ctx.stroke_rounded_rect(frame.x, frame.y, frame.width, frame.height, style.radius());
}
fn resolve_frame(rect: Rect, state: &PopupState, _view: &PopupView<'_>) -> Rect {
if state.position.0 != 0.0 || state.position.1 != 0.0 {
Rect::new(state.position.0, state.position.1, rect.width, rect.height)
} else {
rect
}
}
pub fn body_rect(frame: Rect, settings: &PopupSettings) -> Rect {
let pad = settings.style.padding();
Rect::new(
frame.x + pad,
frame.y + pad,
(frame.width - pad * 2.0).max(0.0),
(frame.height - pad * 2.0).max(0.0),
)
}