refactor: popups for nav bar / segmented buttons
This commit is contained in:
parent
f5fc392ba4
commit
cde1989e78
13 changed files with 544 additions and 57 deletions
|
|
@ -7,7 +7,7 @@ use cosmic::app::Settings;
|
||||||
use cosmic::iced::{Alignment, Length, Size};
|
use cosmic::iced::{Alignment, Length, Size};
|
||||||
use cosmic::prelude::*;
|
use cosmic::prelude::*;
|
||||||
use cosmic::widget::menu::{self, KeyBind};
|
use cosmic::widget::menu::{self, KeyBind};
|
||||||
use cosmic::widget::nav_bar;
|
use cosmic::widget::{nav_bar, RcElementWrapper};
|
||||||
use cosmic::{executor, iced, widget, Core};
|
use cosmic::{executor, iced, widget, Core};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
|
|
@ -43,7 +43,7 @@ pub enum Action {
|
||||||
impl widget::menu::Action for Action {
|
impl widget::menu::Action for Action {
|
||||||
type Message = Message;
|
type Message = Message;
|
||||||
|
|
||||||
fn message(&self) -> Message {
|
fn message(&self) -> Self::Message {
|
||||||
match self {
|
match self {
|
||||||
Action::Hi => Message::Hi,
|
Action::Hi => Message::Hi,
|
||||||
Action::Hi2 => Message::Hi2,
|
Action::Hi2 => Message::Hi2,
|
||||||
|
|
@ -52,6 +52,25 @@ impl widget::menu::Action for Action {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub enum Action2 {
|
||||||
|
Hi,
|
||||||
|
Hi2,
|
||||||
|
Hi3,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl widget::menu::Action for Action2 {
|
||||||
|
type Message = cosmic::Action<Message>;
|
||||||
|
|
||||||
|
fn message(&self) -> Self::Message {
|
||||||
|
cosmic::Action::App(match self {
|
||||||
|
Action2::Hi => Message::Hi,
|
||||||
|
Action2::Hi2 => Message::Hi2,
|
||||||
|
Action2::Hi3 => Message::Hi3,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Runs application with these settings
|
/// Runs application with these settings
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|
@ -158,6 +177,70 @@ impl cosmic::Application for App {
|
||||||
self.update_title()
|
self.update_title()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn nav_context_menu(&self) -> Option<Vec<menu::Tree<cosmic::Action<Self::Message>>>> {
|
||||||
|
Some(menu::nav_context(
|
||||||
|
&HashMap::new(),
|
||||||
|
vec![
|
||||||
|
vec![cosmic::widget::menu::Item::Button(
|
||||||
|
"Hi",
|
||||||
|
Some(cosmic::widget::icon::from_name("screenshot-window-symbolic").into()),
|
||||||
|
Action2::Hi,
|
||||||
|
)],
|
||||||
|
vec![
|
||||||
|
cosmic::widget::menu::Item::Button(
|
||||||
|
"Hi 2",
|
||||||
|
Some(cosmic::widget::icon::from_name("screenshot-window-symbolic").into()),
|
||||||
|
Action2::Hi,
|
||||||
|
),
|
||||||
|
cosmic::widget::menu::Item::Button(
|
||||||
|
"Hi 22",
|
||||||
|
Some(cosmic::widget::icon::from_name("screenshot-window-symbolic").into()),
|
||||||
|
Action2::Hi,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
vec![
|
||||||
|
cosmic::widget::menu::Item::Button(
|
||||||
|
"Hi 3",
|
||||||
|
Some(cosmic::widget::icon::from_name("screenshot-window-symbolic").into()),
|
||||||
|
Action2::Hi,
|
||||||
|
),
|
||||||
|
cosmic::widget::menu::Item::Button(
|
||||||
|
"Hi 33",
|
||||||
|
Some(cosmic::widget::icon::from_name("screenshot-window-symbolic").into()),
|
||||||
|
Action2::Hi,
|
||||||
|
),
|
||||||
|
cosmic::widget::menu::Item::Button(
|
||||||
|
"Hi 333",
|
||||||
|
Some(cosmic::widget::icon::from_name("screenshot-window-symbolic").into()),
|
||||||
|
Action2::Hi,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
vec![
|
||||||
|
cosmic::widget::menu::Item::Button(
|
||||||
|
"Hi 44",
|
||||||
|
Some(cosmic::widget::icon::from_name("screenshot-window-symbolic").into()),
|
||||||
|
Action2::Hi,
|
||||||
|
),
|
||||||
|
cosmic::widget::menu::Item::Button(
|
||||||
|
"Hi 4444",
|
||||||
|
Some(cosmic::widget::icon::from_name("screenshot-window-symbolic").into()),
|
||||||
|
Action2::Hi,
|
||||||
|
),
|
||||||
|
cosmic::widget::menu::Item::Button(
|
||||||
|
"Hi 444444",
|
||||||
|
Some(cosmic::widget::icon::from_name("screenshot-window-symbolic").into()),
|
||||||
|
Action2::Hi,
|
||||||
|
),
|
||||||
|
cosmic::widget::menu::Item::Button(
|
||||||
|
"Hi 444444444444",
|
||||||
|
Some(cosmic::widget::icon::from_name("screenshot-window-symbolic").into()),
|
||||||
|
Action2::Hi,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
/// Handle application events here.
|
/// Handle application events here.
|
||||||
fn update(&mut self, message: Self::Message) -> cosmic::app::Task<Self::Message> {
|
fn update(&mut self, message: Self::Message) -> cosmic::app::Task<Self::Message> {
|
||||||
match message {
|
match message {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
use cosmic::app::{Core, Settings, Task};
|
use cosmic::app::{Core, Settings, Task};
|
||||||
use cosmic::iced::Size;
|
use cosmic::iced::Size;
|
||||||
use cosmic::widget::{menu, nav_bar};
|
use cosmic::widget::{menu, nav_bar, space};
|
||||||
use cosmic::{executor, iced, ApplicationExt, Element};
|
use cosmic::{executor, iced, ApplicationExt, Element};
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
|
@ -127,18 +127,20 @@ impl cosmic::Application for App {
|
||||||
Some(&self.nav_model)
|
Some(&self.nav_model)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The context menu to display for the given nav bar item ID.
|
/// The context menu list.
|
||||||
fn nav_context_menu(
|
fn nav_context_menu(&self) -> Option<Vec<menu::Tree<cosmic::Action<Self::Message>>>> {
|
||||||
&self,
|
Some(menu::nav_context(
|
||||||
id: nav_bar::Id,
|
|
||||||
) -> Option<Vec<menu::Tree<cosmic::Action<Self::Message>>>> {
|
|
||||||
Some(menu::items(
|
|
||||||
&HashMap::new(),
|
&HashMap::new(),
|
||||||
|
self.nav_model
|
||||||
|
.iter()
|
||||||
|
.map(|e| {
|
||||||
vec![
|
vec![
|
||||||
menu::Item::Button("Move Up", None, NavMenuAction::MoveUp(id)),
|
menu::Item::Button("Move Up", None, NavMenuAction::MoveUp(e)),
|
||||||
menu::Item::Button("Move Down", None, NavMenuAction::MoveDown(id)),
|
menu::Item::Button("Move Down", None, NavMenuAction::MoveDown(e)),
|
||||||
menu::Item::Button("Delete", None, NavMenuAction::Delete(id)),
|
menu::Item::Button("Delete", None, NavMenuAction::Delete(e)),
|
||||||
],
|
]
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -389,7 +389,20 @@ where
|
||||||
let mut nav =
|
let mut nav =
|
||||||
crate::widget::nav_bar(nav_model, |id| crate::Action::Cosmic(Action::NavBar(id)))
|
crate::widget::nav_bar(nav_model, |id| crate::Action::Cosmic(Action::NavBar(id)))
|
||||||
.on_context(|id| crate::Action::Cosmic(Action::NavBarContext(id)))
|
.on_context(|id| crate::Action::Cosmic(Action::NavBarContext(id)))
|
||||||
.context_menu(self.nav_context_menu(self.core().nav_bar_context()))
|
.context_menu(self.nav_context_menu());
|
||||||
|
#[cfg(all(
|
||||||
|
feature = "multi-window",
|
||||||
|
feature = "wayland",
|
||||||
|
target_os = "linux",
|
||||||
|
feature = "winit",
|
||||||
|
feature = "surface-message"
|
||||||
|
))]
|
||||||
|
{
|
||||||
|
nav = nav
|
||||||
|
.window_id_maybe(self.core().main_window_id())
|
||||||
|
.on_surface_action(|m| crate::Action::Cosmic(crate::app::Action::Surface(m)))
|
||||||
|
}
|
||||||
|
let mut nav = nav
|
||||||
.into_container()
|
.into_container()
|
||||||
.width(iced::Length::Shrink)
|
.width(iced::Length::Shrink)
|
||||||
.height(iced::Length::Fill);
|
.height(iced::Length::Fill);
|
||||||
|
|
@ -402,10 +415,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shows a context menu for the active nav bar item.
|
/// Shows a context menu for the active nav bar item.
|
||||||
fn nav_context_menu(
|
fn nav_context_menu(&self) -> Option<Vec<menu::Tree<crate::Action<Self::Message>>>> {
|
||||||
&self,
|
|
||||||
id: nav_bar::Id,
|
|
||||||
) -> Option<Vec<menu::Tree<crate::Action<Self::Message>>>> {
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -635,10 +645,8 @@ impl<App: Application> ApplicationExt for App {
|
||||||
let mut widgets = Vec::with_capacity(3);
|
let mut widgets = Vec::with_capacity(3);
|
||||||
|
|
||||||
// Insert nav bar onto the left side of the window.
|
// Insert nav bar onto the left side of the window.
|
||||||
let has_nav = if let Some(nav) = self
|
let has_nav = if let Some(nav) = self.nav_bar() {
|
||||||
.nav_bar()
|
let nav = id_container(nav, iced_core::id::Id::new("COSMIC_nav_bar"));
|
||||||
.map(|nav| id_container(nav, iced_core::id::Id::new("COSMIC_nav_bar")))
|
|
||||||
{
|
|
||||||
widgets.push(
|
widgets.push(
|
||||||
container(nav)
|
container(nav)
|
||||||
.padding([
|
.padding([
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@ mod menu_inner;
|
||||||
mod menu_tree;
|
mod menu_tree;
|
||||||
pub use menu_tree::{
|
pub use menu_tree::{
|
||||||
MenuItem as Item, MenuTree as Tree, menu_button, menu_items as items, menu_root as root,
|
MenuItem as Item, MenuTree as Tree, menu_button, menu_items as items, menu_root as root,
|
||||||
|
nav_context,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use crate::style::menu_bar::{Appearance, StyleSheet};
|
pub use crate::style::menu_bar::{Appearance, StyleSheet};
|
||||||
|
|
|
||||||
|
|
@ -230,7 +230,7 @@ where
|
||||||
path_highlight: Some(PathHighlight::MenuActive),
|
path_highlight: Some(PathHighlight::MenuActive),
|
||||||
menu_roots,
|
menu_roots,
|
||||||
style: <crate::Theme as StyleSheet>::Style::default(),
|
style: <crate::Theme as StyleSheet>::Style::default(),
|
||||||
window_id: window::Id::NONE,
|
window_id: window::Id::RESERVED,
|
||||||
#[cfg(all(
|
#[cfg(all(
|
||||||
feature = "multi-window",
|
feature = "multi-window",
|
||||||
feature = "wayland",
|
feature = "wayland",
|
||||||
|
|
|
||||||
|
|
@ -525,6 +525,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
|
||||||
);
|
);
|
||||||
let node_size = children_node.size();
|
let node_size = children_node.size();
|
||||||
intrinsic_size.height += node_size.height;
|
intrinsic_size.height += node_size.height;
|
||||||
|
|
||||||
intrinsic_size.width = intrinsic_size.width.max(node_size.width);
|
intrinsic_size.width = intrinsic_size.width.max(node_size.width);
|
||||||
|
|
||||||
nodes.push(children_node);
|
nodes.push(children_node);
|
||||||
|
|
|
||||||
|
|
@ -401,3 +401,19 @@ pub fn menu_items<
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a menu tree from a widget and a vector of sub trees
|
||||||
|
pub fn nav_context<
|
||||||
|
A: MenuAction<Message = Message>,
|
||||||
|
L: Into<Cow<'static, str>> + From<&'static str> + 'static,
|
||||||
|
Message: 'static + std::clone::Clone,
|
||||||
|
>(
|
||||||
|
key_binds: &HashMap<KeyBind, A>,
|
||||||
|
children: Vec<Vec<MenuItem<A, L>>>,
|
||||||
|
) -> Vec<MenuTree<Message>> {
|
||||||
|
let menus = children
|
||||||
|
.into_iter()
|
||||||
|
.map(|m| MenuItem::<A, L>::Folder(L::from(""), m));
|
||||||
|
let root = vec![MenuItem::<A, L>::Folder(L::from(""), menus.collect())];
|
||||||
|
menu_items(key_binds, root)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
use apply::Apply;
|
use apply::Apply;
|
||||||
use iced::clipboard::dnd::DndAction;
|
use iced::clipboard::dnd::DndAction;
|
||||||
use iced::clipboard::mime::AllowedMimeTypes;
|
use iced::clipboard::mime::AllowedMimeTypes;
|
||||||
use iced::{Background, Length};
|
use iced::{Background, Length, window};
|
||||||
use iced_core::{Border, Color, Shadow};
|
use iced_core::{Border, Color, Shadow};
|
||||||
|
|
||||||
use crate::widget::{Container, Icon, container, menu, scrollable, segmented_button};
|
use crate::widget::{Container, Icon, container, menu, scrollable, segmented_button};
|
||||||
|
|
@ -55,7 +55,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub struct NavBar<'a, Message> {
|
pub struct NavBar<'a, Message: Clone + 'static> {
|
||||||
segmented_button:
|
segmented_button:
|
||||||
segmented_button::VerticalSegmentedButton<'a, segmented_button::SingleSelect, Message>,
|
segmented_button::VerticalSegmentedButton<'a, segmented_button::SingleSelect, Message>,
|
||||||
}
|
}
|
||||||
|
|
@ -133,6 +133,42 @@ impl<'a, Message: Clone + 'static> NavBar<'a, Message> {
|
||||||
self.segmented_button = self.segmented_button.on_dnd_leave(handler);
|
self.segmented_button = self.segmented_button.on_dnd_leave(handler);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(
|
||||||
|
feature = "multi-window",
|
||||||
|
feature = "wayland",
|
||||||
|
feature = "winit",
|
||||||
|
target_os = "linux"
|
||||||
|
))]
|
||||||
|
pub fn with_positioner(
|
||||||
|
mut self,
|
||||||
|
positioner: iced_runtime::platform_specific::wayland::popup::SctkPositioner,
|
||||||
|
) -> Self {
|
||||||
|
self.segmented_button = self.segmented_button.with_positioner(positioner);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn window_id(mut self, id: window::Id) -> Self {
|
||||||
|
self.segmented_button = self.segmented_button.window_id(id);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn window_id_maybe(mut self, id: Option<window::Id>) -> Self {
|
||||||
|
self.segmented_button = self.segmented_button.window_id_maybe(id);
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn on_surface_action(
|
||||||
|
mut self,
|
||||||
|
handler: impl Fn(crate::surface::Action) -> Message + Send + Sync + 'static,
|
||||||
|
) -> Self {
|
||||||
|
self.segmented_button = self.segmented_button.on_surface_action(handler);
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Message: Clone + 'static> From<NavBar<'a, Message>>
|
impl<'a, Message: Clone + 'static> From<NavBar<'a, Message>>
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ pub struct Horizontal;
|
||||||
/// Horizontal implementation of the [`SegmentedButton`].
|
/// Horizontal implementation of the [`SegmentedButton`].
|
||||||
///
|
///
|
||||||
/// For details on the model, see the [`segmented_button`](super) module for more details.
|
/// For details on the model, see the [`segmented_button`](super) module for more details.
|
||||||
pub fn horizontal<SelectionMode: Default, Message>(
|
pub fn horizontal<SelectionMode: Default, Message: Clone + 'static>(
|
||||||
model: &Model<SelectionMode>,
|
model: &Model<SelectionMode>,
|
||||||
) -> SegmentedButton<'_, Horizontal, SelectionMode, Message>
|
) -> SegmentedButton<'_, Horizontal, SelectionMode, Message>
|
||||||
where
|
where
|
||||||
|
|
@ -30,7 +30,7 @@ where
|
||||||
SegmentedButton::new(model)
|
SegmentedButton::new(model)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<SelectionMode, Message> SegmentedVariant
|
impl<SelectionMode, Message: Clone + 'static> SegmentedVariant
|
||||||
for SegmentedButton<'_, Horizontal, SelectionMode, Message>
|
for SegmentedButton<'_, Horizontal, SelectionMode, Message>
|
||||||
where
|
where
|
||||||
Model<SelectionMode>: Selectable,
|
Model<SelectionMode>: Selectable,
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ pub type VerticalSegmentedButton<'a, SelectionMode, Message> =
|
||||||
/// Vertical implementation of the [`SegmentedButton`].
|
/// Vertical implementation of the [`SegmentedButton`].
|
||||||
///
|
///
|
||||||
/// For details on the model, see the [`segmented_button`](super) module for more details.
|
/// For details on the model, see the [`segmented_button`](super) module for more details.
|
||||||
pub fn vertical<SelectionMode, Message>(
|
pub fn vertical<SelectionMode, Message: Clone + 'static>(
|
||||||
model: &Model<SelectionMode>,
|
model: &Model<SelectionMode>,
|
||||||
) -> SegmentedButton<'_, Vertical, SelectionMode, Message>
|
) -> SegmentedButton<'_, Vertical, SelectionMode, Message>
|
||||||
where
|
where
|
||||||
|
|
@ -30,7 +30,7 @@ where
|
||||||
SegmentedButton::new(model)
|
SegmentedButton::new(model)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<SelectionMode, Message> SegmentedVariant
|
impl<SelectionMode, Message: Clone + 'static> SegmentedVariant
|
||||||
for SegmentedButton<'_, Vertical, SelectionMode, Message>
|
for SegmentedButton<'_, Vertical, SelectionMode, Message>
|
||||||
where
|
where
|
||||||
Model<SelectionMode>: Selectable,
|
Model<SelectionMode>: Selectable,
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ use crate::widget::menu::{
|
||||||
self, CloseCondition, ItemHeight, ItemWidth, MenuBarState, PathHighlight, menu_roots_children,
|
self, CloseCondition, ItemHeight, ItemWidth, MenuBarState, PathHighlight, menu_roots_children,
|
||||||
menu_roots_diff,
|
menu_roots_diff,
|
||||||
};
|
};
|
||||||
use crate::widget::{Icon, icon};
|
use crate::widget::{Icon, context_menu, icon};
|
||||||
use crate::{Element, Renderer};
|
use crate::{Element, Renderer};
|
||||||
use derive_setters::Setters;
|
use derive_setters::Setters;
|
||||||
use iced::clipboard::dnd::{
|
use iced::clipboard::dnd::{
|
||||||
|
|
@ -38,6 +38,7 @@ use std::collections::HashSet;
|
||||||
use std::collections::hash_map::DefaultHasher;
|
use std::collections::hash_map::DefaultHasher;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
use std::sync::Arc;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
thread_local! {
|
thread_local! {
|
||||||
|
|
@ -107,7 +108,7 @@ pub trait SegmentedVariant {
|
||||||
/// A conjoined group of items that function together as a button.
|
/// A conjoined group of items that function together as a button.
|
||||||
#[derive(Setters)]
|
#[derive(Setters)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub struct SegmentedButton<'a, Variant, SelectionMode, Message>
|
pub struct SegmentedButton<'a, Variant, SelectionMode, Message: Clone + 'static>
|
||||||
where
|
where
|
||||||
Model<SelectionMode>: Selectable,
|
Model<SelectionMode>: Selectable,
|
||||||
SelectionMode: Default,
|
SelectionMode: Default,
|
||||||
|
|
@ -189,14 +190,28 @@ where
|
||||||
pub(super) tab_drag: Option<TabDragSource<Message>>,
|
pub(super) tab_drag: Option<TabDragSource<Message>>,
|
||||||
#[setters(skip)]
|
#[setters(skip)]
|
||||||
pub(super) on_drop_hint: Option<Box<dyn Fn(Option<(Entity, bool)>) -> Message + 'static>>,
|
pub(super) on_drop_hint: Option<Box<dyn Fn(Option<(Entity, bool)>) -> Message + 'static>>,
|
||||||
|
|
||||||
#[setters(skip)]
|
#[setters(skip)]
|
||||||
pub(super) on_reorder: Option<Box<dyn Fn(ReorderEvent) -> Message + 'static>>,
|
pub(super) on_reorder: Option<Box<dyn Fn(ReorderEvent) -> Message + 'static>>,
|
||||||
#[setters(skip)]
|
#[setters(skip)]
|
||||||
|
window_id: window::Id,
|
||||||
|
#[cfg(all(
|
||||||
|
feature = "multi-window",
|
||||||
|
feature = "wayland",
|
||||||
|
feature = "winit",
|
||||||
|
target_os = "linux"
|
||||||
|
))]
|
||||||
|
positioner: iced_runtime::platform_specific::wayland::popup::SctkPositioner,
|
||||||
|
#[setters(skip)]
|
||||||
|
pub(crate) on_surface_action:
|
||||||
|
Option<Arc<dyn Fn(crate::surface::Action) -> Message + Send + Sync + 'static>>,
|
||||||
|
|
||||||
/// Defines the implementation of this struct
|
/// Defines the implementation of this struct
|
||||||
variant: PhantomData<Variant>,
|
variant: PhantomData<Variant>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Variant, SelectionMode, Message> SegmentedButton<'a, Variant, SelectionMode, Message>
|
impl<'a, Variant, SelectionMode, Message: Clone + 'static>
|
||||||
|
SegmentedButton<'a, Variant, SelectionMode, Message>
|
||||||
where
|
where
|
||||||
Self: SegmentedVariant,
|
Self: SegmentedVariant,
|
||||||
Model<SelectionMode>: Selectable,
|
Model<SelectionMode>: Selectable,
|
||||||
|
|
@ -243,6 +258,15 @@ where
|
||||||
tab_drag: None,
|
tab_drag: None,
|
||||||
on_drop_hint: None,
|
on_drop_hint: None,
|
||||||
on_reorder: None,
|
on_reorder: None,
|
||||||
|
window_id: window::Id::RESERVED,
|
||||||
|
#[cfg(all(
|
||||||
|
feature = "multi-window",
|
||||||
|
feature = "wayland",
|
||||||
|
feature = "winit",
|
||||||
|
target_os = "linux"
|
||||||
|
))]
|
||||||
|
positioner: iced_runtime::platform_specific::wayland::popup::SctkPositioner::default(),
|
||||||
|
on_surface_action: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -306,12 +330,7 @@ where
|
||||||
where
|
where
|
||||||
Message: Clone + 'static,
|
Message: Clone + 'static,
|
||||||
{
|
{
|
||||||
self.context_menu = context_menu.map(|menus| {
|
self.context_menu = context_menu;
|
||||||
vec![menu::Tree::with_children(
|
|
||||||
crate::Element::from(crate::widget::Row::new()),
|
|
||||||
menus,
|
|
||||||
)]
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(ref mut context_menu) = self.context_menu {
|
if let Some(ref mut context_menu) = self.context_menu {
|
||||||
context_menu.iter_mut().for_each(menu::Tree::set_index);
|
context_menu.iter_mut().for_each(menu::Tree::set_index);
|
||||||
|
|
@ -863,6 +882,253 @@ where
|
||||||
|id| id.0,
|
|id| id.0,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(
|
||||||
|
feature = "multi-window",
|
||||||
|
feature = "wayland",
|
||||||
|
feature = "winit",
|
||||||
|
target_os = "linux"
|
||||||
|
))]
|
||||||
|
pub fn with_positioner(
|
||||||
|
mut self,
|
||||||
|
positioner: iced_runtime::platform_specific::wayland::popup::SctkPositioner,
|
||||||
|
) -> Self {
|
||||||
|
self.positioner = positioner;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn window_id(mut self, id: window::Id) -> Self {
|
||||||
|
self.window_id = id;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn window_id_maybe(mut self, id: Option<window::Id>) -> Self {
|
||||||
|
if let Some(id) = id {
|
||||||
|
self.window_id = id;
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn on_surface_action(
|
||||||
|
mut self,
|
||||||
|
handler: impl Fn(crate::surface::Action) -> Message + Send + Sync + 'static,
|
||||||
|
) -> Self {
|
||||||
|
self.on_surface_action = Some(Arc::new(handler));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(
|
||||||
|
feature = "multi-window",
|
||||||
|
feature = "wayland",
|
||||||
|
target_os = "linux",
|
||||||
|
feature = "winit",
|
||||||
|
feature = "surface-message"
|
||||||
|
))]
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
|
fn create_popup<'b>(
|
||||||
|
&mut self,
|
||||||
|
layout: Layout<'b>,
|
||||||
|
view_cursor: mouse::Cursor,
|
||||||
|
renderer: &'b Renderer,
|
||||||
|
shell: &'b mut Shell<'_, Message>,
|
||||||
|
viewport: &'b Rectangle,
|
||||||
|
tree: &'b mut Tree,
|
||||||
|
) {
|
||||||
|
let state = tree.state.downcast_mut::<LocalState>();
|
||||||
|
let my_state = state.menu_state.clone();
|
||||||
|
|
||||||
|
if self.window_id != window::Id::NONE {
|
||||||
|
use crate::surface::action::{LiveSettings, destroy_popup};
|
||||||
|
use iced_runtime::platform_specific::wayland::popup::{
|
||||||
|
SctkPopupSettings, SctkPositioner,
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(surface_action) = self.on_surface_action.as_ref() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let id = my_state.inner.with_data_mut(|state| {
|
||||||
|
if let Some(id) = state.popup_id.get(&self.window_id).copied() {
|
||||||
|
// close existing popups
|
||||||
|
state.menu_states.clear();
|
||||||
|
state.active_root.clear();
|
||||||
|
shell.publish(surface_action(destroy_popup(id)));
|
||||||
|
state.view_cursor = view_cursor;
|
||||||
|
id
|
||||||
|
} else {
|
||||||
|
window::Id::unique()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let Some(entity) = state.show_context else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some((mut bounds, i)) = self
|
||||||
|
.variant_bounds(state, layout.bounds())
|
||||||
|
.enumerate()
|
||||||
|
.find_map(|(i, item)| match item {
|
||||||
|
ItemBounds::Button(e, bounds) if e == entity => Some((bounds, i)),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
self.context_menu
|
||||||
|
.as_ref()
|
||||||
|
.is_none_or(|m| m[0].children.len() == self.model.len()),
|
||||||
|
"model length must match the number of context menus"
|
||||||
|
);
|
||||||
|
let menu = self
|
||||||
|
.context_menu
|
||||||
|
.as_mut()
|
||||||
|
.map(|m| m[0].children[i].clone())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
bounds.x = state.context_cursor.x;
|
||||||
|
bounds.y = state.context_cursor.y;
|
||||||
|
|
||||||
|
let mut popup_menu: menu::Menu<'static, _> = menu::Menu {
|
||||||
|
tree: my_state.clone(),
|
||||||
|
menu_roots: std::borrow::Cow::Owned(vec![menu]),
|
||||||
|
bounds_expand: 0,
|
||||||
|
menu_overlays_parent: false,
|
||||||
|
close_condition: CloseCondition {
|
||||||
|
leave: false,
|
||||||
|
click_outside: true,
|
||||||
|
click_inside: true,
|
||||||
|
},
|
||||||
|
item_width: ItemWidth::Uniform(240),
|
||||||
|
item_height: ItemHeight::Dynamic(40),
|
||||||
|
bar_bounds: bounds,
|
||||||
|
main_offset: 0,
|
||||||
|
cross_offset: 0,
|
||||||
|
root_bounds_list: vec![bounds],
|
||||||
|
path_highlight: Some(PathHighlight::MenuActive),
|
||||||
|
style: std::borrow::Cow::Borrowed(&crate::theme::menu_bar::MenuBarStyle::Default),
|
||||||
|
position: Point::new(0., 0.),
|
||||||
|
is_overlay: false,
|
||||||
|
window_id: id,
|
||||||
|
depth: 0,
|
||||||
|
on_surface_action: self.on_surface_action.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
menu::init_root_menu(
|
||||||
|
&mut popup_menu,
|
||||||
|
renderer,
|
||||||
|
shell,
|
||||||
|
view_cursor.position().unwrap(),
|
||||||
|
viewport.size(),
|
||||||
|
Vector::new(0., 0.),
|
||||||
|
bounds,
|
||||||
|
0., // TODO offset?
|
||||||
|
);
|
||||||
|
let (anchor_rect, gravity) = my_state.inner.with_data_mut(|state| {
|
||||||
|
state.popup_id.insert(self.window_id, id);
|
||||||
|
(state
|
||||||
|
.menu_states
|
||||||
|
.iter()
|
||||||
|
.find(|s| s.index.is_none())
|
||||||
|
.map(|s| s.menu_bounds.parent_bounds)
|
||||||
|
.map_or_else(
|
||||||
|
|| {
|
||||||
|
let bounds = layout.bounds();
|
||||||
|
Rectangle {
|
||||||
|
x: bounds.x as i32,
|
||||||
|
y: bounds.y as i32,
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|r| Rectangle {
|
||||||
|
x: r.x as i32,
|
||||||
|
y: r.y as i32,
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
},
|
||||||
|
), match (state.horizontal_direction, state.vertical_direction) {
|
||||||
|
(menu::Direction::Positive, menu::Direction::Positive) => cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::BottomRight,
|
||||||
|
(menu::Direction::Positive, menu::Direction::Negative) => cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::TopRight,
|
||||||
|
(menu::Direction::Negative, menu::Direction::Positive) => cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::BottomLeft,
|
||||||
|
(menu::Direction::Negative, menu::Direction::Negative) => cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::TopLeft,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
let menu_node =
|
||||||
|
popup_menu.layout(renderer, layout::Limits::NONE.min_width(1.).min_height(1.));
|
||||||
|
let popup_size = menu_node.size();
|
||||||
|
let positioner = SctkPositioner {
|
||||||
|
size: Some((
|
||||||
|
popup_size.width.ceil() as u32 + 2,
|
||||||
|
popup_size.height.ceil() as u32 + 2,
|
||||||
|
)),
|
||||||
|
anchor_rect,
|
||||||
|
anchor:
|
||||||
|
cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Anchor::BottomLeft,
|
||||||
|
gravity,
|
||||||
|
reactive: true,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let parent = self.window_id;
|
||||||
|
/// Used to create a popup message from within a widget.
|
||||||
|
#[cfg(all(feature = "wayland", target_os = "linux", feature = "winit"))]
|
||||||
|
#[must_use]
|
||||||
|
pub fn simple_popup<Message: 'static>(
|
||||||
|
live_settings: impl Fn() -> LiveSettings + Send + Sync + 'static,
|
||||||
|
settings: impl Fn()
|
||||||
|
-> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings
|
||||||
|
+ Send
|
||||||
|
+ Sync
|
||||||
|
+ 'static,
|
||||||
|
view: Option<impl Fn() -> crate::Element<'static, Message> + Send + Sync + 'static>,
|
||||||
|
) -> crate::surface::Action {
|
||||||
|
use std::any::Any;
|
||||||
|
|
||||||
|
let boxed: Box<
|
||||||
|
dyn Fn() -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings
|
||||||
|
+ Send
|
||||||
|
+ Sync
|
||||||
|
+ 'static,
|
||||||
|
> = Box::new(settings);
|
||||||
|
let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed);
|
||||||
|
|
||||||
|
let boxed_live: Box<dyn Fn() -> LiveSettings + Send + Sync + 'static> =
|
||||||
|
Box::new(live_settings);
|
||||||
|
let boxed_live: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed_live);
|
||||||
|
|
||||||
|
crate::surface::Action::Popup(
|
||||||
|
Arc::new(boxed),
|
||||||
|
Arc::new(boxed_live),
|
||||||
|
view.map(|view| {
|
||||||
|
let boxed: Box<
|
||||||
|
dyn Fn() -> crate::Element<'static, Message> + Send + Sync + 'static,
|
||||||
|
> = Box::new(view);
|
||||||
|
let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed);
|
||||||
|
Arc::new(boxed)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
shell.publish((surface_action)(simple_popup(
|
||||||
|
LiveSettings::default,
|
||||||
|
move || SctkPopupSettings {
|
||||||
|
parent,
|
||||||
|
id,
|
||||||
|
positioner: positioner.clone(),
|
||||||
|
parent_size: None,
|
||||||
|
grab: true,
|
||||||
|
close_with_children: false,
|
||||||
|
input_zone: None,
|
||||||
|
},
|
||||||
|
Some(move || {
|
||||||
|
Element::from(crate::widget::container(popup_menu.clone()).center(Length::Fill))
|
||||||
|
}),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Variant, SelectionMode, Message> Widget<Message, crate::Theme, Renderer>
|
impl<Variant, SelectionMode, Message> Widget<Message, crate::Theme, Renderer>
|
||||||
|
|
@ -974,10 +1240,10 @@ where
|
||||||
mut event: &Event,
|
mut event: &Event,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
cursor_position: mouse::Cursor,
|
cursor_position: mouse::Cursor,
|
||||||
_renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
clipboard: &mut dyn Clipboard,
|
clipboard: &mut dyn Clipboard,
|
||||||
shell: &mut Shell<'_, Message>,
|
shell: &mut Shell<'_, Message>,
|
||||||
_viewport: &iced::Rectangle,
|
viewport: &iced::Rectangle,
|
||||||
) {
|
) {
|
||||||
let my_bounds = layout.bounds();
|
let my_bounds = layout.bounds();
|
||||||
let state = tree.state.downcast_mut::<LocalState>();
|
let state = tree.state.downcast_mut::<LocalState>();
|
||||||
|
|
@ -1396,6 +1662,7 @@ where
|
||||||
&& (right_button_released(&event)
|
&& (right_button_released(&event)
|
||||||
|| (touch_lifted(&event) && fingers_pressed == 2))
|
|| (touch_lifted(&event) && fingers_pressed == 2))
|
||||||
{
|
{
|
||||||
|
// TODO create popup
|
||||||
state.show_context = Some(key);
|
state.show_context = Some(key);
|
||||||
state.context_cursor = cursor_position.position().unwrap_or_default();
|
state.context_cursor = cursor_position.position().unwrap_or_default();
|
||||||
|
|
||||||
|
|
@ -1408,9 +1675,29 @@ where
|
||||||
|
|
||||||
shell.publish(on_context(key));
|
shell.publish(on_context(key));
|
||||||
shell.capture_event();
|
shell.capture_event();
|
||||||
|
|
||||||
|
#[cfg(all(
|
||||||
|
feature = "multi-window",
|
||||||
|
feature = "wayland",
|
||||||
|
target_os = "linux",
|
||||||
|
feature = "winit",
|
||||||
|
feature = "surface-message"
|
||||||
|
))]
|
||||||
|
if matches!(
|
||||||
|
crate::app::cosmic::WINDOWING_SYSTEM.get(),
|
||||||
|
Some(crate::app::cosmic::WindowingSystem::Wayland)
|
||||||
|
) {
|
||||||
|
self.create_popup(
|
||||||
|
layout,
|
||||||
|
cursor_position,
|
||||||
|
renderer,
|
||||||
|
shell,
|
||||||
|
viewport,
|
||||||
|
tree,
|
||||||
|
);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Middle)) =
|
if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Middle)) =
|
||||||
event
|
event
|
||||||
{
|
{
|
||||||
|
|
@ -1528,6 +1815,34 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (matches!(event, Event::Mouse(mouse::Event::ButtonReleased(_))) || (touch_lifted(event)))
|
||||||
|
&& let Some(_id) = state
|
||||||
|
.menu_state
|
||||||
|
.inner
|
||||||
|
.with_data_mut(|ms| ms.popup_id.remove(&self.window_id))
|
||||||
|
{
|
||||||
|
#[cfg(all(
|
||||||
|
feature = "wayland",
|
||||||
|
target_os = "linux",
|
||||||
|
feature = "winit",
|
||||||
|
feature = "surface-message"
|
||||||
|
))]
|
||||||
|
{
|
||||||
|
let surface_action = self.on_surface_action.as_ref().unwrap();
|
||||||
|
shell.capture_event();
|
||||||
|
|
||||||
|
shell.publish(surface_action(crate::surface::action::destroy_popup(_id)));
|
||||||
|
}
|
||||||
|
state.show_context = None;
|
||||||
|
|
||||||
|
state.menu_state.inner.with_data_mut(|data| {
|
||||||
|
// Clear stale MenuBounds from any previous context menu before opening a new one.
|
||||||
|
data.reset();
|
||||||
|
data.open = false;
|
||||||
|
data.view_cursor = cursor_position;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if matches!(
|
if matches!(
|
||||||
event,
|
event,
|
||||||
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
|
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
|
||||||
|
|
@ -2146,19 +2461,44 @@ where
|
||||||
_viewport: &iced_core::Rectangle,
|
_viewport: &iced_core::Rectangle,
|
||||||
translation: Vector,
|
translation: Vector,
|
||||||
) -> Option<iced_core::overlay::Element<'b, Message, crate::Theme, Renderer>> {
|
) -> Option<iced_core::overlay::Element<'b, Message, crate::Theme, Renderer>> {
|
||||||
|
#[cfg(all(
|
||||||
|
feature = "multi-window",
|
||||||
|
feature = "wayland",
|
||||||
|
target_os = "linux",
|
||||||
|
feature = "winit",
|
||||||
|
feature = "surface-message"
|
||||||
|
))]
|
||||||
|
if matches!(
|
||||||
|
crate::app::cosmic::WINDOWING_SYSTEM.get(),
|
||||||
|
Some(crate::app::cosmic::WindowingSystem::Wayland)
|
||||||
|
) && self.on_surface_action.is_some()
|
||||||
|
&& self.window_id != window::Id::NONE
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
let state = tree.state.downcast_mut::<LocalState>();
|
let state = tree.state.downcast_mut::<LocalState>();
|
||||||
let menu_state = state.menu_state.clone();
|
let menu_state = state.menu_state.clone();
|
||||||
|
|
||||||
let entity = state.show_context?;
|
let entity = state.show_context?;
|
||||||
|
|
||||||
let mut bounds =
|
let (mut bounds, i) = self
|
||||||
self.variant_bounds(state, layout.bounds())
|
.variant_bounds(state, layout.bounds())
|
||||||
.find_map(|item| match item {
|
.enumerate()
|
||||||
ItemBounds::Button(e, bounds) if e == entity => Some(bounds),
|
.find_map(|(i, item)| match item {
|
||||||
|
ItemBounds::Button(e, bounds) if e == entity => Some((bounds, i)),
|
||||||
_ => None,
|
_ => None,
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let context_menu = self.context_menu.as_mut()?;
|
assert!(
|
||||||
|
self.context_menu
|
||||||
|
.as_ref()
|
||||||
|
.is_none_or(|m| m[0].children.len() == self.model.len())
|
||||||
|
);
|
||||||
|
let menu = self
|
||||||
|
.context_menu
|
||||||
|
.as_mut()
|
||||||
|
.map(|m| m[0].children[i].clone())?;
|
||||||
|
|
||||||
if !menu_state.inner.with_data(|data| data.open) {
|
if !menu_state.inner.with_data(|data| data.open) {
|
||||||
// If the menu is not open, we don't need to show it.
|
// If the menu is not open, we don't need to show it.
|
||||||
|
|
@ -2176,7 +2516,7 @@ where
|
||||||
Some(
|
Some(
|
||||||
crate::widget::menu::Menu {
|
crate::widget::menu::Menu {
|
||||||
tree: menu_state,
|
tree: menu_state,
|
||||||
menu_roots: std::borrow::Cow::Owned(context_menu.clone()),
|
menu_roots: std::borrow::Cow::Owned(vec![menu]),
|
||||||
bounds_expand: 16,
|
bounds_expand: 16,
|
||||||
menu_overlays_parent: true,
|
menu_overlays_parent: true,
|
||||||
close_condition: CloseCondition {
|
close_condition: CloseCondition {
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ use super::segmented_button::{
|
||||||
/// The data for the widget comes from a model that is maintained the application.
|
/// The data for the widget comes from a model that is maintained the application.
|
||||||
///
|
///
|
||||||
/// For details on the model, see the [`segmented_button`] module for more details.
|
/// For details on the model, see the [`segmented_button`] module for more details.
|
||||||
pub fn horizontal<SelectionMode: Default, Message>(
|
pub fn horizontal<SelectionMode: Default, Message: Clone + 'static>(
|
||||||
model: &Model<SelectionMode>,
|
model: &Model<SelectionMode>,
|
||||||
) -> HorizontalSegmentedButton<'_, SelectionMode, Message>
|
) -> HorizontalSegmentedButton<'_, SelectionMode, Message>
|
||||||
where
|
where
|
||||||
|
|
@ -37,7 +37,7 @@ where
|
||||||
/// The data for the widget comes from a model that is maintained the application.
|
/// The data for the widget comes from a model that is maintained the application.
|
||||||
///
|
///
|
||||||
/// For details on the model, see the [`segmented_button`] module for more details.
|
/// For details on the model, see the [`segmented_button`] module for more details.
|
||||||
pub fn vertical<SelectionMode, Message>(
|
pub fn vertical<SelectionMode, Message: Clone + 'static>(
|
||||||
model: &Model<SelectionMode>,
|
model: &Model<SelectionMode>,
|
||||||
) -> VerticalSegmentedButton<'_, SelectionMode, Message>
|
) -> VerticalSegmentedButton<'_, SelectionMode, Message>
|
||||||
where
|
where
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ use super::segmented_button::{
|
||||||
/// The data for the widget comes from a model supplied by the application.
|
/// The data for the widget comes from a model supplied by the application.
|
||||||
///
|
///
|
||||||
/// For details on the model, see the [`segmented_button`] module for more details.
|
/// For details on the model, see the [`segmented_button`] module for more details.
|
||||||
pub fn horizontal<SelectionMode: Default, Message>(
|
pub fn horizontal<SelectionMode: Default, Message: Clone + 'static>(
|
||||||
model: &Model<SelectionMode>,
|
model: &Model<SelectionMode>,
|
||||||
) -> HorizontalSegmentedButton<'_, SelectionMode, Message>
|
) -> HorizontalSegmentedButton<'_, SelectionMode, Message>
|
||||||
where
|
where
|
||||||
|
|
@ -35,7 +35,7 @@ where
|
||||||
///
|
///
|
||||||
/// The data for the widget comes from a model that is maintained the application.
|
/// The data for the widget comes from a model that is maintained the application.
|
||||||
/// For details on the model, see the [`segmented_button`] module for more details.
|
/// For details on the model, see the [`segmented_button`] module for more details.
|
||||||
pub fn vertical<SelectionMode, Message>(
|
pub fn vertical<SelectionMode, Message: Clone + 'static>(
|
||||||
model: &Model<SelectionMode>,
|
model: &Model<SelectionMode>,
|
||||||
) -> VerticalSegmentedButton<'_, SelectionMode, Message>
|
) -> VerticalSegmentedButton<'_, SelectionMode, Message>
|
||||||
where
|
where
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue