use crate::core::types::Rect;
use crate::layout::docking::DockPanel;
use crate::layout::{LayoutManager, LayoutNodeId, PopupHandle, PopupNode, StyleManager};
use crate::render::RenderContext;
use crate::types::{OverflowMode, SizeMode};
use crate::ui::widgets::composite::popup::input::register_layout_manager_popup;
use crate::ui::widgets::composite::popup::settings::PopupSettings;
use crate::ui::widgets::composite::popup::style::{DefaultPopupStyle, PopupStyle};
use crate::ui::widgets::composite::popup::theme::{DefaultPopupTheme, PopupTheme};
use crate::ui::widgets::composite::popup::types::{
BackdropKind, PopupRenderKind, PopupView, PopupViewKind,
};
struct StyledPopupTheme {
bg: String,
border: String,
item_bg_hover: String,
item_bg_selected: String,
item_text: String,
accent: String,
fallback: DefaultPopupTheme,
}
impl StyledPopupTheme {
fn from_styles(s: &StyleManager) -> Self {
let accent = s.color_or_owned("accent", "#2962ff");
let accent_dim = s.color_or_owned("accent_dim","rgba(41,98,255,0.15)");
Self {
bg: s.color_or_owned("surface", "#1e222d"),
border: s.color_or_owned("border_strong", "#363a45"),
item_bg_hover: s.color_or_owned("surface_raised","#2a2e39"),
item_bg_selected: accent_dim,
item_text: s.color_or_owned("fg_1", "#d1d4dc"),
accent: accent,
fallback: DefaultPopupTheme,
}
}
}
impl PopupTheme for StyledPopupTheme {
fn bg(&self) -> &str { &self.bg }
fn border(&self) -> &str { &self.border }
fn shadow(&self) -> &str { self.fallback.shadow() }
fn item_bg_normal(&self) -> &str { self.fallback.item_bg_normal() }
fn item_bg_hover(&self) -> &str { &self.item_bg_hover }
fn item_bg_selected(&self) -> &str { &self.item_bg_selected }
fn item_text(&self) -> &str { &self.item_text }
fn item_text_hover(&self) -> &str { self.fallback.item_text_hover() }
fn item_text_disabled(&self) -> &str { self.fallback.item_text_disabled() }
fn item_text_danger(&self) -> &str { self.fallback.item_text_danger() }
fn item_bg_danger_hover(&self) -> &str { self.fallback.item_bg_danger_hover() }
fn header_text(&self) -> &str { self.fallback.header_text() }
fn separator(&self) -> &str { &self.border }
fn hex_input_bg(&self) -> &str { self.fallback.hex_input_bg() }
fn hex_input_text(&self) -> &str { self.fallback.hex_input_text() }
fn hex_input_border_focus(&self) -> &str { &self.accent }
fn hsv_indicator(&self) -> &str { self.fallback.hsv_indicator() }
fn accent(&self) -> &str { &self.accent }
fn backdrop_dim(&self) -> &str { self.fallback.backdrop_dim() }
}
fn popup_settings_from_styles(s: &StyleManager) -> PopupSettings {
PopupSettings {
theme: Box::new(StyledPopupTheme::from_styles(s)),
style: Box::<DefaultPopupStyle>::default(),
}
}
pub struct PopupBuilder<'a> {
handle: &'a PopupHandle,
parent: LayoutNodeId,
slot_id: Option<&'a str>,
overlay_rect: Option<Rect>,
anchor: Option<Rect>,
anchor_widget_id: Option<&'a str>,
origin: (f64, f64),
backdrop: BackdropKind,
size_mode: SizeMode,
overflow: OverflowMode,
settings: Option<PopupSettings>,
theme_override: Option<Box<dyn PopupTheme>>,
style_override: Option<Box<dyn PopupStyle>>,
kind: PopupRenderKind,
}
pub fn popup<'a>(handle: &'a PopupHandle) -> PopupBuilder<'a> {
PopupBuilder::new(handle)
}
impl<'a> PopupBuilder<'a> {
pub fn new(handle: &'a PopupHandle) -> Self {
Self {
handle,
parent: LayoutNodeId::ROOT,
slot_id: None,
overlay_rect: None,
anchor: None,
anchor_widget_id: None,
origin: (0.0, 0.0),
backdrop: BackdropKind::default(),
size_mode: SizeMode::default(),
overflow: OverflowMode::Clip,
settings: None,
theme_override: None,
style_override: None,
kind: PopupRenderKind::Plain,
}
}
pub fn parent(mut self, p: LayoutNodeId) -> Self { self.parent = p; self }
pub fn slot_id(mut self, s: &'a str) -> Self { self.slot_id = Some(s); self }
pub fn origin(mut self, o: (f64, f64)) -> Self { self.origin = o; self }
pub fn rect(mut self, r: Rect) -> Self { self.overlay_rect = Some(r); self }
pub fn anchor(mut self, r: Rect) -> Self { self.anchor = Some(r); self }
pub fn anchor_to(mut self, widget_id: &'a str) -> Self {
self.anchor_widget_id = Some(widget_id);
self
}
pub fn backdrop(mut self, b: BackdropKind) -> Self { self.backdrop = b; self }
pub fn size_mode(mut self, m: SizeMode) -> Self { self.size_mode = m; self }
pub fn overflow(mut self, m: OverflowMode) -> Self { self.overflow = m; self }
pub fn settings(mut self, s: PopupSettings) -> Self { self.settings = Some(s); self }
pub fn kind(mut self, k: PopupRenderKind) -> Self { self.kind = k; self }
pub fn theme(mut self, t: Box<dyn PopupTheme>) -> Self {
self.theme_override = Some(t);
self
}
pub fn style(mut self, s: Box<dyn PopupStyle>) -> Self {
self.style_override = Some(s);
self
}
pub fn build<P: DockPanel>(
self,
layout: &mut LayoutManager<P>,
render: &mut dyn RenderContext,
) -> Option<PopupNode> {
self.build_with_body(layout, render, |_, _, _: Rect| {})
}
pub fn build_with_body<P, F>(
self,
layout: &mut LayoutManager<P>,
render: &mut dyn RenderContext,
body: F,
) -> Option<PopupNode>
where
P: DockPanel,
F: FnOnce(&mut LayoutManager<P>, &mut dyn RenderContext, Rect),
{
let slot_id = self.slot_id
.map(str::to_owned)
.unwrap_or_else(|| self.handle.id_str().to_string());
let resolved_anchor: Option<Rect> = self.anchor.or_else(|| {
self.anchor_widget_id.and_then(|wid| {
layout.ctx().input.widget_rect(&crate::types::unsafe_widget_id(wid))
})
});
let resolved_origin = if self.origin == (0.0, 0.0) {
resolved_anchor
.map(|a| (a.x, a.y + a.height))
.unwrap_or(self.origin)
} else {
self.origin
};
let overlay_rect = self.overlay_rect.unwrap_or_else(|| {
let (w, h) = match self.size_mode {
SizeMode::Fixed(w, h) => (w, h),
_ => (240.0, 200.0),
};
Rect::new(resolved_origin.0, resolved_origin.1, w, h)
});
let mut view = PopupView {
origin: resolved_origin,
anchor: resolved_anchor,
backdrop: self.backdrop,
kind: PopupViewKind::Plain,
size_mode: self.size_mode,
overflow: self.overflow,
};
let mut settings = self.settings.unwrap_or_else(|| popup_settings_from_styles(layout.styles()));
if let Some(t) = self.theme_override { settings.theme = t; }
if let Some(s) = self.style_override { settings.style = s; }
let kind = self.kind;
let parent = self.parent;
let handle = self.handle;
let node = register_layout_manager_popup(
layout,
render,
parent,
&slot_id,
handle,
overlay_rect,
resolved_anchor,
&mut view,
&settings,
kind,
);
let frame = layout
.rect_for_overlay(&slot_id)
.unwrap_or(overlay_rect);
if frame.width > 0.0 && frame.height > 0.0 {
let body_rect = crate::ui::widgets::composite::popup::render::body_rect(frame, &settings);
render.save();
render.clip_rect(body_rect.x, body_rect.y, body_rect.width, body_rect.height);
body(layout, render, body_rect);
render.restore();
}
node
}
}