cushy/src/widgets/select.rs
Jonathan Johnson 1c8d4e0176
ButtonClick + Overlays at locations
The overlay example now supports right-clicking to open overlays at the
clicked location, showing how a context menu widget can begin being
built.
2024-05-08 08:38:26 -07:00

91 lines
2.9 KiB
Rust

//! A selectable, labeled widget representing a value.
use std::fmt::Debug;
use kludgine::Color;
use crate::styles::components::OutlineColor;
use crate::styles::{Component, DynamicComponent};
use crate::value::{Destination, Dynamic, IntoDynamic, IntoValue, MapEach, Source, Value};
use crate::widget::{MakeWidget, MakeWidgetWithTag, WidgetInstance};
use crate::widgets::button::{ButtonBackground, ButtonHoverBackground, ButtonKind};
/// A selectable, labeled widget representing a value.
#[derive(Debug)]
pub struct Select<T> {
/// The value this button represents.
pub value: T,
/// The state (value) of the select.
pub state: Dynamic<T>,
/// The button kind to use as the basis for this select. Selects default to
/// [`ButtonKind::Transparent`].
pub kind: Value<ButtonKind>,
label: WidgetInstance,
}
impl<T> Select<T> {
/// Returns a new select that sets `state` to `value` when pressed. `label`
/// is drawn inside of the button.
pub fn new(value: T, state: impl IntoDynamic<T>, label: impl MakeWidget) -> Self {
Self {
value,
state: state.into_dynamic(),
kind: Value::Constant(ButtonKind::Transparent),
label: label.make_widget(),
}
}
/// Updates the button kind to use as the basis for this select, and
/// returns self.
///
/// Selects default to [`ButtonKind::Transparent`].
#[must_use]
pub fn kind(mut self, kind: impl IntoValue<ButtonKind>) -> Self {
self.kind = kind.into_value();
self
}
}
impl<T> MakeWidgetWithTag for Select<T>
where
T: Clone + Debug + PartialEq + Send + Sync + 'static,
{
fn make_with_tag(self, id: crate::widget::WidgetTag) -> WidgetInstance {
let selected = self.state.map_each({
let value = self.value.clone();
move |state| state == &value
});
let selected_color = DynamicComponent::new({
let selected = selected.clone();
move |context| {
if selected.get_tracking_redraw(context) {
Some(Component::Color(context.get(&SelectedColor)))
} else {
None
}
}
});
let kind = (&selected, &self.kind.into_dynamic()).map_each(|(selected, default_kind)| {
if *selected {
ButtonKind::Solid
} else {
*default_kind
}
});
self.label
.into_button()
.on_click(move |_| {
self.state.set(self.value.clone());
})
.kind(kind)
.with_dynamic(&ButtonBackground, selected_color.clone())
.with_dynamic(&ButtonHoverBackground, selected_color)
.make_with_tag(id)
}
}
define_components! {
Select {
/// The color of the selected [`Select`] widget.
SelectedColor(Color, "color", @OutlineColor)
}
}