Compare commits
57 commits
de1247123a
...
1a57634add
| Author | SHA1 | Date | |
|---|---|---|---|
| 1a57634add | |||
| 362b4abb15 | |||
| cf7828fb70 | |||
| 3982e04aa2 | |||
| 4e2e58e03d | |||
|
|
b90b1413a6 | ||
|
|
ce5ea665e2 | ||
| f1f42f2bff | |||
| 702b6359a5 | |||
| a50a29b390 | |||
|
|
c781ff6199 | ||
|
|
633ed14411 | ||
|
|
da9d15b2e8 | ||
|
|
8e94441efa | ||
|
|
46781102fe | ||
|
|
5cc9f8d621 | ||
|
|
6be49cb530 | ||
|
|
b8b0804140 | ||
|
|
6521f7fa20 | ||
|
|
f6fa506f6c | ||
|
|
e0c7d353aa | ||
|
|
a63d57261f | ||
|
|
1bcba7e30c | ||
|
|
9f41055861 | ||
|
|
7afd9ee0f8 | ||
|
|
62b90179a2 | ||
|
|
c3c8fcf52c | ||
|
|
f725961c87 | ||
|
|
dcf4cf0194 | ||
|
|
5013f63956 | ||
|
|
ebb3b125bf | ||
|
|
590e093086 | ||
|
|
9c748f1656 | ||
|
|
dfcd989947 | ||
|
|
0094b347d4 | ||
|
|
a7d71d9ace | ||
|
|
6105d8bdef | ||
|
|
98fb6a0fba | ||
|
|
b007caba44 | ||
|
|
a7ca336eb4 | ||
|
|
2e1e4bd6e0 | ||
|
|
9ae155b8e2 | ||
|
|
346faf2e79 | ||
|
|
251254ad51 | ||
|
|
347d91f7ea | ||
|
|
1444d5c43e | ||
|
|
f752bba18f | ||
|
|
d7595fab95 | ||
|
|
e000c71ff2 | ||
|
|
3928e0d385 | ||
|
|
90d2c418a7 | ||
|
|
aa2a870c35 | ||
|
|
8f10a3bd34 | ||
|
|
64b97077b7 | ||
|
|
13b8d3eab6 | ||
|
|
1bf1e33317 | ||
|
|
d2b2715835 |
90 changed files with 2236 additions and 990 deletions
772
Cargo.lock
generated
772
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
55
Cargo.toml
55
Cargo.toml
|
|
@ -120,7 +120,7 @@ winit = [
|
||||||
]
|
]
|
||||||
wayland = ["iced_renderer/wayland", "iced_winit/wayland"]
|
wayland = ["iced_renderer/wayland", "iced_winit/wayland"]
|
||||||
cctk = ["iced_winit/cctk", "iced_widget/cctk", "iced_core/cctk", "wayland"]
|
cctk = ["iced_winit/cctk", "iced_widget/cctk", "iced_core/cctk", "wayland"]
|
||||||
# Enables the sctk shell.
|
# Enables the sctk shell.
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -202,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.89"
|
rust-version = "1.92"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
iced = { version = "0.14.0", path = "." }
|
iced = { version = "0.14.0", path = "." }
|
||||||
|
|
@ -238,10 +238,9 @@ 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 = { git = "https://github.com/pop-os/cosmic-text.git" }
|
cosmic-text = { path = "../../cosmic-text" }
|
||||||
# cosmic-text = "0.10"
|
cryoglyph = { package = "cryoglyph", path = "../../glyphon" }
|
||||||
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"
|
||||||
|
|
@ -275,8 +274,11 @@ tiny-skia = { version = "0.11", default-features = false, features = [
|
||||||
"std",
|
"std",
|
||||||
"simd",
|
"simd",
|
||||||
] }
|
] }
|
||||||
cctk = { git = "https://github.com/pop-os/cosmic-protocols", package = "cosmic-client-toolkit", rev = "160b086" }
|
cctk = { path = "../../cosmic-protocols/client-toolkit", package = "cosmic-client-toolkit" }
|
||||||
softbuffer = { git = "https://github.com/pop-os/softbuffer", tag = "cosmic-4.0" }
|
# Yoda: default-features=false drops softbuffer's x11 / x11-dlopen (pulled
|
||||||
|
# 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"
|
||||||
|
|
@ -288,7 +290,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 = "27.0", default-features = false, features = [
|
wgpu = { version = "28.0", default-features = false, features = [
|
||||||
"std",
|
"std",
|
||||||
"wgsl",
|
"wgsl",
|
||||||
] }
|
] }
|
||||||
|
|
@ -302,13 +304,40 @@ wayland-client = { version = "0.31.5" }
|
||||||
winapi = "0.3"
|
winapi = "0.3"
|
||||||
# window_clipboard = "0.4.1"
|
# window_clipboard = "0.4.1"
|
||||||
|
|
||||||
window_clipboard = { git = "https://github.com/pop-os/window_clipboard.git", tag = "sctk-0.20" }
|
# Yoda: window_clipboard pinned to our public fork that has x11 gated behind
|
||||||
dnd = { git = "https://github.com/pop-os/window_clipboard.git", tag = "sctk-0.20" }
|
# an opt-in feature. default-features=false + features=["wayland"] drops
|
||||||
mime = { git = "https://github.com/pop-os/window_clipboard.git", tag = "sctk-0.20" }
|
# clipboard_x11 + x11rb from the build. Branch yoda-x11-optional on
|
||||||
winit = { git = "https://github.com/pop-os/winit.git", tag = "cosmic-0.14" }
|
# https://forge.aditua.com/leyoda/window_clipboard.
|
||||||
winit-core = { git = "https://github.com/pop-os/winit.git", tag = "cosmic-0.14" }
|
window_clipboard = { path = "../../window_clipboard", default-features = false, features = ["wayland"] }
|
||||||
|
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 }
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ 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
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::event::{self, Event};
|
use crate::event::Event;
|
||||||
use crate::id::Id;
|
use crate::id::Id;
|
||||||
use crate::layout;
|
use crate::layout;
|
||||||
use crate::mouse;
|
use crate::mouse;
|
||||||
|
|
|
||||||
|
|
@ -46,4 +46,6 @@ 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, Point, Rectangle, Shell, Size};
|
use crate::{Clipboard, Event, Layout, Overlay, Shell, Size};
|
||||||
|
|
||||||
/// An [`Overlay`] container that displays multiple overlay [`overlay::Element`]
|
/// An [`Overlay`] container that displays multiple overlay [`overlay::Element`]
|
||||||
/// children.
|
/// children.
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,8 @@ 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`].
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use super::Operation;
|
||||||
use crate::{
|
use crate::{
|
||||||
Rectangle,
|
Rectangle,
|
||||||
id::Id,
|
id::Id,
|
||||||
widget::operation::{Outcome, focusable::Count},
|
widget::operation::Outcome,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Produces an [`Operation`] that searches for the Id
|
/// Produces an [`Operation`] that searches for the Id
|
||||||
|
|
|
||||||
|
|
@ -22,16 +22,18 @@
|
||||||
//! ```
|
//! ```
|
||||||
use crate::alignment;
|
use crate::alignment;
|
||||||
use crate::layout;
|
use crate::layout;
|
||||||
use crate::mouse;
|
use crate::mouse::{self, click};
|
||||||
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::{
|
||||||
Color, Element, Layout, Length, Pixels, Rectangle, Size, Theme, Widget,
|
Clipboard, Color, Element, Event, Layout, Length, Pixels, Point, Rectangle,
|
||||||
|
Shell, Size, Theme, Widget, keyboard, touch,
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
pub use text::{Alignment, Ellipsize, LineHeight, Shaping, Wrapping};
|
pub use text::{Alignment, Ellipsize, LineHeight, Shaping, Wrapping};
|
||||||
|
|
||||||
/// A bunch of text.
|
/// A bunch of text.
|
||||||
|
|
@ -65,6 +67,7 @@ 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>
|
||||||
|
|
@ -79,6 +82,7 @@ where
|
||||||
fragment: fragment.into_fragment(),
|
fragment: fragment.into_fragment(),
|
||||||
format: Format::default(),
|
format: Format::default(),
|
||||||
class: Theme::default(),
|
class: Theme::default(),
|
||||||
|
selectable: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -189,7 +193,17 @@ where
|
||||||
{
|
{
|
||||||
let color = color.map(Into::into);
|
let color = color.map(Into::into);
|
||||||
|
|
||||||
self.style(move |_theme| Style { color })
|
self.style(move |_theme| Style {
|
||||||
|
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`].
|
||||||
|
|
@ -202,7 +216,114 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The internal state of a [`Text`] widget.
|
/// The internal state of a [`Text`] widget.
|
||||||
pub type State<P> = paragraph::Plain<P>;
|
pub struct State<P: Paragraph> {
|
||||||
|
/// 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>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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>
|
||||||
|
|
@ -215,7 +336,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn state(&self) -> tree::State {
|
fn state(&self) -> tree::State {
|
||||||
tree::State::new(paragraph::Plain::<Renderer::Paragraph>::default())
|
tree::State::new(State::<Renderer::Paragraph>::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn size(&self) -> Size<Length> {
|
fn size(&self) -> Size<Length> {
|
||||||
|
|
@ -231,8 +352,10 @@ 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(
|
||||||
tree.state.downcast_mut::<State<Renderer::Paragraph>>(),
|
&mut state.paragraph,
|
||||||
renderer,
|
renderer,
|
||||||
limits,
|
limits,
|
||||||
&self.fragment,
|
&self.fragment,
|
||||||
|
|
@ -252,25 +375,325 @@ 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();
|
||||||
|
if let Some(sel) = &state.selection {
|
||||||
|
let left = sel.anchor.min(sel.end);
|
||||||
|
let right = sel.anchor.max(sel.end);
|
||||||
|
let content: &str = self.fragment.as_ref();
|
||||||
|
|
||||||
draw(
|
if left != right {
|
||||||
renderer,
|
let lo_byte = grapheme_to_byte(content, left);
|
||||||
defaults,
|
let hi_byte = grapheme_to_byte(content, right);
|
||||||
layout.bounds(),
|
|
||||||
state.raw(),
|
let anchor = bounds.anchor(
|
||||||
style,
|
paragraph.min_bounds(),
|
||||||
viewport,
|
paragraph.align_x(),
|
||||||
);
|
paragraph.align_y(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let rects = paragraph.highlight(
|
||||||
|
0,
|
||||||
|
(lo_byte, text::Affinity::After),
|
||||||
|
(hi_byte, text::Affinity::Before),
|
||||||
|
);
|
||||||
|
|
||||||
|
for r in rects {
|
||||||
|
renderer.fill_quad(
|
||||||
|
renderer::Quad {
|
||||||
|
bounds: Rectangle {
|
||||||
|
x: anchor.x + r.x,
|
||||||
|
y: anchor.y + r.y,
|
||||||
|
width: r.width,
|
||||||
|
height: r.height,
|
||||||
|
},
|
||||||
|
..renderer::Quad::default()
|
||||||
|
},
|
||||||
|
style.selected_fill,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
draw(renderer, defaults, bounds, paragraph, style, 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() {
|
||||||
|
state.focused = false;
|
||||||
|
state.keyboard_focused = false;
|
||||||
|
if let Some(sel) = &mut state.selection {
|
||||||
|
sel.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Right)) => {
|
||||||
|
if let Some(pos) = cursor.position_over(bounds) {
|
||||||
|
state.context_menu_position = Some(pos);
|
||||||
|
state.focused = true;
|
||||||
|
state.keyboard_focused = false;
|
||||||
|
shell.capture_event();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
);
|
||||||
|
|
||||||
|
sel.end = hit_to_grapheme(paragraph, relative, content);
|
||||||
|
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();
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
keyboard::Key::Named(keyboard::key::Named::Home) => {
|
||||||
|
if modifiers.shift() {
|
||||||
|
sel.end = 0;
|
||||||
|
} else {
|
||||||
|
sel.anchor = 0;
|
||||||
|
sel.end = 0;
|
||||||
|
}
|
||||||
|
shell.capture_event();
|
||||||
|
}
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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")]
|
||||||
|
|
@ -471,12 +894,23 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The appearance of some text.
|
/// The appearance of some text.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Default)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Style {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
color: None,
|
||||||
|
selected_fill: DEFAULT_SELECTION_COLOR,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The theme catalog of a [`Text`].
|
/// The theme catalog of a [`Text`].
|
||||||
|
|
@ -510,13 +944,14 @@ 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 { color: None }
|
Style::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -524,6 +959,7 @@ 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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -531,6 +967,7 @@ 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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -538,6 +975,7 @@ 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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -545,6 +983,7 @@ 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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -552,5 +991,177 @@ 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;
|
||||||
|
|
||||||
|
/// 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 (enables Cut / Paste).
|
||||||
|
fn is_editable(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 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::{Error, simulator};
|
use iced_test::{simulator, Error};
|
||||||
|
|
||||||
#[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::FilterMode::Linear,
|
mipmap_filter: wgpu::MipmapFilterMode::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],
|
||||||
push_constant_ranges: &[],
|
immediate_size: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
let shader =
|
let shader =
|
||||||
|
|
@ -280,7 +280,7 @@ impl Pipeline {
|
||||||
compilation_options:
|
compilation_options:
|
||||||
wgpu::PipelineCompilationOptions::default(),
|
wgpu::PipelineCompilationOptions::default(),
|
||||||
}),
|
}),
|
||||||
multiview: None,
|
multiview_mask: None,
|
||||||
cache: None,
|
cache: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -388,6 +388,7 @@ 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(
|
||||||
|
|
@ -478,7 +479,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],
|
||||||
push_constant_ranges: &[],
|
immediate_size: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
let shader =
|
let shader =
|
||||||
|
|
@ -520,7 +521,7 @@ impl DepthPipeline {
|
||||||
compilation_options:
|
compilation_options:
|
||||||
wgpu::PipelineCompilationOptions::default(),
|
wgpu::PipelineCompilationOptions::default(),
|
||||||
}),
|
}),
|
||||||
multiview: None,
|
multiview_mask: None,
|
||||||
cache: None,
|
cache: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -586,6 +587,7 @@ 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,6 +43,7 @@ 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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -64,8 +65,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 {
|
||||||
|
|
@ -101,7 +102,7 @@ fn build_pipeline(
|
||||||
mask: !0,
|
mask: !0,
|
||||||
alpha_to_coverage_enabled: false,
|
alpha_to_coverage_enabled: false,
|
||||||
},
|
},
|
||||||
multiview: None,
|
multiview_mask: 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,
|
mouse, overlay, Event, Length, Rectangle,
|
||||||
};
|
};
|
||||||
|
use iced::{id::Id, Element};
|
||||||
use iced_core::{
|
use iced_core::{
|
||||||
self, Clipboard, Layout, Shell, Widget, layout,
|
self, 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, mouse, overlay, Event, Length, Point, Rectangle,
|
||||||
};
|
};
|
||||||
use iced_core::{
|
use iced_core::{
|
||||||
Clipboard, Shell, layout, renderer,
|
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,7 +7,6 @@ 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,
|
||||||
|
|
@ -15,6 +14,7 @@ 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,6 +477,7 @@ 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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,6 @@
|
||||||
#[cfg(feature = "image")]
|
#[cfg(feature = "image")]
|
||||||
use crate::core::Bytes;
|
use crate::core::Bytes;
|
||||||
|
|
||||||
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;
|
||||||
|
|
|
||||||
|
|
@ -161,6 +161,14 @@ 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,6 +336,10 @@ 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
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
//! Access the clipboard.
|
//! Access the clipboard.
|
||||||
|
|
||||||
use std::any::Any;
|
|
||||||
|
|
||||||
use dnd::{DndDestinationRectangle, DndSurface};
|
use dnd::{DndDestinationRectangle, DndSurface};
|
||||||
use iced_core::clipboard::DndSource;
|
use window_clipboard::mime::AllowedMimeTypes;
|
||||||
use window_clipboard::mime::{AllowedMimeTypes, AsMimeTypes};
|
|
||||||
|
|
||||||
use crate::{oneshot, task, Action, Task};
|
use crate::{Action, Task, oneshot, task};
|
||||||
|
|
||||||
/// An action to be performed on the system.
|
/// An action to be performed on the system.
|
||||||
pub enum DndAction {
|
pub enum DndAction {
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@ 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;
|
||||||
|
|
|
||||||
|
|
@ -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::window::Id;
|
|
||||||
use iced_core::Rectangle;
|
use iced_core::Rectangle;
|
||||||
|
use iced_core::window::Id;
|
||||||
|
|
||||||
/// output for layer surface
|
/// output for layer surface
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -150,6 +150,13 @@ 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,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Action {
|
impl fmt::Debug for Action {
|
||||||
|
|
@ -162,35 +169,51 @@ 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: {:?} }}", id, width, height
|
"Action::LayerSurfaceAction::Size {{ id: {:#?}, width: {:?}, height: {:?} }}",
|
||||||
|
id, width, height
|
||||||
),
|
),
|
||||||
Action::Destroy(id) => write!(
|
Action::Destroy(id) => write!(
|
||||||
f,
|
f,
|
||||||
"Action::LayerSurfaceAction::Destroy {{ id: {:#?} }}", id
|
"Action::LayerSurfaceAction::Destroy {{ id: {:#?} }}",
|
||||||
|
id
|
||||||
),
|
),
|
||||||
Action::Anchor { id, anchor } => write!(
|
Action::Anchor { id, anchor } => write!(
|
||||||
f,
|
f,
|
||||||
"Action::LayerSurfaceAction::Anchor {{ id: {:#?}, anchor: {:?} }}", id, anchor
|
"Action::LayerSurfaceAction::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} }}", id
|
"Action::LayerSurfaceAction::ExclusiveZone {{ id: {:#?}, exclusive_zone: {exclusive_zone} }}",
|
||||||
|
id
|
||||||
),
|
),
|
||||||
Action::Margin { id, margin } => write!(
|
Action::Margin { id, margin } => write!(
|
||||||
f,
|
f,
|
||||||
"Action::LayerSurfaceAction::Margin {{ id: {:#?}, margin: {:?} }}", id, margin
|
"Action::LayerSurfaceAction::Margin {{ id: {:#?}, margin: {:?} }}",
|
||||||
|
id, margin
|
||||||
),
|
),
|
||||||
Action::KeyboardInteractivity { id, keyboard_interactivity } => write!(
|
Action::KeyboardInteractivity {
|
||||||
|
id,
|
||||||
|
keyboard_interactivity,
|
||||||
|
} => write!(
|
||||||
f,
|
f,
|
||||||
"Action::LayerSurfaceAction::KeyboardInteractivity {{ id: {:#?}, keyboard_interactivity: {:?} }}", id, keyboard_interactivity
|
"Action::LayerSurfaceAction::KeyboardInteractivity {{ id: {:#?}, keyboard_interactivity: {:?} }}",
|
||||||
|
id, keyboard_interactivity
|
||||||
),
|
),
|
||||||
Action::InputZone { id, zone } => write!(
|
Action::InputZone { id, zone } => write!(
|
||||||
f,
|
f,
|
||||||
"Action::LayerSurfaceAction::InputZone {{ id: {:#?}, zone: {:?} }}", id, zone
|
"Action::LayerSurfaceAction::InputZone {{ id: {:#?}, zone: {:?} }}",
|
||||||
|
id, zone
|
||||||
),
|
),
|
||||||
Action::Layer { id, layer } => write!(
|
Action::Layer { id, layer } => write!(
|
||||||
f,
|
f,
|
||||||
"Action::LayerSurfaceAction::Layer {{ id: {:#?}, layer: {:?} }}", id, layer
|
"Action::LayerSurfaceAction::Layer {{ id: {:#?}, layer: {:?} }}",
|
||||||
|
id, layer
|
||||||
|
),
|
||||||
|
Action::Padding { id, padding } => write!(
|
||||||
|
f,
|
||||||
|
"Action::LayerSurfaceAction::Padding {{ id: {:#?}, padding: {:?} }}",
|
||||||
|
id, padding
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use iced_core::window::Id;
|
use iced_core::{Rectangle, window::Id};
|
||||||
|
|
||||||
/// activation Actions
|
/// activation Actions
|
||||||
pub mod activation;
|
pub mod activation;
|
||||||
|
|
@ -35,6 +35,8 @@ pub enum Action {
|
||||||
InhibitShortcuts(bool),
|
InhibitShortcuts(bool),
|
||||||
/// Rounded corners in logical space
|
/// Rounded corners in logical space
|
||||||
RoundedCorners(iced_core::window::Id, Option<CornerRadius>),
|
RoundedCorners(iced_core::window::Id, Option<CornerRadius>),
|
||||||
|
/// Blur effect for a surface
|
||||||
|
BlurSurface(Id, Option<Vec<Rectangle>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Default)]
|
#[derive(Debug, Clone, Copy, PartialEq, Default)]
|
||||||
|
|
@ -70,6 +72,11 @@ impl Debug for Action {
|
||||||
Action::RoundedCorners(id, v) => {
|
Action::RoundedCorners(id, v) => {
|
||||||
f.debug_tuple("RoundedCorners").field(id).field(v).finish()
|
f.debug_tuple("RoundedCorners").field(id).field(v).finish()
|
||||||
}
|
}
|
||||||
|
Action::BlurSurface(id, rectangles) => f
|
||||||
|
.debug_tuple("BlurSurface")
|
||||||
|
.field(id)
|
||||||
|
.field(rectangles)
|
||||||
|
.finish(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -124,26 +124,34 @@ 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, .. } => write!(
|
Action::Popup { popup, .. } => {
|
||||||
f,
|
write!(f, "Action::PopupAction::Popup {{ popup: {:?} }}", popup)
|
||||||
"Action::PopupAction::Popup {{ popup: {:?} }}",
|
}
|
||||||
popup
|
Action::Destroy { id } => {
|
||||||
),
|
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
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -683,6 +683,8 @@ 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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -775,6 +775,7 @@ impl<P: Program + 'static> Tester<P> {
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
|
..text::Style::default()
|
||||||
})
|
})
|
||||||
.into()
|
.into()
|
||||||
},
|
},
|
||||||
|
|
@ -945,6 +946,7 @@ 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(),
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
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};
|
||||||
|
|
@ -74,6 +75,7 @@ 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)]
|
||||||
|
|
@ -87,12 +89,15 @@ 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();
|
||||||
|
|
||||||
// TODO: Reuse `cosmic-text` font database
|
// Reuse the font database already loaded by the `cosmic-text` font
|
||||||
if self.fontdb.is_none() {
|
// system. We clone it once and refresh only when a font is
|
||||||
let mut fontdb = usvg::fontdb::Database::new();
|
// loaded (i.e. when the font system's version changes).
|
||||||
fontdb.load_system_fonts();
|
let font_system = text::font_system().read().expect("Read font system");
|
||||||
|
let version = font_system.version();
|
||||||
|
|
||||||
self.fontdb = Some(Arc::new(fontdb));
|
if self.fontdb_version != Some(version) {
|
||||||
|
self.fontdb = Some(Arc::new(font_system.db().clone()));
|
||||||
|
self.fontdb_version = Some(version);
|
||||||
}
|
}
|
||||||
|
|
||||||
let options = usvg::Options {
|
let options = usvg::Options {
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,11 @@ 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"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
iced_debug.workspace = true
|
iced_debug.workspace = true
|
||||||
|
|
@ -57,9 +62,9 @@ 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 = "1.0.1"
|
as-raw-xcb-connection = { version = "1.0.1", optional = true }
|
||||||
tiny-xlib = "0.2.3"
|
tiny-xlib = { version = "0.2.3", optional = true }
|
||||||
x11rb = { version = "0.13.1", features = [
|
x11rb = { version = "0.13.1", optional = true, features = [
|
||||||
"allow-unsafe-code",
|
"allow-unsafe-code",
|
||||||
"dl-libxcb",
|
"dl-libxcb",
|
||||||
"dri3",
|
"dri3",
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,6 @@ 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,
|
||||||
|
|
@ -79,7 +78,6 @@ 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],
|
||||||
|
|
@ -100,7 +98,6 @@ 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],
|
||||||
push_constant_ranges: &[],
|
immediate_size: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
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: None,
|
multiview_mask: None,
|
||||||
cache: None,
|
cache: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -194,6 +194,7 @@ 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, device, encoder, belt,
|
pixels, width, 0, allocation, encoder, belt,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Entry::Fragmented { fragments, .. } => {
|
Entry::Fragmented { fragments, .. } => {
|
||||||
|
|
@ -145,7 +145,6 @@ impl Atlas {
|
||||||
width,
|
width,
|
||||||
offset,
|
offset,
|
||||||
&fragment.allocation,
|
&fragment.allocation,
|
||||||
device,
|
|
||||||
encoder,
|
encoder,
|
||||||
belt,
|
belt,
|
||||||
);
|
);
|
||||||
|
|
@ -312,7 +311,6 @@ 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,
|
||||||
) {
|
) {
|
||||||
|
|
@ -334,7 +332,6 @@ 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;
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,10 @@ impl Cache {
|
||||||
raster: Raster {
|
raster: Raster {
|
||||||
cache: crate::image::raster::Cache::default(),
|
cache: crate::image::raster::Cache::default(),
|
||||||
pending: HashMap::new(),
|
pending: HashMap::new(),
|
||||||
belt: wgpu::util::StagingBelt::new(2 * 1024 * 1024),
|
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(),
|
||||||
|
|
@ -451,7 +454,10 @@ mod worker {
|
||||||
backend,
|
backend,
|
||||||
texture_layout,
|
texture_layout,
|
||||||
shell: shell.clone(),
|
shell: shell.clone(),
|
||||||
belt: wgpu::util::StagingBelt::new(4 * 1024 * 1024),
|
belt: wgpu::util::StagingBelt::new(
|
||||||
|
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::FilterMode::Nearest,
|
mipmap_filter: wgpu::MipmapFilterMode::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::FilterMode::Linear,
|
mipmap_filter: wgpu::MipmapFilterMode::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: None,
|
multiview_mask: None,
|
||||||
cache: None,
|
cache: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -528,7 +528,6 @@ 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);
|
||||||
|
|
||||||
|
|
@ -539,11 +538,11 @@ impl Layer {
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
|
|
||||||
if !nearest.is_empty() {
|
if !nearest.is_empty() {
|
||||||
offset += self.instances.write(device, encoder, belt, 0, nearest);
|
offset += self.instances.write(encoder, belt, 0, nearest);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !linear.is_empty() {
|
if !linear.is_empty() {
|
||||||
let _ = self.instances.write(device, encoder, belt, offset, linear);
|
let _ = self.instances.write(encoder, belt, offset, linear);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,7 @@ 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,
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|
@ -454,6 +455,7 @@ 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,
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
@ -528,6 +530,7 @@ 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,
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
@ -621,6 +624,7 @@ 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(device, encoder, belt, transformation, scale);
|
self.update(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,7 +223,6 @@ 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,
|
||||||
|
|
@ -237,7 +236,6 @@ 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(device, encoder, belt, 0, instances);
|
let _ = self.instances.write(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: None,
|
multiview_mask: 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(device, encoder, belt, 0, instances);
|
let _ = self.instances.write(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: None,
|
multiview_mask: None,
|
||||||
cache: None,
|
cache: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -619,7 +619,7 @@ fn prepare(
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Some(cryoglyph::TextArea {
|
Some(cryoglyph::TextArea {
|
||||||
buffer,
|
text: buffer.layout_runs(),
|
||||||
left: position.x,
|
left: position.x,
|
||||||
top: position.y,
|
top: position.y,
|
||||||
scale: transformation.scale_factor()
|
scale: transformation.scale_factor()
|
||||||
|
|
|
||||||
|
|
@ -313,6 +313,7 @@ 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,
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -425,18 +426,13 @@ impl Layer {
|
||||||
|
|
||||||
let indices = mesh.indices();
|
let indices = mesh.indices();
|
||||||
|
|
||||||
index_offset += self.index_buffer.write(
|
index_offset +=
|
||||||
device,
|
self.index_buffer
|
||||||
encoder,
|
.write(encoder, belt, index_offset, indices);
|
||||||
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,
|
||||||
|
|
@ -444,16 +440,14 @@ 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,
|
||||||
|
|
@ -461,7 +455,6 @@ 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,
|
||||||
|
|
@ -731,7 +724,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],
|
||||||
push_constant_ranges: &[],
|
immediate_size: 0,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -784,7 +777,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: None,
|
multiview_mask: None,
|
||||||
cache: None,
|
cache: None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
@ -886,7 +879,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],
|
||||||
push_constant_ranges: &[],
|
immediate_size: 0,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -949,7 +942,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: None,
|
multiview_mask: 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: None,
|
multiview_mask: None,
|
||||||
cache: None,
|
cache: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -177,6 +177,7 @@ 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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -320,7 +321,6 @@ 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,6 +351,7 @@ 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);
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ pub mod compositor;
|
||||||
not(target_os = "redox")
|
not(target_os = "redox")
|
||||||
))]
|
))]
|
||||||
mod wayland;
|
mod wayland;
|
||||||
#[cfg(all(unix, not(target_os = "macos"), not(target_os = "redox")))]
|
#[cfg(all(unix, feature = "x11", not(target_os = "macos"), not(target_os = "redox")))]
|
||||||
mod x11;
|
mod x11;
|
||||||
|
|
||||||
pub use compositor::Compositor;
|
pub use compositor::Compositor;
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ use crate::{Engine, Renderer};
|
||||||
not(target_os = "redox")
|
not(target_os = "redox")
|
||||||
))]
|
))]
|
||||||
use super::wayland::get_wayland_device_ids;
|
use super::wayland::get_wayland_device_ids;
|
||||||
#[cfg(all(unix, not(target_os = "macos"), not(target_os = "redox")))]
|
#[cfg(all(unix, feature = "x11", 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;
|
use std::future::Future;
|
||||||
|
|
||||||
|
|
@ -72,22 +72,29 @@ impl Compositor {
|
||||||
not(target_os = "redox")
|
not(target_os = "redox")
|
||||||
))]
|
))]
|
||||||
let ids = compatible_window.as_ref().and_then(|window| {
|
let ids = compatible_window.as_ref().and_then(|window| {
|
||||||
get_wayland_device_ids(window)
|
let w = get_wayland_device_ids(window);
|
||||||
.or_else(|| get_x11_device_ids(window))
|
#[cfg(feature = "x11")]
|
||||||
|
let w = w.or_else(|| get_x11_device_ids(window));
|
||||||
|
w
|
||||||
});
|
});
|
||||||
|
|
||||||
|
#[cfg(all(
|
||||||
|
unix,
|
||||||
|
not(feature = "cctk"),
|
||||||
|
not(target_os = "macos"),
|
||||||
|
not(target_os = "redox")
|
||||||
|
))]
|
||||||
|
let ids = compatible_window
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|window| get_x11_device_ids(window));
|
||||||
|
|
||||||
// HACK:
|
// HACK:
|
||||||
// 1. If we specifically didn't select an nvidia gpu
|
// 1. If we specifically didn't select an nvidia gpu
|
||||||
// 2. and the user didn't specifically request an nvidia gpu
|
// 2. and the user didn't specifically request an nvidia gpu
|
||||||
// 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(all(
|
#[cfg(all(unix, not(target_os = "macos"), not(target_os = "redox")))]
|
||||||
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")
|
||||||
|
|
@ -126,6 +133,7 @@ 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();
|
||||||
|
|
@ -148,13 +156,13 @@ impl Compositor {
|
||||||
if std::env::var_os("WGPU_ADAPTER_NAME").is_none() {
|
if std::env::var_os("WGPU_ADAPTER_NAME").is_none() {
|
||||||
#[cfg(all(
|
#[cfg(all(
|
||||||
unix,
|
unix,
|
||||||
feature = "cctk",
|
|
||||||
not(target_os = "macos"),
|
not(target_os = "macos"),
|
||||||
not(target_os = "redox")
|
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();
|
||||||
|
|
@ -172,6 +180,7 @@ 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();
|
||||||
|
|
@ -184,6 +193,8 @@ impl Compositor {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
adapter = instance.request_adapter(&adapter_options).await.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
let adapter = match adapter {
|
let adapter = match adapter {
|
||||||
|
|
@ -286,6 +297,14 @@ 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 {
|
||||||
|
|
@ -294,7 +313,7 @@ impl Compositor {
|
||||||
label: Some(
|
label: Some(
|
||||||
"iced_wgpu::window::compositor device descriptor",
|
"iced_wgpu::window::compositor device descriptor",
|
||||||
),
|
),
|
||||||
required_features: wgpu::Features::empty(),
|
required_features,
|
||||||
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,
|
||||||
|
|
@ -352,6 +371,14 @@ 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());
|
||||||
|
|
@ -367,6 +394,10 @@ 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 {
|
||||||
|
|
|
||||||
|
|
@ -505,6 +505,7 @@ 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::{Action, Task, task};
|
use iced_runtime::{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;
|
||||||
self.traverse(operate);
|
operate(self);
|
||||||
self.depth -= 1;
|
self.depth -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
//! 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;
|
||||||
|
|
@ -293,9 +292,12 @@ 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
|
child.as_widget_mut().operate(
|
||||||
.as_widget_mut()
|
state,
|
||||||
.operate(state, c_layout.with_virtual_offset(layout.virtual_offset()), renderer, operation);
|
c_layout.with_virtual_offset(layout.virtual_offset()),
|
||||||
|
renderer,
|
||||||
|
operation,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -318,7 +320,13 @@ where
|
||||||
.zip(layout.children())
|
.zip(layout.children())
|
||||||
{
|
{
|
||||||
child.as_widget_mut().update(
|
child.as_widget_mut().update(
|
||||||
tree, event, c_layout.with_virtual_offset(layout.virtual_offset()), cursor, renderer, clipboard, shell,
|
tree,
|
||||||
|
event,
|
||||||
|
c_layout.with_virtual_offset(layout.virtual_offset()),
|
||||||
|
cursor,
|
||||||
|
renderer,
|
||||||
|
clipboard,
|
||||||
|
shell,
|
||||||
viewport,
|
viewport,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -337,9 +345,13 @@ where
|
||||||
.zip(&tree.children)
|
.zip(&tree.children)
|
||||||
.zip(layout.children())
|
.zip(layout.children())
|
||||||
.map(|((child, tree), c_layout)| {
|
.map(|((child, tree), c_layout)| {
|
||||||
child
|
child.as_widget().mouse_interaction(
|
||||||
.as_widget()
|
tree,
|
||||||
.mouse_interaction(tree, c_layout.with_virtual_offset(layout.virtual_offset()), cursor, viewport, renderer)
|
c_layout.with_virtual_offset(layout.virtual_offset()),
|
||||||
|
cursor,
|
||||||
|
viewport,
|
||||||
|
renderer,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.max()
|
.max()
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#![allow(missing_docs)]
|
#![allow(missing_docs)]
|
||||||
use crate::core::event::{self, Event};
|
use crate::core::event::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,24 +363,25 @@ 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;
|
||||||
|
|
||||||
self.visible_elements
|
for (element, (index, layout, tree)) in self
|
||||||
|
.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,7 +2,6 @@
|
||||||
|
|
||||||
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;
|
||||||
|
|
|
||||||
|
|
@ -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, event,
|
Vector,
|
||||||
};
|
};
|
||||||
use crate::pane_grid::{Draggable, TitleBar};
|
use crate::pane_grid::{Draggable, TitleBar};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -459,6 +459,7 @@ 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,7 +27,6 @@ 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;
|
||||||
|
|
@ -933,7 +932,7 @@ where
|
||||||
|
|
||||||
let had_input_method = shell.input_method().is_enabled();
|
let had_input_method = shell.input_method().is_enabled();
|
||||||
|
|
||||||
let mut c_event = match event.clone() {
|
let c_event = match event.clone() {
|
||||||
Event::Dnd(dnd::DndEvent::Offer(
|
Event::Dnd(dnd::DndEvent::Offer(
|
||||||
id,
|
id,
|
||||||
dnd::OfferEvent::Enter {
|
dnd::OfferEvent::Enter {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
//! 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,7 +179,10 @@ where
|
||||||
{
|
{
|
||||||
let color = color.map(Into::into);
|
let color = color.map(Into::into);
|
||||||
|
|
||||||
self.style(move |_theme| Style { color })
|
self.style(move |_theme| Style {
|
||||||
|
color,
|
||||||
|
..Style::default()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the default style class of the [`Rich`] text.
|
/// Sets the default style class of the [`Rich`] text.
|
||||||
|
|
|
||||||
|
|
@ -544,6 +544,8 @@ 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>,
|
||||||
|
pending_edit: Option<Action>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -579,6 +581,14 @@ 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
|
||||||
|
|
@ -593,7 +603,7 @@ impl<Highlighter: text::Highlighter> operation::Focusable
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unfocus(&mut self) {
|
fn unfocus(&mut self) {
|
||||||
self.focus = None;
|
self.clear_focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -621,6 +631,8 @@ 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,
|
||||||
|
pending_edit: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -699,6 +711,58 @@ 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);
|
||||||
|
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;
|
||||||
};
|
};
|
||||||
|
|
@ -1223,6 +1287,23 @@ 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);
|
||||||
|
|
@ -1545,3 +1626,63 @@ 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 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,6 +1,5 @@
|
||||||
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,7 +37,6 @@ 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;
|
||||||
|
|
@ -483,6 +482,7 @@ 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 {
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,8 @@ cctk = [
|
||||||
"xkeysym",
|
"xkeysym",
|
||||||
"dep:cctk",
|
"dep:cctk",
|
||||||
]
|
]
|
||||||
|
# Prevents multiple separate process instances.
|
||||||
|
single-instance = []
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
|
||||||
|
|
@ -178,7 +178,7 @@ pub fn window_attributes(
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
#[cfg(target_os = "linux")]
|
||||||
{
|
{
|
||||||
use winit::platform::wayland::WindowAttributesWayland;
|
use winit::platform::wayland::WindowAttributesWayland;
|
||||||
|
|
||||||
|
|
@ -387,19 +387,19 @@ pub fn window_event(
|
||||||
self::modifiers(new_modifiers.state()),
|
self::modifiers(new_modifiers.state()),
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
WindowEvent::Ime(event) => Some(Event::InputMethod(match event {
|
WindowEvent::Ime(event) => match event {
|
||||||
Ime::Enabled => input_method::Event::Opened,
|
Ime::Enabled => Some(input_method::Event::Opened),
|
||||||
Ime::Preedit(content, size) => input_method::Event::Preedit(
|
Ime::Preedit(content, size) => Some(input_method::Event::Preedit(
|
||||||
content,
|
content,
|
||||||
size.map(|(start, end)| start..end),
|
size.map(|(start, end)| start..end),
|
||||||
),
|
)),
|
||||||
Ime::Commit(content) => input_method::Event::Commit(content),
|
Ime::Commit(content) => Some(input_method::Event::Commit(content)),
|
||||||
Ime::Disabled => input_method::Event::Closed,
|
Ime::Disabled => Some(input_method::Event::Closed),
|
||||||
Ime::DeleteSurrounding {
|
// Upstream iced depends on the winit version which doesn't support
|
||||||
before_bytes,
|
// this kind of ime event.
|
||||||
after_bytes,
|
Ime::DeleteSurrounding { .. } => None,
|
||||||
} => 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 {
|
||||||
|
|
|
||||||
249
winit/src/lib.rs
249
winit/src/lib.rs
|
|
@ -40,12 +40,6 @@ 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;
|
||||||
|
|
@ -192,11 +186,15 @@ 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>>,
|
||||||
control_sender: mpsc::UnboundedSender<Control>,
|
|
||||||
|
|
||||||
#[cfg(feature = "a11y")]
|
#[cfg(feature = "a11y")]
|
||||||
adapters: std::collections::HashMap<window::Id, (u64, iced_accessibility::accesskit_winit::Adapter)>,
|
control_sender: mpsc::UnboundedSender<Control>,
|
||||||
|
|
||||||
|
#[cfg(feature = "a11y")]
|
||||||
|
adapters: std::collections::HashMap<
|
||||||
|
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")]
|
||||||
|
|
@ -215,6 +213,7 @@ 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),
|
||||||
|
|
@ -501,9 +500,15 @@ where
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
let window: Arc<dyn winit::window::Window + 'static> = Arc::from(window);
|
let window: Arc<
|
||||||
|
dyn winit::window::Window + 'static,
|
||||||
|
> = Arc::from(window);
|
||||||
#[cfg(feature = "a11y")]
|
#[cfg(feature = "a11y")]
|
||||||
self.init_adapter(event_loop, id, window.clone());
|
self.init_adapter(
|
||||||
|
event_loop,
|
||||||
|
id,
|
||||||
|
window.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
self.process_event(
|
self.process_event(
|
||||||
event_loop,
|
event_loop,
|
||||||
|
|
@ -572,11 +577,13 @@ where
|
||||||
.expect("Send event");
|
.expect("Send event");
|
||||||
}
|
}
|
||||||
Control::Winit(id, e) => {
|
Control::Winit(id, e) => {
|
||||||
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
#[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(
|
||||||
|
|
@ -597,7 +604,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.sender
|
self.sender
|
||||||
.start_send(Event::Winit(id, e))
|
.start_send(Event::Winit(id, e))
|
||||||
.expect("Send event");
|
.expect("Send event");
|
||||||
|
|
@ -610,7 +617,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)),
|
||||||
|
|
@ -618,8 +625,8 @@ where
|
||||||
}
|
}
|
||||||
#[cfg(feature = "a11y")]
|
#[cfg(feature = "a11y")]
|
||||||
Control::Cleanup(id) => {
|
Control::Cleanup(id) => {
|
||||||
_ = self.adapters.remove(&id);
|
_ = self.adapters.remove(&id);
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
break;
|
break;
|
||||||
|
|
@ -633,34 +640,35 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "a11y")]
|
#[cfg(feature = "a11y")]
|
||||||
fn init_adapter(&mut self, event_loop: &(dyn winit::event_loop::ActiveEventLoop + 'static), id: core::window::Id, window: Arc<dyn winit::window::Window + 'static>) {
|
fn init_adapter(
|
||||||
|
&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,
|
ActivationHandler, Node, NodeId, Role, Tree, TreeUpdate,
|
||||||
Tree, TreeUpdate,
|
|
||||||
};
|
};
|
||||||
use iced_accessibility::accesskit_winit::Adapter;
|
use iced_accessibility::accesskit_winit::Adapter;
|
||||||
|
|
||||||
let node_id =
|
let node_id = iced_runtime::core::id::window_node_id();
|
||||||
iced_runtime::core::id::window_node_id();
|
|
||||||
|
let activation_handler = WinitActivationHandler {
|
||||||
let activation_handler =
|
proxy: self.control_sender.clone(),
|
||||||
WinitActivationHandler {
|
title: String::new(),
|
||||||
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 =
|
let deactivation_handler = WinitDeactivationHandler {
|
||||||
WinitDeactivationHandler {
|
proxy: self.control_sender.clone(),
|
||||||
proxy: self.control_sender.clone(),
|
};
|
||||||
};
|
|
||||||
|
|
||||||
_ = self.adapters.insert(
|
_ = self.adapters.insert(
|
||||||
id,
|
id,
|
||||||
(
|
(
|
||||||
|
|
@ -674,8 +682,6 @@ where
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -838,6 +844,28 @@ 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,
|
||||||
|
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() {
|
||||||
|
|
@ -857,35 +885,36 @@ 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(all(feature = "cctk", target_os = "linux"))]
|
#[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),
|
||||||
);
|
);
|
||||||
match create_compositor::<P>(
|
if compositor.is_none() {
|
||||||
window.clone(),
|
match create_compositor::<P>(
|
||||||
CreateCompositor {
|
window.clone(),
|
||||||
proxy: &proxy,
|
CreateCompositor {
|
||||||
runtime: &mut runtime,
|
proxy: &proxy,
|
||||||
display_handle: &display_handle,
|
runtime: &mut runtime,
|
||||||
graphics_settings: &graphics_settings,
|
display_handle: &display_handle,
|
||||||
default_fonts: &default_fonts,
|
graphics_settings: &graphics_settings,
|
||||||
},
|
default_fonts: &default_fonts,
|
||||||
)
|
},
|
||||||
.await
|
)
|
||||||
{
|
.await
|
||||||
Ok(comp) => {
|
{
|
||||||
compositor = Some(comp);
|
Ok(comp) => {
|
||||||
}
|
compositor = Some(comp);
|
||||||
Err(error) => {
|
}
|
||||||
control_sender
|
Err(error) => {
|
||||||
.start_send(Control::Crash(
|
control_sender
|
||||||
Error::GraphicsCreationFailed(error),
|
.start_send(Control::Crash(
|
||||||
))
|
Error::GraphicsCreationFailed(error),
|
||||||
.expect("Send control message");
|
))
|
||||||
continue;
|
.expect("Send control message");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -913,7 +942,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(
|
||||||
|
|
@ -999,22 +1028,7 @@ async fn run_instance<P>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Event::UserEvent(action) => {
|
Event::UserEvent(action) => {
|
||||||
let exited = run_action(
|
let exited = run_action!(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());
|
||||||
}
|
}
|
||||||
|
|
@ -1025,7 +1039,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;
|
||||||
|
|
@ -1040,12 +1054,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(all(feature = "cctk", target_os = "linux"))]
|
#[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),
|
||||||
|
|
@ -1156,22 +1170,7 @@ async fn run_instance<P>(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = run_action(
|
_ = run_action!(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() {
|
||||||
|
|
@ -1326,9 +1325,7 @@ 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) =
|
if let Some(func) = window.drag_resize_window_func.as_mut() {
|
||||||
window.drag_resize_window_func.as_mut()
|
|
||||||
{
|
|
||||||
if func(window.raw.as_ref(), &event) {
|
if func(window.raw.as_ref(), &event) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -1354,22 +1351,9 @@ 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(
|
_ = run_action!(Action::Window(
|
||||||
Action::Window(runtime::window::Action::Close(id)),
|
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);
|
||||||
|
|
||||||
|
|
@ -1547,22 +1531,7 @@ async fn run_instance<P>(
|
||||||
));
|
));
|
||||||
|
|
||||||
for action in actions {
|
for action in actions {
|
||||||
let exited = run_action(
|
let exited = run_action!(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());
|
||||||
}
|
}
|
||||||
|
|
@ -1772,7 +1741,6 @@ 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",
|
||||||
|
|
@ -2106,6 +2074,7 @@ 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,
|
||||||
|
is_daemon: bool,
|
||||||
) -> bool
|
) -> bool
|
||||||
where
|
where
|
||||||
P: Program,
|
P: Program,
|
||||||
|
|
@ -2148,7 +2117,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) => {
|
||||||
|
|
@ -2190,7 +2159,7 @@ where
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if window_manager.is_empty() {
|
if window_manager.is_empty() && !is_daemon {
|
||||||
*compositor = None;
|
*compositor = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -153,9 +153,12 @@ impl PlatformSpecific {
|
||||||
) -> Option<Box<dyn HasWindowHandle + Send + Sync + 'static>> {
|
) -> Option<Box<dyn HasWindowHandle + Send + Sync + 'static>> {
|
||||||
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
#[cfg(all(feature = "cctk", target_os = "linux"))]
|
||||||
{
|
{
|
||||||
return self.wayland.create_surface();
|
self.wayland.create_surface()
|
||||||
|
}
|
||||||
|
#[cfg(not(all(feature = "cctk", target_os = "linux")))]
|
||||||
|
{
|
||||||
|
None
|
||||||
}
|
}
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn update_surface_shm(
|
pub(crate) fn update_surface_shm(
|
||||||
|
|
|
||||||
|
|
@ -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,
|
self, Action, Task,
|
||||||
platform_specific::{self, wayland},
|
platform_specific::{self, wayland},
|
||||||
task, Action, Task,
|
task,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn request_token(
|
pub fn request_token(
|
||||||
|
|
|
||||||
17
winit/src/platform_specific/wayland/commands/blur.rs
Normal file
17
winit/src/platform_specific/wayland/commands/blur.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
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,
|
self, Action, Task,
|
||||||
platform_specific::{
|
platform_specific::{
|
||||||
self,
|
self,
|
||||||
wayland::{self, CornerRadius},
|
wayland::{self, CornerRadius},
|
||||||
},
|
},
|
||||||
task, Action, Task,
|
task,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn corner_radius(
|
pub fn corner_radius(
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use iced_runtime::{
|
use iced_runtime::{
|
||||||
self,
|
self, Action, Task,
|
||||||
platform_specific::{self, wayland},
|
platform_specific::{self, wayland},
|
||||||
task, Action, Task,
|
task,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn inhibit_shortcuts(inhibit: bool) -> Task<()> {
|
pub fn inhibit_shortcuts(inhibit: bool) -> Task<()> {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use crate::core::window::Id as SurfaceId;
|
use crate::core::window::Id as SurfaceId;
|
||||||
use iced_runtime::{
|
use iced_runtime::{
|
||||||
self,
|
self, Action, Task,
|
||||||
platform_specific::{
|
platform_specific::{
|
||||||
self,
|
self,
|
||||||
wayland::{
|
wayland::{
|
||||||
|
|
@ -10,7 +10,7 @@ use iced_runtime::{
|
||||||
layer_surface::{IcedMargin, SctkLayerSurfaceSettings},
|
layer_surface::{IcedMargin, SctkLayerSurfaceSettings},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
task, Action, Task,
|
task,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use cctk::sctk::shell::wlr_layer::{Anchor, KeyboardInteractivity, Layer};
|
pub use cctk::sctk::shell::wlr_layer::{Anchor, KeyboardInteractivity, Layer};
|
||||||
|
|
@ -114,3 +114,14 @@ 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 },
|
||||||
|
)),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
//! 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,7 +1,8 @@
|
||||||
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, Action, Task,
|
task,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Request subscription for overlap notification events on the surface
|
/// Request subscription for overlap notification events on the surface
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,15 @@
|
||||||
//! 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,
|
self, Action, Task,
|
||||||
platform_specific::{
|
platform_specific::{
|
||||||
self,
|
self,
|
||||||
wayland::{self, popup::SctkPopupSettings},
|
wayland::{
|
||||||
|
self,
|
||||||
|
popup::{SctkPopupSettings, SctkPositioner},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
task, Action, Task,
|
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>
|
||||||
|
|
@ -32,6 +35,18 @@ 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,
|
self, Action, Task,
|
||||||
platform_specific::{self, wayland},
|
platform_specific::{self, wayland},
|
||||||
task, Action, Task,
|
task,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn lock<Message>() -> Task<Message> {
|
pub fn lock<Message>() -> Task<Message> {
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
use crate::core::window::Id as SurfaceId;
|
use crate::core::window::Id as SurfaceId;
|
||||||
pub use cctk::sctk::shell::wlr_layer::{Anchor, KeyboardInteractivity, Layer};
|
pub use cctk::sctk::shell::wlr_layer::{Anchor, KeyboardInteractivity, Layer};
|
||||||
use iced_runtime::{
|
use iced_runtime::{
|
||||||
self,
|
self, Action, Task,
|
||||||
platform_specific::{
|
platform_specific::{
|
||||||
self,
|
self,
|
||||||
wayland::{self, subsurface::SctkSubsurfaceSettings},
|
wayland::{self, subsurface::SctkSubsurfaceSettings},
|
||||||
},
|
},
|
||||||
task, Action, Task,
|
task,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn get_subsurface<Message>(
|
pub fn get_subsurface<Message>(
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,10 @@ use crate::platform_specific::SurfaceIdWrapper;
|
||||||
use crate::{
|
use crate::{
|
||||||
Control,
|
Control,
|
||||||
futures::futures::channel::mpsc,
|
futures::futures::channel::mpsc,
|
||||||
handlers::{overlap::OverlapNotifyV1, text_input::TextInputManager},
|
handlers::{
|
||||||
|
ext_background_effect, overlap::OverlapNotifyV1,
|
||||||
|
text_input::TextInputManager,
|
||||||
|
},
|
||||||
platform_specific::wayland::{
|
platform_specific::wayland::{
|
||||||
handlers::{
|
handlers::{
|
||||||
wp_fractional_scaling::FractionalScalingManager,
|
wp_fractional_scaling::FractionalScalingManager,
|
||||||
|
|
@ -30,7 +33,7 @@ use cctk::{
|
||||||
reexports::{
|
reexports::{
|
||||||
calloop::{self, EventLoop},
|
calloop::{self, EventLoop},
|
||||||
client::{
|
client::{
|
||||||
ConnectError, Connection, Proxy, globals::registry_queue_init,
|
Connection, Proxy, globals::registry_queue_init,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
registry::RegistryState,
|
registry::RegistryState,
|
||||||
|
|
@ -43,8 +46,6 @@ use cctk::{
|
||||||
};
|
};
|
||||||
use raw_window_handle::HasDisplayHandle;
|
use raw_window_handle::HasDisplayHandle;
|
||||||
use state::{FrameStatus, SctkWindow, send_event};
|
use state::{FrameStatus, SctkWindow, send_event};
|
||||||
#[cfg(feature = "a11y")]
|
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
|
|
@ -68,8 +69,8 @@ pub struct SctkEventLoop {
|
||||||
pub(crate) state: SctkState,
|
pub(crate) state: SctkState,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
Connect(ConnectError),
|
|
||||||
Calloop(calloop::Error),
|
Calloop(calloop::Error),
|
||||||
Global(GlobalError),
|
Global(GlobalError),
|
||||||
NoDisplayHandle,
|
NoDisplayHandle,
|
||||||
|
|
@ -126,7 +127,7 @@ impl SctkEventLoop {
|
||||||
crate::platform_specific::Action::ResizeWindow(id) => {
|
crate::platform_specific::Action::ResizeWindow(id) => {
|
||||||
if let Some((_, v)) = state.windows.iter()
|
if let Some((_, v)) = state.windows.iter()
|
||||||
.find(|w| w.id == id)
|
.find(|w| w.id == id)
|
||||||
.map(|w| w.corner_radius.as_ref())
|
.map(|w| state.corner_radii.get(&id))
|
||||||
.unwrap_or_default() {
|
.unwrap_or_default() {
|
||||||
_ = state.handle_action(iced_runtime::platform_specific::wayland::Action::RoundedCorners(id, *v));
|
_ = state.handle_action(iced_runtime::platform_specific::wayland::Action::RoundedCorners(id, *v));
|
||||||
}
|
}
|
||||||
|
|
@ -135,7 +136,7 @@ impl SctkEventLoop {
|
||||||
window,
|
window,
|
||||||
id,
|
id,
|
||||||
) => {
|
) => {
|
||||||
state.windows.push(SctkWindow { window, id, corner_radius: Default::default() });
|
state.windows.push(SctkWindow { window, id });
|
||||||
if let Some(v) = state.pending_corner_radius.remove(&id) {
|
if let Some(v) = state.pending_corner_radius.remove(&id) {
|
||||||
_ = state.handle_action(iced_runtime::platform_specific::wayland::Action::RoundedCorners(id, Some(v)));
|
_ = state.handle_action(iced_runtime::platform_specific::wayland::Action::RoundedCorners(id, Some(v)));
|
||||||
}
|
}
|
||||||
|
|
@ -346,26 +347,26 @@ impl SctkEventLoop {
|
||||||
&qh,
|
&qh,
|
||||||
),
|
),
|
||||||
corner_radius_manager: registry_state.bind_one::<CosmicCornerRadiusManagerV1, _, _>(
|
corner_radius_manager: registry_state.bind_one::<CosmicCornerRadiusManagerV1, _, _>(
|
||||||
&qh,
|
&qh,
|
||||||
1..=1,
|
1..=2,
|
||||||
(),
|
(),
|
||||||
).ok(),
|
).ok(),
|
||||||
toplevel_manager: ToplevelManagerState::try_new(
|
toplevel_manager: ToplevelManagerState::try_new(
|
||||||
®istry_state,
|
®istry_state,
|
||||||
&qh,
|
&qh,
|
||||||
),
|
),
|
||||||
inhibitor_manager: registry_state.bind_one::<zwp_keyboard_shortcuts_inhibit_manager_v1::ZwpKeyboardShortcutsInhibitManagerV1, _, _>(
|
inhibitor_manager: registry_state.bind_one::<zwp_keyboard_shortcuts_inhibit_manager_v1::ZwpKeyboardShortcutsInhibitManagerV1, _, _>(
|
||||||
&qh,
|
&qh,
|
||||||
1..=1,
|
1..=1,
|
||||||
(),
|
(),
|
||||||
).ok(),
|
).ok(),
|
||||||
text_input_manager: TextInputManager::try_new(®istry_state, &qh),
|
text_input_manager: TextInputManager::try_new(®istry_state, &qh),
|
||||||
|
ext_background_effect_manager: ext_background_effect::ExtBackgroundEffectManager::new(&globals, &qh).ok(),
|
||||||
registry_state,
|
registry_state,
|
||||||
|
|
||||||
queue_handle: qh,
|
queue_handle: qh,
|
||||||
loop_handle,
|
loop_handle,
|
||||||
|
|
||||||
|
|
||||||
inhibitor: None,
|
inhibitor: None,
|
||||||
inhibited: false,
|
inhibited: false,
|
||||||
_cursor_surface: None,
|
_cursor_surface: None,
|
||||||
|
|
@ -373,6 +374,8 @@ impl SctkEventLoop {
|
||||||
outputs: Vec::new(),
|
outputs: Vec::new(),
|
||||||
seats: Vec::new(),
|
seats: Vec::new(),
|
||||||
windows: Vec::new(),
|
windows: Vec::new(),
|
||||||
|
blur_surfaces: HashMap::new(),
|
||||||
|
corner_radii: HashMap::new(),
|
||||||
layer_surfaces: Vec::new(),
|
layer_surfaces: Vec::new(),
|
||||||
popups: Vec::new(),
|
popups: Vec::new(),
|
||||||
lock_surfaces: Vec::new(),
|
lock_surfaces: Vec::new(),
|
||||||
|
|
@ -398,6 +401,7 @@ impl SctkEventLoop {
|
||||||
preedit: None,
|
preedit: None,
|
||||||
pending_delete: None,
|
pending_delete: None,
|
||||||
pending_commit: None,
|
pending_commit: None,
|
||||||
|
pending_blur: HashMap::new(),
|
||||||
},
|
},
|
||||||
_features: Default::default(),
|
_features: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
@ -484,6 +488,23 @@ impl SctkEventLoop {
|
||||||
let had_events = !state.state.sctk_events.is_empty();
|
let had_events = !state.state.sctk_events.is_empty();
|
||||||
let mut wake_up = had_events;
|
let mut wake_up = had_events;
|
||||||
|
|
||||||
|
for e in state.state.sctk_events.drain(..) {
|
||||||
|
if let SctkEvent::Winit(id, e) = e {
|
||||||
|
_ = state
|
||||||
|
.state
|
||||||
|
.events_sender
|
||||||
|
.unbounded_send(Control::Winit(id, e));
|
||||||
|
} else {
|
||||||
|
_ =
|
||||||
|
state
|
||||||
|
.state
|
||||||
|
.events_sender
|
||||||
|
.unbounded_send(Control::PlatformSpecific(
|
||||||
|
crate::platform_specific::Event::Wayland(e),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for s in
|
for s in
|
||||||
state
|
state
|
||||||
.state
|
.state
|
||||||
|
|
@ -528,22 +549,6 @@ impl SctkEventLoop {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for e in state.state.sctk_events.drain(..) {
|
|
||||||
if let SctkEvent::Winit(id, e) = e {
|
|
||||||
_ = state
|
|
||||||
.state
|
|
||||||
.events_sender
|
|
||||||
.unbounded_send(Control::Winit(id, e));
|
|
||||||
} else {
|
|
||||||
_ =
|
|
||||||
state
|
|
||||||
.state
|
|
||||||
.events_sender
|
|
||||||
.unbounded_send(Control::PlatformSpecific(
|
|
||||||
crate::platform_specific::Event::Wayland(e),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if wake_up {
|
if wake_up {
|
||||||
state.state.proxy.wake_up();
|
state.state.proxy.wake_up();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use cctk::sctk::reexports::calloop;
|
use cctk::sctk::reexports::calloop;
|
||||||
use iced_futures::futures::{
|
use iced_futures::futures::{
|
||||||
|
Sink,
|
||||||
channel::mpsc,
|
channel::mpsc,
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
Sink,
|
|
||||||
};
|
};
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,9 @@ use crate::{
|
||||||
Control,
|
Control,
|
||||||
handlers::{
|
handlers::{
|
||||||
activation::IcedRequestData,
|
activation::IcedRequestData,
|
||||||
|
ext_background_effect,
|
||||||
overlap::{OverlapNotificationV1, OverlapNotifyV1},
|
overlap::{OverlapNotificationV1, OverlapNotifyV1},
|
||||||
|
shell::corner_radius::CornerRadiusWrapper,
|
||||||
text_input::{Preedit, TextInputManager},
|
text_input::{Preedit, TextInputManager},
|
||||||
},
|
},
|
||||||
platform_specific::{
|
platform_specific::{
|
||||||
|
|
@ -25,11 +27,10 @@ use iced_futures::{
|
||||||
};
|
};
|
||||||
use raw_window_handle::HasWindowHandle;
|
use raw_window_handle::HasWindowHandle;
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet, hash_map::Entry},
|
||||||
convert::Infallible,
|
convert::Infallible,
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
sync::{Arc, Mutex, atomic::AtomicU32},
|
sync::{Arc, Mutex, atomic::AtomicU32},
|
||||||
thread::panicking,
|
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
use wayland_backend::client::ObjectId;
|
use wayland_backend::client::ObjectId;
|
||||||
|
|
@ -40,17 +41,13 @@ use winit::{
|
||||||
|
|
||||||
use cctk::{
|
use cctk::{
|
||||||
cosmic_protocols::{
|
cosmic_protocols::{
|
||||||
corner_radius::v1::client::{
|
corner_radius::v1::client::cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1,
|
||||||
cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1,
|
|
||||||
cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1,
|
|
||||||
},
|
|
||||||
overlap_notify::v1::client::zcosmic_overlap_notification_v1::ZcosmicOverlapNotificationV1,
|
overlap_notify::v1::client::zcosmic_overlap_notification_v1::ZcosmicOverlapNotificationV1,
|
||||||
},
|
},
|
||||||
sctk::{
|
sctk::{
|
||||||
activation::{ActivationState, RequestData},
|
activation::{ActivationState, RequestData},
|
||||||
compositor::CompositorState,
|
compositor::CompositorState,
|
||||||
error::GlobalError,
|
error::GlobalError,
|
||||||
globals::GlobalData,
|
|
||||||
output::OutputState,
|
output::OutputState,
|
||||||
reexports::{
|
reexports::{
|
||||||
calloop::{LoopHandle, timer::TimeoutAction},
|
calloop::{LoopHandle, timer::TimeoutAction},
|
||||||
|
|
@ -66,11 +63,12 @@ use cctk::{
|
||||||
wl_touch::WlTouch,
|
wl_touch::WlTouch,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
protocols_wlr::layer_shell::v1::client::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
|
||||||
},
|
},
|
||||||
registry::RegistryState,
|
registry::RegistryState,
|
||||||
seat::{
|
seat::{
|
||||||
SeatState,
|
SeatState,
|
||||||
keyboard::KeyEvent,
|
keyboard::{KeyEvent, Modifiers},
|
||||||
pointer::{CursorIcon, PointerData, ThemedPointer},
|
pointer::{CursorIcon, PointerData, ThemedPointer},
|
||||||
touch::TouchData,
|
touch::TouchData,
|
||||||
},
|
},
|
||||||
|
|
@ -96,7 +94,6 @@ use cctk::{
|
||||||
};
|
};
|
||||||
use iced_runtime::{
|
use iced_runtime::{
|
||||||
core::{self, Point, touch},
|
core::{self, Point, touch},
|
||||||
keyboard::Modifiers,
|
|
||||||
platform_specific::{
|
platform_specific::{
|
||||||
self,
|
self,
|
||||||
wayland::{
|
wayland::{
|
||||||
|
|
@ -108,6 +105,10 @@ use iced_runtime::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use wayland_protocols::{
|
use wayland_protocols::{
|
||||||
|
ext::background_effect::v1::client::{
|
||||||
|
ext_background_effect_manager_v1,
|
||||||
|
ext_background_effect_surface_v1::ExtBackgroundEffectSurfaceV1,
|
||||||
|
},
|
||||||
wp::{
|
wp::{
|
||||||
fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1,
|
fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1,
|
||||||
keyboard_shortcuts_inhibit::zv1::client::{
|
keyboard_shortcuts_inhibit::zv1::client::{
|
||||||
|
|
@ -341,22 +342,12 @@ pub struct SctkPopupData {
|
||||||
pub(crate) grab: bool,
|
pub(crate) grab: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct MyCosmicCornerRadiusToplevelV1(CosmicCornerRadiusToplevelV1);
|
|
||||||
|
|
||||||
impl Drop for MyCosmicCornerRadiusToplevelV1 {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.0.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct SctkCornerRadius(Arc<MyCosmicCornerRadiusToplevelV1>);
|
pub struct SctkCornerRadius(Arc<CornerRadiusWrapper>);
|
||||||
|
|
||||||
pub struct SctkWindow {
|
pub struct SctkWindow {
|
||||||
pub(crate) window: Arc<dyn winit::window::Window>,
|
pub(crate) window: Arc<dyn winit::window::Window>,
|
||||||
pub(crate) id: core::window::Id,
|
pub(crate) id: core::window::Id,
|
||||||
pub(crate) corner_radius: Option<(SctkCornerRadius, Option<CornerRadius>)>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SctkWindow {
|
impl SctkWindow {
|
||||||
|
|
@ -443,6 +434,8 @@ pub struct SctkState {
|
||||||
pub(crate) popups: Vec<SctkPopup>,
|
pub(crate) popups: Vec<SctkPopup>,
|
||||||
pub(crate) subsurfaces: Vec<SctkSubsurface>,
|
pub(crate) subsurfaces: Vec<SctkSubsurface>,
|
||||||
pub(crate) lock_surfaces: Vec<SctkLockSurface>,
|
pub(crate) lock_surfaces: Vec<SctkLockSurface>,
|
||||||
|
pub(crate) blur_surfaces: HashMap<core::window::Id, ExtBackgroundEffectSurfaceV1>,
|
||||||
|
pub(crate) corner_radii: HashMap<core::window::Id, (SctkCornerRadius, Option<CornerRadius>)>,
|
||||||
pub(crate) touch_points: HashMap<touch::Finger, (WlSurface, Point)>,
|
pub(crate) touch_points: HashMap<touch::Finger, (WlSurface, Point)>,
|
||||||
|
|
||||||
/// Window updates, which are coming from SCTK or the compositor, which require
|
/// Window updates, which are coming from SCTK or the compositor, which require
|
||||||
|
|
@ -485,6 +478,7 @@ pub struct SctkState {
|
||||||
pub(crate) toplevel_info: Option<ToplevelInfoState>,
|
pub(crate) toplevel_info: Option<ToplevelInfoState>,
|
||||||
pub(crate) toplevel_manager: Option<ToplevelManagerState>,
|
pub(crate) toplevel_manager: Option<ToplevelManagerState>,
|
||||||
pub(crate) subsurface_state: Option<SubsurfaceState>,
|
pub(crate) subsurface_state: Option<SubsurfaceState>,
|
||||||
|
pub(crate) ext_background_effect_manager: Option<ext_background_effect::ExtBackgroundEffectManager>,
|
||||||
|
|
||||||
pub(crate) activation_token_ctr: u32,
|
pub(crate) activation_token_ctr: u32,
|
||||||
pub(crate) token_senders: HashMap<u32, oneshot::Sender<Option<String>>>,
|
pub(crate) token_senders: HashMap<u32, oneshot::Sender<Option<String>>>,
|
||||||
|
|
@ -495,6 +489,7 @@ pub struct SctkState {
|
||||||
|
|
||||||
pub(crate) corner_radius_manager: Option<CosmicCornerRadiusManagerV1>,
|
pub(crate) corner_radius_manager: Option<CosmicCornerRadiusManagerV1>,
|
||||||
pub(crate) pending_corner_radius: HashMap<core::window::Id, CornerRadius>,
|
pub(crate) pending_corner_radius: HashMap<core::window::Id, CornerRadius>,
|
||||||
|
pub(crate) pending_blur: HashMap<core::window::Id, Vec<Rectangle>>,
|
||||||
|
|
||||||
pub(crate) text_input_manager: Option<TextInputManager>,
|
pub(crate) text_input_manager: Option<TextInputManager>,
|
||||||
pub(crate) text_input: Option<Arc<ZwpTextInputV3>>,
|
pub(crate) text_input: Option<Arc<ZwpTextInputV3>>,
|
||||||
|
|
@ -506,6 +501,10 @@ pub struct SctkState {
|
||||||
/// An error that occurred while running an application.
|
/// An error that occurred while running an application.
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum PopupCreationError {
|
pub enum PopupCreationError {
|
||||||
|
/// The requested ID already exists.
|
||||||
|
#[error("The requested ID already exists")]
|
||||||
|
AlreadyExists,
|
||||||
|
|
||||||
/// Positioner creation failed
|
/// Positioner creation failed
|
||||||
#[error("Positioner creation failed")]
|
#[error("Positioner creation failed")]
|
||||||
PositionerCreationFailed(GlobalError),
|
PositionerCreationFailed(GlobalError),
|
||||||
|
|
@ -526,6 +525,10 @@ pub enum PopupCreationError {
|
||||||
/// An error that occurred while running an application.
|
/// An error that occurred while running an application.
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum LayerSurfaceCreationError {
|
pub enum LayerSurfaceCreationError {
|
||||||
|
/// The requested ID already exists.
|
||||||
|
#[error("The requested ID already exists")]
|
||||||
|
AlreadyExists,
|
||||||
|
|
||||||
/// Layer shell is not supported by the compositor
|
/// Layer shell is not supported by the compositor
|
||||||
#[error("Layer shell is not supported by the compositor")]
|
#[error("Layer shell is not supported by the compositor")]
|
||||||
LayerShellNotSupported,
|
LayerShellNotSupported,
|
||||||
|
|
@ -542,6 +545,10 @@ pub enum LayerSurfaceCreationError {
|
||||||
/// An error that occurred while running an application.
|
/// An error that occurred while running an application.
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum SubsurfaceCreationError {
|
pub enum SubsurfaceCreationError {
|
||||||
|
/// The requested ID already exists.
|
||||||
|
#[error("The requested ID already exists")]
|
||||||
|
AlreadyExists,
|
||||||
|
|
||||||
/// Subsurface creation failed
|
/// Subsurface creation failed
|
||||||
#[error("Subsurface creation failed")]
|
#[error("Subsurface creation failed")]
|
||||||
CreationFailed(GlobalError),
|
CreationFailed(GlobalError),
|
||||||
|
|
@ -666,9 +673,7 @@ impl SctkState {
|
||||||
|
|
||||||
// TODO winit sets cursor size after handling the change for the window, so maybe that should be done as well.
|
// TODO winit sets cursor size after handling the change for the window, so maybe that should be done as well.
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl SctkState {
|
|
||||||
pub fn get_popup(
|
pub fn get_popup(
|
||||||
&mut self,
|
&mut self,
|
||||||
settings: SctkPopupSettings,
|
settings: SctkPopupSettings,
|
||||||
|
|
@ -682,6 +687,18 @@ impl SctkState {
|
||||||
),
|
),
|
||||||
PopupCreationError,
|
PopupCreationError,
|
||||||
> {
|
> {
|
||||||
|
if self.layer_surfaces.iter().any(|s| s.id == settings.id)
|
||||||
|
|| self.windows.iter().any(|w| w.id == settings.id)
|
||||||
|
|| self.popups.iter().any(|p| p.data.id == settings.id)
|
||||||
|
|| self.subsurfaces.iter().any(|s| s.id == settings.id)
|
||||||
|
{
|
||||||
|
log::warn!(
|
||||||
|
"Popup surface with id {:?} already exists",
|
||||||
|
settings.id
|
||||||
|
);
|
||||||
|
return Err(PopupCreationError::AlreadyExists);
|
||||||
|
}
|
||||||
|
|
||||||
let (parent, toplevel) = if let Some(parent) =
|
let (parent, toplevel) = if let Some(parent) =
|
||||||
self.layer_surfaces.iter().find(|l| l.id == settings.parent)
|
self.layer_surfaces.iter().find(|l| l.id == settings.parent)
|
||||||
{
|
{
|
||||||
|
|
@ -823,6 +840,7 @@ impl SctkState {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let toplevel = toplevel.clone();
|
||||||
|
|
||||||
popup.xdg_surface().set_window_geometry(
|
popup.xdg_surface().set_window_geometry(
|
||||||
0,
|
0,
|
||||||
|
|
@ -855,7 +873,10 @@ impl SctkState {
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = wl_surface.frame(&self.queue_handle, wl_surface.clone());
|
_ = wl_surface.frame(&self.queue_handle, wl_surface.clone());
|
||||||
wl_surface.commit();
|
|
||||||
|
if let Some(blur) = self.pending_blur.remove(&settings.id) {
|
||||||
|
self.apply_blur(settings.id, Some(blur), &wl_surface);
|
||||||
|
}
|
||||||
|
|
||||||
let wp_viewport = self.viewporter_state.as_ref().map(|state| {
|
let wp_viewport = self.viewporter_state.as_ref().map(|state| {
|
||||||
let viewport =
|
let viewport =
|
||||||
|
|
@ -888,6 +909,13 @@ impl SctkState {
|
||||||
close_with_children: settings.close_with_children,
|
close_with_children: settings.close_with_children,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if let Some(corners) = self.pending_corner_radius.remove(&settings.id) {
|
||||||
|
self.handle_action(Action::RoundedCorners(
|
||||||
|
settings.id,
|
||||||
|
Some(corners),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
wl_surface.commit();
|
||||||
Ok((
|
Ok((
|
||||||
settings.id,
|
settings.id,
|
||||||
parent.wl_surface().clone(),
|
parent.wl_surface().clone(),
|
||||||
|
|
@ -922,6 +950,15 @@ impl SctkState {
|
||||||
IcedOutput::Output(output) => Some(output),
|
IcedOutput::Output(output) => Some(output),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if self.layer_surfaces.iter().any(|s| s.id == id)
|
||||||
|
|| self.windows.iter().any(|w| w.id == id)
|
||||||
|
|| self.popups.iter().any(|p| p.data.id == id)
|
||||||
|
|| self.subsurfaces.iter().any(|s| s.id == id)
|
||||||
|
{
|
||||||
|
log::warn!("Layer surface with id {id:?} already exists");
|
||||||
|
return Err(LayerSurfaceCreationError::AlreadyExists);
|
||||||
|
}
|
||||||
|
|
||||||
let layer_shell = self
|
let layer_shell = self
|
||||||
.layer_shell
|
.layer_shell
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|
@ -974,7 +1011,9 @@ impl SctkState {
|
||||||
layer_surface.set_input_region(Some(®ion));
|
layer_surface.set_input_region(Some(®ion));
|
||||||
region.destroy();
|
region.destroy();
|
||||||
}
|
}
|
||||||
layer_surface.commit();
|
if let Some(blur) = self.pending_blur.remove(&id) {
|
||||||
|
self.apply_blur(id, Some(blur), &wl_surface);
|
||||||
|
}
|
||||||
|
|
||||||
let wp_viewport = self.viewporter_state.as_ref().map(|state| {
|
let wp_viewport = self.viewporter_state.as_ref().map(|state| {
|
||||||
state.get_viewport(layer_surface.wl_surface(), &self.queue_handle)
|
state.get_viewport(layer_surface.wl_surface(), &self.queue_handle)
|
||||||
|
|
@ -1008,6 +1047,10 @@ impl SctkState {
|
||||||
wp_fractional_scale,
|
wp_fractional_scale,
|
||||||
common: common.clone(),
|
common: common.clone(),
|
||||||
});
|
});
|
||||||
|
if let Some(corners) = self.pending_corner_radius.remove(&id) {
|
||||||
|
_ = self.handle_action(Action::RoundedCorners(id, Some(corners)));
|
||||||
|
}
|
||||||
|
layer_surface.commit();
|
||||||
Ok((id, CommonSurface::Layer(layer_surface), common))
|
Ok((id, CommonSurface::Layer(layer_surface), common))
|
||||||
}
|
}
|
||||||
pub fn get_lock_surface(
|
pub fn get_lock_surface(
|
||||||
|
|
@ -1015,6 +1058,14 @@ impl SctkState {
|
||||||
id: core::window::Id,
|
id: core::window::Id,
|
||||||
output: &WlOutput,
|
output: &WlOutput,
|
||||||
) -> Option<(CommonSurface, Arc<Mutex<Common>>)> {
|
) -> Option<(CommonSurface, Arc<Mutex<Common>>)> {
|
||||||
|
if self.layer_surfaces.iter().any(|s| s.id == id)
|
||||||
|
|| self.windows.iter().any(|w| w.id == id)
|
||||||
|
|| self.popups.iter().any(|p| p.data.id == id)
|
||||||
|
|| self.subsurfaces.iter().any(|s| s.id == id)
|
||||||
|
{
|
||||||
|
log::warn!("Lock surface with id {id:?} already exists");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
if let Some(lock) = self.session_lock.as_ref() {
|
if let Some(lock) = self.session_lock.as_ref() {
|
||||||
let wl_surface =
|
let wl_surface =
|
||||||
self.compositor_state.create_surface(&self.queue_handle);
|
self.compositor_state.create_surface(&self.queue_handle);
|
||||||
|
|
@ -1088,9 +1139,20 @@ impl SctkState {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
platform_specific::wayland::layer_surface::Action::Destroy(id) => {
|
platform_specific::wayland::layer_surface::Action::Destroy(id) => {
|
||||||
|
if let Some(p) = self
|
||||||
|
.popups
|
||||||
|
.iter().find_map(|p|
|
||||||
|
self.layer_surfaces.iter().find_map(|l| if l.id == id && l.surface.wl_surface() == p.data.parent.wl_surface() {Some(p.data.id)} else {None})) {
|
||||||
|
_ = self.handle_action(Action::Popup(platform_specific::wayland::popup::Action::Destroy { id: p }));
|
||||||
|
}
|
||||||
if let Some(i) = self.layer_surfaces.iter().position(|l| l.id == id) {
|
if let Some(i) = self.layer_surfaces.iter().position(|l| l.id == id) {
|
||||||
let l = self.layer_surfaces.remove(i);
|
let l = self.layer_surfaces.remove(i);
|
||||||
|
|
||||||
|
if let Some(blurred) = self.blur_surfaces.remove(&l.id) {
|
||||||
|
blurred.destroy();
|
||||||
|
}
|
||||||
|
_ = self.corner_radii.remove(&id);
|
||||||
|
|
||||||
let (removed, remaining): (Vec<_>, Vec<_>) = self
|
let (removed, remaining): (Vec<_>, Vec<_>) = self
|
||||||
.subsurfaces
|
.subsurfaces
|
||||||
.drain(..)
|
.drain(..)
|
||||||
|
|
@ -1112,6 +1174,7 @@ impl SctkState {
|
||||||
if let Some(destroyed) = self.id_map.remove(&l.surface.wl_surface().id()) {
|
if let Some(destroyed) = self.id_map.remove(&l.surface.wl_surface().id()) {
|
||||||
_ = self.destroyed.insert(destroyed);
|
_ = self.destroyed.insert(destroyed);
|
||||||
}
|
}
|
||||||
|
|
||||||
send_event(&self.events_sender, &self.proxy, SctkEvent::LayerSurfaceEvent {
|
send_event(&self.events_sender, &self.proxy, SctkEvent::LayerSurfaceEvent {
|
||||||
variant: LayerSurfaceEventVariant::Done,
|
variant: LayerSurfaceEventVariant::Done,
|
||||||
id: l.surface.wl_surface().clone(),
|
id: l.surface.wl_surface().clone(),
|
||||||
|
|
@ -1186,6 +1249,14 @@ impl SctkState {
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
platform_specific::wayland::layer_surface::Action::Padding { id, padding } => {
|
||||||
|
// corner radius padding
|
||||||
|
if let Some((protocol_object, _)) = self.corner_radii.get_mut(&id) {
|
||||||
|
if let CornerRadiusWrapper::Wlr(protocol_object) = protocol_object.0.as_ref() {
|
||||||
|
protocol_object.set_padding(padding.top, padding.right, padding.bottom, padding.left);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Action::Popup(action) => match action {
|
Action::Popup(action) => match action {
|
||||||
platform_specific::wayland::popup::Action::Popup { popup: settings } => {
|
platform_specific::wayland::popup::Action::Popup { popup: settings } => {
|
||||||
|
|
@ -1290,7 +1361,6 @@ impl SctkState {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// log::error!("Invalid popup Id {:?}", popup.id);
|
|
||||||
} else {
|
} else {
|
||||||
self.pending_popup = None;
|
self.pending_popup = None;
|
||||||
match self.get_popup(settings) {
|
match self.get_popup(settings) {
|
||||||
|
|
@ -1344,6 +1414,12 @@ impl SctkState {
|
||||||
_ = self.destroyed.insert(id);
|
_ = self.destroyed.insert(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(blurred) = self.blur_surfaces.remove(&id) {
|
||||||
|
blurred.destroy();
|
||||||
|
}
|
||||||
|
_ = self.corner_radii.remove(&id);
|
||||||
|
|
||||||
|
|
||||||
let (removed, remaining): (Vec<_>, Vec<_>) = self
|
let (removed, remaining): (Vec<_>, Vec<_>) = self
|
||||||
.subsurfaces
|
.subsurfaces
|
||||||
.drain(..)
|
.drain(..)
|
||||||
|
|
@ -1379,6 +1455,48 @@ impl SctkState {
|
||||||
SctkEvent::PopupEvent { variant: crate::sctk_event::PopupEventVariant::Size(width, height), toplevel_id: sctk_popup.data.parent.wl_surface().clone(), parent_id: sctk_popup.data.parent.wl_surface().clone(), id: surface });
|
SctkEvent::PopupEvent { variant: crate::sctk_event::PopupEventVariant::Size(width, height), toplevel_id: sctk_popup.data.parent.wl_surface().clone(), parent_id: sctk_popup.data.parent.wl_surface().clone(), id: surface });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
platform_specific::wayland::popup::Action::Reposition { id, positioner } => {
|
||||||
|
if let Some(sctk_popup) = self
|
||||||
|
.popups
|
||||||
|
.iter_mut()
|
||||||
|
.find(|s| s.data.id == id)
|
||||||
|
{
|
||||||
|
sctk_popup.data.positioner.set_anchor(positioner.anchor);
|
||||||
|
sctk_popup.data.positioner.set_anchor_rect(
|
||||||
|
positioner.anchor_rect.x,
|
||||||
|
positioner.anchor_rect.y,
|
||||||
|
positioner.anchor_rect.width,
|
||||||
|
positioner.anchor_rect.height,
|
||||||
|
);
|
||||||
|
if let Ok(constraint_adjustment) =
|
||||||
|
positioner.constraint_adjustment.try_into()
|
||||||
|
{
|
||||||
|
sctk_popup.data.positioner.set_constraint_adjustment(constraint_adjustment);
|
||||||
|
}
|
||||||
|
sctk_popup.data.positioner.set_gravity(positioner.gravity);
|
||||||
|
sctk_popup.data.positioner.set_offset(
|
||||||
|
positioner.offset.0,
|
||||||
|
positioner.offset.1,
|
||||||
|
);
|
||||||
|
if positioner.reactive {
|
||||||
|
sctk_popup.data.positioner.set_reactive();
|
||||||
|
}
|
||||||
|
let guard =sctk_popup.common.lock().unwrap();
|
||||||
|
let w = guard.size.width;
|
||||||
|
let h = guard.size.height;
|
||||||
|
drop(guard);
|
||||||
|
let size = positioner.size.unwrap_or((w, h));
|
||||||
|
sctk_popup.popup
|
||||||
|
.xdg_surface()
|
||||||
|
.set_window_geometry(0, 0, w as i32, h as i32);
|
||||||
|
sctk_popup.update_viewport(w, h);
|
||||||
|
// update positioner
|
||||||
|
sctk_popup.data.positioner.set_size(w as i32, h as i32);
|
||||||
|
sctk_popup.popup.reposition(&sctk_popup.data.positioner, TOKEN_CTR.fetch_add(1, std::sync::atomic::Ordering::Relaxed)); let surface = sctk_popup.popup.wl_surface().clone();
|
||||||
|
_ = send_event(&self.events_sender, &self.proxy,
|
||||||
|
SctkEvent::PopupEvent { variant: crate::sctk_event::PopupEventVariant::Size(size.0, size.1), toplevel_id: sctk_popup.data.parent.wl_surface().clone(), parent_id: sctk_popup.data.parent.wl_surface().clone(), id: surface });
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Action::Activation(activation_event) => match activation_event {
|
Action::Activation(activation_event) => match activation_event {
|
||||||
platform_specific::wayland::activation::Action::RequestToken { app_id, window, channel } => {
|
platform_specific::wayland::activation::Action::RequestToken { app_id, window, channel } => {
|
||||||
|
|
@ -1455,6 +1573,7 @@ impl SctkState {
|
||||||
// TODO how to handle this when there's no lock?
|
// TODO how to handle this when there's no lock?
|
||||||
if let Some((surface, _)) = self.get_lock_surface(id, &output) {
|
if let Some((surface, _)) = self.get_lock_surface(id, &output) {
|
||||||
let wl_surface = surface.wl_surface();
|
let wl_surface = surface.wl_surface();
|
||||||
|
|
||||||
receive_frame(&mut self.frame_status, &wl_surface);
|
receive_frame(&mut self.frame_status, &wl_surface);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1465,6 +1584,11 @@ impl SctkState {
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
let surface = self.lock_surfaces.remove(i);
|
let surface = self.lock_surfaces.remove(i);
|
||||||
|
if let Some(blurred) = self.blur_surfaces.remove(&surface.id) {
|
||||||
|
blurred.destroy();
|
||||||
|
}
|
||||||
|
_ = self.corner_radii.remove(&id);
|
||||||
|
|
||||||
let (removed, remaining): (Vec<_>, Vec<_>) = self
|
let (removed, remaining): (Vec<_>, Vec<_>) = self
|
||||||
.subsurfaces
|
.subsurfaces
|
||||||
.drain(..)
|
.drain(..)
|
||||||
|
|
@ -1504,7 +1628,7 @@ impl SctkState {
|
||||||
};
|
};
|
||||||
let notification = overlap_notify_state.notify.notify_on_overlap(wlr, &self.queue_handle, OverlapNotificationV1 { surface: layer_surface.surface.wl_surface().clone() });
|
let notification = overlap_notify_state.notify.notify_on_overlap(wlr, &self.queue_handle, OverlapNotificationV1 { surface: layer_surface.surface.wl_surface().clone() });
|
||||||
_ = self.overlap_notifications.insert(my_id, notification);
|
_ = self.overlap_notifications.insert(my_id, notification);
|
||||||
} else {
|
} else if !enabled {
|
||||||
_ = self.overlap_notifications.remove(&my_id);
|
_ = self.overlap_notifications.remove(&my_id);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1536,7 +1660,7 @@ impl SctkState {
|
||||||
let mut destroyed = vec![];
|
let mut destroyed = vec![];
|
||||||
if let Some(subsurface) = self.subsurfaces.iter().position(|s| s.id == id) {
|
if let Some(subsurface) = self.subsurfaces.iter().position(|s| s.id == id) {
|
||||||
let subsurface = self.subsurfaces.remove(subsurface);
|
let subsurface = self.subsurfaces.remove(subsurface);
|
||||||
destroyed.push((subsurface.instance.wl_surface.clone(), subsurface.instance.parent.clone()));
|
destroyed.push((subsurface.instance.wl_surface.clone(), subsurface.instance.parent.clone(), subsurface.id));
|
||||||
|
|
||||||
subsurface.instance.wl_surface.attach(None, 0, 0);
|
subsurface.instance.wl_surface.attach(None, 0, 0);
|
||||||
subsurface.instance.wl_surface.commit();
|
subsurface.instance.wl_surface.commit();
|
||||||
|
|
@ -1544,7 +1668,12 @@ impl SctkState {
|
||||||
SctkEvent::SubsurfaceEvent( crate::sctk_event::SubsurfaceEventVariant::Destroyed(subsurface.instance) )
|
SctkEvent::SubsurfaceEvent( crate::sctk_event::SubsurfaceEventVariant::Destroyed(subsurface.instance) )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for (destroyed, parent) in destroyed {
|
for (destroyed, parent, id) in destroyed {
|
||||||
|
if let Some(blurred) = self.blur_surfaces.remove(&id) {
|
||||||
|
blurred.destroy();
|
||||||
|
}
|
||||||
|
_ = self.corner_radii.remove(&id);
|
||||||
|
|
||||||
if let Some((wl_surface, f)) = self.seats.iter_mut().find(|f| {
|
if let Some((wl_surface, f)) = self.seats.iter_mut().find(|f| {
|
||||||
f.kbd_focus.as_ref().is_some_and(|f| *f == destroyed)
|
f.kbd_focus.as_ref().is_some_and(|f| *f == destroyed)
|
||||||
}).and_then(|f| Some((parent, &mut f.kbd_focus))) {
|
}).and_then(|f| Some((parent, &mut f.kbd_focus))) {
|
||||||
|
|
@ -1572,9 +1701,26 @@ impl SctkState {
|
||||||
}
|
}
|
||||||
Action::RoundedCorners(id, v) => {
|
Action::RoundedCorners(id, v) => {
|
||||||
if let Some(manager) = self.corner_radius_manager.as_ref() {
|
if let Some(manager) = self.corner_radius_manager.as_ref() {
|
||||||
if let Some(w) = self.windows.iter_mut().find(|w| w.id == id) {
|
enum Surface {
|
||||||
let geo_size: LogicalSize<f64> = w.window.surface_size().cast::<f64>().to_logical(w.window.scale_factor());
|
Xdg(XdgSurface, Option<XdgToplevel>),
|
||||||
let half_min_dim = ((geo_size.width as u32).min(geo_size.height as u32) / 2);
|
Wlr(ZwlrLayerSurfaceV1),
|
||||||
|
}
|
||||||
|
let s = if let Some(w) = self.windows.iter_mut().find(|w| w.id == id) {
|
||||||
|
Some((Surface::Xdg(w.xdg_surface(&self.connection), Some(w.xdg_toplevel(&self.connection))), w.window.surface_size().cast::<f64>().to_logical(w.window.scale_factor())))
|
||||||
|
} else if let Some(p) = self.popups.iter_mut().find(|w| w.data.id == id) {
|
||||||
|
let guard = p.common.lock().unwrap();
|
||||||
|
Some((Surface::Xdg(p.popup.xdg_surface().clone(), None), guard.size.cast::<f64>()))
|
||||||
|
} else if let Some(l) = self.layer_surfaces.iter_mut().find(|l| l.id == id) {
|
||||||
|
let guard = l.common.lock().unwrap();
|
||||||
|
match l.surface.kind() {
|
||||||
|
SurfaceKind::Wlr(l) => Some((Surface::Wlr(l.clone()), guard.size.cast::<f64>())),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
if let Some((s, geo_size)) = s {
|
||||||
|
let half_min_dim = (geo_size.width as u32).min(geo_size.height as u32) / 2;
|
||||||
|
|
||||||
if let Some(radii) = v {
|
if let Some(radii) = v {
|
||||||
let adjusted_radii = CornerRadius {
|
let adjusted_radii = CornerRadius {
|
||||||
|
|
@ -1583,37 +1729,73 @@ impl SctkState {
|
||||||
bottom_right: radii.bottom_right.min(half_min_dim),
|
bottom_right: radii.bottom_right.min(half_min_dim),
|
||||||
bottom_left: radii.bottom_left.min(half_min_dim),
|
bottom_left: radii.bottom_left.min(half_min_dim),
|
||||||
};
|
};
|
||||||
if let Some((protocol_object, corner_radii)) = w.corner_radius.as_mut() {
|
if let Some((protocol_object, corner_radii)) = self.corner_radii.get_mut(&id) {
|
||||||
if *corner_radii != Some(adjusted_radii) {
|
if *corner_radii != Some(adjusted_radii) {
|
||||||
protocol_object.0.0.set_radius(
|
match protocol_object.0.as_ref() {
|
||||||
|
CornerRadiusWrapper::Xdg(protocol_object) => protocol_object.set_radius(
|
||||||
|
adjusted_radii.top_left,
|
||||||
|
adjusted_radii.top_right,
|
||||||
|
adjusted_radii.bottom_right,
|
||||||
|
adjusted_radii.bottom_left,
|
||||||
|
),
|
||||||
|
CornerRadiusWrapper::Wlr(protocol_object) => protocol_object.set_radius(
|
||||||
|
adjusted_radii.top_left,
|
||||||
|
adjusted_radii.top_right,
|
||||||
|
adjusted_radii.bottom_right,
|
||||||
|
adjusted_radii.bottom_left,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
*corner_radii = Some(adjusted_radii.clone());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
let protocol_object = match s {
|
||||||
|
Surface::Xdg(s, w) => {
|
||||||
|
if manager.version() == 1 {
|
||||||
|
if let Some(w) = w {
|
||||||
|
CornerRadiusWrapper::Xdg(manager.get_corner_radius(&w, &self.queue_handle, ()))
|
||||||
|
} else {
|
||||||
|
log::error!("Corner radius is not supported for popups on xdg shell v1");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
CornerRadiusWrapper::Xdg(manager.get_corner_radius_surface(&s, &self.queue_handle, ()))}
|
||||||
|
}
|
||||||
|
Surface::Wlr(l) => {
|
||||||
|
if manager.version() == 1 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
CornerRadiusWrapper::Wlr(manager.get_corner_radius_layer(&l, &self.queue_handle, ()))}
|
||||||
|
};
|
||||||
|
match &protocol_object {
|
||||||
|
CornerRadiusWrapper::Xdg(protocol_object) => protocol_object.set_radius(
|
||||||
adjusted_radii.top_left,
|
adjusted_radii.top_left,
|
||||||
adjusted_radii.top_right,
|
adjusted_radii.top_right,
|
||||||
adjusted_radii.bottom_right,
|
adjusted_radii.bottom_right,
|
||||||
adjusted_radii.bottom_left,
|
adjusted_radii.bottom_left,
|
||||||
);
|
),
|
||||||
*corner_radii = Some(adjusted_radii.clone());
|
CornerRadiusWrapper::Wlr(protocol_object) => protocol_object.set_radius(
|
||||||
}
|
adjusted_radii.top_left,
|
||||||
} else {
|
adjusted_radii.top_right,
|
||||||
let toplevel = w.xdg_toplevel(&self.connection);
|
adjusted_radii.bottom_right,
|
||||||
|
adjusted_radii.bottom_left,
|
||||||
let protocol_object = manager.get_corner_radius(&toplevel, &self.queue_handle, ());
|
)
|
||||||
|
};
|
||||||
protocol_object.set_radius(
|
_ = self.corner_radii.insert(id, (SctkCornerRadius(Arc::new(protocol_object)), Some(adjusted_radii.clone())));
|
||||||
adjusted_radii.top_left,
|
|
||||||
adjusted_radii.top_right,
|
|
||||||
adjusted_radii.bottom_right,
|
|
||||||
adjusted_radii.bottom_left,
|
|
||||||
);
|
|
||||||
w.corner_radius = Some((SctkCornerRadius(Arc::new(MyCosmicCornerRadiusToplevelV1( protocol_object))), Some(adjusted_radii.clone())));
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if let Some(old) = w.corner_radius.as_mut() {
|
if let Some(old) = self.corner_radii.get_mut(&id) {
|
||||||
old.0.0.as_ref().0.unset_radius();
|
match old.0.0.as_ref() {
|
||||||
|
CornerRadiusWrapper::Xdg(protocol_object) => protocol_object.unset_radius(),
|
||||||
|
CornerRadiusWrapper::Wlr(protocol_object) => {
|
||||||
|
protocol_object.unset_radius()
|
||||||
|
}
|
||||||
|
};
|
||||||
old.1 = None;
|
old.1 = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if let Some(v) = v{
|
if let Some(v) = v {
|
||||||
_ = self.pending_corner_radius.insert(id, v);
|
_ = self.pending_corner_radius.insert(id, v);
|
||||||
} else {
|
} else {
|
||||||
_ = self.pending_corner_radius.remove(&id);
|
_ = self.pending_corner_radius.remove(&id);
|
||||||
|
|
@ -1621,10 +1803,95 @@ impl SctkState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Action::BlurSurface(id, rectangles) => {
|
||||||
|
use wayland_protocols::ext::background_effect::v1::client::ext_background_effect_manager_v1;
|
||||||
|
|
||||||
|
if let Some(bg_effect_mgr) = self.ext_background_effect_manager.as_mut() && !bg_effect_mgr.capabilities().contains(ext_background_effect_manager_v1::Capability::Blur) {
|
||||||
|
bg_effect_mgr.enqueue(id, rectangles.clone());
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let s = if let Some(s) = self.popups.iter().find(|s| s.data.id == id) {
|
||||||
|
s.popup.wl_surface()
|
||||||
|
} else if let Some(s) = self.layer_surfaces.iter().find(|s| s.id == id) {
|
||||||
|
s.surface.wl_surface()
|
||||||
|
} else if let Some(s) = self.lock_surfaces.iter().find(|s| s.id == id) {
|
||||||
|
s.session_lock_surface.wl_surface()
|
||||||
|
} else if let Some(subsurface) = self.subsurfaces.iter().find(|s| s.id == id) {
|
||||||
|
&subsurface.instance.wl_surface
|
||||||
|
} else {
|
||||||
|
log::info!("Failed to find surface for blur action");
|
||||||
|
if let Some(rectangles) = rectangles {
|
||||||
|
_ = self.pending_blur.insert(id, rectangles.clone());
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}.clone();
|
||||||
|
self.apply_blur(id, rectangles, &s)
|
||||||
|
},
|
||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn apply_blur(
|
||||||
|
&mut self,
|
||||||
|
id: core::window::Id,
|
||||||
|
rectangles: Option<Vec<Rectangle>>,
|
||||||
|
s: &WlSurface,
|
||||||
|
) {
|
||||||
|
let existing_blur = self.blur_surfaces.entry(id);
|
||||||
|
match (existing_blur, rectangles) {
|
||||||
|
(Entry::Occupied(occupied_entry), None) => {
|
||||||
|
let blur_surface = occupied_entry.remove();
|
||||||
|
blur_surface.destroy();
|
||||||
|
}
|
||||||
|
(Entry::Occupied(mut occupied_entry), Some(rectangles)) => {
|
||||||
|
let blur_surface = occupied_entry.get_mut();
|
||||||
|
let region = self
|
||||||
|
.compositor_state
|
||||||
|
.wl_compositor()
|
||||||
|
.create_region(&self.queue_handle, ());
|
||||||
|
for rect in rectangles.into_iter() {
|
||||||
|
region.add(
|
||||||
|
rect.x.round() as i32,
|
||||||
|
rect.y.round() as i32,
|
||||||
|
rect.width.round() as i32,
|
||||||
|
rect.height.round() as i32,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update existing blur surfaces
|
||||||
|
blur_surface.set_blur_region(Some(®ion));
|
||||||
|
}
|
||||||
|
(Entry::Vacant(..), None) => {
|
||||||
|
// nothing to remove
|
||||||
|
}
|
||||||
|
(Entry::Vacant(vacant_entry), Some(rectangles)) => {
|
||||||
|
if self.ext_background_effect_manager.is_none() {
|
||||||
|
log::error!("Blur effect is not supported.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let region = self
|
||||||
|
.compositor_state
|
||||||
|
.wl_compositor()
|
||||||
|
.create_region(&self.queue_handle, ());
|
||||||
|
for rect in rectangles {
|
||||||
|
region.add(
|
||||||
|
rect.x.round() as i32,
|
||||||
|
rect.y.round() as i32,
|
||||||
|
rect.width.round() as i32,
|
||||||
|
rect.height.round() as i32,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let blur_manager =
|
||||||
|
self.ext_background_effect_manager.as_mut().unwrap();
|
||||||
|
let blur_surface = blur_manager.blur(s, &self.queue_handle);
|
||||||
|
blur_surface.set_blur_region(Some(®ion));
|
||||||
|
_ = vacant_entry.insert(blur_surface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_subsurface(
|
pub fn get_subsurface(
|
||||||
&mut self,
|
&mut self,
|
||||||
settings: SctkSubsurfaceSettings,
|
settings: SctkSubsurfaceSettings,
|
||||||
|
|
@ -1638,9 +1905,17 @@ impl SctkState {
|
||||||
),
|
),
|
||||||
SubsurfaceCreationError,
|
SubsurfaceCreationError,
|
||||||
> {
|
> {
|
||||||
let Some(subsurface_state) = self.subsurface_state.as_ref() else {
|
let Some(subsurface_state) = self.subsurface_state.clone() else {
|
||||||
return Err(SubsurfaceCreationError::Unsupported);
|
return Err(SubsurfaceCreationError::Unsupported);
|
||||||
};
|
};
|
||||||
|
if self.layer_surfaces.iter().any(|s| s.id == settings.id)
|
||||||
|
|| self.windows.iter().any(|w| w.id == settings.id)
|
||||||
|
|| self.popups.iter().any(|p| p.data.id == settings.id)
|
||||||
|
|| self.subsurfaces.iter().any(|s| s.id == settings.id)
|
||||||
|
{
|
||||||
|
log::warn!("Subsurface with id {:?} already exists", settings.id);
|
||||||
|
return Err(SubsurfaceCreationError::AlreadyExists);
|
||||||
|
}
|
||||||
|
|
||||||
let size = settings.size.unwrap_or(Size::new(1., 1.));
|
let size = settings.size.unwrap_or(Size::new(1., 1.));
|
||||||
let half_w = size.width / 2.;
|
let half_w = size.width / 2.;
|
||||||
|
|
@ -1746,7 +2021,9 @@ impl SctkState {
|
||||||
region.destroy();
|
region.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
wl_surface.commit();
|
if let Some(blur) = self.pending_blur.remove(&settings.id) {
|
||||||
|
_ = self.apply_blur(settings.id, Some(blur), &wl_surface);
|
||||||
|
}
|
||||||
|
|
||||||
let wp_viewport = subsurface_state.wp_viewporter.get_viewport(
|
let wp_viewport = subsurface_state.wp_viewporter.get_viewport(
|
||||||
&wl_surface,
|
&wl_surface,
|
||||||
|
|
@ -1822,7 +2099,11 @@ impl SctkState {
|
||||||
// XXX subsurfaces need to be sorted by z in descending order
|
// XXX subsurfaces need to be sorted by z in descending order
|
||||||
self.subsurfaces
|
self.subsurfaces
|
||||||
.sort_by(|a, b| b.instance.z.cmp(&a.instance.z));
|
.sort_by(|a, b| b.instance.z.cmp(&a.instance.z));
|
||||||
|
if let Some(corners) = self.pending_corner_radius.remove(&id) {
|
||||||
|
self.handle_action(Action::RoundedCorners(id, Some(corners)));
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_surface.commit();
|
||||||
Ok((
|
Ok((
|
||||||
id,
|
id,
|
||||||
parent.wl_surface().clone(),
|
parent.wl_surface().clone(),
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,115 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use cctk::sctk;
|
||||||
|
use iced_futures::futures::SinkExt;
|
||||||
|
use iced_runtime::core::Rectangle;
|
||||||
|
use iced_runtime::platform_specific::wayland::Action;
|
||||||
|
use sctk::globals::GlobalData;
|
||||||
|
use sctk::reexports::client::globals::{BindError, GlobalList};
|
||||||
|
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||||
|
use sctk::reexports::client::{Connection, Dispatch, Proxy, QueueHandle, delegate_dispatch};
|
||||||
|
use wayland_protocols::ext::background_effect::v1::client::ext_background_effect_manager_v1::{Capability, Event, ExtBackgroundEffectManagerV1};
|
||||||
|
use wayland_protocols::ext::background_effect::v1::client::ext_background_effect_surface_v1::ExtBackgroundEffectSurfaceV1;
|
||||||
|
|
||||||
|
use crate::event_loop::state::SctkState;
|
||||||
|
use crate::{platform_specific, window};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ExtBackgroundEffectManager {
|
||||||
|
manager: ExtBackgroundEffectManagerV1,
|
||||||
|
capabilities: Capability,
|
||||||
|
queued_blur_actions: HashMap<window::Id, Option<Vec<Rectangle>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExtBackgroundEffectManager {
|
||||||
|
pub fn new(
|
||||||
|
globals: &GlobalList,
|
||||||
|
queue_handle: &QueueHandle<SctkState>,
|
||||||
|
) -> Result<Self, BindError> {
|
||||||
|
let manager = globals.bind(queue_handle, 1..=1, GlobalData)?;
|
||||||
|
Ok(Self {
|
||||||
|
manager,
|
||||||
|
capabilities: Capability::empty(),
|
||||||
|
queued_blur_actions: HashMap::new(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn blur(
|
||||||
|
&mut self,
|
||||||
|
surface: &WlSurface,
|
||||||
|
queue_handle: &QueueHandle<SctkState>,
|
||||||
|
) -> ExtBackgroundEffectSurfaceV1 {
|
||||||
|
self.manager
|
||||||
|
.get_background_effect(surface, queue_handle, ())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enqueue(&mut self, id: window::Id, rects: Option<Vec<Rectangle>>) {
|
||||||
|
_ = self.queued_blur_actions.insert(id, rects);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn capabilities(&self) -> Capability {
|
||||||
|
self.capabilities
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dispatch<ExtBackgroundEffectManagerV1, GlobalData, SctkState>
|
||||||
|
for ExtBackgroundEffectManager
|
||||||
|
{
|
||||||
|
fn event(
|
||||||
|
state: &mut SctkState,
|
||||||
|
_: &ExtBackgroundEffectManagerV1,
|
||||||
|
event: <ExtBackgroundEffectManagerV1 as Proxy>::Event,
|
||||||
|
_: &GlobalData,
|
||||||
|
_: &Connection,
|
||||||
|
_: &QueueHandle<SctkState>,
|
||||||
|
) {
|
||||||
|
match event {
|
||||||
|
Event::Capabilities { flags } => match flags {
|
||||||
|
wayland_client::WEnum::Value(capability) => {
|
||||||
|
let mut queued_actions = Vec::new();
|
||||||
|
if let Some(bg_effect_mgr) =
|
||||||
|
state.ext_background_effect_manager.as_mut()
|
||||||
|
{
|
||||||
|
bg_effect_mgr.capabilities = capability;
|
||||||
|
if capability.contains(Capability::Blur) {
|
||||||
|
queued_actions = bg_effect_mgr
|
||||||
|
.queued_blur_actions
|
||||||
|
.drain()
|
||||||
|
.collect();
|
||||||
|
state
|
||||||
|
.sctk_events
|
||||||
|
.push(crate::sctk_event::SctkEvent::Blur);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (id, rects) in queued_actions {
|
||||||
|
_ = state.handle_action(Action::BlurSurface(id, rects));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wayland_client::WEnum::Unknown(u) => {
|
||||||
|
log::warn!("Unknown value: {u:?}");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
e => {
|
||||||
|
log::warn!("Ignored event {e:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dispatch<ExtBackgroundEffectSurfaceV1, (), SctkState>
|
||||||
|
for ExtBackgroundEffectManager
|
||||||
|
{
|
||||||
|
fn event(
|
||||||
|
_: &mut SctkState,
|
||||||
|
_: &ExtBackgroundEffectSurfaceV1,
|
||||||
|
_: <ExtBackgroundEffectSurfaceV1 as Proxy>::Event,
|
||||||
|
_: &(),
|
||||||
|
_: &Connection,
|
||||||
|
_: &QueueHandle<SctkState>,
|
||||||
|
) {
|
||||||
|
// There is no event
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate_dispatch!(SctkState: [ExtBackgroundEffectManagerV1: GlobalData] => ExtBackgroundEffectManager);
|
||||||
|
delegate_dispatch!(SctkState: [ExtBackgroundEffectSurfaceV1: ()] => ExtBackgroundEffectManager);
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// handlers
|
// handlers
|
||||||
pub mod activation;
|
pub mod activation;
|
||||||
pub mod compositor;
|
pub mod compositor;
|
||||||
|
pub mod ext_background_effect;
|
||||||
pub mod output;
|
pub mod output;
|
||||||
pub mod overlap;
|
pub mod overlap;
|
||||||
pub mod seat;
|
pub mod seat;
|
||||||
|
|
@ -12,14 +13,14 @@ pub mod toplevel;
|
||||||
pub mod wp_fractional_scaling;
|
pub mod wp_fractional_scaling;
|
||||||
pub mod wp_viewporter;
|
pub mod wp_viewporter;
|
||||||
|
|
||||||
use cctk::{sctk::{
|
use cctk::sctk::{
|
||||||
delegate_registry, delegate_shm,
|
delegate_registry, delegate_shm,
|
||||||
output::OutputState,
|
output::OutputState,
|
||||||
registry::{ProvidesRegistryState, RegistryState},
|
registry::{ProvidesRegistryState, RegistryState},
|
||||||
registry_handlers,
|
registry_handlers,
|
||||||
seat::SeatState,
|
seat::SeatState,
|
||||||
shm::{Shm, ShmHandler},
|
shm::{Shm, ShmHandler},
|
||||||
}};
|
};
|
||||||
|
|
||||||
use crate::platform_specific::wayland::event_loop::state::SctkState;
|
use crate::platform_specific::wayland::event_loop::state::SctkState;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,46 @@ use crate::platform_specific::wayland::{
|
||||||
};
|
};
|
||||||
use cctk::sctk::{
|
use cctk::sctk::{
|
||||||
delegate_keyboard,
|
delegate_keyboard,
|
||||||
seat::keyboard::{KeyboardHandler, Keysym},
|
seat::keyboard::{KeyboardHandler, Keysym, Modifiers},
|
||||||
};
|
};
|
||||||
use cctk::sctk::{reexports::client::Proxy, seat::keyboard::RawModifiers};
|
use cctk::sctk::{reexports::client::Proxy, seat::keyboard::RawModifiers};
|
||||||
|
|
||||||
|
fn modifiers_from_keysyms(
|
||||||
|
keysyms: &[Keysym],
|
||||||
|
previous: Modifiers,
|
||||||
|
) -> Modifiers {
|
||||||
|
let mut modifiers = Modifiers {
|
||||||
|
caps_lock: previous.caps_lock,
|
||||||
|
num_lock: previous.num_lock,
|
||||||
|
..Modifiers::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
for keysym in keysyms {
|
||||||
|
match keysym.raw() {
|
||||||
|
xkeysym::key::Shift_L | xkeysym::key::Shift_R => {
|
||||||
|
modifiers.shift = true
|
||||||
|
}
|
||||||
|
xkeysym::key::Control_L | xkeysym::key::Control_R => {
|
||||||
|
modifiers.ctrl = true
|
||||||
|
}
|
||||||
|
xkeysym::key::Alt_L | xkeysym::key::Alt_R => modifiers.alt = true,
|
||||||
|
xkeysym::key::Super_L
|
||||||
|
| xkeysym::key::Super_R
|
||||||
|
| xkeysym::key::Hyper_L
|
||||||
|
| xkeysym::key::Hyper_R
|
||||||
|
| xkeysym::key::Meta_L
|
||||||
|
| xkeysym::key::Meta_R => modifiers.logo = true,
|
||||||
|
xkeysym::key::Caps_Lock | xkeysym::key::Shift_Lock => {
|
||||||
|
modifiers.caps_lock = true
|
||||||
|
}
|
||||||
|
xkeysym::key::Num_Lock => modifiers.num_lock = true,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modifiers
|
||||||
|
}
|
||||||
|
|
||||||
impl KeyboardHandler for SctkState {
|
impl KeyboardHandler for SctkState {
|
||||||
fn enter(
|
fn enter(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
@ -17,9 +53,9 @@ impl KeyboardHandler for SctkState {
|
||||||
surface: &cctk::sctk::reexports::client::protocol::wl_surface::WlSurface,
|
surface: &cctk::sctk::reexports::client::protocol::wl_surface::WlSurface,
|
||||||
_serial: u32,
|
_serial: u32,
|
||||||
_raw: &[u32],
|
_raw: &[u32],
|
||||||
_keysyms: &[Keysym],
|
keysyms: &[Keysym],
|
||||||
) {
|
) {
|
||||||
let (i, mut is_active, seat) = {
|
let (i, mut is_active, seat, modifiers) = {
|
||||||
let (i, is_active, my_seat) =
|
let (i, is_active, my_seat) =
|
||||||
match self.seats.iter_mut().enumerate().find_map(|(i, s)| {
|
match self.seats.iter_mut().enumerate().find_map(|(i, s)| {
|
||||||
if s.kbd.as_ref() == Some(keyboard) {
|
if s.kbd.as_ref() == Some(keyboard) {
|
||||||
|
|
@ -41,9 +77,11 @@ impl KeyboardHandler for SctkState {
|
||||||
surface
|
surface
|
||||||
};
|
};
|
||||||
_ = my_seat.kbd_focus.replace(surface.clone());
|
_ = my_seat.kbd_focus.replace(surface.clone());
|
||||||
|
let modifiers = modifiers_from_keysyms(keysyms, my_seat._modifiers);
|
||||||
|
my_seat._modifiers = modifiers;
|
||||||
|
|
||||||
let seat = my_seat.seat.clone();
|
let seat = my_seat.seat.clone();
|
||||||
(i, is_active, seat)
|
(i, is_active, seat, modifiers)
|
||||||
};
|
};
|
||||||
|
|
||||||
if !is_active && self.seats[0].kbd_focus.is_none() {
|
if !is_active && self.seats[0].kbd_focus.is_none() {
|
||||||
|
|
@ -73,6 +111,12 @@ impl KeyboardHandler for SctkState {
|
||||||
seat_id: seat.clone(),
|
seat_id: seat.clone(),
|
||||||
surface: surface.clone(),
|
surface: surface.clone(),
|
||||||
});
|
});
|
||||||
|
self.sctk_events.push(SctkEvent::KeyboardEvent {
|
||||||
|
variant: KeyboardEventVariant::Modifiers(modifiers),
|
||||||
|
kbd_id: keyboard.clone(),
|
||||||
|
seat_id: seat.clone(),
|
||||||
|
surface: surface.clone(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -126,6 +170,7 @@ impl KeyboardHandler for SctkState {
|
||||||
if self.windows.iter().any(|w| w.window.id() == id) {
|
if self.windows.iter().any(|w| w.window.id() == id) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
let modifiers = s._modifiers;
|
||||||
self.sctk_events.push(SctkEvent::Winit(
|
self.sctk_events.push(SctkEvent::Winit(
|
||||||
id,
|
id,
|
||||||
winit::event::WindowEvent::Focused(true),
|
winit::event::WindowEvent::Focused(true),
|
||||||
|
|
@ -137,6 +182,12 @@ impl KeyboardHandler for SctkState {
|
||||||
kbd_id: s.kbd.clone().unwrap(),
|
kbd_id: s.kbd.clone().unwrap(),
|
||||||
seat_id: s.seat.clone(),
|
seat_id: s.seat.clone(),
|
||||||
surface: surface.clone(),
|
surface: surface.clone(),
|
||||||
|
});
|
||||||
|
self.sctk_events.push(SctkEvent::KeyboardEvent {
|
||||||
|
variant: KeyboardEventVariant::Modifiers(modifiers),
|
||||||
|
kbd_id: s.kbd.clone().unwrap(),
|
||||||
|
seat_id: s.seat.clone(),
|
||||||
|
surface: surface.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -248,6 +299,7 @@ impl KeyboardHandler for SctkState {
|
||||||
};
|
};
|
||||||
let seat_id = my_seat.seat.clone();
|
let seat_id = my_seat.seat.clone();
|
||||||
let kbd_id = keyboard.clone();
|
let kbd_id = keyboard.clone();
|
||||||
|
my_seat._modifiers = modifiers;
|
||||||
|
|
||||||
if is_active {
|
if is_active {
|
||||||
if let Some(surface) = my_seat.kbd_focus.clone() {
|
if let Some(surface) = my_seat.kbd_focus.clone() {
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,9 @@ use crate::platform_specific::wayland::{
|
||||||
};
|
};
|
||||||
use cctk::sctk::{
|
use cctk::sctk::{
|
||||||
delegate_seat,
|
delegate_seat,
|
||||||
reexports::client::{protocol::wl_keyboard::WlKeyboard, Proxy},
|
reexports::client::{Proxy, protocol::wl_keyboard::WlKeyboard},
|
||||||
seat::{pointer::ThemeSpec, SeatHandler},
|
seat::{SeatHandler, keyboard::Modifiers, pointer::ThemeSpec},
|
||||||
};
|
};
|
||||||
use iced_runtime::keyboard::Modifiers;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
impl SeatHandler for SctkState {
|
impl SeatHandler for SctkState {
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,12 @@ use crate::{
|
||||||
use cctk::sctk::{
|
use cctk::sctk::{
|
||||||
delegate_touch,
|
delegate_touch,
|
||||||
reexports::client::{
|
reexports::client::{
|
||||||
protocol::{wl_surface::WlSurface, wl_touch::WlTouch},
|
|
||||||
Connection, Proxy, QueueHandle,
|
Connection, Proxy, QueueHandle,
|
||||||
|
protocol::{wl_surface::WlSurface, wl_touch::WlTouch},
|
||||||
},
|
},
|
||||||
seat::touch::TouchHandler,
|
seat::touch::TouchHandler,
|
||||||
};
|
};
|
||||||
use iced_runtime::core::{touch, Point};
|
use iced_runtime::core::{Point, touch};
|
||||||
|
|
||||||
impl TouchHandler for SctkState {
|
impl TouchHandler for SctkState {
|
||||||
fn down(
|
fn down(
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
event_loop::state::CommonSurface,
|
event_loop::state::CommonSurface,
|
||||||
platform_specific::wayland::{handlers::SctkState, sctk_event::SctkEvent},
|
platform_specific::wayland::{
|
||||||
|
event_loop::state::receive_frame, handlers::SctkState,
|
||||||
|
sctk_event::SctkEvent,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use cctk::sctk::{
|
use cctk::sctk::{
|
||||||
delegate_session_lock,
|
delegate_session_lock,
|
||||||
|
|
@ -67,6 +70,10 @@ impl SessionLockHandler for SctkState {
|
||||||
configure,
|
configure,
|
||||||
first,
|
first,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let wl_surface = session_lock_surface.wl_surface().clone();
|
||||||
|
receive_frame(&mut self.frame_status, &wl_surface);
|
||||||
|
self.request_redraw(&wl_surface);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,29 @@
|
||||||
use cctk::{sctk, cosmic_protocols::{
|
use cctk::{
|
||||||
corner_radius::v1::client::{
|
cosmic_protocols::corner_radius::v1::client::{
|
||||||
|
cosmic_corner_radius_layer_v1::CosmicCornerRadiusLayerV1,
|
||||||
cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1,
|
cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1,
|
||||||
cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1,
|
cosmic_corner_radius_toplevel_v1::CosmicCornerRadiusToplevelV1,
|
||||||
},
|
},
|
||||||
overlap_notify::v1::client::zcosmic_overlap_notification_v1::ZcosmicOverlapNotificationV1,
|
sctk,
|
||||||
}};
|
|
||||||
use sctk::reexports::{
|
|
||||||
client::{Connection, Dispatch, Proxy},
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
use sctk::reexports::client::{Connection, Dispatch, Proxy};
|
||||||
|
|
||||||
use crate::event_loop::state::SctkState;
|
use crate::event_loop::state::SctkState;
|
||||||
use crate::platform_specific::wayland::SctkEvent;
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum CornerRadiusWrapper {
|
||||||
|
Xdg(CosmicCornerRadiusToplevelV1),
|
||||||
|
Wlr(CosmicCornerRadiusLayerV1),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for CornerRadiusWrapper {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
match self {
|
||||||
|
Self::Xdg(c) => c.destroy(),
|
||||||
|
Self::Wlr(c) => c.destroy(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Dispatch<CosmicCornerRadiusManagerV1, ()> for SctkState {
|
impl Dispatch<CosmicCornerRadiusManagerV1, ()> for SctkState {
|
||||||
fn event(
|
fn event(
|
||||||
|
|
@ -21,15 +33,11 @@ impl Dispatch<CosmicCornerRadiusManagerV1, ()> for SctkState {
|
||||||
_data: &(),
|
_data: &(),
|
||||||
_conn: &Connection,
|
_conn: &Connection,
|
||||||
_qhandle: &sctk::reexports::client::QueueHandle<Self>,
|
_qhandle: &sctk::reexports::client::QueueHandle<Self>,
|
||||||
) {}
|
) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl
|
impl Dispatch<CosmicCornerRadiusToplevelV1, ()> for SctkState {
|
||||||
Dispatch<
|
|
||||||
CosmicCornerRadiusToplevelV1,
|
|
||||||
(),
|
|
||||||
> for SctkState
|
|
||||||
{
|
|
||||||
fn event(
|
fn event(
|
||||||
state: &mut Self,
|
state: &mut Self,
|
||||||
_proxy: &CosmicCornerRadiusToplevelV1,
|
_proxy: &CosmicCornerRadiusToplevelV1,
|
||||||
|
|
@ -39,7 +47,22 @@ impl
|
||||||
_qhandle: &sctk::reexports::client::QueueHandle<Self>,
|
_qhandle: &sctk::reexports::client::QueueHandle<Self>,
|
||||||
) {
|
) {
|
||||||
match event {
|
match event {
|
||||||
_ => unimplemented!()
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dispatch<CosmicCornerRadiusLayerV1, ()> for SctkState {
|
||||||
|
fn event(
|
||||||
|
state: &mut Self,
|
||||||
|
_proxy: &CosmicCornerRadiusLayerV1,
|
||||||
|
event: <CosmicCornerRadiusLayerV1 as Proxy>::Event,
|
||||||
|
_data: &(),
|
||||||
|
_conn: &Connection,
|
||||||
|
_qhandle: &sctk::reexports::client::QueueHandle<Self>,
|
||||||
|
) {
|
||||||
|
match event {
|
||||||
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
use crate::platform_specific::wayland::{
|
use crate::platform_specific::wayland::{
|
||||||
event_loop::state::{receive_frame, SctkState},
|
event_loop::state::{SctkState, receive_frame},
|
||||||
sctk_event::{LayerSurfaceEventVariant, SctkEvent},
|
sctk_event::{LayerSurfaceEventVariant, SctkEvent},
|
||||||
};
|
};
|
||||||
use cctk::sctk::{
|
use cctk::sctk::{
|
||||||
delegate_layer,
|
delegate_layer,
|
||||||
reexports::client::Proxy,
|
reexports::client::Proxy,
|
||||||
shell::{
|
shell::{
|
||||||
wlr_layer::{Anchor, KeyboardInteractivity, LayerShellHandler},
|
|
||||||
WaylandSurface,
|
WaylandSurface,
|
||||||
|
wlr_layer::{Anchor, KeyboardInteractivity, LayerShellHandler},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
|
pub mod corner_radius;
|
||||||
pub mod layer;
|
pub mod layer;
|
||||||
pub mod xdg_popup;
|
pub mod xdg_popup;
|
||||||
pub mod xdg_window;
|
pub mod xdg_window;
|
||||||
pub mod corner_radius;
|
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,10 @@ impl Dispatch<ZwpTextInputV3, (), SctkState> for TextInputManager {
|
||||||
_conn: &Connection,
|
_conn: &Connection,
|
||||||
_qhandle: &QueueHandle<SctkState>,
|
_qhandle: &QueueHandle<SctkState>,
|
||||||
) {
|
) {
|
||||||
|
if cfg!(not(feature = "single-instance")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let kbd_focus =
|
let kbd_focus =
|
||||||
match state.seats.iter_mut().find_map(|s| s.kbd_focus.clone()) {
|
match state.seats.iter_mut().find_map(|s| s.kbd_focus.clone()) {
|
||||||
Some(surface) => surface,
|
Some(surface) => surface,
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
//! Handling of the wp-viewporter.
|
//! Handling of the wp-viewporter.
|
||||||
|
|
||||||
|
use cctk::sctk::reexports::client::Dispatch;
|
||||||
use cctk::sctk::reexports::client::globals::{BindError, GlobalList};
|
use cctk::sctk::reexports::client::globals::{BindError, GlobalList};
|
||||||
use cctk::sctk::reexports::client::protocol::wl_surface::WlSurface;
|
use cctk::sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||||
use cctk::sctk::reexports::client::Dispatch;
|
|
||||||
use cctk::sctk::reexports::client::{
|
use cctk::sctk::reexports::client::{
|
||||||
delegate_dispatch, Connection, Proxy, QueueHandle,
|
Connection, Proxy, QueueHandle, delegate_dispatch,
|
||||||
};
|
};
|
||||||
use cctk::sctk::reexports::protocols::wp::viewporter::client::wp_viewport::WpViewport;
|
use cctk::sctk::reexports::protocols::wp::viewporter::client::wp_viewport::WpViewport;
|
||||||
use cctk::sctk::reexports::protocols::wp::viewporter::client::wp_viewporter::WpViewporter;
|
use cctk::sctk::reexports::protocols::wp::viewporter::client::wp_viewporter::WpViewporter;
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ use iced_runtime::core::{Vector, window};
|
||||||
use raw_window_handle::{DisplayHandle, HasDisplayHandle, HasWindowHandle};
|
use raw_window_handle::{DisplayHandle, HasDisplayHandle, HasWindowHandle};
|
||||||
use raw_window_handle::{HasRawDisplayHandle, RawWindowHandle};
|
use raw_window_handle::{HasRawDisplayHandle, RawWindowHandle};
|
||||||
use sctk_event::SctkEvent;
|
use sctk_event::SctkEvent;
|
||||||
|
use std::sync::OnceLock;
|
||||||
use std::{collections::HashMap, sync::Arc};
|
use std::{collections::HashMap, sync::Arc};
|
||||||
use subsurface_widget::{SubsurfaceInstance, SubsurfaceState};
|
use subsurface_widget::{SubsurfaceInstance, SubsurfaceState};
|
||||||
use wayland_backend::client::ObjectId;
|
use wayland_backend::client::ObjectId;
|
||||||
|
|
@ -128,7 +129,6 @@ impl PlatformSpecific {
|
||||||
};
|
};
|
||||||
self.wayland.display_handle = Some(display);
|
self.wayland.display_handle = Some(display);
|
||||||
self.wayland.proxy = Some(raw);
|
self.wayland.proxy = Some(raw);
|
||||||
// TODO remove this
|
|
||||||
self.wayland.sender =
|
self.wayland.sender =
|
||||||
crate::platform_specific::event_loop::SctkEventLoop::new(
|
crate::platform_specific::event_loop::SctkEventLoop::new(
|
||||||
self.wayland.winit_event_sender.clone().unwrap(),
|
self.wayland.winit_event_sender.clone().unwrap(),
|
||||||
|
|
|
||||||
|
|
@ -68,10 +68,8 @@ use cctk::{
|
||||||
xdg::{popup::PopupConfigure, window::WindowConfigure},
|
xdg::{popup::PopupConfigure, window::WindowConfigure},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wayland_client::protocol::wl_subsurface::WlSubsurface,
|
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
num::NonZeroU32,
|
num::NonZeroU32,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
|
|
@ -98,9 +96,7 @@ use super::{
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum SctkEvent {
|
pub enum SctkEvent {
|
||||||
//
|
Blur,
|
||||||
// Input events
|
|
||||||
//
|
|
||||||
SeatEvent {
|
SeatEvent {
|
||||||
variant: SeatEventVariant,
|
variant: SeatEventVariant,
|
||||||
id: WlSeat,
|
id: WlSeat,
|
||||||
|
|
@ -122,11 +118,6 @@ pub enum SctkEvent {
|
||||||
seat_id: WlSeat,
|
seat_id: WlSeat,
|
||||||
surface: WlSurface,
|
surface: WlSurface,
|
||||||
},
|
},
|
||||||
// TODO data device & touch
|
|
||||||
|
|
||||||
//
|
|
||||||
// Surface Events
|
|
||||||
//
|
|
||||||
WindowEvent {
|
WindowEvent {
|
||||||
variant: WindowEventVariant,
|
variant: WindowEventVariant,
|
||||||
id: WlSurface,
|
id: WlSurface,
|
||||||
|
|
@ -167,10 +158,6 @@ pub enum SctkEvent {
|
||||||
},
|
},
|
||||||
|
|
||||||
SubsurfaceEvent(SubsurfaceEventVariant),
|
SubsurfaceEvent(SubsurfaceEventVariant),
|
||||||
|
|
||||||
//
|
|
||||||
// output events
|
|
||||||
//
|
|
||||||
NewOutput {
|
NewOutput {
|
||||||
id: WlOutput,
|
id: WlOutput,
|
||||||
info: Option<OutputInfo>,
|
info: Option<OutputInfo>,
|
||||||
|
|
@ -180,9 +167,7 @@ pub enum SctkEvent {
|
||||||
info: OutputInfo,
|
info: OutputInfo,
|
||||||
},
|
},
|
||||||
RemovedOutput(WlOutput),
|
RemovedOutput(WlOutput),
|
||||||
//
|
|
||||||
// compositor events
|
|
||||||
//
|
|
||||||
ScaleFactorChanged {
|
ScaleFactorChanged {
|
||||||
factor: f64,
|
factor: f64,
|
||||||
id: WlOutput,
|
id: WlOutput,
|
||||||
|
|
@ -380,6 +365,12 @@ impl SctkEvent {
|
||||||
(variant.position.0, variant.position.1).into(),
|
(variant.position.0, variant.position.1).into(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
events.push((
|
||||||
|
id.clone(),
|
||||||
|
iced_runtime::core::Event::Mouse(
|
||||||
|
mouse::Event::CursorEntered,
|
||||||
|
),
|
||||||
|
));
|
||||||
events.push((
|
events.push((
|
||||||
id,
|
id,
|
||||||
iced_runtime::core::Event::Mouse(
|
iced_runtime::core::Event::Mouse(
|
||||||
|
|
@ -1732,6 +1723,12 @@ impl SctkEvent {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
|
SctkEvent::Blur => events.push((
|
||||||
|
None,
|
||||||
|
iced_runtime::core::Event::PlatformSpecific(
|
||||||
|
PlatformSpecific::Wayland(wayland::Event::BlurEnabled),
|
||||||
|
),
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ use crate::core::{
|
||||||
widget::{self, Widget},
|
widget::{self, Widget},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
borrow::BorrowMut,
|
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
|
|
@ -410,7 +409,10 @@ impl SubsurfaceState {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
canvas[0..width as usize * height as usize * 4].copy_from_slice(data);
|
canvas[0..width as usize * height as usize * 4].copy_from_slice(data);
|
||||||
surface.damage_buffer(0, 0, width as i32, height as i32);
|
surface.damage_buffer(0, 0, width as i32, height as i32);
|
||||||
buffer.attach_to(&surface);
|
if let Err(err) = buffer.attach_to(&surface) {
|
||||||
|
log::warn!("failed to attach shm buffer to subsurface: {err}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
surface.offset(offset.x as i32, offset.y as i32);
|
surface.offset(offset.x as i32, offset.y as i32);
|
||||||
wp_viewport.set_destination(
|
wp_viewport.set_destination(
|
||||||
(width as f64 / scale) as i32,
|
(width as f64 / scale) as i32,
|
||||||
|
|
|
||||||
|
|
@ -287,7 +287,21 @@ impl winit::window::Window for SctkWinitWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_blur(&self, blur: bool) {
|
fn set_blur(&self, blur: bool) {
|
||||||
// TODO
|
_ = self.tx.send(Action::Action(
|
||||||
|
iced_runtime::platform_specific::wayland::Action::BlurSurface(
|
||||||
|
self.id.inner(),
|
||||||
|
if blur {
|
||||||
|
Some(vec![iced_runtime::core::Rectangle {
|
||||||
|
x: 0.,
|
||||||
|
y: 0.,
|
||||||
|
width: f32::MAX,
|
||||||
|
height: f32::MAX,
|
||||||
|
}])
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_visible(&self, visible: bool) {}
|
fn set_visible(&self, visible: bool) {}
|
||||||
|
|
|
||||||
|
|
@ -335,19 +335,9 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.ime_state != Some((cursor, purpose)) {
|
if self.ime_state != Some((cursor, purpose)) {
|
||||||
// Specify only the bottom-left position of the cursor on Linux
|
|
||||||
// because fcitx5 doesn't work well with cursor areas of
|
|
||||||
// the top-left position and height.
|
|
||||||
// The candidate window hides the composing text (a.k.a. preedit).
|
|
||||||
let (cursor_y, cursor_height) =
|
|
||||||
if cfg!(not(any(target_os = "windows", target_os = "macos"))) {
|
|
||||||
(cursor.y + cursor.height, 0.0)
|
|
||||||
} else {
|
|
||||||
(cursor.y, cursor.height)
|
|
||||||
};
|
|
||||||
self.raw.set_ime_cursor_area(
|
self.raw.set_ime_cursor_area(
|
||||||
LogicalPosition::new(cursor.x, cursor_y).into(),
|
LogicalPosition::new(cursor.x, cursor.y).into(),
|
||||||
LogicalSize::new(cursor.width, cursor_height).into(),
|
LogicalSize::new(cursor.width, cursor.height).into(),
|
||||||
);
|
);
|
||||||
self.raw.set_ime_purpose(conversion::ime_purpose(purpose));
|
self.raw.set_ime_purpose(conversion::ime_purpose(purpose));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,7 @@ where
|
||||||
self.ready = ready;
|
self.ready = ready;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "a11y")]
|
||||||
pub(crate) fn set_a11y_ready(&mut self, ready: bool) {
|
pub(crate) fn set_a11y_ready(&mut self, ready: bool) {
|
||||||
self.a11y_ready = ready;
|
self.a11y_ready = ready;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue