Skip to content

Commit

Permalink
finish dropdown module
Browse files Browse the repository at this point in the history
  • Loading branch information
jrmoulton committed Oct 12, 2024
1 parent 01a064c commit 5bc8770
Showing 1 changed file with 98 additions and 51 deletions.
149 changes: 98 additions & 51 deletions src/views/dropdown.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
#![deny(missing_docs)]
//! A view that allows the user to select an item from a list of items.
//!
//! The [Dropdown] struct provides several constructors, each offering different levels of customization and ease of use
//!
//! The [DropdownCustomStyle] struct allows for easy and advanced customization of the dropdown's appearance
use std::{any::Any, rc::Rc};

use floem_reactive::{
Expand All @@ -16,40 +22,47 @@ use crate::{
prop, prop_extractor,
style::{CustomStylable, Style, StyleClass, Width},
style_class,
unit::{PxPctAuto, UnitExt},
unit::PxPctAuto,
view::{default_compute_layout, IntoView, View},
views::{container, scroll, stack, svg, text, Decorators},
AnyView,
};

use super::list;

type ChildFn<T> = dyn Fn(T) -> (Box<dyn View>, Scope);
type ChildFn<T> = dyn Fn(T) -> (AnyView, Scope);

style_class!(pub DropdownClass);
style_class!(
/// A Style class that is applied to all dropdowns.
pub DropdownClass
);

prop!(pub CloseOnAccept: bool {} = true);
prop!(
/// A property that determines whether the dropdown should close automatically when an item is selected.
pub CloseOnAccept: bool {} = true
);
prop_extractor!(DropdownStyle {
close_on_accept: CloseOnAccept,
});

pub fn dropdown<T, MF, I, LF, AIF>(
active_item: AIF,
main_view: MF,
iterator: I,
list_item_fn: LF,
) -> Dropdown<T>
where
MF: Fn(T) -> Box<dyn View> + 'static,
I: IntoIterator<Item = T> + Clone + 'static,
LF: Fn(T) -> Box<dyn View> + Clone + 'static,
T: Clone + 'static,
AIF: Fn() -> T + 'static,
{
Dropdown::new(active_item, main_view, iterator, list_item_fn)
}

/// A dropdown widget
/// # A customizable dropdown view for selecting an item from a list.
///
/// The `Dropdown` struct provides several constructors, each offering different levels of
/// customization and ease of use:
///
/// - [`Dropdown::basic_rw`]: The simplest constructor, ideal for quick setup with minimal customization.
/// It uses default views and assumes direct access to a signal that can be both read from and written to for driving the selection of an item.
///
/// - [`Dropdown::basic`]: Similar to `basic_rw`, but uses a read-only function for the active item, and requires that you manually provide an `on_accept` callback.
///
/// - [`Dropdown::new`]: Offers full customization, letting you define custom view functions for
/// both the main display and list items. Uses a read-only function for the active item and requires that you manually provide an `on_accept` callback.
///
/// - [`Dropdown::new_rw`]: Combines the full customization of `new` with the read-write signal
/// capability of `basic_rw`.
///
///
/// Choose the constructor that best fits your needs based on the level of customization required.
///
/// **Styling**:
/// You can modify the behavior of the dropdown through the `CloseOnAccept` property.
Expand Down Expand Up @@ -179,6 +192,10 @@ impl<T: 'static> View for Dropdown<T> {
}

impl<T> Dropdown<T> {
/// Creates a default main view for the dropdown.
///
/// This function generates a view that displays the given item as text,
/// along with a chevron-down icon to indicate that it's a dropdown.
pub fn default_main_view(item: T) -> AnyView
where
T: std::fmt::Display,
Expand All @@ -191,30 +208,63 @@ impl<T> Dropdown<T> {
</svg>
"##;

// TODO: this should be more customizable
stack((
text(item),
container(svg(CHEVRON_DOWN).style(|s| s.size(12, 12).color(Color::BLACK))).style(|s| {
s.items_center()
.padding(3.)
.border_radius(7.pct())
.border_radius(5)
.hover(move |s| s.background(Color::LIGHT_GRAY))
}),
))
.style(|s| s.items_center().justify_between().size_full())
.into_any()
}

/// Creates a new dropdown
/// Creates a new customizable dropdown.
///
/// You might want to use some of the simpler constructors like [Dropdown::basic] or [Dropdown::basic_rw].
///
/// # Example
/// ```rust
/// # use floem::{*, views::*, reactive::*};
/// # use floem::views::dropdown::*;
/// let active_item = RwSignal::new(3);
///
/// Dropdown::new(
/// move || active_item.get(),
/// |main_item| text(main_item).into_any(),
/// 1..=5,
/// |list_item| text(list_item).into_any(),
/// )
/// .on_accept(move |item| active_item.set(item));
/// ```
///
/// This function provides full control over the dropdown's appearance and behavior
/// by allowing custom view functions for both the main display and list items.
///
/// # Arguments
///
/// * `active_item` - A function that returns the currently selected item.
///
/// * `main_view` - A function that takes a value of type `T` and returns an `AnyView`
/// to be used as the main dropdown display.
///
/// * `iterator` - An iterator that provides the items to be displayed in the dropdown list.
///
/// * `list_item_fn` - A function that takes a value of type `T` and returns an `AnyView`
/// to be used for each item in the dropdown list.
pub fn new<MF, I, LF, AIF>(
active_item: AIF,
main_view: MF,
iterator: I,
list_item_fn: LF,
) -> Dropdown<T>
where
MF: Fn(T) -> Box<dyn View> + 'static,
MF: Fn(T) -> AnyView + 'static,
I: IntoIterator<Item = T> + Clone + 'static,
LF: Fn(T) -> Box<dyn View> + Clone + 'static,
LF: Fn(T) -> AnyView + Clone + 'static,
T: Clone + 'static,
AIF: Fn() -> T + 'static,
{
Expand Down Expand Up @@ -281,7 +331,7 @@ impl<T> Dropdown<T> {
/// # use floem::views::dropdown::*;
/// let active_item = RwSignal::new(3);
///
/// Dropdown::basic(move || active_item.get(), 1..=5).on_accept(move |val| active_item.set(val)));
/// Dropdown::basic(move || active_item.get(), 1..=5).on_accept(move |val| active_item.set(val));
/// ```
///
/// This function is a convenience wrapper around `Dropdown::new` that uses default views
Expand All @@ -292,12 +342,8 @@ impl<T> Dropdown<T> {
/// # Arguments
///
/// * `active_item` - A function that returns the currently selected item.
/// * `AIF` - The type of the active item function of type `T`.
/// * `T` - The type of items in the dropdown. Must implement `Clone` and `std::fmt::Display`.
///
/// * `iterator` - An iterator that provides the items to be displayed in the dropdown list.
/// It must be `Clone` and iterate over items of type `T`.
/// * `I` - The type of the iterator.
pub fn basic<AIF, I>(active_item: AIF, iterator: I) -> Dropdown<T>
where
AIF: Fn() -> T + 'static,
Expand Down Expand Up @@ -331,22 +377,14 @@ impl<T> Dropdown<T> {
/// # Arguments
///
/// * `active_item` - A read-write signal representing the currently selected item.
/// It must implement both `SignalGet<T>` and `SignalUpdate<T>`.
/// * `T` - The type of items in the dropdown. Must implement `Clone`.
/// * `AI` - The type of the active item signal.
///
/// * `main_view` - A function that takes a value of type `T` and returns an `AnyView`
/// to be used as the main dropdown display.
///
/// * `iterator` - An iterator that provides the items to be displayed in the dropdown list.
/// It must be `Clone` and iterate over items of type `T`.
///
/// * `list_item_fn` - A function that takes a value of type `T` and returns an `AnyView`
/// to be used for each item in the dropdown list.
///
/// # Type Parameters
///
/// * `MF` - The type of the main view function.
/// * `I` - The type of the iterator.
/// * `LF` - The type of the list item function.
pub fn new_rw<AI, MF, I, LF>(
active_item: AI,
main_view: MF,
Expand Down Expand Up @@ -382,11 +420,8 @@ impl<T> Dropdown<T> {
///
/// * `active_item` - A read-write signal representing the currently selected item.
/// It must implement `SignalGet<T>` and `SignalUpdate<T>`.
/// * `T` - The type of items in the dropdown. Must implement `Clone` and `std::fmt::Display`.
/// * `AI` - The type of the active item signal.
///
/// * `iterator` - An iterator that provides the items to be displayed in the dropdown list.
/// It must be `Clone` and iterate over items of type `T`.
/// * `I` - The type of the iterator.
pub fn basic_rw<AI, I>(active_item: AI, iterator: I) -> Dropdown<T>
where
AI: SignalGet<T> + SignalUpdate<T> + Copy + 'static,
Expand All @@ -398,6 +433,10 @@ impl<T> Dropdown<T> {
})
}

/// Sets a reactive condition for showing or hiding the dropdown list.
///
/// # Reactivity
/// The `show` function will be re-run whenever any signal it depends on changes.
pub fn show_list(self, show: impl Fn() -> bool + 'static) -> Self {
let id = self.id();
create_effect(move |_| {
Expand All @@ -407,11 +446,17 @@ impl<T> Dropdown<T> {
self
}

/// Sets a callback function to be called when an item is selected from the dropdown.
///
/// Only one `on_accept` callback can be set at a time.
pub fn on_accept(mut self, on_accept: impl Fn(T) + 'static) -> Self {
self.on_accept = Some(Box::new(on_accept));
self
}

/// Sets a callback function to be called when the dropdown is opened.
///
/// Only one `on_open` callback can be set at a time.
pub fn on_open(mut self, on_open: impl Fn(bool) + 'static) -> Self {
self.on_open = Some(Box::new(on_open));
self
Expand Down Expand Up @@ -472,27 +517,29 @@ impl<T> Dropdown<T> {
}));
}

/// Sets the custom style properties of the `DropDown`.
/// Sets the custom style properties of the `Dropdown`.
pub fn dropdown_style(
self,
style: impl Fn(DropDownCustomStyle) -> DropDownCustomStyle + 'static,
style: impl Fn(DropdownCustomStyle) -> DropdownCustomStyle + 'static,
) -> Self {
self.custom_style(style)
}
}

#[derive(Debug, Clone, Default)]
pub struct DropDownCustomStyle(Style);
impl From<DropDownCustomStyle> for Style {
fn from(val: DropDownCustomStyle) -> Self {
/// A struct that allows for easy custom styling of the `Dropdown` using the [Dropdown::dropdown_style] method or the [Style::custom_style](crate::style::CustomStyle::custom_style) method.
pub struct DropdownCustomStyle(Style);
impl From<DropdownCustomStyle> for Style {
fn from(val: DropdownCustomStyle) -> Self {
val.0
}
}
impl<T> CustomStylable<DropDownCustomStyle> for Dropdown<T> {
impl<T> CustomStylable<DropdownCustomStyle> for Dropdown<T> {
type DV = Self;
}

impl DropDownCustomStyle {
impl DropdownCustomStyle {
/// Creates a new `DropDownCustomStyle` with default values.
pub fn new() -> Self {
Self::default()
}
Expand Down

0 comments on commit 5bc8770

Please sign in to comment.