diff --git a/examples/widget-gallery/src/dropdown.rs b/examples/widget-gallery/src/dropdown.rs
index 7eed6a05..639bc559 100644
--- a/examples/widget-gallery/src/dropdown.rs
+++ b/examples/widget-gallery/src/dropdown.rs
@@ -1,10 +1,8 @@
use strum::IntoEnumIterator;
use floem::{
- peniko::{Brush, Color},
reactive::{create_effect, RwSignal, SignalGet},
- unit::UnitExt,
- views::{container, dropdown::Dropdown, label, stack, svg, Decorators, SvgColor},
+ views::{dropdown::Dropdown, Decorators},
IntoView,
};
@@ -24,29 +22,7 @@ impl std::fmt::Display for Values {
}
}
-const CHEVRON_DOWN: &str = r##""##;
-
pub fn dropdown_view() -> impl IntoView {
- let main_drop_view = move |item| {
- stack((
- label(move || item),
- container(
- svg(CHEVRON_DOWN)
- .style(|s| s.size(12, 12).set(SvgColor, Brush::Solid(Color::BLACK))),
- )
- .style(|s| {
- s.items_center()
- .padding(3.)
- .border_radius(7.pct())
- .hover(move |s| s.background(Color::LIGHT_GRAY))
- }),
- ))
- .style(|s| s.items_center().justify_between().size_full())
- .into_any()
- };
-
let dropdown_active_item = RwSignal::new(Values::Three);
create_effect(move |_| {
@@ -56,17 +32,7 @@ pub fn dropdown_view() -> impl IntoView {
form::form({
(form_item("Dropdown".to_string(), 120.0, move || {
- Dropdown::new_rw(
- // state
- dropdown_active_item,
- // main view function
- main_drop_view,
- // iterator to build list in dropdown
- Values::iter(),
- // view for each item in the list
- |item| label(move || item).into_any(),
- )
- .keyboard_navigable()
+ Dropdown::basic_rw(dropdown_active_item, Values::iter()).keyboard_navigatable()
}),)
})
}
diff --git a/src/id.rs b/src/id.rs
index 6044f48a..d5fa6857 100644
--- a/src/id.rs
+++ b/src/id.rs
@@ -338,6 +338,8 @@ impl ViewId {
}
/// Set the sytem popout menu that should be shown when this view is clicked
+ ///
+ /// Adds a primary-click context menu, which opens below the view.
pub fn update_popout_menu(&self, menu: impl Fn() -> Menu + 'static) {
self.state().borrow_mut().popout_menu = Some(Rc::new(menu));
}
diff --git a/src/style.rs b/src/style.rs
index a09cdf8a..a34ce6af 100644
--- a/src/style.rs
+++ b/src/style.rs
@@ -794,6 +794,7 @@ macro_rules! prop {
) => {
$(#[$meta])*
#[derive(Default, Copy, Clone)]
+ #[allow(missing_docs)]
$v struct $name;
impl $crate::style::StyleProp for $name {
type Type = $ty;
diff --git a/src/views/dropdown.rs b/src/views/dropdown.rs
index 2bad8da5..8a967d8d 100644
--- a/src/views/dropdown.rs
+++ b/src/views/dropdown.rs
@@ -4,7 +4,10 @@ use floem_reactive::{
as_child_of_current_scope, create_effect, create_updater, Scope, SignalGet, SignalUpdate,
};
use floem_winit::keyboard::{Key, NamedKey};
-use peniko::kurbo::{Point, Rect};
+use peniko::{
+ kurbo::{Point, Rect},
+ Color,
+};
use crate::{
action::{add_overlay, remove_overlay},
@@ -13,9 +16,10 @@ use crate::{
prop, prop_extractor,
style::{CustomStylable, Style, StyleClass, Width},
style_class,
- unit::PxPctAuto,
+ unit::{PxPctAuto, UnitExt},
view::{default_compute_layout, IntoView, View},
- views::{scroll, Decorators},
+ views::{container, scroll, stack, svg, text, Decorators},
+ AnyView,
};
use super::list;
@@ -23,7 +27,6 @@ use super::list;
type ChildFn = dyn Fn(T) -> (Box, Scope);
style_class!(pub DropdownClass);
-style_class!(pub DropdownScrollClass);
prop!(pub CloseOnAccept: bool {} = true);
prop_extractor!(DropdownStyle {
@@ -59,13 +62,12 @@ where
/// # use floem::views::empty;
/// # use floem::views::Decorators;
/// // root view
-/// empty()
-/// .style(|s|
-/// s.class(dropdown::DropdownClass, |s| {
-/// s.set(dropdown::CloseOnAccept, false)
-/// })
-/// );
-///```
+/// empty().style(|s| {
+/// s.class(dropdown::DropdownClass, |s| {
+/// s.set(dropdown::CloseOnAccept, false)
+/// })
+/// });
+/// ```
pub struct Dropdown {
id: ViewId,
main_view: ViewId,
@@ -162,7 +164,10 @@ impl View for Dropdown {
return EventPropagation::Stop;
}
Event::KeyUp(ref key_event)
- if key_event.key.logical_key == Key::Named(NamedKey::Enter) =>
+ if matches!(
+ key_event.key.logical_key,
+ Key::Named(NamedKey::Enter) | Key::Named(NamedKey::Space)
+ ) =>
{
self.swap_state()
}
@@ -174,6 +179,31 @@ impl View for Dropdown {
}
impl Dropdown {
+ pub fn default_main_view(item: T) -> AnyView
+ where
+ T: std::fmt::Display,
+ {
+ const CHEVRON_DOWN: &str = r##"
+
+ "##;
+
+ 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())
+ .hover(move |s| s.background(Color::LIGHT_GRAY))
+ }),
+ ))
+ .style(|s| s.items_center().justify_between().size_full())
+ .into_any()
+ }
+
/// Creates a new dropdown
pub fn new(
active_item: AIF,
@@ -213,7 +243,6 @@ impl Dropdown {
.on_event_stop(EventListener::FocusGained, move |_| {
inner_list_id.request_focus();
})
- .class(DropdownScrollClass)
.into_any()
});
@@ -244,22 +273,131 @@ impl Dropdown {
.class(DropdownClass)
}
- pub fn new_rw(
- active_item: impl SignalGet + SignalUpdate + Copy + 'static,
+ /// Creates a basic dropdown with a read-only function for the active item.
+ ///
+ /// # Example
+ /// ```rust
+ /// # use floem::{*, views::*, reactive::*};
+ /// # 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)));
+ /// ```
+ ///
+ /// This function is a convenience wrapper around `Dropdown::new` that uses default views
+ /// for the main and list items.
+ ///
+ /// See also [Dropdown::basic_rw].
+ ///
+ /// # 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(active_item: AIF, iterator: I) -> Dropdown
+ where
+ AIF: Fn() -> T + 'static,
+ I: IntoIterator- + Clone + 'static,
+ T: Clone + std::fmt::Display + 'static,
+ {
+ Self::new(active_item, Self::default_main_view, iterator, |v| {
+ crate::views::text(v).into_any()
+ })
+ }
+
+ /// Creates a new dropdown with a read-write signal for the active item.
+ ///
+ /// # Example
+ /// ```rust
+ /// # use floem::{*, views::*, reactive::*};
+ /// # use floem::{views::dropdown::*};
+ /// let active_item = RwSignal::new(3);
+ ///
+ /// Dropdown::new_rw(
+ /// active_item,
+ /// |item| text(item).into_any(),
+ /// 1..=5,
+ /// |item| text(item).into_any(),
+ /// );
+ /// ```
+ ///
+ /// This function allows for more customization compared to `basic_rw` by letting you specify
+ /// custom view functions for both the main dropdown display and the list items.
+ ///
+ /// # Arguments
+ ///
+ /// * `active_item` - A read-write signal representing the currently selected item.
+ /// It must implement both `SignalGet` and `SignalUpdate`.
+ /// * `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(
+ active_item: AI,
main_view: MF,
iterator: I,
list_item_fn: LF,
) -> Dropdown
where
- MF: Fn(T) -> Box + 'static,
+ AI: SignalGet + SignalUpdate + Copy + 'static,
+ MF: Fn(T) -> AnyView + 'static,
I: IntoIterator
- + Clone + 'static,
- LF: Fn(T) -> Box + Clone + 'static,
+ LF: Fn(T) -> AnyView + Clone + 'static,
T: Clone + 'static,
{
Self::new(move || active_item.get(), main_view, iterator, list_item_fn)
.on_accept(move |nv| active_item.set(nv))
}
+ /// Creates a basic dropdown with a read-write signal for the active item.
+ ///
+ /// # Example:
+ /// ```rust
+ /// # use floem::{*, views::*, reactive::*};
+ /// # use floem::{views::dropdown::*};
+ /// let dropdown_active_item = RwSignal::new(3);
+ ///
+ /// Dropdown::basic_rw(dropdown_active_item, 1..=5);
+ /// ```
+ ///
+ /// This function is a convenience wrapper around `Dropdown::new_rw` that uses default views
+ /// for the main and list items.
+ ///
+ /// # Arguments
+ ///
+ /// * `active_item` - A read-write signal representing the currently selected item.
+ /// It must implement `SignalGet` and `SignalUpdate`.
+ /// * `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(active_item: AI, iterator: I) -> Dropdown
+ where
+ AI: SignalGet + SignalUpdate + Copy + 'static,
+ I: IntoIterator
- + Clone + 'static,
+ T: Clone + std::fmt::Display + 'static,
+ {
+ Self::new_rw(active_item, Self::default_main_view, iterator, |v| {
+ text(v).into_any()
+ })
+ }
+
pub fn show_list(self, show: impl Fn() -> bool + 'static) -> Self {
let id = self.id();
create_effect(move |_| {
diff --git a/src/views/list.rs b/src/views/list.rs
index c4948ecf..299c0569 100644
--- a/src/views/list.rs
+++ b/src/views/list.rs
@@ -63,7 +63,11 @@ impl List {
/// ## Example
/// ```rust
/// use floem::views::*;
-/// list(vec![1,1,2,2,3,4,5,6,7,8,9].iter().map(|val| text(val)));
+/// list(
+/// vec![1, 1, 2, 2, 3, 4, 5, 6, 7, 8, 9]
+/// .iter()
+/// .map(|val| text(val)),
+/// );
/// ```
pub fn list(iterator: impl IntoIterator
- ) -> List
where
@@ -139,7 +143,7 @@ where
}
EventPropagation::Stop
}
- Key::Named(NamedKey::Enter) => {
+ Key::Named(NamedKey::Enter) | Key::Named(NamedKey::Space) => {
list_id.update_state(ListUpdate::Accept);
EventPropagation::Stop
}