wip rebase updates

This commit is contained in:
Ashley Wulber 2026-02-10 15:37:41 -05:00
parent 86dcf8af6c
commit e10459fb37
68 changed files with 1776 additions and 1544 deletions

View file

@ -8,13 +8,27 @@ rust-version = "1.90"
name = "cosmic" name = "cosmic"
[features] [features]
default = ["dbus-config", "multi-window", "a11y"] # default = ["dbus-config", "multi-window", "a11y"]
default = [ "debug",
"winit",
"tokio",
# "xdg-portal",
"a11y",
"wgpu",
"single-instance",
"surface-message",
"dbus-config",
"x11",
"wayland",
"multi-window",
"about","animated-image","autosize", "dbus-config", "pipewire", "process", "rfd", "desktop", "desktop-systemd-scope", "serde-keycode", "qr_code", "markdown", "highlighter"
]
# Accessibility support # Accessibility support
a11y = ["iced/a11y", "iced_accessibility"] a11y = ["iced/a11y", "iced_accessibility"]
# Enable about widget # Enable about widget
about = [] about = []
# Builds support for animated images # Builds support for animated images
animated-image = ["dep:async-fs", "image/gif", "tokio?/io-util", "tokio?/fs"] animated-image = ["dep:async-fs", "image/gif", "image/webp", "image/png", "tokio?/io-util", "tokio?/fs"]
# XXX autosize should not be used on winit windows unless dialogs # XXX autosize should not be used on winit windows unless dialogs
autosize = [] autosize = []
applet = [ applet = [
@ -76,7 +90,7 @@ wayland = [
] ]
surface-message = [] surface-message = []
# multi-window support # multi-window support
multi-window = ["iced/multi-window"] multi-window = []
# Render with wgpu # Render with wgpu
wgpu = ["iced/wgpu", "iced_wgpu"] wgpu = ["iced/wgpu", "iced_wgpu"]
# X11 window support via winit # X11 window support via winit
@ -96,6 +110,7 @@ async-std = [
"zbus?/async-io", "zbus?/async-io",
"iced/async-std", "iced/async-std",
] ]
x11 = ["iced/x11", "iced_winit/x11"]
[dependencies] [dependencies]
apply = "0.3.0" apply = "0.3.0"

View file

@ -1,11 +1,11 @@
use std::ops::Deref; use std::{any::TypeId, ops::Deref};
use crate::{CosmicConfigEntry, Update}; use crate::{CosmicConfigEntry, Update};
use cosmic_settings_daemon::{Changed, ConfigProxy, CosmicSettingsDaemonProxy}; use cosmic_settings_daemon::{Changed, ConfigProxy, CosmicSettingsDaemonProxy};
use futures_util::SinkExt; use futures_util::SinkExt;
use iced_futures::{ use iced_futures::{
Subscription, Subscription,
futures::{self, Stream, StreamExt, future::pending}, futures::{self, StreamExt, future::pending},
stream, stream,
}; };
@ -57,6 +57,20 @@ impl Watcher {
} }
} }
#[derive(Clone)]
struct Wrapper(
TypeId,
CosmicSettingsDaemonProxy<'static>,
&'static str,
bool,
);
impl std::hash::Hash for Wrapper {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
pub fn watcher_subscription<T: CosmicConfigEntry + Send + Sync + Default + 'static + Clone>( pub fn watcher_subscription<T: CosmicConfigEntry + Send + Sync + Default + 'static + Clone>(
settings_daemon: CosmicSettingsDaemonProxy<'static>, settings_daemon: CosmicSettingsDaemonProxy<'static>,
@ -64,166 +78,185 @@ pub fn watcher_subscription<T: CosmicConfigEntry + Send + Sync + Default + 'stat
is_state: bool, is_state: bool,
) -> iced_futures::Subscription<Update<T>> { ) -> iced_futures::Subscription<Update<T>> {
let id = std::any::TypeId::of::<T>(); let id = std::any::TypeId::of::<T>();
Subscription::run_with_id( Subscription::run_with(
(id, config_id), Wrapper(id, settings_daemon, config_id, is_state),
watcher_stream(settings_daemon, config_id, is_state), |&Wrapper(_, ref settings_daemon, ref config_id, ref is_state)| {
) let is_state = *is_state;
} let config_id = *config_id;
let settings_daemon = settings_daemon.clone();
enum Change {
Changes(Changed),
OwnerChanged(bool),
}
stream::channel(
5,
move |mut tx: futures::channel::mpsc::Sender<Update<T>>| async move {
let version = T::VERSION;
fn watcher_stream<T: CosmicConfigEntry + Send + Sync + Default + 'static + Clone>( let Ok(cosmic_config) = (if is_state {
settings_daemon: CosmicSettingsDaemonProxy<'static>, crate::Config::new_state(config_id, version)
config_id: &'static str, } else {
is_state: bool, crate::Config::new(config_id, version)
) -> impl Stream<Item = Update<T>> { }) else {
enum Change { pending::<()>().await;
Changes(Changed), unreachable!();
OwnerChanged(bool), };
}
stream::channel(5, move |mut tx| async move {
let version = T::VERSION;
let Ok(cosmic_config) = (if is_state { let mut attempts = 0;
crate::Config::new_state(config_id, version)
} else {
crate::Config::new(config_id, version)
}) else {
pending::<()>().await;
unreachable!();
};
let mut attempts = 0; loop {
let watcher = if is_state {
Watcher::new_state(&settings_daemon, config_id, version).await
} else {
Watcher::new_config(&settings_daemon, config_id, version).await
};
let Ok(watcher) = watcher else {
tracing::error!("Failed to create watcher for {config_id}");
loop { #[cfg(feature = "tokio")]
let watcher = if is_state { ::tokio::time::sleep(::tokio::time::Duration::from_secs(
Watcher::new_state(&settings_daemon, config_id, version).await 2_u64.pow(attempts),
} else { ))
Watcher::new_config(&settings_daemon, config_id, version).await .await;
}; #[cfg(feature = "async-std")]
let Ok(watcher) = watcher else { async_std::task::sleep(std::time::Duration::from_secs(
tracing::error!("Failed to create watcher for {config_id}"); 2_u64.pow(attempts),
))
.await;
#[cfg(not(any(feature = "tokio", feature = "async-std")))]
{
pending::<()>().await;
unreachable!();
}
attempts += 1;
// The settings daemon has exited
continue;
};
let Ok(changes) = watcher.receive_changed().await else {
tracing::error!("Failed to listen for changes for {config_id}");
#[cfg(feature = "tokio")] #[cfg(feature = "tokio")]
::tokio::time::sleep(::tokio::time::Duration::from_secs(2_u64.pow(attempts))).await; ::tokio::time::sleep(::tokio::time::Duration::from_secs(
#[cfg(feature = "async-std")] 2_u64.pow(attempts),
async_std::task::sleep(std::time::Duration::from_secs(2_u64.pow(attempts))).await; ))
#[cfg(not(any(feature = "tokio", feature = "async-std")))] .await;
{ #[cfg(feature = "async-std")]
pending::<()>().await; async_std::task::sleep(std::time::Duration::from_secs(
unreachable!(); 2_u64.pow(attempts),
} ))
attempts += 1; .await;
// The settings daemon has exited #[cfg(not(any(feature = "tokio", feature = "async-std")))]
continue; {
}; pending::<()>().await;
let Ok(changes) = watcher.receive_changed().await else { unreachable!();
tracing::error!("Failed to listen for changes for {config_id}"); }
attempts += 1;
// The settings daemon has exited
continue;
};
#[cfg(feature = "tokio")] let mut changes = changes.map(Change::Changes).fuse();
::tokio::time::sleep(::tokio::time::Duration::from_secs(2_u64.pow(attempts))).await;
#[cfg(feature = "async-std")]
async_std::task::sleep(std::time::Duration::from_secs(2_u64.pow(attempts))).await;
#[cfg(not(any(feature = "tokio", feature = "async-std")))]
{
pending::<()>().await;
unreachable!();
}
attempts += 1;
// The settings daemon has exited
continue;
};
let mut changes = changes.map(Change::Changes).fuse(); let Ok(owner_changed) = watcher.inner().receive_owner_changed().await
else {
tracing::error!("Failed to listen for owner changes for {config_id}");
#[cfg(feature = "tokio")]
::tokio::time::sleep(::tokio::time::Duration::from_secs(
2_u64.pow(attempts),
))
.await;
#[cfg(feature = "async-std")]
async_std::task::sleep(std::time::Duration::from_secs(
2_u64.pow(attempts),
))
.await;
#[cfg(not(any(feature = "tokio", feature = "async-std")))]
{
pending::<()>().await;
unreachable!();
}
attempts += 1;
// The settings daemon has exited
continue;
};
let mut owner_changed = owner_changed
.map(|c| Change::OwnerChanged(c.is_some()))
.fuse();
let Ok(owner_changed) = watcher.inner().receive_owner_changed().await else { // update now, just in case we missed changes while setting up stream
tracing::error!("Failed to listen for owner changes for {config_id}"); let mut config = match T::get_entry(&cosmic_config) {
#[cfg(feature = "tokio")] Ok(config) => config,
::tokio::time::sleep(::tokio::time::Duration::from_secs(2_u64.pow(attempts))).await; Err((errors, default)) => {
#[cfg(feature = "async-std")] for why in &errors {
async_std::task::sleep(std::time::Duration::from_secs(2_u64.pow(attempts))).await; if why.is_err() {
#[cfg(not(any(feature = "tokio", feature = "async-std")))] if let crate::Error::GetKey(_, err) = &why {
{ if err.kind() == std::io::ErrorKind::NotFound {
pending::<()>().await; // No system default config installed; don't error
unreachable!(); continue;
} }
attempts += 1; }
// The settings daemon has exited tracing::error!("error getting config: {config_id} {why}");
continue; }
}; }
let mut owner_changed = owner_changed default
.map(|c| Change::OwnerChanged(c.is_some())) }
.fuse(); };
// update now, just in case we missed changes while setting up stream if let Err(err) = tx
let mut config = match T::get_entry(&cosmic_config) { .send(Update {
Ok(config) => config, errors: Vec::new(),
Err((errors, default)) => { keys: Vec::new(),
for why in &errors { config: config.clone(),
if why.is_err() { })
if let crate::Error::GetKey(_, err) = &why { .await
if err.kind() == std::io::ErrorKind::NotFound { {
// No system default config installed; don't error tracing::error!("Failed to send config: {err}");
continue; }
loop {
let change: Changed = futures::select! {
c = changes.next() => {
let Some(Change::Changes(c)) = c else {
break;
};
c
}
c = owner_changed.next() => {
let Some(Change::OwnerChanged(cont)) = c else {
break;
};
if cont {
continue;
} else {
// The settings daemon has exited
break;
}
},
};
// Reset the attempts counter if we received a change
attempts = 0;
let Ok(args) = change.args() else {
// The settings daemon has exited
break;
};
let (errors, keys) = config.update_keys(&cosmic_config, &[args.key]);
if !keys.is_empty() {
if let Err(err) = tx
.send(Update {
errors,
keys,
config: config.clone(),
})
.await
{
tracing::error!("Failed to send config update: {err}");
} }
} }
tracing::error!("error getting config: {config_id} {why}");
} }
} }
default },
} )
}; },
)
if let Err(err) = tx
.send(Update {
errors: Vec::new(),
keys: Vec::new(),
config: config.clone(),
})
.await
{
tracing::error!("Failed to send config: {err}");
}
loop {
let change: Changed = futures::select! {
c = changes.next() => {
let Some(Change::Changes(c)) = c else {
break;
};
c
}
c = owner_changed.next() => {
let Some(Change::OwnerChanged(cont)) = c else {
break;
};
if cont {
continue;
} else {
// The settings daemon has exited
break;
}
},
};
// Reset the attempts counter if we received a change
attempts = 0;
let Ok(args) = change.args() else {
// The settings daemon has exited
break;
};
let (errors, keys) = config.update_keys(&cosmic_config, &[args.key]);
if !keys.is_empty() {
if let Err(err) = tx
.send(Update {
errors,
keys,
config: config.clone(),
})
.await
{
tracing::error!("Failed to send config update: {err}");
}
}
}
}
})
} }

View file

@ -25,7 +25,24 @@ pub fn config_subscription<
config_id: Cow<'static, str>, config_id: Cow<'static, str>,
config_version: u64, config_version: u64,
) -> iced_futures::Subscription<crate::Update<T>> { ) -> iced_futures::Subscription<crate::Update<T>> {
iced_futures::Subscription::run_with_id(id, watcher_stream(config_id, config_version, false)) iced_futures::Subscription::run_with(
(id, config_id, config_version, false),
// FIXME there are type issues related to the 'static lifetime of the Cow if this is extracted to a named function...
|(_, config_id, config_version, is_state)| {
let config_id = config_id.clone();
let config_version = *config_version;
let is_state = *is_state;
stream::channel(100, move |mut output| async move {
let config_id = config_id.clone();
let mut state = ConfigState::Init(config_id, config_version, is_state);
loop {
state = start_listening::<T>(state, &mut output).await;
}
})
},
)
} }
#[cold] #[cold]
@ -37,25 +54,23 @@ pub fn config_state_subscription<
config_id: Cow<'static, str>, config_id: Cow<'static, str>,
config_version: u64, config_version: u64,
) -> iced_futures::Subscription<crate::Update<T>> { ) -> iced_futures::Subscription<crate::Update<T>> {
iced_futures::Subscription::run_with_id(id, watcher_stream(config_id, config_version, true)) iced_futures::Subscription::run_with(
} (id, config_id, config_version, true),
|(_, config_id, config_version, is_state)| {
fn watcher_stream<T: 'static + Send + Sync + PartialEq + Clone + CosmicConfigEntry>(
config_id: Cow<'static, str>,
config_version: u64,
is_state: bool,
) -> impl Stream<Item = crate::Update<T>> {
stream::channel(100, move |mut output| {
let config_id = config_id.clone();
async move {
let config_id = config_id.clone(); let config_id = config_id.clone();
let mut state = ConfigState::Init(config_id, config_version, is_state); let config_version = *config_version;
let is_state = *is_state;
loop { stream::channel(100, move |mut output| async move {
state = start_listening::<T>(state, &mut output).await; let config_id = config_id.clone();
} let mut state = ConfigState::Init(config_id, config_version, is_state);
}
}) loop {
state = start_listening::<T>(state, &mut output).await;
}
})
},
)
} }
async fn start_listening<T: 'static + Send + Sync + PartialEq + Clone + CosmicConfigEntry>( async fn start_listening<T: 'static + Send + Sync + PartialEq + Clone + CosmicConfigEntry>(

View file

@ -23,4 +23,5 @@ features = [
"wgpu", "wgpu",
"single-instance", "single-instance",
"surface-message", "surface-message",
"multi-window",
] ]

2
iced

@ -1 +1 @@
Subproject commit d36e4df47f2e277fafcd3505229d53438c7f128d Subproject commit 73369a18eb4069f3f3d1916fd1e17537ee87a587

View file

@ -8,8 +8,6 @@ use crate::{config::CosmicTk, keyboard_nav};
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
use cctk::sctk::reexports::csd_frame::{WindowManagerCapabilities, WindowState}; use cctk::sctk::reexports::csd_frame::{WindowManagerCapabilities, WindowState};
use cosmic_theme::ThemeMode; use cosmic_theme::ThemeMode;
#[cfg(not(any(feature = "multi-window", feature = "wayland")))]
use iced::Application as IcedApplication;
/// A message managed internally by COSMIC. /// A message managed internally by COSMIC.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]

View file

@ -15,7 +15,7 @@ use cosmic_theme::ThemeMode;
use iced::Application as IcedApplication; use iced::Application as IcedApplication;
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
use iced::event::wayland; use iced::event::wayland;
use iced::{Task, window}; use iced::{Task, theme, window};
use iced_futures::event::listen_with; use iced_futures::event::listen_with;
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
use iced_winit::SurfaceIdWrapper; use iced_winit::SurfaceIdWrapper;
@ -397,15 +397,16 @@ where
f64::from(self.app.core().scale_factor()) f64::from(self.app.core().scale_factor())
} }
pub fn style(&self, theme: &Theme) -> iced_runtime::Appearance { pub fn style(&self, theme: &Theme) -> theme::Style {
if let Some(style) = self.app.style() { if let Some(style) = self.app.style() {
style style
} else if self.app.core().window.is_maximized { } else if self.app.core().window.is_maximized {
let theme = THEME.lock().unwrap(); let theme = THEME.lock().unwrap();
crate::style::iced::application::appearance(theme.borrow()) crate::style::iced::application::style(theme.borrow())
} else { } else {
let theme = THEME.lock().unwrap(); let theme = THEME.lock().unwrap();
iced_runtime::Appearance {
theme::Style {
background_color: iced_core::Color::TRANSPARENT, background_color: iced_core::Color::TRANSPARENT,
icon_color: theme.cosmic().on_bg_color().into(), icon_color: theme.cosmic().on_bg_color().into(),
text_color: theme.cosmic().on_bg_color().into(), text_color: theme.cosmic().on_bg_color().into(),
@ -635,7 +636,7 @@ impl<T: Application> Cosmic<T> {
self.app.on_window_resize(id, width, height); self.app.on_window_resize(id, width, height);
//TODO: more efficient test of maximized (winit has no event for maximize if set by the OS) //TODO: more efficient test of maximized (winit has no event for maximize if set by the OS)
return iced::window::get_maximized(id).map(move |maximized| { return iced::window::is_maximized(id).map(move |maximized| {
crate::Action::Cosmic(Action::WindowMaximized(id, maximized)) crate::Action::Cosmic(Action::WindowMaximized(id, maximized))
}); });
} }
@ -711,10 +712,10 @@ impl<T: Application> Cosmic<T> {
Action::KeyboardNav(message) => match message { Action::KeyboardNav(message) => match message {
keyboard_nav::Action::FocusNext => { keyboard_nav::Action::FocusNext => {
return iced::widget::focus_next().map(crate::Action::Cosmic); return iced::widget::operation::focus_next().map(crate::Action::Cosmic);
} }
keyboard_nav::Action::FocusPrevious => { keyboard_nav::Action::FocusPrevious => {
return iced::widget::focus_previous().map(crate::Action::Cosmic); return iced::widget::operation::focus_previous().map(crate::Action::Cosmic);
} }
keyboard_nav::Action::Escape => return self.app.on_escape(), keyboard_nav::Action::Escape => return self.app.on_escape(),
keyboard_nav::Action::Search => return self.app.on_search(), keyboard_nav::Action::Search => return self.app.on_search(),

View file

@ -11,9 +11,8 @@ pub use action::Action;
use cosmic_config::CosmicConfigEntry; use cosmic_config::CosmicConfigEntry;
pub mod context_drawer; pub mod context_drawer;
pub use context_drawer::{ContextDrawer, context_drawer}; pub use context_drawer::{ContextDrawer, context_drawer};
use iced::application::BootFn;
pub mod cosmic; pub mod cosmic;
#[cfg(all(feature = "winit", feature = "multi-window"))]
pub(crate) mod multi_window;
pub mod settings; pub mod settings;
pub type Task<M> = iced::Task<crate::Action<M>>; pub type Task<M> = iced::Task<crate::Action<M>>;
@ -21,12 +20,13 @@ pub type Task<M> = iced::Task<crate::Action<M>>;
pub use crate::Core; pub use crate::Core;
use crate::prelude::*; use crate::prelude::*;
use crate::theme::THEME; use crate::theme::THEME;
use crate::widget::{container, horizontal_space, id_container, menu, nav_bar, popover}; use crate::widget::{container, id_container, menu, nav_bar, popover, space};
use apply::Apply; use apply::Apply;
use iced::window;
use iced::{Length, Subscription}; use iced::{Length, Subscription};
use iced::{theme, window};
pub use settings::Settings; pub use settings::Settings;
use std::borrow::Cow; use std::borrow::Cow;
use std::{cell::RefCell, rc::Rc};
#[cold] #[cold]
pub(crate) fn iced_settings<App: Application>( pub(crate) fn iced_settings<App: Application>(
@ -72,7 +72,7 @@ pub(crate) fn iced_settings<App: Application>(
core.exit_on_main_window_closed = exit_on_close; core.exit_on_main_window_closed = exit_on_close;
if let Some(border_size) = settings.resizable { if let Some(border_size) = settings.resizable {
window_settings.resize_border = border_size as u32; // window_settings.resize_border = border_size as u32;
window_settings.resizable = true; window_settings.resizable = true;
} }
window_settings.decorations = !settings.client_decorations; window_settings.decorations = !settings.client_decorations;
@ -82,7 +82,7 @@ pub(crate) fn iced_settings<App: Application>(
window_settings.min_size = Some(min_size); window_settings.min_size = Some(min_size);
} }
let max_size = settings.size_limits.max(); let max_size = settings.size_limits.max();
if max_size != iced::Size::INFINITY { if max_size != iced::Size::INFINITE {
window_settings.max_size = Some(max_size); window_settings.max_size = Some(max_size);
} }
@ -90,6 +90,22 @@ pub(crate) fn iced_settings<App: Application>(
(iced, (core, flags), window_settings) (iced, (core, flags), window_settings)
} }
pub(crate) struct BootDataInner<A: crate::app::Application> {
pub flags: A::Flags,
pub core: Core,
}
pub(crate) struct BootData<A: crate::app::Application>(pub Rc<RefCell<Option<BootDataInner<A>>>>);
impl<A: crate::app::Application> BootFn<cosmic::Cosmic<A>, crate::Action<A::Message>>
for BootData<A>
{
fn boot(&self) -> (cosmic::Cosmic<A>, iced::Task<crate::Action<A::Message>>) {
let mut data = self.0.borrow_mut();
let data = data.take().unwrap();
cosmic::Cosmic::<A>::init((data.core, data.flags))
}
}
/// Launch a COSMIC application with the given [`Settings`]. /// Launch a COSMIC application with the given [`Settings`].
/// ///
/// # Errors /// # Errors
@ -102,39 +118,50 @@ pub fn run<App: Application>(settings: Settings, flags: App::Flags) -> iced::Res
} }
let default_font = settings.default_font; let default_font = settings.default_font;
let (settings, mut flags, window_settings) = iced_settings::<App>(settings, flags); let (settings, (mut core, flags), window_settings) = iced_settings::<App>(settings, flags);
#[cfg(not(feature = "multi-window"))] #[cfg(not(feature = "multi-window"))]
{ {
flags.0.main_window = Some(iced::window::Id::RESERVED); core.main_window = Some(iced::window::Id::RESERVED);
iced::application( iced::application(
cosmic::Cosmic::title, BootData(Rc::new(RefCell::new(Some(BootDataInner::<App> {
flags,
core,
})))),
cosmic::Cosmic::update, cosmic::Cosmic::update,
cosmic::Cosmic::view, cosmic::Cosmic::view,
) )
.subscription(cosmic::Cosmic::subscription) .subscription(cosmic::Cosmic::subscription)
.title(cosmic::Cosmic::title)
.style(cosmic::Cosmic::style) .style(cosmic::Cosmic::style)
.theme(cosmic::Cosmic::theme) .theme(cosmic::Cosmic::theme)
.window_size((500.0, 800.0)) .window_size((500.0, 800.0))
.settings(settings) .settings(settings)
.window(window_settings) .window(window_settings)
.run_with(move || cosmic::Cosmic::<App>::init(flags)) .run()
} }
#[cfg(feature = "multi-window")] #[cfg(feature = "multi-window")]
{ {
let mut app = multi_window::multi_window::<_, _, _, _, App::Executor>( let no_main_window = core.main_window.is_none();
cosmic::Cosmic::title, if no_main_window {
// app = app.window(window_settings);
core.main_window = Some(iced_core::window::Id::RESERVED);
}
let mut app = iced::daemon(
BootData(Rc::new(RefCell::new(Some(BootDataInner::<App> {
flags,
core,
})))),
cosmic::Cosmic::update, cosmic::Cosmic::update,
cosmic::Cosmic::view, cosmic::Cosmic::view,
); );
if flags.0.main_window.is_none() {
app = app.window(window_settings);
flags.0.main_window = Some(iced_core::window::Id::RESERVED);
}
app.subscription(cosmic::Cosmic::subscription) app.subscription(cosmic::Cosmic::subscription)
.title(cosmic::Cosmic::title)
.style(cosmic::Cosmic::style) .style(cosmic::Cosmic::style)
.theme(cosmic::Cosmic::theme) .theme(cosmic::Cosmic::theme)
.settings(settings) .settings(settings)
.run_with(move || cosmic::Cosmic::<App>::init(flags)) .run()
} }
} }
@ -204,13 +231,16 @@ where
tracing::info!("Another instance is running"); tracing::info!("Another instance is running");
Ok(()) Ok(())
} else { } else {
let (settings, mut flags, window_settings) = iced_settings::<App>(settings, flags); let (settings, (mut core, flags), window_settings) = iced_settings::<App>(settings, flags);
flags.0.single_instance = true; core.single_instance = true;
#[cfg(not(feature = "multi-window"))] #[cfg(not(feature = "multi-window"))]
{ {
iced::application( iced::application(
cosmic::Cosmic::title, BootData(Rc::new(RefCell::new(Some(BootDataInner::<App> {
flags,
core,
})))),
cosmic::Cosmic::update, cosmic::Cosmic::update,
cosmic::Cosmic::view, cosmic::Cosmic::view,
) )
@ -220,24 +250,30 @@ where
.window_size((500.0, 800.0)) .window_size((500.0, 800.0))
.settings(settings) .settings(settings)
.window(window_settings) .window(window_settings)
.run_with(move || cosmic::Cosmic::<App>::init(flags)) .run()
} }
#[cfg(feature = "multi-window")] #[cfg(feature = "multi-window")]
{ {
let mut app = multi_window::multi_window::<_, _, _, _, App::Executor>( let no_main_window = core.main_window.is_none();
cosmic::Cosmic::title, if no_main_window {
// app = app.window(window_settings);
core.main_window = Some(iced_core::window::Id::RESERVED);
}
let mut app = iced::daemon(
BootData(Rc::new(RefCell::new(Some(BootDataInner::<App> {
flags,
core,
})))),
cosmic::Cosmic::update, cosmic::Cosmic::update,
cosmic::Cosmic::view, cosmic::Cosmic::view,
); );
if flags.0.main_window.is_none() {
app = app.window(window_settings);
flags.0.main_window = Some(iced_core::window::Id::RESERVED);
}
app.subscription(cosmic::Cosmic::subscription) app.subscription(cosmic::Cosmic::subscription)
.style(cosmic::Cosmic::style) .style(cosmic::Cosmic::style)
.title(cosmic::Cosmic::title)
.theme(cosmic::Cosmic::theme) .theme(cosmic::Cosmic::theme)
.settings(settings) .settings(settings)
.run_with(move || cosmic::Cosmic::<App>::init(flags)) .run()
} }
} }
} }
@ -428,7 +464,7 @@ where
} }
/// Overrides the default style for applications /// Overrides the default style for applications
fn style(&self) -> Option<iced_runtime::Appearance> { fn style(&self) -> Option<theme::Style> {
None None
} }
@ -667,7 +703,7 @@ impl<App: Application> ApplicationExt for App {
) )
} else { } else {
//TODO: this element is added to workaround state issues //TODO: this element is added to workaround state issues
widgets.push(horizontal_space().width(Length::Shrink).into()); widgets.push(space::horizontal().width(Length::Shrink).into());
} }
} }
} }

View file

@ -1,244 +0,0 @@
// Copyright 2024 System76 <info@system76.com>
// SPDX-License-Identifier: MPL-2.0
//! Create and run daemons that run in the background.
//! Copied from iced 0.13, but adds optional initial window
use iced::application;
use iced::window;
use iced::{
self, Program,
program::{self, with_style, with_subscription, with_theme, with_title},
runtime::{Appearance, DefaultStyle},
};
use iced::{Element, Result, Settings, Subscription, Task};
use std::marker::PhantomData;
pub(crate) struct Instance<State, Message, Theme, Renderer, Update, View, Executor> {
update: Update,
view: View,
_state: PhantomData<State>,
_message: PhantomData<Message>,
_theme: PhantomData<Theme>,
_renderer: PhantomData<Renderer>,
_executor: PhantomData<Executor>,
}
/// Creates an iced [`MultiWindow`] given its title, update, and view logic.
pub fn multi_window<State, Message, Theme, Renderer, Executor>(
title: impl Title<State>,
update: impl application::Update<State, Message>,
view: impl for<'a> self::View<'a, State, Message, Theme, Renderer>,
) -> MultiWindow<impl Program<State = State, Message = Message, Theme = Theme>>
where
State: 'static,
Message: Send + std::fmt::Debug + 'static,
Theme: Default + DefaultStyle,
Renderer: program::Renderer,
Executor: iced::Executor,
{
use std::marker::PhantomData;
impl<State, Message, Theme, Renderer, Update, View, Executor> Program
for Instance<State, Message, Theme, Renderer, Update, View, Executor>
where
Message: Send + std::fmt::Debug + 'static,
Theme: Default + DefaultStyle,
Renderer: program::Renderer,
Update: application::Update<State, Message>,
View: for<'a> self::View<'a, State, Message, Theme, Renderer>,
Executor: iced::Executor,
{
type State = State;
type Message = Message;
type Theme = Theme;
type Renderer = Renderer;
type Executor = Executor;
fn update(&self, state: &mut Self::State, message: Self::Message) -> Task<Self::Message> {
self.update.update(state, message).into()
}
fn view<'a>(
&self,
state: &'a Self::State,
window: window::Id,
) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
self.view.view(state, window).into()
}
}
MultiWindow {
raw: Instance {
update,
view,
_state: PhantomData,
_message: PhantomData,
_theme: PhantomData,
_renderer: PhantomData,
_executor: PhantomData::<Executor>,
},
settings: Settings::default(),
window: None,
}
.title(title)
}
/// The underlying definition and configuration of an iced daemon.
///
/// You can use this API to create and run iced applications
/// step by step—without coupling your logic to a trait
/// or a specific type.
///
/// You can create a [`MultiWindow`] with the [`daemon`] helper.
#[derive(Debug)]
pub struct MultiWindow<P: Program> {
raw: P,
settings: Settings,
window: Option<window::Settings>,
}
impl<P: Program> MultiWindow<P> {
#[cfg(any(feature = "winit", feature = "wayland"))]
/// Runs the [`MultiWindow`].
///
/// The state of the [`MultiWindow`] must implement [`Default`].
/// If your state does not implement [`Default`], use [`run_with`]
/// instead.
///
/// [`run_with`]: Self::run_with
pub fn run(self) -> Result
where
Self: 'static,
P::State: Default,
{
self.raw.run(self.settings, self.window)
}
#[cfg(any(feature = "winit", feature = "wayland"))]
/// Runs the [`MultiWindow`] with a closure that creates the initial state.
pub fn run_with<I>(self, initialize: I) -> Result
where
Self: 'static,
I: FnOnce() -> (P::State, Task<P::Message>) + 'static,
{
self.raw.run_with(self.settings, self.window, initialize)
}
/// Sets the [`Settings`] that will be used to run the [`MultiWindow`].
pub fn settings(self, settings: Settings) -> Self {
Self { settings, ..self }
}
/// Sets the [`Title`] of the [`MultiWindow`].
pub(crate) fn title(
self,
title: impl Title<P::State>,
) -> MultiWindow<impl Program<State = P::State, Message = P::Message, Theme = P::Theme>> {
MultiWindow {
raw: with_title(self.raw, move |state, window| title.title(state, window)),
settings: self.settings,
window: self.window,
}
}
/// Sets the subscription logic of the [`MultiWindow`].
pub fn subscription(
self,
f: impl Fn(&P::State) -> Subscription<P::Message>,
) -> MultiWindow<impl Program<State = P::State, Message = P::Message, Theme = P::Theme>> {
MultiWindow {
raw: with_subscription(self.raw, f),
settings: self.settings,
window: self.window,
}
}
/// Sets the theme logic of the [`MultiWindow`].
pub fn theme(
self,
f: impl Fn(&P::State, window::Id) -> P::Theme,
) -> MultiWindow<impl Program<State = P::State, Message = P::Message, Theme = P::Theme>> {
MultiWindow {
raw: with_theme(self.raw, f),
settings: self.settings,
window: self.window,
}
}
/// Sets the style logic of the [`MultiWindow`].
pub fn style(
self,
f: impl Fn(&P::State, &P::Theme) -> Appearance,
) -> MultiWindow<impl Program<State = P::State, Message = P::Message, Theme = P::Theme>> {
MultiWindow {
raw: with_style(self.raw, f),
settings: self.settings,
window: self.window,
}
}
/// Sets the window settings of the [`MultiWindow`].
pub fn window(self, window: window::Settings) -> Self {
Self {
raw: self.raw,
settings: self.settings,
window: Some(window),
}
}
}
/// The title logic of some [`MultiWindow`].
///
/// This trait is implemented both for `&static str` and
/// any closure `Fn(&State, window::Id) -> String`.
///
/// This trait allows the [`daemon`] builder to take any of them.
pub trait Title<State> {
/// Produces the title of the [`MultiWindow`].
fn title(&self, state: &State, window: window::Id) -> String;
}
impl<State> Title<State> for &'static str {
fn title(&self, _state: &State, _window: window::Id) -> String {
(*self).to_string()
}
}
impl<T, State> Title<State> for T
where
T: Fn(&State, window::Id) -> String,
{
fn title(&self, state: &State, window: window::Id) -> String {
self(state, window)
}
}
/// The view logic of some [`MultiWindow`].
///
/// This trait allows the [`daemon`] builder to take any closure that
/// returns any `Into<Element<'_, Message>>`.
pub trait View<'a, State, Message, Theme, Renderer> {
/// Produces the widget of the [`MultiWindow`].
fn view(
&self,
state: &'a State,
window: window::Id,
) -> impl Into<Element<'a, Message, Theme, Renderer>>;
}
impl<'a, T, State, Message, Theme, Renderer, Widget> View<'a, State, Message, Theme, Renderer> for T
where
T: Fn(&'a State, window::Id) -> Widget,
State: 'static,
Widget: Into<Element<'a, Message, Theme, Renderer>>,
{
fn view(
&self,
state: &'a State,
window: window::Id,
) -> impl Into<Element<'a, Message, Theme, Renderer>> {
self(state, window)
}
}

View file

@ -217,7 +217,7 @@ where
} }
fn layout( fn layout(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
renderer: &Renderer, renderer: &Renderer,
limits: &layout::Limits, limits: &layout::Limits,
@ -233,25 +233,26 @@ where
self.padding, self.padding,
self.spacing, self.spacing,
self.align, self.align,
&self.children, &mut self.children,
&mut tree.children, &mut tree.children,
) )
} }
fn operate( fn operate(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
layout: Layout<'_>, layout: Layout<'_>,
renderer: &Renderer, renderer: &Renderer,
operation: &mut dyn Operation, operation: &mut dyn Operation,
) { ) {
operation.container(None, layout.bounds(), &mut |operation| { operation.container(None, layout.bounds());
operation.traverse(&mut |operation| {
self.children self.children
.iter() .iter_mut()
.zip(&mut tree.children) .zip(&mut tree.children)
.zip(layout.children()) .zip(layout.children())
.for_each(|((child, state), c_layout)| { .for_each(|((child, state), c_layout)| {
child.as_widget().operate( child.as_widget_mut().operate(
state, state,
c_layout.with_virtual_offset(layout.virtual_offset()), c_layout.with_virtual_offset(layout.virtual_offset()),
renderer, renderer,
@ -261,17 +262,17 @@ where
}); });
} }
fn on_event( fn update(
&mut self, &mut self,
tree: &mut Tree, tree: &mut Tree,
event: Event, event: &Event,
layout: Layout<'_>, layout: Layout<'_>,
cursor: mouse::Cursor, cursor: mouse::Cursor,
renderer: &Renderer, renderer: &Renderer,
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
viewport: &Rectangle, viewport: &Rectangle,
) -> event::Status { ) {
let my_state = tree.state.downcast_mut::<State>(); let my_state = tree.state.downcast_mut::<State>();
if let Some(hovered) = my_state.hovered { if let Some(hovered) = my_state.hovered {
@ -285,7 +286,7 @@ where
e, e,
mouse::Event::CursorLeft | mouse::Event::ButtonReleased { .. } mouse::Event::CursorLeft | mouse::Event::ButtonReleased { .. }
) { ) {
return self.children[hovered].as_widget_mut().on_event( return self.children[hovered].as_widget_mut().update(
&mut tree.children[hovered], &mut tree.children[hovered],
event, event,
child_layout.with_virtual_offset(layout.virtual_offset()), child_layout.with_virtual_offset(layout.virtual_offset()),
@ -302,7 +303,7 @@ where
iced::core::touch::Event::FingerLifted { .. } iced::core::touch::Event::FingerLifted { .. }
| iced::core::touch::Event::FingerLost { .. } | iced::core::touch::Event::FingerLost { .. }
) { ) {
return self.children[hovered].as_widget_mut().on_event( return self.children[hovered].as_widget_mut().update(
&mut tree.children[hovered], &mut tree.children[hovered],
event, event,
child_layout.with_virtual_offset(layout.virtual_offset()), child_layout.with_virtual_offset(layout.virtual_offset()),
@ -336,9 +337,9 @@ where
) && cursor.is_over(c_layout.bounds()) ) && cursor.is_over(c_layout.bounds())
{ {
my_state.hovered = Some(i); my_state.hovered = Some(i);
return child.as_widget_mut().on_event( return child.as_widget_mut().update(
state, state,
event.clone(), &event,
c_layout.with_virtual_offset(layout.virtual_offset()), c_layout.with_virtual_offset(layout.virtual_offset()),
cursor_virtual, cursor_virtual,
renderer, renderer,
@ -350,9 +351,9 @@ where
cursor_virtual = mouse::Cursor::Unavailable; cursor_virtual = mouse::Cursor::Unavailable;
} }
child.as_widget_mut().on_event( child.as_widget_mut().update(
state, state,
event.clone(), &event,
c_layout.with_virtual_offset(layout.virtual_offset()), c_layout.with_virtual_offset(layout.virtual_offset()),
cursor_virtual, cursor_virtual,
renderer, renderer,
@ -360,8 +361,7 @@ where
shell, shell,
viewport, viewport,
) )
}) });
.fold(event::Status::Ignored, event::Status::merge)
} }
fn mouse_interaction( fn mouse_interaction(
@ -436,11 +436,19 @@ where
fn overlay<'b>( fn overlay<'b>(
&'b mut self, &'b mut self,
tree: &'b mut Tree, tree: &'b mut Tree,
layout: Layout<'_>, layout: Layout<'b>,
renderer: &Renderer, renderer: &Renderer,
viewport: &Rectangle,
translation: Vector, translation: Vector,
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> { ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
overlay::from_children(&mut self.children, tree, layout, renderer, translation) overlay::from_children(
&mut self.children,
tree,
layout,
renderer,
viewport,
translation,
)
} }
#[cfg(feature = "a11y")] #[cfg(feature = "a11y")]

View file

@ -1,7 +1,7 @@
#[cfg(feature = "applet-token")] #[cfg(feature = "applet-token")]
pub mod token; pub mod token;
use crate::app::cosmic; use crate::app::{BootData, BootDataInner, cosmic};
use crate::{ use crate::{
Application, Element, Renderer, Application, Element, Renderer,
app::iced_settings, app::iced_settings,
@ -18,17 +18,19 @@ use crate::{
self, self,
autosize::{self, Autosize, autosize}, autosize::{self, Autosize, autosize},
column::Column, column::Column,
horizontal_space, layer_container, layer_container,
row::Row, row::Row,
vertical_space, space::horizontal,
space::vertical,
}, },
}; };
pub use cosmic_panel_config; pub use cosmic_panel_config;
use cosmic_panel_config::{CosmicPanelBackground, PanelAnchor, PanelSize}; use cosmic_panel_config::{CosmicPanelBackground, PanelAnchor, PanelSize};
use iced_core::{Padding, Shadow}; use iced_core::{Padding, Shadow};
use iced_runtime::platform_specific::wayland::popup::{SctkPopupSettings, SctkPositioner};
use iced_widget::Text; use iced_widget::Text;
use iced_widget::runtime::platform_specific::wayland::popup::{SctkPopupSettings, SctkPositioner};
use sctk::reexports::protocols::xdg::shell::client::xdg_positioner::{Anchor, Gravity}; use sctk::reexports::protocols::xdg::shell::client::xdg_positioner::{Anchor, Gravity};
use std::cell::RefCell;
use std::{borrow::Cow, num::NonZeroU32, rc::Rc, sync::LazyLock, time::Duration}; use std::{borrow::Cow, num::NonZeroU32, rc::Rc, sync::LazyLock, time::Duration};
use tracing::info; use tracing::info;
@ -386,6 +388,7 @@ impl Context {
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
icon_color: Some(cosmic.background.on.into()), icon_color: Some(cosmic.background.on.into()),
snap: true,
} }
}), }),
) )
@ -567,30 +570,36 @@ pub fn run<App: Application>(flags: App::Flags) -> iced::Result {
window_settings.decorations = false; window_settings.decorations = false;
window_settings.exit_on_close_request = true; window_settings.exit_on_close_request = true;
window_settings.resizable = false; window_settings.resizable = false;
window_settings.resize_border = 0; // window_settings.resize_border = 0;
// TODO make multi-window not mandatory // TODO make multi-window not mandatory
let mut app = super::app::multi_window::multi_window::<_, _, _, _, App::Executor>( let no_main_window = core.main_window.is_none();
cosmic::Cosmic::title, if no_main_window {
// TODO still apply window settings?
// window_settings = window_settings.clone();
core.main_window = Some(iced_core::window::Id::RESERVED);
}
let mut app = iced::daemon(
BootData(Rc::new(RefCell::new(Some(BootDataInner::<App> {
flags,
core,
})))),
cosmic::Cosmic::update, cosmic::Cosmic::update,
cosmic::Cosmic::view, cosmic::Cosmic::view,
); );
if core.main_window.is_none() {
app = app.window(window_settings.clone());
core.main_window = Some(iced_core::window::Id::RESERVED);
}
app.subscription(cosmic::Cosmic::subscription) app.subscription(cosmic::Cosmic::subscription)
.style(cosmic::Cosmic::style) .style(cosmic::Cosmic::style)
.theme(cosmic::Cosmic::theme) .theme(cosmic::Cosmic::theme)
.settings(iced_settings) .settings(iced_settings)
.run_with(move || cosmic::Cosmic::<App>::init((core, flags))) .run()
} }
#[must_use] #[must_use]
pub fn style() -> iced_runtime::Appearance { pub fn style() -> iced::theme::Style {
let theme = crate::theme::THEME.lock().unwrap(); let theme = crate::theme::THEME.lock().unwrap();
iced_runtime::Appearance { iced::theme::Style {
background_color: Color::from_rgba(0.0, 0.0, 0.0, 0.0), background_color: Color::from_rgba(0.0, 0.0, 0.0, 0.0),
text_color: theme.cosmic().on_bg_color().into(), text_color: theme.cosmic().on_bg_color().into(),
icon_color: theme.cosmic().on_bg_color().into(), icon_color: theme.cosmic().on_bg_color().into(),

View file

@ -208,7 +208,7 @@ where
} }
fn layout( fn layout(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
renderer: &Renderer, renderer: &Renderer,
limits: &layout::Limits, limits: &layout::Limits,
@ -222,25 +222,26 @@ where
self.padding, self.padding,
self.spacing, self.spacing,
self.align, self.align,
&self.children, &mut self.children,
&mut tree.children, &mut tree.children,
) )
} }
fn operate( fn operate(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
layout: Layout<'_>, layout: Layout<'_>,
renderer: &Renderer, renderer: &Renderer,
operation: &mut dyn Operation, operation: &mut dyn Operation,
) { ) {
operation.container(None, layout.bounds(), &mut |operation| { operation.container(None, layout.bounds());
operation.traverse(&mut |operation| {
self.children self.children
.iter() .iter_mut()
.zip(&mut tree.children) .zip(&mut tree.children)
.zip(layout.children()) .zip(layout.children())
.for_each(|((child, state), c_layout)| { .for_each(|((child, state), c_layout)| {
child.as_widget().operate( child.as_widget_mut().operate(
state, state,
c_layout.with_virtual_offset(layout.virtual_offset()), c_layout.with_virtual_offset(layout.virtual_offset()),
renderer, renderer,
@ -250,17 +251,17 @@ where
}); });
} }
fn on_event( fn update(
&mut self, &mut self,
tree: &mut Tree, tree: &mut Tree,
event: Event, event: &Event,
layout: Layout<'_>, layout: Layout<'_>,
cursor: mouse::Cursor, cursor: mouse::Cursor,
renderer: &Renderer, renderer: &Renderer,
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
viewport: &Rectangle, viewport: &Rectangle,
) -> event::Status { ) {
let my_state = tree.state.downcast_mut::<State>(); let my_state = tree.state.downcast_mut::<State>();
if let Some(hovered) = my_state.hovered { if let Some(hovered) = my_state.hovered {
@ -274,7 +275,7 @@ where
e, e,
mouse::Event::CursorLeft | mouse::Event::ButtonReleased { .. } mouse::Event::CursorLeft | mouse::Event::ButtonReleased { .. }
) { ) {
return self.children[hovered].as_widget_mut().on_event( return self.children[hovered].as_widget_mut().update(
&mut tree.children[hovered], &mut tree.children[hovered],
event, event,
child_layout.with_virtual_offset(layout.virtual_offset()), child_layout.with_virtual_offset(layout.virtual_offset()),
@ -291,7 +292,7 @@ where
iced::core::touch::Event::FingerLifted { .. } iced::core::touch::Event::FingerLifted { .. }
| iced::core::touch::Event::FingerLost { .. } | iced::core::touch::Event::FingerLost { .. }
) { ) {
return self.children[hovered].as_widget_mut().on_event( return self.children[hovered].as_widget_mut().update(
&mut tree.children[hovered], &mut tree.children[hovered],
event, event,
child_layout.with_virtual_offset(layout.virtual_offset()), child_layout.with_virtual_offset(layout.virtual_offset()),
@ -326,9 +327,9 @@ where
) && cursor.is_over(c_layout.bounds()) ) && cursor.is_over(c_layout.bounds())
{ {
my_state.hovered = Some(i); my_state.hovered = Some(i);
return child.as_widget_mut().on_event( return child.as_widget_mut().update(
state, state,
event.clone(), &event,
c_layout.with_virtual_offset(layout.virtual_offset()), c_layout.with_virtual_offset(layout.virtual_offset()),
cursor_virtual, cursor_virtual,
renderer, renderer,
@ -340,9 +341,9 @@ where
cursor_virtual = mouse::Cursor::Unavailable; cursor_virtual = mouse::Cursor::Unavailable;
} }
child.as_widget_mut().on_event( child.as_widget_mut().update(
state, state,
event.clone(), &event,
c_layout.with_virtual_offset(layout.virtual_offset()), c_layout.with_virtual_offset(layout.virtual_offset()),
cursor_virtual, cursor_virtual,
renderer, renderer,
@ -350,8 +351,7 @@ where
shell, shell,
viewport, viewport,
) )
}) });
.fold(event::Status::Ignored, event::Status::merge)
} }
fn mouse_interaction( fn mouse_interaction(
@ -426,11 +426,19 @@ where
fn overlay<'b>( fn overlay<'b>(
&'b mut self, &'b mut self,
tree: &'b mut Tree, tree: &'b mut Tree,
layout: Layout<'_>, layout: Layout<'b>,
renderer: &Renderer, renderer: &Renderer,
viewport: &Rectangle,
translation: Vector, translation: Vector,
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> { ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
overlay::from_children(&mut self.children, tree, layout, renderer, translation) overlay::from_children(
&mut self.children,
tree,
layout,
renderer,
viewport,
translation,
)
} }
#[cfg(feature = "a11y")] #[cfg(feature = "a11y")]

View file

@ -14,16 +14,15 @@ use super::wayland_handler::wayland_handler;
pub fn activation_token_subscription<I: 'static + Hash + Copy + Send + Sync + Debug>( pub fn activation_token_subscription<I: 'static + Hash + Copy + Send + Sync + Debug>(
id: I, id: I,
) -> iced::Subscription<TokenUpdate> { ) -> iced::Subscription<TokenUpdate> {
Subscription::run_with_id( Subscription::run_with(id, |_| {
id,
stream::channel(50, move |mut output| async move { stream::channel(50, move |mut output| async move {
let mut state = State::Ready; let mut state = State::Ready;
loop { loop {
state = start_listening(state, &mut output).await; state = start_listening(state, &mut output).await;
} }
}), })
) })
} }
pub enum State { pub enum State {

View file

@ -39,7 +39,7 @@ pub fn set_theme<M: Send + 'static>(theme: crate::Theme) -> iced::Task<crate::Ac
/// Sets the window mode to windowed. /// Sets the window mode to windowed.
pub fn set_windowed<M>(id: window::Id) -> iced::Task<crate::Action<M>> { pub fn set_windowed<M>(id: window::Id) -> iced::Task<crate::Action<M>> {
iced_runtime::window::change_mode(id, window::Mode::Windowed) iced_runtime::window::set_mode(id, window::Mode::Windowed)
} }
/// Toggles the windows' maximize state. /// Toggles the windows' maximize state.

View file

@ -16,75 +16,80 @@ use {
#[cold] #[cold]
pub fn subscription<App: ApplicationExt>() -> Subscription<crate::Action<App::Message>> { pub fn subscription<App: ApplicationExt>() -> Subscription<crate::Action<App::Message>> {
use iced_futures::futures::StreamExt; use iced_futures::futures::StreamExt;
iced_futures::Subscription::run_with_id( iced_futures::Subscription::run_with(TypeId::of::<DbusActivation>(), |_| {
TypeId::of::<DbusActivation>(), iced::stream::channel(
iced::stream::channel(10, move |mut output| async move { 10,
let mut single_instance: DbusActivation = DbusActivation::new(); move |mut output: Sender<crate::Action<App::Message>>| async move {
let mut rx = single_instance.rx(); let mut single_instance: DbusActivation = DbusActivation::new();
if let Ok(builder) = zbus::connection::Builder::session() { let mut rx = single_instance.rx();
let path: String = format!("/{}", App::APP_ID.replace('.', "/")); if let Ok(builder) = zbus::connection::Builder::session() {
if let Ok(conn) = builder.build().await { let path: String = format!("/{}", App::APP_ID.replace('.', "/"));
// XXX Setup done this way seems to be more reliable. if let Ok(conn) = builder.build().await {
// // XXX Setup done this way seems to be more reliable.
// the docs for serve_at seem to imply it will replace the //
// existing interface at the requested path, but it doesn't // the docs for serve_at seem to imply it will replace the
// seem to work that way all the time. The docs for // existing interface at the requested path, but it doesn't
// object_server().at() imply it won't replace the existing // seem to work that way all the time. The docs for
// interface. // object_server().at() imply it won't replace the existing
// // interface.
// request_name is used either way, with the builder or //
// with the connection, but it must be done after the // request_name is used either way, with the builder or
// object server is setup. // with the connection, but it must be done after the
if conn.object_server().at(path, single_instance).await != Ok(true) { // object server is setup.
tracing::error!("Failed to serve dbus"); if conn.object_server().at(path, single_instance).await != Ok(true) {
std::process::exit(1); tracing::error!("Failed to serve dbus");
} std::process::exit(1);
if conn.request_name(App::APP_ID).await.is_err() { }
tracing::error!("Failed to serve dbus"); if conn.request_name(App::APP_ID).await.is_err() {
std::process::exit(1); tracing::error!("Failed to serve dbus");
} std::process::exit(1);
}
output output
.send(crate::Action::Cosmic(crate::app::Action::DbusConnection( .send(crate::Action::Cosmic(crate::app::Action::DbusConnection(
conn.clone(), conn.clone(),
))) )))
.await; .await;
#[cfg(feature = "smol")] #[cfg(feature = "smol")]
let handle = { let handle = {
std::thread::spawn(move || { std::thread::spawn(move || {
let conn_clone = _conn.clone(); let conn_clone = _conn.clone();
zbus::block_on(async move { zbus::block_on(async move {
loop { loop {
conn_clone.executor().tick().await; conn_clone.executor().tick().await;
} }
})
}) })
}) };
}; while let Some(mut msg) = rx.next().await {
while let Some(mut msg) = rx.next().await { if let Some(token) = msg.activation_token.take() {
if let Some(token) = msg.activation_token.take() { if let Err(err) = output
if let Err(err) = output .send(crate::Action::Cosmic(crate::app::Action::Activate(
.send(crate::Action::Cosmic(crate::app::Action::Activate(token))) token,
.await )))
.await
{
tracing::error!(?err, "Failed to send message");
}
}
if let Err(err) = output.send(crate::Action::DbusActivation(msg)).await
{ {
tracing::error!(?err, "Failed to send message"); tracing::error!(?err, "Failed to send message");
} }
} }
if let Err(err) = output.send(crate::Action::DbusActivation(msg)).await {
tracing::error!(?err, "Failed to send message");
}
} }
} else {
tracing::warn!("Failed to connect to dbus for single instance");
} }
} else {
tracing::warn!("Failed to connect to dbus for single instance");
}
loop { loop {
iced::futures::pending!(); iced::futures::pending!();
} }
}), },
) )
})
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View file

@ -26,4 +26,8 @@ impl iced::Executor for Executor {
let _guard = self.0.enter(); let _guard = self.0.enter();
f() f()
} }
fn block_on<T>(&self, future: impl Future<Output = T>) -> T {
self.0.block_on(future)
}
} }

View file

@ -30,4 +30,8 @@ impl iced::Executor for Executor {
let _guard = self.0.enter(); let _guard = self.0.enter();
f() f()
} }
fn block_on<T>(&self, future: impl Future<Output = T>) -> T {
self.0.block_on(future)
}
} }

View file

@ -13,9 +13,8 @@ pub enum Desktop {
#[cold] #[cold]
pub fn desktop_settings() -> iced_futures::Subscription<Desktop> { pub fn desktop_settings() -> iced_futures::Subscription<Desktop> {
iced_futures::Subscription::run_with_id( iced_futures::Subscription::run(|| {
std::any::TypeId::of::<Desktop>(), stream::channel(10, |mut tx: futures::channel::mpsc::Sender<Desktop>| {
stream::channel(10, |mut tx| {
async move { async move {
let mut attempts = 0; let mut attempts = 0;
loop { loop {
@ -99,6 +98,6 @@ pub fn desktop_settings() -> iced_futures::Subscription<Desktop> {
} }
} }
} }
}), })
) })
} }

View file

@ -7,6 +7,7 @@ use crate::theme::{CosmicComponent, TRANSPARENT_COMPONENT, Theme};
use cosmic_theme::composite::over; use cosmic_theme::composite::over;
use iced::{ use iced::{
overlay::menu, overlay::menu,
theme::Base,
widget::{ widget::{
button as iced_button, checkbox as iced_checkbox, combo_box, container as iced_container, button as iced_button, checkbox as iced_checkbox, combo_box, container as iced_container,
pane_grid, pick_list, progress_bar, radio, rule, scrollable, pane_grid, pick_list, progress_bar, radio, rule, scrollable,
@ -15,7 +16,7 @@ use iced::{
}, },
}; };
use iced_core::{Background, Border, Color, Shadow, Vector}; use iced_core::{Background, Border, Color, Shadow, Vector};
use iced_widget::{pane_grid::Highlight, text_editor, text_input}; use iced_widget::{pane_grid::Highlight, scrollable::AutoScroll, text_editor, text_input};
use palette::WithAlpha; use palette::WithAlpha;
use std::rc::Rc; use std::rc::Rc;
@ -36,13 +37,13 @@ pub mod application {
} }
} }
pub fn appearance(theme: &Theme) -> Appearance { pub fn style(theme: &Theme) -> iced::theme::Style {
let cosmic = theme.cosmic(); let cosmic = theme.cosmic();
Appearance { iced::theme::Style {
icon_color: cosmic.bg_color().into(),
background_color: cosmic.bg_color().into(), background_color: cosmic.bg_color().into(),
text_color: cosmic.on_bg_color().into(), text_color: cosmic.on_bg_color().into(),
icon_color: cosmic.bg_color().into(),
} }
} }
} }
@ -422,6 +423,7 @@ impl<'a> Container<'a> {
..Default::default() ..Default::default()
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
} }
} }
@ -436,6 +438,7 @@ impl<'a> Container<'a> {
..Default::default() ..Default::default()
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
} }
} }
@ -450,6 +453,7 @@ impl<'a> Container<'a> {
..Default::default() ..Default::default()
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
} }
} }
} }
@ -493,6 +497,7 @@ impl iced_container::Catalog for Theme {
..Default::default() ..Default::default()
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
Container::List => { Container::List => {
@ -506,6 +511,7 @@ impl iced_container::Catalog for Theme {
..Default::default() ..Default::default()
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
} }
} }
@ -552,6 +558,7 @@ impl iced_container::Catalog for Theme {
.into(), .into(),
..Default::default() ..Default::default()
}, },
snap: true,
shadow: Shadow::default(), shadow: Shadow::default(),
} }
} }
@ -582,6 +589,7 @@ impl iced_container::Catalog for Theme {
radius: cosmic.corner_radii.radius_s.into(), radius: cosmic.corner_radii.radius_s.into(),
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
Container::Tooltip => iced_container::Style { Container::Tooltip => iced_container::Style {
@ -593,6 +601,7 @@ impl iced_container::Catalog for Theme {
..Default::default() ..Default::default()
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
Container::Card => { Container::Card => {
@ -610,6 +619,7 @@ impl iced_container::Catalog for Theme {
..Default::default() ..Default::default()
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
cosmic_theme::Layer::Primary => iced_container::Style { cosmic_theme::Layer::Primary => iced_container::Style {
icon_color: Some(Color::from(cosmic.primary.component.on)), icon_color: Some(Color::from(cosmic.primary.component.on)),
@ -622,6 +632,7 @@ impl iced_container::Catalog for Theme {
..Default::default() ..Default::default()
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
cosmic_theme::Layer::Secondary => iced_container::Style { cosmic_theme::Layer::Secondary => iced_container::Style {
icon_color: Some(Color::from(cosmic.secondary.component.on)), icon_color: Some(Color::from(cosmic.secondary.component.on)),
@ -634,6 +645,7 @@ impl iced_container::Catalog for Theme {
..Default::default() ..Default::default()
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
} }
} }
@ -652,6 +664,7 @@ impl iced_container::Catalog for Theme {
offset: Vector::new(0.0, 4.0), offset: Vector::new(0.0, 4.0),
blur_radius: 16.0, blur_radius: 16.0,
}, },
snap: true,
}, },
} }
} }
@ -791,6 +804,7 @@ impl menu::Catalog for Theme {
}, },
selected_text_color: cosmic.accent_text_color().into(), selected_text_color: cosmic.accent_text_color().into(),
selected_background: Background::Color(cosmic.background.component.hover.into()), selected_background: Background::Color(cosmic.background.component.hover.into()),
shadow: Default::default(),
} }
} }
} }
@ -830,7 +844,7 @@ impl pick_list::Catalog for Theme {
background: Background::Color(cosmic.background.base.into()), background: Background::Color(cosmic.background.base.into()),
..appearance ..appearance
}, },
pick_list::Status::Opened => appearance, pick_list::Status::Opened { is_hovered: _ } => appearance,
} }
} }
} }
@ -920,6 +934,8 @@ impl toggler::Catalog for Theme {
background_border_color: Color::TRANSPARENT, background_border_color: Color::TRANSPARENT,
foreground_border_width: 0.0, foreground_border_width: 0.0,
foreground_border_color: Color::TRANSPARENT, foreground_border_color: Color::TRANSPARENT,
text_color: None,
padding_ratio: 0.0,
}; };
match status { match status {
toggler::Status::Active { is_toggled } => active, toggler::Status::Active { is_toggled } => active,
@ -942,9 +958,9 @@ impl toggler::Catalog for Theme {
..active ..active
} }
} }
toggler::Status::Disabled => { toggler::Status::Disabled { is_toggled } => {
active.background.a /= 2.; active.background = active.background.scale_alpha(0.5);
active.foreground.a /= 2.; active.foreground = active.foreground.scale_alpha(0.5);
active active
} }
} }
@ -1086,21 +1102,21 @@ impl rule::Catalog for Theme {
match class { match class {
Rule::Default => rule::Style { Rule::Default => rule::Style {
color: self.current_container().divider.into(), color: self.current_container().divider.into(),
width: 1,
radius: 0.0.into(), radius: 0.0.into(),
fill_mode: rule::FillMode::Full, fill_mode: rule::FillMode::Full,
snap: true,
}, },
Rule::LightDivider => rule::Style { Rule::LightDivider => rule::Style {
color: self.current_container().divider.into(), color: self.current_container().divider.into(),
width: 1,
radius: 0.0.into(), radius: 0.0.into(),
fill_mode: rule::FillMode::Padded(8), fill_mode: rule::FillMode::Padded(8),
snap: true,
}, },
Rule::HeavyDivider => rule::Style { Rule::HeavyDivider => rule::Style {
color: self.current_container().divider.into(), color: self.current_container().divider.into(),
width: 4,
radius: 2.0.into(), radius: 2.0.into(),
fill_mode: rule::FillMode::Full, fill_mode: rule::FillMode::Full,
snap: true,
}, },
Rule::Custom(f) => f(self), Rule::Custom(f) => f(self),
} }
@ -1126,7 +1142,10 @@ impl scrollable::Catalog for Theme {
fn style(&self, class: &Self::Class<'_>, status: scrollable::Status) -> scrollable::Style { fn style(&self, class: &Self::Class<'_>, status: scrollable::Status) -> scrollable::Style {
match status { match status {
scrollable::Status::Active => { scrollable::Status::Active {
is_horizontal_scrollbar_disabled,
is_vertical_scrollbar_disabled,
} => {
let cosmic = self.cosmic(); let cosmic = self.cosmic();
let neutral_5 = cosmic.palette.neutral_5.with_alpha(0.7); let neutral_5 = cosmic.palette.neutral_5.with_alpha(0.7);
let neutral_6 = cosmic.palette.neutral_6.with_alpha(0.7); let neutral_6 = cosmic.palette.neutral_6.with_alpha(0.7);
@ -1139,7 +1158,7 @@ impl scrollable::Catalog for Theme {
}, },
background: None, background: None,
scroller: scrollable::Scroller { scroller: scrollable::Scroller {
color: if cosmic.is_dark { background: if cosmic.is_dark {
neutral_6.into() neutral_6.into()
} else { } else {
neutral_5.into() neutral_5.into()
@ -1157,7 +1176,7 @@ impl scrollable::Catalog for Theme {
}, },
background: None, background: None,
scroller: scrollable::Scroller { scroller: scrollable::Scroller {
color: if cosmic.is_dark { background: if cosmic.is_dark {
neutral_6.into() neutral_6.into()
} else { } else {
neutral_5.into() neutral_5.into()
@ -1169,6 +1188,13 @@ impl scrollable::Catalog for Theme {
}, },
}, },
gap: None, gap: None,
// TODO: what is auto scroll?
auto_scroll: AutoScroll {
background: Color::TRANSPARENT.into(),
border: Border::default(),
shadow: Shadow::default(),
icon: Color::TRANSPARENT.into(),
},
}; };
let small_widget_container = self.current_container().small_widget.with_alpha(0.7); let small_widget_container = self.current_container().small_widget.with_alpha(0.7);
@ -1200,7 +1226,7 @@ impl scrollable::Catalog for Theme {
}, },
background: None, background: None,
scroller: scrollable::Scroller { scroller: scrollable::Scroller {
color: if cosmic.is_dark { background: if cosmic.is_dark {
neutral_6.into() neutral_6.into()
} else { } else {
neutral_5.into() neutral_5.into()
@ -1218,7 +1244,7 @@ impl scrollable::Catalog for Theme {
}, },
background: None, background: None,
scroller: scrollable::Scroller { scroller: scrollable::Scroller {
color: if cosmic.is_dark { background: if cosmic.is_dark {
neutral_6.into() neutral_6.into()
} else { } else {
neutral_5.into() neutral_5.into()
@ -1230,6 +1256,13 @@ impl scrollable::Catalog for Theme {
}, },
}, },
gap: None, gap: None,
// TODO: what is auto scroll?
auto_scroll: AutoScroll {
background: Color::TRANSPARENT.into(),
border: Border::default(),
shadow: Shadow::default(),
icon: Color::TRANSPARENT.into(),
},
}; };
if matches!(class, Scrollable::Permanent) { if matches!(class, Scrollable::Permanent) {
@ -1400,7 +1433,7 @@ impl text_input::Catalog for Theme {
}, },
} }
} }
text_input::Status::Focused => { text_input::Status::Focused { is_hovered } => {
let bg = self.current_container().small_widget.with_alpha(0.25); let bg = self.current_container().small_widget.with_alpha(0.25);
match class { match class {
@ -1477,7 +1510,8 @@ impl iced_widget::text_editor::Catalog for Theme {
let selection = cosmic.accent.base.into(); let selection = cosmic.accent.base.into();
let value = cosmic.palette.neutral_9.into(); let value = cosmic.palette.neutral_9.into();
let placeholder = cosmic.palette.neutral_9.with_alpha(0.7).into(); let placeholder = cosmic.palette.neutral_9.with_alpha(0.7).into();
let icon = cosmic.background.on.into(); let icon: Color = cosmic.background.on.into();
// TODO do we need to add icon color back?
match status { match status {
iced_widget::text_editor::Status::Active iced_widget::text_editor::Status::Active
@ -1489,23 +1523,23 @@ impl iced_widget::text_editor::Catalog for Theme {
width: f32::from(cosmic.space_xxxs()), width: f32::from(cosmic.space_xxxs()),
color: iced::Color::from(cosmic.bg_divider()), color: iced::Color::from(cosmic.bg_divider()),
}, },
icon,
placeholder,
value,
selection,
},
iced_widget::text_editor::Status::Focused => iced_widget::text_editor::Style {
background: iced::Color::from(cosmic.bg_color()).into(),
border: Border {
radius: cosmic.corner_radii.radius_0.into(),
width: f32::from(cosmic.space_xxxs()),
color: iced::Color::from(cosmic.accent.base),
},
icon,
placeholder, placeholder,
value, value,
selection, selection,
}, },
iced_widget::text_editor::Status::Focused { is_hovered } => {
iced_widget::text_editor::Style {
background: iced::Color::from(cosmic.bg_color()).into(),
border: Border {
radius: cosmic.corner_radii.radius_0.into(),
width: f32::from(cosmic.space_xxxs()),
color: iced::Color::from(cosmic.accent.base),
},
placeholder,
value,
selection,
}
}
} }
} }
} }
@ -1522,6 +1556,21 @@ impl iced_widget::markdown::Catalog for Theme {
} }
} }
impl iced_widget::table::Catalog for Theme {
type Class<'a> = iced_widget::table::StyleFn<'a, Self>;
fn default<'a>() -> Self::Class<'a> {
Box::new(|theme| iced_widget::table::Style {
separator_x: theme.current_container().divider.into(),
separator_y: theme.current_container().divider.into(),
})
}
fn style(&self, class: &Self::Class<'_>) -> iced_widget::table::Style {
class(self)
}
}
#[cfg(feature = "qr_code")] #[cfg(feature = "qr_code")]
impl iced_widget::qr_code::Catalog for Theme { impl iced_widget::qr_code::Catalog for Theme {
type Class<'a> = iced_widget::qr_code::StyleFn<'a, Self>; type Class<'a> = iced_widget::qr_code::StyleFn<'a, Self>;
@ -1539,3 +1588,50 @@ impl iced_widget::qr_code::Catalog for Theme {
} }
impl combo_box::Catalog for Theme {} impl combo_box::Catalog for Theme {}
impl Base for Theme {
fn default(preference: iced::theme::Mode) -> Self {
match preference {
iced::theme::Mode::Light => Theme::light(),
iced::theme::Mode::Dark | iced::theme::Mode::None => Theme::dark(),
}
}
fn mode(&self) -> iced::theme::Mode {
if self.theme_type.is_dark() {
iced::theme::Mode::Dark
} else {
iced::theme::Mode::Light
}
}
fn base(&self) -> iced::theme::Style {
iced::theme::Style {
background_color: self.cosmic().bg_color().into(),
text_color: self.cosmic().on_bg_color().into(),
icon_color: self.cosmic().on_bg_color().into(),
}
}
fn palette(&self) -> Option<iced::theme::Palette> {
Some(iced::theme::Palette {
primary: self.cosmic().accent.base.into(),
success: self.cosmic().success.base.into(),
warning: self.cosmic().warning.base.into(),
danger: self.cosmic().destructive.base.into(),
background: iced::Color::from(self.cosmic().bg_color()),
text: iced::Color::from(self.cosmic().on_bg_color()),
})
}
fn name(&self) -> &str {
match &self.theme_type {
crate::theme::ThemeType::Dark => "Cosmic Dark Theme",
crate::theme::ThemeType::Light => "Cosmic Light Theme",
crate::theme::ThemeType::HighContrastDark => "Cosmic High Contrast Dark Theme",
crate::theme::ThemeType::HighContrastLight => "Cosmic High Contrast Light Theme",
crate::theme::ThemeType::Custom(theme) => "Custom Cosmic Theme",
crate::theme::ThemeType::System { prefer_dark, theme } => &theme.name,
}
}
}

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
Apply, Element, fl, Apply, Element, fl,
iced::{Alignment, Length}, iced::{Alignment, Length},
widget::{self, horizontal_space}, widget::{self, space},
}; };
#[derive(Debug, Default, Clone, derive_setters::Setters)] #[derive(Debug, Default, Clone, derive_setters::Setters)]
@ -99,7 +99,7 @@ pub fn about<'a, Message: Clone + 'static>(
let section_button = |name: &'a str, url: &'a str| -> Element<'a, Message> { let section_button = |name: &'a str, url: &'a str| -> Element<'a, Message> {
widget::row() widget::row()
.push(widget::text(name)) .push(widget::text(name))
.push(horizontal_space()) .push(space::horizontal())
.push_maybe( .push_maybe(
(!url.is_empty()).then_some(crate::widget::icon::from_name("link-symbolic").icon()), (!url.is_empty()).then_some(crate::widget::icon::from_name("link-symbolic").icon()),
) )

View file

@ -2,7 +2,7 @@
use iced::Size; use iced::Size;
use iced::widget::Container; use iced::widget::Container;
use iced_core::event::{self, Event}; use iced_core::event::Event;
use iced_core::layout; use iced_core::layout;
use iced_core::mouse; use iced_core::mouse;
use iced_core::overlay; use iced_core::overlay;
@ -172,7 +172,7 @@ where
} }
fn layout( fn layout(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
renderer: &Renderer, renderer: &Renderer,
limits: &layout::Limits, limits: &layout::Limits,
@ -186,7 +186,7 @@ where
} }
fn operate( fn operate(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
layout: Layout<'_>, layout: Layout<'_>,
renderer: &Renderer, renderer: &Renderer,
@ -195,18 +195,18 @@ where
self.container.operate(tree, layout, renderer, operation); self.container.operate(tree, layout, renderer, operation);
} }
fn on_event( fn update(
&mut self, &mut self,
tree: &mut Tree, tree: &mut Tree,
event: Event, 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: &Rectangle, viewport: &Rectangle,
) -> event::Status { ) {
self.container.on_event( self.container.update(
tree, tree,
event, event,
layout, layout,
@ -254,11 +254,13 @@ where
fn overlay<'b>( fn overlay<'b>(
&'b mut self, &'b mut self,
tree: &'b mut Tree, tree: &'b mut Tree,
layout: Layout<'_>, layout: Layout<'b>,
renderer: &Renderer, renderer: &Renderer,
viewport: &Rectangle,
translation: Vector, translation: Vector,
) -> Option<overlay::Element<'b, Message, crate::Theme, Renderer>> { ) -> Option<overlay::Element<'b, Message, crate::Theme, Renderer>> {
self.container.overlay(tree, layout, renderer, translation) self.container
.overlay(tree, layout, renderer, viewport, translation)
} }
#[cfg(feature = "a11y")] #[cfg(feature = "a11y")]

View file

@ -5,7 +5,7 @@ use iced_core::layout;
use iced_core::mouse; use iced_core::mouse;
use iced_core::overlay; use iced_core::overlay;
use iced_core::renderer; use iced_core::renderer;
use iced_core::widget::{Id, Tree}; use iced_core::widget::{Id, Operation, Tree};
use iced_core::{Clipboard, Element, Layout, Length, Rectangle, Shell, Vector, Widget}; use iced_core::{Clipboard, Element, Layout, Length, Rectangle, Shell, Vector, Widget};
pub use iced_widget::container::{Catalog, Style}; pub use iced_widget::container::{Catalog, Style};
@ -115,7 +115,7 @@ where
} }
fn layout( fn layout(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
renderer: &Renderer, renderer: &Renderer,
limits: &layout::Limits, limits: &layout::Limits,
@ -131,22 +131,23 @@ where
} }
let node = self let node = self
.content .content
.as_widget() .as_widget_mut()
.layout(&mut tree.children[0], renderer, &my_limits); .layout(&mut tree.children[0], renderer, &my_limits);
let size = node.size(); let size = node.size();
layout::Node::with_children(size, vec![node]) layout::Node::with_children(size, vec![node])
} }
fn operate( fn operate(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
layout: Layout<'_>, layout: Layout<'_>,
renderer: &Renderer, renderer: &Renderer,
operation: &mut dyn iced_core::widget::Operation<()>, operation: &mut dyn Operation,
) { ) {
operation.container(Some(&self.id), layout.bounds(), &mut |operation| { operation.container(Some(&self.id), layout.bounds());
self.content.as_widget().operate( operation.traverse(&mut |operation| {
&mut tree.children[0], self.content.as_widget_mut().operate(
tree,
layout layout
.children() .children()
.next() .next()
@ -158,17 +159,17 @@ where
}); });
} }
fn on_event( fn update(
&mut self, &mut self,
tree: &mut Tree, tree: &mut Tree,
event: Event, 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: &Rectangle, viewport: &Rectangle,
) -> event::Status { ) {
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
if matches!( if matches!(
event, event,
@ -179,9 +180,9 @@ where
let bounds = layout.bounds().size(); let bounds = layout.bounds().size();
clipboard.request_logical_window_size(bounds.width.max(1.), bounds.height.max(1.)); clipboard.request_logical_window_size(bounds.width.max(1.), bounds.height.max(1.));
} }
self.content.as_widget_mut().on_event( self.content.as_widget_mut().update(
&mut tree.children[0], &mut tree.children[0],
event.clone(), event,
layout layout
.children() .children()
.next() .next()
@ -238,8 +239,9 @@ where
fn overlay<'b>( fn overlay<'b>(
&'b mut self, &'b mut self,
tree: &'b mut Tree, tree: &'b mut Tree,
layout: Layout<'_>, layout: Layout<'b>,
renderer: &Renderer, renderer: &Renderer,
viewport: &Rectangle,
translation: Vector, translation: Vector,
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> { ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
self.content.as_widget_mut().overlay( self.content.as_widget_mut().overlay(
@ -250,6 +252,7 @@ where
.unwrap() .unwrap()
.with_virtual_offset(layout.virtual_offset()), .with_virtual_offset(layout.virtual_offset()),
renderer, renderer,
viewport,
translation, translation,
) )
} }

View file

@ -318,7 +318,7 @@ impl<'a, Message: 'a + Clone> Widget<Message, crate::Theme, crate::Renderer>
} }
fn layout( fn layout(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
renderer: &crate::Renderer, renderer: &crate::Renderer,
limits: &layout::Limits, limits: &layout::Limits,
@ -331,21 +331,22 @@ impl<'a, Message: 'a + Clone> Widget<Message, crate::Theme, crate::Renderer>
self.padding, self.padding,
|renderer, limits| { |renderer, limits| {
self.content self.content
.as_widget() .as_widget_mut()
.layout(&mut tree.children[0], renderer, limits) .layout(&mut tree.children[0], renderer, limits)
}, },
) )
} }
fn operate( fn operate(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
layout: Layout<'_>, layout: Layout<'_>,
renderer: &crate::Renderer, renderer: &crate::Renderer,
operation: &mut dyn Operation<()>, operation: &mut dyn Operation<()>,
) { ) {
operation.container(None, layout.bounds(), &mut |operation| { operation.container(None, layout.bounds());
self.content.as_widget().operate( operation.traverse(&mut |operation| {
self.content.as_widget_mut().operate(
&mut tree.children[0], &mut tree.children[0],
layout layout
.children() .children()
@ -357,20 +358,19 @@ impl<'a, Message: 'a + Clone> Widget<Message, crate::Theme, crate::Renderer>
); );
}); });
let state = tree.state.downcast_mut::<State>(); let state = tree.state.downcast_mut::<State>();
operation.focusable(state, Some(&self.id));
} }
fn on_event( fn update(
&mut self, &mut self,
tree: &mut Tree, tree: &mut Tree,
event: Event, event: &Event,
layout: Layout<'_>, layout: Layout<'_>,
cursor: mouse::Cursor, cursor: mouse::Cursor,
renderer: &crate::Renderer, renderer: &crate::Renderer,
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
viewport: &Rectangle, viewport: &Rectangle,
) -> event::Status { ) {
if let Variant::Image { if let Variant::Image {
on_remove: Some(on_remove), on_remove: Some(on_remove),
.. ..
@ -383,7 +383,8 @@ impl<'a, Message: 'a + Clone> Widget<Message, crate::Theme, crate::Renderer>
if let Some(position) = cursor.position() { if let Some(position) = cursor.position() {
if removal_bounds(layout.bounds(), 4.0).contains(position) { if removal_bounds(layout.bounds(), 4.0).contains(position) {
shell.publish(on_remove.clone()); shell.publish(on_remove.clone());
return event::Status::Captured; shell.capture_event();
return;
} }
} }
} }
@ -391,10 +392,9 @@ impl<'a, Message: 'a + Clone> Widget<Message, crate::Theme, crate::Renderer>
_ => (), _ => (),
} }
} }
self.content.as_widget_mut().update(
if self.content.as_widget_mut().on_event(
&mut tree.children[0], &mut tree.children[0],
event.clone(), event,
layout layout
.children() .children()
.next() .next()
@ -405,9 +405,9 @@ impl<'a, Message: 'a + Clone> Widget<Message, crate::Theme, crate::Renderer>
clipboard, clipboard,
shell, shell,
viewport, viewport,
) == event::Status::Captured );
{ if shell.is_event_captured() {
return event::Status::Captured; return;
} }
update( update(
@ -541,6 +541,7 @@ impl<'a, Message: 'a + Clone> Widget<Message, crate::Theme, crate::Renderer>
..Default::default() ..Default::default()
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
selection_background, selection_background,
); );
@ -554,7 +555,7 @@ impl<'a, Message: 'a + Clone> Widget<Message, crate::Theme, crate::Renderer>
y: bounds.y + (bounds.height - 18.0 - styling.border_width), y: bounds.y + (bounds.height - 18.0 - styling.border_width),
}; };
if bounds.intersects(viewport) { if bounds.intersects(viewport) {
iced_core::svg::Renderer::draw_svg(renderer, svg_handle, bounds); iced_core::svg::Renderer::draw_svg(renderer, svg_handle, bounds, bounds);
} }
} }
@ -570,6 +571,7 @@ impl<'a, Message: 'a + Clone> Widget<Message, crate::Theme, crate::Renderer>
radius: c_rad.radius_m.into(), radius: c_rad.radius_m.into(),
..Default::default() ..Default::default()
}, },
snap: true,
}, },
selection_background, selection_background,
); );
@ -583,6 +585,12 @@ impl<'a, Message: 'a + Clone> Widget<Message, crate::Theme, crate::Renderer>
x: bounds.x + 4.0, x: bounds.x + 4.0,
y: bounds.y + 4.0, y: bounds.y + 4.0,
}, },
Rectangle {
width: 16.0,
height: 16.0,
x: bounds.x + 4.0,
y: bounds.y + 4.0,
},
); );
} }
} }
@ -609,8 +617,9 @@ impl<'a, Message: 'a + Clone> Widget<Message, crate::Theme, crate::Renderer>
fn overlay<'b>( fn overlay<'b>(
&'b mut self, &'b mut self,
tree: &'b mut Tree, tree: &'b mut Tree,
layout: Layout<'_>, layout: Layout<'b>,
renderer: &crate::Renderer, renderer: &crate::Renderer,
viewport: &Rectangle,
mut translation: Vector, mut translation: Vector,
) -> Option<overlay::Element<'b, Message, crate::Theme, crate::Renderer>> { ) -> Option<overlay::Element<'b, Message, crate::Theme, crate::Renderer>> {
let position = layout.bounds().position(); let position = layout.bounds().position();
@ -624,6 +633,7 @@ impl<'a, Message: 'a + Clone> Widget<Message, crate::Theme, crate::Renderer>
.unwrap() .unwrap()
.with_virtual_offset(layout.virtual_offset()), .with_virtual_offset(layout.virtual_offset()),
renderer, renderer,
viewport,
translation, translation,
) )
} }
@ -638,7 +648,7 @@ impl<'a, Message: 'a + Clone> Widget<Message, crate::Theme, crate::Renderer>
) -> iced_accessibility::A11yTree { ) -> iced_accessibility::A11yTree {
use iced_accessibility::{ use iced_accessibility::{
A11yNode, A11yTree, A11yNode, A11yTree,
accesskit::{Action, DefaultActionVerb, NodeBuilder, NodeId, Rect, Role}, accesskit::{Action, Node, NodeId, Rect, Role},
}; };
// TODO why is state None sometimes? // TODO why is state None sometimes?
if matches!(state.state, iced_core::widget::tree::State::None) { if matches!(state.state, iced_core::widget::tree::State::None) {
@ -658,12 +668,12 @@ impl<'a, Message: 'a + Clone> Widget<Message, crate::Theme, crate::Renderer>
let bounds = Rect::new(x as f64, y as f64, (x + width) as f64, (y + height) as f64); let bounds = Rect::new(x as f64, y as f64, (x + width) as f64, (y + height) as f64);
let is_hovered = state.state.downcast_ref::<State>().is_hovered; let is_hovered = state.state.downcast_ref::<State>().is_hovered;
let mut node = NodeBuilder::new(Role::Button); let mut node = Node::new(Role::Button);
node.add_action(Action::Focus); node.add_action(Action::Focus);
node.add_action(Action::Default); node.add_action(Action::Click);
node.set_bounds(bounds); node.set_bounds(bounds);
if let Some(name) = self.name.as_ref() { if let Some(name) = self.name.as_ref() {
node.set_name(name.clone()); node.set_label(name.clone());
} }
match self.description.as_ref() { match self.description.as_ref() {
Some(iced_accessibility::Description::Id(id)) => { Some(iced_accessibility::Description::Id(id)) => {
@ -682,10 +692,10 @@ impl<'a, Message: 'a + Clone> Widget<Message, crate::Theme, crate::Renderer>
if self.on_press.is_none() { if self.on_press.is_none() {
node.set_disabled(); node.set_disabled();
} }
if is_hovered { // TODO hover
node.set_hovered(); // if is_hovered {
} // node.set_hovered();
node.set_default_action_verb(DefaultActionVerb::Click); // }
if let Some(child_tree) = child_tree.map(|child_tree| { if let Some(child_tree) = child_tree.map(|child_tree| {
self.content.as_widget().a11y_nodes( self.content.as_widget().a11y_nodes(
@ -761,14 +771,14 @@ impl State {
#[allow(clippy::needless_pass_by_value, clippy::too_many_arguments)] #[allow(clippy::needless_pass_by_value, clippy::too_many_arguments)]
pub fn update<'a, Message: Clone>( pub fn update<'a, Message: Clone>(
_id: Id, _id: Id,
event: Event, event: &Event,
layout: Layout<'_>, layout: Layout<'_>,
cursor: mouse::Cursor, cursor: mouse::Cursor,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
on_press: Option<&dyn Fn(Vector, Rectangle) -> Message>, on_press: Option<&dyn Fn(Vector, Rectangle) -> Message>,
on_press_down: Option<&dyn Fn(Vector, Rectangle) -> Message>, on_press_down: Option<&dyn Fn(Vector, Rectangle) -> Message>,
state: impl FnOnce() -> &'a mut State, state: impl FnOnce() -> &'a mut State,
) -> event::Status { ) {
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 { .. }) => {
@ -787,7 +797,8 @@ pub fn update<'a, Message: Clone>(
shell.publish(msg); shell.publish(msg);
} }
return event::Status::Captured; shell.capture_event();
return;
} }
} }
} }
@ -806,7 +817,8 @@ pub fn update<'a, Message: Clone>(
shell.publish(msg); shell.publish(msg);
} }
return event::Status::Captured; shell.capture_event();
return;
} }
} else if on_press_down.is_some() { } else if on_press_down.is_some() {
let state = state(); let state = state();
@ -816,7 +828,7 @@ pub fn update<'a, Message: Clone>(
#[cfg(feature = "a11y")] #[cfg(feature = "a11y")]
Event::A11y(event_id, iced_accessibility::accesskit::ActionRequest { action, .. }) => { Event::A11y(event_id, iced_accessibility::accesskit::ActionRequest { action, .. }) => {
let state = state(); let state = state();
if let Some(on_press) = matches!(action, iced_accessibility::accesskit::Action::Default) if let Some(on_press) = matches!(action, iced_accessibility::accesskit::Action::Click)
.then_some(on_press) .then_some(on_press)
.flatten() .flatten()
{ {
@ -825,17 +837,19 @@ pub fn update<'a, Message: Clone>(
shell.publish(msg); shell.publish(msg);
} }
return event::Status::Captured; shell.capture_event();
return;
} }
Event::Keyboard(keyboard::Event::KeyPressed { key, .. }) => { Event::Keyboard(keyboard::Event::KeyPressed { key, .. }) => {
if let Some(on_press) = on_press { if let Some(on_press) = on_press {
let state = state(); let state = state();
if state.is_focused && key == keyboard::Key::Named(keyboard::key::Named::Enter) { if state.is_focused && *key == keyboard::Key::Named(keyboard::key::Named::Enter) {
state.is_pressed = true; state.is_pressed = true;
let msg = (on_press)(layout.virtual_offset(), layout.bounds()); let msg = (on_press)(layout.virtual_offset(), layout.bounds());
shell.publish(msg); shell.publish(msg);
return event::Status::Captured; shell.capture_event();
return;
} }
} }
} }
@ -846,8 +860,6 @@ pub fn update<'a, Message: Clone>(
} }
_ => {} _ => {}
} }
event::Status::Ignored
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
@ -879,6 +891,7 @@ pub fn draw<Renderer: iced_core::Renderer, Theme>(
radius: styling.border_radius, radius: styling.border_radius,
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
Color::TRANSPARENT, Color::TRANSPARENT,
); );
@ -900,6 +913,7 @@ pub fn draw<Renderer: iced_core::Renderer, Theme>(
..Default::default() ..Default::default()
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
Background::Color([0.0, 0.0, 0.0, 0.5].into()), Background::Color([0.0, 0.0, 0.0, 0.5].into()),
); );
@ -915,6 +929,7 @@ pub fn draw<Renderer: iced_core::Renderer, Theme>(
..Default::default() ..Default::default()
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
background, background,
); );
@ -930,6 +945,7 @@ pub fn draw<Renderer: iced_core::Renderer, Theme>(
..Default::default() ..Default::default()
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
overlay, overlay,
); );
@ -953,6 +969,7 @@ pub fn draw<Renderer: iced_core::Renderer, Theme>(
radius: styling.border_radius, radius: styling.border_radius,
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
Color::TRANSPARENT, Color::TRANSPARENT,
); );

View file

@ -213,7 +213,9 @@ where
let content_list = column::with_children([ let content_list = column::with_children([
row::with_children([ row::with_children([
column().push(date).push(day).into(), column().push(date).push(day).into(),
crate::widget::Space::with_width(Length::Fill).into(), crate::widget::space::horizontal()
.width(Length::Fill)
.into(),
month_controls.into(), month_controls.into(),
]) ])
.align_y(Vertical::Center) .align_y(Vertical::Center)

View file

@ -26,7 +26,10 @@ use iced_core::{
}; };
use iced_widget::slider::HandleShape; use iced_widget::slider::HandleShape;
use iced_widget::{Row, canvas, column, horizontal_space, row, scrollable, vertical_space}; use iced_widget::{
Row, canvas, column, row, scrollable,
space::{horizontal, vertical},
};
use palette::{FromColor, RgbHue}; use palette::{FromColor, RgbHue};
use super::divider::horizontal; use super::divider::horizontal;
@ -334,7 +337,7 @@ where
.width(self.width), .width(self.width),
// canvas with gradient for the current color // canvas with gradient for the current color
// still needs the canvas and the handle to be drawn on it // still needs the canvas and the handle to be drawn on it
container(vertical_space().height(self.height)) container(vertical().height(self.height))
.width(self.width) .width(self.width)
.height(self.height), .height(self.height),
slider( slider(
@ -548,13 +551,13 @@ where
} }
fn layout( fn layout(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
renderer: &crate::Renderer, renderer: &crate::Renderer,
limits: &layout::Limits, limits: &layout::Limits,
) -> layout::Node { ) -> layout::Node {
self.inner self.inner
.as_widget() .as_widget_mut()
.layout(&mut tree.children[0], renderer, limits) .layout(&mut tree.children[0], renderer, limits)
} }
@ -657,6 +660,7 @@ where
radius: (1.0 + handle_radius).into(), radius: (1.0 + handle_radius).into(),
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
Color::TRANSPARENT, Color::TRANSPARENT,
); );
@ -674,6 +678,7 @@ where
radius: handle_radius.into(), radius: handle_radius.into(),
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
Color::TRANSPARENT, Color::TRANSPARENT,
); );
@ -684,26 +689,31 @@ where
fn overlay<'b>( fn overlay<'b>(
&'b mut self, &'b mut self,
state: &'b mut Tree, state: &'b mut Tree,
layout: Layout<'_>, layout: Layout<'b>,
renderer: &crate::Renderer, renderer: &crate::Renderer,
viewport: &Rectangle,
translation: Vector, translation: Vector,
) -> Option<iced_core::overlay::Element<'b, Message, crate::Theme, crate::Renderer>> { ) -> Option<iced_core::overlay::Element<'b, Message, crate::Theme, crate::Renderer>> {
self.inner self.inner.as_widget_mut().overlay(
.as_widget_mut() &mut state.children[0],
.overlay(&mut state.children[0], layout, renderer, translation) layout,
renderer,
viewport,
translation,
)
} }
fn on_event( fn update(
&mut self, &mut self,
tree: &mut Tree, tree: &mut Tree,
event: Event, event: &Event,
layout: Layout<'_>, layout: Layout<'_>,
cursor: mouse::Cursor, cursor: mouse::Cursor,
renderer: &crate::Renderer, renderer: &crate::Renderer,
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
viewport: &Rectangle, viewport: &Rectangle,
) -> event::Status { ) {
// if the pointer is performing a drag, intercept pointer motion and button events // if the pointer is performing a drag, intercept pointer motion and button events
// else check if event is handled by child elements // else check if event is handled by child elements
// if the event is not handled by a child element, check if it is over the canvas when pressing a button // if the event is not handled by a child element, check if it is over the canvas when pressing a button
@ -732,24 +742,26 @@ where
shell.publish((self.on_update)(ColorPickerUpdate::ActionFinished)); shell.publish((self.on_update)(ColorPickerUpdate::ActionFinished));
state.dragging = false; state.dragging = false;
} }
_ => return event::Status::Ignored, _ => return,
}; };
return event::Status::Captured; shell.capture_event();
return;
} }
let column_tree = &mut tree.children[0]; let column_tree = &mut tree.children[0];
if self.inner.as_widget_mut().on_event( self.inner.as_widget_mut().update(
column_tree, column_tree,
event.clone(), &event,
column_layout, column_layout,
cursor, cursor,
renderer, renderer,
clipboard, clipboard,
shell, shell,
viewport, viewport,
) == event::Status::Captured );
{ if shell.is_event_captured() {
return event::Status::Captured; shell.capture_event();
return;
} }
match event { match event {
@ -764,12 +776,10 @@ where
state.dragging = true; state.dragging = true;
let hsv: palette::Hsv = palette::Hsv::new(self.active_color.hue, s, v); let hsv: palette::Hsv = palette::Hsv::new(self.active_color.hue, s, v);
shell.publish((self.on_update)(ColorPickerUpdate::ActiveColor(hsv))); shell.publish((self.on_update)(ColorPickerUpdate::ActiveColor(hsv)));
event::Status::Captured shell.capture_event();
} else {
event::Status::Ignored
} }
} }
_ => event::Status::Ignored, _ => {}
} }
} }
@ -812,12 +822,12 @@ pub fn color_button<'a, Message: Clone + 'static>(
let spacing = THEME.lock().unwrap().cosmic().spacing; let spacing = THEME.lock().unwrap().cosmic().spacing;
button::custom(if color.is_some() { button::custom(if color.is_some() {
Element::from(vertical_space().height(Length::Fixed(f32::from(spacing.space_s)))) Element::from(vertical().height(Length::Fixed(f32::from(spacing.space_s))))
} else { } else {
Element::from(column![ Element::from(column![
vertical_space().height(Length::FillPortion(6)), vertical().height(Length::FillPortion(6)),
row![ row![
horizontal_space().width(Length::FillPortion(6)), horizontal().width(Length::FillPortion(6)),
Icon::from( Icon::from(
icon::from_name("list-add-symbolic") icon::from_name("list-add-symbolic")
.prefer_svg(true) .prefer_svg(true)
@ -827,11 +837,11 @@ pub fn color_button<'a, Message: Clone + 'static>(
.width(icon_portion) .width(icon_portion)
.height(Length::Fill) .height(Length::Fill)
.content_fit(iced_core::ContentFit::Contain), .content_fit(iced_core::ContentFit::Contain),
horizontal_space().width(Length::FillPortion(6)), horizontal().width(Length::FillPortion(6)),
] ]
.height(icon_portion) .height(icon_portion)
.width(Length::Fill), .width(Length::Fill),
vertical_space().height(Length::FillPortion(6)), vertical().height(Length::FillPortion(6)),
]) ])
}) })
.width(Length::Fixed(f32::from(spacing.space_s))) .width(Length::Fixed(f32::from(spacing.space_s)))

View file

@ -7,7 +7,7 @@ use iced::advanced::layout::{self, Layout};
use iced::advanced::widget::{self, Operation}; use iced::advanced::widget::{self, Operation};
use iced::advanced::{Clipboard, Shell}; use iced::advanced::{Clipboard, Shell};
use iced::advanced::{overlay, renderer}; use iced::advanced::{overlay, renderer};
use iced::{Event, Point, Rectangle, Size, event, mouse}; use iced::{Event, Point, Size, mouse};
use iced_core::Renderer; use iced_core::Renderer;
pub(super) struct Overlay<'a, 'b, Message> { pub(super) struct Overlay<'a, 'b, Message> {
@ -29,7 +29,7 @@ where
let node = self let node = self
.content .content
.as_widget() .as_widget_mut()
.layout(self.tree, renderer, &limits); .layout(self.tree, renderer, &limits);
let node_size = node.size(); let node_size = node.size();
@ -47,16 +47,16 @@ where
}) })
} }
fn on_event( fn update(
&mut self, &mut self,
event: Event, event: &Event,
layout: Layout<'_>, layout: Layout<'_>,
cursor: mouse::Cursor, cursor: mouse::Cursor,
renderer: &crate::Renderer, renderer: &crate::Renderer,
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
) -> event::Status { ) {
self.content.as_widget_mut().on_event( self.content.as_widget_mut().update(
self.tree, self.tree,
event, event,
layout, layout,
@ -104,9 +104,10 @@ where
&self, &self,
layout: Layout<'_>, layout: Layout<'_>,
cursor: mouse::Cursor, cursor: mouse::Cursor,
viewport: &Rectangle,
renderer: &crate::Renderer, renderer: &crate::Renderer,
) -> mouse::Interaction { ) -> mouse::Interaction {
// TODO how to handle viewport here?
let viewport = &layout.bounds();
self.content self.content
.as_widget() .as_widget()
.mouse_interaction(self.tree, layout, cursor, viewport, renderer) .mouse_interaction(self.tree, layout, cursor, viewport, renderer)
@ -114,11 +115,17 @@ where
fn overlay<'c>( fn overlay<'c>(
&'c mut self, &'c mut self,
layout: Layout<'_>, layout: Layout<'c>,
renderer: &crate::Renderer, renderer: &crate::Renderer,
) -> Option<overlay::Element<'c, Message, crate::Theme, crate::Renderer>> { ) -> Option<overlay::Element<'c, Message, crate::Theme, crate::Renderer>> {
self.content let viewport = &layout.bounds();
.as_widget_mut()
.overlay(self.tree, layout, renderer, iced::Vector::default()) self.content.as_widget_mut().overlay(
self.tree,
layout,
renderer,
viewport,
iced::Vector::default(),
)
} }
} }

View file

@ -7,7 +7,7 @@ use crate::{Apply, Element, Renderer, Theme, fl};
use std::borrow::Cow; use std::borrow::Cow;
use iced_core::Alignment; use iced_core::Alignment;
use iced_core::event::{self, Event}; use iced_core::event::Event;
use iced_core::widget::{Operation, Tree}; use iced_core::widget::{Operation, Tree};
use iced_core::{ use iced_core::{
Clipboard, Layout, Length, Rectangle, Shell, Vector, Widget, layout, mouse, Clipboard, Layout, Length, Rectangle, Shell, Vector, Widget, layout, mouse,
@ -65,7 +65,7 @@ impl<'a, Message: Clone + 'static> ContextDrawer<'a, Message> {
} else { } else {
let title = title let title = title
.map(|title| text::title4(title).width(Length::Fill).apply(Element::from)) .map(|title| text::title4(title).width(Length::Fill).apply(Element::from))
.unwrap_or_else(|| widget::horizontal_space().apply(Element::from)); .unwrap_or_else(|| widget::space::horizontal().apply(Element::from));
(title, None) (title, None)
}; };
@ -196,40 +196,40 @@ impl<Message: Clone> Widget<Message, crate::Theme, Renderer> for ContextDrawer<'
} }
fn layout( fn layout(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
renderer: &Renderer, renderer: &Renderer,
limits: &layout::Limits, limits: &layout::Limits,
) -> layout::Node { ) -> layout::Node {
self.content self.content
.as_widget() .as_widget_mut()
.layout(&mut tree.children[0], renderer, limits) .layout(&mut tree.children[0], renderer, limits)
} }
fn operate( fn operate(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
layout: Layout<'_>, layout: Layout<'_>,
renderer: &Renderer, renderer: &Renderer,
operation: &mut dyn Operation<()>, operation: &mut dyn Operation<()>,
) { ) {
self.content self.content
.as_widget() .as_widget_mut()
.operate(&mut tree.children[0], layout, renderer, operation); .operate(&mut tree.children[0], layout, renderer, operation);
} }
fn on_event( fn update(
&mut self, &mut self,
tree: &mut Tree, tree: &mut Tree,
event: Event, event: &Event,
layout: Layout<'_>, layout: Layout<'_>,
cursor: mouse::Cursor, cursor: mouse::Cursor,
renderer: &Renderer, renderer: &Renderer,
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
viewport: &Rectangle, viewport: &Rectangle,
) -> event::Status { ) {
self.content.as_widget_mut().on_event( self.content.as_widget_mut().update(
&mut tree.children[0], &mut tree.children[0],
event, event,
layout, layout,
@ -282,8 +282,9 @@ impl<Message: Clone> Widget<Message, crate::Theme, Renderer> for ContextDrawer<'
fn overlay<'b>( fn overlay<'b>(
&'b mut self, &'b mut self,
tree: &'b mut Tree, tree: &'b mut Tree,
layout: Layout<'_>, layout: Layout<'b>,
_renderer: &Renderer, _renderer: &Renderer,
_viewport: &Rectangle,
translation: Vector, translation: Vector,
) -> Option<iced_overlay::Element<'b, Message, crate::Theme, Renderer>> { ) -> Option<iced_overlay::Element<'b, Message, crate::Theme, Renderer>> {
let bounds = layout.bounds(); let bounds = layout.bounds();

View file

@ -270,13 +270,13 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, crate::Renderer>
} }
fn layout( fn layout(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
renderer: &crate::Renderer, renderer: &crate::Renderer,
limits: &iced_core::layout::Limits, limits: &iced_core::layout::Limits,
) -> iced_core::layout::Node { ) -> iced_core::layout::Node {
self.content self.content
.as_widget() .as_widget_mut()
.layout(&mut tree.children[0], renderer, limits) .layout(&mut tree.children[0], renderer, limits)
} }
@ -302,29 +302,29 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, crate::Renderer>
} }
fn operate( fn operate(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
layout: iced_core::Layout<'_>, layout: iced_core::Layout<'_>,
renderer: &crate::Renderer, renderer: &crate::Renderer,
operation: &mut dyn iced_core::widget::Operation<()>, operation: &mut dyn iced_core::widget::Operation<()>,
) { ) {
self.content self.content
.as_widget() .as_widget_mut()
.operate(&mut tree.children[0], layout, renderer, operation); .operate(&mut tree.children[0], layout, renderer, operation);
} }
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
fn on_event( fn update(
&mut self, &mut self,
tree: &mut Tree, tree: &mut Tree,
event: iced::Event, event: &iced::Event,
layout: iced_core::Layout<'_>, layout: iced_core::Layout<'_>,
cursor: iced_core::mouse::Cursor, cursor: iced_core::mouse::Cursor,
renderer: &crate::Renderer, renderer: &crate::Renderer,
clipboard: &mut dyn iced_core::Clipboard, clipboard: &mut dyn iced_core::Clipboard,
shell: &mut iced_core::Shell<'_, Message>, shell: &mut iced_core::Shell<'_, Message>,
viewport: &iced::Rectangle, viewport: &iced::Rectangle,
) -> iced_core::event::Status { ) {
let state = tree.state.downcast_mut::<LocalState>(); let state = tree.state.downcast_mut::<LocalState>();
let bounds = layout.bounds(); let bounds = layout.bounds();
@ -384,7 +384,7 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, crate::Renderer>
match event { match event {
Event::Touch(touch::Event::FingerPressed { id, .. }) => { Event::Touch(touch::Event::FingerPressed { id, .. }) => {
state.fingers_pressed.insert(id); state.fingers_pressed.insert(*id);
} }
Event::Touch(touch::Event::FingerLifted { id, .. }) => { Event::Touch(touch::Event::FingerLifted { id, .. }) => {
@ -410,7 +410,8 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, crate::Renderer>
self.create_popup(layout, cursor, renderer, shell, viewport, state); self.create_popup(layout, cursor, renderer, shell, viewport, state);
} }
return event::Status::Captured; shell.capture_event();
return;
} else if !was_open && right_button_released(&event) } else if !was_open && right_button_released(&event)
|| (touch_lifted(&event)) || (touch_lifted(&event))
|| left_button_released(&event) || left_button_released(&event)
@ -440,7 +441,7 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, crate::Renderer>
}); });
} }
} }
self.content.as_widget_mut().on_event( self.content.as_widget_mut().update(
&mut tree.children[0], &mut tree.children[0],
event, event,
layout, layout,
@ -457,6 +458,7 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, crate::Renderer>
tree: &'b mut Tree, tree: &'b mut Tree,
layout: iced_core::Layout<'_>, layout: iced_core::Layout<'_>,
_renderer: &crate::Renderer, _renderer: &crate::Renderer,
viewport: &iced::Rectangle,
translation: Vector, translation: Vector,
) -> Option<iced_core::overlay::Element<'b, Message, crate::Theme, crate::Renderer>> { ) -> Option<iced_core::overlay::Element<'b, Message, crate::Theme, crate::Renderer>> {
#[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))] #[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))]

View file

@ -123,7 +123,7 @@ impl<'a, Message: Clone + 'static> From<Dialog<'a, Message>> for Element<'a, Mes
if let Some(body) = dialog.body { if let Some(body) = dialog.body {
if should_space { if should_space {
content_col = content_col content_col = content_col
.push(widget::vertical_space().height(Length::Fixed(space_xxs.into()))); .push(widget::space::vertical().height(Length::Fixed(space_xxs.into())));
} }
content_col = content_col.push( content_col = content_col.push(
widget::container(widget::scrollable(widget::text::body(body))).max_height(300.), widget::container(widget::scrollable(widget::text::body(body))).max_height(300.),
@ -133,7 +133,7 @@ impl<'a, Message: Clone + 'static> From<Dialog<'a, Message>> for Element<'a, Mes
for control in dialog.controls { for control in dialog.controls {
if should_space { if should_space {
content_col = content_col content_col = content_col
.push(widget::vertical_space().height(Length::Fixed(space_s.into()))); .push(widget::space::vertical().height(Length::Fixed(space_s.into())));
} }
content_col = content_col.push(control); content_col = content_col.push(control);
should_space = true; should_space = true;
@ -149,7 +149,7 @@ impl<'a, Message: Clone + 'static> From<Dialog<'a, Message>> for Element<'a, Mes
if let Some(button) = dialog.tertiary_action { if let Some(button) = dialog.tertiary_action {
button_row = button_row.push(button); button_row = button_row.push(button);
} }
button_row = button_row.push(widget::horizontal_space()); button_row = button_row.push(widget::space::horizontal());
if let Some(button) = dialog.secondary_action { if let Some(button) = dialog.secondary_action {
button_row = button_row.push(button); button_row = button_row.push(button);
} }

View file

@ -303,43 +303,43 @@ impl<Message: 'static> Widget<Message, crate::Theme, crate::Renderer>
} }
fn layout( fn layout(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
renderer: &crate::Renderer, renderer: &crate::Renderer,
limits: &layout::Limits, limits: &layout::Limits,
) -> layout::Node { ) -> layout::Node {
self.container self.container
.as_widget() .as_widget_mut()
.layout(&mut tree.children[0], renderer, limits) .layout(&mut tree.children[0], renderer, limits)
} }
fn operate( fn operate(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
layout: layout::Layout<'_>, layout: layout::Layout<'_>,
renderer: &crate::Renderer, renderer: &crate::Renderer,
operation: &mut dyn iced_core::widget::Operation<()>, operation: &mut dyn iced_core::widget::Operation<()>,
) { ) {
self.container self.container
.as_widget() .as_widget_mut()
.operate(&mut tree.children[0], layout, renderer, operation); .operate(&mut tree.children[0], layout, renderer, operation);
} }
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
fn on_event( fn update(
&mut self, &mut self,
tree: &mut Tree, tree: &mut Tree,
event: Event, event: &Event,
layout: layout::Layout<'_>, layout: layout::Layout<'_>,
cursor: mouse::Cursor, cursor: mouse::Cursor,
renderer: &crate::Renderer, renderer: &crate::Renderer,
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
viewport: &Rectangle, viewport: &Rectangle,
) -> event::Status { ) {
let s = self.container.as_widget_mut().on_event( let s = self.container.as_widget_mut().update(
&mut tree.children[0], &mut tree.children[0],
event.clone(), event,
layout, layout,
cursor, cursor,
renderer, renderer,
@ -347,8 +347,8 @@ impl<Message: 'static> Widget<Message, crate::Theme, crate::Renderer>
shell, shell,
viewport, viewport,
); );
if matches!(s, event::Status::Captured) { if shell.is_event_captured() {
return event::Status::Captured; return;
} }
let state = tree.state.downcast_mut::<State<()>>(); let state = tree.state.downcast_mut::<State<()>>();
@ -367,23 +367,23 @@ impl<Message: 'static> Widget<Message, crate::Theme, crate::Renderer>
OfferEvent::Enter { OfferEvent::Enter {
x, y, mime_types, .. x, y, mime_types, ..
}, },
)) if id == Some(my_id) => { )) if *id == Some(my_id) => {
if !self.mime_matches(&mime_types) { if !self.mime_matches(&mime_types) {
log::trace!( log::trace!(
target: DND_DEST_LOG_TARGET, target: DND_DEST_LOG_TARGET,
"offer enter id={my_id:?} ignored (mimes={mime_types:?} not in {:?})", "offer enter id={my_id:?} ignored (mimes={mime_types:?} not in {:?})",
self.mime_types self.mime_types
); );
return event::Status::Ignored; return;
} }
log::trace!( log::trace!(
target: DND_DEST_LOG_TARGET, target: DND_DEST_LOG_TARGET,
"offer enter id={my_id:?} coords=({x},{y}) mimes={mime_types:?}" "offer enter id={my_id:?} coords=({x},{y}) mimes={mime_types:?}"
); );
if let Some(msg) = state.on_enter( if let Some(msg) = state.on_enter(
x, *x,
y, *y,
mime_types, mime_types.clone(),
self.on_enter.as_ref().map(std::convert::AsRef::as_ref), self.on_enter.as_ref().map(std::convert::AsRef::as_ref),
(), (),
) { ) {
@ -391,13 +391,13 @@ impl<Message: 'static> Widget<Message, crate::Theme, crate::Renderer>
} }
if self.forward_drag_as_cursor { if self.forward_drag_as_cursor {
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
let drag_cursor = mouse::Cursor::Available((x as f32, y as f32).into()); let drag_cursor = mouse::Cursor::Available((*x as f32, *y as f32).into());
let event = Event::Mouse(mouse::Event::CursorMoved { let event = Event::Mouse(mouse::Event::CursorMoved {
position: drag_cursor.position().unwrap(), position: drag_cursor.position().unwrap(),
}); });
self.container.as_widget_mut().on_event( self.container.as_widget_mut().update(
&mut tree.children[0], &mut tree.children[0],
event, &event,
layout, layout,
drag_cursor, drag_cursor,
renderer, renderer,
@ -406,7 +406,8 @@ impl<Message: 'static> Widget<Message, crate::Theme, crate::Renderer>
viewport, viewport,
); );
} }
return event::Status::Captured; shell.capture_event();
return;
} }
Event::Dnd(DndEvent::Offer(_, OfferEvent::Leave)) => { Event::Dnd(DndEvent::Offer(_, OfferEvent::Leave)) => {
log::trace!( log::trace!(
@ -423,9 +424,9 @@ impl<Message: 'static> Widget<Message, crate::Theme, crate::Renderer>
if self.forward_drag_as_cursor { if self.forward_drag_as_cursor {
let drag_cursor = mouse::Cursor::Unavailable; let drag_cursor = mouse::Cursor::Unavailable;
let event = Event::Mouse(mouse::Event::CursorLeft); let event = Event::Mouse(mouse::Event::CursorLeft);
self.container.as_widget_mut().on_event( self.container.as_widget_mut().update(
&mut tree.children[0], &mut tree.children[0],
event, &event,
layout, layout,
drag_cursor, drag_cursor,
renderer, renderer,
@ -434,16 +435,16 @@ impl<Message: 'static> Widget<Message, crate::Theme, crate::Renderer>
viewport, viewport,
); );
} }
return event::Status::Ignored; return;
} }
Event::Dnd(DndEvent::Offer(id, OfferEvent::Motion { x, y })) if id == Some(my_id) => { Event::Dnd(DndEvent::Offer(id, OfferEvent::Motion { x, y })) if *id == Some(my_id) => {
log::trace!( log::trace!(
target: DND_DEST_LOG_TARGET, target: DND_DEST_LOG_TARGET,
"offer motion id={my_id:?} coords=({x},{y})" "offer motion id={my_id:?} coords=({x},{y})"
); );
if let Some(msg) = state.on_motion( if let Some(msg) = state.on_motion(
x, *x,
y, *y,
self.on_motion.as_ref().map(std::convert::AsRef::as_ref), self.on_motion.as_ref().map(std::convert::AsRef::as_ref),
self.on_enter.as_ref().map(std::convert::AsRef::as_ref), self.on_enter.as_ref().map(std::convert::AsRef::as_ref),
(), (),
@ -453,13 +454,13 @@ impl<Message: 'static> Widget<Message, crate::Theme, crate::Renderer>
if self.forward_drag_as_cursor { if self.forward_drag_as_cursor {
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
let drag_cursor = mouse::Cursor::Available((x as f32, y as f32).into()); let drag_cursor = mouse::Cursor::Available((*x as f32, *y as f32).into());
let event = Event::Mouse(mouse::Event::CursorMoved { let event = Event::Mouse(mouse::Event::CursorMoved {
position: drag_cursor.position().unwrap(), position: drag_cursor.position().unwrap(),
}); });
self.container.as_widget_mut().on_event( self.container.as_widget_mut().update(
&mut tree.children[0], &mut tree.children[0],
event, &event,
layout, layout,
drag_cursor, drag_cursor,
renderer, renderer,
@ -468,7 +469,8 @@ impl<Message: 'static> Widget<Message, crate::Theme, crate::Renderer>
viewport, viewport,
); );
} }
return event::Status::Captured; shell.capture_event();
return;
} }
Event::Dnd(DndEvent::Offer(_, OfferEvent::LeaveDestination)) => { Event::Dnd(DndEvent::Offer(_, OfferEvent::LeaveDestination)) => {
log::trace!( log::trace!(
@ -481,9 +483,9 @@ impl<Message: 'static> Widget<Message, crate::Theme, crate::Renderer>
{ {
shell.publish(msg); shell.publish(msg);
} }
return event::Status::Ignored; return;
} }
Event::Dnd(DndEvent::Offer(id, OfferEvent::Drop)) if id == Some(my_id) => { Event::Dnd(DndEvent::Offer(id, OfferEvent::Drop)) if *id == Some(my_id) => {
log::trace!( log::trace!(
target: DND_DEST_LOG_TARGET, target: DND_DEST_LOG_TARGET,
"offer drop id={my_id:?}" "offer drop id={my_id:?}"
@ -493,27 +495,29 @@ impl<Message: 'static> Widget<Message, crate::Theme, crate::Renderer>
{ {
shell.publish(msg); shell.publish(msg);
} }
return event::Status::Captured; shell.capture_event();
return;
} }
Event::Dnd(DndEvent::Offer(id, OfferEvent::SelectedAction(action))) Event::Dnd(DndEvent::Offer(id, OfferEvent::SelectedAction(action)))
if id == Some(my_id) => if *id == Some(my_id) =>
{ {
log::trace!( log::trace!(
target: DND_DEST_LOG_TARGET, target: DND_DEST_LOG_TARGET,
"offer selected-action id={my_id:?} action={action:?}" "offer selected-action id={my_id:?} action={action:?}"
); );
if let Some(msg) = state.on_action_selected( if let Some(msg) = state.on_action_selected(
action, *action,
self.on_action_selected self.on_action_selected
.as_ref() .as_ref()
.map(std::convert::AsRef::as_ref), .map(std::convert::AsRef::as_ref),
) { ) {
shell.publish(msg); shell.publish(msg);
} }
return event::Status::Captured; shell.capture_event();
return;
} }
Event::Dnd(DndEvent::Offer(id, OfferEvent::Data { data, mime_type })) Event::Dnd(DndEvent::Offer(id, OfferEvent::Data { data, mime_type }))
if id == Some(my_id) => if *id == Some(my_id) =>
{ {
log::trace!( log::trace!(
target: DND_DEST_LOG_TARGET, target: DND_DEST_LOG_TARGET,
@ -531,21 +535,28 @@ impl<Message: 'static> Widget<Message, crate::Theme, crate::Renderer>
} }
if let (Some(msg), ret) = state.on_data_received( if let (Some(msg), ret) = state.on_data_received(
mime_type, mime_type.clone(),
data, data.clone(),
self.on_data_received self.on_data_received
.as_ref() .as_ref()
.map(std::convert::AsRef::as_ref), .map(std::convert::AsRef::as_ref),
self.on_finish.as_ref().map(std::convert::AsRef::as_ref), self.on_finish.as_ref().map(std::convert::AsRef::as_ref),
) { ) {
shell.publish(msg); shell.publish(msg);
return ret; if ret == event::Status::Captured {
log::trace!(
target: DND_DEST_LOG_TARGET,
"offer data id={my_id:?} captured"
);
shell.capture_event();
}
return;
} }
return event::Status::Captured; shell.capture_event();
return;
} }
_ => {} _ => {}
} }
event::Status::Ignored
} }
fn mouse_interaction( fn mouse_interaction(
@ -589,13 +600,18 @@ impl<Message: 'static> Widget<Message, crate::Theme, crate::Renderer>
fn overlay<'b>( fn overlay<'b>(
&'b mut self, &'b mut self,
tree: &'b mut Tree, tree: &'b mut Tree,
layout: layout::Layout<'_>, layout: layout::Layout<'b>,
renderer: &crate::Renderer, renderer: &crate::Renderer,
viewport: &Rectangle,
translation: Vector, translation: Vector,
) -> Option<overlay::Element<'b, Message, crate::Theme, crate::Renderer>> { ) -> Option<overlay::Element<'b, Message, crate::Theme, crate::Renderer>> {
self.container self.container.as_widget_mut().overlay(
.as_widget_mut() &mut tree.children[0],
.overlay(&mut tree.children[0], layout, renderer, translation) layout,
renderer,
viewport,
translation,
)
} }
fn drag_destinations( fn drag_destinations(

View file

@ -1,6 +1,6 @@
use std::any::Any; use std::any::Any;
use iced_core::window; use iced_core::{widget::Operation, window};
use crate::{ use crate::{
Element, Element,
@ -176,7 +176,7 @@ impl<Message: Clone + 'static, D: iced::clipboard::mime::AsMimeTypes + std::mark
} }
fn layout( fn layout(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
renderer: &crate::Renderer, renderer: &crate::Renderer,
limits: &layout::Limits, limits: &layout::Limits,
@ -184,41 +184,48 @@ impl<Message: Clone + 'static, D: iced::clipboard::mime::AsMimeTypes + std::mark
let state = tree.state.downcast_mut::<State>(); let state = tree.state.downcast_mut::<State>();
let node = self let node = self
.container .container
.as_widget() .as_widget_mut()
.layout(&mut tree.children[0], renderer, limits); .layout(&mut tree.children[0], renderer, limits);
state.cached_bounds = node.bounds(); state.cached_bounds = node.bounds();
node node
} }
fn operate( fn operate(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
layout: layout::Layout<'_>, layout: layout::Layout<'_>,
renderer: &crate::Renderer, renderer: &crate::Renderer,
operation: &mut dyn iced_core::widget::Operation<()>, operation: &mut dyn Operation,
) { ) {
operation.custom((&mut tree.state) as &mut dyn Any, Some(&self.id)); operation.container(Some(&self.id), layout.bounds());
operation.container(Some(&self.id), layout.bounds(), &mut |operation| { operation.traverse(&mut |operation| {
self.container self.container.as_widget_mut().operate(
.as_widget() tree,
.operate(&mut tree.children[0], layout, renderer, operation) layout
.children()
.next()
.unwrap()
.with_virtual_offset(layout.virtual_offset()),
renderer,
operation,
);
}); });
} }
fn on_event( fn update(
&mut self, &mut self,
tree: &mut Tree, tree: &mut Tree,
event: Event, event: &Event,
layout: layout::Layout<'_>, layout: layout::Layout<'_>,
cursor: mouse::Cursor, cursor: mouse::Cursor,
renderer: &crate::Renderer, renderer: &crate::Renderer,
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
viewport: &Rectangle, viewport: &Rectangle,
) -> event::Status { ) {
let ret = self.container.as_widget_mut().on_event( let ret = self.container.as_widget_mut().update(
&mut tree.children[0], &mut tree.children[0],
event.clone(), &event,
layout, layout,
cursor, cursor,
renderer, renderer,
@ -238,14 +245,16 @@ impl<Message: Clone + 'static, D: iced::clipboard::mime::AsMimeTypes + std::mark
} }
state.left_pressed_position = Some(position); state.left_pressed_position = Some(position);
return event::Status::Captured; shell.capture_event();
return;
} }
} }
mouse::Event::ButtonReleased(mouse::Button::Left) mouse::Event::ButtonReleased(mouse::Button::Left)
if state.left_pressed_position.is_some() => if state.left_pressed_position.is_some() =>
{ {
state.left_pressed_position = None; state.left_pressed_position = None;
return event::Status::Captured; shell.capture_event();
return;
} }
mouse::Event::CursorMoved { .. } => { mouse::Event::CursorMoved { .. } => {
if let Some(position) = cursor.position() { if let Some(position) = cursor.position() {
@ -277,7 +286,8 @@ impl<Message: Clone + 'static, D: iced::clipboard::mime::AsMimeTypes + std::mark
} else if cursor.is_over(layout.bounds()) { } else if cursor.is_over(layout.bounds()) {
state.hovered = true; state.hovered = true;
} }
return event::Status::Captured; shell.capture_event();
return;
} }
} }
_ => return ret, _ => return ret,
@ -288,7 +298,8 @@ impl<Message: Clone + 'static, D: iced::clipboard::mime::AsMimeTypes + std::mark
shell.publish(m.clone()); shell.publish(m.clone());
} }
state.is_dragging = false; state.is_dragging = false;
return event::Status::Captured; shell.capture_event();
return;
} }
return ret; return ret;
} }
@ -298,7 +309,8 @@ impl<Message: Clone + 'static, D: iced::clipboard::mime::AsMimeTypes + std::mark
shell.publish(m.clone()); shell.publish(m.clone());
} }
state.is_dragging = false; state.is_dragging = false;
return event::Status::Captured; shell.capture_event();
return;
} }
return ret; return ret;
} }
@ -352,13 +364,18 @@ impl<Message: Clone + 'static, D: iced::clipboard::mime::AsMimeTypes + std::mark
fn overlay<'b>( fn overlay<'b>(
&'b mut self, &'b mut self,
tree: &'b mut Tree, tree: &'b mut Tree,
layout: layout::Layout<'_>, layout: layout::Layout<'b>,
renderer: &crate::Renderer, renderer: &crate::Renderer,
viewport: &Rectangle,
translation: Vector, translation: Vector,
) -> Option<overlay::Element<'b, Message, crate::Theme, crate::Renderer>> { ) -> Option<overlay::Element<'b, Message, crate::Theme, crate::Renderer>> {
self.container self.container.as_widget_mut().overlay(
.as_widget_mut() &mut tree.children[0],
.overlay(&mut tree.children[0], layout, renderer, translation) layout,
renderer,
viewport,
translation,
)
} }
fn drag_destinations( fn drag_destinations(

View file

@ -213,7 +213,7 @@ impl<'a, Message: Clone + 'a> Overlay<'a, Message> {
} }
} }
fn _layout(&self, renderer: &crate::Renderer, bounds: Size) -> layout::Node { fn _layout(&mut self, renderer: &crate::Renderer, bounds: Size) -> layout::Node {
let space_below = bounds.height - (self.position.y + self.target_height); let space_below = bounds.height - (self.position.y + self.target_height);
let space_above = self.position.y; let space_above = self.position.y;
@ -242,19 +242,19 @@ impl<'a, Message: Clone + 'a> Overlay<'a, Message> {
}) })
} }
fn _on_event( fn _update(
&mut self, &mut self,
event: Event, event: &Event,
layout: Layout<'_>, layout: Layout<'_>,
cursor: mouse::Cursor, cursor: mouse::Cursor,
renderer: &crate::Renderer, renderer: &crate::Renderer,
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
) -> event::Status { ) {
let bounds = layout.bounds(); let bounds = layout.bounds();
self.state.with_data_mut(|tree| { self.state.with_data_mut(|tree| {
self.container.on_event( self.container.update(
tree, event, layout, cursor, renderer, clipboard, shell, &bounds, tree, event, layout, cursor, renderer, clipboard, shell, &bounds,
) )
}) })
@ -293,6 +293,7 @@ impl<'a, Message: Clone + 'a> Overlay<'a, Message> {
radius: appearance.border_radius, radius: appearance.border_radius,
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
appearance.background, appearance.background,
); );
@ -311,26 +312,25 @@ impl<'a, Message: Clone + 'a> iced_core::Overlay<Message, crate::Theme, crate::R
self._layout(renderer, bounds) self._layout(renderer, bounds)
} }
fn on_event( fn update(
&mut self, &mut self,
event: Event, event: &Event,
layout: Layout<'_>, layout: Layout<'_>,
cursor: mouse::Cursor, cursor: mouse::Cursor,
renderer: &crate::Renderer, renderer: &crate::Renderer,
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
) -> event::Status { ) {
self._on_event(event, layout, cursor, renderer, clipboard, shell) self._update(event, layout, cursor, renderer, clipboard, shell)
} }
fn mouse_interaction( fn mouse_interaction(
&self, &self,
layout: Layout<'_>, layout: Layout<'_>,
cursor: mouse::Cursor, cursor: mouse::Cursor,
viewport: &Rectangle,
renderer: &crate::Renderer, renderer: &crate::Renderer,
) -> mouse::Interaction { ) -> mouse::Interaction {
self._mouse_interaction(layout, cursor, viewport, renderer) self._mouse_interaction(layout, cursor, &layout.bounds(), renderer)
} }
fn draw( fn draw(
@ -353,7 +353,7 @@ impl<'a, Message: Clone + 'a> crate::widget::Widget<Message, crate::Theme, crate
} }
fn layout( fn layout(
&self, &mut self,
_tree: &mut iced_core::widget::Tree, _tree: &mut iced_core::widget::Tree,
renderer: &crate::Renderer, renderer: &crate::Renderer,
limits: &iced::Limits, limits: &iced::Limits,
@ -375,18 +375,18 @@ impl<'a, Message: Clone + 'a> crate::widget::Widget<Message, crate::Theme, crate
self._mouse_interaction(layout, cursor, viewport, renderer) self._mouse_interaction(layout, cursor, viewport, renderer)
} }
fn on_event( fn update(
&mut self, &mut self,
_tree: &mut Tree, _tree: &mut Tree,
event: Event, event: &Event,
layout: Layout<'_>, layout: Layout<'_>,
cursor: mouse::Cursor, cursor: mouse::Cursor,
renderer: &crate::Renderer, renderer: &crate::Renderer,
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
_viewport: &Rectangle, _viewport: &Rectangle,
) -> event::Status { ) {
self._on_event(event, layout, cursor, renderer, clipboard, shell) self._update(event, layout, cursor, renderer, clipboard, shell)
} }
fn draw( fn draw(
@ -435,7 +435,7 @@ where
} }
fn layout( fn layout(
&self, &mut self,
_tree: &mut Tree, _tree: &mut Tree,
renderer: &crate::Renderer, renderer: &crate::Renderer,
limits: &layout::Limits, limits: &layout::Limits,
@ -452,7 +452,7 @@ where
let size = { let size = {
let intrinsic = Size::new( let intrinsic = Size::new(
0.0, 0.0,
(f32::from(text_line_height) + self.padding.vertical()) * self.options.len() as f32, (f32::from(text_line_height) + self.padding.y()) * self.options.len() as f32,
); );
limits.resolve(Length::Fill, Length::Shrink, intrinsic) limits.resolve(Length::Fill, Length::Shrink, intrinsic)
@ -461,17 +461,17 @@ where
layout::Node::new(size) layout::Node::new(size)
} }
fn on_event( fn update(
&mut self, &mut self,
_state: &mut Tree, _state: &mut Tree,
event: Event, event: &Event,
layout: Layout<'_>, layout: Layout<'_>,
cursor: mouse::Cursor, cursor: mouse::Cursor,
renderer: &crate::Renderer, renderer: &crate::Renderer,
_clipboard: &mut dyn Clipboard, _clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
_viewport: &Rectangle, _viewport: &Rectangle,
) -> event::Status { ) {
match event { match event {
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => { Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => {
let hovered_guard = self.hovered_option.lock().unwrap(); let hovered_guard = self.hovered_option.lock().unwrap();
@ -481,7 +481,8 @@ where
if let Some(close_on_selected) = self.close_on_selected.as_ref() { if let Some(close_on_selected) = self.close_on_selected.as_ref() {
shell.publish(close_on_selected.clone()); shell.publish(close_on_selected.clone());
} }
return event::Status::Captured; shell.capture_event();
return;
} }
} }
} }
@ -493,7 +494,7 @@ where
let option_height = let option_height =
f32::from(self.text_line_height.to_absolute(Pixels(text_size))) f32::from(self.text_line_height.to_absolute(Pixels(text_size)))
+ self.padding.vertical(); + self.padding.y();
let new_hovered_option = (cursor_position.y / option_height) as usize; let new_hovered_option = (cursor_position.y / option_height) as usize;
let mut hovered_guard = self.hovered_option.lock().unwrap(); let mut hovered_guard = self.hovered_option.lock().unwrap();
@ -515,7 +516,7 @@ where
let option_height = let option_height =
f32::from(self.text_line_height.to_absolute(Pixels(text_size))) f32::from(self.text_line_height.to_absolute(Pixels(text_size)))
+ self.padding.vertical(); + self.padding.y();
let mut hovered_guard = self.hovered_option.lock().unwrap(); let mut hovered_guard = self.hovered_option.lock().unwrap();
*hovered_guard = Some((cursor_position.y / option_height) as usize); *hovered_guard = Some((cursor_position.y / option_height) as usize);
@ -525,14 +526,13 @@ where
if let Some(close_on_selected) = self.close_on_selected.as_ref() { if let Some(close_on_selected) = self.close_on_selected.as_ref() {
shell.publish(close_on_selected.clone()); shell.publish(close_on_selected.clone());
} }
return event::Status::Captured; shell.capture_event();
return;
} }
} }
} }
_ => {} _ => {}
} }
event::Status::Ignored
} }
fn mouse_interaction( fn mouse_interaction(
@ -568,8 +568,8 @@ where
let text_size = self let text_size = self
.text_size .text_size
.unwrap_or_else(|| text::Renderer::default_size(renderer).0); .unwrap_or_else(|| text::Renderer::default_size(renderer).0);
let option_height = f32::from(self.text_line_height.to_absolute(Pixels(text_size))) let option_height =
+ self.padding.vertical(); f32::from(self.text_line_height.to_absolute(Pixels(text_size))) + self.padding.y();
let offset = viewport.y - bounds.y; let offset = viewport.y - bounds.y;
let start = (offset / option_height) as usize; let start = (offset / option_height) as usize;
@ -605,6 +605,7 @@ where
..Default::default() ..Default::default()
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
appearance.selected_background, appearance.selected_background,
); );
@ -614,16 +615,13 @@ where
.color(appearance.selected_text_color) .color(appearance.selected_text_color)
.border_radius(appearance.border_radius); .border_radius(appearance.border_radius);
svg::Renderer::draw_svg( let bounds = Rectangle {
renderer, x: item_x + item_width - 16.0 - 8.0,
svg_handle, y: bounds.y + (bounds.height / 2.0 - 8.0),
Rectangle { width: 16.0,
x: item_x + item_width - 16.0 - 8.0, height: 16.0,
y: bounds.y + (bounds.height / 2.0 - 8.0), };
width: 16.0, svg::Renderer::draw_svg(renderer, svg_handle, bounds, bounds);
height: 16.0,
},
);
(appearance.selected_text_color, crate::font::semibold()) (appearance.selected_text_color, crate::font::semibold())
} else if *hovered_guard == Some(i) { } else if *hovered_guard == Some(i) {
@ -642,6 +640,7 @@ where
..Default::default() ..Default::default()
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
appearance.hovered_background, appearance.hovered_background,
); );
@ -678,8 +677,8 @@ where
size: Pixels(text_size), size: Pixels(text_size),
line_height: self.text_line_height, line_height: self.text_line_height,
font, font,
horizontal_alignment: alignment::Horizontal::Left, align_x: text::Alignment::Left,
vertical_alignment: alignment::Vertical::Center, align_y: alignment::Vertical::Center,
shaping: text::Shaping::Advanced, shaping: text::Shaping::Advanced,
wrapping: text::Wrapping::default(), wrapping: text::Wrapping::default(),
ellipsize: text::Ellipsize::default(), ellipsize: text::Ellipsize::default(),

View file

@ -56,12 +56,12 @@ pub fn popup_dropdown<
dropdown dropdown
} }
/// Produces a [`Task`] that closes the [`Dropdown`]. // /// Produces a [`Task`] that closes the [`Dropdown`].
pub fn close<Message: 'static>(id: Id) -> iced_runtime::Task<Message> { // pub fn close<Message: 'static>(id: Id) -> iced_runtime::Task<Message> {
iced_runtime::task::effect(iced_runtime::Action::Widget(Box::new(operation::close(id)))) // iced_runtime::task::effect(iced_runtime::Action::Widget(Box::new(operation::close(id))))
} // }
/// Produces a [`Task`] that opens the [`Dropdown`]. // /// Produces a [`Task`] that opens the [`Dropdown`].
pub fn open<Message: 'static>(id: Id) -> iced_runtime::Task<Message> { // pub fn open<Message: 'static>(id: Id) -> iced_runtime::Task<Message> {
iced_runtime::task::effect(iced_runtime::Action::Widget(Box::new(operation::open(id)))) // iced_runtime::task::effect(iced_runtime::Action::Widget(Box::new(operation::open(id))))
} // }

View file

@ -209,18 +209,18 @@ impl<Message> iced_core::Overlay<Message, crate::Theme, crate::Renderer> for Ove
}) })
} }
fn on_event( fn update(
&mut self, &mut self,
event: Event, event: &Event,
layout: Layout<'_>, layout: Layout<'_>,
cursor: mouse::Cursor, cursor: mouse::Cursor,
renderer: &crate::Renderer, renderer: &crate::Renderer,
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
) -> event::Status { ) {
let bounds = layout.bounds(); let bounds = layout.bounds();
self.container.on_event( self.container.update(
self.state, event, layout, cursor, renderer, clipboard, shell, &bounds, self.state, event, layout, cursor, renderer, clipboard, shell, &bounds,
) )
} }
@ -229,11 +229,10 @@ impl<Message> iced_core::Overlay<Message, crate::Theme, crate::Renderer> for Ove
&self, &self,
layout: Layout<'_>, layout: Layout<'_>,
cursor: mouse::Cursor, cursor: mouse::Cursor,
viewport: &Rectangle,
renderer: &crate::Renderer, renderer: &crate::Renderer,
) -> mouse::Interaction { ) -> mouse::Interaction {
self.container self.container
.mouse_interaction(self.state, layout, cursor, viewport, renderer) .mouse_interaction(self.state, layout, cursor, &layout.bounds(), renderer)
} }
fn draw( fn draw(
@ -256,6 +255,7 @@ impl<Message> iced_core::Overlay<Message, crate::Theme, crate::Renderer> for Ove
radius: appearance.border_radius, radius: appearance.border_radius,
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
appearance.background, appearance.background,
); );
@ -287,7 +287,7 @@ where
} }
fn layout( fn layout(
&self, &mut self,
_tree: &mut Tree, _tree: &mut Tree,
renderer: &crate::Renderer, renderer: &crate::Renderer,
limits: &layout::Limits, limits: &layout::Limits,
@ -309,7 +309,7 @@ where
) )
}); });
let vertical_padding = self.padding.vertical(); let vertical_padding = self.padding.y();
let text_line_height = f32::from(text_line_height); let text_line_height = f32::from(text_line_height);
let size = { let size = {
@ -328,17 +328,17 @@ where
layout::Node::new(size) layout::Node::new(size)
} }
fn on_event( fn update(
&mut self, &mut self,
_state: &mut Tree, _state: &mut Tree,
event: Event, event: &Event,
layout: Layout<'_>, layout: Layout<'_>,
cursor: mouse::Cursor, cursor: mouse::Cursor,
renderer: &crate::Renderer, renderer: &crate::Renderer,
_clipboard: &mut dyn Clipboard, _clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
_viewport: &Rectangle, _viewport: &Rectangle,
) -> event::Status { ) {
let bounds = layout.bounds(); let bounds = layout.bounds();
match event { match event {
@ -346,7 +346,8 @@ where
if cursor.is_over(bounds) { if cursor.is_over(bounds) {
if let Some(item) = self.hovered_option.as_ref() { if let Some(item) = self.hovered_option.as_ref() {
shell.publish((self.on_selected)(item.clone())); shell.publish((self.on_selected)(item.clone()));
return event::Status::Captured; shell.capture_event();
return;
} }
} }
} }
@ -361,7 +362,7 @@ where
let heights = self let heights = self
.options .options
.element_heights(self.padding.vertical(), text_line_height); .element_heights(self.padding.y(), text_line_height);
let mut current_offset = 0.0; let mut current_offset = 0.0;
@ -408,7 +409,7 @@ where
let heights = self let heights = self
.options .options
.element_heights(self.padding.vertical(), text_line_height); .element_heights(self.padding.y(), text_line_height);
let mut current_offset = 0.0; let mut current_offset = 0.0;
@ -446,8 +447,6 @@ where
} }
_ => {} _ => {}
} }
event::Status::Ignored
} }
fn mouse_interaction( fn mouse_interaction(
@ -490,7 +489,7 @@ where
let text_line_height = f32::from(self.text_line_height.to_absolute(Pixels(text_size))); let text_line_height = f32::from(self.text_line_height.to_absolute(Pixels(text_size)));
let visible_options = self.options.visible_options( let visible_options = self.options.visible_options(
self.padding.vertical(), self.padding.y(),
text_line_height, text_line_height,
offset, offset,
viewport.height, viewport.height,
@ -528,24 +527,23 @@ where
..Default::default() ..Default::default()
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
appearance.selected_background, appearance.selected_background,
); );
let svg_bounds = Rectangle {
x: item_x + item_width - 16.0 - 8.0,
y: bounds.y + (bounds.height / 2.0 - 8.0),
width: 16.0,
height: 16.0,
};
let svg_handle = let svg_handle =
svg::Svg::new(crate::widget::common::object_select().clone()) svg::Svg::new(crate::widget::common::object_select().clone())
.color(appearance.selected_text_color) .color(appearance.selected_text_color)
.border_radius(appearance.border_radius); .border_radius(appearance.border_radius);
svg::Renderer::draw_svg( svg::Renderer::draw_svg(renderer, svg_handle, svg_bounds, svg_bounds);
renderer,
svg_handle,
Rectangle {
x: item_x + item_width - 16.0 - 8.0,
y: bounds.y + (bounds.height / 2.0 - 8.0),
width: 16.0,
height: 16.0,
},
);
(appearance.selected_text_color, crate::font::semibold()) (appearance.selected_text_color, crate::font::semibold())
} else if self.hovered_option.as_ref() == Some(item) { } else if self.hovered_option.as_ref() == Some(item) {
@ -566,6 +564,7 @@ where
..Default::default() ..Default::default()
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
appearance.hovered_background, appearance.hovered_background,
); );
@ -590,8 +589,8 @@ where
size: iced::Pixels(text_size), size: iced::Pixels(text_size),
line_height: self.text_line_height, line_height: self.text_line_height,
font, font,
horizontal_alignment: alignment::Horizontal::Left, align_x: text::Alignment::Left,
vertical_alignment: alignment::Vertical::Center, align_y: alignment::Vertical::Center,
shaping: text::Shaping::Advanced, shaping: text::Shaping::Advanced,
wrapping: text::Wrapping::default(), wrapping: text::Wrapping::default(),
ellipsize: text::Ellipsize::default(), ellipsize: text::Ellipsize::default(),
@ -611,7 +610,7 @@ where
}) })
.move_to(Point { .move_to(Point {
x: bounds.x, x: bounds.x,
y: bounds.y + (self.padding.vertical() / 2.0) - 4.0, y: bounds.y + (self.padding.y() / 2.0) - 4.0,
}); });
Widget::<Message, crate::Theme, crate::Renderer>::draw( Widget::<Message, crate::Theme, crate::Renderer>::draw(
@ -640,8 +639,8 @@ where
size: iced::Pixels(text_size), size: iced::Pixels(text_size),
line_height: text::LineHeight::Absolute(Pixels(text_line_height + 4.0)), line_height: text::LineHeight::Absolute(Pixels(text_line_height + 4.0)),
font: crate::font::default(), font: crate::font::default(),
horizontal_alignment: alignment::Horizontal::Center, align_x: text::Alignment::Center,
vertical_alignment: alignment::Vertical::Center, align_y: alignment::Vertical::Center,
shaping: text::Shaping::Advanced, shaping: text::Shaping::Advanced,
wrapping: text::Wrapping::default(), wrapping: text::Wrapping::default(),
ellipsize: text::Ellipsize::default(), ellipsize: text::Ellipsize::default(),

View file

@ -78,7 +78,7 @@ impl<'a, S: AsRef<str>, Message: 'a, Item: Clone + PartialEq + 'static>
} }
fn layout( fn layout(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
renderer: &crate::Renderer, renderer: &crate::Renderer,
limits: &layout::Limits, limits: &layout::Limits,
@ -116,17 +116,17 @@ impl<'a, S: AsRef<str>, Message: 'a, Item: Clone + PartialEq + 'static>
) )
} }
fn on_event( fn update(
&mut self, &mut self,
tree: &mut Tree, tree: &mut Tree,
event: Event, event: &Event,
layout: Layout<'_>, layout: Layout<'_>,
cursor: mouse::Cursor, cursor: mouse::Cursor,
_renderer: &crate::Renderer, _renderer: &crate::Renderer,
_clipboard: &mut dyn Clipboard, _clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
_viewport: &Rectangle, _viewport: &Rectangle,
) -> event::Status { ) {
update( update(
&event, &event,
layout, layout,
@ -183,8 +183,9 @@ impl<'a, S: AsRef<str>, Message: 'a, Item: Clone + PartialEq + 'static>
fn overlay<'b>( fn overlay<'b>(
&'b mut self, &'b mut self,
tree: &'b mut Tree, tree: &'b mut Tree,
layout: Layout<'_>, layout: Layout<'b>,
renderer: &crate::Renderer, renderer: &crate::Renderer,
_viewport: &Rectangle,
translation: Vector, translation: Vector,
) -> Option<overlay::Element<'b, Message, crate::Theme, crate::Renderer>> { ) -> Option<overlay::Element<'b, Message, crate::Theme, crate::Renderer>> {
let state = tree.state.downcast_mut::<State<Item>>(); let state = tree.state.downcast_mut::<State<Item>>();
@ -275,8 +276,8 @@ pub fn layout(
size: iced::Pixels(text_size), size: iced::Pixels(text_size),
line_height: text_line_height, line_height: text_line_height,
font: font.unwrap_or_else(crate::font::default), font: font.unwrap_or_else(crate::font::default),
horizontal_alignment: alignment::Horizontal::Left, align_x: text::Alignment::Left,
vertical_alignment: alignment::Vertical::Top, align_y: alignment::Vertical::Top,
shaping: text::Shaping::Advanced, shaping: text::Shaping::Advanced,
wrapping: text::Wrapping::default(), wrapping: text::Wrapping::default(),
ellipsize: text::Ellipsize::default(), ellipsize: text::Ellipsize::default(),
@ -314,7 +315,7 @@ pub fn update<'a, S: AsRef<str>, Message, Item: Clone + PartialEq + 'static + 'a
on_selected: &dyn Fn(Item) -> Message, on_selected: &dyn Fn(Item) -> Message,
selections: &super::Model<S, Item>, selections: &super::Model<S, Item>,
state: impl FnOnce() -> &'a mut State<Item>, state: impl FnOnce() -> &'a mut State<Item>,
) -> event::Status { ) {
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 { .. }) => {
@ -325,14 +326,12 @@ pub fn update<'a, S: AsRef<str>, Message, Item: Clone + PartialEq + 'static + 'a
// bounds or on the drop-down, either way we close the overlay. // bounds or on the drop-down, either way we close the overlay.
state.is_open = false; state.is_open = false;
event::Status::Captured shell.capture_event();
} else if cursor.is_over(layout.bounds()) { } else if cursor.is_over(layout.bounds()) {
state.is_open = true; state.is_open = true;
state.hovered_option = selections.selected.clone(); state.hovered_option = selections.selected.clone();
event::Status::Captured shell.capture_event();
} else {
event::Status::Ignored
} }
} }
Event::Mouse(mouse::Event::WheelScrolled { Event::Mouse(mouse::Event::WheelScrolled {
@ -348,19 +347,15 @@ pub fn update<'a, S: AsRef<str>, Message, Item: Clone + PartialEq + 'static + 'a
shell.publish((on_selected)(option.1.clone())); shell.publish((on_selected)(option.1.clone()));
} }
event::Status::Captured shell.capture_event();
} else {
event::Status::Ignored
} }
} }
Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) => { Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) => {
let state = state(); let state = state();
state.keyboard_modifiers = *modifiers; state.keyboard_modifiers = *modifiers;
event::Status::Ignored
} }
_ => event::Status::Ignored, _ => {}
} }
} }
@ -420,8 +415,8 @@ pub fn overlay<'a, S: AsRef<str>, Message: 'a, Item: Clone + PartialEq + 'static
size: iced::Pixels(text_size), size: iced::Pixels(text_size),
line_height, line_height,
font: font.unwrap_or_else(crate::font::default), font: font.unwrap_or_else(crate::font::default),
horizontal_alignment: alignment::Horizontal::Left, align_x: text::Alignment::Left,
vertical_alignment: alignment::Vertical::Top, align_y: alignment::Vertical::Top,
shaping: text::Shaping::Advanced, shaping: text::Shaping::Advanced,
wrapping: text::Wrapping::default(), wrapping: text::Wrapping::default(),
ellipsize: text::Ellipsize::default(), ellipsize: text::Ellipsize::default(),
@ -430,7 +425,7 @@ pub fn overlay<'a, S: AsRef<str>, Message: 'a, Item: Clone + PartialEq + 'static
}; };
let mut desc_count = 0; let mut desc_count = 0;
padding.horizontal().mul_add( padding.x().mul_add(
2.0, 2.0,
selections selections
.elements() .elements()
@ -517,22 +512,20 @@ pub fn draw<'a, S, Item: Clone + PartialEq + 'static>(
bounds, bounds,
border: style.border, border: style.border,
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
style.background, style.background,
); );
if let Some(handle) = state.icon.as_ref() { if let Some(handle) = state.icon.as_ref() {
let svg_handle = iced_core::Svg::new(handle.clone()).color(style.text_color); let svg_handle = iced_core::Svg::new(handle.clone()).color(style.text_color);
svg::Renderer::draw_svg( let svg_bounds = Rectangle {
renderer, x: bounds.x + bounds.width - gap - 16.0,
svg_handle, y: bounds.center_y() - 8.0,
Rectangle { width: 16.0,
x: bounds.x + bounds.width - gap - 16.0, height: 16.0,
y: bounds.center_y() - 8.0, };
width: 16.0, svg::Renderer::draw_svg(renderer, svg_handle, svg_bounds, svg_bounds);
height: 16.0,
},
);
} }
if let Some(content) = selected.map(AsRef::as_ref) { if let Some(content) = selected.map(AsRef::as_ref) {
@ -541,7 +534,7 @@ pub fn draw<'a, S, Item: Clone + PartialEq + 'static>(
let bounds = Rectangle { let bounds = Rectangle {
x: bounds.x + padding.left, x: bounds.x + padding.left,
y: bounds.center_y(), y: bounds.center_y(),
width: bounds.width - padding.horizontal(), width: bounds.width - padding.x(),
height: f32::from(text_line_height.to_absolute(Pixels(text_size))), height: f32::from(text_line_height.to_absolute(Pixels(text_size))),
}; };
@ -553,8 +546,8 @@ pub fn draw<'a, S, Item: Clone + PartialEq + 'static>(
line_height: text_line_height, line_height: text_line_height,
font, font,
bounds: bounds.size(), bounds: bounds.size(),
horizontal_alignment: alignment::Horizontal::Left, align_x: text::Alignment::Left,
vertical_alignment: alignment::Vertical::Center, align_y: alignment::Vertical::Center,
shaping: text::Shaping::Advanced, shaping: text::Shaping::Advanced,
wrapping: text::Wrapping::default(), wrapping: text::Wrapping::default(),
ellipsize: text::Ellipsize::default(), ellipsize: text::Ellipsize::default(),

View file

@ -11,62 +11,62 @@ pub trait Dropdown {
fn open(&mut self); fn open(&mut self);
} }
/// Produces a [`Task`] that closes a [`Dropdown`] popup. // /// Produces a [`Task`] that closes a [`Dropdown`] popup.
pub fn close<T>(id: Id) -> impl Operation<T> { // pub fn close<T>(id: Id) -> impl Operation<T> {
struct Close(Id); // struct Close(Id);
impl<T> Operation<T> for Close { // impl<T> Operation<T> for Close {
fn custom(&mut self, state: &mut dyn std::any::Any, id: Option<&Id>) { // fn custom(&mut self, state: &mut dyn std::any::Any, id: Option<&Id>) {
if id.map_or(true, |id| id != &self.0) { // if id.map_or(true, |id| id != &self.0) {
return; // return;
} // }
let Some(state) = state.downcast_mut::<State>() else { // let Some(state) = state.downcast_mut::<State>() else {
return; // return;
}; // };
state.close(); // state.close();
} // }
fn container( // fn container(
&mut self, // &mut self,
_id: Option<&Id>, // _id: Option<&Id>,
_bounds: Rectangle, // _bounds: Rectangle,
operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>), // operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>),
) { // ) {
operate_on_children(self) // operate_on_children(self)
} // }
} // }
Close(id) // Close(id)
} // }
/// Produces a [`Task`] that opens a [`Dropdown`] popup. // /// Produces a [`Task`] that opens a [`Dropdown`] popup.
pub fn open<T>(id: Id) -> impl Operation<T> { // pub fn open<T>(id: Id) -> impl Operation<T> {
struct Open(Id); // struct Open(Id);
impl<T> Operation<T> for Open { // impl<T> Operation<T> for Open {
fn custom(&mut self, state: &mut dyn std::any::Any, id: Option<&Id>) { // fn custom(&mut self, state: &mut dyn std::any::Any, id: Option<&Id>) {
if id.map_or(true, |id| id != &self.0) { // if id.map_or(true, |id| id != &self.0) {
return; // return;
} // }
let Some(state) = state.downcast_mut::<State>() else { // let Some(state) = state.downcast_mut::<State>() else {
return; // return;
}; // };
state.open(); // state.open();
} // }
fn container( // fn container(
&mut self, // &mut self,
_id: Option<&Id>, // _id: Option<&Id>,
_bounds: Rectangle, // _bounds: Rectangle,
operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>), // operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>),
) { // ) {
operate_on_children(self) // operate_on_children(self)
} // }
} // }
Open(id) // Open(id)
} // }

View file

@ -203,13 +203,13 @@ where
state.hashes[i] = text_hash; state.hashes[i] = text_hash;
state.selections[i].update(Text { state.selections[i].update(Text {
content: selection.as_ref(), content: selection.as_ref(),
bounds: Size::INFINITY, bounds: Size::INFINITE,
// TODO use the renderer default size // TODO use the renderer default size
size: iced::Pixels(self.text_size.unwrap_or(14.0)), size: iced::Pixels(self.text_size.unwrap_or(14.0)),
line_height: self.text_line_height, line_height: self.text_line_height,
font: self.font.unwrap_or_else(crate::font::default), font: self.font.unwrap_or_else(crate::font::default),
horizontal_alignment: alignment::Horizontal::Left, align_x: text::Alignment::Left,
vertical_alignment: alignment::Vertical::Top, align_y: alignment::Vertical::Top,
shaping: text::Shaping::Advanced, shaping: text::Shaping::Advanced,
wrapping: text::Wrapping::default(), wrapping: text::Wrapping::default(),
ellipsize: text::Ellipsize::default(), ellipsize: text::Ellipsize::default(),
@ -227,7 +227,7 @@ where
} }
fn layout( fn layout(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
renderer: &crate::Renderer, renderer: &crate::Renderer,
limits: &layout::Limits, limits: &layout::Limits,
@ -252,17 +252,17 @@ where
) )
} }
fn on_event( fn update(
&mut self, &mut self,
tree: &mut Tree, tree: &mut Tree,
event: Event, event: &Event,
layout: Layout<'_>, layout: Layout<'_>,
cursor: mouse::Cursor, cursor: mouse::Cursor,
_renderer: &crate::Renderer, _renderer: &crate::Renderer,
_clipboard: &mut dyn Clipboard, _clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
_viewport: &Rectangle, _viewport: &Rectangle,
) -> event::Status { ) {
update::<S, Message, AppMessage>( update::<S, Message, AppMessage>(
&event, &event,
layout, layout,
@ -327,21 +327,23 @@ where
} }
fn operate( fn operate(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
_layout: Layout<'_>, _layout: Layout<'_>,
_renderer: &crate::Renderer, _renderer: &crate::Renderer,
operation: &mut dyn iced_core::widget::Operation, operation: &mut dyn iced_core::widget::Operation,
) { ) {
let state = tree.state.downcast_mut::<State>(); // TODO: double check operation handling
operation.custom(state, self.id.as_ref()); // 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,
layout: Layout<'_>, layout: Layout<'b>,
renderer: &crate::Renderer, renderer: &crate::Renderer,
viewport: &Rectangle,
translation: Vector, translation: Vector,
) -> Option<overlay::Element<'b, Message, crate::Theme, crate::Renderer>> { ) -> Option<overlay::Element<'b, Message, crate::Theme, crate::Renderer>> {
#[cfg(all(feature = "winit", feature = "wayland"))] #[cfg(all(feature = "winit", feature = "wayland"))]
@ -469,24 +471,38 @@ pub fn layout(
let max_width = match width { let max_width = match width {
Length::Shrink => { Length::Shrink => {
let measure = move |(label, paragraph): (_, Option<&mut crate::Plain>)| -> f32 { let measure = move |(label, paragraph): (_, Option<&mut crate::Plain>)| -> f32 {
let text = Text {
content: label,
bounds: Size::new(f32::MAX, f32::MAX),
size: iced::Pixels(text_size),
line_height: text_line_height,
font: font.unwrap_or_else(crate::font::default),
horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Top,
shaping: text::Shaping::Advanced,
wrapping: text::Wrapping::default(),
ellipsize: text::Ellipsize::default(),
};
let paragraph = match paragraph { let paragraph = match paragraph {
Some(p) => { Some(p) => {
let text = Text {
content: label,
bounds: Size::new(f32::MAX, f32::MAX),
size: iced::Pixels(text_size),
line_height: text_line_height,
font: font.unwrap_or_else(crate::font::default),
align_x: text::Alignment::Left,
align_y: alignment::Vertical::Top,
shaping: text::Shaping::Advanced,
wrapping: text::Wrapping::default(),
ellipsize: text::Ellipsize::default(),
};
p.update(text); p.update(text);
p p
} }
None => &mut crate::Plain::new(text), None => {
let text = Text {
content: label.to_string(),
bounds: Size::new(f32::MAX, f32::MAX),
size: iced::Pixels(text_size),
line_height: text_line_height,
font: font.unwrap_or_else(crate::font::default),
align_x: text::Alignment::Left,
align_y: alignment::Vertical::Top,
shaping: text::Shaping::Advanced,
wrapping: text::Wrapping::default(),
ellipsize: text::Ellipsize::default(),
};
&mut crate::Plain::new(text)
}
}; };
paragraph.min_width().round() paragraph.min_width().round()
}; };
@ -544,7 +560,7 @@ pub fn update<
text_size: Option<f32>, text_size: Option<f32>,
font: Option<crate::font::Font>, font: Option<crate::font::Font>,
selected_option: Option<usize>, selected_option: Option<usize>,
) -> event::Status { ) {
let state = state(); let state = state();
let open = |shell: &mut Shell<'_, Message>, let open = |shell: &mut Shell<'_, Message>,
@ -575,7 +591,7 @@ pub fn update<
let measure = |_label: &str, selection_paragraph: &crate::Paragraph| -> f32 { let measure = |_label: &str, selection_paragraph: &crate::Paragraph| -> f32 {
selection_paragraph.min_width().round() selection_paragraph.min_width().round()
}; };
let pad_width = padding.horizontal().mul_add(2.0, 16.0); let pad_width = padding.x().mul_add(2.0, 16.0);
let selections_width = selections let selections_width = selections
.iter() .iter()
@ -669,12 +685,10 @@ pub fn update<
if let Some(on_close) = on_surface_action { if let Some(on_close) = on_surface_action {
shell.publish(on_close(surface::action::destroy_popup(state.popup_id))); shell.publish(on_close(surface::action::destroy_popup(state.popup_id)));
} }
event::Status::Captured shell.capture_event();
} else if cursor.is_over(layout.bounds()) { } else if cursor.is_over(layout.bounds()) {
open(shell, state, on_selected); open(shell, state, on_selected);
event::Status::Captured shell.capture_event();
} else {
event::Status::Ignored
} }
} }
Event::Mouse(mouse::Event::WheelScrolled { Event::Mouse(mouse::Event::WheelScrolled {
@ -689,17 +703,13 @@ pub fn update<
shell.publish((on_selected)(next_index)); shell.publish((on_selected)(next_index));
} }
event::Status::Captured shell.capture_event();
} else {
event::Status::Ignored
} }
} }
Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) => { Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) => {
state.keyboard_modifiers = *modifiers; state.keyboard_modifiers = *modifiers;
event::Status::Ignored
} }
_ => event::Status::Ignored, _ => {}
} }
} }
@ -746,7 +756,7 @@ where
.zip(state.selections.iter()) .zip(state.selections.iter())
.map(|(label, selection)| measure(label.as_ref(), selection.raw())) .map(|(label, selection)| measure(label.as_ref(), selection.raw()))
.fold(0.0, |next, current| current.max(next)); .fold(0.0, |next, current| current.max(next));
let pad_width = padding.horizontal().mul_add(2.0, 16.0); let pad_width = padding.x().mul_add(2.0, 16.0);
let width = selections_width + gap + pad_width + icon_width; let width = selections_width + gap + pad_width + icon_width;
let is_open = state.is_open.clone(); let is_open = state.is_open.clone();
@ -822,7 +832,7 @@ where
selection_paragraph.min_width().round() selection_paragraph.min_width().round()
}; };
let pad_width = padding.horizontal().mul_add(2.0, 16.0); let pad_width = padding.x().mul_add(2.0, 16.0);
let icon_width = if icons.is_empty() { 0.0 } else { 24.0 }; let icon_width = if icons.is_empty() { 0.0 } else { 24.0 };
@ -883,23 +893,20 @@ pub fn draw<'a, S>(
bounds, bounds,
border: style.border, border: style.border,
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
style.background, style.background,
); );
if let Some(handle) = state.icon.clone() { if let Some(handle) = state.icon.clone() {
let svg_handle = svg::Svg::new(handle).color(style.text_color); let svg_handle = svg::Svg::new(handle).color(style.text_color);
let bounds = Rectangle {
svg::Renderer::draw_svg( x: bounds.x + bounds.width - gap - 16.0,
renderer, y: bounds.center_y() - 8.0,
svg_handle, width: 16.0,
Rectangle { height: 16.0,
x: bounds.x + bounds.width - gap - 16.0, };
y: bounds.center_y() - 8.0, svg::Renderer::draw_svg(renderer, svg_handle, bounds, bounds);
width: 16.0,
height: 16.0,
},
);
} }
if let Some(content) = selected.map(AsRef::as_ref).or(placeholder) { if let Some(content) = selected.map(AsRef::as_ref).or(placeholder) {
@ -908,7 +915,7 @@ pub fn draw<'a, S>(
let mut bounds = Rectangle { let mut bounds = Rectangle {
x: bounds.x + padding.left, x: bounds.x + padding.left,
y: bounds.center_y(), y: bounds.center_y(),
width: bounds.width - padding.horizontal(), width: bounds.width - padding.x(),
height: f32::from(text_line_height.to_absolute(Pixels(text_size))), height: f32::from(text_line_height.to_absolute(Pixels(text_size))),
}; };
@ -932,8 +939,8 @@ pub fn draw<'a, S>(
line_height: text_line_height, line_height: text_line_height,
font, font,
bounds: bounds.size(), bounds: bounds.size(),
horizontal_alignment: alignment::Horizontal::Left, align_x: text::Alignment::Left,
vertical_alignment: alignment::Vertical::Center, align_y: alignment::Vertical::Center,
shaping: text::Shaping::Advanced, shaping: text::Shaping::Advanced,
wrapping: text::Wrapping::default(), wrapping: text::Wrapping::default(),
ellipsize: text::Ellipsize::default(), ellipsize: text::Ellipsize::default(),

View file

@ -15,7 +15,7 @@ use taffy::{AlignContent, TaffyTree};
pub fn resolve<Message>( pub fn resolve<Message>(
renderer: &Renderer, renderer: &Renderer,
limits: &Limits, limits: &Limits,
items: &[Element<'_, Message>], items: &mut [Element<'_, Message>],
padding: Padding, padding: Padding,
column_spacing: f32, column_spacing: f32,
row_spacing: f32, row_spacing: f32,
@ -61,8 +61,8 @@ pub fn resolve<Message>(
..taffy::Style::default() ..taffy::Style::default()
}; };
for (child, tree) in items.iter().zip(tree.iter_mut()) { for (child, tree) in items.iter_mut().zip(tree.iter_mut()) {
let child_widget = child.as_widget(); let child_widget = child.as_widget_mut();
let child_node = child_widget.layout(tree, renderer, limits); let child_node = child_widget.layout(tree, renderer, limits);
let size = child_node.size(); let size = child_node.size();
@ -138,7 +138,7 @@ pub fn resolve<Message>(
leafs leafs
.into_iter() .into_iter()
.zip(items.iter()) .zip(items.iter_mut())
.zip(nodes.iter_mut()) .zip(nodes.iter_mut())
.zip(tree) .zip(tree)
.for_each(|(((leaf, child), node), tree)| { .for_each(|(((leaf, child), node), tree)| {
@ -146,7 +146,7 @@ pub fn resolve<Message>(
return; return;
}; };
let child_widget = child.as_widget(); let child_widget = child.as_widget_mut();
let c_size = child_widget.size(); let c_size = child_widget.size();
match c_size.width { match c_size.width {
Length::Fill | Length::FillPortion(_) => { Length::Fill | Length::FillPortion(_) => {

View file

@ -100,7 +100,7 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, Renderer> for FlexR
} }
fn layout( fn layout(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
renderer: &Renderer, renderer: &Renderer,
limits: &layout::Limits, limits: &layout::Limits,
@ -114,7 +114,7 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, Renderer> for FlexR
super::layout::resolve( super::layout::resolve(
renderer, renderer,
&limits, &limits,
&self.children, &mut self.children,
self.padding, self.padding,
f32::from(self.column_spacing), f32::from(self.column_spacing),
f32::from(self.row_spacing), f32::from(self.row_spacing),
@ -127,19 +127,19 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, Renderer> for FlexR
} }
fn operate( fn operate(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
layout: Layout<'_>, layout: Layout<'_>,
renderer: &Renderer, renderer: &Renderer,
operation: &mut dyn Operation<()>, operation: &mut dyn Operation<()>,
) { ) {
operation.container(None, layout.bounds(), &mut |operation| { operation.traverse(&mut |operation| {
self.children self.children
.iter() .iter_mut()
.zip(&mut tree.children) .zip(&mut tree.children)
.zip(layout.children()) .zip(layout.children())
.for_each(|((child, state), c_layout)| { .for_each(|((child, state), c_layout)| {
child.as_widget().operate( child.as_widget_mut().operate(
state, state,
c_layout.with_virtual_offset(layout.virtual_offset()), c_layout.with_virtual_offset(layout.virtual_offset()),
renderer, renderer,
@ -149,25 +149,25 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, Renderer> for FlexR
}); });
} }
fn on_event( fn update(
&mut self, &mut self,
tree: &mut Tree, tree: &mut Tree,
event: Event, event: &Event,
layout: Layout<'_>, layout: Layout<'_>,
cursor: mouse::Cursor, cursor: mouse::Cursor,
renderer: &Renderer, renderer: &Renderer,
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
viewport: &Rectangle, viewport: &Rectangle,
) -> event::Status { ) {
self.children self.children
.iter_mut() .iter_mut()
.zip(&mut tree.children) .zip(&mut tree.children)
.zip(layout.children()) .zip(layout.children())
.map(|((child, state), c_layout)| { .map(|((child, state), c_layout)| {
child.as_widget_mut().on_event( child.as_widget_mut().update(
state, state,
event.clone(), event,
c_layout.with_virtual_offset(layout.virtual_offset()), c_layout.with_virtual_offset(layout.virtual_offset()),
cursor, cursor,
renderer, renderer,
@ -175,8 +175,7 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, Renderer> for FlexR
shell, shell,
viewport, viewport,
) )
}) });
.fold(event::Status::Ignored, event::Status::merge)
} }
fn mouse_interaction( fn mouse_interaction(
@ -235,11 +234,19 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, Renderer> for FlexR
fn overlay<'b>( fn overlay<'b>(
&'b mut self, &'b mut self,
tree: &'b mut Tree, tree: &'b mut Tree,
layout: Layout<'_>, layout: Layout<'b>,
renderer: &Renderer, renderer: &Renderer,
viewport: &Rectangle,
translation: Vector, translation: Vector,
) -> Option<overlay::Element<'b, Message, crate::Theme, Renderer>> { ) -> Option<overlay::Element<'b, Message, crate::Theme, Renderer>> {
overlay::from_children(&mut self.children, tree, layout, renderer, translation) overlay::from_children(
&mut self.children,
tree,
layout,
renderer,
viewport,
translation,
)
} }
#[cfg(feature = "a11y")] #[cfg(feature = "a11y")]

View file

@ -8,6 +8,8 @@ use std::path::Path;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use ::image as image_rs; use ::image as image_rs;
use iced::Task;
use iced::mouse;
use iced_core::image::Renderer as ImageRenderer; use iced_core::image::Renderer as ImageRenderer;
use iced_core::mouse::Cursor; use iced_core::mouse::Cursor;
use iced_core::widget::{Tree, tree}; use iced_core::widget::{Tree, tree};
@ -15,7 +17,6 @@ use iced_core::{
Clipboard, ContentFit, Element, Event, Layout, Length, Rectangle, Shell, Size, Vector, Widget, Clipboard, ContentFit, Element, Event, Layout, Length, Rectangle, Shell, Size, Vector, Widget,
event, layout, renderer, window, event, layout, renderer, window,
}; };
use iced_runtime::Command;
use iced_widget::image::{self, Handle}; use iced_widget::image::{self, Handle};
use image_rs::AnimationDecoder; use image_rs::AnimationDecoder;
use image_rs::codecs::gif::GifDecoder; use image_rs::codecs::gif::GifDecoder;
@ -27,7 +28,7 @@ use iced_futures::futures::{AsyncRead, AsyncReadExt};
#[cfg(feature = "tokio")] #[cfg(feature = "tokio")]
use tokio::io::{AsyncRead, AsyncReadExt}; use tokio::io::{AsyncRead, AsyncReadExt};
use super::icon::load_icon; use crate::widget::icon;
#[must_use] #[must_use]
/// Creates a new [`AnimatedImage`] with the given [`animated_image::Frames`] /// Creates a new [`AnimatedImage`] with the given [`animated_image::Frames`]
@ -74,13 +75,13 @@ impl Frames {
size: u16, size: u16,
theme: Option<&str>, theme: Option<&str>,
default_fallbacks: bool, default_fallbacks: bool,
) -> Command<Result<Frames, Error>> { ) -> Task<Result<Frames, Error>> {
let mut name_path_buffer = None; let mut name_path_buffer = None;
if let Some(path) = load_icon(name, size, theme) { if let Some(path) = icon::Named::new(name).size(size).path() {
name_path_buffer = Some(path); name_path_buffer = Some(path);
} else if default_fallbacks { } else if default_fallbacks {
for name in name.rmatch_indices('-').map(|(pos, _)| &name[..pos]) { for name in name.rmatch_indices('-').map(|(pos, _)| &name[..pos]) {
if let Some(path) = load_icon(name, size, theme) { if let Some(path) = icon::Named::new(name).size(size).path() {
name_path_buffer = Some(path); name_path_buffer = Some(path);
break; break;
} }
@ -90,14 +91,14 @@ impl Frames {
if let Some(name_path_buffer) = name_path_buffer { if let Some(name_path_buffer) = name_path_buffer {
Self::load_from_path(name_path_buffer) Self::load_from_path(name_path_buffer)
} else { } else {
Command::perform(async { Err(Error::Missing) }, std::convert::identity) Task::perform(async { Err(Error::Missing) }, std::convert::identity)
} }
} }
/// Load [`Frames`] from the supplied path /// Load [`Frames`] from the supplied path
pub fn load_from_path(path: impl AsRef<Path>) -> Command<Result<Frames, Error>> { pub fn load_from_path(path: impl AsRef<Path>) -> Task<Result<Frames, Error>> {
#[inline(never)] #[inline(never)]
fn inner(path: &Path) -> Command<Result<Frames, Error>> { fn inner(path: &Path) -> Task<Result<Frames, Error>> {
#[cfg(feature = "tokio")] #[cfg(feature = "tokio")]
use tokio::fs::File; use tokio::fs::File;
#[cfg(feature = "tokio")] #[cfg(feature = "tokio")]
@ -108,7 +109,7 @@ impl Frames {
#[cfg(not(feature = "tokio"))] #[cfg(not(feature = "tokio"))]
use iced_futures::futures::io::BufReader; use iced_futures::futures::io::BufReader;
let path = path.as_ref().to_path_buf(); let path = path.to_path_buf();
let f = async move { let f = async move {
let image_type = match &path.extension() { let image_type = match &path.extension() {
@ -119,10 +120,10 @@ impl Frames {
}; };
let reader = BufReader::new(File::open(path).await?); let reader = BufReader::new(File::open(path).await?);
Self::from_reader(reader, image_type).await Frames::from_reader(reader, image_type).await
}; };
Command::perform(f, std::convert::identity) Task::perform(f, std::convert::identity)
} }
inner(path.as_ref()) inner(path.as_ref())
@ -168,9 +169,9 @@ impl Frames {
let total_bytes = frames let total_bytes = frames
.iter() .iter()
.map(|f| match f.handle.data() { .map(|f| match f.handle.data() {
iced_core::image::Data::Path(_) => 0, iced_core::image::Handle::Path(..) => 0,
iced_core::image::Data::Bytes(b) => b.len(), iced_core::image::Handle::Bytes(_, b) => b.len(),
iced_core::image::Data::Rgba { pixels, .. } => pixels.len(), iced_core::image::Handle::Rgba { pixels, .. } => pixels.len(),
}) })
.sum::<usize>() .sum::<usize>()
.try_into() .try_into()
@ -195,7 +196,7 @@ impl From<image_rs::Frame> for Frame {
let delay = frame.delay().into(); let delay = frame.delay().into();
let handle = image::Handle::from_pixels(width, height, frame.into_buffer().into_vec()); let handle = image::Handle::from_rgba(width, height, frame.into_buffer().into_vec());
Self { delay, handle } Self { delay, handle }
} }
@ -278,12 +279,8 @@ impl<'a, Message, Renderer> Widget<Message, crate::Theme, Renderer> for Animated
where where
Renderer: ImageRenderer<Handle = Handle>, Renderer: ImageRenderer<Handle = Handle>,
{ {
fn width(&self) -> Length { fn size(&self) -> Size<Length> {
self.width Size::new(self.width.into(), self.height.into())
}
fn height(&self) -> Length {
self.height
} }
fn tag(&self) -> tree::Tag { fn tag(&self) -> tree::Tag {
@ -315,7 +312,12 @@ where
} }
} }
fn layout(&self, renderer: &Renderer, limits: &layout::Limits) -> layout::Node { fn layout(
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
iced_widget::image::layout( iced_widget::image::layout(
renderer, renderer,
limits, limits,
@ -326,19 +328,20 @@ where
) )
} }
fn on_event( fn update(
&mut self, &mut self,
tree: &mut Tree, tree: &mut Tree,
event: Event, event: &Event,
_layout: Layout<'_>, layout: Layout<'_>,
_cursor_position: 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>,
) -> event::Status { viewport: &Rectangle,
) {
let state = tree.state.downcast_mut::<State>(); let state = tree.state.downcast_mut::<State>();
if let Event::Window(_, window::Event::RedrawRequested(now)) = event { if let Event::Window(window::Event::RedrawRequested(now)) = event {
let elapsed = now.duration_since(state.current.started); let elapsed = now.duration_since(state.current.started);
if elapsed > state.current.frame.delay { if elapsed > state.current.frame.delay {
@ -346,15 +349,14 @@ where
state.current = self.frames.frames[state.index].clone().into(); state.current = self.frames.frames[state.index].clone().into();
shell.request_redraw(window::RedrawRequest::At(now + state.current.frame.delay)); shell
.request_redraw_at(window::RedrawRequest::At(*now + state.current.frame.delay));
} else { } else {
let remaining = state.current.frame.delay - elapsed; let remaining = state.current.frame.delay - elapsed;
shell.request_redraw(window::RedrawRequest::At(now + remaining)); shell.request_redraw_at(window::RedrawRequest::At(*now + remaining));
} }
} }
event::Status::Ignored
} }
fn draw( fn draw(

View file

@ -17,7 +17,7 @@ use taffy::{AlignContent, TaffyTree};
pub fn resolve<Message>( pub fn resolve<Message>(
renderer: &Renderer, renderer: &Renderer,
limits: &Limits, limits: &Limits,
items: &[Element<'_, Message>], items: &mut [Element<'_, Message>],
assignments: &[Assignment], assignments: &[Assignment],
width: Length, width: Length,
height: Length, height: Length,
@ -37,9 +37,13 @@ pub fn resolve<Message>(
let mut taffy = TaffyTree::<()>::with_capacity(items.len() + 1); let mut taffy = TaffyTree::<()>::with_capacity(items.len() + 1);
// Attach widgets as child nodes. // Attach widgets as child nodes.
for ((child, assignment), tree) in items.iter().zip(assignments.iter()).zip(tree.iter_mut()) { for ((child, assignment), tree) in items
.iter_mut()
.zip(assignments.iter())
.zip(tree.iter_mut())
{
// Calculate the dimensions of the item. // Calculate the dimensions of the item.
let child_widget = child.as_widget(); let child_widget = child.as_widget_mut();
let child_node = child_widget.layout(tree, renderer, limits); let child_node = child_widget.layout(tree, renderer, limits);
let size = child_node.size(); let size = child_node.size();
@ -172,12 +176,12 @@ pub fn resolve<Message>(
for (((leaf, child), node), tree) in leafs for (((leaf, child), node), tree) in leafs
.into_iter() .into_iter()
.zip(items.iter()) .zip(items.iter_mut())
.zip(nodes.iter_mut()) .zip(nodes.iter_mut())
.zip(tree) .zip(tree)
{ {
if let Ok(leaf_layout) = taffy.layout(leaf) { if let Ok(leaf_layout) = taffy.layout(leaf) {
let child_widget = child.as_widget(); let child_widget = child.as_widget_mut();
let c_size = child_widget.size(); let c_size = child_widget.size();
match c_size.width { match c_size.width {
Length::Fill | Length::FillPortion(_) => { Length::Fill | Length::FillPortion(_) => {

View file

@ -127,7 +127,7 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, Renderer> for Grid<
} }
fn layout( fn layout(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
renderer: &Renderer, renderer: &Renderer,
limits: &layout::Limits, limits: &layout::Limits,
@ -141,7 +141,7 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, Renderer> for Grid<
super::layout::resolve( super::layout::resolve(
renderer, renderer,
&limits, &limits,
&self.children, &mut self.children,
&self.assignments, &self.assignments,
self.width, self.width,
self.height, self.height,
@ -156,19 +156,19 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, Renderer> for Grid<
} }
fn operate( fn operate(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
layout: Layout<'_>, layout: Layout<'_>,
renderer: &Renderer, renderer: &Renderer,
operation: &mut dyn Operation<()>, operation: &mut dyn Operation<()>,
) { ) {
operation.container(None, layout.bounds(), &mut |operation| { operation.traverse(&mut |operation| {
self.children self.children
.iter() .iter_mut()
.zip(&mut tree.children) .zip(&mut tree.children)
.zip(layout.children()) .zip(layout.children())
.for_each(|((child, state), c_layout)| { .for_each(|((child, state), c_layout)| {
child.as_widget().operate( child.as_widget_mut().operate(
state, state,
c_layout.with_virtual_offset(layout.virtual_offset()), c_layout.with_virtual_offset(layout.virtual_offset()),
renderer, renderer,
@ -178,25 +178,25 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, Renderer> for Grid<
}); });
} }
fn on_event( fn update(
&mut self, &mut self,
tree: &mut Tree, tree: &mut Tree,
event: Event, event: &Event,
layout: Layout<'_>, layout: Layout<'_>,
cursor: mouse::Cursor, cursor: mouse::Cursor,
renderer: &Renderer, renderer: &Renderer,
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
viewport: &Rectangle, viewport: &Rectangle,
) -> event::Status { ) {
self.children self.children
.iter_mut() .iter_mut()
.zip(&mut tree.children) .zip(&mut tree.children)
.zip(layout.children()) .zip(layout.children())
.map(|((child, state), c_layout)| { .map(|((child, state), c_layout)| {
child.as_widget_mut().on_event( child.as_widget_mut().update(
state, state,
event.clone(), event,
c_layout.with_virtual_offset(layout.virtual_offset()), c_layout.with_virtual_offset(layout.virtual_offset()),
cursor, cursor,
renderer, renderer,
@ -204,8 +204,7 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, Renderer> for Grid<
shell, shell,
viewport, viewport,
) )
}) });
.fold(event::Status::Ignored, event::Status::merge)
} }
fn mouse_interaction( fn mouse_interaction(
@ -264,11 +263,19 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, Renderer> for Grid<
fn overlay<'b>( fn overlay<'b>(
&'b mut self, &'b mut self,
tree: &'b mut Tree, tree: &'b mut Tree,
layout: Layout<'_>, layout: Layout<'b>,
renderer: &Renderer, renderer: &Renderer,
viewport: &Rectangle,
translation: Vector, translation: Vector,
) -> Option<overlay::Element<'b, Message, crate::Theme, Renderer>> { ) -> Option<overlay::Element<'b, Message, crate::Theme, Renderer>> {
overlay::from_children(&mut self.children, tree, layout, renderer, translation) overlay::from_children(
&mut self.children,
tree,
layout,
renderer,
viewport,
translation,
)
} }
#[cfg(feature = "a11y")] #[cfg(feature = "a11y")]

View file

@ -157,7 +157,7 @@ impl<Message: Clone + 'static> Widget<Message, crate::Theme, crate::Renderer>
} }
fn layout( fn layout(
&self, &mut self,
tree: &mut tree::Tree, tree: &mut tree::Tree,
renderer: &crate::Renderer, renderer: &crate::Renderer,
limits: &iced_core::layout::Limits, limits: &iced_core::layout::Limits,
@ -165,7 +165,7 @@ impl<Message: Clone + 'static> Widget<Message, crate::Theme, crate::Renderer>
let child_tree = &mut tree.children[0]; let child_tree = &mut tree.children[0];
let child = self let child = self
.header_bar_inner .header_bar_inner
.as_widget() .as_widget_mut()
.layout(child_tree, renderer, limits); .layout(child_tree, renderer, limits);
iced_core::layout::Node::with_children(child.size(), vec![child]) iced_core::layout::Node::with_children(child.size(), vec![child])
} }
@ -193,20 +193,20 @@ impl<Message: Clone + 'static> Widget<Message, crate::Theme, crate::Renderer>
); );
} }
fn on_event( fn update(
&mut self, &mut self,
state: &mut tree::Tree, state: &mut tree::Tree,
event: iced_core::Event, event: &iced_core::Event,
layout: iced_core::Layout<'_>, layout: iced_core::Layout<'_>,
cursor: iced_core::mouse::Cursor, cursor: iced_core::mouse::Cursor,
renderer: &crate::Renderer, renderer: &crate::Renderer,
clipboard: &mut dyn iced_core::Clipboard, clipboard: &mut dyn iced_core::Clipboard,
shell: &mut iced_core::Shell<'_, Message>, shell: &mut iced_core::Shell<'_, Message>,
viewport: &iced_core::Rectangle, viewport: &iced_core::Rectangle,
) -> iced_core::event::Status { ) {
let child_state = &mut state.children[0]; let child_state = &mut state.children[0];
let child_layout = layout.children().next().unwrap(); let child_layout = layout.children().next().unwrap();
self.header_bar_inner.as_widget_mut().on_event( self.header_bar_inner.as_widget_mut().update(
child_state, child_state,
event, event,
child_layout, child_layout,
@ -238,7 +238,7 @@ impl<Message: Clone + 'static> Widget<Message, crate::Theme, crate::Renderer>
} }
fn operate( fn operate(
&self, &mut self,
state: &mut tree::Tree, state: &mut tree::Tree,
layout: iced_core::Layout<'_>, layout: iced_core::Layout<'_>,
renderer: &crate::Renderer, renderer: &crate::Renderer,
@ -246,16 +246,20 @@ impl<Message: Clone + 'static> Widget<Message, crate::Theme, crate::Renderer>
) { ) {
let child_tree = &mut state.children[0]; let child_tree = &mut state.children[0];
let child_layout = layout.children().next().unwrap(); let child_layout = layout.children().next().unwrap();
self.header_bar_inner self.header_bar_inner.as_widget_mut().operate(
.as_widget() child_tree,
.operate(child_tree, child_layout, renderer, operation); child_layout,
renderer,
operation,
);
} }
fn overlay<'b>( fn overlay<'b>(
&'b mut self, &'b mut self,
state: &'b mut tree::Tree, state: &'b mut tree::Tree,
layout: iced_core::Layout<'_>, layout: iced_core::Layout<'b>,
renderer: &crate::Renderer, renderer: &crate::Renderer,
viewport: &iced_core::Rectangle,
translation: Vector, translation: Vector,
) -> Option<iced_core::overlay::Element<'b, Message, crate::Theme, crate::Renderer>> { ) -> Option<iced_core::overlay::Element<'b, Message, crate::Theme, crate::Renderer>> {
let child_tree = &mut state.children[0]; let child_tree = &mut state.children[0];
@ -264,6 +268,7 @@ impl<Message: Clone + 'static> Widget<Message, crate::Theme, crate::Renderer>
child_tree, child_tree,
child_layout, child_layout,
renderer, renderer,
viewport,
translation, translation,
) )
} }

View file

@ -15,7 +15,7 @@ pub use handle::{Data, Handle, from_path, from_raster_bytes, from_raster_pixels,
use crate::Element; use crate::Element;
use derive_setters::Setters; use derive_setters::Setters;
use iced::widget::{Image, Svg}; use iced::widget::{Image, Svg};
use iced::{ContentFit, Length, Rectangle}; use iced::{ContentFit, Length, Radians, Rectangle};
use iced_core::Rotation; use iced_core::Rotation;
/// Create an [`Icon`] from a pre-existing [`Handle`] /// Create an [`Icon`] from a pre-existing [`Handle`]
@ -125,17 +125,22 @@ pub fn draw(renderer: &mut crate::Renderer, handle: &Handle, icon_bounds: Rectan
renderer, renderer,
iced_core::svg::Svg::new(handle), iced_core::svg::Svg::new(handle),
icon_bounds, icon_bounds,
icon_bounds,
), ),
Data::Image(handle) => { Data::Image(handle) => {
iced_core::image::Renderer::draw_image( iced_core::image::Renderer::draw_image(
renderer, renderer,
handle, iced_core::Image {
iced_core::image::FilterMethod::Linear, handle,
filter_method: iced_core::image::FilterMethod::Linear,
rotation: Radians(0.),
border_radius: [0.0; 4].into(),
opacity: 1.0,
snap: true,
},
icon_bounds,
icon_bounds, icon_bounds,
iced_core::Radians::from(0),
1.0,
[0.0; 4],
); );
} }
} }

View file

@ -3,7 +3,7 @@ use iced_core::layout;
use iced_core::mouse; use iced_core::mouse;
use iced_core::overlay; use iced_core::overlay;
use iced_core::renderer; use iced_core::renderer;
use iced_core::widget::{Id, Tree}; use iced_core::widget::{Id, Operation, Tree};
use iced_core::{Clipboard, Element, Layout, Length, Rectangle, Shell, Vector, Widget}; use iced_core::{Clipboard, Element, Layout, Length, Rectangle, Shell, Vector, Widget};
pub use iced_widget::container::{Catalog, Style}; pub use iced_widget::container::{Catalog, Style};
@ -65,29 +65,30 @@ where
} }
fn layout( fn layout(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
renderer: &Renderer, renderer: &Renderer,
limits: &layout::Limits, limits: &layout::Limits,
) -> layout::Node { ) -> layout::Node {
let node = self let node = self
.content .content
.as_widget() .as_widget_mut()
.layout(&mut tree.children[0], renderer, limits); .layout(&mut tree.children[0], renderer, limits);
let size = node.size(); let size = node.size();
layout::Node::with_children(size, vec![node]) layout::Node::with_children(size, vec![node])
} }
fn operate( fn operate(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
layout: Layout<'_>, layout: Layout<'_>,
renderer: &Renderer, renderer: &Renderer,
operation: &mut dyn iced_core::widget::Operation<()>, operation: &mut dyn Operation,
) { ) {
operation.container(Some(&self.id), layout.bounds(), &mut |operation| { operation.container(Some(&self.id), layout.bounds());
self.content.as_widget().operate( operation.traverse(&mut |operation| {
&mut tree.children[0], self.content.as_widget_mut().operate(
tree,
layout layout
.children() .children()
.next() .next()
@ -99,18 +100,18 @@ where
}); });
} }
fn on_event( fn update(
&mut self, &mut self,
tree: &mut Tree, tree: &mut Tree,
event: Event, 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: &Rectangle, viewport: &Rectangle,
) -> event::Status { ) {
self.content.as_widget_mut().on_event( self.content.as_widget_mut().update(
&mut tree.children[0], &mut tree.children[0],
event, event,
layout layout
@ -169,8 +170,9 @@ where
fn overlay<'b>( fn overlay<'b>(
&'b mut self, &'b mut self,
tree: &'b mut Tree, tree: &'b mut Tree,
layout: Layout<'_>, layout: Layout<'b>,
renderer: &Renderer, renderer: &Renderer,
viewport: &Rectangle,
translation: Vector, translation: Vector,
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> { ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
self.content.as_widget_mut().overlay( self.content.as_widget_mut().overlay(
@ -181,6 +183,7 @@ where
.unwrap() .unwrap()
.with_virtual_offset(layout.virtual_offset()), .with_virtual_offset(layout.virtual_offset()),
renderer, renderer,
viewport,
translation, translation,
) )
} }

View file

@ -172,7 +172,7 @@ where
} }
fn layout( fn layout(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
renderer: &Renderer, renderer: &Renderer,
limits: &layout::Limits, limits: &layout::Limits,
@ -181,7 +181,7 @@ where
} }
fn operate( fn operate(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
layout: Layout<'_>, layout: Layout<'_>,
renderer: &Renderer, renderer: &Renderer,
@ -190,18 +190,18 @@ where
self.container.operate(tree, layout, renderer, operation); self.container.operate(tree, layout, renderer, operation);
} }
fn on_event( fn update(
&mut self, &mut self,
tree: &mut Tree, tree: &mut Tree,
event: Event, 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: &Rectangle, viewport: &Rectangle,
) -> event::Status { ) {
self.container.on_event( self.container.update(
tree, tree,
event, event,
layout, layout,
@ -257,11 +257,13 @@ where
fn overlay<'b>( fn overlay<'b>(
&'b mut self, &'b mut self,
tree: &'b mut Tree, tree: &'b mut Tree,
layout: Layout<'_>, layout: Layout<'b>,
renderer: &Renderer, renderer: &Renderer,
viewport: &Rectangle,
translation: Vector, translation: Vector,
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> { ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
self.container.overlay(tree, layout, renderer, translation) self.container
.overlay(tree, layout, renderer, viewport, translation)
} }
fn drag_destinations( fn drag_destinations(

View file

@ -6,7 +6,7 @@ use iced_widget::container::Catalog;
use crate::{ use crate::{
Apply, Element, theme, Apply, Element, theme,
widget::{container, divider, vertical_space}, widget::{container, divider, space::vertical},
}; };
#[inline] #[inline]
@ -65,7 +65,7 @@ impl<'a, Message: 'static> ListColumn<'a, Message> {
// Ensure a minimum height of 32. // Ensure a minimum height of 32.
let list_item = iced::widget::row![ let list_item = iced::widget::row![
container(item).align_y(iced::Alignment::Center), container(item).align_y(iced::Alignment::Center),
vertical_space().height(iced::Length::Fixed(32.)) vertical().height(iced::Length::Fixed(32.))
] ]
.padding(this.list_item_padding) .padding(this.list_item_padding)
.align_y(iced::Alignment::Center); .align_y(iced::Alignment::Center);

View file

@ -57,11 +57,11 @@ pub fn resolve<'a, E, Message, Renderer>(
padding: Padding, padding: Padding,
spacing: f32, spacing: f32,
align_items: Alignment, align_items: Alignment,
items: &[E], items: &mut [E],
tree: &mut [&mut Tree], tree: &mut [&mut Tree],
) -> Node ) -> Node
where where
E: std::borrow::Borrow<Element<'a, Message, crate::Theme, Renderer>>, E: std::borrow::BorrowMut<Element<'a, Message, crate::Theme, Renderer>>,
Renderer: renderer::Renderer, Renderer: renderer::Renderer,
{ {
let limits = limits.shrink(padding); let limits = limits.shrink(padding);
@ -69,7 +69,7 @@ where
let max_cross = axis.cross(limits.max()); let max_cross = axis.cross(limits.max());
let mut fill_sum = 0; let mut fill_sum = 0;
let mut cross = axis.cross(limits.min()).max(axis.cross(Size::INFINITY)); let mut cross = axis.cross(limits.min()).max(axis.cross(Size::INFINITE));
let mut available = axis.main(limits.max()) - total_spacing; let mut available = axis.main(limits.max()) - total_spacing;
let mut nodes: Vec<Node> = Vec::with_capacity(items.len()); let mut nodes: Vec<Node> = Vec::with_capacity(items.len());
@ -78,8 +78,8 @@ where
if align_items == Alignment::Center { if align_items == Alignment::Center {
let mut fill_cross = axis.cross(limits.min()); let mut fill_cross = axis.cross(limits.min());
for (child, tree) in items.iter().zip(tree.iter_mut()) { for (child, tree) in items.iter_mut().zip(tree.iter_mut()) {
let child = child.borrow(); let child = child.borrow_mut();
let c_size = child.as_widget().size(); let c_size = child.as_widget().size();
let cross_fill_factor = match axis { let cross_fill_factor = match axis {
Axis::Horizontal => c_size.height, Axis::Horizontal => c_size.height,
@ -92,7 +92,7 @@ where
let child_limits = Limits::new(Size::ZERO, Size::new(max_width, max_height)); let child_limits = Limits::new(Size::ZERO, Size::new(max_width, max_height));
let layout = child.as_widget().layout(tree, renderer, &child_limits); let layout = child.as_widget_mut().layout(tree, renderer, &child_limits);
let size = layout.size(); let size = layout.size();
fill_cross = fill_cross.max(axis.cross(size)); fill_cross = fill_cross.max(axis.cross(size));
@ -102,8 +102,8 @@ where
cross = fill_cross; cross = fill_cross;
} }
for (i, (child, tree)) in items.iter().zip(tree.iter_mut()).enumerate() { for (i, (child, tree)) in items.iter_mut().zip(tree.iter_mut()).enumerate() {
let child = child.borrow(); let child = child.borrow_mut();
let c_size = child.as_widget().size(); let c_size = child.as_widget().size();
let fill_factor = match axis { let fill_factor = match axis {
Axis::Horizontal => c_size.width, Axis::Horizontal => c_size.width,
@ -129,7 +129,7 @@ where
Size::new(max_width, max_height), Size::new(max_width, max_height),
); );
let layout = child.as_widget().layout(tree, renderer, &child_limits); let layout = child.as_widget_mut().layout(tree, renderer, &child_limits);
let size = layout.size(); let size = layout.size();
available -= axis.main(size); available -= axis.main(size);
@ -146,8 +146,8 @@ where
let remaining = available.max(0.0); let remaining = available.max(0.0);
for (i, (child, tree)) in items.iter().zip(tree.iter_mut()).enumerate() { for (i, (child, tree)) in items.iter_mut().zip(tree.iter_mut()).enumerate() {
let child = child.borrow(); let child = child.borrow_mut();
let c_size = child.as_widget().size(); let c_size = child.as_widget().size();
let fill_factor = match axis { let fill_factor = match axis {
Axis::Horizontal => c_size.width, Axis::Horizontal => c_size.width,
@ -180,7 +180,7 @@ where
Size::new(max_width, max_height), Size::new(max_width, max_height),
); );
let layout = child.as_widget().layout(tree, renderer, &child_limits); let layout = child.as_widget_mut().layout(tree, renderer, &child_limits);
if align_items != Alignment::Center { if align_items != Alignment::Center {
cross = cross.max(axis.cross(layout.size())); cross = cross.max(axis.cross(layout.size()));
@ -231,7 +231,7 @@ pub fn resolve_wrapper<'a, Message>(
padding: Padding, padding: Padding,
spacing: f32, spacing: f32,
align_items: Alignment, align_items: Alignment,
items: &[&RcElementWrapper<Message>], items: &mut [&mut RcElementWrapper<Message>],
tree: &mut [&mut Tree], tree: &mut [&mut Tree],
) -> Node { ) -> Node {
let limits = limits.shrink(padding); let limits = limits.shrink(padding);
@ -239,7 +239,7 @@ pub fn resolve_wrapper<'a, Message>(
let max_cross = axis.cross(limits.max()); let max_cross = axis.cross(limits.max());
let mut fill_sum = 0; let mut fill_sum = 0;
let mut cross = axis.cross(limits.min()).max(axis.cross(Size::INFINITY)); let mut cross = axis.cross(limits.min()).max(axis.cross(Size::INFINITE));
let mut available = axis.main(limits.max()) - total_spacing; let mut available = axis.main(limits.max()) - total_spacing;
let mut nodes: Vec<Node> = Vec::with_capacity(items.len()); let mut nodes: Vec<Node> = Vec::with_capacity(items.len());
@ -248,7 +248,7 @@ pub fn resolve_wrapper<'a, Message>(
if align_items == Alignment::Center { if align_items == Alignment::Center {
let mut fill_cross = axis.cross(limits.min()); let mut fill_cross = axis.cross(limits.min());
for (child, tree) in items.iter().zip(tree.iter_mut()) { for (child, tree) in items.into_iter().zip(tree.iter_mut()) {
let c_size = child.size(); let c_size = child.size();
let cross_fill_factor = match axis { let cross_fill_factor = match axis {
Axis::Horizontal => c_size.height, Axis::Horizontal => c_size.height,
@ -271,7 +271,7 @@ pub fn resolve_wrapper<'a, Message>(
cross = fill_cross; cross = fill_cross;
} }
for (i, (child, tree)) in items.iter().zip(tree.iter_mut()).enumerate() { for (i, (child, tree)) in items.into_iter().zip(tree.iter_mut()).enumerate() {
let c_size = child.size(); let c_size = child.size();
let fill_factor = match axis { let fill_factor = match axis {
Axis::Horizontal => c_size.width, Axis::Horizontal => c_size.width,
@ -314,7 +314,7 @@ pub fn resolve_wrapper<'a, Message>(
let remaining = available.max(0.0); let remaining = available.max(0.0);
for (i, (child, tree)) in items.iter().zip(tree.iter_mut()).enumerate() { for (i, (child, tree)) in items.into_iter().zip(tree.iter_mut()).enumerate() {
let c_size = child.size(); let c_size = child.size();
let fill_factor = match axis { let fill_factor = match axis {
Axis::Horizontal => c_size.width, Axis::Horizontal => c_size.width,

View file

@ -26,7 +26,7 @@ use crate::{
}, },
}; };
use iced::{Point, Shadow, Vector, window}; use iced::{Point, Shadow, Vector, event::Status, window};
use iced_core::Border; use iced_core::Border;
use iced_widget::core::{ use iced_widget::core::{
Alignment, Clipboard, Element, Layout, Length, Padding, Rectangle, Shell, Widget, event, Alignment, Clipboard, Element, Layout, Length, Padding, Rectangle, Shell, Widget, event,
@ -533,14 +533,14 @@ where
menu_roots_children(&self.menu_roots) menu_roots_children(&self.menu_roots)
} }
fn layout(&self, tree: &mut Tree, renderer: &Renderer, limits: &Limits) -> Node { fn layout(&mut self, tree: &mut Tree, renderer: &Renderer, limits: &Limits) -> Node {
use super::flex; use super::flex;
let limits = limits.width(self.width).height(self.height); let limits = limits.width(self.width).height(self.height);
let children = self let mut children = self
.menu_roots .menu_roots
.iter() .iter_mut()
.map(|root| &root.item) .map(|root| &mut root.item)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
// the first children of the tree are the menu roots items // the first children of the tree are the menu roots items
let mut tree_children = tree let mut tree_children = tree
@ -555,28 +555,28 @@ where
self.padding, self.padding,
self.spacing, self.spacing,
Alignment::Center, Alignment::Center,
&children, &mut children,
&mut tree_children, &mut tree_children,
) )
} }
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
fn on_event( fn update(
&mut self, &mut self,
tree: &mut Tree, tree: &mut Tree,
event: event::Event, event: &event::Event,
layout: Layout<'_>, layout: Layout<'_>,
view_cursor: Cursor, view_cursor: Cursor,
renderer: &Renderer, renderer: &Renderer,
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
viewport: &Rectangle, viewport: &Rectangle,
) -> event::Status { ) {
use event::Event::{Mouse, Touch}; use event::Event::{Mouse, Touch};
use mouse::{Button::Left, Event::ButtonReleased}; use mouse::{Button::Left, Event::ButtonReleased};
use touch::Event::{FingerLifted, FingerLost}; use touch::Event::{FingerLifted, FingerLost};
let root_status = process_root_events( process_root_events(
&mut self.menu_roots, &mut self.menu_roots,
view_cursor, view_cursor,
tree, tree,
@ -638,7 +638,7 @@ where
}); });
if !create_popup { if !create_popup {
return event::Status::Ignored; return;
} }
#[cfg(all( #[cfg(all(
feature = "multi-window", feature = "multi-window",
@ -665,8 +665,6 @@ where
} }
_ => (), _ => (),
} }
root_status
} }
fn draw( fn draw(
@ -704,6 +702,7 @@ where
..Default::default() ..Default::default()
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}; };
renderer.fill_quad(path_quad, styling.path); renderer.fill_quad(path_quad, styling.path);
@ -731,8 +730,9 @@ where
fn overlay<'b>( fn overlay<'b>(
&'b mut self, &'b mut self,
tree: &'b mut Tree, tree: &'b mut Tree,
layout: Layout<'_>, layout: Layout<'b>,
_renderer: &Renderer, _renderer: &Renderer,
viewport: &Rectangle,
translation: Vector, translation: Vector,
) -> Option<overlay::Element<'b, Message, crate::Theme, Renderer>> { ) -> Option<overlay::Element<'b, Message, crate::Theme, Renderer>> {
#[cfg(all( #[cfg(all(
@ -799,18 +799,16 @@ fn process_root_events<Message>(
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
viewport: &Rectangle, viewport: &Rectangle,
) -> event::Status ) {
where
{
menu_roots menu_roots
.iter_mut() .iter_mut()
.zip(&mut tree.children) .zip(&mut tree.children)
.zip(layout.children()) .zip(layout.children())
.map(|((root, t), lo)| { .map(|((root, t), lo)| {
// assert!(t.tag == tree::Tag::stateless()); // assert!(t.tag == tree::Tag::stateless());
root.item.on_event( root.item.update(
&mut t.children[root.index], &mut t.children[root.index],
event.clone(), event,
lo, lo,
view_cursor, view_cursor,
renderer, renderer,
@ -818,6 +816,5 @@ where
shell, shell,
viewport, viewport,
) )
}) });
.fold(event::Status::Ignored, event::Status::merge)
} }

View file

@ -310,7 +310,7 @@ pub(crate) struct MenuState {
} }
impl MenuState { impl MenuState {
pub(super) fn layout<Message>( pub(super) fn layout<Message>(
&self, &mut self,
overlay_offset: Vector, overlay_offset: Vector,
slice: MenuSlice, slice: MenuSlice,
renderer: &crate::Renderer, renderer: &crate::Renderer,
@ -329,8 +329,8 @@ impl MenuState {
// viewport space children bounds // viewport space children bounds
let children_bounds = self.menu_bounds.children_bounds + overlay_offset; let children_bounds = self.menu_bounds.children_bounds + overlay_offset;
let child_nodes = self.menu_bounds.child_positions[start_index..=end_index] let child_nodes = self.menu_bounds.child_positions[start_index..=end_index]
.iter() .iter_mut()
.zip(self.menu_bounds.child_sizes[start_index..=end_index].iter()) .zip(self.menu_bounds.child_sizes[start_index..=end_index].iter_mut())
.zip(menu_tree[start_index..=end_index].iter()) .zip(menu_tree[start_index..=end_index].iter())
.map(|((cp, size), mt)| { .map(|((cp, size), mt)| {
let mut position = *cp; let mut position = *cp;
@ -347,7 +347,11 @@ impl MenuState {
let limits = Limits::new(size, size); let limits = Limits::new(size, size);
mt.item mt.item
.layout(&mut tree[mt.index], renderer, &limits) .element
.with_data_mut(|e| {
e.as_widget_mut()
.layout(&mut tree[mt.index], renderer, &limits)
})
.move_to(Point::new(0.0, position + self.scroll_offset)) .move_to(Point::new(0.0, position + self.scroll_offset))
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -360,7 +364,7 @@ impl MenuState {
overlay_offset: Vector, overlay_offset: Vector,
index: usize, index: usize,
renderer: &crate::Renderer, renderer: &crate::Renderer,
menu_tree: &MenuTree<Message>, menu_tree: &mut MenuTree<Message>,
tree: &mut Tree, tree: &mut Tree,
) -> Node { ) -> Node {
// viewport space children bounds // viewport space children bounds
@ -499,7 +503,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
} else { } else {
self.depth self.depth
}] }]
.iter() .iter_mut()
.enumerate() .enumerate()
.filter(|ms| self.is_overlay || ms.0 < 1) .filter(|ms| self.is_overlay || ms.0 < 1)
.fold( .fold(
@ -545,15 +549,15 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
} }
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
fn on_event( fn update(
&mut self, &mut self,
event: event::Event, event: &event::Event,
layout: Layout<'_>, layout: Layout<'_>,
view_cursor: Cursor, view_cursor: Cursor,
renderer: &crate::Renderer, renderer: &crate::Renderer,
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
) -> (Option<(usize, MenuState)>, event::Status) { ) -> Option<(usize, MenuState)> {
use event::{ use event::{
Event::{Mouse, Touch}, Event::{Mouse, Touch},
Status::{Captured, Ignored}, Status::{Captured, Ignored},
@ -569,7 +573,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
.inner .inner
.with_data(|data| data.open || data.active_root.len() <= self.depth) .with_data(|data| data.open || data.active_root.len() <= self.depth)
{ {
return (None, Ignored); return None;
} }
let viewport = layout.bounds(); let viewport = layout.bounds();
@ -583,7 +587,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
}; };
let menu_status = process_menu_events( let menu_status = process_menu_events(
self, self,
event.clone(), &event,
view_cursor, view_cursor,
renderer, renderer,
clipboard, clipboard,
@ -602,25 +606,28 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
self.main_offset as f32, self.main_offset as f32,
); );
let ret = match event { match event {
Mouse(WheelScrolled { delta }) => { Mouse(WheelScrolled { delta }) => process_scroll_events(
process_scroll_events(self, delta, overlay_cursor, viewport_size, overlay_offset) self,
.merge(menu_status) shell,
} *delta,
overlay_cursor,
viewport_size,
overlay_offset,
),
Mouse(ButtonPressed(Left)) | Touch(FingerPressed { .. }) => { Mouse(ButtonPressed(Left)) | Touch(FingerPressed { .. }) => {
self.tree.inner.with_data_mut(|data| { self.tree.inner.with_data_mut(|data| {
data.pressed = true; data.pressed = true;
data.view_cursor = view_cursor; data.view_cursor = view_cursor;
}); });
Captured
} }
Mouse(CursorMoved { position }) | Touch(FingerMoved { position, .. }) => { Mouse(CursorMoved { position }) | Touch(FingerMoved { position, .. }) => {
let view_cursor = Cursor::Available(position); let view_cursor = Cursor::Available(*position);
let overlay_cursor = view_cursor.position().unwrap_or_default() - overlay_offset; let overlay_cursor = view_cursor.position().unwrap_or_default() - overlay_offset;
if !self.is_overlay && !view_cursor.is_over(viewport) { if !self.is_overlay && !view_cursor.is_over(viewport) {
return (None, menu_status); return None;
} }
let (new_root, status) = process_overlay_events( let (new_root, status) = process_overlay_events(
@ -634,7 +641,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
shell, shell,
); );
return (new_root, status.merge(menu_status)); return new_root;
} }
Mouse(ButtonReleased(_)) | Touch(FingerLifted { .. }) => { Mouse(ButtonReleased(_)) | Touch(FingerLifted { .. }) => {
@ -694,23 +701,19 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
} }
state.reset(); state.reset();
return Captured;
} }
} }
// close all menus when clicking inside the menu bar // close all menus when clicking inside the menu bar
if self.bar_bounds.contains(overlay_cursor) { if self.bar_bounds.contains(overlay_cursor) {
state.reset(); state.reset();
Captured
} else {
menu_status
} }
}) })
} }
_ => menu_status, _ => {}
}; };
(None, ret) None
} }
#[allow(unused_results, clippy::too_many_lines)] #[allow(unused_results, clippy::too_many_lines)]
@ -734,7 +737,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
let render_bounds = if self.is_overlay { let render_bounds = if self.is_overlay {
Rectangle::new(Point::ORIGIN, viewport.size()) Rectangle::new(Point::ORIGIN, viewport.size())
} else { } else {
Rectangle::new(Point::ORIGIN, Size::INFINITY) Rectangle::new(Point::ORIGIN, Size::INFINITE)
}; };
let styling = theme.appearance(&self.style); let styling = theme.appearance(&self.style);
@ -796,6 +799,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
color: styling.border_color, color: styling.border_color,
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}; };
let menu_color = styling.background; let menu_color = styling.background;
r.fill_quad(menu_quad, menu_color); r.fill_quad(menu_quad, menu_color);
@ -815,6 +819,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
..Default::default() ..Default::default()
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}; };
r.fill_quad(path_quad, styling.path); r.fill_quad(path_quad, styling.path);
@ -867,17 +872,16 @@ impl<Message: Clone + 'static> overlay::Overlay<Message, crate::Theme, crate::Re
) )
} }
fn on_event( fn update(
&mut self, &mut self,
event: iced::Event, event: &iced::Event,
layout: Layout<'_>, layout: Layout<'_>,
cursor: mouse::Cursor, cursor: mouse::Cursor,
renderer: &crate::Renderer, renderer: &crate::Renderer,
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
) -> event::Status { ) {
self.on_event(event, layout, cursor, renderer, clipboard, shell) self.update(event, layout, cursor, renderer, clipboard, shell);
.1
} }
fn draw( fn draw(
@ -903,7 +907,7 @@ impl<Message: std::clone::Clone + 'static> Widget<Message, crate::Theme, crate::
} }
fn layout( fn layout(
&self, &mut self,
_tree: &mut Tree, _tree: &mut Tree,
renderer: &crate::Renderer, renderer: &crate::Renderer,
limits: &iced_core::layout::Limits, limits: &iced_core::layout::Limits,
@ -925,18 +929,18 @@ impl<Message: std::clone::Clone + 'static> Widget<Message, crate::Theme, crate::
} }
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
fn on_event( fn update(
&mut self, &mut self,
tree: &mut Tree, tree: &mut Tree,
event: iced::Event, event: &iced::Event,
layout: Layout<'_>, layout: Layout<'_>,
cursor: mouse::Cursor, cursor: mouse::Cursor,
renderer: &crate::Renderer, renderer: &crate::Renderer,
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
viewport: &Rectangle, viewport: &Rectangle,
) -> event::Status { ) {
let (new_root, status) = self.on_event(event, layout, cursor, renderer, clipboard, shell); let new_root = self.update(event, layout, cursor, renderer, clipboard, shell);
#[cfg(all( #[cfg(all(
feature = "multi-window", feature = "multi-window",
@ -997,7 +1001,7 @@ impl<Message: std::clone::Clone + 'static> Widget<Message, crate::Theme, crate::
Some((popup_menu, popup_id)) Some((popup_menu, popup_id))
}) else { }) else {
return status; return;
}; };
// XXX we push a new active root manually instead // XXX we push a new active root manually instead
init_root_popup_menu( init_root_popup_menu(
@ -1040,7 +1044,7 @@ impl<Message: std::clone::Clone + 'static> Widget<Message, crate::Theme, crate::
}); });
let menu_node = Widget::layout( let menu_node = Widget::layout(
&menu, &mut menu,
&mut Tree::empty(), &mut Tree::empty(),
renderer, renderer,
&Limits::NONE.min_width(1.).min_height(1.), &Limits::NONE.min_width(1.).min_height(1.),
@ -1082,10 +1086,9 @@ impl<Message: std::clone::Clone + 'static> Widget<Message, crate::Theme, crate::
), ),
)); ));
return status; return;
} }
} }
status
} }
} }
@ -1103,8 +1106,8 @@ fn pad_rectangle(rect: Rectangle, padding: Padding) -> Rectangle {
Rectangle { Rectangle {
x: rect.x - padding.left, x: rect.x - padding.left,
y: rect.y - padding.top, y: rect.y - padding.top,
width: rect.width + padding.horizontal(), width: rect.width + padding.x(),
height: rect.height + padding.vertical(), height: rect.height + padding.y(),
} }
} }
@ -1274,15 +1277,13 @@ pub(super) fn init_root_popup_menu<Message>(
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn process_menu_events<Message: std::clone::Clone>( fn process_menu_events<Message: std::clone::Clone>(
menu: &mut Menu<Message>, menu: &mut Menu<Message>,
event: event::Event, event: &event::Event,
view_cursor: Cursor, view_cursor: Cursor,
renderer: &crate::Renderer, renderer: &crate::Renderer,
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
overlay_offset: Vector, overlay_offset: Vector,
) -> event::Status { ) {
use event::Status;
let my_state = &mut menu.tree; let my_state = &mut menu.tree;
let menu_roots = match &mut menu.menu_roots { let menu_roots = match &mut menu.menu_roots {
Cow::Borrowed(_) => panic!(), Cow::Borrowed(_) => panic!(),
@ -1290,15 +1291,15 @@ fn process_menu_events<Message: std::clone::Clone>(
}; };
my_state.inner.with_data_mut(|state| { my_state.inner.with_data_mut(|state| {
if state.active_root.len() <= menu.depth { if state.active_root.len() <= menu.depth {
return event::Status::Ignored; return;
} }
let Some(hover) = state.menu_states.last_mut() else { let Some(hover) = state.menu_states.last_mut() else {
return Status::Ignored; return;
}; };
let Some(hover_index) = hover.index else { let Some(hover_index) = hover.index else {
return Status::Ignored; return;
}; };
let mt = state.active_root.iter().skip(1).fold( let mt = state.active_root.iter().skip(1).fold(
@ -1321,7 +1322,7 @@ fn process_menu_events<Message: std::clone::Clone>(
let child_layout = Layout::new(&child_node); let child_layout = Layout::new(&child_node);
// process only the last widget // process only the last widget
mt.item.on_event( mt.item.update(
tree, tree,
event, event,
child_layout, child_layout,
@ -1330,7 +1331,7 @@ fn process_menu_events<Message: std::clone::Clone>(
clipboard, clipboard,
shell, shell,
&Rectangle::default(), &Rectangle::default(),
) );
}) })
} }
@ -1561,12 +1562,12 @@ where
fn process_scroll_events<Message>( fn process_scroll_events<Message>(
menu: &mut Menu<'_, Message>, menu: &mut Menu<'_, Message>,
shell: &mut Shell<'_, Message>,
delta: mouse::ScrollDelta, delta: mouse::ScrollDelta,
overlay_cursor: Point, overlay_cursor: Point,
viewport_size: Size, viewport_size: Size,
overlay_offset: Vector, overlay_offset: Vector,
) -> event::Status ) where
where
Message: Clone, Message: Clone,
{ {
use event::Status::{Captured, Ignored}; use event::Status::{Captured, Ignored};
@ -1590,12 +1591,12 @@ where
// update // update
if state.menu_states.is_empty() { if state.menu_states.is_empty() {
return Ignored; return;
} else if state.menu_states.len() == 1 { } else if state.menu_states.len() == 1 {
let last_ms = &mut state.menu_states[0]; let last_ms = &mut state.menu_states[0];
if last_ms.index.is_none() { if last_ms.index.is_none() {
return Captured; return;
} }
let (max_offset, min_offset) = calc_offset_bounds(last_ms, viewport_size); let (max_offset, min_offset) = calc_offset_bounds(last_ms, viewport_size);
@ -1616,7 +1617,8 @@ where
.children_bounds .children_bounds
.contains(overlay_cursor) .contains(overlay_cursor)
{ {
return Captured; shell.capture_event();
return;
} }
// scroll the second last one // scroll the second last one
@ -1632,8 +1634,8 @@ where
last_two[1].menu_bounds.check_bounds.y += clamped_delta_y; last_two[1].menu_bounds.check_bounds.y += clamped_delta_y;
} }
} }
Captured shell.capture_event();
}) });
} }
#[allow(clippy::pedantic)] #[allow(clippy::pedantic)]
@ -1666,11 +1668,11 @@ fn get_children_layout<Message>(
.map(|mt| { .map(|mt| {
mt.item mt.item
.element .element
.with_data(|w| match w.as_widget().size().height { .with_data_mut(|w| match w.as_widget_mut().size().height {
Length::Fixed(f) => Size::new(width, f), Length::Fixed(f) => Size::new(width, f),
Length::Shrink => { Length::Shrink => {
let l_height = w let l_height = w
.as_widget() .as_widget_mut()
.layout( .layout(
&mut tree[mt.index], &mut tree[mt.index],
renderer, renderer,

View file

@ -253,13 +253,16 @@ pub fn menu_items<
let key = find_key(&action, key_binds); let key = find_key(&action, key_binds);
let mut items = vec![ let mut items = vec![
widget::text(l).into(), widget::text(l).into(),
widget::horizontal_space().into(), widget::space::horizontal().into(),
widget::text(key).class(key_class).into(), widget::text(key).class(key_class).into(),
]; ];
if let Some(icon) = icon { if let Some(icon) = icon {
items.insert(0, widget::icon::icon(icon).size(14).into()); items.insert(0, widget::icon::icon(icon).size(14).into());
items.insert(1, widget::Space::with_width(spacing.space_xxs).into()); items.insert(
1,
widget::space::horizontal().width(spacing.space_xxs).into(),
);
} }
let menu_button = menu_button(items).on_press(action.message()); let menu_button = menu_button(items).on_press(action.message());
@ -273,13 +276,16 @@ pub fn menu_items<
let mut items = vec![ let mut items = vec![
widget::text(l).into(), widget::text(l).into(),
widget::horizontal_space().into(), widget::space::horizontal().into(),
widget::text(key).class(key_class).into(), widget::text(key).class(key_class).into(),
]; ];
if let Some(icon) = icon { if let Some(icon) = icon {
items.insert(0, widget::icon::icon(icon).size(14).into()); items.insert(0, widget::icon::icon(icon).size(14).into());
items.insert(1, widget::Space::with_width(spacing.space_xxs).into()); items.insert(
1,
widget::space::horizontal().width(spacing.space_xxs).into(),
);
} }
let menu_button = menu_button(items); let menu_button = menu_button(items);
@ -301,16 +307,21 @@ pub fn menu_items<
.width(Length::Fixed(16.0)) .width(Length::Fixed(16.0))
.into() .into()
} else { } else {
widget::Space::with_width(Length::Fixed(16.0)).into() widget::space::horizontal()
.width(Length::Fixed(16.0))
.into()
}, },
widget::Space::with_width(spacing.space_xxs).into(), widget::space::horizontal().width(spacing.space_xxs).into(),
widget::text(label).align_x(iced::Alignment::Start).into(), widget::text(label).align_x(iced::Alignment::Start).into(),
widget::horizontal_space().into(), widget::space::horizontal().into(),
widget::text(key).class(key_class).into(), widget::text(key).class(key_class).into(),
]; ];
if let Some(icon) = icon { if let Some(icon) = icon {
items.insert(1, widget::Space::with_width(spacing.space_xxs).into()); items.insert(
1,
widget::space::horizontal().width(spacing.space_xxs).into(),
);
items.insert(2, widget::icon::icon(icon).size(14).into()); items.insert(2, widget::icon::icon(icon).size(14).into());
} }
@ -325,7 +336,7 @@ pub fn menu_items<
RcElementWrapper::new(crate::Element::from( RcElementWrapper::new(crate::Element::from(
menu_button::<'static, _>(vec![ menu_button::<'static, _>(vec![
widget::text(l.clone()).into(), widget::text(l.clone()).into(),
widget::horizontal_space().into(), widget::space::horizontal().into(),
widget::icon::from_name("pan-end-symbolic") widget::icon::from_name("pan-end-symbolic")
.size(16) .size(16)
.icon() .icon()

View file

@ -60,7 +60,7 @@ pub use iced::widget::{ComboBox, combo_box};
pub use iced::widget::{Container, container}; pub use iced::widget::{Container, container};
#[doc(inline)] #[doc(inline)]
pub use iced::widget::{Space, horizontal_space, vertical_space}; pub use iced::widget::{Space, space};
#[doc(inline)] #[doc(inline)]
pub use iced::widget::{Image, image}; pub use iced::widget::{Image, image};
@ -175,47 +175,47 @@ pub use dialog::{Dialog, dialog};
pub mod divider { pub mod divider {
/// Horizontal variant of a divider. /// Horizontal variant of a divider.
pub mod horizontal { pub mod horizontal {
use iced::widget::{Rule, horizontal_rule}; use iced::{widget::Rule, widget::rule};
/// Horizontal divider with default thickness /// Horizontal divider with default thickness
#[must_use] #[must_use]
pub fn default<'a>() -> Rule<'a, crate::Theme> { pub fn default<'a>() -> Rule<'a, crate::Theme> {
horizontal_rule(1).class(crate::theme::Rule::Default) rule::horizontal(1).class(crate::theme::Rule::Default)
} }
/// Horizontal divider with light thickness /// Horizontal divider with light thickness
#[must_use] #[must_use]
pub fn light<'a>() -> Rule<'a, crate::Theme> { pub fn light<'a>() -> Rule<'a, crate::Theme> {
horizontal_rule(1).class(crate::theme::Rule::LightDivider) rule::horizontal(1).class(crate::theme::Rule::LightDivider)
} }
/// Horizontal divider with heavy thickness. /// Horizontal divider with heavy thickness.
#[must_use] #[must_use]
pub fn heavy<'a>() -> Rule<'a, crate::Theme> { pub fn heavy<'a>() -> Rule<'a, crate::Theme> {
horizontal_rule(4).class(crate::theme::Rule::HeavyDivider) rule::horizontal(4).class(crate::theme::Rule::HeavyDivider)
} }
} }
/// Vertical variant of a divider. /// Vertical variant of a divider.
pub mod vertical { pub mod vertical {
use iced::widget::{Rule, vertical_rule}; use iced::widget::{Rule, rule};
/// Vertical divider with default thickness /// Vertical divider with default thickness
#[must_use] #[must_use]
pub fn default<'a>() -> Rule<'a, crate::Theme> { pub fn default<'a>() -> Rule<'a, crate::Theme> {
vertical_rule(1).class(crate::theme::Rule::Default) rule::vertical(1).class(crate::theme::Rule::Default)
} }
/// Vertical divider with light thickness /// Vertical divider with light thickness
#[must_use] #[must_use]
pub fn light<'a>() -> Rule<'a, crate::Theme> { pub fn light<'a>() -> Rule<'a, crate::Theme> {
vertical_rule(4).class(crate::theme::Rule::LightDivider) rule::vertical(4).class(crate::theme::Rule::LightDivider)
} }
/// Vertical divider with heavy thickness. /// Vertical divider with heavy thickness.
#[must_use] #[must_use]
pub fn heavy<'a>() -> Rule<'a, crate::Theme> { pub fn heavy<'a>() -> Rule<'a, crate::Theme> {
vertical_rule(10).class(crate::theme::Rule::HeavyDivider) rule::vertical(10).class(crate::theme::Rule::HeavyDivider)
} }
} }
} }

View file

@ -180,5 +180,6 @@ pub fn nav_bar_style(theme: &Theme) -> iced_widget::container::Style {
radius: cosmic.corner_radii.radius_s.into(), radius: cosmic.corner_radii.radius_s.into(),
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
} }
} }

View file

@ -3,6 +3,7 @@
//! A container which displays an overlay when a popup widget is attached. //! A container which displays an overlay when a popup widget is attached.
use iced::widget;
use iced_core::event::{self, Event}; use iced_core::event::{self, Event};
use iced_core::layout; use iced_core::layout;
use iced_core::mouse; use iced_core::mouse;
@ -33,6 +34,7 @@ pub enum Position {
/// A container which displays overlays when a popup widget is assigned. /// A container which displays overlays when a popup widget is assigned.
#[must_use] #[must_use]
pub struct Popover<'a, Message, Renderer> { pub struct Popover<'a, Message, Renderer> {
id: widget::Id,
content: Element<'a, Message, crate::Theme, Renderer>, content: Element<'a, Message, crate::Theme, Renderer>,
modal: bool, modal: bool,
popup: Option<Element<'a, Message, crate::Theme, Renderer>>, popup: Option<Element<'a, Message, crate::Theme, Renderer>>,
@ -43,6 +45,7 @@ pub struct Popover<'a, Message, Renderer> {
impl<'a, Message, Renderer> Popover<'a, Message, Renderer> { impl<'a, Message, Renderer> Popover<'a, Message, Renderer> {
pub fn new(content: impl Into<Element<'a, Message, crate::Theme, Renderer>>) -> Self { pub fn new(content: impl Into<Element<'a, Message, crate::Theme, Renderer>>) -> Self {
Self { Self {
id: widget::Id::unique(),
content: content.into(), content: content.into(),
modal: false, modal: false,
popup: None, popup: None,
@ -51,6 +54,13 @@ impl<'a, Message, Renderer> Popover<'a, Message, Renderer> {
} }
} }
/// Set the Id
#[inline]
pub fn id(mut self, id: widget::Id) -> Self {
self.id = id;
self
}
/// A modal popup intercepts user inputs while a popup is active. /// A modal popup intercepts user inputs while a popup is active.
#[inline] #[inline]
pub fn modal(mut self, modal: bool) -> Self { pub fn modal(mut self, modal: bool) -> Self {
@ -83,6 +93,14 @@ impl<Message: Clone, Renderer> Widget<Message, crate::Theme, Renderer>
where where
Renderer: iced_core::Renderer, Renderer: iced_core::Renderer,
{ {
fn id(&self) -> Option<widget::Id> {
Some(self.id.clone())
}
fn set_id(&mut self, id: widget::Id) {
self.id = id;
}
fn children(&self) -> Vec<Tree> { fn children(&self) -> Vec<Tree> {
if let Some(popup) = &self.popup { if let Some(popup) = &self.popup {
vec![Tree::new(&self.content), Tree::new(popup)] vec![Tree::new(&self.content), Tree::new(popup)]
@ -104,42 +122,53 @@ where
} }
fn layout( fn layout(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
renderer: &Renderer, renderer: &Renderer,
limits: &layout::Limits, limits: &layout::Limits,
) -> layout::Node { ) -> layout::Node {
let tree = content_tree_mut(tree); let tree = content_tree_mut(tree);
self.content.as_widget().layout(tree, renderer, limits) self.content.as_widget_mut().layout(tree, renderer, limits)
} }
fn operate( fn operate(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
layout: Layout<'_>, layout: Layout<'_>,
renderer: &Renderer, renderer: &Renderer,
operation: &mut dyn Operation<()>, operation: &mut dyn Operation,
) { ) {
self.content operation.container(Some(&self.id), layout.bounds());
.as_widget() operation.traverse(&mut |operation| {
.operate(content_tree_mut(tree), layout, renderer, operation); self.content.as_widget_mut().operate(
tree,
layout
.children()
.next()
.unwrap()
.with_virtual_offset(layout.virtual_offset()),
renderer,
operation,
);
});
} }
fn on_event( fn update(
&mut self, &mut self,
tree: &mut Tree, tree: &mut Tree,
event: Event, 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: &Rectangle, viewport: &Rectangle,
) -> event::Status { ) {
if self.popup.is_some() { if self.popup.is_some() {
if self.modal { if self.modal {
if matches!(event, Event::Mouse(_) | Event::Touch(_)) { if matches!(event, Event::Mouse(_) | Event::Touch(_)) {
return event::Status::Captured; shell.capture_event();
return;
} }
} else if let Some(on_close) = self.on_close.as_ref() { } else if let Some(on_close) = self.on_close.as_ref() {
if matches!( if matches!(
@ -153,7 +182,7 @@ where
} }
} }
self.content.as_widget_mut().on_event( self.content.as_widget_mut().update(
content_tree_mut(tree), content_tree_mut(tree),
event, event,
layout, layout,
@ -209,8 +238,9 @@ where
fn overlay<'b>( fn overlay<'b>(
&'b mut self, &'b mut self,
tree: &'b mut Tree, tree: &'b mut Tree,
layout: Layout<'_>, layout: Layout<'b>,
renderer: &Renderer, renderer: &Renderer,
viewport: &Rectangle,
mut translation: Vector, mut translation: Vector,
) -> Option<overlay::Element<'b, Message, crate::Theme, Renderer>> { ) -> Option<overlay::Element<'b, Message, crate::Theme, Renderer>> {
if let Some(popup) = &mut self.popup { if let Some(popup) = &mut self.popup {
@ -248,6 +278,7 @@ where
content_tree_mut(tree), content_tree_mut(tree),
layout, layout,
renderer, renderer,
viewport,
translation, translation,
) )
} }
@ -312,7 +343,7 @@ where
let limits = layout::Limits::new(Size::UNIT, bounds); let limits = layout::Limits::new(Size::UNIT, bounds);
let node = self let node = self
.content .content
.as_widget() .as_widget_mut()
.layout(self.tree, renderer, &limits); .layout(self.tree, renderer, &limits);
match self.position { match self.position {
Position::Center => { Position::Center => {
@ -353,27 +384,28 @@ where
operation: &mut dyn Operation<()>, operation: &mut dyn Operation<()>,
) { ) {
self.content self.content
.as_widget() .as_widget_mut()
.operate(self.tree, layout, renderer, operation); .operate(self.tree, layout, renderer, operation);
} }
fn on_event( fn update(
&mut self, &mut self,
event: Event, 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>,
) -> event::Status { ) {
if self.modal if self.modal
&& matches!(event, Event::Mouse(_) | Event::Touch(_)) && matches!(event, Event::Mouse(_) | Event::Touch(_))
&& !cursor_position.is_over(layout.bounds()) && !cursor_position.is_over(layout.bounds())
{ {
return event::Status::Captured; shell.capture_event();
return;
} }
self.content.as_widget_mut().on_event( self.content.as_widget_mut().update(
self.tree, self.tree,
event, event,
layout, layout,
@ -389,7 +421,6 @@ where
&self, &self,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: mouse::Cursor, cursor_position: mouse::Cursor,
viewport: &Rectangle,
renderer: &Renderer, renderer: &Renderer,
) -> mouse::Interaction { ) -> mouse::Interaction {
if self.modal && !cursor_position.is_over(layout.bounds()) { if self.modal && !cursor_position.is_over(layout.bounds()) {
@ -400,7 +431,7 @@ where
self.tree, self.tree,
layout, layout,
cursor_position, cursor_position,
viewport, &layout.bounds(),
renderer, renderer,
) )
} }
@ -427,12 +458,16 @@ where
fn overlay<'c>( fn overlay<'c>(
&'c mut self, &'c mut self,
layout: Layout<'_>, layout: Layout<'c>,
renderer: &Renderer, renderer: &Renderer,
) -> Option<overlay::Element<'c, Message, crate::Theme, Renderer>> { ) -> Option<overlay::Element<'c, Message, crate::Theme, Renderer>> {
self.content self.content.as_widget_mut().overlay(
.as_widget_mut() self.tree,
.overlay(self.tree, layout, renderer, Default::default()) layout,
renderer,
&layout.bounds(),
Default::default(),
)
} }
} }

View file

@ -175,7 +175,7 @@ where
} }
fn layout( fn layout(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
renderer: &Renderer, renderer: &Renderer,
limits: &layout::Limits, limits: &layout::Limits,
@ -186,20 +186,20 @@ where
|_| layout::Node::new(Size::new(self.size, self.size)), |_| layout::Node::new(Size::new(self.size, self.size)),
|limits| { |limits| {
self.label self.label
.as_widget() .as_widget_mut()
.layout(&mut tree.children[0], renderer, limits) .layout(&mut tree.children[0], renderer, limits)
}, },
) )
} }
fn operate( fn operate(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
layout: Layout<'_>, layout: Layout<'_>,
renderer: &Renderer, renderer: &Renderer,
operation: &mut dyn iced_core::widget::Operation<()>, operation: &mut dyn iced_core::widget::Operation<()>,
) { ) {
self.label.as_widget().operate( self.label.as_widget_mut().operate(
&mut tree.children[0], &mut tree.children[0],
layout.children().nth(1).unwrap(), layout.children().nth(1).unwrap(),
renderer, renderer,
@ -207,20 +207,20 @@ where
); );
} }
fn on_event( fn update(
&mut self, &mut self,
tree: &mut Tree, tree: &mut Tree,
event: Event, event: &Event,
layout: Layout<'_>, layout: Layout<'_>,
cursor: mouse::Cursor, cursor: mouse::Cursor,
renderer: &Renderer, renderer: &Renderer,
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
viewport: &Rectangle, viewport: &Rectangle,
) -> event::Status { ) {
let status = self.label.as_widget_mut().on_event( self.label.as_widget_mut().update(
&mut tree.children[0], &mut tree.children[0],
event.clone(), event,
layout.children().nth(1).unwrap(), layout.children().nth(1).unwrap(),
cursor, cursor,
renderer, renderer,
@ -229,22 +229,19 @@ where
viewport, viewport,
); );
if status == event::Status::Ignored { if !shell.is_event_captured() {
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 { .. }) => {
if cursor.is_over(layout.bounds()) { if cursor.is_over(layout.bounds()) {
shell.publish(self.on_click.clone()); shell.publish(self.on_click.clone());
return event::Status::Captured; shell.capture_event();
return;
} }
} }
_ => {} _ => {}
} }
event::Status::Ignored
} else {
status
} }
} }
@ -359,14 +356,16 @@ where
fn overlay<'b>( fn overlay<'b>(
&'b mut self, &'b mut self,
tree: &'b mut Tree, tree: &'b mut Tree,
layout: Layout<'_>, layout: Layout<'b>,
renderer: &Renderer, renderer: &Renderer,
viewport: &Rectangle,
translation: Vector, translation: Vector,
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> { ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
self.label.as_widget_mut().overlay( self.label.as_widget_mut().overlay(
&mut tree.children[0], &mut tree.children[0],
layout.children().nth(1).unwrap(), layout.children().nth(1).unwrap(),
renderer, renderer,
viewport,
translation, translation,
) )
} }

View file

@ -204,7 +204,7 @@ where
} }
fn layout( fn layout(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
renderer: &Renderer, renderer: &Renderer,
limits: &layout::Limits, limits: &layout::Limits,
@ -221,7 +221,7 @@ where
} }
fn operate( fn operate(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
layout: Layout<'_>, layout: Layout<'_>,
renderer: &Renderer, renderer: &Renderer,
@ -230,18 +230,18 @@ where
self.container.operate(tree, layout, renderer, operation); self.container.operate(tree, layout, renderer, operation);
} }
fn on_event( fn update(
&mut self, &mut self,
tree: &mut Tree, tree: &mut Tree,
event: Event, 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_core::Rectangle, viewport: &iced_core::Rectangle,
) -> event::Status { ) {
self.container.on_event( self.container.update(
tree, tree,
event, event,
layout, layout,
@ -290,11 +290,13 @@ where
fn overlay<'b>( fn overlay<'b>(
&'b mut self, &'b mut self,
tree: &'b mut Tree, tree: &'b mut Tree,
layout: Layout<'_>, layout: Layout<'b>,
renderer: &Renderer, renderer: &Renderer,
viewport: &Rectangle,
translation: Vector, translation: Vector,
) -> Option<overlay::Element<'b, Message, crate::Theme, Renderer>> { ) -> Option<overlay::Element<'b, Message, crate::Theme, Renderer>> {
self.container.overlay(tree, layout, renderer, translation) self.container
.overlay(tree, layout, renderer, viewport, translation)
} }
fn drag_destinations( fn drag_destinations(

View file

@ -18,10 +18,10 @@ pub fn rectangle_tracker_subscription<
>( >(
id: I, id: I,
) -> Subscription<(I, RectangleUpdate<R>)> { ) -> Subscription<(I, RectangleUpdate<R>)> {
Subscription::run_with_id( Subscription::run_with(id, |id| {
id, let id = *id;
stream::unfold(State::Ready, move |state| start_listening(id, state)), stream::unfold(State::Ready, move |state| start_listening(id, state))
) })
} }
pub enum State<I> { pub enum State<I> {

View file

@ -6,7 +6,7 @@ use iced_core::layout;
use iced_core::mouse; use iced_core::mouse;
use iced_core::overlay; use iced_core::overlay;
use iced_core::renderer; use iced_core::renderer;
use iced_core::widget::{Id, Tree, tree}; use iced_core::widget::{Id, Operation, Tree, tree};
use iced_core::{Clipboard, Element, Layout, Length, Rectangle, Shell, Vector, Widget}; use iced_core::{Clipboard, Element, Layout, Length, Rectangle, Shell, Vector, Widget};
pub(crate) fn responsive_container<'a, Message: 'static, Theme, E>( pub(crate) fn responsive_container<'a, Message: 'static, Theme, E>(
@ -89,7 +89,7 @@ where
} }
fn layout( fn layout(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
renderer: &Renderer, renderer: &Renderer,
limits: &layout::Limits, limits: &layout::Limits,
@ -98,7 +98,7 @@ where
let unrestricted_size = self.size.unwrap_or_else(|| { let unrestricted_size = self.size.unwrap_or_else(|| {
let node = let node =
self.content self.content
.as_widget() .as_widget_mut()
.layout(&mut tree.children[0], renderer, &Limits::NONE); .layout(&mut tree.children[0], renderer, &Limits::NONE);
node.size() node.size()
}); });
@ -115,22 +115,23 @@ where
let node = self let node = self
.content .content
.as_widget() .as_widget_mut()
.layout(&mut tree.children[0], renderer, limits); .layout(&mut tree.children[0], renderer, limits);
let size = node.size(); let size = node.size();
layout::Node::with_children(size, vec![node]) layout::Node::with_children(size, vec![node])
} }
fn operate( fn operate(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
layout: Layout<'_>, layout: Layout<'_>,
renderer: &Renderer, renderer: &Renderer,
operation: &mut dyn iced_core::widget::Operation<()>, operation: &mut dyn Operation,
) { ) {
operation.container(Some(&self.id), layout.bounds(), &mut |operation| { operation.container(Some(&self.id), layout.bounds());
self.content.as_widget().operate( operation.traverse(&mut |operation| {
&mut tree.children[0], self.content.as_widget_mut().operate(
tree,
layout layout
.children() .children()
.next() .next()
@ -142,17 +143,17 @@ where
}); });
} }
fn on_event( fn update(
&mut self, &mut self,
tree: &mut Tree, tree: &mut Tree,
event: Event, 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: &Rectangle, viewport: &Rectangle,
) -> event::Status { ) {
let state = tree.state.downcast_mut::<State>(); let state = tree.state.downcast_mut::<State>();
if state.needs_update { if state.needs_update {
@ -166,7 +167,7 @@ where
state.needs_update = false; state.needs_update = false;
} }
self.content.as_widget_mut().on_event( self.content.as_widget_mut().update(
&mut tree.children[0], &mut tree.children[0],
event, event,
layout layout
@ -225,8 +226,9 @@ where
fn overlay<'b>( fn overlay<'b>(
&'b mut self, &'b mut self,
tree: &'b mut Tree, tree: &'b mut Tree,
layout: Layout<'_>, layout: Layout<'b>,
renderer: &Renderer, renderer: &Renderer,
viewport: &Rectangle,
translation: Vector, translation: Vector,
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> { ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
self.content.as_widget_mut().overlay( self.content.as_widget_mut().overlay(
@ -237,6 +239,7 @@ where
.unwrap() .unwrap()
.with_virtual_offset(layout.virtual_offset()), .with_virtual_offset(layout.virtual_offset()),
renderer, renderer,
viewport,
translation, translation,
) )
} }

View file

@ -23,7 +23,7 @@ use iced::{
event, keyboard, mouse, touch, window, event, keyboard, mouse, touch, window,
}; };
use iced_core::mouse::ScrollDelta; use iced_core::mouse::ScrollDelta;
use iced_core::text::{Ellipsize, LineHeight, Renderer as TextRenderer, Shaping, Wrapping}; use iced_core::text::{self, Ellipsize, LineHeight, Renderer as TextRenderer, Shaping, Wrapping};
use iced_core::widget::operation::Focusable; use iced_core::widget::operation::Focusable;
use iced_core::widget::{self, operation, tree}; use iced_core::widget::{self, operation, tree};
use iced_core::{Border, Point, Renderer as IcedRenderer, Shadow, Text}; use iced_core::{Border, Point, Renderer as IcedRenderer, Shadow, Text};
@ -265,22 +265,33 @@ where
} }
} }
let text = Text {
content: text.as_ref(),
size: iced::Pixels(self.font_size),
bounds: Size::INFINITY,
font,
horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Center,
shaping: Shaping::Advanced,
wrapping: Wrapping::None,
ellipsize: Ellipsize::None,
line_height: self.line_height,
};
if let Some(paragraph) = state.paragraphs.get_mut(key) { if let Some(paragraph) = state.paragraphs.get_mut(key) {
let text = Text {
content: text.as_ref(),
size: iced::Pixels(self.font_size),
bounds: Size::INFINITE,
font,
align_x: text::Alignment::Left,
align_y: alignment::Vertical::Center,
shaping: Shaping::Advanced,
wrapping: Wrapping::None,
line_height: self.line_height,
ellipsize: Ellipsize::default(),
};
paragraph.update(text); paragraph.update(text);
} else { } else {
let text = Text {
content: text.to_string(),
size: iced::Pixels(self.font_size),
bounds: Size::INFINITE,
font,
align_x: text::Alignment::Left,
align_y: alignment::Vertical::Center,
shaping: Shaping::Advanced,
wrapping: Wrapping::None,
line_height: self.line_height,
ellipsize: Ellipsize::default(),
};
state.paragraphs.insert(key, crate::Plain::new(text)); state.paragraphs.insert(key, crate::Plain::new(text));
} }
} }
@ -441,7 +452,7 @@ where
} }
/// Item the previous item in the widget. /// Item the previous item in the widget.
fn focus_previous(&mut self, state: &mut LocalState) -> event::Status { fn focus_previous(&mut self, state: &mut LocalState, shell: &mut Shell<'_, Message>) {
match state.focused_item { match state.focused_item {
Item::Tab(entity) => { Item::Tab(entity) => {
let mut keys = self.iterate_visible_tabs(state).rev(); let mut keys = self.iterate_visible_tabs(state).rev();
@ -455,7 +466,8 @@ where
} }
state.focused_item = Item::Tab(key); state.focused_item = Item::Tab(key);
return event::Status::Captured; shell.capture_event();
return;
} }
break; break;
@ -464,24 +476,28 @@ where
if self.prev_tab_sensitive(state) { if self.prev_tab_sensitive(state) {
state.focused_item = Item::PrevButton; state.focused_item = Item::PrevButton;
return event::Status::Captured; shell.capture_event();
return;
} }
} }
Item::NextButton => { Item::NextButton => {
if let Some(last) = self.last_tab(state) { if let Some(last) = self.last_tab(state) {
state.focused_item = Item::Tab(last); state.focused_item = Item::Tab(last);
return event::Status::Captured; shell.capture_event();
return;
} }
} }
Item::None => { Item::None => {
if self.next_tab_sensitive(state) { if self.next_tab_sensitive(state) {
state.focused_item = Item::NextButton; state.focused_item = Item::NextButton;
return event::Status::Captured; shell.capture_event();
return;
} else if let Some(last) = self.last_tab(state) { } else if let Some(last) = self.last_tab(state) {
state.focused_item = Item::Tab(last); state.focused_item = Item::Tab(last);
return event::Status::Captured; shell.capture_event();
return;
} }
} }
@ -489,11 +505,10 @@ where
} }
state.focused_item = Item::None; state.focused_item = Item::None;
event::Status::Ignored
} }
/// Item the next item in the widget. /// Item the next item in the widget.
fn focus_next(&mut self, state: &mut LocalState) -> event::Status { fn focus_next(&mut self, state: &mut LocalState, shell: &mut Shell<'_, Message>) {
match state.focused_item { match state.focused_item {
Item::Tab(entity) => { Item::Tab(entity) => {
let mut keys = self.iterate_visible_tabs(state); let mut keys = self.iterate_visible_tabs(state);
@ -506,7 +521,8 @@ where
} }
state.focused_item = Item::Tab(key); state.focused_item = Item::Tab(key);
return event::Status::Captured; shell.capture_event();
return;
} }
break; break;
@ -515,24 +531,28 @@ where
if self.next_tab_sensitive(state) { if self.next_tab_sensitive(state) {
state.focused_item = Item::NextButton; state.focused_item = Item::NextButton;
return event::Status::Captured; shell.capture_event();
return;
} }
} }
Item::PrevButton => { Item::PrevButton => {
if let Some(first) = self.first_tab(state) { if let Some(first) = self.first_tab(state) {
state.focused_item = Item::Tab(first); state.focused_item = Item::Tab(first);
return event::Status::Captured; shell.capture_event();
return;
} }
} }
Item::None => { Item::None => {
if self.prev_tab_sensitive(state) { if self.prev_tab_sensitive(state) {
state.focused_item = Item::PrevButton; state.focused_item = Item::PrevButton;
return event::Status::Captured; shell.capture_event();
return;
} else if let Some(first) = self.first_tab(state) { } else if let Some(first) = self.first_tab(state) {
state.focused_item = Item::Tab(first); state.focused_item = Item::Tab(first);
return event::Status::Captured; shell.capture_event();
return;
} }
} }
@ -540,7 +560,6 @@ where
} }
state.focused_item = Item::None; state.focused_item = Item::None;
event::Status::Ignored
} }
fn iterate_visible_tabs<'b>( fn iterate_visible_tabs<'b>(
@ -595,12 +614,12 @@ where
icon_spacing = f32::from(self.button_spacing); icon_spacing = f32::from(self.button_spacing);
let paragraph = entry.or_insert_with(|| { let paragraph = entry.or_insert_with(|| {
crate::Plain::new(Text { crate::Plain::new(Text {
content: text.as_ref(), content: text.to_string(), // TODO should we just use String at this point?
size: iced::Pixels(self.font_size), size: iced::Pixels(self.font_size),
bounds: Size::INFINITY, bounds: Size::INFINITE,
font, font,
horizontal_alignment: alignment::Horizontal::Left, align_x: text::Alignment::Left,
vertical_alignment: alignment::Vertical::Center, align_y: alignment::Vertical::Center,
shaping: Shaping::Advanced, shaping: Shaping::Advanced,
wrapping: Wrapping::default(), wrapping: Wrapping::default(),
ellipsize: Ellipsize::default(), ellipsize: Ellipsize::default(),
@ -888,7 +907,7 @@ where
} }
fn layout( fn layout(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
renderer: &Renderer, renderer: &Renderer,
limits: &layout::Limits, limits: &layout::Limits,
@ -902,17 +921,17 @@ where
} }
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
fn on_event( fn update(
&mut self, &mut self,
tree: &mut Tree, tree: &mut Tree,
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,
) -> event::Status { ) {
let my_bounds = layout.bounds(); let my_bounds = layout.bounds();
let state = tree.state.downcast_mut::<LocalState>(); let state = tree.state.downcast_mut::<LocalState>();
@ -941,7 +960,8 @@ where
"tab drag source finished id={:?}", "tab drag source finished id={:?}",
my_id my_id
); );
return event::Status::Captured; shell.capture_event();
return;
} }
} }
DndEvent::Offer( DndEvent::Offer(
@ -1137,8 +1157,8 @@ where
}); });
let (maybe_msg, ret) = state.dnd_state.on_data_received( let (maybe_msg, ret) = state.dnd_state.on_data_received(
mem::take(mime_type), mime_type.clone(),
mem::take(data), data.clone(),
None::<fn(_, _) -> Message>, None::<fn(_, _) -> Message>,
on_drop, on_drop,
); );
@ -1160,10 +1180,11 @@ where
} }
if let Some(on_reorder) = self.on_reorder.as_ref() { if let Some(on_reorder) = self.on_reorder.as_ref() {
shell.publish(on_reorder(event)); shell.publish(on_reorder(event));
return event::Status::Captured; shell.capture_event();
return;
} }
} }
return ret; return;
} }
} }
_ => {} _ => {}
@ -1175,7 +1196,7 @@ where
match event { match event {
Event::Touch(touch::Event::FingerPressed { id, .. }) => { Event::Touch(touch::Event::FingerPressed { id, .. }) => {
state.fingers_pressed.insert(id); state.fingers_pressed.insert(*id);
} }
Event::Touch(touch::Event::FingerLifted { id, .. }) => { Event::Touch(touch::Event::FingerLifted { id, .. }) => {
@ -1252,7 +1273,8 @@ where
|| (touch_lifted(&event) && fingers_pressed == 1)) || (touch_lifted(&event) && fingers_pressed == 1))
{ {
shell.publish(on_close(key)); shell.publish(on_close(key));
return event::Status::Captured; shell.capture_event();
return;
} }
if self.on_middle_press.is_none() { if self.on_middle_press.is_none() {
@ -1263,7 +1285,8 @@ where
{ {
if state.middle_clicked == Some(Item::Tab(key)) { if state.middle_clicked == Some(Item::Tab(key)) {
shell.publish(on_close(key)); shell.publish(on_close(key));
return event::Status::Captured; shell.capture_event();
return;
} }
state.middle_clicked = None; state.middle_clicked = None;
@ -1315,7 +1338,8 @@ where
state.set_focused(); state.set_focused();
state.focused_item = Item::Tab(key); state.focused_item = Item::Tab(key);
state.pressed_item = None; state.pressed_item = None;
return event::Status::Captured; shell.capture_event();
return;
} }
} }
} }
@ -1336,7 +1360,8 @@ where
}); });
shell.publish(on_context(key)); shell.publish(on_context(key));
return event::Status::Captured; shell.capture_event();
return;
} }
} }
} }
@ -1347,7 +1372,8 @@ where
state.middle_clicked = Some(Item::Tab(key)); state.middle_clicked = Some(Item::Tab(key));
if let Some(on_middle_press) = self.on_middle_press.as_ref() { if let Some(on_middle_press) = self.on_middle_press.as_ref() {
shell.publish(on_middle_press(key)); shell.publish(on_middle_press(key));
return event::Status::Captured; shell.capture_event();
return;
} }
} }
} }
@ -1374,7 +1400,7 @@ where
ScrollDelta::Lines { y, .. } | ScrollDelta::Pixels { y, .. } => { ScrollDelta::Lines { y, .. } | ScrollDelta::Pixels { y, .. } => {
let mut activate_key = None; let mut activate_key = None;
if y < 0.0 { if *y < 0.0 {
let mut prev_key = Entity::null(); let mut prev_key = Entity::null();
for key in self.model.order.iter().copied() { for key in self.model.order.iter().copied() {
@ -1386,7 +1412,7 @@ where
prev_key = key; prev_key = key;
} }
} }
} else if y > 0.0 { } else if *y > 0.0 {
let mut buttons = self.model.order.iter().copied(); let mut buttons = self.model.order.iter().copied();
while let Some(key) = buttons.next() { while let Some(key) = buttons.next() {
if self.model.is_active(key) { if self.model.is_active(key) {
@ -1405,7 +1431,8 @@ where
shell.publish(on_activate(key)); shell.publish(on_activate(key));
state.set_focused(); state.set_focused();
state.focused_item = Item::Tab(key); state.focused_item = Item::Tab(key);
return event::Status::Captured; shell.capture_event();
return;
} }
} }
} }
@ -1424,7 +1451,7 @@ where
if is_pressed(&event) { if is_pressed(&event) {
state.unfocus(); state.unfocus();
state.pressed_item = None; state.pressed_item = None;
return event::Status::Ignored; return;
} }
} else if is_lifted(&event) { } else if is_lifted(&event) {
state.pressed_item = None; state.pressed_item = None;
@ -1452,7 +1479,8 @@ where
position, position,
clipboard, clipboard,
) { ) {
return event::Status::Captured; shell.capture_event();
return;
} }
} }
} }
@ -1475,12 +1503,10 @@ where
}) = event }) = event
{ {
state.focused_visible = true; state.focused_visible = true;
return if modifiers == keyboard::Modifiers::SHIFT { return if *modifiers == keyboard::Modifiers::SHIFT {
self.focus_previous(state) self.focus_previous(state, shell)
} else if modifiers.is_empty() { } else if modifiers.is_empty() {
self.focus_next(state) self.focus_next(state, shell)
} else {
event::Status::Ignored
}; };
} }
@ -1524,24 +1550,23 @@ where
Item::None | Item::Set => (), Item::None | Item::Set => (),
} }
return event::Status::Captured; shell.capture_event();
return;
} }
} }
} }
event::Status::Ignored
} }
fn operate( fn operate(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
_layout: Layout<'_>, layout: Layout<'_>,
_renderer: &Renderer, _renderer: &Renderer,
operation: &mut dyn iced_core::widget::Operation<()>, operation: &mut dyn iced_core::widget::Operation<()>,
) { ) {
let state = tree.state.downcast_mut::<LocalState>(); let state = tree.state.downcast_mut::<LocalState>();
operation.focusable(state, Some(&self.id.0)); operation.focusable(Some(&self.id.0), layout.bounds(), state);
operation.custom(state, Some(&self.id.0)); operation.custom(Some(&self.id.0), layout.bounds(), state);
if let Item::Set = state.focused_item { if let Item::Set = state.focused_item {
if self.prev_tab_sensitive(state) { if self.prev_tab_sensitive(state) {
@ -1616,6 +1641,7 @@ where
bounds, bounds,
border: appearance.border, border: appearance.border,
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
background, background,
); );
@ -1644,6 +1670,7 @@ where
..Default::default() ..Default::default()
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
background_appearance background_appearance
.background .background
@ -1692,6 +1719,7 @@ where
..Default::default() ..Default::default()
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
background_appearance background_appearance
.background .background
@ -1747,6 +1775,7 @@ where
bounds, bounds,
border: Border::default(), border: Border::default(),
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
{ {
let theme = crate::theme::active(); let theme = crate::theme::active();
@ -1842,6 +1871,7 @@ where
..Default::default() ..Default::default()
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
appearance.active.text_color, appearance.active.text_color,
); );
@ -1878,6 +1908,7 @@ where
..Default::default() ..Default::default()
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
divider_background, divider_background,
); );
@ -1910,6 +1941,7 @@ where
button_appearance.border button_appearance.border
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
status_appearance status_appearance
.background .background
@ -2069,8 +2101,9 @@ where
fn overlay<'b>( fn overlay<'b>(
&'b mut self, &'b mut self,
tree: &'b mut Tree, tree: &'b mut Tree,
layout: iced_core::Layout<'_>, layout: iced_core::Layout<'b>,
_renderer: &Renderer, _renderer: &Renderer,
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>> {
let state = tree.state.downcast_mut::<LocalState>(); let state = tree.state.downcast_mut::<LocalState>();
@ -2662,6 +2695,7 @@ fn draw_drop_indicator(
..Default::default() ..Default::default()
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
Background::Color(color), Background::Color(color),
); );

View file

@ -5,10 +5,11 @@ use std::borrow::Cow;
use crate::{ use crate::{
Element, theme, Element, theme,
widget::{FlexRow, Row, column, container, flex_row, horizontal_space, row, text}, widget::{FlexRow, Row, column, container, flex_row, row, text},
}; };
use derive_setters::Setters; use derive_setters::Setters;
use iced_core::{Length, text::Wrapping}; use iced_core::{Length, text::Wrapping};
use iced_widget::space;
use taffy::AlignContent; use taffy::AlignContent;
/// A settings item aligned in a row /// A settings item aligned in a row
@ -25,7 +26,7 @@ pub fn item<'a, Message: 'static>(
) -> Row<'a, Message> { ) -> Row<'a, Message> {
item_row(vec![ item_row(vec![
text(title).wrapping(Wrapping::Word).into(), text(title).wrapping(Wrapping::Word).into(),
horizontal_space().into(), space::horizontal().into(),
widget, widget,
]) ])
} }

View file

@ -313,6 +313,7 @@ fn container_style(theme: &crate::Theme) -> iced_widget::container::Style {
background: None, background: None,
border, border,
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
} }
} }

View file

@ -131,6 +131,7 @@ where
..Default::default() ..Default::default()
}, },
shadow: Default::default(), shadow: Default::default(),
snap: true,
} }
})) }))
.apply(widget::mouse_area) .apply(widget::mouse_area)

View file

@ -192,6 +192,7 @@ where
..Default::default() ..Default::default()
}, },
shadow: Default::default(), shadow: Default::default(),
snap: true,
} }
})) }))
.apply(widget::mouse_area) .apply(widget::mouse_area)

View file

@ -699,7 +699,7 @@ where
} }
fn layout( fn layout(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
renderer: &crate::Renderer, renderer: &crate::Renderer,
limits: &layout::Limits, limits: &layout::Limits,
@ -711,7 +711,7 @@ where
let size = self.size.unwrap_or_else(|| renderer.default_size().0); let size = self.size.unwrap_or_else(|| renderer.default_size().0);
let bounds = limits.resolve(Length::Shrink, Length::Fill, Size::INFINITY); let bounds = limits.resolve(Length::Shrink, Length::Fill, Size::INFINITE);
let value_paragraph = &mut state.value; let value_paragraph = &mut state.value;
let v = self.value.to_string(); let v = self.value.to_string();
value_paragraph.update(Text { value_paragraph.update(Text {
@ -723,8 +723,8 @@ where
font, font,
bounds, bounds,
size: iced::Pixels(size), size: iced::Pixels(size),
horizontal_alignment: alignment::Horizontal::Left, align_x: text::Alignment::Left,
vertical_alignment: alignment::Vertical::Center, align_y: alignment::Vertical::Center,
line_height: text::LineHeight::default(), line_height: text::LineHeight::default(),
shaping: text::Shaping::Advanced, shaping: text::Shaping::Advanced,
wrapping: text::Wrapping::None, wrapping: text::Wrapping::None,
@ -743,8 +743,8 @@ where
self.width, self.width,
self.padding, self.padding,
self.size, self.size,
self.leading_icon.as_ref(), self.leading_icon.as_mut(),
self.trailing_icon.as_ref(), self.trailing_icon.as_mut(),
self.line_height, self.line_height,
self.label.as_deref(), self.label.as_deref(),
self.helper_text.as_deref(), self.helper_text.as_deref(),
@ -780,24 +780,25 @@ where
} }
fn operate( fn operate(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
_layout: Layout<'_>, layout: Layout<'_>,
_renderer: &crate::Renderer, renderer: &crate::Renderer,
operation: &mut dyn Operation<()>, operation: &mut dyn Operation,
) { ) {
operation.container(Some(&self.id), layout.bounds());
let state = tree.state.downcast_mut::<State>(); let state = tree.state.downcast_mut::<State>();
operation.custom(state, Some(&self.id)); operation.focusable(Some(&self.id), layout.bounds(), state);
operation.focusable(state, Some(&self.id)); operation.text_input(Some(&self.id), layout.bounds(), state);
operation.text_input(state, Some(&self.id));
} }
fn overlay<'b>( fn overlay<'b>(
&'b mut self, &'b mut self,
tree: &'b mut Tree, tree: &'b mut Tree,
layout: Layout<'_>, layout: Layout<'b>,
renderer: &crate::Renderer, renderer: &crate::Renderer,
viewport: &Rectangle,
translation: Vector, translation: Vector,
) -> Option<overlay::Element<'b, Message, crate::Theme, crate::Renderer>> { ) -> Option<overlay::Element<'b, Message, crate::Theme, crate::Renderer>> {
let mut layout_ = Vec::with_capacity(2); let mut layout_ = Vec::with_capacity(2);
@ -823,24 +824,24 @@ where
.filter_map(|((child, state), layout)| { .filter_map(|((child, state), layout)| {
child child
.as_widget_mut() .as_widget_mut()
.overlay(state, layout, renderer, translation) .overlay(state, layout, renderer, viewport, translation)
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
(!children.is_empty()).then(|| Group::with_children(children).overlay()) (!children.is_empty()).then(|| Group::with_children(children).overlay())
} }
fn on_event( fn update(
&mut self, &mut self,
tree: &mut Tree, tree: &mut Tree,
event: Event, event: &Event,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: mouse::Cursor, cursor_position: mouse::Cursor,
renderer: &crate::Renderer, renderer: &crate::Renderer,
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
viewport: &Rectangle, viewport: &Rectangle,
) -> event::Status { ) {
let text_layout = self.text_layout(layout); let text_layout = self.text_layout(layout);
let mut trailing_icon_layout = None; let mut trailing_icon_layout = None;
let font = self.font.unwrap_or_else(|| renderer.default_font()); let font = self.font.unwrap_or_else(|| renderer.default_font());
@ -877,9 +878,9 @@ where
// Enable custom buttons defined on the trailing icon position to be handled. // Enable custom buttons defined on the trailing icon position to be handled.
if !self.is_editable_variant { if !self.is_editable_variant {
if let Some(trailing_layout) = trailing_icon_layout { if let Some(trailing_layout) = trailing_icon_layout {
let res = trailing_icon.as_widget_mut().on_event( let res = trailing_icon.as_widget_mut().update(
tree, tree,
event.clone(), event,
trailing_layout, trailing_layout,
cursor_position, cursor_position,
renderer, renderer,
@ -888,8 +889,8 @@ where
viewport, viewport,
); );
if res == event::Status::Captured { if shell.is_event_captured() {
return res; return;
} }
} }
} }
@ -1133,8 +1134,8 @@ pub fn layout<Message>(
width: Length, width: Length,
padding: Padding, padding: Padding,
size: Option<f32>, size: Option<f32>,
leading_icon: Option<&Element<'_, Message, crate::Theme, crate::Renderer>>, leading_icon: Option<&mut Element<'_, Message, crate::Theme, crate::Renderer>>,
trailing_icon: Option<&Element<'_, Message, crate::Theme, crate::Renderer>>, trailing_icon: Option<&mut Element<'_, Message, crate::Theme, crate::Renderer>>,
line_height: text::LineHeight, line_height: text::LineHeight,
label: Option<&str>, label: Option<&str>,
helper_text: Option<&str>, helper_text: Option<&str>,
@ -1148,7 +1149,7 @@ pub fn layout<Message>(
let mut nodes = Vec::with_capacity(3); let mut nodes = Vec::with_capacity(3);
let text_pos = if let Some(label) = label { let text_pos = if let Some(label) = label {
let text_bounds = limits.resolve(width, Length::Shrink, Size::INFINITY); let text_bounds = limits.resolve(width, Length::Shrink, Size::INFINITE);
let state = tree.state.downcast_mut::<State>(); let state = tree.state.downcast_mut::<State>();
let label_paragraph = &mut state.label; let label_paragraph = &mut state.label;
label_paragraph.update(Text { label_paragraph.update(Text {
@ -1156,8 +1157,8 @@ pub fn layout<Message>(
font, font,
bounds: text_bounds, bounds: text_bounds,
size: iced::Pixels(size.unwrap_or_else(|| renderer.default_size().0)), size: iced::Pixels(size.unwrap_or_else(|| renderer.default_size().0)),
horizontal_alignment: alignment::Horizontal::Left, align_x: text::Alignment::Left,
vertical_alignment: alignment::Vertical::Center, align_y: alignment::Vertical::Center,
line_height, line_height,
shaping: text::Shaping::Advanced, shaping: text::Shaping::Advanced,
wrapping: text::Wrapping::None, wrapping: text::Wrapping::None,
@ -1186,7 +1187,7 @@ pub fn layout<Message>(
let (leading_icon_width, mut leading_icon) = let (leading_icon_width, mut leading_icon) =
if let Some((icon, tree)) = leading_icon.zip(children.get_mut(c_i)) { if let Some((icon, tree)) = leading_icon.zip(children.get_mut(c_i)) {
let size = icon.as_widget().size(); let size = icon.as_widget().size();
let icon_node = icon.as_widget().layout( let icon_node = icon.as_widget_mut().layout(
tree, tree,
renderer, renderer,
&Limits::NONE.width(size.width).height(size.height), &Limits::NONE.width(size.width).height(size.height),
@ -1201,7 +1202,7 @@ pub fn layout<Message>(
let (trailing_icon_width, mut trailing_icon) = let (trailing_icon_width, mut trailing_icon) =
if let Some((icon, tree)) = trailing_icon.zip(children.get_mut(c_i)) { if let Some((icon, tree)) = trailing_icon.zip(children.get_mut(c_i)) {
let size = icon.as_widget().size(); let size = icon.as_widget().size();
let icon_node = icon.as_widget().layout( let icon_node = icon.as_widget_mut().layout(
tree, tree,
renderer, renderer,
&Limits::NONE.width(size.width).height(size.height), &Limits::NONE.width(size.width).height(size.height),
@ -1214,7 +1215,7 @@ pub fn layout<Message>(
let text_limits = limits let text_limits = limits
.width(width) .width(width)
.height(line_height.to_absolute(text_size.into())); .height(line_height.to_absolute(text_size.into()));
let text_bounds = text_limits.resolve(Length::Shrink, Length::Shrink, Size::INFINITY); let text_bounds = text_limits.resolve(Length::Shrink, Length::Shrink, Size::INFINITE);
let text_node = layout::Node::new( let text_node = layout::Node::new(
text_bounds - Size::new(leading_icon_width + trailing_icon_width, 0.0), text_bounds - Size::new(leading_icon_width + trailing_icon_width, 0.0),
) )
@ -1266,9 +1267,9 @@ pub fn layout<Message>(
} else { } else {
let limits = limits let limits = limits
.width(width) .width(width)
.height(text_input_height + padding.vertical()) .height(text_input_height + padding.y())
.shrink(padding); .shrink(padding);
let text_bounds = limits.resolve(Length::Shrink, Length::Shrink, Size::INFINITY); let text_bounds = limits.resolve(Length::Shrink, Length::Shrink, Size::INFINITE);
let text = layout::Node::new(text_bounds).move_to(Point::new(padding.left, padding.top)); let text = layout::Node::new(text_bounds).move_to(Point::new(padding.left, padding.top));
@ -1286,7 +1287,7 @@ pub fn layout<Message>(
.width(width) .width(width)
.shrink(padding) .shrink(padding)
.height(helper_text_line_height.to_absolute(helper_text_size.into())); .height(helper_text_line_height.to_absolute(helper_text_size.into()));
let text_bounds = limits.resolve(width, Length::Shrink, Size::INFINITY); let text_bounds = limits.resolve(width, Length::Shrink, Size::INFINITE);
let state = tree.state.downcast_mut::<State>(); let state = tree.state.downcast_mut::<State>();
let helper_text_paragraph = &mut state.helper_text; let helper_text_paragraph = &mut state.helper_text;
helper_text_paragraph.update(Text { helper_text_paragraph.update(Text {
@ -1294,8 +1295,8 @@ pub fn layout<Message>(
font, font,
bounds: text_bounds, bounds: text_bounds,
size: iced::Pixels(helper_text_size), size: iced::Pixels(helper_text_size),
horizontal_alignment: alignment::Horizontal::Left, align_x: text::Alignment::Left,
vertical_alignment: alignment::Vertical::Center, align_y: alignment::Vertical::Center,
line_height: helper_text_line_height, line_height: helper_text_line_height,
shaping: text::Shaping::Advanced, shaping: text::Shaping::Advanced,
wrapping: text::Wrapping::None, wrapping: text::Wrapping::None,
@ -1332,7 +1333,7 @@ pub fn layout<Message>(
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
pub fn update<'a, Message: Clone + 'static>( pub fn update<'a, Message: Clone + 'static>(
id: Option<Id>, id: Option<Id>,
event: Event, event: &Event,
text_layout: Layout<'_>, text_layout: Layout<'_>,
edit_button_layout: Option<Layout<'_>>, edit_button_layout: Option<Layout<'_>>,
cursor: mouse::Cursor, cursor: mouse::Cursor,
@ -1357,7 +1358,7 @@ pub fn update<'a, Message: Clone + 'static>(
layout: Layout<'_>, layout: Layout<'_>,
manage_value: bool, manage_value: bool,
drag_threshold: f32, drag_threshold: f32,
) -> event::Status { ) {
let update_cache = |state, value| { let update_cache = |state, value| {
replace_paragraph( replace_paragraph(
state, state,
@ -1420,7 +1421,8 @@ pub fn update<'a, Message: Clone + 'static>(
}); });
} }
return event::Status::Captured; shell.capture_event();
return;
} }
let target = cursor_position.x - text_layout.bounds().x; let target = cursor_position.x - text_layout.bounds().x;
@ -1461,13 +1463,15 @@ pub fn update<'a, Message: Clone + 'static>(
if cursor.is_over(selection_bounds) && (on_input.is_some() || manage_value) if cursor.is_over(selection_bounds) && (on_input.is_some() || manage_value)
{ {
state.dragging_state = Some(DraggingState::PrepareDnd(cursor_position)); state.dragging_state = Some(DraggingState::PrepareDnd(cursor_position));
return event::Status::Captured; shell.capture_event();
return;
} }
// clear selection and place cursor at click position // clear selection and place cursor at click position
update_cache(state, value); update_cache(state, value);
state.setting_selection(value, text_layout.bounds(), target); state.setting_selection(value, text_layout.bounds(), target);
state.dragging_state = None; state.dragging_state = None;
return event::Status::Captured; shell.capture_event();
return;
} }
(None, click::Kind::Single, _) => { (None, click::Kind::Single, _) => {
state.setting_selection(value, text_layout.bounds(), target); state.setting_selection(value, text_layout.bounds(), target);
@ -1528,7 +1532,8 @@ pub fn update<'a, Message: Clone + 'static>(
state.last_click = Some(click); state.last_click = Some(click);
return event::Status::Captured; shell.capture_event();
return;
} else { } else {
state.unfocus(); state.unfocus();
@ -1551,12 +1556,10 @@ pub fn update<'a, Message: Clone + 'static>(
} }
} }
state.dragging_state = None; state.dragging_state = None;
if cursor.is_over(layout.bounds()) {
return if cursor.is_over(layout.bounds()) { shell.capture_event();
event::Status::Captured }
} else { return;
event::Status::Ignored
};
} }
Event::Mouse(mouse::Event::CursorMoved { position }) Event::Mouse(mouse::Event::CursorMoved { position })
| Event::Touch(touch::Event::FingerMoved { position, .. }) => { | Event::Touch(touch::Event::FingerMoved { position, .. }) => {
@ -1573,7 +1576,8 @@ pub fn update<'a, Message: Clone + 'static>(
.cursor .cursor
.select_range(state.cursor.start(value), position); .select_range(state.cursor.start(value), position);
return event::Status::Captured; shell.capture_event();
return;
} }
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
if let Some(DraggingState::PrepareDnd(start_position)) = state.dragging_state { if let Some(DraggingState::PrepareDnd(start_position)) = state.dragging_state {
@ -1583,7 +1587,7 @@ pub fn update<'a, Message: Clone + 'static>(
if distance >= drag_threshold { if distance >= drag_threshold {
if is_secure { if is_secure {
return event::Status::Ignored; return;
} }
let input_text = state.selected_text(&value.to_string()).unwrap_or_default(); let input_text = state.selected_text(&value.to_string()).unwrap_or_default();
@ -1625,7 +1629,8 @@ pub fn update<'a, Message: Clone + 'static>(
state.dragging_state = Some(DraggingState::PrepareDnd(start_position)); state.dragging_state = Some(DraggingState::PrepareDnd(start_position));
} }
return event::Status::Captured; shell.capture_event();
return;
} }
} }
Event::Keyboard(keyboard::Event::KeyPressed { Event::Keyboard(keyboard::Event::KeyPressed {
@ -1636,11 +1641,11 @@ pub fn update<'a, Message: Clone + 'static>(
.. ..
}) => { }) => {
let state = state(); let state = state();
state.keyboard_modifiers = modifiers; state.keyboard_modifiers = *modifiers;
if let Some(focus) = state.is_focused.as_mut().filter(|f| f.focused) { if let Some(focus) = state.is_focused.as_mut().filter(|f| f.focused) {
if state.is_read_only || (!manage_value && on_input.is_none()) { if state.is_read_only || (!manage_value && on_input.is_none()) {
return event::Status::Ignored; return;
}; };
let modifiers = state.keyboard_modifiers; let modifiers = state.keyboard_modifiers;
focus.updated_at = Instant::now(); focus.updated_at = Instant::now();
@ -1724,12 +1729,14 @@ pub fn update<'a, Message: Clone + 'static>(
}; };
update_cache(state, &value); update_cache(state, &value);
return event::Status::Captured; shell.capture_event();
return;
} }
keyboard::Key::Character("a") | keyboard::Key::Character("A") => { keyboard::Key::Character("a") | keyboard::Key::Character("A") => {
state.cursor.select_all(value); state.cursor.select_all(value);
return event::Status::Captured; shell.capture_event();
return;
} }
_ => {} _ => {}
@ -1737,9 +1744,12 @@ pub fn update<'a, Message: Clone + 'static>(
} }
// Capture keyboard inputs that should be submitted. // Capture keyboard inputs that should be submitted.
if let Some(c) = text.and_then(|t| t.chars().next().filter(|c| !c.is_control())) { if let Some(c) = text
.as_ref()
.and_then(|t| t.chars().next().filter(|c| !c.is_control()))
{
if state.is_read_only || (!manage_value && on_input.is_none()) { if state.is_read_only || (!manage_value && on_input.is_none()) {
return event::Status::Ignored; return;
}; };
state.is_pasting = None; state.is_pasting = None;
@ -1769,7 +1779,8 @@ pub fn update<'a, Message: Clone + 'static>(
update_cache(state, &value); update_cache(state, &value);
return event::Status::Captured; shell.capture_event();
return;
} }
} }
@ -1902,19 +1913,20 @@ pub fn update<'a, Message: Clone + 'static>(
shell.publish(on_unfocus.clone()); shell.publish(on_unfocus.clone());
} }
return event::Status::Ignored; return;
}; };
} }
keyboard::Key::Named( keyboard::Key::Named(
keyboard::key::Named::ArrowUp | keyboard::key::Named::ArrowDown, keyboard::key::Named::ArrowUp | keyboard::key::Named::ArrowDown,
) => { ) => {
return event::Status::Ignored; return;
} }
_ => {} _ => {}
} }
return event::Status::Captured; shell.capture_event();
return;
} }
} }
Event::Keyboard(keyboard::Event::KeyReleased { key, .. }) => { Event::Keyboard(keyboard::Event::KeyReleased { key, .. }) => {
@ -1928,31 +1940,30 @@ pub fn update<'a, Message: Clone + 'static>(
keyboard::Key::Named(keyboard::key::Named::Tab) keyboard::Key::Named(keyboard::key::Named::Tab)
| keyboard::Key::Named(keyboard::key::Named::ArrowUp) | keyboard::Key::Named(keyboard::key::Named::ArrowUp)
| keyboard::Key::Named(keyboard::key::Named::ArrowDown) => { | keyboard::Key::Named(keyboard::key::Named::ArrowDown) => {
return event::Status::Ignored; return;
} }
_ => {} _ => {}
} }
return event::Status::Captured; shell.capture_event();
return;
} }
} }
Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) => { Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) => {
let state = state(); let state = state();
state.keyboard_modifiers = modifiers; state.keyboard_modifiers = *modifiers;
} }
Event::Window(window::Event::RedrawRequested(now)) => { Event::Window(window::Event::RedrawRequested(now)) => {
let state = state(); let state = state();
if let Some(focus) = state.is_focused.as_mut().filter(|f| f.focused) { if let Some(focus) = state.is_focused.as_mut().filter(|f| f.focused) {
focus.now = now; focus.now = *now;
let millis_until_redraw = CURSOR_BLINK_INTERVAL_MILLIS let millis_until_redraw = CURSOR_BLINK_INTERVAL_MILLIS
- (now - focus.updated_at).as_millis() % CURSOR_BLINK_INTERVAL_MILLIS; - (*now - focus.updated_at).as_millis() % CURSOR_BLINK_INTERVAL_MILLIS;
shell.request_redraw(window::RedrawRequest::At( shell.request_redraw();
now + Duration::from_millis(u64::try_from(millis_until_redraw).unwrap()),
));
} }
} }
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
@ -1962,7 +1973,8 @@ pub fn update<'a, Message: Clone + 'static>(
if matches!(state.dragging_state, Some(DraggingState::Dnd(..))) { if matches!(state.dragging_state, Some(DraggingState::Dnd(..))) {
// TODO: restore value in text input // TODO: restore value in text input
state.dragging_state = None; state.dragging_state = None;
return event::Status::Captured; shell.capture_event();
return;
} }
} }
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
@ -1974,23 +1986,23 @@ pub fn update<'a, Message: Clone + 'static>(
mime_types, mime_types,
surface, surface,
}, },
)) if rectangle == Some(dnd_id) => { )) if *rectangle == Some(dnd_id) => {
cold(); cold();
let state = state(); let state = state();
let is_clicked = text_layout.bounds().contains(Point { let is_clicked = text_layout.bounds().contains(Point {
x: x as f32, x: *x as f32,
y: y as f32, y: *y as f32,
}); });
let mut accepted = false; let mut accepted = false;
for m in &mime_types { for m in mime_types {
if SUPPORTED_TEXT_MIME_TYPES.contains(&m.as_str()) { if SUPPORTED_TEXT_MIME_TYPES.contains(&m.as_str()) {
let clone = m.clone(); let clone = m.clone();
accepted = true; accepted = true;
} }
} }
if accepted { if accepted {
let target = x as f32 - text_layout.bounds().x; let target = *x as f32 - text_layout.bounds().x;
state.dnd_offer = state.dnd_offer =
DndOfferState::HandlingOffer(mime_types.clone(), DndAction::empty()); DndOfferState::HandlingOffer(mime_types.clone(), DndAction::empty());
// existing logic for setting the selection // existing logic for setting the selection
@ -2002,16 +2014,17 @@ pub fn update<'a, Message: Clone + 'static>(
}; };
state.cursor.move_to(position.unwrap_or(0)); state.cursor.move_to(position.unwrap_or(0));
return event::Status::Captured; shell.capture_event();
return;
} }
} }
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
Event::Dnd(DndEvent::Offer(rectangle, OfferEvent::Motion { x, y })) Event::Dnd(DndEvent::Offer(rectangle, OfferEvent::Motion { x, y }))
if rectangle == Some(dnd_id) => if *rectangle == Some(dnd_id) =>
{ {
let state = state(); let state = state();
let target = x as f32 - text_layout.bounds().x; let target = *x as f32 - text_layout.bounds().x;
// existing logic for setting the selection // existing logic for setting the selection
let position = if target > 0.0 { let position = if target > 0.0 {
update_cache(state, value); update_cache(state, value);
@ -2021,10 +2034,11 @@ pub fn update<'a, Message: Clone + 'static>(
}; };
state.cursor.move_to(position.unwrap_or(0)); state.cursor.move_to(position.unwrap_or(0));
return event::Status::Captured; shell.capture_event();
return;
} }
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
Event::Dnd(DndEvent::Offer(rectangle, OfferEvent::Drop)) if rectangle == Some(dnd_id) => { Event::Dnd(DndEvent::Offer(rectangle, OfferEvent::Drop)) if *rectangle == Some(dnd_id) => {
cold(); cold();
let state = state(); let state = state();
if let DndOfferState::HandlingOffer(mime_types, _action) = state.dnd_offer.clone() { if let DndOfferState::HandlingOffer(mime_types, _action) = state.dnd_offer.clone() {
@ -2033,15 +2047,16 @@ pub fn update<'a, Message: Clone + 'static>(
.find(|&&m| mime_types.iter().any(|t| t == m)) .find(|&&m| mime_types.iter().any(|t| t == m))
else { else {
state.dnd_offer = DndOfferState::None; state.dnd_offer = DndOfferState::None;
return event::Status::Captured; shell.capture_event();
return;
}; };
state.dnd_offer = DndOfferState::Dropped; state.dnd_offer = DndOfferState::Dropped;
} }
return event::Status::Ignored; return;
} }
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
Event::Dnd(DndEvent::Offer(id, OfferEvent::LeaveDestination)) if Some(dnd_id) != id => {} Event::Dnd(DndEvent::Offer(id, OfferEvent::LeaveDestination)) if Some(dnd_id) != *id => {}
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
Event::Dnd(DndEvent::Offer( Event::Dnd(DndEvent::Offer(
rectangle, rectangle,
@ -2057,21 +2072,24 @@ pub fn update<'a, Message: Clone + 'static>(
state.dnd_offer = DndOfferState::None; state.dnd_offer = DndOfferState::None;
} }
}; };
return event::Status::Captured; shell.capture_event();
return;
} }
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
Event::Dnd(DndEvent::Offer(rectangle, OfferEvent::Data { data, mime_type })) Event::Dnd(DndEvent::Offer(rectangle, OfferEvent::Data { data, mime_type }))
if rectangle == Some(dnd_id) => if *rectangle == Some(dnd_id) =>
{ {
cold(); cold();
let state = state(); let state = state();
if matches!(&state.dnd_offer, DndOfferState::Dropped) { if matches!(&state.dnd_offer, DndOfferState::Dropped) {
state.dnd_offer = DndOfferState::None; state.dnd_offer = DndOfferState::None;
if !SUPPORTED_TEXT_MIME_TYPES.contains(&mime_type.as_str()) || data.is_empty() { if !SUPPORTED_TEXT_MIME_TYPES.contains(&mime_type.as_str()) || data.is_empty() {
return event::Status::Captured; shell.capture_event();
return;
} }
let Ok(content) = String::from_utf8(data) else { let Ok(content) = String::from_utf8(data.clone()) else {
return event::Status::Captured; shell.capture_event();
return;
}; };
let mut editor = Editor::new(unsecured_value, &mut state.cursor); let mut editor = Editor::new(unsecured_value, &mut state.cursor);
@ -2091,14 +2109,13 @@ pub fn update<'a, Message: Clone + 'static>(
unsecured_value unsecured_value
}; };
update_cache(state, &value); update_cache(state, &value);
return event::Status::Captured; shell.capture_event();
return;
} }
return event::Status::Ignored; return;
} }
_ => {} _ => {}
} }
event::Status::Ignored
} }
/// Draws the [`TextInput`] with the given [`Renderer`], overriding its /// Draws the [`TextInput`] with the given [`Renderer`], overriding its
@ -2212,6 +2229,7 @@ pub fn draw<'a, Message>(
color: Color::TRANSPARENT, color: Color::TRANSPARENT,
blur_radius: 0.0, blur_radius: 0.0,
}, },
snap: true,
}, },
appearance.background, appearance.background,
); );
@ -2228,6 +2246,7 @@ pub fn draw<'a, Message>(
color: Color::TRANSPARENT, color: Color::TRANSPARENT,
blur_radius: 0.0, blur_radius: 0.0,
}, },
snap: true,
}, },
Background::Color(Color::TRANSPARENT), Background::Color(Color::TRANSPARENT),
); );
@ -2245,6 +2264,7 @@ pub fn draw<'a, Message>(
color: Color::TRANSPARENT, color: Color::TRANSPARENT,
blur_radius: 0.0, blur_radius: 0.0,
}, },
snap: true,
}, },
appearance.background, appearance.background,
); );
@ -2258,8 +2278,8 @@ pub fn draw<'a, Message>(
size: iced::Pixels(size.unwrap_or_else(|| renderer.default_size().0)), size: iced::Pixels(size.unwrap_or_else(|| renderer.default_size().0)),
font: font.unwrap_or_else(|| renderer.default_font()), font: font.unwrap_or_else(|| renderer.default_font()),
bounds: label_layout.bounds().size(), bounds: label_layout.bounds().size(),
horizontal_alignment: alignment::Horizontal::Left, align_x: text::Alignment::Left,
vertical_alignment: alignment::Vertical::Top, align_y: alignment::Vertical::Top,
line_height, line_height,
shaping: text::Shaping::Advanced, shaping: text::Shaping::Advanced,
wrapping: text::Wrapping::None, wrapping: text::Wrapping::None,
@ -2353,6 +2373,7 @@ pub fn draw<'a, Message>(
color: Color::TRANSPARENT, color: Color::TRANSPARENT,
blur_radius: 0.0, blur_radius: 0.0,
}, },
snap: true,
}, },
text_color, text_color,
)), )),
@ -2403,6 +2424,7 @@ pub fn draw<'a, Message>(
color: Color::TRANSPARENT, color: Color::TRANSPARENT,
blur_radius: 0.0, blur_radius: 0.0,
}, },
snap: true,
}, },
appearance.selected_fill, appearance.selected_fill,
)), )),
@ -2448,8 +2470,8 @@ pub fn draw<'a, Message>(
font, font,
bounds: bounds.size(), bounds: bounds.size(),
size: iced::Pixels(size), size: iced::Pixels(size),
horizontal_alignment: alignment::Horizontal::Left, align_x: text::Alignment::Left,
vertical_alignment: alignment::Vertical::Center, align_y: alignment::Vertical::Center,
line_height: text::LineHeight::default(), line_height: text::LineHeight::default(),
shaping: text::Shaping::Advanced, shaping: text::Shaping::Advanced,
wrapping: text::Wrapping::None, wrapping: text::Wrapping::None,
@ -2497,8 +2519,8 @@ pub fn draw<'a, Message>(
size: iced::Pixels(helper_text_size), size: iced::Pixels(helper_text_size),
font, font,
bounds: helper_text_layout.bounds().size(), bounds: helper_text_layout.bounds().size(),
horizontal_alignment: alignment::Horizontal::Left, align_x: text::Alignment::Left,
vertical_alignment: alignment::Vertical::Top, align_y: alignment::Vertical::Top,
line_height: helper_line_height, line_height: helper_line_height,
shaping: text::Shaping::Advanced, shaping: text::Shaping::Advanced,
wrapping: text::Wrapping::None, wrapping: text::Wrapping::None,
@ -2811,6 +2833,14 @@ impl operation::TextInput for State {
fn select_all(&mut self) { fn select_all(&mut self) {
Self::select_all(self); Self::select_all(self);
} }
fn text(&self) -> &str {
todo!()
}
fn select_range(&mut self, start: usize, end: usize) {
todo!()
}
} }
#[inline(never)] #[inline(never)]
@ -2876,11 +2906,11 @@ fn replace_paragraph(
state.value = crate::Plain::new(Text { state.value = crate::Plain::new(Text {
font, font,
line_height, line_height,
content: &value.to_string(), content: value.to_string(),
bounds, bounds,
size: text_size, size: text_size,
horizontal_alignment: alignment::Horizontal::Left, align_x: text::Alignment::Left,
vertical_alignment: alignment::Vertical::Top, align_y: alignment::Vertical::Top,
shaping: text::Shaping::Advanced, shaping: text::Shaping::Advanced,
wrapping: text::Wrapping::None, wrapping: text::Wrapping::None,
ellipsize: text::Ellipsize::None, ellipsize: text::Ellipsize::None,

View file

@ -45,13 +45,13 @@ where
} }
fn layout( fn layout(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
renderer: &Renderer, renderer: &Renderer,
limits: &layout::Limits, limits: &layout::Limits,
) -> layout::Node { ) -> layout::Node {
self.content self.content
.as_widget() .as_widget_mut()
.layout(&mut tree.children[0], renderer, limits) .layout(&mut tree.children[0], renderer, limits)
} }
@ -85,29 +85,29 @@ where
} }
fn operate<'b>( fn operate<'b>(
&'b self, &'b mut self,
state: &'b mut Tree, state: &'b mut Tree,
layout: Layout<'_>, layout: Layout<'_>,
renderer: &Renderer, renderer: &Renderer,
operation: &mut dyn Operation<()>, operation: &mut dyn Operation<()>,
) { ) {
self.content self.content
.as_widget() .as_widget_mut()
.operate(&mut state.children[0], layout, renderer, operation); .operate(&mut state.children[0], layout, renderer, operation);
} }
fn on_event( fn update(
&mut self, &mut self,
state: &mut Tree, state: &mut Tree,
event: Event, event: &Event,
layout: Layout<'_>, layout: Layout<'_>,
cursor: mouse::Cursor, cursor: mouse::Cursor,
renderer: &Renderer, renderer: &Renderer,
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
viewport: &Rectangle, viewport: &Rectangle,
) -> event::Status { ) {
self.content.as_widget_mut().on_event( self.content.as_widget_mut().update(
&mut state.children[0], &mut state.children[0],
event, event,
layout, layout,
@ -139,8 +139,9 @@ where
fn overlay<'b>( fn overlay<'b>(
&'b mut self, &'b mut self,
state: &'b mut Tree, state: &'b mut Tree,
layout: Layout<'_>, layout: Layout<'b>,
renderer: &Renderer, renderer: &Renderer,
viewport: &Rectangle,
translation: Vector, translation: Vector,
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> { ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
//TODO: this hides the overlay of the content during the toast //TODO: this hides the overlay of the content during the toast
@ -149,6 +150,7 @@ where
&mut state.children[0], &mut state.children[0],
layout, layout,
renderer, renderer,
viewport,
translation, translation,
) )
} else { } else {
@ -201,7 +203,7 @@ where
let node = self let node = self
.element .element
.as_widget() .as_widget_mut()
.layout(self.state, renderer, &limits); .layout(self.state, renderer, &limits);
let offset = 15.; let offset = 15.;
@ -228,16 +230,16 @@ where
.draw(self.state, renderer, theme, style, layout, cursor, &bounds); .draw(self.state, renderer, theme, style, layout, cursor, &bounds);
} }
fn on_event( fn update(
&mut self, &mut self,
event: Event, event: &Event,
layout: Layout<'_>, layout: Layout<'_>,
cursor: mouse::Cursor, cursor: mouse::Cursor,
renderer: &Renderer, renderer: &Renderer,
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
shell: &mut Shell<Message>, shell: &mut Shell<Message>,
) -> event::Status { ) {
self.element.as_widget_mut().on_event( self.element.as_widget_mut().update(
self.state, self.state,
event, event,
layout, layout,
@ -253,22 +255,29 @@ where
&self, &self,
layout: Layout<'_>, layout: Layout<'_>,
cursor: mouse::Cursor, cursor: mouse::Cursor,
viewport: &Rectangle,
renderer: &Renderer, renderer: &Renderer,
) -> mouse::Interaction { ) -> mouse::Interaction {
self.element self.element.as_widget().mouse_interaction(
.as_widget() self.state,
.mouse_interaction(self.state, layout, cursor, viewport, renderer) layout,
cursor,
&layout.bounds(),
renderer,
)
} }
fn overlay<'c>( fn overlay<'c>(
&'c mut self, &'c mut self,
layout: Layout<'_>, layout: Layout<'c>,
renderer: &Renderer, renderer: &Renderer,
) -> Option<overlay::Element<'c, Message, Theme, Renderer>> { ) -> Option<overlay::Element<'c, Message, Theme, Renderer>> {
self.element self.element.as_widget_mut().overlay(
.as_widget_mut() self.state,
.overlay(self.state, layout, renderer, Default::default()) layout,
renderer,
&layout.bounds(),
Default::default(),
)
} }
} }

View file

@ -73,5 +73,6 @@ pub fn warning_container(theme: &Theme) -> widget::container::Style {
offset: iced::Vector::new(0.0, 0.0), offset: iced::Vector::new(0.0, 0.0),
blur_radius: 0.0, blur_radius: 0.0,
}, },
snap: true,
} }
} }

View file

@ -211,7 +211,7 @@ impl<'a, Message: 'static + Clone, TopLevelMessage: 'static + Clone>
} }
fn layout( fn layout(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
renderer: &crate::Renderer, renderer: &crate::Renderer,
limits: &layout::Limits, limits: &layout::Limits,
@ -224,22 +224,23 @@ impl<'a, Message: 'static + Clone, TopLevelMessage: 'static + Clone>
self.padding, self.padding,
|renderer, limits| { |renderer, limits| {
self.content self.content
.as_widget() .as_widget_mut()
.layout(&mut tree.children[0], renderer, limits) .layout(&mut tree.children[0], renderer, limits)
}, },
) )
} }
fn operate( fn operate(
&self, &mut self,
tree: &mut Tree, tree: &mut Tree,
layout: Layout<'_>, layout: Layout<'_>,
renderer: &crate::Renderer, renderer: &crate::Renderer,
operation: &mut dyn Operation<()>, operation: &mut dyn Operation<()>,
) { ) {
operation.container(None, layout.bounds(), &mut |operation| { operation.container(Some(&self.id), layout.bounds());
self.content.as_widget().operate( operation.traverse(&mut |operation| {
&mut tree.children[0], self.content.as_widget_mut().operate(
tree,
layout layout
.children() .children()
.next() .next()
@ -251,17 +252,17 @@ impl<'a, Message: 'static + Clone, TopLevelMessage: 'static + Clone>
}); });
} }
fn on_event( fn update(
&mut self, &mut self,
tree: &mut Tree, tree: &mut Tree,
event: Event, event: &Event,
layout: Layout<'_>, layout: Layout<'_>,
cursor: mouse::Cursor, cursor: mouse::Cursor,
renderer: &crate::Renderer, renderer: &crate::Renderer,
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
viewport: &Rectangle, viewport: &Rectangle,
) -> event::Status { ) {
let status = update( let status = update(
self.id.clone(), self.id.clone(),
event.clone(), event.clone(),
@ -275,22 +276,21 @@ impl<'a, Message: 'static + Clone, TopLevelMessage: 'static + Clone>
&self.on_surface_action, &self.on_surface_action,
|| tree.state.downcast_mut::<State>(), || tree.state.downcast_mut::<State>(),
); );
status.merge(
self.content.as_widget_mut().on_event( self.content.as_widget_mut().update(
&mut tree.children[0], &mut tree.children[0],
event, event,
layout layout
.children() .children()
.next() .next()
.unwrap() .unwrap()
.with_virtual_offset(layout.virtual_offset()), .with_virtual_offset(layout.virtual_offset()),
cursor, cursor,
renderer, renderer,
clipboard, clipboard,
shell, shell,
viewport, viewport,
), );
)
} }
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
@ -359,8 +359,9 @@ impl<'a, Message: 'static + Clone, TopLevelMessage: 'static + Clone>
fn overlay<'b>( fn overlay<'b>(
&'b mut self, &'b mut self,
tree: &'b mut Tree, tree: &'b mut Tree,
layout: Layout<'_>, layout: Layout<'b>,
renderer: &crate::Renderer, renderer: &crate::Renderer,
viewport: &Rectangle,
mut translation: Vector, mut translation: Vector,
) -> Option<overlay::Element<'b, Message, crate::Theme, crate::Renderer>> { ) -> Option<overlay::Element<'b, Message, crate::Theme, crate::Renderer>> {
let position = layout.bounds().position(); let position = layout.bounds().position();
@ -374,6 +375,7 @@ impl<'a, Message: 'static + Clone, TopLevelMessage: 'static + Clone>
.unwrap() .unwrap()
.with_virtual_offset(layout.virtual_offset()), .with_virtual_offset(layout.virtual_offset()),
renderer, renderer,
viewport,
translation, translation,
) )
} }
@ -451,7 +453,7 @@ pub fn update<'a, Message: Clone + 'static, TopLevelMessage: Clone + 'static>(
on_leave: &Message, on_leave: &Message,
on_surface_action: &dyn Fn(crate::surface::Action) -> Message, on_surface_action: &dyn Fn(crate::surface::Action) -> Message,
state: impl FnOnce() -> &'a mut State, state: impl FnOnce() -> &'a mut State,
) -> event::Status { ) {
match event { match event {
Event::Touch(touch::Event::FingerLifted { .. }) => { Event::Touch(touch::Event::FingerLifted { .. }) => {
let state = state(); let state = state();
@ -461,7 +463,8 @@ pub fn update<'a, Message: Clone + 'static, TopLevelMessage: Clone + 'static>(
shell.publish(on_leave.clone()); shell.publish(on_leave.clone());
return event::Status::Captured; shell.capture_event();
return;
} }
} }
@ -579,8 +582,6 @@ pub fn update<'a, Message: Clone + 'static, TopLevelMessage: Clone + 'static>(
} }
_ => {} _ => {}
} }
event::Status::Ignored
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
@ -611,6 +612,7 @@ pub fn draw<Renderer: iced_core::Renderer, Theme>(
radius: styling.border_radius, radius: styling.border_radius,
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
Color::TRANSPARENT, Color::TRANSPARENT,
); );
@ -632,6 +634,7 @@ pub fn draw<Renderer: iced_core::Renderer, Theme>(
..Default::default() ..Default::default()
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
Background::Color([0.0, 0.0, 0.0, 0.5].into()), Background::Color([0.0, 0.0, 0.0, 0.5].into()),
); );
@ -647,6 +650,7 @@ pub fn draw<Renderer: iced_core::Renderer, Theme>(
..Default::default() ..Default::default()
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
background, background,
); );
@ -669,6 +673,7 @@ pub fn draw<Renderer: iced_core::Renderer, Theme>(
radius: styling.border_radius, radius: styling.border_radius,
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
snap: true,
}, },
Color::TRANSPARENT, Color::TRANSPARENT,
); );

View file

@ -90,7 +90,7 @@ impl<M> Widget<M, crate::Theme, crate::Renderer> for RcElementWrapper<M> {
} }
fn layout( fn layout(
&self, &mut self,
tree: &mut tree::Tree, tree: &mut tree::Tree,
renderer: &crate::Renderer, renderer: &crate::Renderer,
limits: &crate::iced_core::layout::Limits, limits: &crate::iced_core::layout::Limits,
@ -132,30 +132,31 @@ impl<M> Widget<M, crate::Theme, crate::Renderer> for RcElementWrapper<M> {
} }
fn operate( fn operate(
&self, &mut self,
state: &mut tree::Tree, state: &mut tree::Tree,
layout: crate::iced_core::Layout<'_>, layout: crate::iced_core::Layout<'_>,
renderer: &crate::Renderer, renderer: &crate::Renderer,
operation: &mut dyn widget::Operation, operation: &mut dyn widget::Operation,
) { ) {
self.element.with_data(|e| { self.element.with_data_mut(|e| {
e.as_widget().operate(state, layout, renderer, operation); e.as_widget_mut()
.operate(state, layout, renderer, operation);
}); });
} }
fn on_event( fn update(
&mut self, &mut self,
state: &mut tree::Tree, state: &mut tree::Tree,
event: crate::iced::Event, event: &crate::iced::Event,
layout: crate::iced_core::Layout<'_>, layout: crate::iced_core::Layout<'_>,
cursor: crate::iced_core::mouse::Cursor, cursor: crate::iced_core::mouse::Cursor,
renderer: &crate::Renderer, renderer: &crate::Renderer,
clipboard: &mut dyn crate::iced_core::Clipboard, clipboard: &mut dyn crate::iced_core::Clipboard,
shell: &mut crate::iced_core::Shell<'_, M>, shell: &mut crate::iced_core::Shell<'_, M>,
viewport: &Rectangle, viewport: &Rectangle,
) -> event::Status { ) {
self.element.with_data_mut(|e| { self.element.with_data_mut(|e| {
e.as_widget_mut().on_event( e.as_widget_mut().update(
state, event, layout, cursor, renderer, clipboard, shell, viewport, state, event, layout, cursor, renderer, clipboard, shell, viewport,
) )
}) })
@ -178,15 +179,16 @@ impl<M> Widget<M, crate::Theme, crate::Renderer> for RcElementWrapper<M> {
fn overlay<'a>( fn overlay<'a>(
&'a mut self, &'a mut self,
state: &'a mut tree::Tree, state: &'a mut tree::Tree,
layout: crate::iced_core::Layout<'_>, layout: crate::iced_core::Layout<'a>,
renderer: &crate::Renderer, renderer: &crate::Renderer,
viewport: &Rectangle,
translation: crate::iced_core::Vector, translation: crate::iced_core::Vector,
) -> Option<crate::iced_core::overlay::Element<'a, M, crate::Theme, crate::Renderer>> { ) -> Option<crate::iced_core::overlay::Element<'a, M, crate::Theme, crate::Renderer>> {
assert_eq!(self.element.thread_id, thread::current().id()); assert_eq!(self.element.thread_id, thread::current().id());
Rc::get_mut(&mut self.element.data).and_then(|e| { Rc::get_mut(&mut self.element.data).and_then(|e| {
e.get_mut() e.get_mut()
.as_widget_mut() .as_widget_mut()
.overlay(state, layout, renderer, translation) .overlay(state, layout, renderer, viewport, translation)
}) })
} }