Compare commits
No commits in common. "post-1.9-merge-20260924" and "master" have entirely different histories.
post-1.9-m
...
master
122 changed files with 2376 additions and 4576 deletions
2308
Cargo.lock
generated
2308
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
74
Cargo.toml
74
Cargo.toml
|
|
@ -118,17 +118,13 @@ winit = [
|
||||||
"iced_accessibility?/accesskit_winit",
|
"iced_accessibility?/accesskit_winit",
|
||||||
"iced_program/winit",
|
"iced_program/winit",
|
||||||
]
|
]
|
||||||
wayland = [
|
wayland = ["iced_renderer/wayland", "iced_winit/wayland"]
|
||||||
"iced_renderer/wayland",
|
cctk = ["iced_winit/cctk", "iced_widget/cctk", "iced_core/cctk", "wayland"]
|
||||||
"iced_winit/wayland",
|
# Enables the sctk shell.
|
||||||
"iced_runtime/wayland",
|
|
||||||
"iced_widget/wayland",
|
|
||||||
"iced_core/wayland",
|
|
||||||
]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
||||||
|
|
||||||
iced_devtools.workspace = true
|
iced_devtools.workspace = true
|
||||||
iced_devtools.optional = true
|
|
||||||
iced_debug.workspace = true
|
iced_debug.workspace = true
|
||||||
|
|
||||||
iced_program.workspace = true
|
iced_program.workspace = true
|
||||||
|
|
@ -138,9 +134,10 @@ iced_renderer.workspace = true
|
||||||
iced_runtime.workspace = true
|
iced_runtime.workspace = true
|
||||||
iced_widget.workspace = true
|
iced_widget.workspace = true
|
||||||
iced_winit.workspace = true
|
iced_winit.workspace = true
|
||||||
iced_winit.optional = true
|
iced_devtools.optional = true
|
||||||
iced_tester.workspace = true
|
iced_tester.workspace = true
|
||||||
iced_tester.optional = true
|
iced_tester.optional = true
|
||||||
|
iced_winit.optional = true
|
||||||
iced_highlighter.workspace = true
|
iced_highlighter.workspace = true
|
||||||
iced_highlighter.optional = true
|
iced_highlighter.optional = true
|
||||||
iced_accessibility.workspace = true
|
iced_accessibility.workspace = true
|
||||||
|
|
@ -193,7 +190,6 @@ members = [
|
||||||
"winit",
|
"winit",
|
||||||
"examples/*",
|
"examples/*",
|
||||||
"accessibility",
|
"accessibility",
|
||||||
"build_helpers",
|
|
||||||
]
|
]
|
||||||
exclude = ["examples/integration"]
|
exclude = ["examples/integration"]
|
||||||
|
|
||||||
|
|
@ -206,7 +202,7 @@ repository = "https://github.com/iced-rs/iced"
|
||||||
homepage = "https://iced.rs"
|
homepage = "https://iced.rs"
|
||||||
categories = ["gui"]
|
categories = ["gui"]
|
||||||
keywords = ["gui", "ui", "graphics", "interface", "widgets"]
|
keywords = ["gui", "ui", "graphics", "interface", "widgets"]
|
||||||
rust-version = "1.92"
|
rust-version = "1.89"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
iced = { version = "0.14.0", path = "." }
|
iced = { version = "0.14.0", path = "." }
|
||||||
|
|
@ -242,9 +238,10 @@ async-std = "1.0"
|
||||||
bitflags = "2.5"
|
bitflags = "2.5"
|
||||||
bytemuck = { version = "1.0", features = ["derive"] }
|
bytemuck = { version = "1.0", features = ["derive"] }
|
||||||
bytes = "1.6"
|
bytes = "1.6"
|
||||||
cosmic-text = { path = "../../cosmic-text" }
|
cosmic-text = { git = "https://github.com/pop-os/cosmic-text.git" }
|
||||||
cryoglyph = { package = "cryoglyph", path = "../../glyphon" }
|
# cosmic-text = "0.10"
|
||||||
dark-light = "1.0"
|
dark-light = "1.0"
|
||||||
|
cryoglyph = { package = "cryoglyph", git = "https://github.com/pop-os/glyphon.git", tag = "cosmic-0.14" }
|
||||||
resvg = "0.45"
|
resvg = "0.45"
|
||||||
web-sys = "0.3.69"
|
web-sys = "0.3.69"
|
||||||
guillotiere = "0.6"
|
guillotiere = "0.6"
|
||||||
|
|
@ -278,11 +275,8 @@ tiny-skia = { version = "0.11", default-features = false, features = [
|
||||||
"std",
|
"std",
|
||||||
"simd",
|
"simd",
|
||||||
] }
|
] }
|
||||||
cctk = { path = "../../cosmic-protocols/client-toolkit", package = "cosmic-client-toolkit" }
|
cctk = { git = "https://github.com/pop-os/cosmic-protocols", package = "cosmic-client-toolkit", rev = "160b086" }
|
||||||
# Yoda: default-features=false drops softbuffer's x11 / x11-dlopen (pulled
|
softbuffer = { git = "https://github.com/pop-os/softbuffer", tag = "cosmic-4.0" }
|
||||||
# tiny-xlib + as-raw-xcb-connection). The wayland + wayland-dlopen features
|
|
||||||
# are re-activated via iced_tiny_skia's own `wayland` feature propagation.
|
|
||||||
softbuffer = { path = "../../softbuffer", default-features = false }
|
|
||||||
syntect = "5.2"
|
syntect = "5.2"
|
||||||
tokio = "1.0"
|
tokio = "1.0"
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
|
|
@ -294,7 +288,7 @@ url = "2.5"
|
||||||
wasm-bindgen-futures = "0.4"
|
wasm-bindgen-futures = "0.4"
|
||||||
wasmtimer = "0.4.2"
|
wasmtimer = "0.4.2"
|
||||||
web-time = "1.1"
|
web-time = "1.1"
|
||||||
wgpu = { version = "28.0", default-features = false, features = [
|
wgpu = { version = "27.0", default-features = false, features = [
|
||||||
"std",
|
"std",
|
||||||
"wgsl",
|
"wgsl",
|
||||||
] }
|
] }
|
||||||
|
|
@ -308,40 +302,13 @@ wayland-client = { version = "0.31.5" }
|
||||||
winapi = "0.3"
|
winapi = "0.3"
|
||||||
# window_clipboard = "0.4.1"
|
# window_clipboard = "0.4.1"
|
||||||
|
|
||||||
# Yoda: window_clipboard pinned to our public fork that has x11 gated behind
|
window_clipboard = { git = "https://github.com/pop-os/window_clipboard.git", tag = "sctk-0.20" }
|
||||||
# an opt-in feature. default-features=false + features=["wayland"] drops
|
dnd = { git = "https://github.com/pop-os/window_clipboard.git", tag = "sctk-0.20" }
|
||||||
# clipboard_x11 + x11rb from the build. Branch yoda-x11-optional on
|
mime = { git = "https://github.com/pop-os/window_clipboard.git", tag = "sctk-0.20" }
|
||||||
# https://forge.aditua.com/leyoda/window_clipboard.
|
winit = { git = "https://github.com/pop-os/winit.git", tag = "cosmic-0.14" }
|
||||||
window_clipboard = { path = "../../window_clipboard", default-features = false, features = ["wayland"] }
|
winit-core = { git = "https://github.com/pop-os/winit.git", tag = "cosmic-0.14" }
|
||||||
dnd = { path = "../../window_clipboard/dnd" }
|
|
||||||
mime = { path = "../../window_clipboard/mime" }
|
|
||||||
# Yoda: default-features = false drops winit's x11 default, which otherwise
|
|
||||||
# pulls winit-x11, x11-dl, tiny-xlib, as-raw-xcb-connection. Our Wayland-only
|
|
||||||
# fork doesn't need any of that — the wayland + wayland-dlopen features are
|
|
||||||
# re-enabled via iced_winit's own feature propagation from libcosmic-yoda.
|
|
||||||
winit = { path = "../../winit/winit", default-features = false }
|
|
||||||
winit-core = { path = "../../winit/winit-core" }
|
|
||||||
cursor-icon = "1.1.0"
|
cursor-icon = "1.1.0"
|
||||||
|
|
||||||
[patch.'https://github.com/pop-os/softbuffer']
|
|
||||||
softbuffer = { path = "../../softbuffer" }
|
|
||||||
|
|
||||||
[patch.'https://github.com/pop-os/smithay-clipboard']
|
|
||||||
smithay-clipboard = { path = "../../smithay-clipboard" }
|
|
||||||
|
|
||||||
[patch.'https://github.com/pop-os/winit.git']
|
|
||||||
dpi = { path = "../../winit/dpi" }
|
|
||||||
winit = { path = "../../winit/winit" }
|
|
||||||
winit-android = { path = "../../winit/winit-android" }
|
|
||||||
winit-appkit = { path = "../../winit/winit-appkit" }
|
|
||||||
winit-common = { path = "../../winit/winit-common" }
|
|
||||||
winit-core = { path = "../../winit/winit-core" }
|
|
||||||
winit-orbital = { path = "../../winit/winit-orbital" }
|
|
||||||
winit-uikit = { path = "../../winit/winit-uikit" }
|
|
||||||
winit-wayland = { path = "../../winit/winit-wayland" }
|
|
||||||
winit-web = { path = "../../winit/winit-web" }
|
|
||||||
winit-win32 = { path = "../../winit/winit-win32" }
|
|
||||||
winit-x11 = { path = "../../winit/winit-x11" }
|
|
||||||
|
|
||||||
[workspace.lints.rust]
|
[workspace.lints.rust]
|
||||||
rust_2018_idioms = { level = "deny", priority = -1 }
|
rust_2018_idioms = { level = "deny", priority = -1 }
|
||||||
|
|
@ -367,6 +334,3 @@ useless_conversion = "deny"
|
||||||
|
|
||||||
[workspace.lints.rustdoc]
|
[workspace.lints.rustdoc]
|
||||||
broken_intra_doc_links = "forbid"
|
broken_intra_doc_links = "forbid"
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
build_helpers = { path = "./build_helpers" }
|
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,10 @@ async-io = ["accesskit_winit?/async-io"]
|
||||||
tokio = ["accesskit_winit?/tokio"]
|
tokio = ["accesskit_winit?/tokio"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
accesskit = { git = "https://github.com/wash2/accesskit", rev = "6c20249" }
|
accesskit = { git = "https://github.com/wash2/accesskit", tag = "cosmic-0.14" }
|
||||||
accesskit_windows = { git = "https://github.com/wash2/accesskit", rev = "6c20249", optional = true }
|
accesskit_windows = { git = "https://github.com/wash2/accesskit", tag = "cosmic-0.14", optional = true }
|
||||||
accesskit_macos = { git = "https://github.com/wash2/accesskit", rev = "6c20249", optional = true }
|
accesskit_macos = { git = "https://github.com/wash2/accesskit", tag = "cosmic-0.14", optional = true }
|
||||||
accesskit_winit = { git = "https://github.com/wash2/accesskit", rev = "6c20249", optional = true, default-features = false, features = [
|
accesskit_winit = { git = "https://github.com/wash2/accesskit", tag = "cosmic-0.14", optional = true, default-features = false, features = [
|
||||||
"rwh_06",
|
"rwh_06",
|
||||||
] }
|
] }
|
||||||
# accesskit = { path = "../../../accesskit/common" }
|
# accesskit = { path = "../../../accesskit/common" }
|
||||||
|
|
|
||||||
3
build.rs
3
build.rs
|
|
@ -1,3 +0,0 @@
|
||||||
fn main() {
|
|
||||||
build_helpers::cfg_aliases_setup();
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "build_helpers"
|
|
||||||
version.workspace = true
|
|
||||||
authors.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
repository.workspace = true
|
|
||||||
homepage.workspace = true
|
|
||||||
categories.workspace = true
|
|
||||||
keywords.workspace = true
|
|
||||||
rust-version.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
cfg_aliases = { version = "0.2.1" }
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
use cfg_aliases::cfg_aliases;
|
|
||||||
|
|
||||||
pub fn cfg_aliases_setup() {
|
|
||||||
// The script doesn't depend on our code
|
|
||||||
println!("cargo:rerun-if-changed=build.rs");
|
|
||||||
|
|
||||||
// Setup cfg aliases
|
|
||||||
cfg_aliases! {
|
|
||||||
// Systems.
|
|
||||||
android_platform: { target_os = "android" },
|
|
||||||
wasm_platform: { all(target_family = "wasm", not(target_os = "emscripten")) },
|
|
||||||
macos_platform: { target_os = "macos" },
|
|
||||||
ios_platform: { target_os = "ios" },
|
|
||||||
windows_platform: { target_os = "windows" },
|
|
||||||
apple: { any(target_os = "ios", target_os = "macos") },
|
|
||||||
free_unix: { all(unix, not(apple), not(android_platform), not(target_os = "emscripten")) },
|
|
||||||
redox: { target_os = "redox" },
|
|
||||||
|
|
||||||
// Native displays.
|
|
||||||
wayland_platform: { all(feature = "wayland", free_unix, not(wasm), not(redox)) },
|
|
||||||
xdg_portal: { all(feature = "xdg-portal", free_unix, not(wasm), not(redox)) },
|
|
||||||
orbital_platform: { redox },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -19,7 +19,6 @@ crisp = []
|
||||||
basic-shaping = []
|
basic-shaping = []
|
||||||
advanced-shaping = []
|
advanced-shaping = []
|
||||||
a11y = ["iced_accessibility"]
|
a11y = ["iced_accessibility"]
|
||||||
wayland = ["cctk"]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
palette = "0.7"
|
palette = "0.7"
|
||||||
|
|
@ -32,7 +31,6 @@ num-traits.workspace = true
|
||||||
rustc-hash.workspace = true
|
rustc-hash.workspace = true
|
||||||
smol_str.workspace = true
|
smol_str.workspace = true
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
unicode-segmentation.workspace = true
|
|
||||||
web-time.workspace = true
|
web-time.workspace = true
|
||||||
|
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
|
|
@ -44,6 +42,8 @@ window_clipboard.workspace = true
|
||||||
dnd.workspace = true
|
dnd.workspace = true
|
||||||
mime.workspace = true
|
mime.workspace = true
|
||||||
|
|
||||||
|
cctk.workspace = true
|
||||||
|
cctk.optional = true
|
||||||
# /TODO(POP)
|
# /TODO(POP)
|
||||||
[dependencies.iced_accessibility]
|
[dependencies.iced_accessibility]
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
@ -55,10 +55,3 @@ raw-window-handle.workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
approx = "0.5"
|
approx = "0.5"
|
||||||
|
|
||||||
[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_vendor = "apple"))))'.dependencies]
|
|
||||||
cctk.workspace = true
|
|
||||||
cctk.optional = true
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
build_helpers = { path = "../build_helpers" }
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
fn main() {
|
|
||||||
build_helpers::cfg_aliases_setup();
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::event::Event;
|
use crate::event::{self, Event};
|
||||||
use crate::id::Id;
|
use crate::id::Id;
|
||||||
use crate::layout;
|
use crate::layout;
|
||||||
use crate::mouse;
|
use crate::mouse;
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,7 @@ use crate::keyboard;
|
||||||
use crate::mouse;
|
use crate::mouse;
|
||||||
use crate::touch;
|
use crate::touch;
|
||||||
use crate::window;
|
use crate::window;
|
||||||
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
#[cfg(wayland_platform)]
|
|
||||||
/// A platform specific event for wayland
|
/// A platform specific event for wayland
|
||||||
pub mod wayland;
|
pub mod wayland;
|
||||||
/// A user interface event.
|
/// A user interface event.
|
||||||
|
|
@ -50,7 +49,7 @@ pub enum Event {
|
||||||
/// A platform specific event
|
/// A platform specific event
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum PlatformSpecific {
|
pub enum PlatformSpecific {
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
/// A Wayland specific event
|
/// A Wayland specific event
|
||||||
Wayland(wayland::Event),
|
Wayland(wayland::Event),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,4 @@ pub enum Event {
|
||||||
Subsurface(SubsurfaceEvent),
|
Subsurface(SubsurfaceEvent),
|
||||||
/// Keyboard inhibit shortcuts
|
/// Keyboard inhibit shortcuts
|
||||||
ShortcutsInhibited(bool),
|
ShortcutsInhibited(bool),
|
||||||
/// Blur Support Enabled
|
|
||||||
BlurEnabled,
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use crate::mouse;
|
||||||
use crate::overlay;
|
use crate::overlay;
|
||||||
use crate::renderer;
|
use crate::renderer;
|
||||||
use crate::widget;
|
use crate::widget;
|
||||||
use crate::{Clipboard, Event, Layout, Overlay, Shell, Size};
|
use crate::{Clipboard, Event, Layout, Overlay, Point, Rectangle, Shell, Size};
|
||||||
|
|
||||||
/// An [`Overlay`] container that displays multiple overlay [`overlay::Element`]
|
/// An [`Overlay`] container that displays multiple overlay [`overlay::Element`]
|
||||||
/// children.
|
/// children.
|
||||||
|
|
|
||||||
|
|
@ -78,8 +78,6 @@ pub enum Action {
|
||||||
SelectLine,
|
SelectLine,
|
||||||
/// Select the entire buffer.
|
/// Select the entire buffer.
|
||||||
SelectAll,
|
SelectAll,
|
||||||
/// Clear the selection
|
|
||||||
ClearSelection,
|
|
||||||
/// Perform an [`Edit`].
|
/// Perform an [`Edit`].
|
||||||
Edit(Edit),
|
Edit(Edit),
|
||||||
/// Click the [`Editor`] at the given [`Point`].
|
/// Click the [`Editor`] at the given [`Point`].
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ pub use scrollable::Scrollable;
|
||||||
pub use text_input::TextInput;
|
pub use text_input::TextInput;
|
||||||
|
|
||||||
use crate::widget::Id;
|
use crate::widget::Id;
|
||||||
use crate::{Rectangle, Vector, window};
|
use crate::{Rectangle, Vector};
|
||||||
|
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
@ -75,12 +75,6 @@ pub trait Operation<T = ()>: Send {
|
||||||
fn finish(&self) -> Outcome<T> {
|
fn finish(&self) -> Outcome<T> {
|
||||||
Outcome::None
|
Outcome::None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Track the active window id being processed if relevant to the operation
|
|
||||||
fn set_window_id(&mut self, _id: window::Id) {}
|
|
||||||
|
|
||||||
/// Used to mark the beginning of operations for a widget and its children
|
|
||||||
fn pre_operation(&mut self, _id: Option<&Id>) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, O> Operation<O> for Box<T>
|
impl<T, O> Operation<O> for Box<T>
|
||||||
|
|
@ -146,14 +140,6 @@ where
|
||||||
fn finish(&self) -> Outcome<O> {
|
fn finish(&self) -> Outcome<O> {
|
||||||
self.as_ref().finish()
|
self.as_ref().finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_window_id(&mut self, id: window::Id) {
|
|
||||||
self.as_mut().set_window_id(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pre_operation(&mut self, id: Option<&Id>) {
|
|
||||||
self.as_mut().pre_operation(id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The result of an [`Operation`].
|
/// The result of an [`Operation`].
|
||||||
|
|
@ -257,14 +243,6 @@ where
|
||||||
fn finish(&self) -> Outcome<O> {
|
fn finish(&self) -> Outcome<O> {
|
||||||
Outcome::None
|
Outcome::None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pre_operation(&mut self, id: Option<&Id>) {
|
|
||||||
self.operation.pre_operation(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_window_id(&mut self, id: window::Id) {
|
|
||||||
self.operation.set_window_id(id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BlackBox { operation }
|
BlackBox { operation }
|
||||||
|
|
@ -363,10 +341,6 @@ where
|
||||||
) {
|
) {
|
||||||
self.operation.custom(id, bounds, state);
|
self.operation.custom(id, bounds, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pre_operation(&mut self, id: Option<&Id>) {
|
|
||||||
self.operation.pre_operation(id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.operation.traverse(&mut |operation| {
|
self.operation.traverse(&mut |operation| {
|
||||||
|
|
@ -436,14 +410,6 @@ where
|
||||||
})),
|
})),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_window_id(&mut self, id: window::Id) {
|
|
||||||
self.operation.set_window_id(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pre_operation(&mut self, id: Option<&Id>) {
|
|
||||||
self.operation.pre_operation(id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Map {
|
Map {
|
||||||
|
|
@ -549,14 +515,6 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_window_id(&mut self, id: window::Id) {
|
|
||||||
self.operation.set_window_id(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pre_operation(&mut self, id: Option<&Id>) {
|
|
||||||
self.operation.pre_operation(id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Chain {
|
Chain {
|
||||||
|
|
@ -608,14 +566,6 @@ pub fn scoped<T: 'static>(
|
||||||
outcome => outcome,
|
outcome => outcome,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pre_operation(&mut self, id: Option<&Id>) {
|
|
||||||
self.operation.pre_operation(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_window_id(&mut self, id: window::Id) {
|
|
||||||
self.operation.set_window_id(id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopedOperation {
|
ScopedOperation {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use super::Operation;
|
||||||
use crate::{
|
use crate::{
|
||||||
Rectangle,
|
Rectangle,
|
||||||
id::Id,
|
id::Id,
|
||||||
widget::operation::Outcome,
|
widget::operation::{Outcome, focusable::Count},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Produces an [`Operation`] that searches for the Id
|
/// Produces an [`Operation`] that searches for the Id
|
||||||
|
|
|
||||||
|
|
@ -22,18 +22,16 @@
|
||||||
//! ```
|
//! ```
|
||||||
use crate::alignment;
|
use crate::alignment;
|
||||||
use crate::layout;
|
use crate::layout;
|
||||||
use crate::mouse::{self, click};
|
use crate::mouse;
|
||||||
use crate::renderer;
|
use crate::renderer;
|
||||||
use crate::text;
|
|
||||||
use crate::text::paragraph::{self, Paragraph};
|
use crate::text::paragraph::{self, Paragraph};
|
||||||
|
use crate::text::{self, Fragment};
|
||||||
use crate::widget::tree::{self, Tree};
|
use crate::widget::tree::{self, Tree};
|
||||||
use crate::{
|
use crate::{
|
||||||
Clipboard, Color, Element, Event, Layout, Length, Pixels, Point, Rectangle,
|
Color, Element, Layout, Length, Pixels, Rectangle, Size, Theme, Widget,
|
||||||
Shell, Size, Theme, Widget, keyboard, touch,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
pub use text::{Alignment, Ellipsize, LineHeight, Shaping, Wrapping};
|
pub use text::{Alignment, Ellipsize, LineHeight, Shaping, Wrapping};
|
||||||
|
|
||||||
/// A bunch of text.
|
/// A bunch of text.
|
||||||
|
|
@ -67,7 +65,6 @@ where
|
||||||
fragment: text::Fragment<'a>,
|
fragment: text::Fragment<'a>,
|
||||||
format: Format<Renderer::Font>,
|
format: Format<Renderer::Font>,
|
||||||
class: Theme::Class<'a>,
|
class: Theme::Class<'a>,
|
||||||
selectable: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Theme, Renderer> Text<'a, Theme, Renderer>
|
impl<'a, Theme, Renderer> Text<'a, Theme, Renderer>
|
||||||
|
|
@ -82,7 +79,6 @@ where
|
||||||
fragment: fragment.into_fragment(),
|
fragment: fragment.into_fragment(),
|
||||||
format: Format::default(),
|
format: Format::default(),
|
||||||
class: Theme::default(),
|
class: Theme::default(),
|
||||||
selectable: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -193,17 +189,7 @@ where
|
||||||
{
|
{
|
||||||
let color = color.map(Into::into);
|
let color = color.map(Into::into);
|
||||||
|
|
||||||
self.style(move |_theme| Style {
|
self.style(move |_theme| Style { color })
|
||||||
color,
|
|
||||||
..Style::default()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Makes the [`Text`] selectable. When enabled, the user can click and
|
|
||||||
/// drag to select text, and copy it with Ctrl+C / Cmd+C.
|
|
||||||
pub fn selectable(mut self) -> Self {
|
|
||||||
self.selectable = true;
|
|
||||||
self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the style class of the [`Text`].
|
/// Sets the style class of the [`Text`].
|
||||||
|
|
@ -216,116 +202,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The internal state of a [`Text`] widget.
|
/// The internal state of a [`Text`] widget.
|
||||||
pub struct State<P: Paragraph> {
|
pub type State<P> = paragraph::Plain<P>;
|
||||||
/// The cached paragraph layout.
|
|
||||||
pub paragraph: paragraph::Plain<P>,
|
|
||||||
/// Lazily allocated when text is selectable and first interacted with.
|
|
||||||
selection: Option<Box<SelectionState>>,
|
|
||||||
focused: bool,
|
|
||||||
keyboard_focused: bool,
|
|
||||||
context_menu_position: Option<Point>,
|
|
||||||
clipboard_has_text: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: Paragraph> Default for State<P> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
paragraph: paragraph::Plain::default(),
|
|
||||||
selection: None,
|
|
||||||
focused: false,
|
|
||||||
keyboard_focused: false,
|
|
||||||
context_menu_position: None,
|
|
||||||
clipboard_has_text: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: Paragraph> std::fmt::Debug for State<P> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.debug_struct("State")
|
|
||||||
.field("selection", &self.selection)
|
|
||||||
.field("focused", &self.focused)
|
|
||||||
.finish_non_exhaustive()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: Paragraph> State<P> {
|
|
||||||
/// Returns `true` if the widget currently has focus.
|
|
||||||
pub fn is_focused(&self) -> bool {
|
|
||||||
self.focused
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if focus was gained via keyboard navigation (Tab).
|
|
||||||
pub fn is_keyboard_focused(&self) -> bool {
|
|
||||||
self.keyboard_focused
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clears focus, selection, and all interaction state.
|
|
||||||
pub fn clear_focus(&mut self) {
|
|
||||||
self.focused = false;
|
|
||||||
self.keyboard_focused = false;
|
|
||||||
self.context_menu_position = None;
|
|
||||||
if let Some(sel) = &mut self.selection {
|
|
||||||
sel.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the context menu position, if a context menu should be shown.
|
|
||||||
pub fn context_menu_position(&self) -> Option<Point> {
|
|
||||||
self.context_menu_position
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets or clears the context menu position.
|
|
||||||
pub fn set_context_menu_position(&mut self, pos: Option<Point>) {
|
|
||||||
self.context_menu_position = pos;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: Paragraph> std::ops::Deref for State<P> {
|
|
||||||
type Target = paragraph::Plain<P>;
|
|
||||||
|
|
||||||
fn deref(&self) -> ¶graph::Plain<P> {
|
|
||||||
&self.paragraph
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: Paragraph> std::ops::DerefMut for State<P> {
|
|
||||||
fn deref_mut(&mut self) -> &mut paragraph::Plain<P> {
|
|
||||||
&mut self.paragraph
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
|
||||||
struct SelectionState {
|
|
||||||
anchor: usize,
|
|
||||||
end: usize,
|
|
||||||
dragging: bool,
|
|
||||||
modifiers: keyboard::Modifiers,
|
|
||||||
last_click: Option<click::Click>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SelectionState {
|
|
||||||
fn clear(&mut self) {
|
|
||||||
self.anchor = 0;
|
|
||||||
self.end = 0;
|
|
||||||
self.dragging = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: Paragraph> crate::widget::operation::Focusable for State<P> {
|
|
||||||
fn is_focused(&self) -> bool {
|
|
||||||
self.focused
|
|
||||||
}
|
|
||||||
|
|
||||||
fn focus(&mut self) {
|
|
||||||
self.focused = true;
|
|
||||||
self.keyboard_focused = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unfocus(&mut self) {
|
|
||||||
self.clear_focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
|
impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
|
||||||
for Text<'_, Theme, Renderer>
|
for Text<'_, Theme, Renderer>
|
||||||
|
|
@ -338,7 +215,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn state(&self) -> tree::State {
|
fn state(&self) -> tree::State {
|
||||||
tree::State::new(State::<Renderer::Paragraph>::default())
|
tree::State::new(paragraph::Plain::<Renderer::Paragraph>::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn size(&self) -> Size<Length> {
|
fn size(&self) -> Size<Length> {
|
||||||
|
|
@ -354,10 +231,8 @@ where
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
limits: &layout::Limits,
|
limits: &layout::Limits,
|
||||||
) -> layout::Node {
|
) -> layout::Node {
|
||||||
let state = tree.state.downcast_mut::<State<Renderer::Paragraph>>();
|
|
||||||
|
|
||||||
layout(
|
layout(
|
||||||
&mut state.paragraph,
|
tree.state.downcast_mut::<State<Renderer::Paragraph>>(),
|
||||||
renderer,
|
renderer,
|
||||||
limits,
|
limits,
|
||||||
&self.fragment,
|
&self.fragment,
|
||||||
|
|
@ -377,372 +252,25 @@ where
|
||||||
) {
|
) {
|
||||||
let state = tree.state.downcast_ref::<State<Renderer::Paragraph>>();
|
let state = tree.state.downcast_ref::<State<Renderer::Paragraph>>();
|
||||||
let style = theme.style(&self.class);
|
let style = theme.style(&self.class);
|
||||||
let bounds = layout.bounds();
|
|
||||||
let paragraph = state.paragraph.raw();
|
draw(
|
||||||
let anchor = bounds.anchor(
|
renderer,
|
||||||
paragraph.min_bounds(),
|
defaults,
|
||||||
paragraph.align_x(),
|
layout.bounds(),
|
||||||
paragraph.align_y(),
|
state.raw(),
|
||||||
|
style,
|
||||||
|
viewport,
|
||||||
);
|
);
|
||||||
|
|
||||||
let rects: Vec<Rectangle> = state
|
|
||||||
.selection
|
|
||||||
.as_ref()
|
|
||||||
.filter(|sel| sel.anchor != sel.end)
|
|
||||||
.map(|sel| {
|
|
||||||
let content: &str = self.fragment.as_ref();
|
|
||||||
let lo_byte =
|
|
||||||
grapheme_to_byte(content, sel.anchor.min(sel.end));
|
|
||||||
let hi_byte =
|
|
||||||
grapheme_to_byte(content, sel.anchor.max(sel.end));
|
|
||||||
|
|
||||||
paragraph
|
|
||||||
.highlight(
|
|
||||||
0,
|
|
||||||
(lo_byte, text::Affinity::After),
|
|
||||||
(hi_byte, text::Affinity::Before),
|
|
||||||
)
|
|
||||||
.into_iter()
|
|
||||||
.map(|r| Rectangle {
|
|
||||||
x: anchor.x + r.x,
|
|
||||||
y: anchor.y + r.y,
|
|
||||||
width: r.width,
|
|
||||||
height: r.height,
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
})
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
let fill_selection = |renderer: &mut Renderer| {
|
|
||||||
for r in &rects {
|
|
||||||
renderer.fill_quad(
|
|
||||||
renderer::Quad {
|
|
||||||
bounds: *r,
|
|
||||||
..renderer::Quad::default()
|
|
||||||
},
|
|
||||||
style.selected_fill,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if style.selected_text_color.is_none() {
|
|
||||||
fill_selection(renderer);
|
|
||||||
}
|
|
||||||
draw(renderer, defaults, bounds, paragraph, style, viewport);
|
|
||||||
if let Some(color) = style.selected_text_color {
|
|
||||||
fill_selection(renderer);
|
|
||||||
for r in &rects {
|
|
||||||
renderer.with_layer(*r, |renderer| {
|
|
||||||
renderer
|
|
||||||
.fill_paragraph(paragraph, anchor, color, *viewport);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(
|
|
||||||
&mut self,
|
|
||||||
tree: &mut Tree,
|
|
||||||
event: &Event,
|
|
||||||
layout: Layout<'_>,
|
|
||||||
cursor: mouse::Cursor,
|
|
||||||
_renderer: &Renderer,
|
|
||||||
clipboard: &mut dyn Clipboard,
|
|
||||||
shell: &mut Shell<'_, Message>,
|
|
||||||
_viewport: &Rectangle,
|
|
||||||
) {
|
|
||||||
if !self.selectable {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let state = tree.state.downcast_mut::<State<Renderer::Paragraph>>();
|
|
||||||
let bounds = layout.bounds();
|
|
||||||
let content: &str = self.fragment.as_ref();
|
|
||||||
let grapheme_count = content.graphemes(true).count();
|
|
||||||
let paragraph = state.paragraph.raw();
|
|
||||||
|
|
||||||
// Any click outside this widget clears selection and focus.
|
|
||||||
if matches!(
|
|
||||||
event,
|
|
||||||
Event::Mouse(mouse::Event::ButtonPressed(_))
|
|
||||||
| Event::Touch(touch::Event::FingerPressed { .. })
|
|
||||||
) {
|
|
||||||
if cursor.position_over(bounds).is_none() {
|
|
||||||
let was_visible = state.focused
|
|
||||||
|| state.keyboard_focused
|
|
||||||
|| state
|
|
||||||
.selection
|
|
||||||
.as_ref()
|
|
||||||
.is_some_and(|sel| sel.anchor != sel.end);
|
|
||||||
|
|
||||||
state.focused = false;
|
|
||||||
state.keyboard_focused = false;
|
|
||||||
if let Some(sel) = &mut state.selection {
|
|
||||||
sel.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
if was_visible {
|
|
||||||
shell.request_redraw();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match event {
|
|
||||||
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
|
|
||||||
| Event::Touch(touch::Event::FingerPressed { .. }) => {
|
|
||||||
if state.context_menu_position.take().is_some() {
|
|
||||||
shell.capture_event();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(pos) = cursor.position_over(bounds) {
|
|
||||||
let sel = state.selection.get_or_insert_with(|| {
|
|
||||||
Box::new(SelectionState::default())
|
|
||||||
});
|
|
||||||
|
|
||||||
let anchor = bounds.anchor(
|
|
||||||
paragraph.min_bounds(),
|
|
||||||
paragraph.align_x(),
|
|
||||||
paragraph.align_y(),
|
|
||||||
);
|
|
||||||
let relative =
|
|
||||||
Point::new(pos.x - anchor.x, pos.y - anchor.y);
|
|
||||||
|
|
||||||
let grapheme_pos =
|
|
||||||
hit_to_grapheme(paragraph, relative, content);
|
|
||||||
|
|
||||||
let new_click = click::Click::new(
|
|
||||||
pos,
|
|
||||||
mouse::Button::Left,
|
|
||||||
sel.last_click.take(),
|
|
||||||
);
|
|
||||||
|
|
||||||
match new_click.kind() {
|
|
||||||
click::Kind::Single => {
|
|
||||||
if sel.modifiers.shift() {
|
|
||||||
sel.end = grapheme_pos;
|
|
||||||
} else {
|
|
||||||
sel.anchor = grapheme_pos;
|
|
||||||
sel.end = grapheme_pos;
|
|
||||||
}
|
|
||||||
sel.dragging = true;
|
|
||||||
}
|
|
||||||
click::Kind::Double => {
|
|
||||||
sel.anchor =
|
|
||||||
previous_start_of_word(content, grapheme_pos);
|
|
||||||
sel.end = next_end_of_word(content, grapheme_pos);
|
|
||||||
sel.dragging = true;
|
|
||||||
}
|
|
||||||
click::Kind::Triple => {
|
|
||||||
sel.anchor = 0;
|
|
||||||
sel.end = grapheme_count;
|
|
||||||
sel.dragging = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sel.last_click = Some(new_click);
|
|
||||||
state.focused = true;
|
|
||||||
state.keyboard_focused = false;
|
|
||||||
shell.capture_event();
|
|
||||||
shell.request_redraw();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Right)) => {
|
|
||||||
if let Some(pos) = cursor.position_over(bounds) {
|
|
||||||
state.context_menu_position = Some(pos);
|
|
||||||
state.clipboard_has_text = clipboard_has_text(clipboard);
|
|
||||||
state.focused = true;
|
|
||||||
state.keyboard_focused = false;
|
|
||||||
shell.capture_event();
|
|
||||||
shell.request_redraw();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
|
|
||||||
| Event::Touch(touch::Event::FingerLifted { .. }) => {
|
|
||||||
if let Some(sel) = &mut state.selection {
|
|
||||||
sel.dragging = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Event::Mouse(mouse::Event::CursorMoved { position })
|
|
||||||
| Event::Touch(touch::Event::FingerMoved { position, .. }) => {
|
|
||||||
if let Some(sel) = &mut state.selection {
|
|
||||||
if sel.dragging {
|
|
||||||
let anchor = bounds.anchor(
|
|
||||||
paragraph.min_bounds(),
|
|
||||||
paragraph.align_x(),
|
|
||||||
paragraph.align_y(),
|
|
||||||
);
|
|
||||||
let relative = Point::new(
|
|
||||||
position.x - anchor.x,
|
|
||||||
position.y - anchor.y,
|
|
||||||
);
|
|
||||||
|
|
||||||
let end = hit_to_grapheme(paragraph, relative, content);
|
|
||||||
|
|
||||||
// Only the drag reaching a new grapheme changes what
|
|
||||||
// is drawn; plain motion inside one does not.
|
|
||||||
if end != sel.end {
|
|
||||||
sel.end = end;
|
|
||||||
shell.request_redraw();
|
|
||||||
}
|
|
||||||
|
|
||||||
shell.capture_event();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Event::Keyboard(keyboard::Event::KeyPressed {
|
|
||||||
key,
|
|
||||||
modifiers,
|
|
||||||
physical_key,
|
|
||||||
text: _,
|
|
||||||
..
|
|
||||||
}) => {
|
|
||||||
if !state.focused {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let sel = state
|
|
||||||
.selection
|
|
||||||
.get_or_insert_with(|| Box::new(SelectionState::default()));
|
|
||||||
|
|
||||||
if modifiers.command() {
|
|
||||||
match key.to_latin(*physical_key) {
|
|
||||||
Some('c') => {
|
|
||||||
let left = sel.anchor.min(sel.end);
|
|
||||||
let right = sel.anchor.max(sel.end);
|
|
||||||
if left != right {
|
|
||||||
let selected: String = content
|
|
||||||
.graphemes(true)
|
|
||||||
.skip(left)
|
|
||||||
.take(right - left)
|
|
||||||
.collect();
|
|
||||||
clipboard.write(
|
|
||||||
crate::clipboard::Kind::Standard,
|
|
||||||
selected,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
shell.capture_event();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Some('a') => {
|
|
||||||
sel.anchor = 0;
|
|
||||||
sel.end = grapheme_count;
|
|
||||||
shell.capture_event();
|
|
||||||
shell.request_redraw();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match key {
|
|
||||||
keyboard::Key::Named(keyboard::key::Named::ArrowLeft) => {
|
|
||||||
let by_word = is_jump_modifier(*modifiers);
|
|
||||||
if modifiers.shift() {
|
|
||||||
sel.end = if by_word {
|
|
||||||
previous_start_of_word(content, sel.end)
|
|
||||||
} else {
|
|
||||||
sel.end.saturating_sub(1)
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
let left = sel.anchor.min(sel.end);
|
|
||||||
let pos = if by_word {
|
|
||||||
previous_start_of_word(content, left)
|
|
||||||
} else {
|
|
||||||
left.saturating_sub(1)
|
|
||||||
};
|
|
||||||
sel.anchor = pos;
|
|
||||||
sel.end = pos;
|
|
||||||
}
|
|
||||||
shell.capture_event();
|
|
||||||
shell.request_redraw();
|
|
||||||
}
|
|
||||||
keyboard::Key::Named(keyboard::key::Named::ArrowRight) => {
|
|
||||||
let by_word = is_jump_modifier(*modifiers);
|
|
||||||
if modifiers.shift() {
|
|
||||||
sel.end = if by_word {
|
|
||||||
next_end_of_word(content, sel.end)
|
|
||||||
} else {
|
|
||||||
(sel.end + 1).min(grapheme_count)
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
let right = sel.anchor.max(sel.end);
|
|
||||||
let pos = if by_word {
|
|
||||||
next_end_of_word(content, right)
|
|
||||||
} else {
|
|
||||||
(right + 1).min(grapheme_count)
|
|
||||||
};
|
|
||||||
sel.anchor = pos;
|
|
||||||
sel.end = pos;
|
|
||||||
}
|
|
||||||
shell.capture_event();
|
|
||||||
shell.request_redraw();
|
|
||||||
}
|
|
||||||
keyboard::Key::Named(keyboard::key::Named::Home) => {
|
|
||||||
if modifiers.shift() {
|
|
||||||
sel.end = 0;
|
|
||||||
} else {
|
|
||||||
sel.anchor = 0;
|
|
||||||
sel.end = 0;
|
|
||||||
}
|
|
||||||
shell.capture_event();
|
|
||||||
shell.request_redraw();
|
|
||||||
}
|
|
||||||
keyboard::Key::Named(keyboard::key::Named::End) => {
|
|
||||||
if modifiers.shift() {
|
|
||||||
sel.end = grapheme_count;
|
|
||||||
} else {
|
|
||||||
sel.anchor = grapheme_count;
|
|
||||||
sel.end = grapheme_count;
|
|
||||||
}
|
|
||||||
shell.capture_event();
|
|
||||||
shell.request_redraw();
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) => {
|
|
||||||
if let Some(sel) = &mut state.selection {
|
|
||||||
sel.modifiers = *modifiers;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mouse_interaction(
|
|
||||||
&self,
|
|
||||||
_tree: &Tree,
|
|
||||||
layout: Layout<'_>,
|
|
||||||
cursor: mouse::Cursor,
|
|
||||||
_viewport: &Rectangle,
|
|
||||||
_renderer: &Renderer,
|
|
||||||
) -> mouse::Interaction {
|
|
||||||
if self.selectable && cursor.is_over(layout.bounds()) {
|
|
||||||
mouse::Interaction::Text
|
|
||||||
} else {
|
|
||||||
mouse::Interaction::None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn operate(
|
fn operate(
|
||||||
&mut self,
|
&mut self,
|
||||||
tree: &mut Tree,
|
_tree: &mut Tree,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
_renderer: &Renderer,
|
_renderer: &Renderer,
|
||||||
operation: &mut dyn super::Operation,
|
operation: &mut dyn super::Operation,
|
||||||
) {
|
) {
|
||||||
operation.text(None, layout.bounds(), &self.fragment);
|
operation.text(None, layout.bounds(), &self.fragment);
|
||||||
|
|
||||||
if self.selectable {
|
|
||||||
let state = tree.state.downcast_mut::<State<Renderer::Paragraph>>();
|
|
||||||
operation.focusable(None, layout.bounds(), state);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "a11y")]
|
#[cfg(feature = "a11y")]
|
||||||
|
|
@ -943,28 +471,12 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The appearance of some text.
|
/// The appearance of some text.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Default)]
|
||||||
pub struct Style {
|
pub struct Style {
|
||||||
/// The [`Color`] of the text.
|
/// The [`Color`] of the text.
|
||||||
///
|
///
|
||||||
/// The default, `None`, means using the inherited color.
|
/// The default, `None`, means using the inherited color.
|
||||||
pub color: Option<Color>,
|
pub color: Option<Color>,
|
||||||
/// The fill [`Color`] of the selection highlight.
|
|
||||||
pub selected_fill: Color,
|
|
||||||
/// The [`Color`] of selected text.
|
|
||||||
///
|
|
||||||
/// The default, `None`, keeps the regular text color.
|
|
||||||
pub selected_text_color: Option<Color>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Style {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
color: None,
|
|
||||||
selected_fill: DEFAULT_SELECTION_COLOR,
|
|
||||||
selected_text_color: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The theme catalog of a [`Text`].
|
/// The theme catalog of a [`Text`].
|
||||||
|
|
@ -998,14 +510,13 @@ impl Catalog for Theme {
|
||||||
|
|
||||||
/// The default text styling; color is inherited.
|
/// The default text styling; color is inherited.
|
||||||
pub fn default(_theme: &Theme) -> Style {
|
pub fn default(_theme: &Theme) -> Style {
|
||||||
Style::default()
|
Style { color: None }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Text with the default base color.
|
/// Text with the default base color.
|
||||||
pub fn base(theme: &Theme) -> Style {
|
pub fn base(theme: &Theme) -> Style {
|
||||||
Style {
|
Style {
|
||||||
color: Some(theme.palette().text),
|
color: Some(theme.palette().text),
|
||||||
..Style::default()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1013,7 +524,6 @@ pub fn base(theme: &Theme) -> Style {
|
||||||
pub fn primary(theme: &Theme) -> Style {
|
pub fn primary(theme: &Theme) -> Style {
|
||||||
Style {
|
Style {
|
||||||
color: Some(theme.palette().primary),
|
color: Some(theme.palette().primary),
|
||||||
..Style::default()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1021,7 +531,6 @@ pub fn primary(theme: &Theme) -> Style {
|
||||||
pub fn secondary(theme: &Theme) -> Style {
|
pub fn secondary(theme: &Theme) -> Style {
|
||||||
Style {
|
Style {
|
||||||
color: Some(theme.extended_palette().secondary.base.color),
|
color: Some(theme.extended_palette().secondary.base.color),
|
||||||
..Style::default()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1029,7 +538,6 @@ pub fn secondary(theme: &Theme) -> Style {
|
||||||
pub fn success(theme: &Theme) -> Style {
|
pub fn success(theme: &Theme) -> Style {
|
||||||
Style {
|
Style {
|
||||||
color: Some(theme.palette().success),
|
color: Some(theme.palette().success),
|
||||||
..Style::default()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1037,7 +545,6 @@ pub fn success(theme: &Theme) -> Style {
|
||||||
pub fn warning(theme: &Theme) -> Style {
|
pub fn warning(theme: &Theme) -> Style {
|
||||||
Style {
|
Style {
|
||||||
color: Some(theme.palette().warning),
|
color: Some(theme.palette().warning),
|
||||||
..Style::default()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1045,206 +552,5 @@ pub fn warning(theme: &Theme) -> Style {
|
||||||
pub fn danger(theme: &Theme) -> Style {
|
pub fn danger(theme: &Theme) -> Style {
|
||||||
Style {
|
Style {
|
||||||
color: Some(theme.palette().danger),
|
color: Some(theme.palette().danger),
|
||||||
..Style::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
use crate::widget::tree::Tree as WidgetTree;
|
|
||||||
|
|
||||||
/// Returns `true` if the clipboard currently holds non-empty text.
|
|
||||||
pub fn clipboard_has_text(clipboard: &dyn Clipboard) -> bool {
|
|
||||||
clipboard
|
|
||||||
.read(crate::clipboard::Kind::Standard)
|
|
||||||
.is_some_and(|s| !s.is_empty())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Implement this on a **widget** to enable context menu support for
|
|
||||||
/// text selection (Copy, Select All, and optionally Cut / Paste) in libcosmic
|
|
||||||
pub trait HasSelectableText {
|
|
||||||
/// Returns the currently selected text, if any.
|
|
||||||
fn selected_text(&self, tree: &WidgetTree) -> Option<String>;
|
|
||||||
|
|
||||||
/// Selects all text.
|
|
||||||
fn select_all(&self, tree: &mut WidgetTree);
|
|
||||||
|
|
||||||
/// Returns `true` if the widget is editable (shows Cut / Paste).
|
|
||||||
fn is_editable(&self) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if the widget has any text (enables Select All).
|
|
||||||
fn has_text(&self, _tree: &WidgetTree) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether the clipboard held text when the context menu was
|
|
||||||
/// requested (enables Paste). Widgets cache this on right-click because
|
|
||||||
/// overlays have no clipboard access.
|
|
||||||
fn clipboard_has_text(&self, _tree: &WidgetTree) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if the widget is currently focused.
|
|
||||||
fn is_focused(&self, tree: &WidgetTree) -> bool;
|
|
||||||
|
|
||||||
/// Returns the position where the context menu should appear
|
|
||||||
fn context_menu_position(&self, tree: &WidgetTree) -> Option<Point>;
|
|
||||||
|
|
||||||
/// Sets or clears the context menu position.
|
|
||||||
fn set_context_menu_position(
|
|
||||||
&self,
|
|
||||||
tree: &mut WidgetTree,
|
|
||||||
pos: Option<Point>,
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Copies the selection to the clipboard.
|
|
||||||
fn copy_to_clipboard(
|
|
||||||
&self,
|
|
||||||
tree: &WidgetTree,
|
|
||||||
clipboard: &mut dyn Clipboard,
|
|
||||||
) {
|
|
||||||
if let Some(text) = self.selected_text(tree) {
|
|
||||||
clipboard.write(crate::clipboard::Kind::Standard, text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deletes the selected text and returns the new full content.
|
|
||||||
/// Only called when [`is_editable`](Self::is_editable) is `true`.
|
|
||||||
fn delete_selection(&self, _tree: &mut WidgetTree) -> Option<String> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Inserts `text` at the cursor (replacing any selection) and returns
|
|
||||||
/// the new full content.
|
|
||||||
/// Only called when [`is_editable`](Self::is_editable) is `true`.
|
|
||||||
fn paste_text(
|
|
||||||
&self,
|
|
||||||
_tree: &mut WidgetTree,
|
|
||||||
_text: &str,
|
|
||||||
) -> Option<String> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Theme: Catalog, Renderer: text::Renderer> HasSelectableText
|
|
||||||
for Text<'_, Theme, Renderer>
|
|
||||||
{
|
|
||||||
fn selected_text(&self, tree: &WidgetTree) -> Option<String> {
|
|
||||||
let state = tree.state.downcast_ref::<State<Renderer::Paragraph>>();
|
|
||||||
let sel = state.selection.as_ref()?;
|
|
||||||
let left = sel.anchor.min(sel.end);
|
|
||||||
let right = sel.anchor.max(sel.end);
|
|
||||||
if left == right {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let content = state.paragraph.content();
|
|
||||||
let lo = grapheme_to_byte(content, left);
|
|
||||||
let hi = grapheme_to_byte(content, right);
|
|
||||||
content.get(lo..hi).map(|s| s.to_owned())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn select_all(&self, tree: &mut WidgetTree) {
|
|
||||||
let state = tree.state.downcast_mut::<State<Renderer::Paragraph>>();
|
|
||||||
let count = state.paragraph.content().graphemes(true).count();
|
|
||||||
let sel = state
|
|
||||||
.selection
|
|
||||||
.get_or_insert_with(|| Box::new(SelectionState::default()));
|
|
||||||
sel.anchor = 0;
|
|
||||||
sel.end = count;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn has_text(&self, tree: &WidgetTree) -> bool {
|
|
||||||
let state = tree.state.downcast_ref::<State<Renderer::Paragraph>>();
|
|
||||||
!state.paragraph.content().is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clipboard_has_text(&self, tree: &WidgetTree) -> bool {
|
|
||||||
let state = tree.state.downcast_ref::<State<Renderer::Paragraph>>();
|
|
||||||
state.clipboard_has_text
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_focused(&self, tree: &WidgetTree) -> bool {
|
|
||||||
let state = tree.state.downcast_ref::<State<Renderer::Paragraph>>();
|
|
||||||
state.is_focused()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn context_menu_position(&self, tree: &WidgetTree) -> Option<Point> {
|
|
||||||
let state = tree.state.downcast_ref::<State<Renderer::Paragraph>>();
|
|
||||||
state.context_menu_position()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_context_menu_position(
|
|
||||||
&self,
|
|
||||||
tree: &mut WidgetTree,
|
|
||||||
pos: Option<Point>,
|
|
||||||
) {
|
|
||||||
let state = tree.state.downcast_mut::<State<Renderer::Paragraph>>();
|
|
||||||
state.set_context_menu_position(pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const DEFAULT_SELECTION_COLOR: Color = Color {
|
|
||||||
r: 0.0,
|
|
||||||
g: 0.47,
|
|
||||||
b: 0.84,
|
|
||||||
a: 0.3,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn grapheme_to_byte(content: &str, grapheme_index: usize) -> usize {
|
|
||||||
content
|
|
||||||
.graphemes(true)
|
|
||||||
.take(grapheme_index)
|
|
||||||
.map(|g| g.len())
|
|
||||||
.sum()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn hit_to_grapheme<P: Paragraph>(
|
|
||||||
paragraph: &P,
|
|
||||||
point: Point,
|
|
||||||
content: &str,
|
|
||||||
) -> usize {
|
|
||||||
match paragraph.hit_test(point) {
|
|
||||||
Some(hit) => {
|
|
||||||
let byte_offset = hit.cursor().min(content.len());
|
|
||||||
content[..byte_offset].graphemes(true).count()
|
|
||||||
}
|
|
||||||
None => content.graphemes(true).count(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn previous_start_of_word(content: &str, grapheme_index: usize) -> usize {
|
|
||||||
let graphemes: Vec<&str> = content.graphemes(true).collect();
|
|
||||||
let clamped = grapheme_index.min(graphemes.len());
|
|
||||||
let before: String = graphemes[..clamped].concat();
|
|
||||||
|
|
||||||
UnicodeSegmentation::split_word_bound_indices(&*before)
|
|
||||||
.filter(|(_, word)| !word.trim_start().is_empty())
|
|
||||||
.next_back()
|
|
||||||
.map_or(0, |(i, prev_word)| {
|
|
||||||
clamped
|
|
||||||
- prev_word.graphemes(true).count()
|
|
||||||
- before[i + prev_word.len()..].graphemes(true).count()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next_end_of_word(content: &str, grapheme_index: usize) -> usize {
|
|
||||||
let graphemes: Vec<&str> = content.graphemes(true).collect();
|
|
||||||
let clamped = grapheme_index.min(graphemes.len());
|
|
||||||
let after: String = graphemes[clamped..].concat();
|
|
||||||
|
|
||||||
UnicodeSegmentation::split_word_bound_indices(&*after)
|
|
||||||
.find(|(_, word)| !word.trim_start().is_empty())
|
|
||||||
.map_or(graphemes.len(), |(i, next_word)| {
|
|
||||||
clamped
|
|
||||||
+ next_word.graphemes(true).count()
|
|
||||||
+ after[..i].graphemes(true).count()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_jump_modifier(modifiers: keyboard::Modifiers) -> bool {
|
|
||||||
if cfg!(target_os = "macos") {
|
|
||||||
modifiers.alt()
|
|
||||||
} else {
|
|
||||||
modifiers.control()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use iced::widget::{button, column, text, Column};
|
|
||||||
use iced::Center;
|
use iced::Center;
|
||||||
|
use iced::widget::{Column, button, column, text};
|
||||||
|
|
||||||
pub fn main() -> iced::Result {
|
pub fn main() -> iced::Result {
|
||||||
iced::run(Counter::update, Counter::view)
|
iced::run(Counter::update, Counter::view)
|
||||||
|
|
@ -42,7 +42,7 @@ impl Counter {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use iced_test::{simulator, Error};
|
use iced_test::{Error, simulator};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn it_counts() -> Result<(), Error> {
|
fn it_counts() -> Result<(), Error> {
|
||||||
|
|
|
||||||
|
|
@ -140,7 +140,7 @@ impl Pipeline {
|
||||||
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
||||||
mag_filter: wgpu::FilterMode::Linear,
|
mag_filter: wgpu::FilterMode::Linear,
|
||||||
min_filter: wgpu::FilterMode::Linear,
|
min_filter: wgpu::FilterMode::Linear,
|
||||||
mipmap_filter: wgpu::MipmapFilterMode::Linear,
|
mipmap_filter: wgpu::FilterMode::Linear,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -223,7 +223,7 @@ impl Pipeline {
|
||||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
label: Some("cubes pipeline layout"),
|
label: Some("cubes pipeline layout"),
|
||||||
bind_group_layouts: &[&uniform_bind_group_layout],
|
bind_group_layouts: &[&uniform_bind_group_layout],
|
||||||
immediate_size: 0,
|
push_constant_ranges: &[],
|
||||||
});
|
});
|
||||||
|
|
||||||
let shader =
|
let shader =
|
||||||
|
|
@ -280,7 +280,7 @@ impl Pipeline {
|
||||||
compilation_options:
|
compilation_options:
|
||||||
wgpu::PipelineCompilationOptions::default(),
|
wgpu::PipelineCompilationOptions::default(),
|
||||||
}),
|
}),
|
||||||
multiview_mask: None,
|
multiview: None,
|
||||||
cache: None,
|
cache: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -388,7 +388,6 @@ impl Pipeline {
|
||||||
),
|
),
|
||||||
timestamp_writes: None,
|
timestamp_writes: None,
|
||||||
occlusion_query_set: None,
|
occlusion_query_set: None,
|
||||||
multiview_mask: None,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
pass.set_viewport(
|
pass.set_viewport(
|
||||||
|
|
@ -479,7 +478,7 @@ impl DepthPipeline {
|
||||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
label: Some("cubes.depth_pipeline.layout"),
|
label: Some("cubes.depth_pipeline.layout"),
|
||||||
bind_group_layouts: &[&bind_group_layout],
|
bind_group_layouts: &[&bind_group_layout],
|
||||||
immediate_size: 0,
|
push_constant_ranges: &[],
|
||||||
});
|
});
|
||||||
|
|
||||||
let shader =
|
let shader =
|
||||||
|
|
@ -521,7 +520,7 @@ impl DepthPipeline {
|
||||||
compilation_options:
|
compilation_options:
|
||||||
wgpu::PipelineCompilationOptions::default(),
|
wgpu::PipelineCompilationOptions::default(),
|
||||||
}),
|
}),
|
||||||
multiview_mask: None,
|
multiview: None,
|
||||||
cache: None,
|
cache: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -587,7 +586,6 @@ impl DepthPipeline {
|
||||||
),
|
),
|
||||||
timestamp_writes: None,
|
timestamp_writes: None,
|
||||||
occlusion_query_set: None,
|
occlusion_query_set: None,
|
||||||
multiview_mask: None,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
pass.set_viewport(
|
pass.set_viewport(
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,6 @@ impl Scene {
|
||||||
depth_stencil_attachment: None,
|
depth_stencil_attachment: None,
|
||||||
timestamp_writes: None,
|
timestamp_writes: None,
|
||||||
occlusion_query_set: None,
|
occlusion_query_set: None,
|
||||||
multiview_mask: None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,8 +64,8 @@ fn build_pipeline(
|
||||||
let pipeline_layout =
|
let pipeline_layout =
|
||||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
|
push_constant_ranges: &[],
|
||||||
bind_group_layouts: &[],
|
bind_group_layouts: &[],
|
||||||
immediate_size: 0,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||||
|
|
@ -102,7 +101,7 @@ fn build_pipeline(
|
||||||
mask: !0,
|
mask: !0,
|
||||||
alpha_to_coverage_enabled: false,
|
alpha_to_coverage_enabled: false,
|
||||||
},
|
},
|
||||||
multiview_mask: None,
|
multiview: None,
|
||||||
cache: None,
|
cache: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,20 +3,20 @@ use std::{
|
||||||
sync::atomic::{AtomicU64, Ordering},
|
sync::atomic::{AtomicU64, Ordering},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use iced::{Element, id::Id};
|
||||||
use iced::{
|
use iced::{
|
||||||
|
Event, Length, Rectangle,
|
||||||
clipboard::{
|
clipboard::{
|
||||||
dnd::{self, DndAction, DndDestinationRectangle, DndEvent, OfferEvent},
|
dnd::{self, DndAction, DndDestinationRectangle, DndEvent, OfferEvent},
|
||||||
mime::AllowedMimeTypes,
|
mime::AllowedMimeTypes,
|
||||||
},
|
},
|
||||||
event,
|
event,
|
||||||
id::Internal,
|
id::Internal,
|
||||||
mouse, overlay, Event, Length, Rectangle,
|
mouse, overlay,
|
||||||
};
|
};
|
||||||
use iced::{id::Id, Element};
|
|
||||||
use iced_core::{
|
use iced_core::{
|
||||||
self, layout,
|
self, Clipboard, Layout, Shell, Widget, layout,
|
||||||
widget::{tree, Tree},
|
widget::{Tree, tree},
|
||||||
Clipboard, Layout, Shell, Widget,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn dnd_destination<'a, Message: 'static>(
|
pub fn dnd_destination<'a, Message: 'static>(
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
|
||||||
|
use iced::Element;
|
||||||
use iced::id::Id;
|
use iced::id::Id;
|
||||||
use iced::widget::container;
|
use iced::widget::container;
|
||||||
use iced::Element;
|
|
||||||
use iced::{
|
use iced::{
|
||||||
|
Event, Length, Point, Rectangle,
|
||||||
clipboard::dnd::{DndAction, DndEvent, SourceEvent},
|
clipboard::dnd::{DndAction, DndEvent, SourceEvent},
|
||||||
event, mouse, overlay, Event, Length, Point, Rectangle,
|
event, mouse, overlay,
|
||||||
};
|
};
|
||||||
use iced_core::{
|
use iced_core::{
|
||||||
layout, renderer,
|
Clipboard, Shell, layout, renderer,
|
||||||
widget::{tree, Tree},
|
widget::{Tree, tree},
|
||||||
Clipboard, Shell,
|
|
||||||
};
|
};
|
||||||
use iced_core::{Layout, Widget};
|
use iced_core::{Layout, Widget};
|
||||||
|
|
||||||
|
|
@ -37,11 +37,11 @@ pub struct DndSource<'a, Message, AppMessage, D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
'a,
|
'a,
|
||||||
Message: 'static,
|
Message: 'static,
|
||||||
AppMessage: 'static,
|
AppMessage: 'static,
|
||||||
D: iced::clipboard::mime::AsMimeTypes + std::marker::Send + 'static,
|
D: iced::clipboard::mime::AsMimeTypes + std::marker::Send + 'static,
|
||||||
> DndSource<'a, Message, AppMessage, D>
|
> DndSource<'a, Message, AppMessage, D>
|
||||||
{
|
{
|
||||||
pub fn new(child: impl Into<Element<'a, Message>>) -> Self {
|
pub fn new(child: impl Into<Element<'a, Message>>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
@ -119,11 +119,11 @@ impl<
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
'a,
|
'a,
|
||||||
Message: 'static,
|
Message: 'static,
|
||||||
AppMessage: 'static,
|
AppMessage: 'static,
|
||||||
D: iced::clipboard::mime::AsMimeTypes + std::marker::Send + 'static,
|
D: iced::clipboard::mime::AsMimeTypes + std::marker::Send + 'static,
|
||||||
> Widget<Message, iced::Theme, iced::Renderer>
|
> Widget<Message, iced::Theme, iced::Renderer>
|
||||||
for DndSource<'a, Message, AppMessage, D>
|
for DndSource<'a, Message, AppMessage, D>
|
||||||
{
|
{
|
||||||
fn children(&self) -> Vec<Tree> {
|
fn children(&self) -> Vec<Tree> {
|
||||||
|
|
@ -357,11 +357,11 @@ impl<
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
'a,
|
'a,
|
||||||
Message: 'static,
|
Message: 'static,
|
||||||
AppMessage: 'static,
|
AppMessage: 'static,
|
||||||
D: iced::clipboard::mime::AsMimeTypes + std::marker::Send + 'static,
|
D: iced::clipboard::mime::AsMimeTypes + std::marker::Send + 'static,
|
||||||
> From<DndSource<'a, Message, AppMessage, D>> for Element<'a, Message>
|
> From<DndSource<'a, Message, AppMessage, D>> for Element<'a, Message>
|
||||||
{
|
{
|
||||||
fn from(e: DndSource<'a, Message, AppMessage, D>) -> Element<'a, Message> {
|
fn from(e: DndSource<'a, Message, AppMessage, D>) -> Element<'a, Message> {
|
||||||
Element::new(e)
|
Element::new(e)
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ use cctk::sctk::reexports::{
|
||||||
|
|
||||||
use iced::platform_specific::shell::commands::subsurface::get_subsurface;
|
use iced::platform_specific::shell::commands::subsurface::get_subsurface;
|
||||||
use iced::{
|
use iced::{
|
||||||
|
Element, Length, Subscription, Task,
|
||||||
event::wayland::Event as WaylandEvent,
|
event::wayland::Event as WaylandEvent,
|
||||||
platform_specific::{
|
platform_specific::{
|
||||||
runtime::wayland::subsurface::SctkSubsurfaceSettings,
|
runtime::wayland::subsurface::SctkSubsurfaceSettings,
|
||||||
|
|
@ -14,7 +15,6 @@ use iced::{
|
||||||
},
|
},
|
||||||
widget::{button, column, text, text_input},
|
widget::{button, column, text, text_input},
|
||||||
window::{self, Id, Settings},
|
window::{self, Id, Settings},
|
||||||
Element, Length, Subscription, Task,
|
|
||||||
};
|
};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -477,7 +477,6 @@ fn delete_icon() -> Text<'static> {
|
||||||
fn subtle(theme: &Theme) -> text::Style {
|
fn subtle(theme: &Theme) -> text::Style {
|
||||||
text::Style {
|
text::Style {
|
||||||
color: Some(theme.extended_palette().background.strongest.color),
|
color: Some(theme.extended_palette().background.strongest.color),
|
||||||
..text::Style::default()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
//! Load and operate on images.
|
//! Load and operate on images.
|
||||||
#[cfg(feature = "image")]
|
#[cfg(feature = "image")]
|
||||||
use crate::core::Bytes;
|
use crate::core::Bytes;
|
||||||
#[cfg(feature = "image")]
|
|
||||||
use crate::core::Size;
|
|
||||||
|
|
||||||
|
use crate::core::Color;
|
||||||
|
use crate::core::Radians;
|
||||||
use crate::core::Rectangle;
|
use crate::core::Rectangle;
|
||||||
use crate::core::image;
|
use crate::core::image;
|
||||||
use crate::core::svg;
|
use crate::core::svg;
|
||||||
|
|
@ -180,105 +180,3 @@ fn to_error(error: ::image::ImageError) -> image::Error {
|
||||||
error => image::Error::Invalid(Arc::new(error)),
|
error => image::Error::Invalid(Arc::new(error)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// resample a raster image to this target
|
|
||||||
#[cfg(feature = "image")]
|
|
||||||
pub fn downsample_target(
|
|
||||||
native: Size<u32>,
|
|
||||||
bounds: Size<f32>,
|
|
||||||
) -> Option<Size<u32>> {
|
|
||||||
if !(bounds.width >= 1.0 && bounds.height >= 1.0) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Round up to 4px so an animated resize does not make a copy per pixel
|
|
||||||
// TODO: maybe find a better approach
|
|
||||||
let quantize = |length: f32| (length.ceil() as u32).div_ceil(4) * 4;
|
|
||||||
|
|
||||||
let target = Size::new(
|
|
||||||
quantize(bounds.width).min(native.width),
|
|
||||||
quantize(bounds.height).min(native.height),
|
|
||||||
);
|
|
||||||
|
|
||||||
let minified = native.width as f32 >= target.width as f32 * 1.25
|
|
||||||
|| native.height as f32 >= target.height as f32 * 1.25;
|
|
||||||
|
|
||||||
// If the target is big then Lanczos will be too expensive, and sampler is good enough
|
|
||||||
let small = u64::from(target.width) * u64::from(target.height) <= 1 << 18;
|
|
||||||
|
|
||||||
(minified && small).then_some(target)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resamples premultiplied pixels down to `target`.
|
|
||||||
#[cfg(feature = "image")]
|
|
||||||
pub fn downsample_premultiplied(
|
|
||||||
pixels: &[u8],
|
|
||||||
size: Size<u32>,
|
|
||||||
target: Size<u32>,
|
|
||||||
) -> Vec<u8> {
|
|
||||||
let image =
|
|
||||||
::image::RgbaImage::from_raw(size.width, size.height, pixels.to_vec())
|
|
||||||
.expect("pixels hold width * height RGBA pixels");
|
|
||||||
|
|
||||||
resize(&image, target).into_raw()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "image")]
|
|
||||||
fn resize(image: &::image::RgbaImage, target: Size<u32>) -> ::image::RgbaImage {
|
|
||||||
use ::image::imageops::{self, FilterType};
|
|
||||||
use std::borrow::Cow;
|
|
||||||
|
|
||||||
// if image is too big compared to target, box average it to double the target
|
|
||||||
// and then do Lanczos. The final result is almost the same, but lanczos is too
|
|
||||||
// expensive on large images.
|
|
||||||
let image = if image.width() >= target.width * 4
|
|
||||||
&& image.height() >= target.height * 4
|
|
||||||
{
|
|
||||||
Cow::Owned(imageops::thumbnail(
|
|
||||||
image,
|
|
||||||
target.width * 2,
|
|
||||||
target.height * 2,
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Cow::Borrowed(image)
|
|
||||||
};
|
|
||||||
|
|
||||||
imageops::resize(&*image, target.width, target.height, FilterType::Lanczos3)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resamples RGBA pixels down to `target`.
|
|
||||||
///
|
|
||||||
/// Premultiplies the image, to avoid fringing the edges of an icon when interpolating
|
|
||||||
/// transparent pixels.
|
|
||||||
#[cfg(feature = "image")]
|
|
||||||
pub fn downsample(image: &Buffer, target: Size<u32>) -> ::image::RgbaImage {
|
|
||||||
let mut image = ::image::RgbaImage::from_raw(
|
|
||||||
image.width(),
|
|
||||||
image.height(),
|
|
||||||
image.as_raw().to_vec(),
|
|
||||||
)
|
|
||||||
.expect("buffer holds width * height RGBA pixels");
|
|
||||||
|
|
||||||
for pixel in image.pixels_mut() {
|
|
||||||
let alpha = u32::from(pixel[3]);
|
|
||||||
|
|
||||||
for channel in &mut pixel.0[..3] {
|
|
||||||
*channel = ((u32::from(*channel) * alpha + 127) / 255) as u8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut image = resize(&image, target);
|
|
||||||
|
|
||||||
for pixel in image.pixels_mut() {
|
|
||||||
let alpha = u32::from(pixel[3]);
|
|
||||||
|
|
||||||
if alpha > 0 {
|
|
||||||
for channel in &mut pixel.0[..3] {
|
|
||||||
*channel = ((u32::from(*channel) * 255 + alpha / 2) / alpha)
|
|
||||||
.min(255) as u8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
image
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -83,20 +83,13 @@ impl<T: Layer> Stack<T> {
|
||||||
/// Pushes a new clipping region in the [`Stack`]; creating a new layer in the
|
/// Pushes a new clipping region in the [`Stack`]; creating a new layer in the
|
||||||
/// process.
|
/// process.
|
||||||
pub fn push_clip(&mut self, bounds: Rectangle) {
|
pub fn push_clip(&mut self, bounds: Rectangle) {
|
||||||
let bounds = bounds * self.transformation();
|
|
||||||
let bounds = bounds
|
|
||||||
.intersection(&self.layers[self.current].bounds())
|
|
||||||
.unwrap_or(Rectangle {
|
|
||||||
width: 0.0,
|
|
||||||
height: 0.0,
|
|
||||||
..bounds
|
|
||||||
});
|
|
||||||
|
|
||||||
self.previous.push(self.current);
|
self.previous.push(self.current);
|
||||||
|
|
||||||
self.current = self.active_count;
|
self.current = self.active_count;
|
||||||
self.active_count += 1;
|
self.active_count += 1;
|
||||||
|
|
||||||
|
let bounds = bounds * self.transformation();
|
||||||
|
|
||||||
if self.current == self.layers.len() {
|
if self.current == self.layers.len() {
|
||||||
self.layers.push(T::with_bounds(bounds));
|
self.layers.push(T::with_bounds(bounds));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -161,14 +161,6 @@ impl FontSystem {
|
||||||
&mut self.raw
|
&mut self.raw
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a reference to the underlying [`fontdb::Database`].
|
|
||||||
///
|
|
||||||
/// Unlike going through [`raw`](Self::raw) and `db_mut`, this does not
|
|
||||||
/// clear `cosmic-text`'s internal font-match cache.
|
|
||||||
pub fn db(&self) -> &cosmic_text::fontdb::Database {
|
|
||||||
self.raw.db()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Loads a font from its bytes.
|
/// Loads a font from its bytes.
|
||||||
pub fn load_font(&mut self, bytes: Cow<'static, [u8]>) {
|
pub fn load_font(&mut self, bytes: Cow<'static, [u8]>) {
|
||||||
if let Cow::Borrowed(bytes) = bytes {
|
if let Cow::Borrowed(bytes) = bytes {
|
||||||
|
|
|
||||||
|
|
@ -336,10 +336,6 @@ impl editor::Editor for Editor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Action::ClearSelection => {
|
|
||||||
editor.set_selection(cosmic_text::Selection::None);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Editing events
|
// Editing events
|
||||||
Action::Edit(edit) => {
|
Action::Edit(edit) => {
|
||||||
let topmost_line_before_edit = editor
|
let topmost_line_before_edit = editor
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ workspace = true
|
||||||
debug = []
|
debug = []
|
||||||
selector = ["dep:iced_selector"]
|
selector = ["dep:iced_selector"]
|
||||||
a11y = ["iced_accessibility", "iced_core/a11y"]
|
a11y = ["iced_accessibility", "iced_core/a11y"]
|
||||||
wayland = ["iced_core/wayland", "dep:cctk"]
|
cctk = ["iced_core/cctk", "dep:cctk"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bytes.workspace = true
|
bytes.workspace = true
|
||||||
|
|
@ -33,14 +33,9 @@ sipper.optional = true
|
||||||
iced_selector.workspace = true
|
iced_selector.workspace = true
|
||||||
iced_selector.optional = true
|
iced_selector.optional = true
|
||||||
iced_futures.features = ["thread-pool"]
|
iced_futures.features = ["thread-pool"]
|
||||||
|
cctk.workspace = true
|
||||||
|
cctk.optional = true
|
||||||
iced_accessibility.workspace = true
|
iced_accessibility.workspace = true
|
||||||
iced_accessibility.optional = true
|
iced_accessibility.optional = true
|
||||||
window_clipboard.workspace = true
|
window_clipboard.workspace = true
|
||||||
dnd.workspace = true
|
dnd.workspace = true
|
||||||
|
|
||||||
[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_vendor = "apple"))))'.dependencies]
|
|
||||||
cctk.workspace = true
|
|
||||||
cctk.optional = true
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
build_helpers = { path = "../build_helpers" }
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
fn main() {
|
|
||||||
build_helpers::cfg_aliases_setup();
|
|
||||||
}
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
//! Access the clipboard.
|
//! Access the clipboard.
|
||||||
|
|
||||||
|
use std::any::Any;
|
||||||
|
|
||||||
use dnd::{DndDestinationRectangle, DndSurface};
|
use dnd::{DndDestinationRectangle, DndSurface};
|
||||||
use window_clipboard::mime::AllowedMimeTypes;
|
use iced_core::clipboard::DndSource;
|
||||||
|
use window_clipboard::mime::{AllowedMimeTypes, AsMimeTypes};
|
||||||
|
|
||||||
use crate::{Action, Task, oneshot, task};
|
use crate::{oneshot, task, Action, Task};
|
||||||
|
|
||||||
/// An action to be performed on the system.
|
/// An action to be performed on the system.
|
||||||
pub enum DndAction {
|
pub enum DndAction {
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ pub use task::Task;
|
||||||
pub use user_interface::UserInterface;
|
pub use user_interface::UserInterface;
|
||||||
pub use window::Window;
|
pub use window::Window;
|
||||||
|
|
||||||
|
use crate::core::Color;
|
||||||
use crate::futures::futures::channel::oneshot;
|
use crate::futures::futures::channel::oneshot;
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
|
||||||
|
|
@ -2,23 +2,23 @@
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
/// Platform specific actions defined for wayland
|
/// Platform specific actions defined for wayland
|
||||||
pub mod wayland;
|
pub mod wayland;
|
||||||
|
|
||||||
/// Platform specific actions defined for wayland
|
/// Platform specific actions defined for wayland
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
/// Wayland Specific Actions
|
/// Wayland Specific Actions
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
Wayland(wayland::Action),
|
Wayland(wayland::Action),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Action {
|
impl fmt::Debug for Action {
|
||||||
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
Action::Wayland(action) => action.fmt(_f),
|
Action::Wayland(action) => action.fmt(_f),
|
||||||
#[cfg(not(wayland_platform))]
|
#[cfg(not(all(feature = "cctk", target_os = "linux")))]
|
||||||
_ => Ok(()),
|
_ => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ impl fmt::Debug for Action {
|
||||||
f,
|
f,
|
||||||
"Action::ActivationAction::Activate {{ window: {:?}, token: {:?} }}",
|
"Action::ActivationAction::Activate {{ window: {:?}, token: {:?} }}",
|
||||||
window, token,
|
window, token,
|
||||||
),
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ use cctk::sctk::{
|
||||||
};
|
};
|
||||||
use iced_core::layout::Limits;
|
use iced_core::layout::Limits;
|
||||||
|
|
||||||
use iced_core::Rectangle;
|
|
||||||
use iced_core::window::Id;
|
use iced_core::window::Id;
|
||||||
|
use iced_core::Rectangle;
|
||||||
|
|
||||||
/// output for layer surface
|
/// output for layer surface
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -150,18 +150,6 @@ pub enum Action {
|
||||||
/// layer of the layer surface
|
/// layer of the layer surface
|
||||||
layer: Layer,
|
layer: Layer,
|
||||||
},
|
},
|
||||||
/// padding of the surface geometry
|
|
||||||
Padding {
|
|
||||||
/// id of the layer surface
|
|
||||||
id: Id,
|
|
||||||
/// padding of the surface geometry
|
|
||||||
padding: IcedMargin,
|
|
||||||
},
|
|
||||||
ShowOnLock {
|
|
||||||
/// id of the layer surface
|
|
||||||
id: Id,
|
|
||||||
value: bool,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Action {
|
impl fmt::Debug for Action {
|
||||||
|
|
@ -174,56 +162,35 @@ impl fmt::Debug for Action {
|
||||||
),
|
),
|
||||||
Action::Size { id, width, height } => write!(
|
Action::Size { id, width, height } => write!(
|
||||||
f,
|
f,
|
||||||
"Action::LayerSurfaceAction::Size {{ id: {:#?}, width: {:?}, height: {:?} }}",
|
"Action::LayerSurfaceAction::Size {{ id: {:#?}, width: {:?}, height: {:?} }}", id, width, height
|
||||||
id, width, height
|
|
||||||
),
|
),
|
||||||
Action::Destroy(id) => write!(
|
Action::Destroy(id) => write!(
|
||||||
f,
|
f,
|
||||||
"Action::LayerSurfaceAction::Destroy {{ id: {:#?} }}",
|
"Action::LayerSurfaceAction::Destroy {{ id: {:#?} }}", id
|
||||||
id
|
|
||||||
),
|
),
|
||||||
Action::Anchor { id, anchor } => write!(
|
Action::Anchor { id, anchor } => write!(
|
||||||
f,
|
f,
|
||||||
"Action::LayerSurfaceAction::Anchor {{ id: {:#?}, anchor: {:?} }}",
|
"Action::LayerSurfaceAction::Anchor {{ id: {:#?}, anchor: {:?} }}", id, anchor
|
||||||
id, anchor
|
|
||||||
),
|
),
|
||||||
Action::ExclusiveZone { id, exclusive_zone } => write!(
|
Action::ExclusiveZone { id, exclusive_zone } => write!(
|
||||||
f,
|
f,
|
||||||
"Action::LayerSurfaceAction::ExclusiveZone {{ id: {:#?}, exclusive_zone: {exclusive_zone} }}",
|
"Action::LayerSurfaceAction::ExclusiveZone {{ id: {:#?}, exclusive_zone: {exclusive_zone} }}", id
|
||||||
id
|
|
||||||
),
|
),
|
||||||
Action::Margin { id, margin } => write!(
|
Action::Margin { id, margin } => write!(
|
||||||
f,
|
f,
|
||||||
"Action::LayerSurfaceAction::Margin {{ id: {:#?}, margin: {:?} }}",
|
"Action::LayerSurfaceAction::Margin {{ id: {:#?}, margin: {:?} }}", id, margin
|
||||||
id, margin
|
|
||||||
),
|
),
|
||||||
Action::KeyboardInteractivity {
|
Action::KeyboardInteractivity { id, keyboard_interactivity } => write!(
|
||||||
id,
|
|
||||||
keyboard_interactivity,
|
|
||||||
} => write!(
|
|
||||||
f,
|
f,
|
||||||
"Action::LayerSurfaceAction::KeyboardInteractivity {{ id: {:#?}, keyboard_interactivity: {:?} }}",
|
"Action::LayerSurfaceAction::KeyboardInteractivity {{ id: {:#?}, keyboard_interactivity: {:?} }}", id, keyboard_interactivity
|
||||||
id, keyboard_interactivity
|
|
||||||
),
|
),
|
||||||
Action::InputZone { id, zone } => write!(
|
Action::InputZone { id, zone } => write!(
|
||||||
f,
|
f,
|
||||||
"Action::LayerSurfaceAction::InputZone {{ id: {:#?}, zone: {:?} }}",
|
"Action::LayerSurfaceAction::InputZone {{ id: {:#?}, zone: {:?} }}", id, zone
|
||||||
id, zone
|
|
||||||
),
|
),
|
||||||
Action::Layer { id, layer } => write!(
|
Action::Layer { id, layer } => write!(
|
||||||
f,
|
f,
|
||||||
"Action::LayerSurfaceAction::Layer {{ id: {:#?}, layer: {:?} }}",
|
"Action::LayerSurfaceAction::Layer {{ id: {:#?}, layer: {:?} }}", id, layer
|
||||||
id, layer
|
|
||||||
),
|
|
||||||
Action::Padding { id, padding } => write!(
|
|
||||||
f,
|
|
||||||
"Action::LayerSurfaceAction::Padding {{ id: {:#?}, padding: {:?} }}",
|
|
||||||
id, padding
|
|
||||||
),
|
|
||||||
Action::ShowOnLock { id, value } => write!(
|
|
||||||
f,
|
|
||||||
"Action::LayerSurfaceAction::ShowOnLock {{ id: {:#?}, value: {:?} }}",
|
|
||||||
id, value
|
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -124,34 +124,26 @@ pub enum Action {
|
||||||
/// height
|
/// height
|
||||||
height: u32,
|
height: u32,
|
||||||
},
|
},
|
||||||
/// set the size of the popup
|
|
||||||
Reposition {
|
|
||||||
/// id of the popup
|
|
||||||
id: Id,
|
|
||||||
/// positioner of the popup
|
|
||||||
positioner: SctkPositioner,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Action {
|
impl fmt::Debug for Action {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Action::Popup { popup, .. } => {
|
Action::Popup { popup, .. } => write!(
|
||||||
write!(f, "Action::PopupAction::Popup {{ popup: {:?} }}", popup)
|
f,
|
||||||
}
|
"Action::PopupAction::Popup {{ popup: {:?} }}",
|
||||||
Action::Destroy { id } => {
|
popup
|
||||||
write!(f, "Action::PopupAction::Destroy {{ id: {:?} }}", id)
|
),
|
||||||
}
|
Action::Destroy { id } => write!(
|
||||||
|
f,
|
||||||
|
"Action::PopupAction::Destroy {{ id: {:?} }}",
|
||||||
|
id
|
||||||
|
),
|
||||||
Action::Size { id, width, height } => write!(
|
Action::Size { id, width, height } => write!(
|
||||||
f,
|
f,
|
||||||
"Action::PopupAction::Size {{ id: {:?}, width: {:?}, height: {:?} }}",
|
"Action::PopupAction::Size {{ id: {:?}, width: {:?}, height: {:?} }}",
|
||||||
id, width, height
|
id, width, height
|
||||||
),
|
),
|
||||||
Action::Reposition { id, positioner } => write!(
|
|
||||||
f,
|
|
||||||
"Action::Reposition {{ id: {:?}, positioner: {:?} }}",
|
|
||||||
id, positioner
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
//! Find and query widgets in your applications.
|
//! Find and query widgets in your applications.
|
||||||
pub use iced_selector::{
|
pub use iced_selector::{
|
||||||
Bounded, Candidate, Selector, Target, Text, focus, id, is_focused,
|
Bounded, Candidate, Selector, Target, Text, id, is_focused,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::Task;
|
use crate::Task;
|
||||||
|
|
|
||||||
|
|
@ -222,7 +222,7 @@ pub fn frames() -> Subscription<(Id, Instant)> {
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
/// Subscribes to the frames of the window of the running application.
|
/// Subscribes to the frames of the window of the running application.
|
||||||
///
|
///
|
||||||
/// The resulting [`Subscription`] will produce items at a rate equal to the
|
/// The resulting [`Subscription`] will produce items at a rate equal to the
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
use iced_core::window;
|
|
||||||
|
|
||||||
use crate::Selector;
|
use crate::Selector;
|
||||||
use crate::core::widget::operation::{
|
use crate::core::widget::operation::{
|
||||||
Focusable, Outcome, Scrollable, TextInput,
|
Focusable, Outcome, Scrollable, TextInput,
|
||||||
|
|
@ -102,10 +100,6 @@ where
|
||||||
fn finish(&self) -> Self::Output {
|
fn finish(&self) -> Self::Output {
|
||||||
self.outputs.clone()
|
self.outputs.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_window_id(&mut self, id: window::Id) {
|
|
||||||
self.selector.set_window_id(id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Strategy {
|
pub trait Strategy {
|
||||||
|
|
@ -116,8 +110,6 @@ pub trait Strategy {
|
||||||
fn is_done(&self) -> bool;
|
fn is_done(&self) -> bool;
|
||||||
|
|
||||||
fn finish(&self) -> Self::Output;
|
fn finish(&self) -> Self::Output;
|
||||||
|
|
||||||
fn set_window_id(&mut self, _id: window::Id) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -281,12 +273,4 @@ where
|
||||||
fn finish(&self) -> Outcome<S::Output> {
|
fn finish(&self) -> Outcome<S::Output> {
|
||||||
Outcome::Some(self.strategy.finish())
|
Outcome::Some(self.strategy.finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_window_id(&mut self, id: window::Id) {
|
|
||||||
self.strategy.set_window_id(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pre_operation(&mut self, id: Option<&Id>) {
|
|
||||||
self.strategy.feed(Candidate::PreOperation { id });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
//! Select data from the widget tree.
|
//! Select data from the widget tree.
|
||||||
use iced_core as core;
|
use iced_core as core;
|
||||||
use iced_core::window;
|
|
||||||
|
|
||||||
mod find;
|
mod find;
|
||||||
mod target;
|
mod target;
|
||||||
|
|
@ -49,8 +48,6 @@ pub trait Selector {
|
||||||
{
|
{
|
||||||
FindAll::new(find::All::new(self))
|
FindAll::new(find::All::new(self))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_window_id(&mut self, _id: window::Id) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Selector for &str {
|
impl Selector for &str {
|
||||||
|
|
@ -174,42 +171,3 @@ pub fn is_focused() -> impl Selector<Output = Target> {
|
||||||
|
|
||||||
IsFocused
|
IsFocused
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a [`Selector`] that matches widgets that are focusable.
|
|
||||||
pub fn focus() -> impl Selector<Output = (bool, Target, window::Id)> {
|
|
||||||
struct Focus {
|
|
||||||
cur_window_id: window::Id,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Selector for Focus {
|
|
||||||
type Output = (bool, Target, window::Id);
|
|
||||||
|
|
||||||
fn select(&mut self, candidate: Candidate<'_>) -> Option<Self::Output> {
|
|
||||||
if let Candidate::Focusable { state, .. } = candidate {
|
|
||||||
Some((
|
|
||||||
state.is_focused(),
|
|
||||||
Target::from(candidate),
|
|
||||||
self.cur_window_id,
|
|
||||||
))
|
|
||||||
} else if matches!(candidate, Candidate::Scrollable { .. }) {
|
|
||||||
Some((false, Target::from(candidate), self.cur_window_id))
|
|
||||||
} else if matches!(candidate, Candidate::PreOperation { .. }) {
|
|
||||||
Some((false, Target::from(candidate), self.cur_window_id))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn description(&self) -> String {
|
|
||||||
"is focusable and focused".to_owned()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_window_id(&mut self, id: iced_core::window::Id) {
|
|
||||||
self.cur_window_id = id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Focus {
|
|
||||||
cur_window_id: window::Id::NONE,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -42,9 +42,6 @@ pub enum Target {
|
||||||
bounds: Rectangle,
|
bounds: Rectangle,
|
||||||
visible_bounds: Option<Rectangle>,
|
visible_bounds: Option<Rectangle>,
|
||||||
},
|
},
|
||||||
PreOperation {
|
|
||||||
id: Option<Id>,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Target {
|
impl Target {
|
||||||
|
|
@ -57,7 +54,6 @@ impl Target {
|
||||||
| Target::TextInput { bounds, .. }
|
| Target::TextInput { bounds, .. }
|
||||||
| Target::Text { bounds, .. }
|
| Target::Text { bounds, .. }
|
||||||
| Target::Custom { bounds, .. } => *bounds,
|
| Target::Custom { bounds, .. } => *bounds,
|
||||||
Target::PreOperation { .. } => Rectangle::default(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,7 +66,6 @@ impl Target {
|
||||||
| Target::TextInput { visible_bounds, .. }
|
| Target::TextInput { visible_bounds, .. }
|
||||||
| Target::Text { visible_bounds, .. }
|
| Target::Text { visible_bounds, .. }
|
||||||
| Target::Custom { visible_bounds, .. } => *visible_bounds,
|
| Target::Custom { visible_bounds, .. } => *visible_bounds,
|
||||||
Target::PreOperation { .. } => None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -143,9 +138,6 @@ impl From<Candidate<'_>> for Target {
|
||||||
bounds,
|
bounds,
|
||||||
visible_bounds,
|
visible_bounds,
|
||||||
},
|
},
|
||||||
Candidate::PreOperation { id } => {
|
|
||||||
Self::PreOperation { id: id.cloned() }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -203,8 +195,6 @@ pub enum Candidate<'a> {
|
||||||
visible_bounds: Option<Rectangle>,
|
visible_bounds: Option<Rectangle>,
|
||||||
state: &'a dyn Any,
|
state: &'a dyn Any,
|
||||||
},
|
},
|
||||||
/// Used to mark the beginning of operations for a widget and its children
|
|
||||||
PreOperation { id: Option<&'a Id> },
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Candidate<'a> {
|
impl<'a> Candidate<'a> {
|
||||||
|
|
@ -216,8 +206,7 @@ impl<'a> Candidate<'a> {
|
||||||
| Candidate::Scrollable { id, .. }
|
| Candidate::Scrollable { id, .. }
|
||||||
| Candidate::TextInput { id, .. }
|
| Candidate::TextInput { id, .. }
|
||||||
| Candidate::Text { id, .. }
|
| Candidate::Text { id, .. }
|
||||||
| Candidate::Custom { id, .. }
|
| Candidate::Custom { id, .. } => *id,
|
||||||
| Candidate::PreOperation { id } => *id,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -230,7 +219,6 @@ impl<'a> Candidate<'a> {
|
||||||
| Candidate::TextInput { bounds, .. }
|
| Candidate::TextInput { bounds, .. }
|
||||||
| Candidate::Text { bounds, .. }
|
| Candidate::Text { bounds, .. }
|
||||||
| Candidate::Custom { bounds, .. } => *bounds,
|
| Candidate::Custom { bounds, .. } => *bounds,
|
||||||
Candidate::PreOperation { .. } => Rectangle::default(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -243,7 +231,6 @@ impl<'a> Candidate<'a> {
|
||||||
| Candidate::TextInput { visible_bounds, .. }
|
| Candidate::TextInput { visible_bounds, .. }
|
||||||
| Candidate::Text { visible_bounds, .. }
|
| Candidate::Text { visible_bounds, .. }
|
||||||
| Candidate::Custom { visible_bounds, .. } => *visible_bounds,
|
| Candidate::Custom { visible_bounds, .. } => *visible_bounds,
|
||||||
Candidate::PreOperation { .. } => None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -606,7 +606,7 @@ pub mod font {
|
||||||
pub mod event {
|
pub mod event {
|
||||||
//! Handle events of a user interface.
|
//! Handle events of a user interface.
|
||||||
pub use crate::core::event::PlatformSpecific;
|
pub use crate::core::event::PlatformSpecific;
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
pub use crate::core::event::wayland;
|
pub use crate::core::event::wayland;
|
||||||
pub use crate::core::event::{Event, Status};
|
pub use crate::core::event::{Event, Status};
|
||||||
pub use iced_futures::event::{listen, listen_raw, listen_with};
|
pub use iced_futures::event::{listen, listen_raw, listen_with};
|
||||||
|
|
@ -683,8 +683,6 @@ pub use event::Event;
|
||||||
pub use executor::Executor;
|
pub use executor::Executor;
|
||||||
pub use font::Font;
|
pub use font::Font;
|
||||||
pub use renderer::Renderer;
|
pub use renderer::Renderer;
|
||||||
#[cfg(feature = "canvas")]
|
|
||||||
pub use renderer::graphics::geometry;
|
|
||||||
pub use task::Task;
|
pub use task::Task;
|
||||||
pub use window::Window;
|
pub use window::Window;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -273,11 +273,6 @@ impl<P: Program + 'static> Emulator<P> {
|
||||||
runtime::Action::Reload => {
|
runtime::Action::Reload => {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
runtime::Action::Dnd(_)
|
|
||||||
| runtime::Action::PlatformSpecific(_) => {
|
|
||||||
// Drag and drop and platform surfaces need a
|
|
||||||
// windowing system, which an emulator has none of.
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -479,9 +474,7 @@ impl<P: Program + 'static> Emulator<P> {
|
||||||
&mut self.renderer,
|
&mut self.renderer,
|
||||||
theme,
|
theme,
|
||||||
&renderer::Style {
|
&renderer::Style {
|
||||||
icon_color: style.text_color,
|
|
||||||
text_color: style.text_color,
|
text_color: style.text_color,
|
||||||
scale_factor: f64::from(scale_factor),
|
|
||||||
},
|
},
|
||||||
mouse::Cursor::Unavailable,
|
mouse::Cursor::Unavailable,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -209,19 +209,17 @@ where
|
||||||
&mut self.messages,
|
&mut self.messages,
|
||||||
);
|
);
|
||||||
|
|
||||||
let scale_factor = 2.0_f32;
|
|
||||||
|
|
||||||
self.raw.draw(
|
self.raw.draw(
|
||||||
&mut self.renderer,
|
&mut self.renderer,
|
||||||
theme,
|
theme,
|
||||||
&core::renderer::Style {
|
&core::renderer::Style {
|
||||||
icon_color: base.text_color,
|
|
||||||
text_color: base.text_color,
|
text_color: base.text_color,
|
||||||
scale_factor: f64::from(scale_factor),
|
|
||||||
},
|
},
|
||||||
self.cursor,
|
self.cursor,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let scale_factor = 2.0;
|
||||||
|
|
||||||
let physical_size = Size::new(
|
let physical_size = Size::new(
|
||||||
(self.size.width * scale_factor).round() as u32,
|
(self.size.width * scale_factor).round() as u32,
|
||||||
(self.size.height * scale_factor).round() as u32,
|
(self.size.height * scale_factor).round() as u32,
|
||||||
|
|
|
||||||
|
|
@ -775,7 +775,6 @@ impl<P: Program + 'static> Tester<P> {
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
..text::Style::default()
|
|
||||||
})
|
})
|
||||||
.into()
|
.into()
|
||||||
},
|
},
|
||||||
|
|
@ -946,7 +945,6 @@ where
|
||||||
text(label).size(14).style(|theme: &core::Theme| {
|
text(label).size(14).style(|theme: &core::Theme| {
|
||||||
text::Style {
|
text::Style {
|
||||||
color: Some(theme.extended_palette().background.weak.text),
|
color: Some(theme.extended_palette().background.weak.text),
|
||||||
..text::Style::default()
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
space::horizontal(),
|
space::horizontal(),
|
||||||
|
|
|
||||||
|
|
@ -263,10 +263,6 @@ impl Engine {
|
||||||
} else {
|
} else {
|
||||||
// Draw corners that have too small border radii as having no border radius,
|
// Draw corners that have too small border radii as having no border radius,
|
||||||
// but mask them with the rounded rectangle with the correct border radius.
|
// but mask them with the rounded rectangle with the correct border radius.
|
||||||
if path_bounds.width < 1.0 || path_bounds.height < 1.0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut temp_pixmap = tiny_skia::Pixmap::new(
|
let mut temp_pixmap = tiny_skia::Pixmap::new(
|
||||||
path_bounds.width as u32,
|
path_bounds.width as u32,
|
||||||
path_bounds.height as u32,
|
path_bounds.height as u32,
|
||||||
|
|
|
||||||
|
|
@ -51,21 +51,7 @@ impl Pipeline {
|
||||||
) {
|
) {
|
||||||
let mut cache = self.cache.borrow_mut();
|
let mut cache = self.cache.borrow_mut();
|
||||||
|
|
||||||
let target = {
|
let Ok(mut image) = cache.allocate(handle) else {
|
||||||
let Ok(image) = cache.allocate(handle) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
graphics::image::downsample_target(
|
|
||||||
Size::new(image.width(), image.height()),
|
|
||||||
bounds.size(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let Ok(mut image) = (match target {
|
|
||||||
Some(target) => cache.allocate_resampled(handle, target),
|
|
||||||
None => cache.allocate(handle),
|
|
||||||
}) else {
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -124,8 +110,6 @@ impl Pipeline {
|
||||||
struct Cache {
|
struct Cache {
|
||||||
entries: FxHashMap<raster::Id, Option<Entry>>,
|
entries: FxHashMap<raster::Id, Option<Entry>>,
|
||||||
hits: FxHashSet<raster::Id>,
|
hits: FxHashSet<raster::Id>,
|
||||||
resampled: FxHashMap<(raster::Id, u32, u32), Entry>,
|
|
||||||
resampled_hits: FxHashSet<(raster::Id, u32, u32)>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cache {
|
impl Cache {
|
||||||
|
|
@ -182,59 +166,9 @@ impl Cache {
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Like [`Self::allocate`], resampled to `target`. Call after
|
|
||||||
/// [`Self::allocate`] has decoded the image.
|
|
||||||
pub fn allocate_resampled(
|
|
||||||
&mut self,
|
|
||||||
handle: &raster::Handle,
|
|
||||||
target: Size<u32>,
|
|
||||||
) -> Result<tiny_skia::PixmapRef<'_>, raster::Error> {
|
|
||||||
let key = (handle.id(), target.width, target.height);
|
|
||||||
|
|
||||||
if !self.resampled.contains_key(&key) {
|
|
||||||
let native = self
|
|
||||||
.entries
|
|
||||||
.get(&handle.id())
|
|
||||||
.and_then(Option::as_ref)
|
|
||||||
.ok_or(raster::Error::Empty)?;
|
|
||||||
|
|
||||||
// Stored pixels are already premultiplied, so resample them as is.
|
|
||||||
let pixels = graphics::image::downsample_premultiplied(
|
|
||||||
bytemuck::cast_slice(&native.pixels),
|
|
||||||
Size::new(native.width, native.height),
|
|
||||||
target,
|
|
||||||
);
|
|
||||||
|
|
||||||
let _ = self.resampled.insert(
|
|
||||||
key,
|
|
||||||
Entry {
|
|
||||||
width: target.width,
|
|
||||||
height: target.height,
|
|
||||||
pixels: pixels
|
|
||||||
.chunks_exact(4)
|
|
||||||
.map(|p| u32::from_ne_bytes([p[0], p[1], p[2], p[3]]))
|
|
||||||
.collect(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let _ = self.resampled_hits.insert(key);
|
|
||||||
let entry = &self.resampled[&key];
|
|
||||||
|
|
||||||
Ok(tiny_skia::PixmapRef::from_bytes(
|
|
||||||
bytemuck::cast_slice(&entry.pixels),
|
|
||||||
entry.width,
|
|
||||||
entry.height,
|
|
||||||
)
|
|
||||||
.expect("Build pixmap from image bytes"))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn trim(&mut self) {
|
fn trim(&mut self) {
|
||||||
self.entries.retain(|key, _| self.hits.contains(key));
|
self.entries.retain(|key, _| self.hits.contains(key));
|
||||||
self.resampled
|
|
||||||
.retain(|key, _| self.resampled_hits.contains(key));
|
|
||||||
self.hits.clear();
|
self.hits.clear();
|
||||||
self.resampled_hits.clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::core::svg::{Data, Handle};
|
use crate::core::svg::{Data, Handle};
|
||||||
use crate::core::{Color, Rectangle, Size};
|
use crate::core::{Color, Rectangle, Size};
|
||||||
use crate::graphics::text;
|
|
||||||
|
|
||||||
use resvg::usvg;
|
use resvg::usvg;
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
|
|
@ -75,7 +74,6 @@ struct Cache {
|
||||||
rasters: FxHashMap<RasterKey, tiny_skia::Pixmap>,
|
rasters: FxHashMap<RasterKey, tiny_skia::Pixmap>,
|
||||||
raster_hits: FxHashSet<RasterKey>,
|
raster_hits: FxHashSet<RasterKey>,
|
||||||
fontdb: Option<Arc<usvg::fontdb::Database>>,
|
fontdb: Option<Arc<usvg::fontdb::Database>>,
|
||||||
fontdb_version: Option<text::Version>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
|
@ -89,15 +87,12 @@ impl Cache {
|
||||||
fn load(&mut self, handle: &Handle) -> Option<&usvg::Tree> {
|
fn load(&mut self, handle: &Handle) -> Option<&usvg::Tree> {
|
||||||
let id = handle.id();
|
let id = handle.id();
|
||||||
|
|
||||||
// Reuse the font database already loaded by the `cosmic-text` font
|
// TODO: Reuse `cosmic-text` font database
|
||||||
// system. We clone it once and refresh only when a font is
|
if self.fontdb.is_none() {
|
||||||
// loaded (i.e. when the font system's version changes).
|
let mut fontdb = usvg::fontdb::Database::new();
|
||||||
let font_system = text::font_system().read().expect("Read font system");
|
fontdb.load_system_fonts();
|
||||||
let version = font_system.version();
|
|
||||||
|
|
||||||
if self.fontdb_version != Some(version) {
|
self.fontdb = Some(Arc::new(fontdb));
|
||||||
self.fontdb = Some(Arc::new(font_system.db().clone()));
|
|
||||||
self.fontdb_version = Some(version);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let options = usvg::Options {
|
let options = usvg::Options {
|
||||||
|
|
|
||||||
|
|
@ -25,12 +25,6 @@ svg = ["iced_graphics/svg", "resvg/text"]
|
||||||
web-colors = ["iced_graphics/web-colors"]
|
web-colors = ["iced_graphics/web-colors"]
|
||||||
webgl = ["wgpu/webgl"]
|
webgl = ["wgpu/webgl"]
|
||||||
strict-assertions = []
|
strict-assertions = []
|
||||||
# Yoda: opt-in X11 backend. Pulls tiny-xlib/as-raw-xcb-connection/x11rb
|
|
||||||
# (which otherwise were unconditional direct deps on unix). Wayland-only
|
|
||||||
# builds just leave this off — window/x11.rs and the DRI3 device-id
|
|
||||||
# fallback in window/compositor.rs are cfg-gated on it.
|
|
||||||
x11 = ["dep:as-raw-xcb-connection", "dep:tiny-xlib", "dep:x11rb"]
|
|
||||||
wayland = ["cctk"]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
iced_debug.workspace = true
|
iced_debug.workspace = true
|
||||||
|
|
@ -63,14 +57,11 @@ wayland-protocols.workspace = true
|
||||||
wayland-backend = { version = "0.3.3", features = ["client_system"] }
|
wayland-backend = { version = "0.3.3", features = ["client_system"] }
|
||||||
wayland-client = { version = "0.31.2" }
|
wayland-client = { version = "0.31.2" }
|
||||||
wayland-sys = { version = "0.31.1", features = ["dlopen"] }
|
wayland-sys = { version = "0.31.1", features = ["dlopen"] }
|
||||||
as-raw-xcb-connection = { version = "1.0.1", optional = true }
|
as-raw-xcb-connection = "1.0.1"
|
||||||
tiny-xlib = { version = "0.2.3", optional = true }
|
tiny-xlib = "0.2.3"
|
||||||
x11rb = { version = "0.13.1", optional = true, features = [
|
x11rb = { version = "0.13.1", features = [
|
||||||
"allow-unsafe-code",
|
"allow-unsafe-code",
|
||||||
"dl-libxcb",
|
"dl-libxcb",
|
||||||
"dri3",
|
"dri3",
|
||||||
"randr",
|
"randr",
|
||||||
] }
|
] }
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
build_helpers = { path = "../build_helpers" }
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
fn main() {
|
|
||||||
build_helpers::cfg_aliases_setup();
|
|
||||||
}
|
|
||||||
|
|
@ -63,6 +63,7 @@ impl<T: bytemuck::Pod> Buffer<T> {
|
||||||
/// Returns the size of the written bytes.
|
/// Returns the size of the written bytes.
|
||||||
pub fn write(
|
pub fn write(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
device: &wgpu::Device,
|
||||||
encoder: &mut wgpu::CommandEncoder,
|
encoder: &mut wgpu::CommandEncoder,
|
||||||
belt: &mut wgpu::util::StagingBelt,
|
belt: &mut wgpu::util::StagingBelt,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
|
|
@ -78,6 +79,7 @@ impl<T: bytemuck::Pod> Buffer<T> {
|
||||||
&self.raw,
|
&self.raw,
|
||||||
(offset + bytes_written) as u64,
|
(offset + bytes_written) as u64,
|
||||||
MAX_WRITE_SIZE_U64,
|
MAX_WRITE_SIZE_U64,
|
||||||
|
device,
|
||||||
)
|
)
|
||||||
.copy_from_slice(
|
.copy_from_slice(
|
||||||
&bytes[bytes_written..bytes_written + MAX_WRITE_SIZE],
|
&bytes[bytes_written..bytes_written + MAX_WRITE_SIZE],
|
||||||
|
|
@ -98,6 +100,7 @@ impl<T: bytemuck::Pod> Buffer<T> {
|
||||||
&self.raw,
|
&self.raw,
|
||||||
(offset + bytes_written) as u64,
|
(offset + bytes_written) as u64,
|
||||||
bytes_left,
|
bytes_left,
|
||||||
|
device,
|
||||||
)
|
)
|
||||||
.copy_from_slice(&bytes[bytes_written..]);
|
.copy_from_slice(&bytes[bytes_written..]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ pub fn convert(
|
||||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
label: Some("iced_wgpu.offscreen.blit.pipeline_layout"),
|
label: Some("iced_wgpu.offscreen.blit.pipeline_layout"),
|
||||||
bind_group_layouts: &[&constant_layout, &texture_layout],
|
bind_group_layouts: &[&constant_layout, &texture_layout],
|
||||||
immediate_size: 0,
|
push_constant_ranges: &[],
|
||||||
});
|
});
|
||||||
|
|
||||||
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||||
|
|
@ -149,7 +149,7 @@ pub fn convert(
|
||||||
},
|
},
|
||||||
depth_stencil: None,
|
depth_stencil: None,
|
||||||
multisample: wgpu::MultisampleState::default(),
|
multisample: wgpu::MultisampleState::default(),
|
||||||
multiview_mask: None,
|
multiview: None,
|
||||||
cache: None,
|
cache: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -194,7 +194,6 @@ pub fn convert(
|
||||||
depth_stencil_attachment: None,
|
depth_stencil_attachment: None,
|
||||||
timestamp_writes: None,
|
timestamp_writes: None,
|
||||||
occlusion_query_set: None,
|
occlusion_query_set: None,
|
||||||
multiview_mask: None,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
pass.set_pipeline(&pipeline);
|
pass.set_pipeline(&pipeline);
|
||||||
|
|
|
||||||
|
|
@ -132,7 +132,7 @@ impl Atlas {
|
||||||
match &entry {
|
match &entry {
|
||||||
Entry::Contiguous(allocation) => {
|
Entry::Contiguous(allocation) => {
|
||||||
self.upload_allocation(
|
self.upload_allocation(
|
||||||
pixels, width, 0, allocation, encoder, belt,
|
pixels, width, 0, allocation, device, encoder, belt,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Entry::Fragmented { fragments, .. } => {
|
Entry::Fragmented { fragments, .. } => {
|
||||||
|
|
@ -145,6 +145,7 @@ impl Atlas {
|
||||||
width,
|
width,
|
||||||
offset,
|
offset,
|
||||||
&fragment.allocation,
|
&fragment.allocation,
|
||||||
|
device,
|
||||||
encoder,
|
encoder,
|
||||||
belt,
|
belt,
|
||||||
);
|
);
|
||||||
|
|
@ -311,6 +312,7 @@ impl Atlas {
|
||||||
image_width: u32,
|
image_width: u32,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
allocation: &Allocation,
|
allocation: &Allocation,
|
||||||
|
device: &wgpu::Device,
|
||||||
encoder: &mut wgpu::CommandEncoder,
|
encoder: &mut wgpu::CommandEncoder,
|
||||||
belt: &mut wgpu::util::StagingBelt,
|
belt: &mut wgpu::util::StagingBelt,
|
||||||
) {
|
) {
|
||||||
|
|
@ -332,6 +334,7 @@ impl Atlas {
|
||||||
let buffer_slice = belt.allocate(
|
let buffer_slice = belt.allocate(
|
||||||
wgpu::BufferSize::new(total_bytes as u64).unwrap(),
|
wgpu::BufferSize::new(total_bytes as u64).unwrap(),
|
||||||
wgpu::BufferSize::new(8 * 4).unwrap(),
|
wgpu::BufferSize::new(8 * 4).unwrap(),
|
||||||
|
device,
|
||||||
);
|
);
|
||||||
|
|
||||||
const PIXEL: usize = 4;
|
const PIXEL: usize = 4;
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,6 @@ use crate::image::atlas::{self, Atlas};
|
||||||
#[cfg(all(feature = "image", not(target_arch = "wasm32")))]
|
#[cfg(all(feature = "image", not(target_arch = "wasm32")))]
|
||||||
use worker::Worker;
|
use worker::Worker;
|
||||||
|
|
||||||
#[cfg(feature = "image")]
|
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
|
||||||
#[cfg(feature = "image")]
|
#[cfg(feature = "image")]
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
|
@ -40,13 +38,7 @@ impl Cache {
|
||||||
raster: Raster {
|
raster: Raster {
|
||||||
cache: crate::image::raster::Cache::default(),
|
cache: crate::image::raster::Cache::default(),
|
||||||
pending: HashMap::new(),
|
pending: HashMap::new(),
|
||||||
resampled: FxHashMap::default(),
|
belt: wgpu::util::StagingBelt::new(2 * 1024 * 1024),
|
||||||
resampled_hits: FxHashSet::default(),
|
|
||||||
should_trim: false,
|
|
||||||
belt: wgpu::util::StagingBelt::new(
|
|
||||||
device.clone(),
|
|
||||||
2 * 1024 * 1024,
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
#[cfg(feature = "svg")]
|
#[cfg(feature = "svg")]
|
||||||
vector: crate::image::vector::Cache::default(),
|
vector: crate::image::vector::Cache::default(),
|
||||||
|
|
@ -214,9 +206,7 @@ impl Cache {
|
||||||
encoder: &mut wgpu::CommandEncoder,
|
encoder: &mut wgpu::CommandEncoder,
|
||||||
belt: &mut wgpu::util::StagingBelt,
|
belt: &mut wgpu::util::StagingBelt,
|
||||||
handle: &core::image::Handle,
|
handle: &core::image::Handle,
|
||||||
bounds: Size<f32>,
|
|
||||||
) -> Option<(&atlas::Entry, &Arc<wgpu::BindGroup>)> {
|
) -> Option<(&atlas::Entry, &Arc<wgpu::BindGroup>)> {
|
||||||
use crate::graphics::image::{downsample, downsample_target, load};
|
|
||||||
use crate::image::raster::Memory;
|
use crate::image::raster::Memory;
|
||||||
|
|
||||||
self.receive();
|
self.receive();
|
||||||
|
|
@ -230,34 +220,6 @@ impl Cache {
|
||||||
None,
|
None,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if let Some(target) = downsample_target(memory.dimensions(), bounds) {
|
|
||||||
let key = (handle.id(), target.width, target.height);
|
|
||||||
|
|
||||||
if !self.raster.resampled.contains_key(&key) {
|
|
||||||
let image = memory.host().or_else(|| load(handle).ok())?;
|
|
||||||
let image = downsample(&image, target);
|
|
||||||
|
|
||||||
let entry = self.atlas.upload(
|
|
||||||
device,
|
|
||||||
encoder,
|
|
||||||
belt,
|
|
||||||
target.width,
|
|
||||||
target.height,
|
|
||||||
&image,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let _ = self.raster.resampled.insert(key, entry);
|
|
||||||
self.raster.should_trim = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let _ = self.raster.resampled_hits.insert(key);
|
|
||||||
|
|
||||||
return Some((
|
|
||||||
self.raster.resampled.get(&key)?,
|
|
||||||
self.atlas.bind_group(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Memory::Device {
|
if let Memory::Device {
|
||||||
entry, bind_group, ..
|
entry, bind_group, ..
|
||||||
} = memory
|
} = memory
|
||||||
|
|
@ -338,23 +300,6 @@ impl Cache {
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
self.worker.drop(_bind_group);
|
self.worker.drop(_bind_group);
|
||||||
});
|
});
|
||||||
|
|
||||||
if self.raster.should_trim {
|
|
||||||
let hits = &self.raster.resampled_hits;
|
|
||||||
let atlas = &mut self.atlas;
|
|
||||||
|
|
||||||
self.raster.resampled.retain(|key, entry| {
|
|
||||||
let retain = hits.contains(key);
|
|
||||||
|
|
||||||
if !retain {
|
|
||||||
atlas.remove(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
retain
|
|
||||||
});
|
|
||||||
self.raster.resampled_hits.clear();
|
|
||||||
self.raster.should_trim = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "svg")]
|
#[cfg(feature = "svg")]
|
||||||
|
|
@ -428,9 +373,6 @@ impl Drop for Cache {
|
||||||
struct Raster {
|
struct Raster {
|
||||||
cache: crate::image::raster::Cache,
|
cache: crate::image::raster::Cache,
|
||||||
pending: HashMap<core::image::Id, Vec<Callback>>,
|
pending: HashMap<core::image::Id, Vec<Callback>>,
|
||||||
resampled: FxHashMap<(core::image::Id, u32, u32), atlas::Entry>,
|
|
||||||
resampled_hits: FxHashSet<(core::image::Id, u32, u32)>,
|
|
||||||
should_trim: bool,
|
|
||||||
belt: wgpu::util::StagingBelt,
|
belt: wgpu::util::StagingBelt,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -509,10 +451,7 @@ mod worker {
|
||||||
backend,
|
backend,
|
||||||
texture_layout,
|
texture_layout,
|
||||||
shell: shell.clone(),
|
shell: shell.clone(),
|
||||||
belt: wgpu::util::StagingBelt::new(
|
belt: wgpu::util::StagingBelt::new(4 * 1024 * 1024),
|
||||||
device.clone(),
|
|
||||||
4 * 1024 * 1024,
|
|
||||||
),
|
|
||||||
jobs: jobs_receiver,
|
jobs: jobs_receiver,
|
||||||
output: work_sender,
|
output: work_sender,
|
||||||
quit: quit_receiver,
|
quit: quit_receiver,
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ impl Pipeline {
|
||||||
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
||||||
min_filter: wgpu::FilterMode::Nearest,
|
min_filter: wgpu::FilterMode::Nearest,
|
||||||
mag_filter: wgpu::FilterMode::Nearest,
|
mag_filter: wgpu::FilterMode::Nearest,
|
||||||
mipmap_filter: wgpu::MipmapFilterMode::Nearest,
|
mipmap_filter: wgpu::FilterMode::Nearest,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -55,7 +55,7 @@ impl Pipeline {
|
||||||
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
||||||
min_filter: wgpu::FilterMode::Linear,
|
min_filter: wgpu::FilterMode::Linear,
|
||||||
mag_filter: wgpu::FilterMode::Linear,
|
mag_filter: wgpu::FilterMode::Linear,
|
||||||
mipmap_filter: wgpu::MipmapFilterMode::Linear,
|
mipmap_filter: wgpu::FilterMode::Linear,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -106,8 +106,8 @@ impl Pipeline {
|
||||||
let layout =
|
let layout =
|
||||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
label: Some("iced_wgpu::image pipeline layout"),
|
label: Some("iced_wgpu::image pipeline layout"),
|
||||||
|
push_constant_ranges: &[],
|
||||||
bind_group_layouts: &[&constant_layout, &texture_layout],
|
bind_group_layouts: &[&constant_layout, &texture_layout],
|
||||||
immediate_size: 0,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let shader =
|
let shader =
|
||||||
|
|
@ -191,7 +191,7 @@ impl Pipeline {
|
||||||
mask: !0,
|
mask: !0,
|
||||||
alpha_to_coverage_enabled: false,
|
alpha_to_coverage_enabled: false,
|
||||||
},
|
},
|
||||||
multiview_mask: None,
|
multiview: None,
|
||||||
cache: None,
|
cache: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -267,13 +267,7 @@ impl State {
|
||||||
clip_bounds,
|
clip_bounds,
|
||||||
} => {
|
} => {
|
||||||
if let Some((atlas_entry, bind_group)) = cache
|
if let Some((atlas_entry, bind_group)) = cache
|
||||||
.upload_raster(
|
.upload_raster(device, encoder, belt, &image.handle)
|
||||||
device,
|
|
||||||
encoder,
|
|
||||||
belt,
|
|
||||||
&image.handle,
|
|
||||||
bounds.size() * scale,
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
match atlas.as_mut() {
|
match atlas.as_mut() {
|
||||||
None => {
|
None => {
|
||||||
|
|
@ -534,6 +528,7 @@ impl Layer {
|
||||||
&self.uniforms,
|
&self.uniforms,
|
||||||
0,
|
0,
|
||||||
(bytes.len() as u64).try_into().expect("Sized uniforms"),
|
(bytes.len() as u64).try_into().expect("Sized uniforms"),
|
||||||
|
device,
|
||||||
)
|
)
|
||||||
.copy_from_slice(bytes);
|
.copy_from_slice(bytes);
|
||||||
|
|
||||||
|
|
@ -544,11 +539,11 @@ impl Layer {
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
|
|
||||||
if !nearest.is_empty() {
|
if !nearest.is_empty() {
|
||||||
offset += self.instances.write(encoder, belt, 0, nearest);
|
offset += self.instances.write(device, encoder, belt, 0, nearest);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !linear.is_empty() {
|
if !linear.is_empty() {
|
||||||
let _ = self.instances.write(encoder, belt, offset, linear);
|
let _ = self.instances.write(device, encoder, belt, offset, linear);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,6 @@ impl Renderer {
|
||||||
// It would be great if the `StagingBelt` API exposed methods
|
// It would be great if the `StagingBelt` API exposed methods
|
||||||
// for introspection to detect when a resize may be worth it.
|
// for introspection to detect when a resize may be worth it.
|
||||||
staging_belt: wgpu::util::StagingBelt::new(
|
staging_belt: wgpu::util::StagingBelt::new(
|
||||||
engine.device.clone(),
|
|
||||||
buffer::MAX_WRITE_SIZE as u64,
|
buffer::MAX_WRITE_SIZE as u64,
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|
@ -455,7 +454,6 @@ impl Renderer {
|
||||||
depth_stencil_attachment: None,
|
depth_stencil_attachment: None,
|
||||||
timestamp_writes: None,
|
timestamp_writes: None,
|
||||||
occlusion_query_set: None,
|
occlusion_query_set: None,
|
||||||
multiview_mask: None,
|
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
@ -530,7 +528,6 @@ impl Renderer {
|
||||||
depth_stencil_attachment: None,
|
depth_stencil_attachment: None,
|
||||||
timestamp_writes: None,
|
timestamp_writes: None,
|
||||||
occlusion_query_set: None,
|
occlusion_query_set: None,
|
||||||
multiview_mask: None,
|
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
@ -624,7 +621,6 @@ impl Renderer {
|
||||||
depth_stencil_attachment: None,
|
depth_stencil_attachment: None,
|
||||||
timestamp_writes: None,
|
timestamp_writes: None,
|
||||||
occlusion_query_set: None,
|
occlusion_query_set: None,
|
||||||
multiview_mask: None,
|
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -209,7 +209,7 @@ impl Layer {
|
||||||
transformation: Transformation,
|
transformation: Transformation,
|
||||||
scale: f32,
|
scale: f32,
|
||||||
) {
|
) {
|
||||||
self.update(encoder, belt, transformation, scale);
|
self.update(device, encoder, belt, transformation, scale);
|
||||||
|
|
||||||
if !quads.solids.is_empty() {
|
if !quads.solids.is_empty() {
|
||||||
self.solid.prepare(device, encoder, belt, &quads.solids);
|
self.solid.prepare(device, encoder, belt, &quads.solids);
|
||||||
|
|
@ -223,6 +223,7 @@ impl Layer {
|
||||||
|
|
||||||
pub fn update(
|
pub fn update(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
device: &wgpu::Device,
|
||||||
encoder: &mut wgpu::CommandEncoder,
|
encoder: &mut wgpu::CommandEncoder,
|
||||||
belt: &mut wgpu::util::StagingBelt,
|
belt: &mut wgpu::util::StagingBelt,
|
||||||
transformation: Transformation,
|
transformation: Transformation,
|
||||||
|
|
@ -236,6 +237,7 @@ impl Layer {
|
||||||
&self.constants_buffer,
|
&self.constants_buffer,
|
||||||
0,
|
0,
|
||||||
(bytes.len() as u64).try_into().expect("Sized uniforms"),
|
(bytes.len() as u64).try_into().expect("Sized uniforms"),
|
||||||
|
device,
|
||||||
)
|
)
|
||||||
.copy_from_slice(bytes);
|
.copy_from_slice(bytes);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ impl Layer {
|
||||||
instances: &[Gradient],
|
instances: &[Gradient],
|
||||||
) {
|
) {
|
||||||
let _ = self.instances.resize(device, instances.len());
|
let _ = self.instances.resize(device, instances.len());
|
||||||
let _ = self.instances.write(encoder, belt, 0, instances);
|
let _ = self.instances.write(device, encoder, belt, 0, instances);
|
||||||
|
|
||||||
self.instance_count = instances.len();
|
self.instance_count = instances.len();
|
||||||
}
|
}
|
||||||
|
|
@ -75,8 +75,8 @@ impl Pipeline {
|
||||||
let layout = device.create_pipeline_layout(
|
let layout = device.create_pipeline_layout(
|
||||||
&wgpu::PipelineLayoutDescriptor {
|
&wgpu::PipelineLayoutDescriptor {
|
||||||
label: Some("iced_wgpu.quad.gradient.pipeline"),
|
label: Some("iced_wgpu.quad.gradient.pipeline"),
|
||||||
|
push_constant_ranges: &[],
|
||||||
bind_group_layouts: &[constants_layout],
|
bind_group_layouts: &[constants_layout],
|
||||||
immediate_size: 0,
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -155,7 +155,7 @@ impl Pipeline {
|
||||||
mask: !0,
|
mask: !0,
|
||||||
alpha_to_coverage_enabled: false,
|
alpha_to_coverage_enabled: false,
|
||||||
},
|
},
|
||||||
multiview_mask: None,
|
multiview: None,
|
||||||
cache: None,
|
cache: None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ impl Layer {
|
||||||
instances: &[Solid],
|
instances: &[Solid],
|
||||||
) {
|
) {
|
||||||
let _ = self.instances.resize(device, instances.len());
|
let _ = self.instances.resize(device, instances.len());
|
||||||
let _ = self.instances.write(encoder, belt, 0, instances);
|
let _ = self.instances.write(device, encoder, belt, 0, instances);
|
||||||
|
|
||||||
self.instance_count = instances.len();
|
self.instance_count = instances.len();
|
||||||
}
|
}
|
||||||
|
|
@ -65,8 +65,8 @@ impl Pipeline {
|
||||||
let layout =
|
let layout =
|
||||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
label: Some("iced_wgpu.quad.solid.pipeline"),
|
label: Some("iced_wgpu.quad.solid.pipeline"),
|
||||||
|
push_constant_ranges: &[],
|
||||||
bind_group_layouts: &[constants_layout],
|
bind_group_layouts: &[constants_layout],
|
||||||
immediate_size: 0,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let shader =
|
let shader =
|
||||||
|
|
@ -139,7 +139,7 @@ impl Pipeline {
|
||||||
mask: !0,
|
mask: !0,
|
||||||
alpha_to_coverage_enabled: false,
|
alpha_to_coverage_enabled: false,
|
||||||
},
|
},
|
||||||
multiview_mask: None,
|
multiview: None,
|
||||||
cache: None,
|
cache: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -619,7 +619,7 @@ fn prepare(
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Some(cryoglyph::TextArea {
|
Some(cryoglyph::TextArea {
|
||||||
text: buffer.layout_runs(),
|
buffer,
|
||||||
left: position.x,
|
left: position.x,
|
||||||
top: position.y,
|
top: position.y,
|
||||||
scale: transformation.scale_factor()
|
scale: transformation.scale_factor()
|
||||||
|
|
|
||||||
|
|
@ -313,7 +313,6 @@ fn render<'a>(
|
||||||
depth_stencil_attachment: None,
|
depth_stencil_attachment: None,
|
||||||
timestamp_writes: None,
|
timestamp_writes: None,
|
||||||
occlusion_query_set: None,
|
occlusion_query_set: None,
|
||||||
multiview_mask: None,
|
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -426,13 +425,18 @@ impl Layer {
|
||||||
|
|
||||||
let indices = mesh.indices();
|
let indices = mesh.indices();
|
||||||
|
|
||||||
index_offset +=
|
index_offset += self.index_buffer.write(
|
||||||
self.index_buffer
|
device,
|
||||||
.write(encoder, belt, index_offset, indices);
|
encoder,
|
||||||
|
belt,
|
||||||
|
index_offset,
|
||||||
|
indices,
|
||||||
|
);
|
||||||
|
|
||||||
match mesh {
|
match mesh {
|
||||||
Mesh::Solid { buffers, .. } => {
|
Mesh::Solid { buffers, .. } => {
|
||||||
solid_vertex_offset += self.solid.vertices.write(
|
solid_vertex_offset += self.solid.vertices.write(
|
||||||
|
device,
|
||||||
encoder,
|
encoder,
|
||||||
belt,
|
belt,
|
||||||
solid_vertex_offset,
|
solid_vertex_offset,
|
||||||
|
|
@ -440,14 +444,16 @@ impl Layer {
|
||||||
);
|
);
|
||||||
|
|
||||||
solid_uniform_offset += self.solid.uniforms.write(
|
solid_uniform_offset += self.solid.uniforms.write(
|
||||||
|
device,
|
||||||
encoder,
|
encoder,
|
||||||
belt,
|
belt,
|
||||||
solid_uniform_offset,
|
solid_uniform_offset,
|
||||||
&[uniforms],
|
&[uniforms],
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
Mesh::Gradient { buffers, .. } => {
|
Mesh::Gradient { buffers, .. } => {
|
||||||
gradient_vertex_offset += self.gradient.vertices.write(
|
gradient_vertex_offset += self.gradient.vertices.write(
|
||||||
|
device,
|
||||||
encoder,
|
encoder,
|
||||||
belt,
|
belt,
|
||||||
gradient_vertex_offset,
|
gradient_vertex_offset,
|
||||||
|
|
@ -455,6 +461,7 @@ impl Layer {
|
||||||
);
|
);
|
||||||
|
|
||||||
gradient_uniform_offset += self.gradient.uniforms.write(
|
gradient_uniform_offset += self.gradient.uniforms.write(
|
||||||
|
device,
|
||||||
encoder,
|
encoder,
|
||||||
belt,
|
belt,
|
||||||
gradient_uniform_offset,
|
gradient_uniform_offset,
|
||||||
|
|
@ -724,7 +731,7 @@ mod solid {
|
||||||
&wgpu::PipelineLayoutDescriptor {
|
&wgpu::PipelineLayoutDescriptor {
|
||||||
label: Some("iced_wgpu.triangle.solid.pipeline_layout"),
|
label: Some("iced_wgpu.triangle.solid.pipeline_layout"),
|
||||||
bind_group_layouts: &[&constants_layout],
|
bind_group_layouts: &[&constants_layout],
|
||||||
immediate_size: 0,
|
push_constant_ranges: &[],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -777,7 +784,7 @@ mod solid {
|
||||||
primitive: triangle::primitive_state(),
|
primitive: triangle::primitive_state(),
|
||||||
depth_stencil: None,
|
depth_stencil: None,
|
||||||
multisample: triangle::multisample_state(antialiasing),
|
multisample: triangle::multisample_state(antialiasing),
|
||||||
multiview_mask: None,
|
multiview: None,
|
||||||
cache: None,
|
cache: None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
@ -879,7 +886,7 @@ mod gradient {
|
||||||
&wgpu::PipelineLayoutDescriptor {
|
&wgpu::PipelineLayoutDescriptor {
|
||||||
label: Some("iced_wgpu.triangle.gradient.pipeline_layout"),
|
label: Some("iced_wgpu.triangle.gradient.pipeline_layout"),
|
||||||
bind_group_layouts: &[&constants_layout],
|
bind_group_layouts: &[&constants_layout],
|
||||||
immediate_size: 0,
|
push_constant_ranges: &[],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -942,7 +949,7 @@ mod gradient {
|
||||||
primitive: triangle::primitive_state(),
|
primitive: triangle::primitive_state(),
|
||||||
depth_stencil: None,
|
depth_stencil: None,
|
||||||
multisample: triangle::multisample_state(antialiasing),
|
multisample: triangle::multisample_state(antialiasing),
|
||||||
multiview_mask: None,
|
multiview: None,
|
||||||
cache: None,
|
cache: None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -69,8 +69,8 @@ impl Pipeline {
|
||||||
let layout =
|
let layout =
|
||||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
label: Some("iced_wgpu::triangle::msaa pipeline layout"),
|
label: Some("iced_wgpu::triangle::msaa pipeline layout"),
|
||||||
|
push_constant_ranges: &[],
|
||||||
bind_group_layouts: &[&constant_layout, &texture_layout],
|
bind_group_layouts: &[&constant_layout, &texture_layout],
|
||||||
immediate_size: 0,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let shader =
|
let shader =
|
||||||
|
|
@ -116,7 +116,7 @@ impl Pipeline {
|
||||||
mask: !0,
|
mask: !0,
|
||||||
alpha_to_coverage_enabled: false,
|
alpha_to_coverage_enabled: false,
|
||||||
},
|
},
|
||||||
multiview_mask: None,
|
multiview: None,
|
||||||
cache: None,
|
cache: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -177,7 +177,6 @@ impl Pipeline {
|
||||||
depth_stencil_attachment: None,
|
depth_stencil_attachment: None,
|
||||||
timestamp_writes: None,
|
timestamp_writes: None,
|
||||||
occlusion_query_set: None,
|
occlusion_query_set: None,
|
||||||
multiview_mask: None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -321,6 +320,7 @@ impl State {
|
||||||
0,
|
0,
|
||||||
NonZeroU64::new(std::mem::size_of::<Ratio>() as u64)
|
NonZeroU64::new(std::mem::size_of::<Ratio>() as u64)
|
||||||
.expect("non-empty ratio"),
|
.expect("non-empty ratio"),
|
||||||
|
device,
|
||||||
)
|
)
|
||||||
.copy_from_slice(bytemuck::bytes_of(&ratio));
|
.copy_from_slice(bytemuck::bytes_of(&ratio));
|
||||||
|
|
||||||
|
|
@ -351,7 +351,6 @@ impl State {
|
||||||
depth_stencil_attachment: None,
|
depth_stencil_attachment: None,
|
||||||
timestamp_writes: None,
|
timestamp_writes: None,
|
||||||
occlusion_query_set: None,
|
occlusion_query_set: None,
|
||||||
multiview_mask: None,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
render_pass.set_pipeline(&pipeline.raw);
|
render_pass.set_pipeline(&pipeline.raw);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,13 @@
|
||||||
//! Display rendering results on windows.
|
//! Display rendering results on windows.
|
||||||
pub mod compositor;
|
pub mod compositor;
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(all(
|
||||||
|
unix,
|
||||||
|
feature = "cctk",
|
||||||
|
not(target_os = "macos"),
|
||||||
|
not(target_os = "redox")
|
||||||
|
))]
|
||||||
mod wayland;
|
mod wayland;
|
||||||
#[cfg(all(unix, feature = "x11", not(target_os = "macos"), not(target_os = "redox")))]
|
#[cfg(all(unix, not(target_os = "macos"), not(target_os = "redox")))]
|
||||||
mod x11;
|
mod x11;
|
||||||
|
|
||||||
pub use compositor::Compositor;
|
pub use compositor::Compositor;
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,16 @@ use crate::graphics::{self, Shell, Viewport};
|
||||||
use crate::settings::{self, Settings};
|
use crate::settings::{self, Settings};
|
||||||
use crate::{Engine, Renderer};
|
use crate::{Engine, Renderer};
|
||||||
|
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(all(
|
||||||
|
unix,
|
||||||
|
feature = "cctk",
|
||||||
|
not(target_os = "macos"),
|
||||||
|
not(target_os = "redox")
|
||||||
|
))]
|
||||||
use super::wayland::get_wayland_device_ids;
|
use super::wayland::get_wayland_device_ids;
|
||||||
#[cfg(all(unix, feature = "x11", not(target_os = "macos"), not(target_os = "redox")))]
|
#[cfg(all(unix, not(target_os = "macos"), not(target_os = "redox")))]
|
||||||
use super::x11::get_x11_device_ids;
|
use super::x11::get_x11_device_ids;
|
||||||
|
use std::future::Future;
|
||||||
|
|
||||||
/// A window graphics backend for iced powered by `wgpu`.
|
/// A window graphics backend for iced powered by `wgpu`.
|
||||||
pub struct Compositor {
|
pub struct Compositor {
|
||||||
|
|
@ -59,12 +65,15 @@ impl Compositor {
|
||||||
compatible_window: Option<W>,
|
compatible_window: Option<W>,
|
||||||
shell: Shell,
|
shell: Shell,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(all(
|
||||||
|
unix,
|
||||||
|
feature = "cctk",
|
||||||
|
not(target_os = "macos"),
|
||||||
|
not(target_os = "redox")
|
||||||
|
))]
|
||||||
let ids = compatible_window.as_ref().and_then(|window| {
|
let ids = compatible_window.as_ref().and_then(|window| {
|
||||||
let w = get_wayland_device_ids(window);
|
get_wayland_device_ids(window)
|
||||||
#[cfg(feature = "x11")]
|
.or_else(|| get_x11_device_ids(window))
|
||||||
let w = w.or_else(|| get_x11_device_ids(window));
|
|
||||||
w
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// HACK:
|
// HACK:
|
||||||
|
|
@ -73,7 +82,12 @@ impl Compositor {
|
||||||
// 3. and the user didn't set an adapter name,
|
// 3. and the user didn't set an adapter name,
|
||||||
// 4. and the user didn't request the high power pref
|
// 4. and the user didn't request the high power pref
|
||||||
// => don't load the nvidia icd, as it might power on the gpu in hybrid setups causing severe delays
|
// => don't load the nvidia icd, as it might power on the gpu in hybrid setups causing severe delays
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(all(
|
||||||
|
unix,
|
||||||
|
feature = "cctk",
|
||||||
|
not(target_os = "macos"),
|
||||||
|
not(target_os = "redox")
|
||||||
|
))]
|
||||||
if !matches!(ids, Some((0x10de, _)))
|
if !matches!(ids, Some((0x10de, _)))
|
||||||
&& std::env::var_os("__NV_PRIME_RENDER_OFFLOAD")
|
&& std::env::var_os("__NV_PRIME_RENDER_OFFLOAD")
|
||||||
.is_none_or(|var| var == "0")
|
.is_none_or(|var| var == "0")
|
||||||
|
|
@ -112,7 +126,6 @@ impl Compositor {
|
||||||
// if log::max_level() >= log::LevelFilter::Info {
|
// if log::max_level() >= log::LevelFilter::Info {
|
||||||
// let available_adapters: Vec<_> = instance
|
// let available_adapters: Vec<_> = instance
|
||||||
// .enumerate_adapters(settings.backends)
|
// .enumerate_adapters(settings.backends)
|
||||||
// .await
|
|
||||||
// .iter()
|
// .iter()
|
||||||
// .map(wgpu::Adapter::get_info)
|
// .map(wgpu::Adapter::get_info)
|
||||||
// .collect();
|
// .collect();
|
||||||
|
|
@ -133,11 +146,15 @@ impl Compositor {
|
||||||
let mut adapter = None;
|
let mut adapter = None;
|
||||||
#[cfg_attr(not(unix), allow(dead_code))]
|
#[cfg_attr(not(unix), allow(dead_code))]
|
||||||
if std::env::var_os("WGPU_ADAPTER_NAME").is_none() {
|
if std::env::var_os("WGPU_ADAPTER_NAME").is_none() {
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(all(
|
||||||
|
unix,
|
||||||
|
feature = "cctk",
|
||||||
|
not(target_os = "macos"),
|
||||||
|
not(target_os = "redox")
|
||||||
|
))]
|
||||||
if let Some((vendor_id, device_id)) = ids {
|
if let Some((vendor_id, device_id)) = ids {
|
||||||
adapter = instance
|
adapter = instance
|
||||||
.enumerate_adapters(settings.backends)
|
.enumerate_adapters(settings.backends)
|
||||||
.await
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|adapter| {
|
.filter(|adapter| {
|
||||||
let info = adapter.get_info();
|
let info = adapter.get_info();
|
||||||
|
|
@ -155,7 +172,6 @@ impl Compositor {
|
||||||
} else if let Ok(name) = std::env::var("WGPU_ADAPTER_NAME") {
|
} else if let Ok(name) = std::env::var("WGPU_ADAPTER_NAME") {
|
||||||
adapter = instance
|
adapter = instance
|
||||||
.enumerate_adapters(settings.backends)
|
.enumerate_adapters(settings.backends)
|
||||||
.await
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|adapter| {
|
.filter(|adapter| {
|
||||||
let info = adapter.get_info();
|
let info = adapter.get_info();
|
||||||
|
|
@ -168,8 +184,6 @@ impl Compositor {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
adapter = instance.request_adapter(&adapter_options).await.ok();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let adapter = match adapter {
|
let adapter = match adapter {
|
||||||
|
|
@ -272,14 +286,6 @@ impl Compositor {
|
||||||
..limits
|
..limits
|
||||||
});
|
});
|
||||||
|
|
||||||
// Request SHADER_F16 only if the adapter supports it (e.g., not available in WebGL2)
|
|
||||||
let required_features =
|
|
||||||
if adapter.features().contains(wgpu::Features::SHADER_F16) {
|
|
||||||
wgpu::Features::SHADER_F16
|
|
||||||
} else {
|
|
||||||
wgpu::Features::empty()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut errors = Vec::new();
|
let mut errors = Vec::new();
|
||||||
|
|
||||||
for required_limits in limits {
|
for required_limits in limits {
|
||||||
|
|
@ -288,7 +294,7 @@ impl Compositor {
|
||||||
label: Some(
|
label: Some(
|
||||||
"iced_wgpu::window::compositor device descriptor",
|
"iced_wgpu::window::compositor device descriptor",
|
||||||
),
|
),
|
||||||
required_features,
|
required_features: wgpu::Features::empty(),
|
||||||
required_limits: required_limits.clone(),
|
required_limits: required_limits.clone(),
|
||||||
memory_hints: wgpu::MemoryHints::MemoryUsage,
|
memory_hints: wgpu::MemoryHints::MemoryUsage,
|
||||||
trace: wgpu::Trace::Off,
|
trace: wgpu::Trace::Off,
|
||||||
|
|
@ -346,14 +352,6 @@ pub fn present(
|
||||||
) -> Result<(), compositor::SurfaceError> {
|
) -> Result<(), compositor::SurfaceError> {
|
||||||
match surface.get_current_texture() {
|
match surface.get_current_texture() {
|
||||||
Ok(frame) => {
|
Ok(frame) => {
|
||||||
if frame.texture.width() != viewport.physical_width()
|
|
||||||
|| frame.texture.height() != viewport.physical_height()
|
|
||||||
{
|
|
||||||
return Err(compositor::SurfaceError::Outdated);
|
|
||||||
}
|
|
||||||
|
|
||||||
let suboptimal = frame.suboptimal;
|
|
||||||
|
|
||||||
let view = &frame
|
let view = &frame
|
||||||
.texture
|
.texture
|
||||||
.create_view(&wgpu::TextureViewDescriptor::default());
|
.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
|
@ -369,10 +367,6 @@ pub fn present(
|
||||||
on_pre_present();
|
on_pre_present();
|
||||||
frame.present();
|
frame.present();
|
||||||
|
|
||||||
if suboptimal {
|
|
||||||
return Err(compositor::SurfaceError::Outdated);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Err(error) => match error {
|
Err(error) => match error {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
#![allow(unexpected_cfgs)]
|
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fs,
|
fs,
|
||||||
io::{BufRead, BufReader},
|
io::{BufRead, BufReader},
|
||||||
|
|
@ -146,9 +144,7 @@ pub fn get_x11_device_ids<W: Window>(window: &W) -> Option<(u16, u16)> {
|
||||||
{
|
{
|
||||||
let stat =
|
let stat =
|
||||||
stat(Path::new("/dev/dri").join(device)).ok()?;
|
stat(Path::new("/dev/dri").join(device)).ok()?;
|
||||||
// Hack (u64 as u64) for E2K CPU architecture
|
let dev = stat.st_rdev;
|
||||||
#[allow(clippy::unnecessary_cast)]
|
|
||||||
let dev = stat.st_rdev as u64;
|
|
||||||
return super::ids_from_dev(dev);
|
return super::ids_from_dev(dev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -169,9 +165,7 @@ pub fn get_x11_device_ids<W: Window>(window: &W) -> Option<(u16, u16)> {
|
||||||
let dri3 = conn.dri3_open(root, x11rb::NONE).ok()?.reply().ok()?;
|
let dri3 = conn.dri3_open(root, x11rb::NONE).ok()?.reply().ok()?;
|
||||||
let device_fd = dri3.device_fd;
|
let device_fd = dri3.device_fd;
|
||||||
let stat = fstat(device_fd).ok()?;
|
let stat = fstat(device_fd).ok()?;
|
||||||
// Hack (u64 as u64) for E2K CPU architecture
|
let dev = stat.st_rdev;
|
||||||
#[allow(clippy::unnecessary_cast)]
|
|
||||||
let dev = stat.st_rdev as u64;
|
|
||||||
super::ids_from_dev(dev)
|
super::ids_from_dev(dev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,13 +29,15 @@ highlighter = ["dep:iced_highlighter"]
|
||||||
advanced = []
|
advanced = []
|
||||||
crisp = []
|
crisp = []
|
||||||
a11y = ["iced_accessibility"]
|
a11y = ["iced_accessibility"]
|
||||||
wayland = ["iced_runtime/wayland", "dep:cctk"]
|
cctk = ["iced_runtime/cctk", "dep:cctk"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
iced_renderer.workspace = true
|
iced_renderer.workspace = true
|
||||||
iced_runtime.workspace = true
|
iced_runtime.workspace = true
|
||||||
iced_accessibility.workspace = true
|
iced_accessibility.workspace = true
|
||||||
iced_accessibility.optional = true
|
iced_accessibility.optional = true
|
||||||
|
cctk.workspace = true
|
||||||
|
cctk.optional = true
|
||||||
num-traits.workspace = true
|
num-traits.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
rustc-hash.workspace = true
|
rustc-hash.workspace = true
|
||||||
|
|
@ -55,10 +57,3 @@ pulldown-cmark.optional = true
|
||||||
|
|
||||||
iced_highlighter.workspace = true
|
iced_highlighter.workspace = true
|
||||||
iced_highlighter.optional = true
|
iced_highlighter.optional = true
|
||||||
|
|
||||||
[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_vendor = "apple"))))'.dependencies]
|
|
||||||
cctk.workspace = true
|
|
||||||
cctk.optional = true
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
build_helpers = { path = "../build_helpers" }
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
fn main() {
|
|
||||||
build_helpers::cfg_aliases_setup();
|
|
||||||
}
|
|
||||||
|
|
@ -267,7 +267,6 @@ struct State {
|
||||||
is_hovered: bool,
|
is_hovered: bool,
|
||||||
is_pressed: bool,
|
is_pressed: bool,
|
||||||
is_focused: bool,
|
is_focused: bool,
|
||||||
needs_redraw: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
|
impl<'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
|
||||||
|
|
@ -369,12 +368,6 @@ where
|
||||||
viewport,
|
viewport,
|
||||||
);
|
);
|
||||||
|
|
||||||
let state = tree.state.downcast_mut::<State>();
|
|
||||||
if state.needs_redraw {
|
|
||||||
state.needs_redraw = false;
|
|
||||||
shell.request_redraw();
|
|
||||||
}
|
|
||||||
|
|
||||||
if shell.is_event_captured() {
|
if shell.is_event_captured() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -784,11 +777,11 @@ impl Default for Style {
|
||||||
///
|
///
|
||||||
/// impl Catalog for MyTheme {
|
/// impl Catalog for MyTheme {
|
||||||
/// type Class<'a> = ButtonClass;
|
/// type Class<'a> = ButtonClass;
|
||||||
///
|
///
|
||||||
/// fn default<'a>() -> Self::Class<'a> {
|
/// fn default<'a>() -> Self::Class<'a> {
|
||||||
/// ButtonClass::default()
|
/// ButtonClass::default()
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
/// fn style(&self, class: &Self::Class<'_>, status: Status) -> Style {
|
/// fn style(&self, class: &Self::Class<'_>, status: Status) -> Style {
|
||||||
/// let mut style = Style::default();
|
/// let mut style = Style::default();
|
||||||
|
|
@ -1007,12 +1000,10 @@ impl operation::Focusable for State {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn focus(&mut self) {
|
fn focus(&mut self) {
|
||||||
self.needs_redraw |= !self.is_focused;
|
|
||||||
self.is_focused = true;
|
self.is_focused = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unfocus(&mut self) {
|
fn unfocus(&mut self) {
|
||||||
self.needs_redraw |= self.is_focused;
|
|
||||||
self.is_focused = false;
|
self.is_focused = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -505,7 +505,6 @@ where
|
||||||
state.raw(),
|
state.raw(),
|
||||||
crate::text::Style {
|
crate::text::Style {
|
||||||
color: style.text_color,
|
color: style.text_color,
|
||||||
..crate::text::Style::default()
|
|
||||||
},
|
},
|
||||||
viewport,
|
viewport,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ use crate::core::{
|
||||||
color,
|
color,
|
||||||
};
|
};
|
||||||
|
|
||||||
use iced_runtime::{Task, task};
|
use iced_runtime::{Action, Task, task};
|
||||||
|
|
||||||
/// A widget that aligns its contents inside of its boundaries.
|
/// A widget that aligns its contents inside of its boundaries.
|
||||||
///
|
///
|
||||||
|
|
@ -601,7 +601,7 @@ pub fn visible_bounds(id: Id) -> Task<Option<Rectangle>> {
|
||||||
operate: &mut dyn FnMut(&mut dyn Operation<Option<Rectangle>>),
|
operate: &mut dyn FnMut(&mut dyn Operation<Option<Rectangle>>),
|
||||||
) {
|
) {
|
||||||
self.depth += 1;
|
self.depth += 1;
|
||||||
operate(self);
|
self.traverse(operate);
|
||||||
self.depth -= 1;
|
self.depth -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
//! Keyed columns distribute content vertically while keeping continuity.
|
//! Keyed columns distribute content vertically while keeping continuity.
|
||||||
//! Distribute content vertically.
|
//! Distribute content vertically.
|
||||||
|
|
||||||
|
use crate::core::event;
|
||||||
use crate::core::layout;
|
use crate::core::layout;
|
||||||
use crate::core::mouse;
|
use crate::core::mouse;
|
||||||
use crate::core::overlay;
|
use crate::core::overlay;
|
||||||
|
|
@ -292,12 +293,9 @@ where
|
||||||
.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_mut().operate(
|
child
|
||||||
state,
|
.as_widget_mut()
|
||||||
c_layout.with_virtual_offset(layout.virtual_offset()),
|
.operate(state, c_layout.with_virtual_offset(layout.virtual_offset()), renderer, operation);
|
||||||
renderer,
|
|
||||||
operation,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -320,13 +318,7 @@ where
|
||||||
.zip(layout.children())
|
.zip(layout.children())
|
||||||
{
|
{
|
||||||
child.as_widget_mut().update(
|
child.as_widget_mut().update(
|
||||||
tree,
|
tree, event, c_layout.with_virtual_offset(layout.virtual_offset()), cursor, renderer, clipboard, shell,
|
||||||
event,
|
|
||||||
c_layout.with_virtual_offset(layout.virtual_offset()),
|
|
||||||
cursor,
|
|
||||||
renderer,
|
|
||||||
clipboard,
|
|
||||||
shell,
|
|
||||||
viewport,
|
viewport,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -345,13 +337,9 @@ where
|
||||||
.zip(&tree.children)
|
.zip(&tree.children)
|
||||||
.zip(layout.children())
|
.zip(layout.children())
|
||||||
.map(|((child, tree), c_layout)| {
|
.map(|((child, tree), c_layout)| {
|
||||||
child.as_widget().mouse_interaction(
|
child
|
||||||
tree,
|
.as_widget()
|
||||||
c_layout.with_virtual_offset(layout.virtual_offset()),
|
.mouse_interaction(tree, c_layout.with_virtual_offset(layout.virtual_offset()), cursor, viewport, renderer)
|
||||||
cursor,
|
|
||||||
viewport,
|
|
||||||
renderer,
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
.max()
|
.max()
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,6 @@ pub use iced_renderer::graphics;
|
||||||
|
|
||||||
pub use core::widget::Id;
|
pub use core::widget::Id;
|
||||||
|
|
||||||
pub(crate) const DRAG_DEADBAND_DISTANCE: f32 = 10.0;
|
|
||||||
|
|
||||||
mod action;
|
mod action;
|
||||||
mod mouse_area;
|
mod mouse_area;
|
||||||
mod pin;
|
mod pin;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#![allow(missing_docs)]
|
#![allow(missing_docs)]
|
||||||
use crate::core::event::Event;
|
use crate::core::event::{self, Event};
|
||||||
use crate::core::layout;
|
use crate::core::layout;
|
||||||
use crate::core::mouse;
|
use crate::core::mouse;
|
||||||
use crate::core::overlay;
|
use crate::core::overlay;
|
||||||
|
|
@ -363,25 +363,24 @@ where
|
||||||
let state = tree.state.downcast_mut::<State>();
|
let state = tree.state.downcast_mut::<State>();
|
||||||
let offset = layout.position() - Point::ORIGIN;
|
let offset = layout.position() - Point::ORIGIN;
|
||||||
|
|
||||||
for (element, (index, layout, tree)) in self
|
self.visible_elements
|
||||||
.visible_elements
|
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.zip(&mut state.visible_layouts)
|
.zip(&mut state.visible_layouts)
|
||||||
{
|
.map(|(element, (index, layout, tree))| {
|
||||||
element.as_widget_mut().update(
|
element.as_widget_mut().update(
|
||||||
tree,
|
tree,
|
||||||
event,
|
event,
|
||||||
Layout::with_offset(
|
Layout::with_offset(
|
||||||
offset + Vector::new(0.0, self.spacing * *index as f32),
|
offset + Vector::new(0.0, self.spacing * *index as f32),
|
||||||
layout,
|
layout,
|
||||||
),
|
),
|
||||||
cursor,
|
cursor,
|
||||||
renderer,
|
renderer,
|
||||||
clipboard,
|
clipboard,
|
||||||
shell,
|
shell,
|
||||||
viewport,
|
viewport,
|
||||||
);
|
)
|
||||||
}
|
});
|
||||||
|
|
||||||
if let Event::Window(window::Event::RedrawRequested(_)) = event {
|
if let Event::Window(window::Event::RedrawRequested(_)) = event {
|
||||||
match &mut state.task {
|
match &mut state.task {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use iced_renderer::core::mouse::Click;
|
use iced_renderer::core::mouse::Click;
|
||||||
|
|
||||||
|
use crate::core::event;
|
||||||
use crate::core::layout;
|
use crate::core::layout;
|
||||||
use crate::core::mouse;
|
use crate::core::mouse;
|
||||||
use crate::core::overlay;
|
use crate::core::overlay;
|
||||||
|
|
|
||||||
|
|
@ -92,8 +92,7 @@ use crate::core::{
|
||||||
};
|
};
|
||||||
use log::trace;
|
use log::trace;
|
||||||
|
|
||||||
use crate::DRAG_DEADBAND_DISTANCE;
|
const DRAG_DEADBAND_DISTANCE: f32 = 10.0;
|
||||||
|
|
||||||
const THICKNESS_RATIO: f32 = 25.0;
|
const THICKNESS_RATIO: f32 = 25.0;
|
||||||
|
|
||||||
/// A collection of panes distributed using either vertical or horizontal splits
|
/// A collection of panes distributed using either vertical or horizontal splits
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use crate::core::renderer;
|
||||||
use crate::core::widget::{self, Tree};
|
use crate::core::widget::{self, Tree};
|
||||||
use crate::core::{
|
use crate::core::{
|
||||||
self, Clipboard, Element, Event, Layout, Point, Rectangle, Shell, Size,
|
self, Clipboard, Element, Event, Layout, Point, Rectangle, Shell, Size,
|
||||||
Vector,
|
Vector, event,
|
||||||
};
|
};
|
||||||
use crate::pane_grid::{Draggable, TitleBar};
|
use crate::pane_grid::{Draggable, TitleBar};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,6 @@ where
|
||||||
width: Length,
|
width: Length,
|
||||||
height: Length,
|
height: Length,
|
||||||
position: Point,
|
position: Point,
|
||||||
overflow: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Message, Theme, Renderer> Pin<'a, Message, Theme, Renderer>
|
impl<'a, Message, Theme, Renderer> Pin<'a, Message, Theme, Renderer>
|
||||||
|
|
@ -76,7 +75,6 @@ where
|
||||||
width: Length::Fill,
|
width: Length::Fill,
|
||||||
height: Length::Fill,
|
height: Length::Fill,
|
||||||
position: Point::ORIGIN,
|
position: Point::ORIGIN,
|
||||||
overflow: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,12 +107,6 @@ where
|
||||||
self.position.y = y.into().0;
|
self.position.y = y.into().0;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets whether the [`Pin`] can overflow its boundaries.
|
|
||||||
pub fn overflow(mut self, overflow: bool) -> Self {
|
|
||||||
self.overflow = overflow;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
|
impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
|
||||||
|
|
@ -153,12 +145,8 @@ where
|
||||||
) -> layout::Node {
|
) -> layout::Node {
|
||||||
let limits = limits.width(self.width).height(self.height);
|
let limits = limits.width(self.width).height(self.height);
|
||||||
|
|
||||||
let available =
|
let available =
|
||||||
if self.overflow {
|
limits.max() - Size::new(self.position.x, self.position.y);
|
||||||
limits.max()
|
|
||||||
} else {
|
|
||||||
limits.max() - Size::new(self.position.x, self.position.y)
|
|
||||||
};
|
|
||||||
|
|
||||||
let node = self
|
let node = self
|
||||||
.content
|
.content
|
||||||
|
|
|
||||||
|
|
@ -459,7 +459,6 @@ where
|
||||||
state.raw(),
|
state.raw(),
|
||||||
crate::text::Style {
|
crate::text::Style {
|
||||||
color: style.text_color,
|
color: style.text_color,
|
||||||
..crate::text::Style::default()
|
|
||||||
},
|
},
|
||||||
viewport,
|
viewport,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ use iced_runtime::core::widget::Id;
|
||||||
#[cfg(feature = "a11y")]
|
#[cfg(feature = "a11y")]
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
use crate::core::event;
|
||||||
use crate::core::keyboard;
|
use crate::core::keyboard;
|
||||||
use crate::core::layout;
|
use crate::core::layout;
|
||||||
use crate::core::mouse;
|
use crate::core::mouse;
|
||||||
|
|
@ -247,19 +248,64 @@ where
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds padding at the ends of the [`Scrollbar`]s of the [`Scrollable`].
|
/// Sets whether the user should be allowed to auto-scroll the [`Scrollable`]
|
||||||
|
/// with the middle mouse button.
|
||||||
///
|
///
|
||||||
/// The `padding` provided will be used as space at the top and bottom of a
|
/// By default, it is disabled.
|
||||||
/// vertical [`Scrollbar`], and at the left and right ends of a horizontal
|
pub fn auto_scroll(mut self, auto_scroll: bool) -> Self {
|
||||||
/// [`Scrollbar`], when they are visible.
|
self.auto_scroll = auto_scroll;
|
||||||
///
|
self
|
||||||
/// Unlike [`Self::spacing`], the padding does not affect the layout of the
|
}
|
||||||
/// [`Scrollable`].
|
|
||||||
pub fn padding(mut self, new_padding: impl Into<Pixels>) -> Self {
|
/// Sets the scrollbar width of the [`Scrollbar`].
|
||||||
let padding = new_padding.into().0;
|
pub fn scrollbar_width(mut self, width: impl Into<Pixels>) -> Self {
|
||||||
|
let width = width.into().0.max(0.0);
|
||||||
|
|
||||||
match &mut self.direction {
|
match &mut self.direction {
|
||||||
Direction::Horizontal(scrollbar) | Direction::Vertical(scrollbar) => {
|
Direction::Horizontal(scrollbar)
|
||||||
|
| Direction::Vertical(scrollbar) => {
|
||||||
|
scrollbar.width = width;
|
||||||
|
}
|
||||||
|
Direction::Both {
|
||||||
|
horizontal,
|
||||||
|
vertical,
|
||||||
|
} => {
|
||||||
|
horizontal.width = width;
|
||||||
|
vertical.width = width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the scroller width of the [`Scrollbar`].
|
||||||
|
pub fn scroller_width(mut self, width: impl Into<Pixels>) -> Self {
|
||||||
|
let width = width.into().0.max(0.0);
|
||||||
|
|
||||||
|
match &mut self.direction {
|
||||||
|
Direction::Horizontal(scrollbar)
|
||||||
|
| Direction::Vertical(scrollbar) => {
|
||||||
|
scrollbar.scroller_width = width;
|
||||||
|
}
|
||||||
|
Direction::Both {
|
||||||
|
horizontal,
|
||||||
|
vertical,
|
||||||
|
} => {
|
||||||
|
horizontal.scroller_width = width;
|
||||||
|
vertical.scroller_width = width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the padding at the start and end of the [`Scrollbar`].
|
||||||
|
pub fn scrollbar_padding(mut self, padding: impl Into<Pixels>) -> Self {
|
||||||
|
let padding = padding.into().0.max(0.0);
|
||||||
|
|
||||||
|
match &mut self.direction {
|
||||||
|
Direction::Horizontal(scrollbar)
|
||||||
|
| Direction::Vertical(scrollbar) => {
|
||||||
scrollbar.padding = padding;
|
scrollbar.padding = padding;
|
||||||
}
|
}
|
||||||
Direction::Both {
|
Direction::Both {
|
||||||
|
|
@ -274,15 +320,6 @@ where
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets whether the user should be allowed to auto-scroll the [`Scrollable`]
|
|
||||||
/// with the middle mouse button.
|
|
||||||
///
|
|
||||||
/// By default, it is disabled.
|
|
||||||
pub fn auto_scroll(mut self, auto_scroll: bool) -> Self {
|
|
||||||
self.auto_scroll = auto_scroll;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the style of this [`Scrollable`].
|
/// Sets the style of this [`Scrollable`].
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn style(mut self, style: impl Fn(&Theme, Status) -> Style + 'a) -> Self
|
pub fn style(mut self, style: impl Fn(&Theme, Status) -> Style + 'a) -> Self
|
||||||
|
|
@ -467,15 +504,9 @@ impl Scrollbar {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the padding of the [`Scrollbar`].
|
/// Sets the padding at the start and end of the [`Scrollbar`].
|
||||||
///
|
|
||||||
/// The padding is added at the top and bottom of the scrollbar (or at the
|
|
||||||
/// left and right ends for a horizontal [`Scrollbar`]) when it is visible.
|
|
||||||
///
|
|
||||||
/// Unlike [`Self::margin`] and [`Self::spacing`], the padding does not
|
|
||||||
/// affect the layout of the [`Scrollable`].
|
|
||||||
pub fn padding(mut self, padding: impl Into<Pixels>) -> Self {
|
pub fn padding(mut self, padding: impl Into<Pixels>) -> Self {
|
||||||
self.padding = padding.into().0;
|
self.padding = padding.into().0.max(0.0);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -645,7 +676,13 @@ where
|
||||||
let translation =
|
let translation =
|
||||||
state.translation(self.direction, bounds, content_bounds);
|
state.translation(self.direction, bounds, content_bounds);
|
||||||
|
|
||||||
operation.pre_operation(Some(&self.id));
|
operation.scrollable(
|
||||||
|
Some(&self.id),
|
||||||
|
bounds,
|
||||||
|
content_bounds,
|
||||||
|
translation,
|
||||||
|
state,
|
||||||
|
);
|
||||||
|
|
||||||
operation.traverse(&mut |operation| {
|
operation.traverse(&mut |operation| {
|
||||||
self.content.as_widget_mut().operate(
|
self.content.as_widget_mut().operate(
|
||||||
|
|
@ -659,14 +696,6 @@ where
|
||||||
operation,
|
operation,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
// XXX must be done after traversing to perform DFS
|
|
||||||
operation.scrollable(
|
|
||||||
Some(&self.id),
|
|
||||||
bounds,
|
|
||||||
content_bounds,
|
|
||||||
translation,
|
|
||||||
state,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(
|
fn update(
|
||||||
|
|
@ -904,7 +933,7 @@ where
|
||||||
|
|
||||||
let had_input_method = shell.input_method().is_enabled();
|
let had_input_method = shell.input_method().is_enabled();
|
||||||
|
|
||||||
let c_event = match event.clone() {
|
let mut c_event = match event.clone() {
|
||||||
Event::Dnd(dnd::DndEvent::Offer(
|
Event::Dnd(dnd::DndEvent::Offer(
|
||||||
id,
|
id,
|
||||||
dnd::OfferEvent::Enter {
|
dnd::OfferEvent::Enter {
|
||||||
|
|
@ -932,16 +961,6 @@ where
|
||||||
y: y + translation.y as f64,
|
y: y + translation.y as f64,
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
Event::Touch(touch::Event::FingerLifted {
|
|
||||||
id,
|
|
||||||
position,
|
|
||||||
}) if matches!(
|
|
||||||
state.interaction,
|
|
||||||
Interaction::TouchScrolling(_)
|
|
||||||
) =>
|
|
||||||
{
|
|
||||||
Event::Touch(touch::Event::FingerLost { id, position })
|
|
||||||
}
|
|
||||||
e => e,
|
e => e,
|
||||||
};
|
};
|
||||||
self.content.as_widget_mut().update(
|
self.content.as_widget_mut().update(
|
||||||
|
|
@ -969,14 +988,6 @@ where
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if matches!(
|
|
||||||
event,
|
|
||||||
Event::Mouse(mouse::Event::CursorMoved { .. })
|
|
||||||
| Event::Touch(touch::Event::FingerPressed { .. })
|
|
||||||
) {
|
|
||||||
state.suppress_touch_hover = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if matches!(
|
if matches!(
|
||||||
event,
|
event,
|
||||||
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
|
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
|
||||||
|
|
@ -985,18 +996,10 @@ where
|
||||||
| touch::Event::FingerLost { .. }
|
| touch::Event::FingerLost { .. }
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
if matches!(state.interaction, Interaction::TouchScrolling(_)) {
|
|
||||||
state.suppress_touch_hover = true;
|
|
||||||
}
|
|
||||||
state.interaction = Interaction::None;
|
state.interaction = Interaction::None;
|
||||||
state.touch_press_start = None;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Event::Touch(touch::Event::FingerPressed { .. }) = event {
|
|
||||||
state.touch_press_start = cursor_over_scrollable;
|
|
||||||
}
|
|
||||||
|
|
||||||
if shell.is_event_captured() {
|
if shell.is_event_captured() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -1090,31 +1093,14 @@ where
|
||||||
Interaction::TouchScrolling(position);
|
Interaction::TouchScrolling(position);
|
||||||
}
|
}
|
||||||
touch::Event::FingerMoved { .. } => {
|
touch::Event::FingerMoved { .. } => {
|
||||||
let Some(cursor_position) = cursor.position()
|
let Interaction::TouchScrolling(
|
||||||
|
scroll_box_touched_at,
|
||||||
|
) = state.interaction
|
||||||
else {
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if !matches!(
|
let Some(cursor_position) = cursor.position()
|
||||||
state.interaction,
|
|
||||||
Interaction::TouchScrolling(_)
|
|
||||||
) {
|
|
||||||
let Some(start) = state.touch_press_start
|
|
||||||
else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
if start.distance(cursor_position)
|
|
||||||
< crate::DRAG_DEADBAND_DISTANCE
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
state.interaction =
|
|
||||||
Interaction::TouchScrolling(start);
|
|
||||||
}
|
|
||||||
|
|
||||||
let Interaction::TouchScrolling(
|
|
||||||
scroll_box_touched_at,
|
|
||||||
) = state.interaction
|
|
||||||
else {
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
@ -1336,14 +1322,6 @@ where
|
||||||
state.translation(self.direction, bounds, content_bounds);
|
state.translation(self.direction, bounds, content_bounds);
|
||||||
|
|
||||||
let cursor = match cursor_over_scrollable {
|
let cursor = match cursor_over_scrollable {
|
||||||
_ if state.suppress_touch_hover
|
|
||||||
|| matches!(
|
|
||||||
state.interaction,
|
|
||||||
Interaction::TouchScrolling(_)
|
|
||||||
) =>
|
|
||||||
{
|
|
||||||
mouse::Cursor::Unavailable
|
|
||||||
}
|
|
||||||
Some(cursor_position)
|
Some(cursor_position)
|
||||||
if !(mouse_over_x_scrollbar || mouse_over_y_scrollbar) =>
|
if !(mouse_over_x_scrollbar || mouse_over_y_scrollbar) =>
|
||||||
{
|
{
|
||||||
|
|
@ -1519,14 +1497,6 @@ where
|
||||||
state.translation(self.direction, bounds, content_bounds);
|
state.translation(self.direction, bounds, content_bounds);
|
||||||
|
|
||||||
let cursor = match cursor_over_scrollable {
|
let cursor = match cursor_over_scrollable {
|
||||||
_ if state.suppress_touch_hover
|
|
||||||
|| matches!(
|
|
||||||
state.interaction,
|
|
||||||
Interaction::TouchScrolling(_)
|
|
||||||
) =>
|
|
||||||
{
|
|
||||||
cursor.levitate() + translation
|
|
||||||
}
|
|
||||||
Some(cursor_position)
|
Some(cursor_position)
|
||||||
if !(mouse_over_x_scrollbar || mouse_over_y_scrollbar) =>
|
if !(mouse_over_x_scrollbar || mouse_over_y_scrollbar) =>
|
||||||
{
|
{
|
||||||
|
|
@ -2052,8 +2022,6 @@ struct State {
|
||||||
last_notified: Option<Viewport>,
|
last_notified: Option<Viewport>,
|
||||||
last_scrolled: Option<Instant>,
|
last_scrolled: Option<Instant>,
|
||||||
is_scrollbar_visible: bool,
|
is_scrollbar_visible: bool,
|
||||||
touch_press_start: Option<Point>,
|
|
||||||
suppress_touch_hover: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
|
@ -2079,8 +2047,6 @@ impl Default for State {
|
||||||
last_notified: None,
|
last_notified: None,
|
||||||
last_scrolled: None,
|
last_scrolled: None,
|
||||||
is_scrollbar_visible: true,
|
is_scrollbar_visible: true,
|
||||||
touch_press_start: None,
|
|
||||||
suppress_touch_hover: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2383,16 +2349,13 @@ impl Scrollbars {
|
||||||
let total_scrollbar_width =
|
let total_scrollbar_width =
|
||||||
width.max(scroller_width) + 2.0 * margin;
|
width.max(scroller_width) + 2.0 * margin;
|
||||||
|
|
||||||
// The padding is purely visual: it shrinks the top and bottom of the
|
|
||||||
// scrollbar without affecting the layout
|
|
||||||
let scrollbar_height = (bounds.height - x_scrollbar_height - 2.0 * padding).max(0.0);
|
|
||||||
|
|
||||||
// Total bounds of the scrollbar + margin + scroller width
|
// Total bounds of the scrollbar + margin + scroller width
|
||||||
let total_scrollbar_bounds = Rectangle {
|
let total_scrollbar_bounds = Rectangle {
|
||||||
x: bounds.x + bounds.width - total_scrollbar_width,
|
x: bounds.x + bounds.width - total_scrollbar_width,
|
||||||
y: bounds.y + padding,
|
y: bounds.y + padding,
|
||||||
width: total_scrollbar_width,
|
width: total_scrollbar_width,
|
||||||
height: scrollbar_height,
|
height: (bounds.height - x_scrollbar_height - 2.0 * padding)
|
||||||
|
.max(0.0),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Bounds of just the scrollbar
|
// Bounds of just the scrollbar
|
||||||
|
|
@ -2402,7 +2365,8 @@ impl Scrollbars {
|
||||||
- width / 2.0,
|
- width / 2.0,
|
||||||
y: bounds.y + padding,
|
y: bounds.y + padding,
|
||||||
width,
|
width,
|
||||||
height: scrollbar_height,
|
height: (bounds.height - x_scrollbar_height - 2.0 * padding)
|
||||||
|
.max(0.0),
|
||||||
};
|
};
|
||||||
|
|
||||||
let ratio = bounds.height / content_bounds.height;
|
let ratio = bounds.height / content_bounds.height;
|
||||||
|
|
@ -2459,15 +2423,12 @@ impl Scrollbars {
|
||||||
let total_scrollbar_height =
|
let total_scrollbar_height =
|
||||||
width.max(scroller_width) + 2.0 * margin;
|
width.max(scroller_width) + 2.0 * margin;
|
||||||
|
|
||||||
// The padding is purely visual: it shrinks the left and right ends of
|
|
||||||
// the scrollbar without affecting the layout
|
|
||||||
let scrollbar_width = (bounds.width - scrollbar_y_width - 2.0 * padding).max(0.0);
|
|
||||||
|
|
||||||
// Total bounds of the scrollbar + margin + scroller width
|
// Total bounds of the scrollbar + margin + scroller width
|
||||||
let total_scrollbar_bounds = Rectangle {
|
let total_scrollbar_bounds = Rectangle {
|
||||||
x: bounds.x + padding,
|
x: bounds.x + padding,
|
||||||
y: bounds.y + bounds.height - total_scrollbar_height,
|
y: bounds.y + bounds.height - total_scrollbar_height,
|
||||||
width: scrollbar_width,
|
width: (bounds.width - scrollbar_y_width - 2.0 * padding)
|
||||||
|
.max(0.0),
|
||||||
height: total_scrollbar_height,
|
height: total_scrollbar_height,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -2477,7 +2438,8 @@ impl Scrollbars {
|
||||||
y: bounds.y + bounds.height
|
y: bounds.y + bounds.height
|
||||||
- total_scrollbar_height / 2.0
|
- total_scrollbar_height / 2.0
|
||||||
- width / 2.0,
|
- width / 2.0,
|
||||||
width: scrollbar_width,
|
width: (bounds.width - scrollbar_y_width - 2.0 * padding)
|
||||||
|
.max(0.0),
|
||||||
height: width,
|
height: width,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -110,8 +110,6 @@ where
|
||||||
height: f32,
|
height: f32,
|
||||||
class: Theme::Class<'a>,
|
class: Theme::Class<'a>,
|
||||||
status: Option<Status>,
|
status: Option<Status>,
|
||||||
handle_width: f32,
|
|
||||||
handle_height: f32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T, Message, Theme> Slider<'a, T, Message, Theme>
|
impl<'a, T, Message, Theme> Slider<'a, T, Message, Theme>
|
||||||
|
|
@ -121,13 +119,7 @@ where
|
||||||
Theme: Catalog,
|
Theme: Catalog,
|
||||||
{
|
{
|
||||||
/// The default height of a [`Slider`].
|
/// The default height of a [`Slider`].
|
||||||
pub const DEFAULT_HEIGHT: f32 = 26.0;
|
pub const DEFAULT_HEIGHT: f32 = 16.0;
|
||||||
|
|
||||||
/// The default max height of a [`Handle`].
|
|
||||||
pub const DEFAULT_MAX_HANDLE_HEIGHT: f32 = 26.0;
|
|
||||||
|
|
||||||
/// The default max width of a [`Handle`].
|
|
||||||
pub const DEFAULT_MAX_HANDLE_WIDTH: f32 = 26.0;
|
|
||||||
|
|
||||||
/// Creates a new [`Slider`].
|
/// Creates a new [`Slider`].
|
||||||
///
|
///
|
||||||
|
|
@ -173,8 +165,6 @@ where
|
||||||
height: Self::DEFAULT_HEIGHT,
|
height: Self::DEFAULT_HEIGHT,
|
||||||
class: Theme::default(),
|
class: Theme::default(),
|
||||||
status: None,
|
status: None,
|
||||||
handle_width: Self::DEFAULT_MAX_HANDLE_WIDTH,
|
|
||||||
handle_height: Self::DEFAULT_MAX_HANDLE_HEIGHT,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -217,24 +207,6 @@ where
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the max width of the [`Handle`].
|
|
||||||
///
|
|
||||||
/// Theme can be used to change the size and shape of handle
|
|
||||||
/// but width of handle defined in theme will not exceed this width
|
|
||||||
pub fn handle_width(mut self, width: impl Into<Pixels>) -> Self {
|
|
||||||
self.handle_width = width.into().0;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the max height of the [`Handle`].
|
|
||||||
///
|
|
||||||
/// Theme can be used to change the size and shape of handle
|
|
||||||
/// but height of handle defined in theme will not exceed this height
|
|
||||||
pub fn handle_height(mut self, height: impl Into<Pixels>) -> Self {
|
|
||||||
self.handle_height = height.into().0;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the step size of the [`Slider`].
|
/// Sets the step size of the [`Slider`].
|
||||||
pub fn step(mut self, step: impl Into<T>) -> Self {
|
pub fn step(mut self, step: impl Into<T>) -> Self {
|
||||||
self.step = step.into();
|
self.step = step.into();
|
||||||
|
|
@ -351,15 +323,8 @@ where
|
||||||
let mut update = || {
|
let mut update = || {
|
||||||
let current_value = self.value;
|
let current_value = self.value;
|
||||||
|
|
||||||
self.handle_width = self.handle_width.min(layout.bounds().width);
|
|
||||||
self.handle_height = self.handle_height.min(layout.bounds().height);
|
|
||||||
|
|
||||||
let locate = |cursor_position: Point| -> Option<T> {
|
let locate = |cursor_position: Point| -> Option<T> {
|
||||||
let bounds = Rectangle {
|
let bounds = layout.bounds();
|
||||||
x: layout.bounds().x + self.handle_width / 2.0,
|
|
||||||
width: layout.bounds().width - self.handle_width,
|
|
||||||
..layout.bounds()
|
|
||||||
};
|
|
||||||
|
|
||||||
if cursor_position.x <= bounds.x {
|
if cursor_position.x <= bounds.x {
|
||||||
Some(*self.range.start())
|
Some(*self.range.start())
|
||||||
|
|
@ -544,29 +509,53 @@ where
|
||||||
_cursor: mouse::Cursor,
|
_cursor: mouse::Cursor,
|
||||||
_viewport: &Rectangle,
|
_viewport: &Rectangle,
|
||||||
) {
|
) {
|
||||||
let bounds = Rectangle {
|
let bounds = layout.bounds();
|
||||||
x: layout.bounds().x + self.handle_width / 2.0,
|
|
||||||
width: layout.bounds().width - self.handle_width,
|
|
||||||
..layout.bounds()
|
|
||||||
};
|
|
||||||
|
|
||||||
let style =
|
let style =
|
||||||
theme.style(&self.class, self.status.unwrap_or(Status::Active));
|
theme.style(&self.class, self.status.unwrap_or(Status::Active));
|
||||||
|
|
||||||
let (mut handle_width, mut handle_height, handle_border_radius) =
|
let border_width = style
|
||||||
|
.handle
|
||||||
|
.border_width
|
||||||
|
.min(bounds.height / 2.0)
|
||||||
|
.min(bounds.width / 2.0);
|
||||||
|
|
||||||
|
let (handle_width, handle_height, handle_border_radius) =
|
||||||
match style.handle.shape {
|
match style.handle.shape {
|
||||||
HandleShape::Circle { radius } => {
|
HandleShape::Circle { radius } => {
|
||||||
|
let radius = (radius)
|
||||||
|
.max(2.0 * border_width)
|
||||||
|
.min(bounds.height / 2.0)
|
||||||
|
.min(bounds.width / 2.0 + 2.0 * border_width);
|
||||||
(radius * 2.0, radius * 2.0, Radius::from(radius))
|
(radius * 2.0, radius * 2.0, Radius::from(radius))
|
||||||
}
|
}
|
||||||
HandleShape::Rectangle {
|
HandleShape::Rectangle {
|
||||||
height,
|
height,
|
||||||
width,
|
width,
|
||||||
border_radius,
|
border_radius,
|
||||||
} => (f32::from(width), f32::from(height), border_radius),
|
} => {
|
||||||
};
|
let width = (f32::from(width)).max(2.0 * border_width);
|
||||||
|
let height = (f32::from(height)).max(2.0 * border_width);
|
||||||
|
let mut border_radius: [f32; 4] = border_radius.into();
|
||||||
|
for r in &mut border_radius {
|
||||||
|
*r = (*r)
|
||||||
|
.min(height / 2.0)
|
||||||
|
.min(width / 2.0)
|
||||||
|
.max(*r * (width + border_width * 2.0) / width);
|
||||||
|
}
|
||||||
|
|
||||||
handle_width = handle_width.min(self.handle_width);
|
(
|
||||||
handle_height = handle_height.min(self.handle_height);
|
width,
|
||||||
|
height,
|
||||||
|
Radius {
|
||||||
|
top_left: border_radius[0],
|
||||||
|
top_right: border_radius[1],
|
||||||
|
bottom_right: border_radius[2],
|
||||||
|
bottom_left: border_radius[3],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let value = self.value.into() as f32;
|
let value = self.value.into() as f32;
|
||||||
let (range_start, range_end) = {
|
let (range_start, range_end) = {
|
||||||
|
|
@ -578,7 +567,8 @@ where
|
||||||
let offset = if range_start >= range_end {
|
let offset = if range_start >= range_end {
|
||||||
0.0
|
0.0
|
||||||
} else {
|
} else {
|
||||||
bounds.width * (value - range_start) / (range_end - range_start)
|
(bounds.width - handle_width) * (value - range_start)
|
||||||
|
/ (range_end - range_start)
|
||||||
};
|
};
|
||||||
|
|
||||||
let rail_y = bounds.y + bounds.height / 2.0;
|
let rail_y = bounds.y + bounds.height / 2.0;
|
||||||
|
|
@ -600,8 +590,7 @@ where
|
||||||
x: bounds.x + offset,
|
x: bounds.x + offset,
|
||||||
y: rail_y + 6.0,
|
y: rail_y + 6.0,
|
||||||
width: BREAKPOINT_WIDTH,
|
width: BREAKPOINT_WIDTH,
|
||||||
height: 8.0_f32
|
height: 8.0,
|
||||||
.min(bounds.y + bounds.height - (rail_y + 6.0)),
|
|
||||||
},
|
},
|
||||||
border: Border {
|
border: Border {
|
||||||
radius: 0.0.into(),
|
radius: 0.0.into(),
|
||||||
|
|
@ -618,9 +607,9 @@ where
|
||||||
renderer::Quad {
|
renderer::Quad {
|
||||||
bounds: Rectangle {
|
bounds: Rectangle {
|
||||||
x: bounds.x,
|
x: bounds.x,
|
||||||
y: rail_y - (style.rail.width).min(bounds.height) / 2.0,
|
y: rail_y - style.rail.width / 2.0,
|
||||||
width: offset,
|
width: offset + handle_width / 2.0,
|
||||||
height: (style.rail.width).min(bounds.height),
|
height: style.rail.width,
|
||||||
},
|
},
|
||||||
border: style.rail.border,
|
border: style.rail.border,
|
||||||
..renderer::Quad::default()
|
..renderer::Quad::default()
|
||||||
|
|
@ -633,10 +622,10 @@ where
|
||||||
renderer.fill_quad(
|
renderer.fill_quad(
|
||||||
renderer::Quad {
|
renderer::Quad {
|
||||||
bounds: Rectangle {
|
bounds: Rectangle {
|
||||||
x: bounds.x + offset,
|
x: bounds.x + offset + handle_width / 2.0,
|
||||||
y: rail_y - (style.rail.width).min(bounds.height) / 2.0,
|
y: rail_y - style.rail.width / 2.0,
|
||||||
width: bounds.width - offset,
|
width: bounds.width - offset - handle_width / 2.0,
|
||||||
height: (style.rail.width).min(bounds.height),
|
height: style.rail.width,
|
||||||
},
|
},
|
||||||
border: style.rail.border,
|
border: style.rail.border,
|
||||||
..renderer::Quad::default()
|
..renderer::Quad::default()
|
||||||
|
|
@ -648,7 +637,7 @@ where
|
||||||
renderer.fill_quad(
|
renderer.fill_quad(
|
||||||
renderer::Quad {
|
renderer::Quad {
|
||||||
bounds: Rectangle {
|
bounds: Rectangle {
|
||||||
x: bounds.x + offset - handle_width / 2.0,
|
x: bounds.x + offset,
|
||||||
y: rail_y - (handle_height / 2.0),
|
y: rail_y - (handle_height / 2.0),
|
||||||
width: handle_width,
|
width: handle_width,
|
||||||
height: handle_height,
|
height: handle_height,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
//! Display content on top of other content.
|
//! Display content on top of other content.
|
||||||
|
|
||||||
|
use crate::core::event;
|
||||||
use crate::core::layout;
|
use crate::core::layout;
|
||||||
use crate::core::mouse;
|
use crate::core::mouse;
|
||||||
use crate::core::overlay;
|
use crate::core::overlay;
|
||||||
|
|
|
||||||
|
|
@ -179,10 +179,7 @@ where
|
||||||
{
|
{
|
||||||
let color = color.map(Into::into);
|
let color = color.map(Into::into);
|
||||||
|
|
||||||
self.style(move |_theme| Style {
|
self.style(move |_theme| Style { color })
|
||||||
color,
|
|
||||||
..Style::default()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the default style class of the [`Rich`] text.
|
/// Sets the default style class of the [`Rich`] text.
|
||||||
|
|
|
||||||
|
|
@ -544,9 +544,6 @@ pub struct State<Highlighter: text::Highlighter> {
|
||||||
highlighter: RefCell<Highlighter>,
|
highlighter: RefCell<Highlighter>,
|
||||||
highlighter_settings: Highlighter::Settings,
|
highlighter_settings: Highlighter::Settings,
|
||||||
highlighter_format_address: usize,
|
highlighter_format_address: usize,
|
||||||
context_menu_position: Option<Point>,
|
|
||||||
clipboard_has_text: bool,
|
|
||||||
pending_edit: Option<Action>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -582,14 +579,6 @@ impl<Highlighter: text::Highlighter> State<Highlighter> {
|
||||||
pub fn is_focused(&self) -> bool {
|
pub fn is_focused(&self) -> bool {
|
||||||
self.focus.is_some()
|
self.focus.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clears focus, selection, and all interaction state.
|
|
||||||
pub fn clear_focus(&mut self) {
|
|
||||||
self.focus = None;
|
|
||||||
self.drag_click = None;
|
|
||||||
self.context_menu_position = None;
|
|
||||||
self.pending_edit = Some(Action::ClearSelection);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Highlighter: text::Highlighter> operation::Focusable
|
impl<Highlighter: text::Highlighter> operation::Focusable
|
||||||
|
|
@ -604,7 +593,7 @@ impl<Highlighter: text::Highlighter> operation::Focusable
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unfocus(&mut self) {
|
fn unfocus(&mut self) {
|
||||||
self.clear_focus();
|
self.focus = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -632,9 +621,6 @@ where
|
||||||
)),
|
)),
|
||||||
highlighter_settings: self.highlighter_settings.clone(),
|
highlighter_settings: self.highlighter_settings.clone(),
|
||||||
highlighter_format_address: self.highlighter_format as usize,
|
highlighter_format_address: self.highlighter_format as usize,
|
||||||
context_menu_position: None,
|
|
||||||
clipboard_has_text: false,
|
|
||||||
pending_edit: None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -713,60 +699,6 @@ where
|
||||||
shell: &mut Shell<'_, Message>,
|
shell: &mut Shell<'_, Message>,
|
||||||
_viewport: &Rectangle,
|
_viewport: &Rectangle,
|
||||||
) {
|
) {
|
||||||
// Handle context menu and focus-loss on clicks outside.
|
|
||||||
{
|
|
||||||
let state = tree.state.downcast_mut::<State<Highlighter>>();
|
|
||||||
let text_bounds = layout.bounds();
|
|
||||||
|
|
||||||
if matches!(
|
|
||||||
event,
|
|
||||||
Event::Mouse(mouse::Event::ButtonPressed(_))
|
|
||||||
| Event::Touch(
|
|
||||||
crate::core::touch::Event::FingerPressed { .. }
|
|
||||||
)
|
|
||||||
) && cursor.position_over(text_bounds).is_none()
|
|
||||||
{
|
|
||||||
state.clear_focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
match event {
|
|
||||||
Event::Mouse(mouse::Event::ButtonPressed(
|
|
||||||
mouse::Button::Right,
|
|
||||||
)) => {
|
|
||||||
if let Some(pos) = cursor.position_over(text_bounds) {
|
|
||||||
if state.focus.is_none() {
|
|
||||||
state.focus = Some(Focus::now());
|
|
||||||
}
|
|
||||||
state.context_menu_position = Some(pos);
|
|
||||||
state.clipboard_has_text =
|
|
||||||
widget::text::clipboard_has_text(clipboard);
|
|
||||||
shell.capture_event();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Event::Mouse(mouse::Event::ButtonPressed(
|
|
||||||
mouse::Button::Left,
|
|
||||||
)) => {
|
|
||||||
if state.context_menu_position.take().is_some() {
|
|
||||||
shell.capture_event();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process pending edits from context menu
|
|
||||||
// publishes via on_edit so the app calls content.perform().
|
|
||||||
{
|
|
||||||
let state = tree.state.downcast_mut::<State<Highlighter>>();
|
|
||||||
if let Some(action) = state.pending_edit.take() {
|
|
||||||
if let Some(on_edit) = self.on_edit.as_ref() {
|
|
||||||
shell.publish((on_edit)(action));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let Some(on_edit) = self.on_edit.as_ref() else {
|
let Some(on_edit) = self.on_edit.as_ref() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
@ -1291,23 +1223,6 @@ impl<Message> Binding<Message> {
|
||||||
return Some(binding);
|
return Some(binding);
|
||||||
}
|
}
|
||||||
|
|
||||||
let alt_clipboard = match key.as_ref() {
|
|
||||||
keyboard::Key::Named(key::Named::Insert) if modifiers.shift() => {
|
|
||||||
Some(Self::Paste)
|
|
||||||
}
|
|
||||||
keyboard::Key::Named(key::Named::Insert) if modifiers.command() => {
|
|
||||||
Some(Self::Copy)
|
|
||||||
}
|
|
||||||
keyboard::Key::Named(key::Named::Delete) if modifiers.shift() => {
|
|
||||||
Some(Self::Cut)
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(binding) = alt_clipboard {
|
|
||||||
return Some(binding);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
let modified_key =
|
let modified_key =
|
||||||
convert_macos_shortcut(&key, modifiers).unwrap_or(modified_key);
|
convert_macos_shortcut(&key, modifiers).unwrap_or(modified_key);
|
||||||
|
|
@ -1630,73 +1545,3 @@ pub(crate) fn convert_macos_shortcut(
|
||||||
|
|
||||||
Some(keyboard::Key::Named(key))
|
Some(keyboard::Key::Named(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
use crate::core::widget::text::HasSelectableText;
|
|
||||||
|
|
||||||
impl<Highlighter, Message, Theme, Renderer> HasSelectableText
|
|
||||||
for TextEditor<'_, Highlighter, Message, Theme, Renderer>
|
|
||||||
where
|
|
||||||
Highlighter: text::Highlighter,
|
|
||||||
Theme: Catalog,
|
|
||||||
Renderer: text::Renderer,
|
|
||||||
{
|
|
||||||
fn selected_text(&self, _tree: &widget::Tree) -> Option<String> {
|
|
||||||
self.content.selection()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn select_all(&self, tree: &mut widget::Tree) {
|
|
||||||
let state = tree.state.downcast_mut::<State<Highlighter>>();
|
|
||||||
state.pending_edit = Some(Action::SelectAll);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_editable(&self) -> bool {
|
|
||||||
self.on_edit.is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn has_text(&self, _tree: &widget::Tree) -> bool {
|
|
||||||
!self.content.is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clipboard_has_text(&self, tree: &widget::Tree) -> bool {
|
|
||||||
tree.state
|
|
||||||
.downcast_ref::<State<Highlighter>>()
|
|
||||||
.clipboard_has_text
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_focused(&self, tree: &widget::Tree) -> bool {
|
|
||||||
tree.state.downcast_ref::<State<Highlighter>>().is_focused()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn context_menu_position(&self, tree: &widget::Tree) -> Option<Point> {
|
|
||||||
tree.state
|
|
||||||
.downcast_ref::<State<Highlighter>>()
|
|
||||||
.context_menu_position
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_context_menu_position(
|
|
||||||
&self,
|
|
||||||
tree: &mut widget::Tree,
|
|
||||||
pos: Option<Point>,
|
|
||||||
) {
|
|
||||||
tree.state
|
|
||||||
.downcast_mut::<State<Highlighter>>()
|
|
||||||
.context_menu_position = pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn delete_selection(&self, tree: &mut widget::Tree) -> Option<String> {
|
|
||||||
let state = tree.state.downcast_mut::<State<Highlighter>>();
|
|
||||||
state.pending_edit = Some(Action::Edit(Edit::Delete));
|
|
||||||
Some(String::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn paste_text(
|
|
||||||
&self,
|
|
||||||
tree: &mut widget::Tree,
|
|
||||||
text: &str,
|
|
||||||
) -> Option<String> {
|
|
||||||
let state = tree.state.downcast_mut::<State<Highlighter>>();
|
|
||||||
state.pending_edit =
|
|
||||||
Some(Action::Edit(Edit::Paste(Arc::new(text.to_owned()))));
|
|
||||||
Some(String::new())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::container;
|
use crate::container;
|
||||||
|
|
||||||
|
use crate::core::event;
|
||||||
use crate::core::layout;
|
use crate::core::layout;
|
||||||
use crate::core::mouse;
|
use crate::core::mouse;
|
||||||
use crate::core::overlay;
|
use crate::core::overlay;
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ use std::borrow::Cow;
|
||||||
use iced_runtime::core::border::Radius;
|
use iced_runtime::core::border::Radius;
|
||||||
|
|
||||||
use crate::core::alignment;
|
use crate::core::alignment;
|
||||||
|
use crate::core::border;
|
||||||
use crate::core::layout;
|
use crate::core::layout;
|
||||||
use crate::core::mouse;
|
use crate::core::mouse;
|
||||||
use crate::core::renderer;
|
use crate::core::renderer;
|
||||||
|
|
@ -482,7 +483,6 @@ where
|
||||||
state.raw(),
|
state.raw(),
|
||||||
crate::text::Style {
|
crate::text::Style {
|
||||||
color: style.text_color,
|
color: style.text_color,
|
||||||
..crate::text::Style::default()
|
|
||||||
},
|
},
|
||||||
viewport,
|
viewport,
|
||||||
);
|
);
|
||||||
|
|
@ -509,7 +509,7 @@ where
|
||||||
|
|
||||||
let space = style.handle_margin;
|
let space = style.handle_margin;
|
||||||
|
|
||||||
let _toggler_background_bounds = Rectangle {
|
let toggler_background_bounds = Rectangle {
|
||||||
x: bounds.x,
|
x: bounds.x,
|
||||||
y: bounds.y,
|
y: bounds.y,
|
||||||
width: bounds.width,
|
width: bounds.width,
|
||||||
|
|
@ -529,7 +529,7 @@ where
|
||||||
style.background,
|
style.background,
|
||||||
);
|
);
|
||||||
|
|
||||||
let _padding = (style.padding_ratio * bounds.height).round();
|
let padding = (style.padding_ratio * bounds.height).round();
|
||||||
let toggler_foreground_bounds = Rectangle {
|
let toggler_foreground_bounds = Rectangle {
|
||||||
x: bounds.x
|
x: bounds.x
|
||||||
+ if self.is_toggled {
|
+ if self.is_toggled {
|
||||||
|
|
|
||||||
|
|
@ -14,18 +14,7 @@ keywords.workspace = true
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
wayland = [
|
wayland = ["winit/wayland", "wayland-csd-adwaita"]
|
||||||
"winit/wayland",
|
|
||||||
"wayland-csd-adwaita",
|
|
||||||
"wayland-protocols",
|
|
||||||
"raw-window-handle",
|
|
||||||
"iced_runtime/wayland",
|
|
||||||
"wayland-backend",
|
|
||||||
"xkbcommon",
|
|
||||||
"xkbcommon-dl",
|
|
||||||
"xkeysym",
|
|
||||||
"dep:cctk",
|
|
||||||
]
|
|
||||||
default = ["x11", "wayland-dlopen"]
|
default = ["x11", "wayland-dlopen"]
|
||||||
debug = ["iced_debug/enable"]
|
debug = ["iced_debug/enable"]
|
||||||
sysinfo = ["dep:sysinfo"]
|
sysinfo = ["dep:sysinfo"]
|
||||||
|
|
@ -37,6 +26,17 @@ program = []
|
||||||
wayland-dlopen = ["winit/wayland-dlopen"]
|
wayland-dlopen = ["winit/wayland-dlopen"]
|
||||||
wayland-csd-adwaita = ["winit/wayland-csd-adwaita"]
|
wayland-csd-adwaita = ["winit/wayland-csd-adwaita"]
|
||||||
a11y = ["iced_accessibility", "iced_runtime/a11y"]
|
a11y = ["iced_accessibility", "iced_runtime/a11y"]
|
||||||
|
cctk = [
|
||||||
|
"wayland",
|
||||||
|
"wayland-protocols",
|
||||||
|
"raw-window-handle",
|
||||||
|
"iced_runtime/cctk",
|
||||||
|
"wayland-backend",
|
||||||
|
"xkbcommon",
|
||||||
|
"xkbcommon-dl",
|
||||||
|
"xkeysym",
|
||||||
|
"dep:cctk",
|
||||||
|
]
|
||||||
# Prevents multiple separate process instances.
|
# Prevents multiple separate process instances.
|
||||||
single-instance = []
|
single-instance = []
|
||||||
|
|
||||||
|
|
@ -64,8 +64,7 @@ iced_debug.workspace = true
|
||||||
iced_program.workspace = true
|
iced_program.workspace = true
|
||||||
|
|
||||||
|
|
||||||
[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_vendor = "apple"))))'.dependencies]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
|
|
||||||
mundy.workspace = true
|
mundy.workspace = true
|
||||||
mundy.optional = true
|
mundy.optional = true
|
||||||
|
|
||||||
|
|
@ -90,6 +89,3 @@ winapi.workspace = true
|
||||||
web-sys.workspace = true
|
web-sys.workspace = true
|
||||||
web-sys.features = ["Document", "Window", "HtmlCanvasElement"]
|
web-sys.features = ["Document", "Window", "HtmlCanvasElement"]
|
||||||
wasm-bindgen-futures.workspace = true
|
wasm-bindgen-futures.workspace = true
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
build_helpers = { path = "../build_helpers" }
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
fn main() {
|
|
||||||
build_helpers::cfg_aliases_setup();
|
|
||||||
}
|
|
||||||
|
|
@ -49,10 +49,7 @@ pub fn event_func(
|
||||||
position,
|
position,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let resizable = !window.is_decorated()
|
if !window.is_decorated() {
|
||||||
&& !window.is_maximized()
|
|
||||||
&& window.fullscreen().is_none();
|
|
||||||
if resizable {
|
|
||||||
let location = cursor_resize_direction(
|
let location = cursor_resize_direction(
|
||||||
window.surface_size(),
|
window.surface_size(),
|
||||||
*position,
|
*position,
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ impl Clipboard {
|
||||||
})
|
})
|
||||||
.unwrap_or(State::Unavailable);
|
.unwrap_or(State::Unavailable);
|
||||||
|
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(target_os = "linux")]
|
||||||
if let State::Connected { clipboard, .. } = &state {
|
if let State::Connected { clipboard, .. } = &state {
|
||||||
clipboard.init_dnd(Box::new(sender));
|
clipboard.init_dnd(Box::new(sender));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,33 +23,6 @@ use winit::event::Force;
|
||||||
use winit::icon::IconProvider;
|
use winit::icon::IconProvider;
|
||||||
use winit::keyboard::SmolStr;
|
use winit::keyboard::SmolStr;
|
||||||
|
|
||||||
/// Returns the activation token provided by the compositor, if any.
|
|
||||||
///
|
|
||||||
/// The token is single-use, so it is only ever handed out to the first window.
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
fn take_activation_token() -> Option<winit::window::ActivationToken> {
|
|
||||||
use std::sync::Mutex;
|
|
||||||
use std::sync::OnceLock;
|
|
||||||
use winit::platform::startup_notify::reset_activation_token_env;
|
|
||||||
|
|
||||||
static TOKEN: OnceLock<Mutex<Option<winit::window::ActivationToken>>> =
|
|
||||||
OnceLock::new();
|
|
||||||
|
|
||||||
TOKEN
|
|
||||||
.get_or_init(|| {
|
|
||||||
let token = std::env::var("XDG_ACTIVATION_TOKEN")
|
|
||||||
.ok()
|
|
||||||
.map(winit::window::ActivationToken::from_raw);
|
|
||||||
|
|
||||||
reset_activation_token_env();
|
|
||||||
|
|
||||||
Mutex::new(token)
|
|
||||||
})
|
|
||||||
.lock()
|
|
||||||
.ok()
|
|
||||||
.and_then(|mut token| token.take())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts some [`window::Settings`] into some `WindowAttributes` from `winit`.
|
/// Converts some [`window::Settings`] into some `WindowAttributes` from `winit`.
|
||||||
pub fn window_attributes(
|
pub fn window_attributes(
|
||||||
settings: window::Settings,
|
settings: window::Settings,
|
||||||
|
|
@ -209,19 +182,12 @@ pub fn window_attributes(
|
||||||
{
|
{
|
||||||
use winit::platform::wayland::WindowAttributesWayland;
|
use winit::platform::wayland::WindowAttributesWayland;
|
||||||
|
|
||||||
let mut wayland_attributes = WindowAttributesWayland::default()
|
attributes = attributes.with_platform_attributes(Box::new(
|
||||||
.with_name(
|
WindowAttributesWayland::default().with_name(
|
||||||
&settings.platform_specific.application_id,
|
&settings.platform_specific.application_id,
|
||||||
&settings.platform_specific.application_id,
|
&settings.platform_specific.application_id,
|
||||||
);
|
),
|
||||||
|
));
|
||||||
if let Some(token) = take_activation_token() {
|
|
||||||
wayland_attributes =
|
|
||||||
wayland_attributes.with_activation_token(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
attributes = attributes
|
|
||||||
.with_platform_attributes(Box::new(wayland_attributes));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -269,13 +235,6 @@ pub fn window_event(
|
||||||
scale_factor,
|
scale_factor,
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
winit::event::PointerSource::TabletTool { .. } => {
|
|
||||||
let position = position.to_logical::<f64>(scale_factor);
|
|
||||||
|
|
||||||
Some(Event::Mouse(mouse::Event::CursorMoved {
|
|
||||||
position: Point::new(position.x as f32, position.y as f32),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
// TODO how to handle this for Touch? Is it different than a press? Need to double check winit impl
|
// TODO how to handle this for Touch? Is it different than a press? Need to double check winit impl
|
||||||
|
|
@ -313,24 +272,6 @@ pub fn window_event(
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
WindowEvent::PointerButton {
|
|
||||||
button: ButtonSource::TabletTool { button, .. },
|
|
||||||
state,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
let button: Option<winit::event::MouseButton> = button.into();
|
|
||||||
|
|
||||||
button.map(mouse_button).map(|button| {
|
|
||||||
Event::Mouse(match state {
|
|
||||||
winit::event::ElementState::Pressed => {
|
|
||||||
mouse::Event::ButtonPressed(button)
|
|
||||||
}
|
|
||||||
winit::event::ElementState::Released => {
|
|
||||||
mouse::Event::ButtonReleased(button)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
WindowEvent::PointerButton {
|
WindowEvent::PointerButton {
|
||||||
button: ButtonSource::Touch { finger_id, force },
|
button: ButtonSource::Touch { finger_id, force },
|
||||||
state,
|
state,
|
||||||
|
|
@ -446,19 +387,19 @@ pub fn window_event(
|
||||||
self::modifiers(new_modifiers.state()),
|
self::modifiers(new_modifiers.state()),
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
WindowEvent::Ime(event) => match event {
|
WindowEvent::Ime(event) => Some(Event::InputMethod(match event {
|
||||||
Ime::Enabled => Some(input_method::Event::Opened),
|
Ime::Enabled => input_method::Event::Opened,
|
||||||
Ime::Preedit(content, size) => Some(input_method::Event::Preedit(
|
Ime::Preedit(content, size) => input_method::Event::Preedit(
|
||||||
content,
|
content,
|
||||||
size.map(|(start, end)| start..end),
|
size.map(|(start, end)| start..end),
|
||||||
)),
|
),
|
||||||
Ime::Commit(content) => Some(input_method::Event::Commit(content)),
|
Ime::Commit(content) => input_method::Event::Commit(content),
|
||||||
Ime::Disabled => Some(input_method::Event::Closed),
|
Ime::Disabled => input_method::Event::Closed,
|
||||||
// Upstream iced depends on the winit version which doesn't support
|
Ime::DeleteSurrounding {
|
||||||
// this kind of ime event.
|
before_bytes,
|
||||||
Ime::DeleteSurrounding { .. } => None,
|
after_bytes,
|
||||||
}
|
} => todo!(),
|
||||||
.map(Event::InputMethod),
|
})),
|
||||||
WindowEvent::Focused(focused) => Some(Event::Window(if focused {
|
WindowEvent::Focused(focused) => Some(Event::Window(if focused {
|
||||||
window::Event::Focused
|
window::Event::Focused
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -483,7 +424,7 @@ pub fn window_event(
|
||||||
WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
|
WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
|
||||||
Some(Event::Window(window::Event::Rescaled(scale_factor as f32)))
|
Some(Event::Window(window::Event::Rescaled(scale_factor as f32)))
|
||||||
}
|
}
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
WindowEvent::SuggestedBounds(bounds) => {
|
WindowEvent::SuggestedBounds(bounds) => {
|
||||||
let size = bounds.map(|bounds| {
|
let size = bounds.map(|bounds| {
|
||||||
let size = bounds.to_logical(scale_factor);
|
let size = bounds.to_logical(scale_factor);
|
||||||
|
|
@ -496,7 +437,7 @@ pub fn window_event(
|
||||||
),
|
),
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
WindowEvent::WindowStateChanged => {
|
WindowEvent::WindowStateChanged => {
|
||||||
use cctk::sctk::reexports::csd_frame::WindowState;
|
use cctk::sctk::reexports::csd_frame::WindowState;
|
||||||
use winit::platform::wayland::WindowExtWayland;
|
use winit::platform::wayland::WindowExtWayland;
|
||||||
|
|
@ -1550,7 +1491,7 @@ pub fn winit_key_code(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
fn winit_native_key_code(
|
fn winit_native_key_code(
|
||||||
keycode: keyboard::key::NativeCode,
|
keycode: keyboard::key::NativeCode,
|
||||||
) -> winit::keyboard::NativeKeyCode {
|
) -> winit::keyboard::NativeKeyCode {
|
||||||
|
|
@ -1574,7 +1515,7 @@ fn winit_native_key_code(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reconstruct the raw keycode
|
/// Reconstruct the raw keycode
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
pub fn physical_to_scancode(physical: keyboard::key::Physical) -> Option<u32> {
|
pub fn physical_to_scancode(physical: keyboard::key::Physical) -> Option<u32> {
|
||||||
let Some(physical_key) = (match physical {
|
let Some(physical_key) = (match physical {
|
||||||
keyboard::key::Physical::Code(code) => {
|
keyboard::key::Physical::Code(code) => {
|
||||||
|
|
|
||||||
309
winit/src/lib.rs
309
winit/src/lib.rs
|
|
@ -40,6 +40,12 @@ pub mod clipboard;
|
||||||
pub mod conversion;
|
pub mod conversion;
|
||||||
pub mod platform_specific;
|
pub mod platform_specific;
|
||||||
|
|
||||||
|
#[cfg(feature = "program")]
|
||||||
|
pub mod program;
|
||||||
|
|
||||||
|
#[cfg(feature = "system")]
|
||||||
|
pub mod system;
|
||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
mod proxy;
|
mod proxy;
|
||||||
mod window;
|
mod window;
|
||||||
|
|
@ -47,6 +53,7 @@ mod window;
|
||||||
pub use clipboard::Clipboard;
|
pub use clipboard::Clipboard;
|
||||||
pub use error::Error;
|
pub use error::Error;
|
||||||
pub use proxy::Proxy;
|
pub use proxy::Proxy;
|
||||||
|
use winit::dpi::LogicalSize;
|
||||||
use winit::dpi::PhysicalPosition;
|
use winit::dpi::PhysicalPosition;
|
||||||
use winit::dpi::PhysicalSize;
|
use winit::dpi::PhysicalSize;
|
||||||
|
|
||||||
|
|
@ -69,8 +76,6 @@ use crate::runtime::user_interface::{self, UserInterface};
|
||||||
use crate::runtime::{Action, Task};
|
use crate::runtime::{Action, Task};
|
||||||
|
|
||||||
use program::Program;
|
use program::Program;
|
||||||
#[cfg(feature = "program")]
|
|
||||||
pub use program::Program;
|
|
||||||
use window::WindowManager;
|
use window::WindowManager;
|
||||||
|
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
@ -93,10 +98,10 @@ where
|
||||||
|
|
||||||
let event_loop = EventLoop::new().expect("Create event loop");
|
let event_loop = EventLoop::new().expect("Create event loop");
|
||||||
|
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
let is_wayland =
|
let is_wayland =
|
||||||
winit::platform::wayland::EventLoopExtWayland::is_wayland(&event_loop);
|
winit::platform::wayland::EventLoopExtWayland::is_wayland(&event_loop);
|
||||||
#[cfg(not(wayland_platform))]
|
#[cfg(not(all(feature = "cctk", target_os = "linux")))]
|
||||||
let is_wayland = false;
|
let is_wayland = false;
|
||||||
|
|
||||||
// TODO this is new..
|
// TODO this is new..
|
||||||
|
|
@ -187,15 +192,11 @@ where
|
||||||
receiver: mpsc::UnboundedReceiver<Control>,
|
receiver: mpsc::UnboundedReceiver<Control>,
|
||||||
error: Option<Error>,
|
error: Option<Error>,
|
||||||
system_theme: Option<oneshot::Sender<theme::Mode>>,
|
system_theme: Option<oneshot::Sender<theme::Mode>>,
|
||||||
#[cfg(feature = "a11y")]
|
|
||||||
control_sender: mpsc::UnboundedSender<Control>,
|
control_sender: mpsc::UnboundedSender<Control>,
|
||||||
|
|
||||||
#[cfg(feature = "a11y")]
|
#[cfg(feature = "a11y")]
|
||||||
adapters: std::collections::HashMap<
|
adapters: std::collections::HashMap<window::Id, (u64, iced_accessibility::accesskit_winit::Adapter)>,
|
||||||
window::Id,
|
|
||||||
(u64, iced_accessibility::accesskit_winit::Adapter),
|
|
||||||
>,
|
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
is_booted: std::rc::Rc<std::cell::RefCell<bool>>,
|
is_booted: std::rc::Rc<std::cell::RefCell<bool>>,
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
|
@ -214,7 +215,6 @@ where
|
||||||
id: settings.id,
|
id: settings.id,
|
||||||
sender: event_sender,
|
sender: event_sender,
|
||||||
receiver: control_receiver,
|
receiver: control_receiver,
|
||||||
#[cfg(feature = "a11y")]
|
|
||||||
control_sender: control_sender.clone(),
|
control_sender: control_sender.clone(),
|
||||||
error: None,
|
error: None,
|
||||||
system_theme: Some(system_theme_sender),
|
system_theme: Some(system_theme_sender),
|
||||||
|
|
@ -263,7 +263,7 @@ where
|
||||||
| winit::event::WindowEvent::Moved(_)
|
| winit::event::WindowEvent::Moved(_)
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
{
|
{
|
||||||
if matches!(event, WindowEvent::RedrawRequested) {
|
if matches!(event, WindowEvent::RedrawRequested) {
|
||||||
for id in
|
for id in
|
||||||
|
|
@ -501,15 +501,9 @@ where
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
let window: Arc<
|
let window: Arc<dyn winit::window::Window + 'static> = Arc::from(window);
|
||||||
dyn winit::window::Window + 'static,
|
|
||||||
> = Arc::from(window);
|
|
||||||
#[cfg(feature = "a11y")]
|
#[cfg(feature = "a11y")]
|
||||||
self.init_adapter(
|
self.init_adapter(event_loop, id, window.clone());
|
||||||
event_loop,
|
|
||||||
id,
|
|
||||||
window.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
self.process_event(
|
self.process_event(
|
||||||
event_loop,
|
event_loop,
|
||||||
|
|
@ -578,10 +572,11 @@ where
|
||||||
.expect("Send event");
|
.expect("Send event");
|
||||||
}
|
}
|
||||||
Control::Winit(id, e) => {
|
Control::Winit(id, e) => {
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
{
|
{
|
||||||
if matches!(e, WindowEvent::RedrawRequested)
|
if matches!(e, WindowEvent::RedrawRequested)
|
||||||
{
|
{
|
||||||
|
|
||||||
for id in crate::subsurface_widget::subsurface_ids(id) {
|
for id in crate::subsurface_widget::subsurface_ids(id) {
|
||||||
_ = self.sender
|
_ = self.sender
|
||||||
.unbounded_send(Event::Winit(
|
.unbounded_send(Event::Winit(
|
||||||
|
|
@ -602,7 +597,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.sender
|
self.sender
|
||||||
.start_send(Event::Winit(id, e))
|
.start_send(Event::Winit(id, e))
|
||||||
.expect("Send event");
|
.expect("Send event");
|
||||||
|
|
@ -615,7 +610,7 @@ where
|
||||||
#[cfg(feature = "a11y")]
|
#[cfg(feature = "a11y")]
|
||||||
Control::InitAdapter(id, window) => {
|
Control::InitAdapter(id, window) => {
|
||||||
self.init_adapter(event_loop, id, window);
|
self.init_adapter(event_loop, id, window);
|
||||||
|
|
||||||
self.process_event(
|
self.process_event(
|
||||||
event_loop,
|
event_loop,
|
||||||
Some(Event::A11yAdapter(id)),
|
Some(Event::A11yAdapter(id)),
|
||||||
|
|
@ -623,8 +618,8 @@ where
|
||||||
}
|
}
|
||||||
#[cfg(feature = "a11y")]
|
#[cfg(feature = "a11y")]
|
||||||
Control::Cleanup(id) => {
|
Control::Cleanup(id) => {
|
||||||
_ = self.adapters.remove(&id);
|
_ = self.adapters.remove(&id);
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
break;
|
break;
|
||||||
|
|
@ -638,35 +633,34 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "a11y")]
|
#[cfg(feature = "a11y")]
|
||||||
fn init_adapter(
|
fn init_adapter(&mut self, event_loop: &(dyn winit::event_loop::ActiveEventLoop + 'static), id: core::window::Id, window: Arc<dyn winit::window::Window + 'static>) {
|
||||||
&mut self,
|
|
||||||
event_loop: &(dyn winit::event_loop::ActiveEventLoop + 'static),
|
|
||||||
id: core::window::Id,
|
|
||||||
window: Arc<dyn winit::window::Window + 'static>,
|
|
||||||
) {
|
|
||||||
use crate::a11y::*;
|
use crate::a11y::*;
|
||||||
use iced_accessibility::accesskit::{
|
use iced_accessibility::accesskit::{
|
||||||
ActivationHandler, Node, NodeId, Role, Tree, TreeUpdate,
|
ActivationHandler, Node, NodeId, Role,
|
||||||
|
Tree, TreeUpdate,
|
||||||
};
|
};
|
||||||
use iced_accessibility::accesskit_winit::Adapter;
|
use iced_accessibility::accesskit_winit::Adapter;
|
||||||
|
|
||||||
let node_id = iced_runtime::core::id::window_node_id();
|
let node_id =
|
||||||
|
iced_runtime::core::id::window_node_id();
|
||||||
let activation_handler = WinitActivationHandler {
|
|
||||||
proxy: self.control_sender.clone(),
|
let activation_handler =
|
||||||
title: String::new(),
|
WinitActivationHandler {
|
||||||
};
|
proxy: self.control_sender.clone(),
|
||||||
|
title: String::new(),
|
||||||
|
};
|
||||||
|
|
||||||
let action_handler = WinitActionHandler {
|
let action_handler = WinitActionHandler {
|
||||||
id,
|
id,
|
||||||
proxy: self.control_sender.clone(),
|
proxy: self.control_sender.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let deactivation_handler = WinitDeactivationHandler {
|
let deactivation_handler =
|
||||||
proxy: self.control_sender.clone(),
|
WinitDeactivationHandler {
|
||||||
};
|
proxy: self.control_sender.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
_ = self.adapters.insert(
|
_ = self.adapters.insert(
|
||||||
id,
|
id,
|
||||||
(
|
(
|
||||||
|
|
@ -680,6 +674,8 @@ where
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -774,7 +770,7 @@ async fn run_instance<P>(
|
||||||
|
|
||||||
let mut platform_specific_handler =
|
let mut platform_specific_handler =
|
||||||
crate::platform_specific::PlatformSpecific::default();
|
crate::platform_specific::PlatformSpecific::default();
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
if is_wayland {
|
if is_wayland {
|
||||||
platform_specific_handler = platform_specific_handler.with_wayland(
|
platform_specific_handler = platform_specific_handler.with_wayland(
|
||||||
control_sender.clone(),
|
control_sender.clone(),
|
||||||
|
|
@ -842,30 +838,6 @@ async fn run_instance<P>(
|
||||||
|
|
||||||
log::info!("System theme: {system_theme:?}");
|
log::info!("System theme: {system_theme:?}");
|
||||||
|
|
||||||
macro_rules! run_action {
|
|
||||||
($action:expr) => {
|
|
||||||
run_action(
|
|
||||||
$action,
|
|
||||||
&program,
|
|
||||||
&mut runtime,
|
|
||||||
&mut compositor,
|
|
||||||
&mut events,
|
|
||||||
&mut messages,
|
|
||||||
&mut clipboard,
|
|
||||||
&mut control_sender,
|
|
||||||
&mut user_interfaces,
|
|
||||||
&mut window_manager,
|
|
||||||
&mut ui_caches,
|
|
||||||
&mut is_window_opening,
|
|
||||||
&mut system_theme,
|
|
||||||
&mut platform_specific_handler,
|
|
||||||
&mut dnd_surface,
|
|
||||||
&mut dnd_surface_id,
|
|
||||||
is_daemon,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
'next_event: loop {
|
'next_event: loop {
|
||||||
// Empty the queue if possible
|
// Empty the queue if possible
|
||||||
let event = if let Ok(event) = event_receiver.try_recv() {
|
let event = if let Ok(event) = event_receiver.try_recv() {
|
||||||
|
|
@ -885,36 +857,35 @@ async fn run_instance<P>(
|
||||||
exit_on_close_request,
|
exit_on_close_request,
|
||||||
make_visible,
|
make_visible,
|
||||||
on_open,
|
on_open,
|
||||||
resize_border,
|
resize_border
|
||||||
} => {
|
} => {
|
||||||
#[cfg(wayland_platform)]
|
|
||||||
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
platform_specific_handler.send_wayland(
|
platform_specific_handler.send_wayland(
|
||||||
platform_specific::Action::TrackWindow(window.clone(), id),
|
platform_specific::Action::TrackWindow(window.clone(), id),
|
||||||
);
|
);
|
||||||
if compositor.is_none() {
|
match create_compositor::<P>(
|
||||||
match create_compositor::<P>(
|
window.clone(),
|
||||||
window.clone(),
|
CreateCompositor {
|
||||||
CreateCompositor {
|
proxy: &proxy,
|
||||||
proxy: &proxy,
|
runtime: &mut runtime,
|
||||||
runtime: &mut runtime,
|
display_handle: &display_handle,
|
||||||
display_handle: &display_handle,
|
graphics_settings: &graphics_settings,
|
||||||
graphics_settings: &graphics_settings,
|
default_fonts: &default_fonts,
|
||||||
default_fonts: &default_fonts,
|
},
|
||||||
},
|
)
|
||||||
)
|
.await
|
||||||
.await
|
{
|
||||||
{
|
Ok(comp) => {
|
||||||
Ok(comp) => {
|
compositor = Some(comp);
|
||||||
compositor = Some(comp);
|
}
|
||||||
}
|
Err(error) => {
|
||||||
Err(error) => {
|
control_sender
|
||||||
control_sender
|
.start_send(Control::Crash(
|
||||||
.start_send(Control::Crash(
|
Error::GraphicsCreationFailed(error),
|
||||||
Error::GraphicsCreationFailed(error),
|
))
|
||||||
))
|
.expect("Send control message");
|
||||||
.expect("Send control message");
|
continue;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -942,7 +913,7 @@ async fn run_instance<P>(
|
||||||
compositor,
|
compositor,
|
||||||
exit_on_close_request,
|
exit_on_close_request,
|
||||||
system_theme,
|
system_theme,
|
||||||
resize_border,
|
resize_border
|
||||||
);
|
);
|
||||||
|
|
||||||
window.raw.set_theme(conversion::window_theme(
|
window.raw.set_theme(conversion::window_theme(
|
||||||
|
|
@ -1028,7 +999,22 @@ async fn run_instance<P>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Event::UserEvent(action) => {
|
Event::UserEvent(action) => {
|
||||||
let exited = run_action!(action);
|
let exited = run_action(
|
||||||
|
action,
|
||||||
|
&program,
|
||||||
|
&mut runtime,
|
||||||
|
&mut compositor,
|
||||||
|
&mut events,
|
||||||
|
&mut messages,
|
||||||
|
&mut clipboard,
|
||||||
|
&mut control_sender,
|
||||||
|
&mut user_interfaces,
|
||||||
|
&mut window_manager,
|
||||||
|
&mut ui_caches,
|
||||||
|
&mut is_window_opening,
|
||||||
|
&mut system_theme,
|
||||||
|
&mut platform_specific_handler,
|
||||||
|
);
|
||||||
if exited {
|
if exited {
|
||||||
runtime.track(None.into_iter());
|
runtime.track(None.into_iter());
|
||||||
}
|
}
|
||||||
|
|
@ -1039,7 +1025,7 @@ async fn run_instance<P>(
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some((id, mut window)) =
|
let Some((id, mut window)) =
|
||||||
window_manager.get_mut_alias(window_id)
|
window_manager.get_mut_alias(window_id)
|
||||||
else {
|
else {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -1054,12 +1040,12 @@ async fn run_instance<P>(
|
||||||
))
|
))
|
||||||
.expect("Send redraw event");
|
.expect("Send redraw event");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// XX must force update to corner radius before the surface is committed.
|
// XX must force update to corner radius before the surface is committed.
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
if (window.surface_version != window.state.surface_version()
|
if (window.surface_version != window.state.surface_version()
|
||||||
|| window.logical_size() != window.state.logical_size())
|
|| window.logical_size() != window.state.logical_size()
|
||||||
&& !crate::subsurface_widget::is_subsurface(window_id)
|
) && !crate::subsurface_widget::is_subsurface(window_id)
|
||||||
{
|
{
|
||||||
platform_specific_handler.send_wayland(
|
platform_specific_handler.send_wayland(
|
||||||
platform_specific::Action::ResizeWindow(id),
|
platform_specific::Action::ResizeWindow(id),
|
||||||
|
|
@ -1170,7 +1156,22 @@ async fn run_instance<P>(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = run_action!(action);
|
_ = run_action(
|
||||||
|
action,
|
||||||
|
&program,
|
||||||
|
&mut runtime,
|
||||||
|
&mut compositor,
|
||||||
|
&mut events,
|
||||||
|
&mut messages,
|
||||||
|
&mut clipboard,
|
||||||
|
&mut control_sender,
|
||||||
|
&mut user_interfaces,
|
||||||
|
&mut window_manager,
|
||||||
|
&mut ui_caches,
|
||||||
|
&mut is_window_opening,
|
||||||
|
&mut system_theme,
|
||||||
|
&mut platform_specific_handler,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (window_id, window) in window_manager.iter_mut() {
|
for (window_id, window) in window_manager.iter_mut() {
|
||||||
|
|
@ -1325,22 +1326,13 @@ async fn run_instance<P>(
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
// Initiates a drag resize window state when found.
|
// Initiates a drag resize window state when found.
|
||||||
if let Some(func) = window.drag_resize_window_func.as_mut() {
|
if let Some(func) =
|
||||||
|
window.drag_resize_window_func.as_mut()
|
||||||
|
{
|
||||||
if func(window.raw.as_ref(), &event) {
|
if func(window.raw.as_ref(), &event) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// to avoid race with the reported modifier state from the popup of
|
|
||||||
// this window, don't empty the modifiers, on focus loss
|
|
||||||
#[cfg(wayland_platform)]
|
|
||||||
if let winit::event::WindowEvent::ModifiersChanged(modifiers) =
|
|
||||||
&event
|
|
||||||
&& modifiers.state().is_empty()
|
|
||||||
&& platform_specific_handler.has_popup(id)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
winit::event::WindowEvent::SurfaceResized(_) => {
|
winit::event::WindowEvent::SurfaceResized(_) => {
|
||||||
window.raw.request_redraw();
|
window.raw.request_redraw();
|
||||||
|
|
@ -1362,9 +1354,22 @@ async fn run_instance<P>(
|
||||||
if matches!(event, winit::event::WindowEvent::CloseRequested)
|
if matches!(event, winit::event::WindowEvent::CloseRequested)
|
||||||
&& window.exit_on_close_request
|
&& window.exit_on_close_request
|
||||||
{
|
{
|
||||||
_ = run_action!(Action::Window(
|
_ = run_action(
|
||||||
runtime::window::Action::Close(id)
|
Action::Window(runtime::window::Action::Close(id)),
|
||||||
));
|
&program,
|
||||||
|
&mut runtime,
|
||||||
|
&mut compositor,
|
||||||
|
&mut events,
|
||||||
|
&mut messages,
|
||||||
|
&mut clipboard,
|
||||||
|
&mut control_sender,
|
||||||
|
&mut user_interfaces,
|
||||||
|
&mut window_manager,
|
||||||
|
&mut ui_caches,
|
||||||
|
&mut is_window_opening,
|
||||||
|
&mut system_theme,
|
||||||
|
&mut platform_specific_handler,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
window.state.update(&program, window.raw.as_ref(), &event);
|
window.state.update(&program, window.raw.as_ref(), &event);
|
||||||
|
|
||||||
|
|
@ -1406,7 +1411,8 @@ async fn run_instance<P>(
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
#[cfg(wayland_platform)]
|
let no_window_events = window_events.is_empty();
|
||||||
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
window_events.push(core::Event::PlatformSpecific(
|
window_events.push(core::Event::PlatformSpecific(
|
||||||
core::event::PlatformSpecific::Wayland(
|
core::event::PlatformSpecific::Wayland(
|
||||||
core::event::wayland::Event::RequestResize,
|
core::event::wayland::Event::RequestResize,
|
||||||
|
|
@ -1423,7 +1429,8 @@ async fn run_instance<P>(
|
||||||
&mut clipboard,
|
&mut clipboard,
|
||||||
&mut messages,
|
&mut messages,
|
||||||
);
|
);
|
||||||
let mut needs_redraw = !messages.is_empty();
|
let mut needs_redraw =
|
||||||
|
!no_window_events || !messages.is_empty();
|
||||||
if let Some(requested_size) =
|
if let Some(requested_size) =
|
||||||
clipboard.requested_logical_size.lock().unwrap().take()
|
clipboard.requested_logical_size.lock().unwrap().take()
|
||||||
{
|
{
|
||||||
|
|
@ -1479,17 +1486,8 @@ async fn run_instance<P>(
|
||||||
mouse_interaction,
|
mouse_interaction,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let mouse_changed =
|
|
||||||
window.mouse_interaction != mouse_interaction;
|
|
||||||
|
|
||||||
window.update_mouse(mouse_interaction);
|
window.update_mouse(mouse_interaction);
|
||||||
|
|
||||||
if mouse_changed {
|
|
||||||
window.request_redraw(
|
|
||||||
core::window::RedrawRequest::NextFrame,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "unconditional-rendering"))]
|
#[cfg(not(feature = "unconditional-rendering"))]
|
||||||
window.request_redraw(_redraw_request);
|
window.request_redraw(_redraw_request);
|
||||||
}
|
}
|
||||||
|
|
@ -1549,7 +1547,22 @@ async fn run_instance<P>(
|
||||||
));
|
));
|
||||||
|
|
||||||
for action in actions {
|
for action in actions {
|
||||||
let exited = run_action!(action);
|
let exited = run_action(
|
||||||
|
action,
|
||||||
|
&program,
|
||||||
|
&mut runtime,
|
||||||
|
&mut compositor,
|
||||||
|
&mut events,
|
||||||
|
&mut messages,
|
||||||
|
&mut clipboard,
|
||||||
|
&mut control_sender,
|
||||||
|
&mut user_interfaces,
|
||||||
|
&mut window_manager,
|
||||||
|
&mut ui_caches,
|
||||||
|
&mut is_window_opening,
|
||||||
|
&mut system_theme,
|
||||||
|
&mut platform_specific_handler,
|
||||||
|
);
|
||||||
if exited {
|
if exited {
|
||||||
runtime.track(None.into_iter());
|
runtime.track(None.into_iter());
|
||||||
}
|
}
|
||||||
|
|
@ -1759,6 +1772,7 @@ async fn run_instance<P>(
|
||||||
};
|
};
|
||||||
|
|
||||||
// search windows for widget with operation
|
// search windows for widget with operation
|
||||||
|
|
||||||
if result.is_none() {
|
if result.is_none() {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
"start_dnd: widget {:?} not found; drag will fail",
|
"start_dnd: widget {:?} not found; drag will fail",
|
||||||
|
|
@ -2092,11 +2106,6 @@ fn run_action<'a, P, C>(
|
||||||
is_window_opening: &mut bool,
|
is_window_opening: &mut bool,
|
||||||
system_theme: &mut theme::Mode,
|
system_theme: &mut theme::Mode,
|
||||||
platform_specific: &mut crate::platform_specific::PlatformSpecific,
|
platform_specific: &mut crate::platform_specific::PlatformSpecific,
|
||||||
dnd_surface: &mut Option<
|
|
||||||
Arc<Box<dyn HasWindowHandle + Send + Sync + 'static>>,
|
|
||||||
>,
|
|
||||||
dnd_surface_id: &mut Option<window::Id>,
|
|
||||||
is_daemon: bool,
|
|
||||||
) -> bool
|
) -> bool
|
||||||
where
|
where
|
||||||
P: Program,
|
P: Program,
|
||||||
|
|
@ -2139,7 +2148,7 @@ where
|
||||||
on_open: channel,
|
on_open: channel,
|
||||||
})
|
})
|
||||||
.expect("Send control action");
|
.expect("Send control action");
|
||||||
|
|
||||||
*is_window_opening = true;
|
*is_window_opening = true;
|
||||||
}
|
}
|
||||||
window::Action::Close(id) => {
|
window::Action::Close(id) => {
|
||||||
|
|
@ -2150,7 +2159,7 @@ where
|
||||||
_ = control_sender.start_send(Control::Cleanup(id)).ok();
|
_ = control_sender.start_send(Control::Cleanup(id)).ok();
|
||||||
}
|
}
|
||||||
let proxy = clipboard.proxy();
|
let proxy = clipboard.proxy();
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
platform_specific
|
platform_specific
|
||||||
.send_wayland(platform_specific::Action::RemoveWindow(id));
|
.send_wayland(platform_specific::Action::RemoveWindow(id));
|
||||||
if let Some(window) = window_manager.remove(id) {
|
if let Some(window) = window_manager.remove(id) {
|
||||||
|
|
@ -2181,7 +2190,7 @@ where
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if window_manager.is_empty() && !is_daemon {
|
if window_manager.is_empty() {
|
||||||
*compositor = None;
|
*compositor = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2534,8 +2543,6 @@ where
|
||||||
while let Some(mut operation) = current_operation.take() {
|
while let Some(mut operation) = current_operation.take() {
|
||||||
for (id, ui) in interfaces.iter_mut() {
|
for (id, ui) in interfaces.iter_mut() {
|
||||||
if let Some(window) = window_manager.get_mut(*id) {
|
if let Some(window) = window_manager.get_mut(*id) {
|
||||||
operation.as_mut().set_window_id(*id);
|
|
||||||
|
|
||||||
ui.operate(&window.renderer, operation.as_mut());
|
ui.operate(&window.renderer, operation.as_mut());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2613,10 +2620,6 @@ where
|
||||||
}
|
}
|
||||||
iced_runtime::dnd::DndAction::EndDnd => {
|
iced_runtime::dnd::DndAction::EndDnd => {
|
||||||
clipboard.end_dnd();
|
clipboard.end_dnd();
|
||||||
*dnd_surface = None;
|
|
||||||
if let Some(id) = dnd_surface_id.take() {
|
|
||||||
platform_specific.retain_subsurfaces(|other| other != id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
iced_runtime::dnd::DndAction::PeekDnd(m, channel) => {
|
iced_runtime::dnd::DndAction::PeekDnd(m, channel) => {
|
||||||
let data = clipboard.peek_dnd(m);
|
let data = clipboard.peek_dnd(m);
|
||||||
|
|
@ -2654,10 +2657,10 @@ where
|
||||||
.and_then(|window| theme::Base::palette(window.state.theme()))
|
.and_then(|window| theme::Base::palette(window.state.theme()))
|
||||||
});
|
});
|
||||||
|
|
||||||
window_manager
|
cached_user_interfaces
|
||||||
.iter_mut()
|
.drain()
|
||||||
.filter_map(|(id, window)| {
|
.filter_map(|(id, cache)| {
|
||||||
let cache = cached_user_interfaces.remove(&id)?;
|
let window = window_manager.get_mut(id)?;
|
||||||
|
|
||||||
Some((
|
Some((
|
||||||
id,
|
id,
|
||||||
|
|
@ -2730,5 +2733,7 @@ fn system_information(
|
||||||
graphics_backend: graphics.backend,
|
graphics_backend: graphics.backend,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "program")]
|
||||||
|
pub use program::Program;
|
||||||
|
|
||||||
pub use platform_specific::*;
|
pub use platform_specific::*;
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,24 @@
|
||||||
//! Wayland specific shell
|
//! Wayland specific shell
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::{borrow::Cow, collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
use iced_graphics::compositor;
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
|
use cctk::sctk::reexports::client::Connection;
|
||||||
|
use iced_graphics::{Compositor, compositor};
|
||||||
use iced_runtime::{
|
use iced_runtime::{
|
||||||
core::{Vector, window},
|
core::{Vector, window},
|
||||||
user_interface,
|
platform_specific, user_interface,
|
||||||
};
|
};
|
||||||
use winit::raw_window_handle::HasWindowHandle;
|
use winit::raw_window_handle::HasWindowHandle;
|
||||||
|
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
pub mod wayland;
|
pub mod wayland;
|
||||||
|
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
pub use wayland::*;
|
pub use wayland::*;
|
||||||
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
|
use wayland_backend::client::Backend;
|
||||||
|
|
||||||
use crate::{CreateCompositor, Program, WindowManager};
|
use crate::{CreateCompositor, Program, WindowManager};
|
||||||
|
|
||||||
|
|
@ -31,7 +35,7 @@ pub type UserInterfaces<'a, P> = HashMap<
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
Wayland(sctk_event::SctkEvent),
|
Wayland(sctk_event::SctkEvent),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -57,7 +61,7 @@ impl SurfaceIdWrapper {
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct PlatformSpecific {
|
pub struct PlatformSpecific {
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
wayland: WaylandSpecific,
|
wayland: WaylandSpecific,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,30 +71,25 @@ impl PlatformSpecific {
|
||||||
action: iced_runtime::platform_specific::Action,
|
action: iced_runtime::platform_specific::Action,
|
||||||
) {
|
) {
|
||||||
match action {
|
match action {
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
iced_runtime::platform_specific::Action::Wayland(a) => {
|
iced_runtime::platform_specific::Action::Wayland(a) => {
|
||||||
self.send_wayland(wayland::Action::Action(a));
|
self.send_wayland(wayland::Action::Action(a));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(wayland_platform)]
|
|
||||||
pub(crate) fn has_popup(&self, toplevel: window::Id) -> bool {
|
|
||||||
self.wayland.has_popup(toplevel)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn retain_subsurfaces<F: Fn(window::Id) -> bool>(
|
pub(crate) fn retain_subsurfaces<F: Fn(window::Id) -> bool>(
|
||||||
&mut self,
|
&mut self,
|
||||||
keep: F,
|
keep: F,
|
||||||
) {
|
) {
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
{
|
{
|
||||||
self.wayland.retain_subsurfaces(keep);
|
self.wayland.retain_subsurfaces(keep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn clear_subsurface_list(&mut self) {
|
pub(crate) fn clear_subsurface_list(&mut self) {
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
{
|
{
|
||||||
self.wayland.clear_subsurface_list();
|
self.wayland.clear_subsurface_list();
|
||||||
}
|
}
|
||||||
|
|
@ -101,7 +100,7 @@ impl PlatformSpecific {
|
||||||
id: window::Id,
|
id: window::Id,
|
||||||
window: &dyn HasWindowHandle,
|
window: &dyn HasWindowHandle,
|
||||||
) {
|
) {
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
{
|
{
|
||||||
use cctk::sctk::reexports::client::{
|
use cctk::sctk::reexports::client::{
|
||||||
Proxy, protocol::wl_surface::WlSurface,
|
Proxy, protocol::wl_surface::WlSurface,
|
||||||
|
|
@ -152,14 +151,11 @@ impl PlatformSpecific {
|
||||||
pub(crate) fn create_surface(
|
pub(crate) fn create_surface(
|
||||||
&mut self,
|
&mut self,
|
||||||
) -> Option<Box<dyn HasWindowHandle + Send + Sync + 'static>> {
|
) -> Option<Box<dyn HasWindowHandle + Send + Sync + 'static>> {
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
{
|
{
|
||||||
self.wayland.create_surface()
|
return self.wayland.create_surface();
|
||||||
}
|
|
||||||
#[cfg(not(wayland_platform))]
|
|
||||||
{
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn update_surface_shm(
|
pub(crate) fn update_surface_shm(
|
||||||
|
|
@ -171,7 +167,7 @@ impl PlatformSpecific {
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
offset: Vector,
|
offset: Vector,
|
||||||
) {
|
) {
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
{
|
{
|
||||||
return self.wayland.update_surface_shm(
|
return self.wayland.update_surface_shm(
|
||||||
surface, width, height, scale, data, offset,
|
surface, width, height, scale, data, offset,
|
||||||
|
|
@ -199,7 +195,7 @@ pub(crate) async fn handle_event<'a, 'b, P>(
|
||||||
P: Program,
|
P: Program,
|
||||||
{
|
{
|
||||||
match e {
|
match e {
|
||||||
#[cfg(wayland_platform)]
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
Event::Wayland(e) => {
|
Event::Wayland(e) => {
|
||||||
platform_specific
|
platform_specific
|
||||||
.wayland
|
.wayland
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::core::window::Id as SurfaceId;
|
use crate::core::window::Id as SurfaceId;
|
||||||
use iced_runtime::{
|
use iced_runtime::{
|
||||||
self, Action, Task,
|
self,
|
||||||
platform_specific::{self, wayland},
|
platform_specific::{self, wayland},
|
||||||
task,
|
task, Action, Task,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn request_token(
|
pub fn request_token(
|
||||||
|
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
use iced_futures::core::window;
|
|
||||||
use iced_runtime::{
|
|
||||||
self, Action, Task,
|
|
||||||
platform_specific::{self, wayland},
|
|
||||||
task,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn blur(
|
|
||||||
id: window::Id,
|
|
||||||
blur: Option<Vec<iced_runtime::core::Rectangle>>,
|
|
||||||
) -> Task<()> {
|
|
||||||
task::oneshot(|_| {
|
|
||||||
Action::PlatformSpecific(platform_specific::Action::Wayland(
|
|
||||||
wayland::Action::BlurSurface(id, blur),
|
|
||||||
))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
use iced_futures::core::window;
|
use iced_futures::core::window;
|
||||||
use iced_runtime::{
|
use iced_runtime::{
|
||||||
self, Action, Task,
|
self,
|
||||||
platform_specific::{
|
platform_specific::{
|
||||||
self,
|
self,
|
||||||
wayland::{self, CornerRadius},
|
wayland::{self, CornerRadius},
|
||||||
},
|
},
|
||||||
task,
|
task, Action, Task,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn corner_radius(
|
pub fn corner_radius(
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use iced_runtime::{
|
use iced_runtime::{
|
||||||
self, Action, Task,
|
self,
|
||||||
platform_specific::{self, wayland},
|
platform_specific::{self, wayland},
|
||||||
task,
|
task, Action, Task,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn inhibit_shortcuts(inhibit: bool) -> Task<()> {
|
pub fn inhibit_shortcuts(inhibit: bool) -> Task<()> {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
//! Interact with the window of your application.
|
//! Interact with the window of your application.
|
||||||
|
|
||||||
use crate::core::Rectangle;
|
|
||||||
use crate::core::window::Id as SurfaceId;
|
use crate::core::window::Id as SurfaceId;
|
||||||
use iced_runtime::{
|
use iced_runtime::{
|
||||||
self, Action, Task,
|
self,
|
||||||
platform_specific::{
|
platform_specific::{
|
||||||
self,
|
self,
|
||||||
wayland::{
|
wayland::{
|
||||||
|
|
@ -11,7 +10,7 @@ use iced_runtime::{
|
||||||
layer_surface::{IcedMargin, SctkLayerSurfaceSettings},
|
layer_surface::{IcedMargin, SctkLayerSurfaceSettings},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
task,
|
task, Action, Task,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use cctk::sctk::shell::wlr_layer::{Anchor, KeyboardInteractivity, Layer};
|
pub use cctk::sctk::shell::wlr_layer::{Anchor, KeyboardInteractivity, Layer};
|
||||||
|
|
@ -115,37 +114,3 @@ pub fn set_layer<Message>(id: SurfaceId, layer: Layer) -> Task<Message> {
|
||||||
)),
|
)),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_padding<Message>(
|
|
||||||
id: SurfaceId,
|
|
||||||
padding: IcedMargin,
|
|
||||||
) -> Task<Message> {
|
|
||||||
task::effect(Action::PlatformSpecific(
|
|
||||||
platform_specific::Action::Wayland(wayland::Action::LayerSurface(
|
|
||||||
wayland::layer_surface::Action::Padding { id, padding },
|
|
||||||
)),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <https://wayland.app/protocols/wayland#wl_surface:request:set_input_region>
|
|
||||||
pub fn set_input_zone<Message>(
|
|
||||||
id: SurfaceId,
|
|
||||||
zone: Option<Vec<Rectangle>>,
|
|
||||||
) -> Task<Message> {
|
|
||||||
task::effect(Action::PlatformSpecific(
|
|
||||||
platform_specific::Action::Wayland(wayland::Action::LayerSurface(
|
|
||||||
wayland::layer_surface::Action::InputZone { id, zone },
|
|
||||||
)),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_show_on_lock<Message>(
|
|
||||||
id: SurfaceId,
|
|
||||||
value: bool,
|
|
||||||
) -> Task<Message> {
|
|
||||||
task::effect(Action::PlatformSpecific(
|
|
||||||
platform_specific::Action::Wayland(wayland::Action::LayerSurface(
|
|
||||||
wayland::layer_surface::Action::ShowOnLock { id, value },
|
|
||||||
)),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
//! Interact with the wayland objects of your application.
|
//! Interact with the wayland objects of your application.
|
||||||
|
|
||||||
pub mod activation;
|
pub mod activation;
|
||||||
pub mod blur;
|
|
||||||
pub mod corner_radius;
|
pub mod corner_radius;
|
||||||
pub mod keyboard_shortcuts_inhibit;
|
pub mod keyboard_shortcuts_inhibit;
|
||||||
pub mod layer_surface;
|
pub mod layer_surface;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
use iced_futures::core::window::Id;
|
use iced_futures::core::window::Id;
|
||||||
use iced_runtime::{
|
use iced_runtime::{
|
||||||
Action, Task,
|
|
||||||
platform_specific::{self, wayland},
|
platform_specific::{self, wayland},
|
||||||
task,
|
task, Action, Task,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Request subscription for overlap notification events on the surface
|
/// Request subscription for overlap notification events on the surface
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,12 @@
|
||||||
//! Interact with the popups of your application.
|
//! Interact with the popups of your application.
|
||||||
use crate::core::window::Id as SurfaceId;
|
use crate::core::window::Id as SurfaceId;
|
||||||
use iced_runtime::{
|
use iced_runtime::{
|
||||||
self, Action, Task,
|
self,
|
||||||
platform_specific::{
|
platform_specific::{
|
||||||
self,
|
self,
|
||||||
wayland::{
|
wayland::{self, popup::SctkPopupSettings},
|
||||||
self,
|
|
||||||
popup::{SctkPopupSettings, SctkPositioner},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
task,
|
task, Action, Task,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <https://wayland.app/protocols/wlr-layer-shell-unstable-v1#zwlr_layer_surface_v1:request:get_popup>
|
/// <https://wayland.app/protocols/wlr-layer-shell-unstable-v1#zwlr_layer_surface_v1:request:get_popup>
|
||||||
|
|
@ -35,18 +32,6 @@ pub fn set_size<Message>(
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <https://wayland.app/protocols/xdg-shell#xdg_popup:request:reposition>
|
|
||||||
pub fn reposition<Message>(
|
|
||||||
id: SurfaceId,
|
|
||||||
positioner: SctkPositioner,
|
|
||||||
) -> Task<Message> {
|
|
||||||
task::effect(Action::PlatformSpecific(
|
|
||||||
platform_specific::Action::Wayland(wayland::Action::Popup(
|
|
||||||
wayland::popup::Action::Reposition { id, positioner },
|
|
||||||
)),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <https://wayland.app/protocols/xdg-shell#xdg_popup:request:destroy>
|
/// <https://wayland.app/protocols/xdg-shell#xdg_popup:request:destroy>
|
||||||
pub fn destroy_popup<Message>(id: SurfaceId) -> Task<Message> {
|
pub fn destroy_popup<Message>(id: SurfaceId) -> Task<Message> {
|
||||||
task::effect(Action::PlatformSpecific(
|
task::effect(Action::PlatformSpecific(
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
use crate::core::window::Id as SurfaceId;
|
use crate::core::window::Id as SurfaceId;
|
||||||
use cctk::sctk::reexports::client::protocol::wl_output::WlOutput;
|
use cctk::sctk::reexports::client::protocol::wl_output::WlOutput;
|
||||||
use iced_runtime::{
|
use iced_runtime::{
|
||||||
self, Action, Task,
|
self,
|
||||||
platform_specific::{self, wayland},
|
platform_specific::{self, wayland},
|
||||||
task,
|
task, Action, Task,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn lock<Message>() -> Task<Message> {
|
pub fn lock<Message>() -> Task<Message> {
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue