use crate::core::types::Rect;
use crate::docking::panels::DockPanel;
use crate::layout::{DropdownHandle, DropdownNode, LayoutManager, LayoutNodeId, StyleManager};
use crate::render::RenderContext;
use crate::types::{OverflowMode, SizeMode};
use crate::ui::widgets::composite::dropdown::input::register_layout_manager_dropdown;
use crate::ui::widgets::composite::dropdown::settings::DropdownSettings;
use crate::ui::widgets::composite::dropdown::style::DefaultDropdownStyle;
use crate::ui::widgets::composite::dropdown::theme::{DefaultDropdownTheme, DropdownTheme};
use crate::ui::widgets::composite::dropdown::types::{
DropdownItem, DropdownRenderKind, DropdownView, DropdownViewKind, SubmenuWidth,
};
struct StyledDropdownTheme {
bg: String,
border: String,
item_bg_hover: String,
item_bg_selected: String,
item_text: String,
accent: String,
fallback: DefaultDropdownTheme,
}
impl StyledDropdownTheme {
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,
fallback: DefaultDropdownTheme,
}
}
}
impl DropdownTheme for StyledDropdownTheme {
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.bg }
fn item_bg_hover(&self) -> &str { &self.item_bg_hover }
fn item_bg_selected(&self) -> &str { &self.item_bg_selected }
fn item_bg_danger_hover(&self) -> &str { self.fallback.item_bg_danger_hover() }
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 header_text(&self) -> &str { self.fallback.header_text() }
fn header_border(&self) -> &str { &self.border }
fn separator(&self) -> &str { &self.border }
fn shortcut_text(&self) -> &str { self.fallback.shortcut_text() }
fn caret_color(&self) -> &str { self.fallback.caret_color() }
fn toggle_on(&self) -> &str { &self.accent }
fn toggle_off(&self) -> &str { self.fallback.toggle_off() }
fn toggle_thumb(&self) -> &str { self.fallback.toggle_thumb() }
fn trigger_bg(&self) -> &str { &self.bg }
fn trigger_bg_hover(&self) -> &str { &self.item_bg_hover }
fn trigger_border(&self) -> &str { &self.border }
fn trigger_text(&self) -> &str { &self.item_text }
fn trigger_arrow(&self) -> &str { self.fallback.trigger_arrow() }
fn checkbox_border(&self) -> &str { self.fallback.checkbox_border() }
fn checkbox_checked(&self) -> &str { &self.accent }
fn cell_bg_hover(&self) -> &str { &self.item_bg_hover }
fn cell_border(&self) -> &str { &self.border }
}
fn dropdown_settings_from_styles(s: &StyleManager) -> DropdownSettings {
DropdownSettings {
theme: Box::new(StyledDropdownTheme::from_styles(s)),
style: Box::<DefaultDropdownStyle>::default(),
}
}
pub struct DropdownBuilder<'a> {
handle: &'a DropdownHandle,
parent: LayoutNodeId,
slot_id: Option<&'a str>,
overlay_rect: Option<Rect>,
anchor: Option<Rect>,
anchor_widget_id: Option<&'a str>,
position_override:Option<(f64, f64)>,
open: bool,
items: &'a [DropdownItem<'a>],
hovered_id: Option<&'a str>,
submenu: Option<(&'a str, &'a [DropdownItem<'a>])>,
submenu_hovered: Option<&'a str>,
submenu_width: SubmenuWidth,
size_mode: SizeMode,
overflow: OverflowMode,
settings: Option<DropdownSettings>,
kind: DropdownRenderKind,
}
pub fn dropdown<'a>(handle: &'a DropdownHandle) -> DropdownBuilder<'a> {
DropdownBuilder::new(handle)
}
impl<'a> DropdownBuilder<'a> {
pub fn new(handle: &'a DropdownHandle) -> Self {
Self {
handle,
parent: LayoutNodeId::ROOT,
slot_id: None,
overlay_rect: None,
anchor: None,
anchor_widget_id: None,
position_override: None,
open: true,
items: &[],
hovered_id: None,
submenu: None,
submenu_hovered: None,
submenu_width: SubmenuWidth::default(),
size_mode: SizeMode::default(),
overflow: OverflowMode::Clip,
settings: None,
kind: DropdownRenderKind::Flat,
}
}
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 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 origin(mut self, o: (f64, f64)) -> Self { self.position_override = Some(o); self }
pub fn rect(mut self, r: Rect) -> Self { self.overlay_rect = Some(r); self }
pub fn open(mut self, on: bool) -> Self { self.open = on; self }
pub fn items(mut self, items: &'a [DropdownItem<'a>]) -> Self { self.items = items; self }
pub fn hovered_id(mut self, id: &'a str) -> Self { self.hovered_id = Some(id); self }
pub fn submenu(mut self, trigger_id: &'a str, items: &'a [DropdownItem<'a>]) -> Self {
self.submenu = Some((trigger_id, items));
self
}
pub fn submenu_hovered(mut self, id: &'a str) -> Self { self.submenu_hovered = Some(id); self }
pub fn submenu_width(mut self, w: SubmenuWidth) -> Self { self.submenu_width = w; 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: DropdownSettings) -> Self { self.settings = Some(s); self }
pub fn kind(mut self, k: DropdownRenderKind) -> Self { self.kind = k; self }
pub fn build<P: DockPanel>(
self,
layout: &mut LayoutManager<P>,
render: &mut dyn RenderContext,
) -> Option<DropdownNode> {
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 overlay_rect = self.overlay_rect.unwrap_or_else(|| {
let (x, y) = self.position_override
.or_else(|| resolved_anchor.map(|a| (a.x, a.y + a.height)))
.unwrap_or((0.0, 0.0));
let (w, h) = match self.size_mode {
SizeMode::Fixed(w, h) => (w, h),
_ => (200.0, 240.0),
};
Rect::new(x, y, w, h)
});
let mut view = DropdownView {
anchor: resolved_anchor,
position_override: self.position_override,
open: self.open,
kind: DropdownViewKind::Flat {
items: self.items,
hovered_id: self.hovered_id,
submenu_items: self.submenu,
submenu_hovered_id: self.submenu_hovered,
},
size_mode: self.size_mode,
overflow: self.overflow,
submenu_width:self.submenu_width,
};
let settings = self.settings.unwrap_or_else(|| dropdown_settings_from_styles(layout.styles()));
register_layout_manager_dropdown(
layout,
render,
self.parent,
&slot_id,
self.handle,
overlay_rect,
resolved_anchor,
&mut view,
&settings,
self.kind,
)
}
}