use crate::*;
#[derive(Clone, Copy)]
enum NodeKind {
Header,
Tree,
}
#[derive(Clone, Copy)]
pub enum NodeStateValue {
Expanded,
Closed,
}
impl NodeStateValue {
pub fn is_expanded(&self) -> bool {
match self {
Self::Expanded => true,
_ => false,
}
}
pub fn is_closed(&self) -> bool {
match self {
Self::Closed => true,
_ => false,
}
}
}
#[derive(Clone)]
pub struct Node {
pub label: String,
pub state: NodeStateValue,
pub font: FontChoice,
pub opt: WidgetOption,
pub bopt: WidgetBehaviourOption,
kind: NodeKind,
}
impl Node {
pub fn new(label: impl Into<String>, state: NodeStateValue) -> Self {
Self::header(label, state)
}
pub fn header(label: impl Into<String>, state: NodeStateValue) -> Self {
Self {
label: label.into(),
state,
font: FontChoice::default(),
opt: WidgetOption::NONE,
bopt: WidgetBehaviourOption::NONE,
kind: NodeKind::Header,
}
}
pub fn tree(label: impl Into<String>, state: NodeStateValue) -> Self {
Self {
label: label.into(),
state,
font: FontChoice::default(),
opt: WidgetOption::NONE,
bopt: WidgetBehaviourOption::NONE,
kind: NodeKind::Tree,
}
}
pub fn with_opt(label: impl Into<String>, state: NodeStateValue, opt: WidgetOption) -> Self {
Self::with_opt_header(label, state, opt)
}
pub fn with_opt_header(label: impl Into<String>, state: NodeStateValue, opt: WidgetOption) -> Self {
Self {
label: label.into(),
state,
font: FontChoice::default(),
opt,
bopt: WidgetBehaviourOption::NONE,
kind: NodeKind::Header,
}
}
pub fn with_opt_tree(label: impl Into<String>, state: NodeStateValue, opt: WidgetOption) -> Self {
Self {
label: label.into(),
state,
font: FontChoice::default(),
opt,
bopt: WidgetBehaviourOption::NONE,
kind: NodeKind::Tree,
}
}
pub fn is_expanded(&self) -> bool {
self.state.is_expanded()
}
pub fn is_closed(&self) -> bool {
self.state.is_closed()
}
pub fn is_tree(&self) -> bool {
matches!(self.kind, NodeKind::Tree)
}
pub fn is_header(&self) -> bool {
matches!(self.kind, NodeKind::Header)
}
fn preferred_size_widget(&self, style: &Style, atlas: &AtlasHandle, _avail: Dimensioni) -> Dimensioni {
let padding = style.padding.max(0);
let vertical_pad = (padding / 2).max(1);
let font = style.resolve_font_choice(self.font);
let font_height = atlas.get_font_height(font) as i32;
let icon = atlas.get_icon_size(EXPAND_ICON);
let text_w = if self.label.is_empty() {
0
} else {
atlas.get_text_size(font, self.label.as_str()).width
};
let height = (font_height.max(icon.height) + vertical_pad * 2).max(0);
let consumed = (height - padding).max(icon.width);
let width = (padding * 2 + consumed + text_w).max(0);
Dimensioni::new(width, height)
}
fn handle_widget(&mut self, ctx: &mut WidgetCtx<'_>, control: &ControlState) -> ResourceState {
let mut res = ResourceState::NONE;
let expanded = self.state.is_expanded();
let style = ctx.style();
let padding = style.padding;
let text_color = style.colors[ControlColor::Text as usize];
let mut r = ctx.rect();
match self.kind {
NodeKind::Tree => {
if control.hovered {
ctx.draw_frame(r, ControlColor::ButtonHover);
}
}
NodeKind::Header => {
ctx.draw_widget_frame(control, r, ControlColor::Button, self.opt);
}
}
ctx.draw_icon(
if expanded { COLLAPSE_ICON } else { EXPAND_ICON },
rect(r.x, r.y, r.height, r.height),
text_color,
);
r.x += r.height - padding;
r.width -= r.height - padding;
let font = ctx.style().resolve_font_choice(self.font);
ctx.draw_control_text_with_font(font, self.label.as_str(), r, ControlColor::Text, self.opt);
if control.clicked {
res |= ResourceState::CHANGE;
}
res
}
}
impl Widget for Node {
fn widget_opt(&self) -> &WidgetOption {
&self.opt
}
fn behaviour_opt(&self) -> &WidgetBehaviourOption {
&self.bopt
}
fn measure(&self, style: &Style, atlas: &AtlasHandle, avail: Dimensioni) -> Dimensioni {
self.preferred_size_widget(style, atlas, avail)
}
fn run(&mut self, ctx: &mut WidgetCtx<'_>, control: &ControlState) -> ResourceState {
let res = self.handle_widget(ctx, control);
if control.clicked {
self.state = if self.state.is_expanded() {
NodeStateValue::Closed
} else {
NodeStateValue::Expanded
};
}
res
}
}