feat: Tooltips and Better Surface Management

This commit is contained in:
Ashley Wulber 2025-03-14 11:56:21 -04:00 committed by GitHub
parent c7edd37b03
commit 337b80d4ca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
90 changed files with 3651 additions and 977 deletions

152
src/surface/action.rs Normal file
View file

@ -0,0 +1,152 @@
// Copyright 2025 System76 <info@system76.com>
// SPDX-License-Identifier: MPL-2.0
use super::Action;
#[cfg(feature = "winit")]
use crate::Application;
use std::{any::Any, sync::Arc};
/// Used to produce a destroy popup message from within a widget.
#[cfg(feature = "wayland")]
#[must_use]
pub fn destroy_popup(id: iced_core::window::Id) -> Action {
Action::DestroyPopup(id)
}
#[cfg(feature = "wayland")]
#[must_use]
pub fn destroy_subsurface(id: iced_core::window::Id) -> Action {
Action::DestroySubsurface(id)
}
#[cfg(all(feature = "wayland", feature = "winit"))]
#[must_use]
pub fn app_popup<App: Application>(
settings: impl Fn(&mut App) -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings
+ Send
+ Sync
+ 'static,
view: Option<
Box<
dyn for<'a> Fn(&'a App) -> crate::Element<'a, crate::Action<App::Message>>
+ Send
+ Sync
+ 'static,
>,
>,
) -> Action {
let boxed: Box<
dyn Fn(&mut App) -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings
+ Send
+ Sync
+ 'static,
> = Box::new(settings);
let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed);
Action::AppPopup(
Arc::new(boxed),
view.map(|view| {
let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(view);
Arc::new(boxed)
}),
)
}
/// Used to create a subsurface message from within a widget.
#[cfg(all(feature = "wayland", feature = "winit"))]
#[must_use]
pub fn simple_subsurface<Message: 'static, V>(
settings: impl Fn() -> iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings
+ Send
+ Sync
+ 'static,
view: Option<
Box<dyn Fn() -> crate::Element<'static, crate::Action<Message>> + Send + Sync + 'static>,
>,
) -> Action {
let boxed: Box<
dyn Fn() -> iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings
+ Send
+ Sync
+ 'static,
> = Box::new(settings);
let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed);
Action::Subsurface(
Arc::new(boxed),
view.map(|view| {
let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(view);
Arc::new(boxed)
}),
)
}
/// Used to create a popup message from within a widget.
#[cfg(all(feature = "wayland", feature = "winit"))]
#[must_use]
pub fn simple_popup<Message: 'static, V>(
settings: impl Fn() -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings
+ Send
+ Sync
+ 'static,
view: Option<
impl Fn() -> crate::Element<'static, crate::Action<Message>> + Send + Sync + 'static,
>,
) -> Action {
let boxed: Box<
dyn Fn() -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings
+ Send
+ Sync
+ 'static,
> = Box::new(settings);
let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed);
Action::Popup(
Arc::new(boxed),
view.map(|view| {
let boxed: Box<
dyn Fn() -> crate::Element<'static, crate::Action<Message>> + Send + Sync + 'static,
> = Box::new(view);
let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed);
Arc::new(boxed)
}),
)
}
#[cfg(all(feature = "wayland", feature = "winit"))]
#[must_use]
pub fn subsurface<App: Application>(
settings: impl Fn(&mut App) -> iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings
+ Send
+ Sync
+ 'static,
// XXX Boxed trait object is required for less cumbersome type inference, but we box it anyways.
view: Option<
Box<
dyn for<'a> Fn(&'a App) -> crate::Element<'a, crate::Action<App::Message>>
+ Send
+ Sync
+ 'static,
>,
>,
) -> Action {
let boxed: Box<
dyn Fn(
&mut App,
)
-> iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings
+ Send
+ Sync
+ 'static,
> = Box::new(settings);
let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed);
Action::AppSubsurface(
Arc::new(boxed),
view.map(|view| {
let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(view);
Arc::new(boxed)
}),
)
}

85
src/surface/mod.rs Normal file
View file

@ -0,0 +1,85 @@
// Copyright 2025 System76 <info@system76.com>
// SPDX-License-Identifier: MPL-2.0
pub mod action;
use iced::Limits;
use iced::Size;
use iced::Task;
use std::future::Future;
use std::sync::Arc;
/// Ignore this message in your application. It will be intercepted.
#[derive(Clone)]
pub enum Action {
/// Create a subsurface with a view function accepting the App as a parameter
AppSubsurface(
std::sync::Arc<Box<dyn std::any::Any + Send + Sync>>,
Option<std::sync::Arc<Box<dyn std::any::Any + Send + Sync>>>,
),
/// Create a subsurface with a view function
Subsurface(
std::sync::Arc<Box<dyn std::any::Any + Send + Sync>>,
Option<std::sync::Arc<Box<dyn std::any::Any + Send + Sync>>>,
),
/// Destroy a subsurface with a view function
DestroySubsurface(iced::window::Id),
/// Create a popup with a view function accepting the App as a parameter
AppPopup(
std::sync::Arc<Box<dyn std::any::Any + Send + Sync>>,
Option<std::sync::Arc<Box<dyn std::any::Any + Send + Sync>>>,
),
/// Create a popup
Popup(
std::sync::Arc<Box<dyn std::any::Any + Send + Sync>>,
Option<std::sync::Arc<Box<dyn std::any::Any + Send + Sync>>>,
),
/// Destroy a subsurface with a view function
DestroyPopup(iced::window::Id),
/// Responsive menu bar update
ResponsiveMenuBar {
/// Id of the menu bar
menu_bar: crate::widget::Id,
/// Limits of the menu bar
limits: Limits,
/// Requested Full Size for expanded menu bar
size: Size,
},
Ignore,
Task(Arc<dyn Fn() -> Task<Action> + Send + Sync>),
}
impl std::fmt::Debug for Action {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::AppSubsurface(arg0, arg1) => f
.debug_tuple("AppSubsurface")
.field(arg0)
.field(arg1)
.finish(),
Self::Subsurface(arg0, arg1) => {
f.debug_tuple("Subsurface").field(arg0).field(arg1).finish()
}
Self::DestroySubsurface(arg0) => {
f.debug_tuple("DestroySubsurface").field(arg0).finish()
}
Self::AppPopup(arg0, arg1) => {
f.debug_tuple("AppPopup").field(arg0).field(arg1).finish()
}
Self::Popup(arg0, arg1) => f.debug_tuple("Popup").field(arg0).field(arg1).finish(),
Self::DestroyPopup(arg0) => f.debug_tuple("DestroyPopup").field(arg0).finish(),
Self::ResponsiveMenuBar {
menu_bar,
limits,
size,
} => f
.debug_struct("ResponsiveMenuBar")
.field("menu_bar", menu_bar)
.field("limits", limits)
.field("size", size)
.finish(),
Self::Ignore => write!(f, "Ignore"),
Self::Task(_) => f.debug_tuple("Future").finish(),
}
}
}