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;
let scroll = crate::ui::widgets::composite::overflow::BodyScrollState {
offset_x: 0.0,
offset_y: state.scroll.offset,
content_w: 0.0,
content_h: state.body_content_h,
};
let layer = crate::input::core::coordinator::LayerId::main();
let overflowing = scroll.overflows(body.width, body.height).any();
let want_chevrons = match view.overflow {
crate::types::OverflowMode::Chevrons => true,
crate::types::OverflowMode::Clip if overflowing => true,
_ => false,
};
if want_chevrons {
crate::ui::widgets::composite::overflow::register_chevrons_helper(
coord, popup_id, body, &scroll, &layer,
);
}
}
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
}
pub 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);
let body = body_rect(frame, settings);
if body.width > 0.0 && body.height > 0.0 {
let scroll = crate::ui::widgets::composite::overflow::BodyScrollState {
offset_x: 0.0,
offset_y: state.scroll.offset,
content_w: 0.0,
content_h: state.body_content_h,
};
let overflowing = scroll.overflows(body.width, body.height).any();
let want_chevrons = match view.overflow {
crate::types::OverflowMode::Chevrons => true,
crate::types::OverflowMode::Clip if overflowing => true,
_ => false,
};
if want_chevrons {
crate::ui::widgets::composite::overflow::draw_chevrons_helper(
ctx, body, &scroll, theme.bg(), theme.bg(),
);
}
}
}
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),
)
}