feat(dropdown): add Id support with custom close, open operations
This commit is contained in:
parent
47cc6dbdbf
commit
7eecbe30d7
3 changed files with 236 additions and 88 deletions
|
|
@ -7,15 +7,17 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
pub mod menu;
|
pub mod menu;
|
||||||
use iced_core::window;
|
|
||||||
pub use menu::Menu;
|
pub use menu::Menu;
|
||||||
|
|
||||||
pub mod multi;
|
pub mod multi;
|
||||||
|
pub mod operation;
|
||||||
|
|
||||||
mod widget;
|
mod widget;
|
||||||
pub use widget::*;
|
pub use widget::*;
|
||||||
|
|
||||||
use crate::surface;
|
use crate::surface;
|
||||||
|
pub use iced_core::widget::Id;
|
||||||
|
use iced_core::window;
|
||||||
|
|
||||||
/// Displays a list of options in a popover menu on select.
|
/// Displays a list of options in a popover menu on select.
|
||||||
pub fn dropdown<
|
pub fn dropdown<
|
||||||
|
|
@ -53,3 +55,13 @@ pub fn popup_dropdown<
|
||||||
|
|
||||||
dropdown
|
dropdown
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Produces a [`Task`] that closes the [`Dropdown`].
|
||||||
|
pub fn close<Message: 'static>(id: Id) -> iced_runtime::Task<Message> {
|
||||||
|
iced_runtime::task::effect(iced_runtime::Action::Widget(Box::new(operation::close(id))))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Produces a [`Task`] that opens the [`Dropdown`].
|
||||||
|
pub fn open<Message: 'static>(id: Id) -> iced_runtime::Task<Message> {
|
||||||
|
iced_runtime::task::effect(iced_runtime::Action::Widget(Box::new(operation::open(id))))
|
||||||
|
}
|
||||||
|
|
|
||||||
72
src/widget/dropdown/operation.rs
Normal file
72
src/widget/dropdown/operation.rs
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
// Copyright 2025 System76 <info@system76.com>
|
||||||
|
// SPDX-License-Identifier: MPL-2.0 AND MIT
|
||||||
|
//! Operate on dropdown widgets.
|
||||||
|
|
||||||
|
use super::State;
|
||||||
|
use iced::Rectangle;
|
||||||
|
use iced_core::widget::{Id, Operation};
|
||||||
|
|
||||||
|
pub trait Dropdown {
|
||||||
|
fn close(&mut self);
|
||||||
|
fn open(&mut self);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Produces a [`Task`] that closes a [`Dropdown`] popup.
|
||||||
|
pub fn close<T>(id: Id) -> impl Operation<T> {
|
||||||
|
struct Close(Id);
|
||||||
|
|
||||||
|
impl<T> Operation<T> for Close {
|
||||||
|
fn custom(&mut self, state: &mut dyn std::any::Any, id: Option<&Id>) {
|
||||||
|
if id.map_or(true, |id| id != &self.0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(state) = state.downcast_mut::<State>() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
state.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn container(
|
||||||
|
&mut self,
|
||||||
|
_id: Option<&Id>,
|
||||||
|
_bounds: Rectangle,
|
||||||
|
operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>),
|
||||||
|
) {
|
||||||
|
operate_on_children(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Close(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Produces a [`Task`] that opens a [`Dropdown`] popup.
|
||||||
|
pub fn open<T>(id: Id) -> impl Operation<T> {
|
||||||
|
struct Open(Id);
|
||||||
|
|
||||||
|
impl<T> Operation<T> for Open {
|
||||||
|
fn custom(&mut self, state: &mut dyn std::any::Any, id: Option<&Id>) {
|
||||||
|
if id.map_or(true, |id| id != &self.0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(state) = state.downcast_mut::<State>() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
state.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn container(
|
||||||
|
&mut self,
|
||||||
|
_id: Option<&Id>,
|
||||||
|
_bounds: Rectangle,
|
||||||
|
operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>),
|
||||||
|
) {
|
||||||
|
operate_on_children(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Open(id)
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// Copyright 2019 Héctor Ramón, Iced contributors
|
// Copyright 2019 Héctor Ramón, Iced contributors
|
||||||
// SPDX-License-Identifier: MPL-2.0 AND MIT
|
// SPDX-License-Identifier: MPL-2.0 AND MIT
|
||||||
|
|
||||||
|
use super::Id;
|
||||||
use super::menu::{self, Menu};
|
use super::menu::{self, Menu};
|
||||||
use crate::widget::icon::{self, Handle};
|
use crate::widget::icon::{self, Handle};
|
||||||
use crate::{Element, surface};
|
use crate::{Element, surface};
|
||||||
|
|
@ -18,19 +19,21 @@ use iced_widget::pick_list::{self, Catalog};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::hash::{DefaultHasher, Hash, Hasher};
|
use std::hash::{DefaultHasher, Hash, Hasher};
|
||||||
use std::marker::PhantomData;
|
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::{Arc, LazyLock, Mutex};
|
use std::sync::{Arc, LazyLock, Mutex};
|
||||||
|
|
||||||
pub type DropdownView<Message> = Arc<dyn Fn() -> Element<'static, Message> + Send + Sync>;
|
pub type DropdownView<Message> = Arc<dyn Fn() -> Element<'static, Message> + Send + Sync>;
|
||||||
static AUTOSIZE_ID: LazyLock<crate::widget::Id> =
|
static AUTOSIZE_ID: LazyLock<crate::widget::Id> =
|
||||||
LazyLock::new(|| crate::widget::Id::new("cosmic-applet-autosize"));
|
LazyLock::new(|| crate::widget::Id::new("cosmic-applet-autosize"));
|
||||||
|
|
||||||
/// A widget for selecting a single value from a list of selections.
|
/// A widget for selecting a single value from a list of selections.
|
||||||
#[derive(Setters)]
|
#[derive(Setters)]
|
||||||
pub struct Dropdown<'a, S: AsRef<str> + Send + Sync + Clone + 'static, Message, AppMessage>
|
pub struct Dropdown<'a, S: AsRef<str> + Send + Sync + Clone + 'static, Message, AppMessage>
|
||||||
where
|
where
|
||||||
[S]: std::borrow::ToOwned,
|
[S]: std::borrow::ToOwned,
|
||||||
{
|
{
|
||||||
|
#[setters(skip)]
|
||||||
|
id: Option<Id>,
|
||||||
#[setters(skip)]
|
#[setters(skip)]
|
||||||
on_selected: Arc<dyn Fn(usize) -> Message + Send + Sync>,
|
on_selected: Arc<dyn Fn(usize) -> Message + Send + Sync>,
|
||||||
#[setters(skip)]
|
#[setters(skip)]
|
||||||
|
|
@ -78,6 +81,7 @@ where
|
||||||
on_selected: impl Fn(usize) -> Message + 'static + Send + Sync,
|
on_selected: impl Fn(usize) -> Message + 'static + Send + Sync,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
id: None,
|
||||||
on_selected: Arc::new(on_selected),
|
on_selected: Arc::new(on_selected),
|
||||||
selections,
|
selections,
|
||||||
icons: Cow::Borrowed(&[]),
|
icons: Cow::Borrowed(&[]),
|
||||||
|
|
@ -100,12 +104,13 @@ where
|
||||||
/// Handle dropdown requests for popup creation.
|
/// Handle dropdown requests for popup creation.
|
||||||
/// Intended to be used with [`crate::app::message::get_popup`]
|
/// Intended to be used with [`crate::app::message::get_popup`]
|
||||||
pub fn with_popup<NewAppMessage>(
|
pub fn with_popup<NewAppMessage>(
|
||||||
mut self,
|
self,
|
||||||
parent_id: window::Id,
|
parent_id: window::Id,
|
||||||
on_surface_action: impl Fn(surface::Action) -> Message + Send + Sync + 'static,
|
on_surface_action: impl Fn(surface::Action) -> Message + Send + Sync + 'static,
|
||||||
action_map: impl Fn(Message) -> NewAppMessage + Send + Sync + 'static,
|
action_map: impl Fn(Message) -> NewAppMessage + Send + Sync + 'static,
|
||||||
) -> Dropdown<'a, S, Message, NewAppMessage> {
|
) -> Dropdown<'a, S, Message, NewAppMessage> {
|
||||||
let Self {
|
let Self {
|
||||||
|
id,
|
||||||
on_selected,
|
on_selected,
|
||||||
selections,
|
selections,
|
||||||
icons,
|
icons,
|
||||||
|
|
@ -121,6 +126,7 @@ where
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
Dropdown::<'a, S, Message, NewAppMessage> {
|
Dropdown::<'a, S, Message, NewAppMessage> {
|
||||||
|
id,
|
||||||
on_selected,
|
on_selected,
|
||||||
selections,
|
selections,
|
||||||
icons,
|
icons,
|
||||||
|
|
@ -138,6 +144,11 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn id(mut self, id: Id) -> Self {
|
||||||
|
self.id = Some(id);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "winit", feature = "wayland"))]
|
#[cfg(all(feature = "winit", feature = "wayland"))]
|
||||||
pub fn with_positioner(
|
pub fn with_positioner(
|
||||||
mut self,
|
mut self,
|
||||||
|
|
@ -299,6 +310,17 @@ where
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn operate(
|
||||||
|
&self,
|
||||||
|
tree: &mut Tree,
|
||||||
|
_layout: Layout<'_>,
|
||||||
|
_renderer: &crate::Renderer,
|
||||||
|
operation: &mut dyn iced_core::widget::Operation,
|
||||||
|
) {
|
||||||
|
let state = tree.state.downcast_mut::<State>();
|
||||||
|
operation.custom(state, self.id.as_ref());
|
||||||
|
}
|
||||||
|
|
||||||
fn overlay<'b>(
|
fn overlay<'b>(
|
||||||
&'b mut self,
|
&'b mut self,
|
||||||
tree: &'b mut Tree,
|
tree: &'b mut Tree,
|
||||||
|
|
@ -364,6 +386,8 @@ pub struct State {
|
||||||
menu: menu::State,
|
menu: menu::State,
|
||||||
keyboard_modifiers: keyboard::Modifiers,
|
keyboard_modifiers: keyboard::Modifiers,
|
||||||
is_open: Arc<AtomicBool>,
|
is_open: Arc<AtomicBool>,
|
||||||
|
close_operation: bool,
|
||||||
|
open_operation: bool,
|
||||||
hovered_option: Arc<Mutex<Option<usize>>>,
|
hovered_option: Arc<Mutex<Option<usize>>>,
|
||||||
hashes: Vec<u64>,
|
hashes: Vec<u64>,
|
||||||
selections: Vec<crate::Plain>,
|
selections: Vec<crate::Plain>,
|
||||||
|
|
@ -389,6 +413,8 @@ impl State {
|
||||||
selections: Vec::new(),
|
selections: Vec::new(),
|
||||||
hashes: Vec::new(),
|
hashes: Vec::new(),
|
||||||
popup_id: window::Id::unique(),
|
popup_id: window::Id::unique(),
|
||||||
|
close_operation: false,
|
||||||
|
open_operation: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -399,6 +425,16 @@ impl Default for State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl super::operation::Dropdown for State {
|
||||||
|
fn close(&mut self) {
|
||||||
|
self.close_operation = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn open(&mut self) {
|
||||||
|
self.open_operation = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Computes the layout of a [`Dropdown`].
|
/// Computes the layout of a [`Dropdown`].
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn layout(
|
pub fn layout(
|
||||||
|
|
@ -484,10 +520,121 @@ pub fn update<
|
||||||
font: Option<crate::font::Font>,
|
font: Option<crate::font::Font>,
|
||||||
selected_option: Option<usize>,
|
selected_option: Option<usize>,
|
||||||
) -> event::Status {
|
) -> event::Status {
|
||||||
|
let state = state();
|
||||||
|
|
||||||
|
let open = |shell: &mut Shell<'_, Message>,
|
||||||
|
state: &mut State,
|
||||||
|
on_selected: Arc<dyn Fn(usize) -> Message + Send + Sync + 'static>| {
|
||||||
|
state.is_open.store(true, Ordering::Relaxed);
|
||||||
|
let mut hovered_guard = state.hovered_option.lock().unwrap();
|
||||||
|
*hovered_guard = selected;
|
||||||
|
let id = window::Id::unique();
|
||||||
|
state.popup_id = id;
|
||||||
|
#[cfg(all(feature = "winit", feature = "wayland"))]
|
||||||
|
if let Some(((on_surface_action, parent), action_map)) = on_surface_action
|
||||||
|
.as_ref()
|
||||||
|
.zip(_window_id)
|
||||||
|
.zip(action_map.clone())
|
||||||
|
{
|
||||||
|
use iced_runtime::platform_specific::wayland::popup::{
|
||||||
|
SctkPopupSettings, SctkPositioner,
|
||||||
|
};
|
||||||
|
let bounds = layout.bounds();
|
||||||
|
let anchor_rect = Rectangle {
|
||||||
|
x: bounds.x as i32,
|
||||||
|
y: bounds.y as i32,
|
||||||
|
width: bounds.width as i32,
|
||||||
|
height: bounds.height as i32,
|
||||||
|
};
|
||||||
|
let icon_width = if icons.is_empty() { 0.0 } else { 24.0 };
|
||||||
|
let measure = |_label: &str, selection_paragraph: &crate::Paragraph| -> f32 {
|
||||||
|
selection_paragraph.min_width().round()
|
||||||
|
};
|
||||||
|
let pad_width = padding.horizontal().mul_add(2.0, 16.0);
|
||||||
|
|
||||||
|
let selections_width = selections
|
||||||
|
.iter()
|
||||||
|
.zip(state.selections.iter_mut())
|
||||||
|
.map(|(label, selection)| measure(label.as_ref(), selection.raw()))
|
||||||
|
.fold(0.0, |next, current| current.max(next));
|
||||||
|
|
||||||
|
let icons: Cow<'static, [Handle]> = Cow::Owned(icons.to_vec());
|
||||||
|
let selections: Cow<'static, [S]> = Cow::Owned(selections.to_vec());
|
||||||
|
let state = state.clone();
|
||||||
|
let on_close = surface::action::destroy_popup(id);
|
||||||
|
let on_surface_action_clone = on_surface_action.clone();
|
||||||
|
let translation = layout.virtual_offset();
|
||||||
|
let get_popup_action = surface::action::simple_popup::<AppMessage>(
|
||||||
|
move || {
|
||||||
|
SctkPopupSettings {
|
||||||
|
parent,
|
||||||
|
id,
|
||||||
|
input_zone: None,
|
||||||
|
positioner: SctkPositioner {
|
||||||
|
size: Some((selections_width as u32 + gap as u32 + pad_width as u32 + icon_width as u32, 10)),
|
||||||
|
anchor_rect,
|
||||||
|
// TODO: left or right alignment based on direction?
|
||||||
|
anchor: cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Anchor::BottomLeft,
|
||||||
|
gravity: cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::BottomRight,
|
||||||
|
reactive: true,
|
||||||
|
offset: ((-padding.left - translation.x) as i32, -translation.y as i32),
|
||||||
|
constraint_adjustment: 9,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
parent_size: None,
|
||||||
|
grab: true,
|
||||||
|
close_with_children: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some(Box::new(move || {
|
||||||
|
let action_map = action_map.clone();
|
||||||
|
let on_selected = on_selected.clone();
|
||||||
|
let e: Element<'static, crate::Action<AppMessage>> =
|
||||||
|
Element::from(menu_widget(
|
||||||
|
bounds,
|
||||||
|
&state,
|
||||||
|
gap,
|
||||||
|
padding,
|
||||||
|
text_size.unwrap_or(14.0),
|
||||||
|
selections.clone(),
|
||||||
|
icons.clone(),
|
||||||
|
selected_option,
|
||||||
|
Arc::new(move |i| on_selected.clone()(i)),
|
||||||
|
Some(on_surface_action_clone(on_close.clone())),
|
||||||
|
))
|
||||||
|
.map(move |m| crate::Action::App(action_map.clone()(m)));
|
||||||
|
e
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
shell.publish(on_surface_action(get_popup_action));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let is_open = state.is_open.load(Ordering::Relaxed);
|
||||||
|
let refresh = state.close_operation && state.open_operation;
|
||||||
|
|
||||||
|
if state.close_operation {
|
||||||
|
state.close_operation = false;
|
||||||
|
state.is_open.store(false, Ordering::SeqCst);
|
||||||
|
if is_open {
|
||||||
|
#[cfg(all(feature = "winit", feature = "wayland"))]
|
||||||
|
if let Some(ref on_close) = on_surface_action {
|
||||||
|
shell.publish(on_close(surface::action::destroy_popup(state.popup_id)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if state.open_operation {
|
||||||
|
state.open_operation = false;
|
||||||
|
state.is_open.store(true, Ordering::SeqCst);
|
||||||
|
if (refresh && is_open) || (!refresh && !is_open) {
|
||||||
|
open(shell, state, on_selected.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
|
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
|
||||||
| Event::Touch(touch::Event::FingerPressed { .. }) => {
|
| Event::Touch(touch::Event::FingerPressed { .. }) => {
|
||||||
let state = state();
|
|
||||||
let is_open = state.is_open.load(Ordering::Relaxed);
|
let is_open = state.is_open.load(Ordering::Relaxed);
|
||||||
if is_open {
|
if is_open {
|
||||||
// Event wasn't processed by overlay, so cursor was clicked either outside it's
|
// Event wasn't processed by overlay, so cursor was clicked either outside it's
|
||||||
|
|
@ -499,87 +646,7 @@ pub fn update<
|
||||||
}
|
}
|
||||||
event::Status::Captured
|
event::Status::Captured
|
||||||
} else if cursor.is_over(layout.bounds()) {
|
} else if cursor.is_over(layout.bounds()) {
|
||||||
state.is_open.store(true, Ordering::Relaxed);
|
open(shell, state, on_selected);
|
||||||
let mut hovered_guard = state.hovered_option.lock().unwrap();
|
|
||||||
*hovered_guard = selected;
|
|
||||||
let id = window::Id::unique();
|
|
||||||
state.popup_id = id;
|
|
||||||
#[cfg(all(feature = "winit", feature = "wayland"))]
|
|
||||||
if let Some(((on_surface_action, parent), action_map)) =
|
|
||||||
on_surface_action.zip(_window_id).zip(action_map)
|
|
||||||
{
|
|
||||||
use iced_runtime::platform_specific::wayland::popup::{
|
|
||||||
SctkPopupSettings, SctkPositioner,
|
|
||||||
};
|
|
||||||
let bounds = layout.bounds();
|
|
||||||
let anchor_rect = Rectangle {
|
|
||||||
x: bounds.x as i32,
|
|
||||||
y: bounds.y as i32,
|
|
||||||
width: bounds.width as i32,
|
|
||||||
height: bounds.height as i32,
|
|
||||||
};
|
|
||||||
let icon_width = if icons.is_empty() { 0.0 } else { 24.0 };
|
|
||||||
let measure = |_label: &str, selection_paragraph: &crate::Paragraph| -> f32 {
|
|
||||||
selection_paragraph.min_width().round()
|
|
||||||
};
|
|
||||||
let pad_width = padding.horizontal().mul_add(2.0, 16.0);
|
|
||||||
|
|
||||||
let selections_width = selections
|
|
||||||
.iter()
|
|
||||||
.zip(state.selections.iter_mut())
|
|
||||||
.map(|(label, selection)| measure(label.as_ref(), selection.raw()))
|
|
||||||
.fold(0.0, |next, current| current.max(next));
|
|
||||||
|
|
||||||
let icons: Cow<'static, [Handle]> = Cow::Owned(icons.to_vec());
|
|
||||||
let selections: Cow<'static, [S]> = Cow::Owned(selections.to_vec());
|
|
||||||
let state = state.clone();
|
|
||||||
let on_close = surface::action::destroy_popup(id);
|
|
||||||
let on_surface_action_clone = on_surface_action.clone();
|
|
||||||
let translation = layout.virtual_offset();
|
|
||||||
let get_popup_action = surface::action::simple_popup::<AppMessage>(
|
|
||||||
move || {
|
|
||||||
SctkPopupSettings {
|
|
||||||
parent,
|
|
||||||
id,
|
|
||||||
input_zone: None,
|
|
||||||
positioner: SctkPositioner {
|
|
||||||
size: Some((selections_width as u32 + gap as u32 + pad_width as u32 + icon_width as u32, 10)),
|
|
||||||
anchor_rect,
|
|
||||||
// TODO: left or right alignment based on direction?
|
|
||||||
anchor: cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Anchor::BottomLeft,
|
|
||||||
gravity: cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::BottomRight,
|
|
||||||
reactive: true,
|
|
||||||
offset: ((-padding.left - translation.x) as i32, -translation.y as i32),
|
|
||||||
constraint_adjustment: 9,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
parent_size: None,
|
|
||||||
grab: true,
|
|
||||||
close_with_children: true,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Some(Box::new(move || {
|
|
||||||
let action_map = action_map.clone();
|
|
||||||
let on_selected = on_selected.clone();
|
|
||||||
let e: Element<'static, crate::Action<AppMessage>> =
|
|
||||||
Element::from(menu_widget(
|
|
||||||
bounds,
|
|
||||||
&state,
|
|
||||||
gap,
|
|
||||||
padding,
|
|
||||||
text_size.unwrap_or(14.0),
|
|
||||||
selections.clone(),
|
|
||||||
icons.clone(),
|
|
||||||
selected_option,
|
|
||||||
Arc::new(move |i| on_selected.clone()(i)),
|
|
||||||
Some(on_surface_action_clone(on_close.clone())),
|
|
||||||
))
|
|
||||||
.map(move |m| crate::Action::App(action_map.clone()(m)));
|
|
||||||
e
|
|
||||||
})),
|
|
||||||
);
|
|
||||||
shell.publish(on_surface_action(get_popup_action));
|
|
||||||
}
|
|
||||||
event::Status::Captured
|
event::Status::Captured
|
||||||
} else {
|
} else {
|
||||||
event::Status::Ignored
|
event::Status::Ignored
|
||||||
|
|
@ -588,7 +655,6 @@ pub fn update<
|
||||||
Event::Mouse(mouse::Event::WheelScrolled {
|
Event::Mouse(mouse::Event::WheelScrolled {
|
||||||
delta: mouse::ScrollDelta::Lines { .. },
|
delta: mouse::ScrollDelta::Lines { .. },
|
||||||
}) => {
|
}) => {
|
||||||
let state = state();
|
|
||||||
let is_open = state.is_open.load(Ordering::Relaxed);
|
let is_open = state.is_open.load(Ordering::Relaxed);
|
||||||
|
|
||||||
if state.keyboard_modifiers.command() && cursor.is_over(layout.bounds()) && !is_open {
|
if state.keyboard_modifiers.command() && cursor.is_over(layout.bounds()) && !is_open {
|
||||||
|
|
@ -604,8 +670,6 @@ pub fn update<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) => {
|
Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) => {
|
||||||
let state = state();
|
|
||||||
|
|
||||||
state.keyboard_modifiers = *modifiers;
|
state.keyboard_modifiers = *modifiers;
|
||||||
|
|
||||||
event::Status::Ignored
|
event::Status::Ignored
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue