Compare commits

..

15 commits

Author SHA1 Message Date
de1247123a chore: use local winit and softbuffer
Some checks failed
Audit / vulnerabilities (push) Has been cancelled
Check / wasm (push) Has been cancelled
Check / widget (push) Has been cancelled
Document / all (push) Has been cancelled
Format / all (push) Has been cancelled
Lint / all (push) Has been cancelled
Test / all (macOS-latest, 1.88) (push) Has been cancelled
Test / all (macOS-latest, beta) (push) Has been cancelled
Test / all (macOS-latest, stable) (push) Has been cancelled
Test / all (ubuntu-latest, 1.88) (push) Has been cancelled
Test / all (ubuntu-latest, beta) (push) Has been cancelled
Test / all (ubuntu-latest, stable) (push) Has been cancelled
Test / all (windows-latest, 1.88) (push) Has been cancelled
Test / all (windows-latest, beta) (push) Has been cancelled
Test / all (windows-latest, stable) (push) Has been cancelled
2026-05-25 19:36:22 +02:00
12fd244e95 chore: use local glyphon and cosmic-text
Some checks are pending
Audit / vulnerabilities (push) Waiting to run
Check / wasm (push) Waiting to run
Check / widget (push) Waiting to run
Document / all (push) Waiting to run
Format / all (push) Waiting to run
Lint / all (push) Waiting to run
Test / all (macOS-latest, 1.88) (push) Waiting to run
Test / all (macOS-latest, beta) (push) Waiting to run
Test / all (macOS-latest, stable) (push) Waiting to run
Test / all (ubuntu-latest, 1.88) (push) Waiting to run
Test / all (ubuntu-latest, beta) (push) Waiting to run
Test / all (ubuntu-latest, stable) (push) Waiting to run
Test / all (windows-latest, 1.88) (push) Waiting to run
Test / all (windows-latest, beta) (push) Waiting to run
Test / all (windows-latest, stable) (push) Waiting to run
2026-05-25 18:27:54 +02:00
756e5691d7 chore: use local cctk and clipboard crates
Some checks are pending
Audit / vulnerabilities (push) Waiting to run
Check / wasm (push) Waiting to run
Check / widget (push) Waiting to run
Document / all (push) Waiting to run
Format / all (push) Waiting to run
Lint / all (push) Waiting to run
Test / all (macOS-latest, 1.88) (push) Waiting to run
Test / all (macOS-latest, beta) (push) Waiting to run
Test / all (macOS-latest, stable) (push) Waiting to run
Test / all (ubuntu-latest, 1.88) (push) Waiting to run
Test / all (ubuntu-latest, beta) (push) Waiting to run
Test / all (ubuntu-latest, stable) (push) Waiting to run
Test / all (windows-latest, 1.88) (push) Waiting to run
Test / all (windows-latest, beta) (push) Waiting to run
Test / all (windows-latest, stable) (push) Waiting to run
2026-05-25 18:09:40 +02:00
8163993539 chore: refresh lockfile for local clipboard fork
Some checks are pending
Audit / vulnerabilities (push) Waiting to run
Check / wasm (push) Waiting to run
Check / widget (push) Waiting to run
Document / all (push) Waiting to run
Format / all (push) Waiting to run
Lint / all (push) Waiting to run
Test / all (macOS-latest, 1.88) (push) Waiting to run
Test / all (macOS-latest, beta) (push) Waiting to run
Test / all (macOS-latest, stable) (push) Waiting to run
Test / all (ubuntu-latest, 1.88) (push) Waiting to run
Test / all (ubuntu-latest, beta) (push) Waiting to run
Test / all (ubuntu-latest, stable) (push) Waiting to run
Test / all (windows-latest, 1.88) (push) Waiting to run
Test / all (windows-latest, beta) (push) Waiting to run
Test / all (windows-latest, stable) (push) Waiting to run
2026-05-25 11:35:03 +02:00
f487018708 chore: gate accessibility-only winit state
Some checks failed
Audit / vulnerabilities (push) Has been cancelled
Check / wasm (push) Has been cancelled
Check / widget (push) Has been cancelled
Document / all (push) Has been cancelled
Format / all (push) Has been cancelled
Lint / all (push) Has been cancelled
Test / all (macOS-latest, 1.88) (push) Has been cancelled
Test / all (macOS-latest, beta) (push) Has been cancelled
Test / all (macOS-latest, stable) (push) Has been cancelled
Test / all (ubuntu-latest, 1.88) (push) Has been cancelled
Test / all (ubuntu-latest, beta) (push) Has been cancelled
Test / all (ubuntu-latest, stable) (push) Has been cancelled
Test / all (windows-latest, 1.88) (push) Has been cancelled
Test / all (windows-latest, beta) (push) Has been cancelled
Test / all (windows-latest, stable) (push) Has been cancelled
2026-05-23 20:55:59 +02:00
Votre Nom
6b069b2b4b yoda: iced_core + iced_wgpu cleanup (3→0 warnings)
Some checks failed
Audit / vulnerabilities (push) Has been cancelled
Check / wasm (push) Has been cancelled
Check / widget (push) Has been cancelled
Document / all (push) Has been cancelled
Format / all (push) Has been cancelled
Lint / all (push) Has been cancelled
Test / all (macOS-latest, 1.88) (push) Has been cancelled
Test / all (macOS-latest, beta) (push) Has been cancelled
Test / all (macOS-latest, stable) (push) Has been cancelled
Test / all (ubuntu-latest, 1.88) (push) Has been cancelled
Test / all (ubuntu-latest, beta) (push) Has been cancelled
Test / all (ubuntu-latest, stable) (push) Has been cancelled
Test / all (windows-latest, 1.88) (push) Has been cancelled
Test / all (windows-latest, beta) (push) Has been cancelled
Test / all (windows-latest, stable) (push) Has been cancelled
iced_core/settings.rs: drop dead `From<Settings> for iced_winit::Settings`
impl gated behind a `winit` feature that doesn't exist on iced_core (and
shouldn't — that would invert the dep direction). The impl was
unreachable code.

iced_wgpu/window/compositor.rs: collapse the unused initial
`adapter_options` declaration into the only branch that uses it (the
fallback path that re-creates the instance with GL allowed). The
original allocation was always re-assigned before being read.

iced_wgpu/lib.rs: remove `Renderer::draw_overlay` — private debug method
with no caller anywhere in the workspace. cargo fix then dropped the
imports that became orphans (Color, Size, Vector, alignment).

Leyoda 2026 – GPLv3
2026-05-05 18:45:49 +02:00
Votre Nom
d33d068c17 yoda: iced_widget cleanup (7→0 warnings)
Some checks are pending
Audit / vulnerabilities (push) Waiting to run
Check / wasm (push) Waiting to run
Check / widget (push) Waiting to run
Document / all (push) Waiting to run
Format / all (push) Waiting to run
Lint / all (push) Waiting to run
Test / all (macOS-latest, beta) (push) Waiting to run
Test / all (macOS-latest, 1.88) (push) Waiting to run
Test / all (macOS-latest, stable) (push) Waiting to run
Test / all (ubuntu-latest, 1.88) (push) Waiting to run
Test / all (ubuntu-latest, beta) (push) Waiting to run
Test / all (ubuntu-latest, stable) (push) Waiting to run
Test / all (windows-latest, 1.88) (push) Waiting to run
Test / all (windows-latest, beta) (push) Waiting to run
Test / all (windows-latest, stable) (push) Waiting to run
- overlay/menu.rs:578: real bug fix — List::draw was rendering with
  text::Wrapping::default() instead of the configured self.text_wrap.
  Wiring it through eliminates the 'field never read' warning AND
  honors the wrap setting that was being silently dropped.
- slider.rs:735-744: replace `if let Ok(x) = …try_into() { … }` with
  irrefutable `let Ok(x) = …try_into();` (the conversion is provably
  Infallible at the call site). Same behavior, no compiler noise.
- lazy.rs:8 / lazy/component.rs:13: drop unused
  `iced_renderer::core::widget::Operation` imports.

Leyoda 2026 – GPLv3
2026-05-05 18:38:57 +02:00
Votre Nom
e424487704 yoda: iced_winit dead-code purge (29→0 warnings)
Some checks are pending
Audit / vulnerabilities (push) Waiting to run
Check / wasm (push) Waiting to run
Check / widget (push) Waiting to run
Document / all (push) Waiting to run
Format / all (push) Waiting to run
Lint / all (push) Waiting to run
Test / all (macOS-latest, 1.88) (push) Waiting to run
Test / all (macOS-latest, beta) (push) Waiting to run
Test / all (macOS-latest, stable) (push) Waiting to run
Test / all (ubuntu-latest, 1.88) (push) Waiting to run
Test / all (ubuntu-latest, beta) (push) Waiting to run
Test / all (ubuntu-latest, stable) (push) Waiting to run
Test / all (windows-latest, 1.88) (push) Waiting to run
Test / all (windows-latest, beta) (push) Waiting to run
Test / all (windows-latest, stable) (push) Waiting to run
Removed code paths inherited from past refactors that no longer have any
reader:

- ViewFn type alias (window.rs)
- a11y_enabled flag and resized flag (lib.rs) — assigned but never read
- BootConfig.fonts/graphics_settings/is_wayland — already passed to
  run_instance(...) directly; the BootConfig copies were never read
- Runner.system_theme oneshot::Sender — stored but no producer ever
  wired; receiver side already falls back to default
- event_loop/control_flow.rs — local ControlFlow enum, all callers use
  winit::event_loop::ControlFlow
- event_loop/proxy.rs — Proxy<Message> never constructed
- Error::Connect(ConnectError) variant — never constructed
- Common.has_focus / ime_pos / ime_size — never read after assignment
- SctkPopupData.grab — replicated settings.grab, never read back
- SubsurfaceEventVariant::Created.surface field — destructure does
  `surface: _` everywhere, the value is unused

Visibility tightening:
- a11y.rs WinitActivationHandler / WinitActionHandler /
  WinitDeactivationHandler: pub proxy → pub(crate) proxy
- conversion::touch_event: pub → private (no external callers)
- proxy::Proxy:🆕 pub → pub(crate)

Allow attributes (intentional, not warning-suppression):
- conversion::RawImage: #[allow(dead_code)] — fields kept for
  IconProvider downcast-via-AsAny on winit's side
- event_loop::Error: #[allow(dead_code)] — variant payloads kept for
  Debug, never inspected programmatically
- lib.rs Event<Message>: #[allow(hidden_glob_reexports)] — intentional
  shadow of winit::event::Event coming from `pub use winit`
- window.rs/lib.rs: #[allow(deprecated)] on enable_ime/disable_ime/
  process_event with TODOs for set_ime_* → request_ime_update and
  try_next → try_recv migrations

Five orphan imports also removed (Hash/Hasher, BorrowMut, 3× Compositor).

Leyoda 2026 – GPLv3
2026-05-05 18:26:08 +02:00
Votre Nom
aa1c2593ac yoda: iced_winit unreachable + deprecation cleanup (45→29 warnings)
- conversion.rs: drop 4 duplicate Interaction arms (Cell/Move/Copy/Help)
  that were already handled higher in the same match — pure dead code
  from a past merge, not a real bug.
- platform_specific/wayland/mod.rs: migrate HasRawDisplayHandle →
  HasDisplayHandle (display_handle().map(|h| h.as_raw())).
- conversion.rs: #[allow(deprecated)] on key_code/winit_key_code — the
  warning suggests Meta but no Meta variant exists in this enum
  (only MetaLeft/MetaRight, which aren't equivalent to Super).
- window.rs: #[allow(deprecated)] on enable_ime/disable_ime with TODO
  to migrate set_ime_* → request_ime_update(ImeRequest::*).
- lib.rs: #[allow(deprecated)] on process_event with TODO to migrate
  try_next → try_recv (futures-channel API change).

Leyoda 2026 – GPLv3
2026-05-05 18:11:19 +02:00
Votre Nom
8a7a32ff92 yoda: cargo fix --lib across all crates — drop ~115 trivial warnings
Some checks are pending
Audit / vulnerabilities (push) Waiting to run
Check / wasm (push) Waiting to run
Check / widget (push) Waiting to run
Document / all (push) Waiting to run
Format / all (push) Waiting to run
Lint / all (push) Waiting to run
Test / all (macOS-latest, 1.88) (push) Waiting to run
Test / all (macOS-latest, beta) (push) Waiting to run
Test / all (macOS-latest, stable) (push) Waiting to run
Test / all (ubuntu-latest, 1.88) (push) Waiting to run
Test / all (ubuntu-latest, beta) (push) Waiting to run
Test / all (ubuntu-latest, stable) (push) Waiting to run
Test / all (windows-latest, 1.88) (push) Waiting to run
Test / all (windows-latest, beta) (push) Waiting to run
Test / all (windows-latest, stable) (push) Waiting to run
Auto-applied suggestions (unused imports, _-prefixed unused params,
redundant mutability) on iced_core, iced_widget, iced_runtime, iced_winit,
iced_wgpu, iced_graphics, iced_tiny_skia. From 170 warnings down to 55.

Leyoda 2026 – GPLv3
2026-05-05 16:45:37 +02:00
Votre Nom
f388dfdfe4 yoda: point window_clipboard at public Forgejo fork
Some checks are pending
Audit / vulnerabilities (push) Waiting to run
Check / wasm (push) Waiting to run
Check / widget (push) Waiting to run
Document / all (push) Waiting to run
Format / all (push) Waiting to run
Lint / all (push) Waiting to run
Test / all (macOS-latest, 1.88) (push) Waiting to run
Test / all (macOS-latest, beta) (push) Waiting to run
Test / all (macOS-latest, stable) (push) Waiting to run
Test / all (ubuntu-latest, 1.88) (push) Waiting to run
Test / all (ubuntu-latest, beta) (push) Waiting to run
Test / all (ubuntu-latest, stable) (push) Waiting to run
Test / all (windows-latest, 1.88) (push) Waiting to run
Test / all (windows-latest, beta) (push) Waiting to run
Test / all (windows-latest, stable) (push) Waiting to run
Replace pop-os/window_clipboard sctk-0.20 (no wayland feature) with
forge.aditua.com/leyoda/window_clipboard branch yoda-x11-optional. This
removes the need for cosmic-files' [patch] redirect to a local path and
makes libcosmic build standalone again.

Leyoda 2026 – GPLv3
2026-05-05 12:52:06 +02:00
Votre Nom
bb9115558d Fix high-signal widget and Wayland warnings
Some checks are pending
Audit / vulnerabilities (push) Waiting to run
Check / wasm (push) Waiting to run
Check / widget (push) Waiting to run
Document / all (push) Waiting to run
Format / all (push) Waiting to run
Lint / all (push) Waiting to run
Test / all (macOS-latest, 1.88) (push) Waiting to run
Test / all (macOS-latest, beta) (push) Waiting to run
Test / all (macOS-latest, stable) (push) Waiting to run
Test / all (ubuntu-latest, 1.88) (push) Waiting to run
Test / all (ubuntu-latest, beta) (push) Waiting to run
Test / all (ubuntu-latest, stable) (push) Waiting to run
Test / all (windows-latest, 1.88) (push) Waiting to run
Test / all (windows-latest, beta) (push) Waiting to run
Test / all (windows-latest, stable) (push) Waiting to run
Avoid recursive operation traversal, execute visible list updates instead of leaving a lazy iterator unused, remove unreachable cfg-gated surface creation code, and handle subsurface buffer attachment failures.
2026-05-05 08:27:03 +02:00
f0c0b8d42e yoda: softbuffer + window_clipboard default-features=false
Some checks failed
Audit / vulnerabilities (push) Has been cancelled
Check / wasm (push) Has been cancelled
Check / widget (push) Has been cancelled
Document / all (push) Has been cancelled
Format / all (push) Has been cancelled
Lint / all (push) Has been cancelled
Test / all (macOS-latest, 1.88) (push) Has been cancelled
Test / all (macOS-latest, beta) (push) Has been cancelled
Test / all (macOS-latest, stable) (push) Has been cancelled
Test / all (ubuntu-latest, 1.88) (push) Has been cancelled
Test / all (ubuntu-latest, beta) (push) Has been cancelled
Test / all (ubuntu-latest, stable) (push) Has been cancelled
Test / all (windows-latest, 1.88) (push) Has been cancelled
Test / all (windows-latest, beta) (push) Has been cancelled
Test / all (windows-latest, stable) (push) Has been cancelled
Two one-line workspace-dep fixes that drop the last remaining X11 crates
for Wayland-only builds:

- softbuffer: its default = [kms, x11, x11-dlopen, wayland, wayland-dlopen]
  pulled tiny-xlib + as-raw-xcb-connection unconditionally even though
  iced_tiny_skia only needed softbuffer/wayland. Disabling the default
  lets iced_tiny_skia's own feature propagation ask for just wayland +
  wayland-dlopen.

- window_clipboard: upstream pulled clipboard_x11 + clipboard_wayland
  unconditionally on unix. Our local fork (branch yoda-x11-optional)
  gates clipboard_x11 behind an opt-in feature, so default-features=false
  + features=["wayland"] drops ~500 x11 symbols (clipboard_x11, x11rb,
  x11rb-protocol).

Combined with previous yoda-v4 cuts, cosmic-yoterm goes from
1526 -> 43 x11 symbols (-97%), and 55.4 MB -> 53.3 MB (-2.1 MB).
2026-04-24 06:53:36 +02:00
8f6be7984a yoda: gate iced_wgpu x11 backend behind an opt-in feature
Some checks are pending
Audit / vulnerabilities (push) Waiting to run
Check / wasm (push) Waiting to run
Check / widget (push) Waiting to run
Document / all (push) Waiting to run
Format / all (push) Waiting to run
Lint / all (push) Waiting to run
Test / all (macOS-latest, 1.88) (push) Waiting to run
Test / all (macOS-latest, beta) (push) Waiting to run
Test / all (macOS-latest, stable) (push) Waiting to run
Test / all (ubuntu-latest, 1.88) (push) Waiting to run
Test / all (ubuntu-latest, beta) (push) Waiting to run
Test / all (ubuntu-latest, stable) (push) Waiting to run
Test / all (windows-latest, 1.88) (push) Waiting to run
Test / all (windows-latest, beta) (push) Waiting to run
Test / all (windows-latest, stable) (push) Waiting to run
iced_wgpu's unix block unconditionally depended on as-raw-xcb-connection,
tiny-xlib and x11rb for a DRI3 device-id fallback in
window/compositor.rs that only fires when wayland can't determine the
GPU. On a Wayland-only build this code path never runs, so the deps
are pure bloat.

Changes:
- Cargo.toml: move the 3 x11 crates to optional + new feature `x11`
  (not in default — wayland-only is the yoda target; users who need
  X11 opt in).
- window.rs: cfg-gate `mod x11;` on feature = "x11"
- window/compositor.rs: cfg-gate `use super::x11::get_x11_device_ids`
  + the or_else fallback on feature = "x11" (wrapped in a let-binding
  so the non-x11 branch compiles cleanly).

Result: iced_wgpu no longer pulls any x11 crate when built without
the feature.
2026-04-24 06:24:15 +02:00
e4d6dc1f68 yoda: drop x11 default on winit workspace dep
Some checks are pending
Audit / vulnerabilities (push) Waiting to run
Check / wasm (push) Waiting to run
Check / widget (push) Waiting to run
Document / all (push) Waiting to run
Format / all (push) Waiting to run
Lint / all (push) Waiting to run
Test / all (macOS-latest, 1.88) (push) Waiting to run
Test / all (macOS-latest, beta) (push) Waiting to run
Test / all (macOS-latest, stable) (push) Waiting to run
Test / all (ubuntu-latest, 1.88) (push) Waiting to run
Test / all (ubuntu-latest, beta) (push) Waiting to run
Test / all (ubuntu-latest, stable) (push) Waiting to run
Test / all (windows-latest, 1.88) (push) Waiting to run
Test / all (windows-latest, beta) (push) Waiting to run
Test / all (windows-latest, stable) (push) Waiting to run
Was pulling winit's full x11 backend (winit-x11 + x11-dl) unconditionally
despite iced_winit's own x11 feature being disabled. Fixed by adding
default-features=false on the workspace-level winit dep — iced_winit/wayland
+ wayland-dlopen propagate to winit/wayland + wayland-dlopen as needed.
2026-04-24 06:00:03 +02:00
94 changed files with 965 additions and 2508 deletions

696
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -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.92" rust-version = "1.89"
[workspace.dependencies] [workspace.dependencies]
iced = { version = "0.14.0", path = "." } iced = { version = "0.14.0", path = "." }
@ -239,8 +239,9 @@ bitflags = "2.5"
bytemuck = { version = "1.0", features = ["derive"] } bytemuck = { version = "1.0", features = ["derive"] }
bytes = "1.6" bytes = "1.6"
cosmic-text = { path = "../../cosmic-text" } cosmic-text = { path = "../../cosmic-text" }
cryoglyph = { package = "cryoglyph", path = "../../glyphon" } # cosmic-text = "0.10"
dark-light = "1.0" dark-light = "1.0"
cryoglyph = { package = "cryoglyph", path = "../../glyphon" }
resvg = "0.45" resvg = "0.45"
web-sys = "0.3.69" web-sys = "0.3.69"
guillotiere = "0.6" guillotiere = "0.6"
@ -290,7 +291,7 @@ url = "2.5"
wasm-bindgen-futures = "0.4" wasm-bindgen-futures = "0.4"
wasmtimer = "0.4.2" wasmtimer = "0.4.2"
web-time = "1.1" web-time = "1.1"
wgpu = { version = "28.0", default-features = false, features = [ wgpu = { version = "27.0", default-features = false, features = [
"std", "std",
"wgsl", "wgsl",
] } ] }

View file

@ -31,7 +31,6 @@ num-traits.workspace = true
rustc-hash.workspace = true rustc-hash.workspace = true
smol_str.workspace = true smol_str.workspace = true
thiserror.workspace = true thiserror.workspace = true
unicode-segmentation.workspace = true
web-time.workspace = true web-time.workspace = true
serde.workspace = true serde.workspace = true

View file

@ -46,6 +46,4 @@ pub enum Event {
Subsurface(SubsurfaceEvent), Subsurface(SubsurfaceEvent),
/// Keyboard inhibit shortcuts /// Keyboard inhibit shortcuts
ShortcutsInhibited(bool), ShortcutsInhibited(bool),
/// Blur Support Enabled
BlurEnabled,
} }

View file

@ -63,13 +63,3 @@ impl Default for Settings {
} }
} }
#[cfg(feature = "winit")]
impl From<Settings> for iced_winit::Settings {
fn from(settings: Settings) -> iced_winit::Settings {
iced_winit::Settings {
id: settings.id,
fonts: settings.fonts,
is_daemon: settings.is_daemon,
}
}
}

View file

@ -78,8 +78,6 @@ pub enum Action {
SelectLine, SelectLine,
/// Select the entire buffer. /// Select the entire buffer.
SelectAll, SelectAll,
/// Clear the selection
ClearSelection,
/// Perform an [`Edit`]. /// Perform an [`Edit`].
Edit(Edit), Edit(Edit),
/// Click the [`Editor`] at the given [`Point`]. /// Click the [`Editor`] at the given [`Point`].

View file

@ -22,18 +22,15 @@
//! ``` //! ```
use crate::alignment; use crate::alignment;
use crate::layout; use crate::layout;
use crate::mouse::{self, click}; use crate::mouse;
use crate::renderer; use crate::renderer;
use crate::text;
use crate::text::paragraph::{self, Paragraph}; use crate::text::paragraph::{self, Paragraph};
use crate::text::{self};
use crate::widget::tree::{self, Tree}; use crate::widget::tree::{self, Tree};
use crate::{ use crate::{
Clipboard, Color, Element, Event, Layout, Length, Pixels, Point, Rectangle, Color, Element, Layout, Length, Pixels, Rectangle, Size, Theme, Widget,
Shell, Size, Theme, Widget, keyboard, touch,
}; };
use unicode_segmentation::UnicodeSegmentation;
pub use text::{Alignment, Ellipsize, LineHeight, Shaping, Wrapping}; pub use text::{Alignment, Ellipsize, LineHeight, Shaping, Wrapping};
/// A bunch of text. /// A bunch of text.
@ -67,7 +64,6 @@ where
fragment: text::Fragment<'a>, fragment: text::Fragment<'a>,
format: Format<Renderer::Font>, format: Format<Renderer::Font>,
class: Theme::Class<'a>, class: Theme::Class<'a>,
selectable: bool,
} }
impl<'a, Theme, Renderer> Text<'a, Theme, Renderer> impl<'a, Theme, Renderer> Text<'a, Theme, Renderer>
@ -82,7 +78,6 @@ where
fragment: fragment.into_fragment(), fragment: fragment.into_fragment(),
format: Format::default(), format: Format::default(),
class: Theme::default(), class: Theme::default(),
selectable: false,
} }
} }
@ -193,17 +188,7 @@ where
{ {
let color = color.map(Into::into); let color = color.map(Into::into);
self.style(move |_theme| Style { self.style(move |_theme| Style { color })
color,
..Style::default()
})
}
/// Makes the [`Text`] selectable. When enabled, the user can click and
/// drag to select text, and copy it with Ctrl+C / Cmd+C.
pub fn selectable(mut self) -> Self {
self.selectable = true;
self
} }
/// Sets the style class of the [`Text`]. /// Sets the style class of the [`Text`].
@ -216,114 +201,7 @@ where
} }
/// The internal state of a [`Text`] widget. /// The internal state of a [`Text`] widget.
pub struct State<P: Paragraph> { pub type State<P> = paragraph::Plain<P>;
/// The cached paragraph layout.
pub paragraph: paragraph::Plain<P>,
/// Lazily allocated when text is selectable and first interacted with.
selection: Option<Box<SelectionState>>,
focused: bool,
keyboard_focused: bool,
context_menu_position: Option<Point>,
}
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) -> &paragraph::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>
@ -336,7 +214,7 @@ where
} }
fn state(&self) -> tree::State { fn state(&self) -> tree::State {
tree::State::new(State::<Renderer::Paragraph>::default()) tree::State::new(paragraph::Plain::<Renderer::Paragraph>::default())
} }
fn size(&self) -> Size<Length> { fn size(&self) -> Size<Length> {
@ -352,10 +230,8 @@ where
renderer: &Renderer, renderer: &Renderer,
limits: &layout::Limits, limits: &layout::Limits,
) -> layout::Node { ) -> layout::Node {
let state = tree.state.downcast_mut::<State<Renderer::Paragraph>>();
layout( layout(
&mut state.paragraph, tree.state.downcast_mut::<State<Renderer::Paragraph>>(),
renderer, renderer,
limits, limits,
&self.fragment, &self.fragment,
@ -375,325 +251,25 @@ where
) { ) {
let state = tree.state.downcast_ref::<State<Renderer::Paragraph>>(); let state = tree.state.downcast_ref::<State<Renderer::Paragraph>>();
let style = theme.style(&self.class); let style = theme.style(&self.class);
let bounds = layout.bounds();
let paragraph = state.paragraph.raw();
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();
if left != right { draw(
let lo_byte = grapheme_to_byte(content, left); renderer,
let hi_byte = grapheme_to_byte(content, right); defaults,
layout.bounds(),
let anchor = bounds.anchor( state.raw(),
paragraph.min_bounds(), style,
paragraph.align_x(), viewport,
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")]
@ -894,23 +470,12 @@ where
} }
/// The appearance of some text. /// The appearance of some text.
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct Style { pub struct Style {
/// The [`Color`] of the text. /// The [`Color`] of the text.
/// ///
/// The default, `None`, means using the inherited color. /// The default, `None`, means using the inherited color.
pub color: Option<Color>, pub color: Option<Color>,
/// The fill [`Color`] of the selection highlight.
pub selected_fill: Color,
}
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`].
@ -944,14 +509,13 @@ impl Catalog for Theme {
/// The default text styling; color is inherited. /// The default text styling; color is inherited.
pub fn default(_theme: &Theme) -> Style { pub fn default(_theme: &Theme) -> Style {
Style::default() Style { color: None }
} }
/// Text with the default base color. /// Text with the default base color.
pub fn base(theme: &Theme) -> Style { pub fn base(theme: &Theme) -> Style {
Style { Style {
color: Some(theme.palette().text), color: Some(theme.palette().text),
..Style::default()
} }
} }
@ -959,7 +523,6 @@ pub fn base(theme: &Theme) -> Style {
pub fn primary(theme: &Theme) -> Style { pub fn primary(theme: &Theme) -> Style {
Style { Style {
color: Some(theme.palette().primary), color: Some(theme.palette().primary),
..Style::default()
} }
} }
@ -967,7 +530,6 @@ pub fn primary(theme: &Theme) -> Style {
pub fn secondary(theme: &Theme) -> Style { pub fn secondary(theme: &Theme) -> Style {
Style { Style {
color: Some(theme.extended_palette().secondary.base.color), color: Some(theme.extended_palette().secondary.base.color),
..Style::default()
} }
} }
@ -975,7 +537,6 @@ pub fn secondary(theme: &Theme) -> Style {
pub fn success(theme: &Theme) -> Style { pub fn success(theme: &Theme) -> Style {
Style { Style {
color: Some(theme.palette().success), color: Some(theme.palette().success),
..Style::default()
} }
} }
@ -983,7 +544,6 @@ pub fn success(theme: &Theme) -> Style {
pub fn warning(theme: &Theme) -> Style { pub fn warning(theme: &Theme) -> Style {
Style { Style {
color: Some(theme.palette().warning), color: Some(theme.palette().warning),
..Style::default()
} }
} }
@ -991,177 +551,5 @@ pub fn warning(theme: &Theme) -> Style {
pub fn danger(theme: &Theme) -> Style { pub fn danger(theme: &Theme) -> Style {
Style { Style {
color: Some(theme.palette().danger), color: Some(theme.palette().danger),
..Style::default()
}
}
use crate::widget::tree::Tree as WidgetTree;
/// 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()
} }
} }

View file

@ -1,5 +1,5 @@
use iced::widget::{button, column, text, Column};
use iced::Center; use iced::Center;
use iced::widget::{Column, button, column, text};
pub fn main() -> iced::Result { pub fn main() -> iced::Result {
iced::run(Counter::update, Counter::view) iced::run(Counter::update, Counter::view)
@ -42,7 +42,7 @@ impl Counter {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use iced_test::{simulator, Error}; use iced_test::{Error, simulator};
#[test] #[test]
fn it_counts() -> Result<(), Error> { fn it_counts() -> Result<(), Error> {

View file

@ -140,7 +140,7 @@ impl Pipeline {
address_mode_w: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Linear, mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Linear,
mipmap_filter: wgpu::MipmapFilterMode::Linear, mipmap_filter: wgpu::FilterMode::Linear,
..Default::default() ..Default::default()
}); });
@ -223,7 +223,7 @@ impl Pipeline {
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("cubes pipeline layout"), label: Some("cubes pipeline layout"),
bind_group_layouts: &[&uniform_bind_group_layout], bind_group_layouts: &[&uniform_bind_group_layout],
immediate_size: 0, push_constant_ranges: &[],
}); });
let shader = let shader =
@ -280,7 +280,7 @@ impl Pipeline {
compilation_options: compilation_options:
wgpu::PipelineCompilationOptions::default(), wgpu::PipelineCompilationOptions::default(),
}), }),
multiview_mask: None, multiview: None,
cache: None, cache: None,
}); });
@ -388,7 +388,6 @@ impl Pipeline {
), ),
timestamp_writes: None, timestamp_writes: None,
occlusion_query_set: None, occlusion_query_set: None,
multiview_mask: None,
}); });
pass.set_viewport( pass.set_viewport(
@ -479,7 +478,7 @@ impl DepthPipeline {
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("cubes.depth_pipeline.layout"), label: Some("cubes.depth_pipeline.layout"),
bind_group_layouts: &[&bind_group_layout], bind_group_layouts: &[&bind_group_layout],
immediate_size: 0, push_constant_ranges: &[],
}); });
let shader = let shader =
@ -521,7 +520,7 @@ impl DepthPipeline {
compilation_options: compilation_options:
wgpu::PipelineCompilationOptions::default(), wgpu::PipelineCompilationOptions::default(),
}), }),
multiview_mask: None, multiview: None,
cache: None, cache: None,
}); });
@ -587,7 +586,6 @@ impl DepthPipeline {
), ),
timestamp_writes: None, timestamp_writes: None,
occlusion_query_set: None, occlusion_query_set: None,
multiview_mask: None,
}); });
pass.set_viewport( pass.set_viewport(

View file

@ -43,7 +43,6 @@ impl Scene {
depth_stencil_attachment: None, depth_stencil_attachment: None,
timestamp_writes: None, timestamp_writes: None,
occlusion_query_set: None, occlusion_query_set: None,
multiview_mask: None,
}) })
} }
@ -65,8 +64,8 @@ fn build_pipeline(
let pipeline_layout = let pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: None, label: None,
push_constant_ranges: &[],
bind_group_layouts: &[], bind_group_layouts: &[],
immediate_size: 0,
}); });
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
@ -102,7 +101,7 @@ fn build_pipeline(
mask: !0, mask: !0,
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
}, },
multiview_mask: None, multiview: None,
cache: None, cache: None,
}) })
} }

View file

@ -3,20 +3,20 @@ use std::{
sync::atomic::{AtomicU64, Ordering}, sync::atomic::{AtomicU64, Ordering},
}; };
use iced::{Element, id::Id};
use iced::{ use iced::{
Event, Length, Rectangle,
clipboard::{ clipboard::{
dnd::{self, DndAction, DndDestinationRectangle, DndEvent, OfferEvent}, dnd::{self, DndAction, DndDestinationRectangle, DndEvent, OfferEvent},
mime::AllowedMimeTypes, mime::AllowedMimeTypes,
}, },
event, event,
id::Internal, id::Internal,
mouse, overlay, Event, Length, Rectangle, mouse, overlay,
}; };
use iced::{id::Id, Element};
use iced_core::{ use iced_core::{
self, layout, self, Clipboard, Layout, Shell, Widget, layout,
widget::{tree, Tree}, widget::{Tree, tree},
Clipboard, Layout, Shell, Widget,
}; };
pub fn dnd_destination<'a, Message: 'static>( pub fn dnd_destination<'a, Message: 'static>(

View file

@ -1,16 +1,16 @@
use std::any::Any; use std::any::Any;
use iced::Element;
use iced::id::Id; use iced::id::Id;
use iced::widget::container; use iced::widget::container;
use iced::Element;
use iced::{ use iced::{
Event, Length, Point, Rectangle,
clipboard::dnd::{DndAction, DndEvent, SourceEvent}, clipboard::dnd::{DndAction, DndEvent, SourceEvent},
event, mouse, overlay, Event, Length, Point, Rectangle, event, mouse, overlay,
}; };
use iced_core::{ use iced_core::{
layout, renderer, Clipboard, Shell, layout, renderer,
widget::{tree, Tree}, widget::{Tree, tree},
Clipboard, Shell,
}; };
use iced_core::{Layout, Widget}; use iced_core::{Layout, Widget};
@ -41,7 +41,7 @@ impl<
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 {
@ -123,7 +123,7 @@ impl<
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> {
@ -361,7 +361,7 @@ impl<
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)

View file

@ -7,6 +7,7 @@ use cctk::sctk::reexports::{
use iced::platform_specific::shell::commands::subsurface::get_subsurface; use iced::platform_specific::shell::commands::subsurface::get_subsurface;
use iced::{ use iced::{
Element, Length, Subscription, Task,
event::wayland::Event as WaylandEvent, event::wayland::Event as WaylandEvent,
platform_specific::{ platform_specific::{
runtime::wayland::subsurface::SctkSubsurfaceSettings, runtime::wayland::subsurface::SctkSubsurfaceSettings,
@ -14,7 +15,6 @@ use iced::{
}, },
widget::{button, column, text, text_input}, widget::{button, column, text, text_input},
window::{self, Id, Settings}, window::{self, Id, Settings},
Element, Length, Subscription, Task,
}; };
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};

View file

@ -477,7 +477,6 @@ fn delete_icon() -> Text<'static> {
fn subtle(theme: &Theme) -> text::Style { fn subtle(theme: &Theme) -> text::Style {
text::Style { text::Style {
color: Some(theme.extended_palette().background.strongest.color), color: Some(theme.extended_palette().background.strongest.color),
..text::Style::default()
} }
} }

View file

@ -161,14 +161,6 @@ impl FontSystem {
&mut self.raw &mut self.raw
} }
/// Returns a reference to the underlying [`fontdb::Database`].
///
/// Unlike going through [`raw`](Self::raw) and `db_mut`, this does not
/// clear `cosmic-text`'s internal font-match cache.
pub fn db(&self) -> &cosmic_text::fontdb::Database {
self.raw.db()
}
/// Loads a font from its bytes. /// Loads a font from its bytes.
pub fn load_font(&mut self, bytes: Cow<'static, [u8]>) { pub fn load_font(&mut self, bytes: Cow<'static, [u8]>) {
if let Cow::Borrowed(bytes) = bytes { if let Cow::Borrowed(bytes) = bytes {

View file

@ -336,10 +336,6 @@ impl editor::Editor for Editor {
} }
} }
Action::ClearSelection => {
editor.set_selection(cosmic_text::Selection::None);
}
// Editing events // Editing events
Action::Edit(edit) => { Action::Edit(edit) => {
let topmost_line_before_edit = editor let topmost_line_before_edit = editor

View file

@ -4,7 +4,7 @@
use dnd::{DndDestinationRectangle, DndSurface}; use dnd::{DndDestinationRectangle, DndSurface};
use window_clipboard::mime::AllowedMimeTypes; use window_clipboard::mime::AllowedMimeTypes;
use crate::{Action, Task, oneshot, task}; use crate::{oneshot, task, Action, Task};
/// An action to be performed on the system. /// An action to be performed on the system.
pub enum DndAction { pub enum DndAction {

View file

@ -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,
), )
} }
} }
} }

View file

@ -6,8 +6,8 @@ use cctk::sctk::{
}; };
use iced_core::layout::Limits; use iced_core::layout::Limits;
use iced_core::Rectangle;
use iced_core::window::Id; use iced_core::window::Id;
use iced_core::Rectangle;
/// output for layer surface /// output for layer surface
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -150,13 +150,6 @@ pub enum Action {
/// layer of the layer surface /// layer of the layer surface
layer: Layer, layer: Layer,
}, },
/// padding of the surface geometry
Padding {
/// id of the layer surface
id: Id,
/// padding of the surface geometry
padding: IcedMargin,
},
} }
impl fmt::Debug for Action { impl fmt::Debug for Action {
@ -169,51 +162,35 @@ impl fmt::Debug for Action {
), ),
Action::Size { id, width, height } => write!( Action::Size { id, width, height } => write!(
f, f,
"Action::LayerSurfaceAction::Size {{ id: {:#?}, width: {:?}, height: {:?} }}", "Action::LayerSurfaceAction::Size {{ id: {:#?}, width: {:?}, height: {:?} }}", id, width, height
id, width, height
), ),
Action::Destroy(id) => write!( Action::Destroy(id) => write!(
f, f,
"Action::LayerSurfaceAction::Destroy {{ id: {:#?} }}", "Action::LayerSurfaceAction::Destroy {{ id: {:#?} }}", id
id
), ),
Action::Anchor { id, anchor } => write!( Action::Anchor { id, anchor } => write!(
f, f,
"Action::LayerSurfaceAction::Anchor {{ id: {:#?}, anchor: {:?} }}", "Action::LayerSurfaceAction::Anchor {{ id: {:#?}, anchor: {:?} }}", id, anchor
id, anchor
), ),
Action::ExclusiveZone { id, exclusive_zone } => write!( Action::ExclusiveZone { id, exclusive_zone } => write!(
f, f,
"Action::LayerSurfaceAction::ExclusiveZone {{ id: {:#?}, exclusive_zone: {exclusive_zone} }}", "Action::LayerSurfaceAction::ExclusiveZone {{ id: {:#?}, exclusive_zone: {exclusive_zone} }}", id
id
), ),
Action::Margin { id, margin } => write!( Action::Margin { id, margin } => write!(
f, f,
"Action::LayerSurfaceAction::Margin {{ id: {:#?}, margin: {:?} }}", "Action::LayerSurfaceAction::Margin {{ id: {:#?}, margin: {:?} }}", id, margin
id, margin
), ),
Action::KeyboardInteractivity { Action::KeyboardInteractivity { id, keyboard_interactivity } => write!(
id,
keyboard_interactivity,
} => write!(
f, f,
"Action::LayerSurfaceAction::KeyboardInteractivity {{ id: {:#?}, keyboard_interactivity: {:?} }}", "Action::LayerSurfaceAction::KeyboardInteractivity {{ id: {:#?}, keyboard_interactivity: {:?} }}", id, keyboard_interactivity
id, keyboard_interactivity
), ),
Action::InputZone { id, zone } => write!( Action::InputZone { id, zone } => write!(
f, f,
"Action::LayerSurfaceAction::InputZone {{ id: {:#?}, zone: {:?} }}", "Action::LayerSurfaceAction::InputZone {{ id: {:#?}, zone: {:?} }}", id, zone
id, zone
), ),
Action::Layer { id, layer } => write!( Action::Layer { id, layer } => write!(
f, f,
"Action::LayerSurfaceAction::Layer {{ id: {:#?}, layer: {:?} }}", "Action::LayerSurfaceAction::Layer {{ id: {:#?}, layer: {:?} }}", id, layer
id, layer
),
Action::Padding { id, padding } => write!(
f,
"Action::LayerSurfaceAction::Padding {{ id: {:#?}, padding: {:?} }}",
id, padding
), ),
} }
} }

View file

@ -2,7 +2,7 @@
use std::fmt::Debug; use std::fmt::Debug;
use iced_core::{Rectangle, window::Id}; use iced_core::window::Id;
/// activation Actions /// activation Actions
pub mod activation; pub mod activation;
@ -35,8 +35,6 @@ 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)]
@ -72,11 +70,6 @@ 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(),
} }
} }
} }

View file

@ -1,14 +1,12 @@
use std::any::Any;
use std::fmt; use std::fmt;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::sync::Arc;
use cctk::sctk::reexports::protocols::xdg::shell::client::xdg_positioner::{ use cctk::sctk::reexports::protocols::xdg::shell::client::xdg_positioner::{
Anchor, Gravity, Anchor, Gravity,
}; };
use iced_core::layout::Limits; use iced_core::layout::Limits;
use iced_core::window::Id; use iced_core::window::Id;
use iced_core::{Element, Rectangle}; use iced_core::Rectangle;
/// Popup creation details /// Popup creation details
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -124,34 +122,26 @@ pub enum Action {
/// height /// height
height: u32, height: u32,
}, },
/// set the size of the popup
Reposition {
/// id of the popup
id: Id,
/// positioner of the popup
positioner: SctkPositioner,
},
} }
impl fmt::Debug for Action { impl fmt::Debug for Action {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Action::Popup { popup, .. } => { Action::Popup { popup, .. } => write!(
write!(f, "Action::PopupAction::Popup {{ popup: {:?} }}", popup) f,
} "Action::PopupAction::Popup {{ popup: {:?} }}",
Action::Destroy { id } => { popup
write!(f, "Action::PopupAction::Destroy {{ id: {:?} }}", id) ),
} Action::Destroy { id } => write!(
f,
"Action::PopupAction::Destroy {{ id: {:?} }}",
id
),
Action::Size { id, width, height } => write!( Action::Size { id, width, height } => write!(
f, f,
"Action::PopupAction::Size {{ id: {:?}, width: {:?}, height: {:?} }}", "Action::PopupAction::Size {{ id: {:?}, width: {:?}, height: {:?} }}",
id, width, height id, width, height
), ),
Action::Reposition { id, positioner } => write!(
f,
"Action::Reposition {{ id: {:?}, positioner: {:?} }}",
id, positioner
),
} }
} }
} }

View file

@ -1,14 +1,9 @@
use std::any::Any;
use std::fmt; use std::fmt;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::sync::Arc;
use cctk::sctk::reexports::protocols::xdg::shell::client::xdg_positioner::{ use cctk::sctk::reexports::protocols::xdg::shell::client::xdg_positioner::Gravity;
Anchor, Gravity,
};
use iced_core::layout::Limits;
use iced_core::window::Id; use iced_core::window::Id;
use iced_core::{Element, Point, Rectangle, Size}; use iced_core::{Point, Rectangle, Size};
/// Subsurface creation details /// Subsurface creation details
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View file

@ -683,8 +683,6 @@ pub use event::Event;
pub use executor::Executor; pub use executor::Executor;
pub use font::Font; pub use font::Font;
pub use renderer::Renderer; pub use renderer::Renderer;
#[cfg(feature = "canvas")]
pub use renderer::graphics::geometry;
pub use task::Task; pub use task::Task;
pub use window::Window; pub use window::Window;

View file

@ -775,7 +775,6 @@ impl<P: Program + 'static> Tester<P> {
} }
_ => None, _ => None,
}, },
..text::Style::default()
}) })
.into() .into()
}, },
@ -946,7 +945,6 @@ where
text(label).size(14).style(|theme: &core::Theme| { text(label).size(14).style(|theme: &core::Theme| {
text::Style { text::Style {
color: Some(theme.extended_palette().background.weak.text), color: Some(theme.extended_palette().background.weak.text),
..text::Style::default()
} }
}), }),
space::horizontal(), space::horizontal(),

View file

@ -57,7 +57,7 @@ impl Pipeline {
let width_scale = bounds.width / image.width() as f32; let width_scale = bounds.width / image.width() as f32;
let height_scale = bounds.height / image.height() as f32; let height_scale = bounds.height / image.height() as f32;
let quality = match filter_method { let _quality = match filter_method {
raster::FilterMethod::Linear => tiny_skia::FilterQuality::Bilinear, raster::FilterMethod::Linear => tiny_skia::FilterQuality::Bilinear,
raster::FilterMethod::Nearest => tiny_skia::FilterQuality::Nearest, raster::FilterMethod::Nearest => tiny_skia::FilterQuality::Nearest,
}; };

View file

@ -1,6 +1,5 @@
use crate::core::svg::{Data, Handle}; use crate::core::svg::{Data, Handle};
use crate::core::{Color, Rectangle, Size}; use crate::core::{Color, Rectangle, Size};
use crate::graphics::text;
use resvg::usvg; use resvg::usvg;
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
@ -75,7 +74,6 @@ struct Cache {
rasters: FxHashMap<RasterKey, tiny_skia::Pixmap>, rasters: FxHashMap<RasterKey, tiny_skia::Pixmap>,
raster_hits: FxHashSet<RasterKey>, raster_hits: FxHashSet<RasterKey>,
fontdb: Option<Arc<usvg::fontdb::Database>>, fontdb: Option<Arc<usvg::fontdb::Database>>,
fontdb_version: Option<text::Version>,
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@ -89,15 +87,12 @@ impl Cache {
fn load(&mut self, handle: &Handle) -> Option<&usvg::Tree> { fn load(&mut self, handle: &Handle) -> Option<&usvg::Tree> {
let id = handle.id(); let id = handle.id();
// Reuse the font database already loaded by the `cosmic-text` font // TODO: Reuse `cosmic-text` font database
// system. We clone it once and refresh only when a font is if self.fontdb.is_none() {
// loaded (i.e. when the font system's version changes). let mut fontdb = usvg::fontdb::Database::new();
let font_system = text::font_system().read().expect("Read font system"); fontdb.load_system_fonts();
let version = font_system.version();
if self.fontdb_version != Some(version) { self.fontdb = Some(Arc::new(fontdb));
self.fontdb = Some(Arc::new(font_system.db().clone()));
self.fontdb_version = Some(version);
} }
let options = usvg::Options { let options = usvg::Options {

View file

@ -63,6 +63,7 @@ impl<T: bytemuck::Pod> Buffer<T> {
/// Returns the size of the written bytes. /// Returns the size of the written bytes.
pub fn write( pub fn write(
&mut self, &mut self,
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder, encoder: &mut wgpu::CommandEncoder,
belt: &mut wgpu::util::StagingBelt, belt: &mut wgpu::util::StagingBelt,
offset: usize, offset: usize,
@ -78,6 +79,7 @@ impl<T: bytemuck::Pod> Buffer<T> {
&self.raw, &self.raw,
(offset + bytes_written) as u64, (offset + bytes_written) as u64,
MAX_WRITE_SIZE_U64, MAX_WRITE_SIZE_U64,
device,
) )
.copy_from_slice( .copy_from_slice(
&bytes[bytes_written..bytes_written + MAX_WRITE_SIZE], &bytes[bytes_written..bytes_written + MAX_WRITE_SIZE],
@ -98,6 +100,7 @@ impl<T: bytemuck::Pod> Buffer<T> {
&self.raw, &self.raw,
(offset + bytes_written) as u64, (offset + bytes_written) as u64,
bytes_left, bytes_left,
device,
) )
.copy_from_slice(&bytes[bytes_written..]); .copy_from_slice(&bytes[bytes_written..]);

View file

@ -99,7 +99,7 @@ pub fn convert(
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("iced_wgpu.offscreen.blit.pipeline_layout"), label: Some("iced_wgpu.offscreen.blit.pipeline_layout"),
bind_group_layouts: &[&constant_layout, &texture_layout], bind_group_layouts: &[&constant_layout, &texture_layout],
immediate_size: 0, push_constant_ranges: &[],
}); });
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
@ -149,7 +149,7 @@ pub fn convert(
}, },
depth_stencil: None, depth_stencil: None,
multisample: wgpu::MultisampleState::default(), multisample: wgpu::MultisampleState::default(),
multiview_mask: None, multiview: None,
cache: None, cache: None,
}); });
@ -194,7 +194,6 @@ pub fn convert(
depth_stencil_attachment: None, depth_stencil_attachment: None,
timestamp_writes: None, timestamp_writes: None,
occlusion_query_set: None, occlusion_query_set: None,
multiview_mask: None,
}); });
pass.set_pipeline(&pipeline); pass.set_pipeline(&pipeline);

View file

@ -132,7 +132,7 @@ impl Atlas {
match &entry { match &entry {
Entry::Contiguous(allocation) => { Entry::Contiguous(allocation) => {
self.upload_allocation( self.upload_allocation(
pixels, width, 0, allocation, encoder, belt, pixels, width, 0, allocation, device, encoder, belt,
); );
} }
Entry::Fragmented { fragments, .. } => { Entry::Fragmented { fragments, .. } => {
@ -145,6 +145,7 @@ impl Atlas {
width, width,
offset, offset,
&fragment.allocation, &fragment.allocation,
device,
encoder, encoder,
belt, belt,
); );
@ -311,6 +312,7 @@ impl Atlas {
image_width: u32, image_width: u32,
offset: usize, offset: usize,
allocation: &Allocation, allocation: &Allocation,
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder, encoder: &mut wgpu::CommandEncoder,
belt: &mut wgpu::util::StagingBelt, belt: &mut wgpu::util::StagingBelt,
) { ) {
@ -332,6 +334,7 @@ impl Atlas {
let buffer_slice = belt.allocate( let buffer_slice = belt.allocate(
wgpu::BufferSize::new(total_bytes as u64).unwrap(), wgpu::BufferSize::new(total_bytes as u64).unwrap(),
wgpu::BufferSize::new(8 * 4).unwrap(), wgpu::BufferSize::new(8 * 4).unwrap(),
device,
); );
const PIXEL: usize = 4; const PIXEL: usize = 4;

View file

@ -38,10 +38,7 @@ impl Cache {
raster: Raster { raster: Raster {
cache: crate::image::raster::Cache::default(), cache: crate::image::raster::Cache::default(),
pending: HashMap::new(), pending: HashMap::new(),
belt: wgpu::util::StagingBelt::new( belt: wgpu::util::StagingBelt::new(2 * 1024 * 1024),
device.clone(),
2 * 1024 * 1024,
),
}, },
#[cfg(feature = "svg")] #[cfg(feature = "svg")]
vector: crate::image::vector::Cache::default(), vector: crate::image::vector::Cache::default(),
@ -454,10 +451,7 @@ mod worker {
backend, backend,
texture_layout, texture_layout,
shell: shell.clone(), shell: shell.clone(),
belt: wgpu::util::StagingBelt::new( belt: wgpu::util::StagingBelt::new(4 * 1024 * 1024),
device.clone(),
4 * 1024 * 1024,
),
jobs: jobs_receiver, jobs: jobs_receiver,
output: work_sender, output: work_sender,
quit: quit_receiver, quit: quit_receiver,

View file

@ -45,7 +45,7 @@ impl Pipeline {
address_mode_w: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge,
min_filter: wgpu::FilterMode::Nearest, min_filter: wgpu::FilterMode::Nearest,
mag_filter: wgpu::FilterMode::Nearest, mag_filter: wgpu::FilterMode::Nearest,
mipmap_filter: wgpu::MipmapFilterMode::Nearest, mipmap_filter: wgpu::FilterMode::Nearest,
..Default::default() ..Default::default()
}); });
@ -55,7 +55,7 @@ impl Pipeline {
address_mode_w: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge,
min_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Linear,
mag_filter: wgpu::FilterMode::Linear, mag_filter: wgpu::FilterMode::Linear,
mipmap_filter: wgpu::MipmapFilterMode::Linear, mipmap_filter: wgpu::FilterMode::Linear,
..Default::default() ..Default::default()
}); });
@ -106,8 +106,8 @@ impl Pipeline {
let layout = let layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("iced_wgpu::image pipeline layout"), label: Some("iced_wgpu::image pipeline layout"),
push_constant_ranges: &[],
bind_group_layouts: &[&constant_layout, &texture_layout], bind_group_layouts: &[&constant_layout, &texture_layout],
immediate_size: 0,
}); });
let shader = let shader =
@ -191,7 +191,7 @@ impl Pipeline {
mask: !0, mask: !0,
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
}, },
multiview_mask: None, multiview: None,
cache: None, cache: None,
}); });
@ -528,6 +528,7 @@ impl Layer {
&self.uniforms, &self.uniforms,
0, 0,
(bytes.len() as u64).try_into().expect("Sized uniforms"), (bytes.len() as u64).try_into().expect("Sized uniforms"),
device,
) )
.copy_from_slice(bytes); .copy_from_slice(bytes);
@ -538,11 +539,11 @@ impl Layer {
let mut offset = 0; let mut offset = 0;
if !nearest.is_empty() { if !nearest.is_empty() {
offset += self.instances.write(encoder, belt, 0, nearest); offset += self.instances.write(device, encoder, belt, 0, nearest);
} }
if !linear.is_empty() { if !linear.is_empty() {
let _ = self.instances.write(encoder, belt, offset, linear); let _ = self.instances.write(device, encoder, belt, offset, linear);
} }
} }

View file

@ -1,4 +1,3 @@
use crate::core::Radians;
use crate::core::{ use crate::core::{
self, Background, Color, Point, Rectangle, Svg, Transformation, renderer, self, Background, Color, Point, Rectangle, Svg, Transformation, renderer,
}; };

View file

@ -64,8 +64,8 @@ pub use geometry::Geometry;
use crate::core::renderer; use crate::core::renderer;
use crate::core::{ use crate::core::{
Background, Color, Font, Pixels, Point, Radians, Rectangle, Size, Background, Color, Font, Pixels, Point, Rectangle, Size,
Transformation, Vector, image::FilterMethod, Transformation,
}; };
use crate::graphics::mesh; use crate::graphics::mesh;
use crate::graphics::text::{Editor, Paragraph}; use crate::graphics::text::{Editor, Paragraph};
@ -126,7 +126,6 @@ impl Renderer {
// It would be great if the `StagingBelt` API exposed methods // It would be great if the `StagingBelt` API exposed methods
// for introspection to detect when a resize may be worth it. // for introspection to detect when a resize may be worth it.
staging_belt: wgpu::util::StagingBelt::new( staging_belt: wgpu::util::StagingBelt::new(
engine.device.clone(),
buffer::MAX_WRITE_SIZE as u64, buffer::MAX_WRITE_SIZE as u64,
), ),
@ -455,7 +454,6 @@ impl Renderer {
depth_stencil_attachment: None, depth_stencil_attachment: None,
timestamp_writes: None, timestamp_writes: None,
occlusion_query_set: None, occlusion_query_set: None,
multiview_mask: None,
}, },
)); ));
@ -530,7 +528,6 @@ impl Renderer {
depth_stencil_attachment: None, depth_stencil_attachment: None,
timestamp_writes: None, timestamp_writes: None,
occlusion_query_set: None, occlusion_query_set: None,
multiview_mask: None,
}, },
)); ));
} }
@ -624,7 +621,6 @@ impl Renderer {
depth_stencil_attachment: None, depth_stencil_attachment: None,
timestamp_writes: None, timestamp_writes: None,
occlusion_query_set: None, occlusion_query_set: None,
multiview_mask: None,
}, },
)); ));
} }
@ -675,50 +671,6 @@ impl Renderer {
}); });
} }
fn draw_overlay(
&mut self,
overlay: &[impl AsRef<str>],
viewport: &Viewport,
) {
use crate::core::Renderer as _;
use crate::core::alignment;
use crate::core::text::Renderer as _;
self.with_layer(
Rectangle::with_size(viewport.logical_size()),
|renderer| {
for (i, line) in overlay.iter().enumerate() {
let text = crate::core::Text {
content: line.as_ref().to_owned(),
bounds: viewport.logical_size(),
size: Pixels(20.0),
line_height: core::text::LineHeight::default(),
font: Font::MONOSPACE,
align_x: core::text::Alignment::Left,
align_y: alignment::Vertical::Top,
shaping: core::text::Shaping::Advanced,
wrapping: core::text::Wrapping::Word,
ellipsize: core::text::Ellipsize::None,
};
renderer.fill_text(
text.clone(),
Point::new(11.0, 11.0 + 25.0 * i as f32),
Color::from_rgba(0.9, 0.9, 0.9, 1.0),
Rectangle::with_size(Size::INFINITE),
);
renderer.fill_text(
text,
Point::new(11.0, 11.0 + 25.0 * i as f32)
+ Vector::new(-1.0, -1.0),
Color::BLACK,
Rectangle::with_size(Size::INFINITE),
);
}
},
);
}
} }
impl core::Renderer for Renderer { impl core::Renderer for Renderer {

View file

@ -209,7 +209,7 @@ impl Layer {
transformation: Transformation, transformation: Transformation,
scale: f32, scale: f32,
) { ) {
self.update(encoder, belt, transformation, scale); self.update(device, encoder, belt, transformation, scale);
if !quads.solids.is_empty() { if !quads.solids.is_empty() {
self.solid.prepare(device, encoder, belt, &quads.solids); self.solid.prepare(device, encoder, belt, &quads.solids);
@ -223,6 +223,7 @@ impl Layer {
pub fn update( pub fn update(
&mut self, &mut self,
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder, encoder: &mut wgpu::CommandEncoder,
belt: &mut wgpu::util::StagingBelt, belt: &mut wgpu::util::StagingBelt,
transformation: Transformation, transformation: Transformation,
@ -236,6 +237,7 @@ impl Layer {
&self.constants_buffer, &self.constants_buffer,
0, 0,
(bytes.len() as u64).try_into().expect("Sized uniforms"), (bytes.len() as u64).try_into().expect("Sized uniforms"),
device,
) )
.copy_from_slice(bytes); .copy_from_slice(bytes);
} }

View file

@ -51,7 +51,7 @@ impl Layer {
instances: &[Gradient], instances: &[Gradient],
) { ) {
let _ = self.instances.resize(device, instances.len()); let _ = self.instances.resize(device, instances.len());
let _ = self.instances.write(encoder, belt, 0, instances); let _ = self.instances.write(device, encoder, belt, 0, instances);
self.instance_count = instances.len(); self.instance_count = instances.len();
} }
@ -75,8 +75,8 @@ impl Pipeline {
let layout = device.create_pipeline_layout( let layout = device.create_pipeline_layout(
&wgpu::PipelineLayoutDescriptor { &wgpu::PipelineLayoutDescriptor {
label: Some("iced_wgpu.quad.gradient.pipeline"), label: Some("iced_wgpu.quad.gradient.pipeline"),
push_constant_ranges: &[],
bind_group_layouts: &[constants_layout], bind_group_layouts: &[constants_layout],
immediate_size: 0,
}, },
); );
@ -155,7 +155,7 @@ impl Pipeline {
mask: !0, mask: !0,
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
}, },
multiview_mask: None, multiview: None,
cache: None, cache: None,
}, },
); );

View file

@ -45,7 +45,7 @@ impl Layer {
instances: &[Solid], instances: &[Solid],
) { ) {
let _ = self.instances.resize(device, instances.len()); let _ = self.instances.resize(device, instances.len());
let _ = self.instances.write(encoder, belt, 0, instances); let _ = self.instances.write(device, encoder, belt, 0, instances);
self.instance_count = instances.len(); self.instance_count = instances.len();
} }
@ -65,8 +65,8 @@ impl Pipeline {
let layout = let layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("iced_wgpu.quad.solid.pipeline"), label: Some("iced_wgpu.quad.solid.pipeline"),
push_constant_ranges: &[],
bind_group_layouts: &[constants_layout], bind_group_layouts: &[constants_layout],
immediate_size: 0,
}); });
let shader = let shader =
@ -139,7 +139,7 @@ impl Pipeline {
mask: !0, mask: !0,
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
}, },
multiview_mask: None, multiview: None,
cache: None, cache: None,
}); });

View file

@ -619,7 +619,7 @@ fn prepare(
)?; )?;
Some(cryoglyph::TextArea { Some(cryoglyph::TextArea {
text: buffer.layout_runs(), buffer,
left: position.x, left: position.x,
top: position.y, top: position.y,
scale: transformation.scale_factor() scale: transformation.scale_factor()

View file

@ -313,7 +313,6 @@ fn render<'a>(
depth_stencil_attachment: None, depth_stencil_attachment: None,
timestamp_writes: None, timestamp_writes: None,
occlusion_query_set: None, occlusion_query_set: None,
multiview_mask: None,
}) })
}; };
@ -426,13 +425,18 @@ impl Layer {
let indices = mesh.indices(); let indices = mesh.indices();
index_offset += index_offset += self.index_buffer.write(
self.index_buffer device,
.write(encoder, belt, index_offset, indices); encoder,
belt,
index_offset,
indices,
);
match mesh { match mesh {
Mesh::Solid { buffers, .. } => { Mesh::Solid { buffers, .. } => {
solid_vertex_offset += self.solid.vertices.write( solid_vertex_offset += self.solid.vertices.write(
device,
encoder, encoder,
belt, belt,
solid_vertex_offset, solid_vertex_offset,
@ -440,14 +444,16 @@ impl Layer {
); );
solid_uniform_offset += self.solid.uniforms.write( solid_uniform_offset += self.solid.uniforms.write(
device,
encoder, encoder,
belt, belt,
solid_uniform_offset, solid_uniform_offset,
&[uniforms], &[uniforms],
) );
} }
Mesh::Gradient { buffers, .. } => { Mesh::Gradient { buffers, .. } => {
gradient_vertex_offset += self.gradient.vertices.write( gradient_vertex_offset += self.gradient.vertices.write(
device,
encoder, encoder,
belt, belt,
gradient_vertex_offset, gradient_vertex_offset,
@ -455,6 +461,7 @@ impl Layer {
); );
gradient_uniform_offset += self.gradient.uniforms.write( gradient_uniform_offset += self.gradient.uniforms.write(
device,
encoder, encoder,
belt, belt,
gradient_uniform_offset, gradient_uniform_offset,
@ -724,7 +731,7 @@ mod solid {
&wgpu::PipelineLayoutDescriptor { &wgpu::PipelineLayoutDescriptor {
label: Some("iced_wgpu.triangle.solid.pipeline_layout"), label: Some("iced_wgpu.triangle.solid.pipeline_layout"),
bind_group_layouts: &[&constants_layout], bind_group_layouts: &[&constants_layout],
immediate_size: 0, push_constant_ranges: &[],
}, },
); );
@ -777,7 +784,7 @@ mod solid {
primitive: triangle::primitive_state(), primitive: triangle::primitive_state(),
depth_stencil: None, depth_stencil: None,
multisample: triangle::multisample_state(antialiasing), multisample: triangle::multisample_state(antialiasing),
multiview_mask: None, multiview: None,
cache: None, cache: None,
}, },
); );
@ -879,7 +886,7 @@ mod gradient {
&wgpu::PipelineLayoutDescriptor { &wgpu::PipelineLayoutDescriptor {
label: Some("iced_wgpu.triangle.gradient.pipeline_layout"), label: Some("iced_wgpu.triangle.gradient.pipeline_layout"),
bind_group_layouts: &[&constants_layout], bind_group_layouts: &[&constants_layout],
immediate_size: 0, push_constant_ranges: &[],
}, },
); );
@ -942,7 +949,7 @@ mod gradient {
primitive: triangle::primitive_state(), primitive: triangle::primitive_state(),
depth_stencil: None, depth_stencil: None,
multisample: triangle::multisample_state(antialiasing), multisample: triangle::multisample_state(antialiasing),
multiview_mask: None, multiview: None,
cache: None, cache: None,
}, },
); );

View file

@ -69,8 +69,8 @@ impl Pipeline {
let layout = let layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("iced_wgpu::triangle::msaa pipeline layout"), label: Some("iced_wgpu::triangle::msaa pipeline layout"),
push_constant_ranges: &[],
bind_group_layouts: &[&constant_layout, &texture_layout], bind_group_layouts: &[&constant_layout, &texture_layout],
immediate_size: 0,
}); });
let shader = let shader =
@ -116,7 +116,7 @@ impl Pipeline {
mask: !0, mask: !0,
alpha_to_coverage_enabled: false, alpha_to_coverage_enabled: false,
}, },
multiview_mask: None, multiview: None,
cache: None, cache: None,
}); });
@ -177,7 +177,6 @@ impl Pipeline {
depth_stencil_attachment: None, depth_stencil_attachment: None,
timestamp_writes: None, timestamp_writes: None,
occlusion_query_set: None, occlusion_query_set: None,
multiview_mask: None,
}) })
} }
} }
@ -321,6 +320,7 @@ impl State {
0, 0,
NonZeroU64::new(std::mem::size_of::<Ratio>() as u64) NonZeroU64::new(std::mem::size_of::<Ratio>() as u64)
.expect("non-empty ratio"), .expect("non-empty ratio"),
device,
) )
.copy_from_slice(bytemuck::bytes_of(&ratio)); .copy_from_slice(bytemuck::bytes_of(&ratio));
@ -351,7 +351,6 @@ impl State {
depth_stencil_attachment: None, depth_stencil_attachment: None,
timestamp_writes: None, timestamp_writes: None,
occlusion_query_set: None, occlusion_query_set: None,
multiview_mask: None,
}); });
render_pass.set_pipeline(&pipeline.raw); render_pass.set_pipeline(&pipeline.raw);

View file

@ -18,7 +18,6 @@ use crate::{Engine, Renderer};
use super::wayland::get_wayland_device_ids; use super::wayland::get_wayland_device_ids;
#[cfg(all(unix, feature = "x11", not(target_os = "macos"), not(target_os = "redox")))] #[cfg(all(unix, 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;
/// A window graphics backend for iced powered by `wgpu`. /// A window graphics backend for iced powered by `wgpu`.
pub struct Compositor { pub struct Compositor {
@ -78,23 +77,18 @@ impl Compositor {
w 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(unix, not(target_os = "macos"), not(target_os = "redox")))] #[cfg(all(
unix,
feature = "cctk",
not(target_os = "macos"),
not(target_os = "redox")
))]
if !matches!(ids, Some((0x10de, _))) if !matches!(ids, Some((0x10de, _)))
&& std::env::var_os("__NV_PRIME_RENDER_OFFLOAD") && std::env::var_os("__NV_PRIME_RENDER_OFFLOAD")
.is_none_or(|var| var == "0") .is_none_or(|var| var == "0")
@ -133,7 +127,6 @@ impl Compositor {
// if log::max_level() >= log::LevelFilter::Info { // if log::max_level() >= log::LevelFilter::Info {
// let available_adapters: Vec<_> = instance // let available_adapters: Vec<_> = instance
// .enumerate_adapters(settings.backends) // .enumerate_adapters(settings.backends)
// .await
// .iter() // .iter()
// .map(wgpu::Adapter::get_info) // .map(wgpu::Adapter::get_info)
// .collect(); // .collect();
@ -145,24 +138,18 @@ impl Compositor {
.clone() .clone()
.and_then(|window| instance.create_surface(window).ok()); .and_then(|window| instance.create_surface(window).ok());
let mut adapter_options = wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::from_env()
.unwrap_or(wgpu::PowerPreference::HighPerformance),
compatible_surface: compatible_surface.as_ref(),
force_fallback_adapter: false,
};
let mut adapter = None; let mut adapter = None;
#[cfg_attr(not(unix), allow(dead_code))] #[cfg_attr(not(unix), allow(dead_code))]
if std::env::var_os("WGPU_ADAPTER_NAME").is_none() { if std::env::var_os("WGPU_ADAPTER_NAME").is_none() {
#[cfg(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();
@ -180,7 +167,6 @@ impl Compositor {
} else if let Ok(name) = std::env::var("WGPU_ADAPTER_NAME") { } else if let Ok(name) = std::env::var("WGPU_ADAPTER_NAME") {
adapter = instance adapter = instance
.enumerate_adapters(settings.backends) .enumerate_adapters(settings.backends)
.await
.into_iter() .into_iter()
.filter(|adapter| { .filter(|adapter| {
let info = adapter.get_info(); let info = adapter.get_info();
@ -193,8 +179,6 @@ impl Compositor {
true true
} }
}); });
} else {
adapter = instance.request_adapter(&adapter_options).await.ok();
} }
let adapter = match adapter { let adapter = match adapter {
@ -215,7 +199,7 @@ impl Compositor {
.await; .await;
compatible_surface = compatible_window compatible_surface = compatible_window
.and_then(|window| instance.create_surface(window).ok()); .and_then(|window| instance.create_surface(window).ok());
adapter_options = wgpu::RequestAdapterOptions { let adapter_options = wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::from_env() power_preference: wgpu::PowerPreference::from_env()
.unwrap_or(wgpu::PowerPreference::HighPerformance), .unwrap_or(wgpu::PowerPreference::HighPerformance),
compatible_surface: compatible_surface.as_ref(), compatible_surface: compatible_surface.as_ref(),
@ -297,14 +281,6 @@ impl Compositor {
..limits ..limits
}); });
// Request SHADER_F16 only if the adapter supports it (e.g., not available in WebGL2)
let required_features =
if adapter.features().contains(wgpu::Features::SHADER_F16) {
wgpu::Features::SHADER_F16
} else {
wgpu::Features::empty()
};
let mut errors = Vec::new(); let mut errors = Vec::new();
for required_limits in limits { for required_limits in limits {
@ -313,7 +289,7 @@ impl Compositor {
label: Some( label: Some(
"iced_wgpu::window::compositor device descriptor", "iced_wgpu::window::compositor device descriptor",
), ),
required_features, required_features: wgpu::Features::empty(),
required_limits: required_limits.clone(), required_limits: required_limits.clone(),
memory_hints: wgpu::MemoryHints::MemoryUsage, memory_hints: wgpu::MemoryHints::MemoryUsage,
trace: wgpu::Trace::Off, trace: wgpu::Trace::Off,
@ -371,14 +347,6 @@ pub fn present(
) -> Result<(), compositor::SurfaceError> { ) -> Result<(), compositor::SurfaceError> {
match surface.get_current_texture() { match surface.get_current_texture() {
Ok(frame) => { Ok(frame) => {
if frame.texture.width() != viewport.physical_width()
|| frame.texture.height() != viewport.physical_height()
{
return Err(compositor::SurfaceError::Outdated);
}
let suboptimal = frame.suboptimal;
let view = &frame let view = &frame
.texture .texture
.create_view(&wgpu::TextureViewDescriptor::default()); .create_view(&wgpu::TextureViewDescriptor::default());
@ -394,10 +362,6 @@ pub fn present(
on_pre_present(); on_pre_present();
frame.present(); frame.present();
if suboptimal {
return Err(compositor::SurfaceError::Outdated);
}
Ok(()) Ok(())
} }
Err(error) => match error { Err(error) => match error {

View file

@ -599,7 +599,7 @@ where
(x + width) as f64, (x + width) as f64,
(y + height) as f64, (y + height) as f64,
); );
let is_hovered = state.state.downcast_ref::<State>().is_hovered; let _is_hovered = state.state.downcast_ref::<State>().is_hovered;
let mut node = Node::new(Role::Button); let mut node = Node::new(Role::Button);
node.add_action(Action::Focus); node.add_action(Action::Focus);

View file

@ -505,7 +505,6 @@ where
state.raw(), state.raw(),
crate::text::Style { crate::text::Style {
color: style.text_color, color: style.text_color,
..crate::text::Style::default()
}, },
viewport, viewport,
); );
@ -537,7 +536,7 @@ where
}; };
let bounds = layout.bounds(); let bounds = layout.bounds();
let is_hovered = cursor.is_over(bounds); let _is_hovered = cursor.is_over(bounds);
let Rectangle { let Rectangle {
x, x,
y, y,

View file

@ -293,8 +293,8 @@ fn drawing_bounds<Renderer, Handle>(
content_fit: ContentFit, content_fit: ContentFit,
rotation: Rotation, rotation: Rotation,
scale: f32, scale: f32,
opacity: f32, _opacity: f32,
border_radius: [f32; 4], _border_radius: [f32; 4],
) -> Rectangle ) -> Rectangle
where where
Renderer: image::Renderer<Handle = Handle>, Renderer: image::Renderer<Handle = Handle>,

View file

@ -292,12 +292,9 @@ where
.zip(&mut tree.children) .zip(&mut tree.children)
.zip(layout.children()) .zip(layout.children())
.for_each(|((child, state), c_layout)| { .for_each(|((child, state), c_layout)| {
child.as_widget_mut().operate( child
state, .as_widget_mut()
c_layout.with_virtual_offset(layout.virtual_offset()), .operate(state, c_layout.with_virtual_offset(layout.virtual_offset()), renderer, operation);
renderer,
operation,
);
}); });
}); });
} }
@ -320,13 +317,7 @@ where
.zip(layout.children()) .zip(layout.children())
{ {
child.as_widget_mut().update( child.as_widget_mut().update(
tree, tree, event, c_layout.with_virtual_offset(layout.virtual_offset()), cursor, renderer, clipboard, shell,
event,
c_layout.with_virtual_offset(layout.virtual_offset()),
cursor,
renderer,
clipboard,
shell,
viewport, viewport,
); );
} }
@ -345,13 +336,9 @@ where
.zip(&tree.children) .zip(&tree.children)
.zip(layout.children()) .zip(layout.children())
.map(|((child, tree), c_layout)| { .map(|((child, tree), c_layout)| {
child.as_widget().mouse_interaction( child
tree, .as_widget()
c_layout.with_virtual_offset(layout.virtual_offset()), .mouse_interaction(tree, c_layout.with_virtual_offset(layout.virtual_offset()), cursor, viewport, renderer)
cursor,
viewport,
renderer,
)
}) })
.max() .max()
.unwrap_or_default() .unwrap_or_default()

View file

@ -5,7 +5,6 @@ pub mod component;
#[allow(deprecated)] #[allow(deprecated)]
pub use component::Component; pub use component::Component;
use iced_renderer::core::widget::Operation;
mod cache; mod cache;
@ -15,7 +14,7 @@ use crate::core::mouse;
use crate::core::overlay; use crate::core::overlay;
use crate::core::renderer; use crate::core::renderer;
use crate::core::widget::tree::{self, Tree}; use crate::core::widget::tree::{self, Tree};
use crate::core::widget::{self, Widget}; use crate::core::widget::Widget;
use crate::core::{ use crate::core::{
self, Clipboard, Event, Length, Rectangle, Shell, Size, Vector, self, Clipboard, Event, Length, Rectangle, Shell, Size, Vector,
}; };

View file

@ -10,7 +10,6 @@ use crate::core::{
self, Clipboard, Element, Length, Rectangle, Shell, Size, Vector, Widget, self, Clipboard, Element, Length, Rectangle, Shell, Size, Vector, Widget,
}; };
use iced_renderer::core::widget::Operation;
use ouroboros::self_referencing; use ouroboros::self_referencing;
use std::cell::RefCell; use std::cell::RefCell;
use std::marker::PhantomData; use std::marker::PhantomData;
@ -527,11 +526,11 @@ where
renderer: &Renderer, renderer: &Renderer,
dnd_rectangles: &mut core::clipboard::DndDestinationRectangles, dnd_rectangles: &mut core::clipboard::DndDestinationRectangles,
) { ) {
let mut tree = tree let tree = tree
.state .state
.downcast_ref::<Rc<RefCell<Option<Tree>>>>() .downcast_ref::<Rc<RefCell<Option<Tree>>>>()
.borrow_mut(); .borrow_mut();
let mut tree = tree.as_ref().unwrap(); let tree = tree.as_ref().unwrap();
self.with_element(|element| { self.with_element(|element| {
element.as_widget().drag_destinations( element.as_widget().drag_destinations(
&tree.children[0], &tree.children[0],

View file

@ -363,11 +363,10 @@ where
let state = tree.state.downcast_mut::<State>(); let state = tree.state.downcast_mut::<State>();
let offset = layout.position() - Point::ORIGIN; let offset = layout.position() - Point::ORIGIN;
for (element, (index, layout, tree)) in self self.visible_elements
.visible_elements
.iter_mut() .iter_mut()
.zip(&mut state.visible_layouts) .zip(&mut state.visible_layouts)
{ .for_each(|(element, (index, layout, tree))| {
element.as_widget_mut().update( element.as_widget_mut().update(
tree, tree,
event, event,
@ -380,8 +379,8 @@ where
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 {

View file

@ -575,7 +575,7 @@ where
align_x: text::Alignment::Default, align_x: text::Alignment::Default,
align_y: alignment::Vertical::Center, align_y: alignment::Vertical::Center,
shaping: self.text_shaping, shaping: self.text_shaping,
wrapping: text::Wrapping::default(), wrapping: self.text_wrap,
ellipsize: text::Ellipsize::default(), ellipsize: text::Ellipsize::default(),
}, },
Point::new(bounds.x + self.padding.left, bounds.center_y()), Point::new(bounds.x + self.padding.left, bounds.center_y()),

View file

@ -459,7 +459,6 @@ where
state.raw(), state.raw(),
crate::text::Style { crate::text::Style {
color: style.text_color, color: style.text_color,
..crate::text::Style::default()
}, },
viewport, viewport,
); );

View file

@ -1585,7 +1585,7 @@ where
return A11yTree::default(); return A11yTree::default();
} }
let window = layout.bounds(); let window = layout.bounds();
let is_hovered = cursor.is_over(window); let _is_hovered = cursor.is_over(window);
let Rectangle { let Rectangle {
x, x,
y, y,
@ -1669,7 +1669,7 @@ where
})) }))
{ {
let scrollbar_bounds = scrollbar.total_bounds; let scrollbar_bounds = scrollbar.total_bounds;
let is_hovered = cursor.is_over(scrollbar_bounds); let _is_hovered = cursor.is_over(scrollbar_bounds);
let Rectangle { let Rectangle {
x, x,
y, y,

View file

@ -695,7 +695,7 @@ where
}; };
let bounds = layout.bounds(); let bounds = layout.bounds();
let is_hovered = cursor.is_over(bounds); let _is_hovered = cursor.is_over(bounds);
let Rectangle { let Rectangle {
x, x,
y, y,
@ -732,18 +732,14 @@ where
node.set_labelled_by(label.clone()); node.set_labelled_by(label.clone());
} }
if let Ok(min) = self.range.start().clone().try_into() { let Ok(min) = self.range.start().clone().try_into();
node.set_min_numeric_value(min); node.set_min_numeric_value(min);
} let Ok(max) = self.range.end().clone().try_into();
if let Ok(max) = self.range.end().clone().try_into() {
node.set_max_numeric_value(max); node.set_max_numeric_value(max);
} let Ok(value) = self.value.clone().try_into();
if let Ok(value) = self.value.clone().try_into() {
node.set_numeric_value(value); node.set_numeric_value(value);
} let Ok(step) = self.step.clone().try_into();
if let Ok(step) = self.step.clone().try_into() {
node.set_numeric_value_step(step); node.set_numeric_value_step(step);
}
// TODO: This could be a setting on the slider // TODO: This could be a setting on the slider
node.set_live(iced_accessibility::accesskit::Live::Polite); node.set_live(iced_accessibility::accesskit::Live::Polite);

View file

@ -31,7 +31,6 @@ use crate::core::{
#[cfg(feature = "a11y")] #[cfg(feature = "a11y")]
use std::borrow::Cow; use std::borrow::Cow;
use std::marker::PhantomData;
use std::path::PathBuf; use std::path::PathBuf;
pub use crate::core::svg::Handle; pub use crate::core::svg::Handle;

View file

@ -179,10 +179,7 @@ where
{ {
let color = color.map(Into::into); let color = color.map(Into::into);
self.style(move |_theme| Style { self.style(move |_theme| Style { color })
color,
..Style::default()
})
} }
/// Sets the default style class of the [`Rich`] text. /// Sets the default style class of the [`Rich`] text.

View file

@ -544,8 +544,6 @@ pub struct State<Highlighter: text::Highlighter> {
highlighter: RefCell<Highlighter>, highlighter: RefCell<Highlighter>,
highlighter_settings: Highlighter::Settings, highlighter_settings: Highlighter::Settings,
highlighter_format_address: usize, highlighter_format_address: usize,
context_menu_position: Option<Point>,
pending_edit: Option<Action>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -581,14 +579,6 @@ impl<Highlighter: text::Highlighter> State<Highlighter> {
pub fn is_focused(&self) -> bool { pub fn is_focused(&self) -> bool {
self.focus.is_some() self.focus.is_some()
} }
/// Clears focus, selection, and all interaction state.
pub fn clear_focus(&mut self) {
self.focus = None;
self.drag_click = None;
self.context_menu_position = None;
self.pending_edit = Some(Action::ClearSelection);
}
} }
impl<Highlighter: text::Highlighter> operation::Focusable impl<Highlighter: text::Highlighter> operation::Focusable
@ -603,7 +593,7 @@ impl<Highlighter: text::Highlighter> operation::Focusable
} }
fn unfocus(&mut self) { fn unfocus(&mut self) {
self.clear_focus(); self.focus = None;
} }
} }
@ -631,8 +621,6 @@ where
)), )),
highlighter_settings: self.highlighter_settings.clone(), highlighter_settings: self.highlighter_settings.clone(),
highlighter_format_address: self.highlighter_format as usize, highlighter_format_address: self.highlighter_format as usize,
context_menu_position: None,
pending_edit: None,
}) })
} }
@ -711,58 +699,6 @@ where
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
_viewport: &Rectangle, _viewport: &Rectangle,
) { ) {
// Handle context menu and focus-loss on clicks outside.
{
let state = tree.state.downcast_mut::<State<Highlighter>>();
let text_bounds = layout.bounds();
if matches!(
event,
Event::Mouse(mouse::Event::ButtonPressed(_))
| Event::Touch(
crate::core::touch::Event::FingerPressed { .. }
)
) && cursor.position_over(text_bounds).is_none()
{
state.clear_focus();
}
match event {
Event::Mouse(mouse::Event::ButtonPressed(
mouse::Button::Right,
)) => {
if let Some(pos) = cursor.position_over(text_bounds) {
if state.focus.is_none() {
state.focus = Some(Focus::now());
}
state.context_menu_position = Some(pos);
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;
}; };
@ -1287,23 +1223,6 @@ impl<Message> Binding<Message> {
return Some(binding); return Some(binding);
} }
let alt_clipboard = match key.as_ref() {
keyboard::Key::Named(key::Named::Insert) if modifiers.shift() => {
Some(Self::Paste)
}
keyboard::Key::Named(key::Named::Insert) if modifiers.command() => {
Some(Self::Copy)
}
keyboard::Key::Named(key::Named::Delete) if modifiers.shift() => {
Some(Self::Cut)
}
_ => None,
};
if let Some(binding) = alt_clipboard {
return Some(binding);
}
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
let modified_key = let modified_key =
convert_macos_shortcut(&key, modifiers).unwrap_or(modified_key); convert_macos_shortcut(&key, modifiers).unwrap_or(modified_key);
@ -1626,63 +1545,3 @@ pub(crate) fn convert_macos_shortcut(
Some(keyboard::Key::Named(key)) Some(keyboard::Key::Named(key))
} }
use crate::core::widget::text::HasSelectableText;
impl<Highlighter, Message, Theme, Renderer> HasSelectableText
for TextEditor<'_, Highlighter, Message, Theme, Renderer>
where
Highlighter: text::Highlighter,
Theme: Catalog,
Renderer: text::Renderer,
{
fn selected_text(&self, _tree: &widget::Tree) -> Option<String> {
self.content.selection()
}
fn select_all(&self, tree: &mut widget::Tree) {
let state = tree.state.downcast_mut::<State<Highlighter>>();
state.pending_edit = Some(Action::SelectAll);
}
fn is_editable(&self) -> bool {
self.on_edit.is_some()
}
fn 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())
}
}

View file

@ -482,7 +482,6 @@ where
state.raw(), state.raw(),
crate::text::Style { crate::text::Style {
color: style.text_color, color: style.text_color,
..crate::text::Style::default()
}, },
viewport, viewport,
); );
@ -570,7 +569,7 @@ where
}; };
let bounds = layout.bounds(); let bounds = layout.bounds();
let is_hovered = cursor.is_over(bounds); let _is_hovered = cursor.is_over(bounds);
let Rectangle { let Rectangle {
x, x,
y, y,

View file

@ -37,8 +37,6 @@ cctk = [
"xkeysym", "xkeysym",
"dep:cctk", "dep:cctk",
] ]
# Prevents multiple separate process instances.
single-instance = []
[dependencies] [dependencies]

View file

@ -4,11 +4,10 @@ use crate::futures::futures::channel::mpsc;
use iced_accessibility::accesskit::{ use iced_accessibility::accesskit::{
ActivationHandler, Node, NodeId, Role, Tree, TreeId, TreeUpdate, ActivationHandler, Node, NodeId, Role, Tree, TreeId, TreeUpdate,
}; };
use iced_accessibility::accesskit_winit::Adapter;
use iced_runtime::core; use iced_runtime::core;
pub struct WinitActivationHandler { pub struct WinitActivationHandler {
pub proxy: mpsc::UnboundedSender<Control>, pub(crate) proxy: mpsc::UnboundedSender<Control>,
pub title: String, pub title: String,
} }
@ -34,7 +33,7 @@ impl ActivationHandler for WinitActivationHandler {
pub struct WinitActionHandler { pub struct WinitActionHandler {
pub id: core::window::Id, pub id: core::window::Id,
pub proxy: mpsc::UnboundedSender<Control>, pub(crate) proxy: mpsc::UnboundedSender<Control>,
} }
impl iced_accessibility::accesskit::ActionHandler for WinitActionHandler { impl iced_accessibility::accesskit::ActionHandler for WinitActionHandler {
@ -49,7 +48,7 @@ impl iced_accessibility::accesskit::ActionHandler for WinitActionHandler {
} }
pub struct WinitDeactivationHandler { pub struct WinitDeactivationHandler {
pub proxy: mpsc::UnboundedSender<Control>, pub(crate) proxy: mpsc::UnboundedSender<Control>,
} }
impl iced_accessibility::accesskit::DeactivationHandler impl iced_accessibility::accesskit::DeactivationHandler

View file

@ -25,7 +25,7 @@ const DRAG_RESIZE_SUPPORTED: bool = false;
/// If supported by winit, returns a closure that implements cursor resize support. /// If supported by winit, returns a closure that implements cursor resize support.
pub fn event_func( pub fn event_func(
window: &dyn winit::window::Window, _window: &dyn winit::window::Window,
border_size: f64, border_size: f64,
) -> Option< ) -> Option<
Box< Box<

View file

@ -1,7 +1,7 @@
//! Access the clipboard. //! Access the clipboard.
use std::sync::Mutex; use std::sync::Mutex;
use std::{any::Any, borrow::Cow}; use std::borrow::Cow;
use crate::Control; use crate::Control;

View file

@ -3,9 +3,6 @@
//! [`winit`]: https://github.com/rust-windowing/winit //! [`winit`]: https://github.com/rust-windowing/winit
//! [`iced_runtime`]: https://github.com/iced-rs/iced/tree/0.14/runtime //! [`iced_runtime`]: https://github.com/iced-rs/iced/tree/0.14/runtime
use crate::core::input_method; use crate::core::input_method;
use std::hash::DefaultHasher;
use std::hash::Hash;
use std::hash::Hasher;
use std::sync::Arc; use std::sync::Arc;
use crate::core::keyboard; use crate::core::keyboard;
@ -14,7 +11,6 @@ use crate::core::theme;
use crate::core::touch; use crate::core::touch;
use crate::core::window; use crate::core::window;
use crate::core::{Event, Point, Size}; use crate::core::{Event, Point, Size};
use iced_futures::core::event::PlatformSpecific;
use winit::dpi::PhysicalPosition; use winit::dpi::PhysicalPosition;
use winit::event::ButtonSource; use winit::event::ButtonSource;
use winit::event::ElementState; use winit::event::ElementState;
@ -27,7 +23,7 @@ use winit::keyboard::SmolStr;
pub fn window_attributes( pub fn window_attributes(
settings: window::Settings, settings: window::Settings,
title: &str, title: &str,
scale_factor: f64, _scale_factor: f64,
primary_monitor: Option<winit::monitor::MonitorHandle>, primary_monitor: Option<winit::monitor::MonitorHandle>,
_id: Option<String>, _id: Option<String>,
) -> winit::window::WindowAttributes { ) -> winit::window::WindowAttributes {
@ -178,7 +174,7 @@ pub fn window_attributes(
), ),
)); ));
} }
#[cfg(target_os = "linux")] #[cfg(all(feature = "cctk", target_os = "linux"))]
{ {
use winit::platform::wayland::WindowAttributesWayland; use winit::platform::wayland::WindowAttributesWayland;
@ -387,19 +383,19 @@ pub fn window_event(
self::modifiers(new_modifiers.state()), self::modifiers(new_modifiers.state()),
))) )))
} }
WindowEvent::Ime(event) => match event { WindowEvent::Ime(event) => Some(Event::InputMethod(match event {
Ime::Enabled => Some(input_method::Event::Opened), Ime::Enabled => input_method::Event::Opened,
Ime::Preedit(content, size) => Some(input_method::Event::Preedit( Ime::Preedit(content, size) => input_method::Event::Preedit(
content, content,
size.map(|(start, end)| start..end), size.map(|(start, end)| start..end),
)), ),
Ime::Commit(content) => Some(input_method::Event::Commit(content)), Ime::Commit(content) => input_method::Event::Commit(content),
Ime::Disabled => Some(input_method::Event::Closed), Ime::Disabled => input_method::Event::Closed,
// Upstream iced depends on the winit version which doesn't support Ime::DeleteSurrounding {
// this kind of ime event. before_bytes: _,
Ime::DeleteSurrounding { .. } => None, after_bytes: _,
} } => todo!(),
.map(Event::InputMethod), })),
WindowEvent::Focused(focused) => Some(Event::Window(if focused { WindowEvent::Focused(focused) => Some(Event::Window(if focused {
window::Event::Focused window::Event::Focused
} else { } else {
@ -411,7 +407,7 @@ pub fn window_event(
WindowEvent::DragDropped { paths, .. } => { WindowEvent::DragDropped { paths, .. } => {
Some(Event::Window(window::Event::FileDropped(paths.clone()))) Some(Event::Window(window::Event::FileDropped(paths.clone())))
} }
WindowEvent::DragLeft { position } => { WindowEvent::DragLeft { position: _ } => {
Some(Event::Window(window::Event::FilesHoveredLeft)) Some(Event::Window(window::Event::FilesHoveredLeft))
} }
@ -642,10 +638,6 @@ pub fn mouse_interaction(
Interaction::AllScroll => winit_core::cursor::CursorIcon::AllScroll, Interaction::AllScroll => winit_core::cursor::CursorIcon::AllScroll,
Interaction::ZoomIn => winit_core::cursor::CursorIcon::ZoomIn, Interaction::ZoomIn => winit_core::cursor::CursorIcon::ZoomIn,
Interaction::ZoomOut => winit_core::cursor::CursorIcon::ZoomOut, Interaction::ZoomOut => winit_core::cursor::CursorIcon::ZoomOut,
Interaction::Cell => winit_core::cursor::CursorIcon::Cell,
Interaction::Move => winit_core::cursor::CursorIcon::Move,
Interaction::Copy => winit_core::cursor::CursorIcon::Copy,
Interaction::Help => winit_core::cursor::CursorIcon::Help,
Interaction::Hidden => { Interaction::Hidden => {
return None; return None;
} }
@ -704,10 +696,10 @@ enum TouchInternal {
/// ///
/// [`winit`]: https://github.com/rust-windowing/winit /// [`winit`]: https://github.com/rust-windowing/winit
/// [`iced`]: https://github.com/iced-rs/iced/tree/0.12 /// [`iced`]: https://github.com/iced-rs/iced/tree/0.12
pub fn touch_event( fn touch_event(
finger: FingerId, finger: FingerId,
state: TouchInternal, state: TouchInternal,
force: Option<Force>, _force: Option<Force>,
position: Option<PhysicalPosition<f64>>, position: Option<PhysicalPosition<f64>>,
scale_factor: f64, scale_factor: f64,
) -> touch::Event { ) -> touch::Event {
@ -1081,6 +1073,7 @@ pub fn physical_key(
/// ///
/// [`winit`]: https://github.com/rust-windowing/winit /// [`winit`]: https://github.com/rust-windowing/winit
/// [`iced`]: https://github.com/iced-rs/iced/tree/0.12 /// [`iced`]: https://github.com/iced-rs/iced/tree/0.12
#[allow(deprecated)] // KeyCode::Super/Hyper/Turbo: legacy spec, no Meta variant in this enum
pub fn key_code( pub fn key_code(
key_code: winit::keyboard::KeyCode, key_code: winit::keyboard::KeyCode,
) -> Option<keyboard::key::Code> { ) -> Option<keyboard::key::Code> {
@ -1285,6 +1278,7 @@ pub fn key_code(
}) })
} }
#[allow(deprecated)] // KeyCode::Super/Hyper/Turbo: legacy spec, no Meta variant in this enum
pub fn winit_key_code( pub fn winit_key_code(
key_code: keyboard::key::Code, key_code: keyboard::key::Code,
) -> Option<winit::keyboard::KeyCode> { ) -> Option<winit::keyboard::KeyCode> {
@ -1595,7 +1589,10 @@ pub fn resize_direction(
} }
} }
// Fields are passed through to winit via `IconProvider: AsAny` for potential
// downcast; they are not read directly here.
#[derive(Debug)] #[derive(Debug)]
#[allow(dead_code)]
pub struct RawImage(Vec<u8>, Size<u32>); pub struct RawImage(Vec<u8>, Size<u32>);
impl IconProvider for RawImage {} impl IconProvider for RawImage {}
impl From<RawImage> for winit_core::icon::Icon { impl From<RawImage> for winit_core::icon::Icon {

View file

@ -40,6 +40,12 @@ pub mod clipboard;
pub mod conversion; pub mod conversion;
pub mod platform_specific; pub mod platform_specific;
#[cfg(feature = "program")]
pub mod program;
#[cfg(feature = "system")]
pub mod system;
mod error; mod error;
mod proxy; mod proxy;
mod window; mod window;
@ -47,7 +53,6 @@ mod window;
pub use clipboard::Clipboard; pub use clipboard::Clipboard;
pub use error::Error; pub use error::Error;
pub use proxy::Proxy; pub use proxy::Proxy;
use winit::dpi::LogicalSize;
use winit::dpi::PhysicalPosition; use winit::dpi::PhysicalPosition;
use winit::dpi::PhysicalSize; use winit::dpi::PhysicalSize;
@ -152,7 +157,8 @@ where
let (boot_sender, boot_receiver) = oneshot::channel(); let (boot_sender, boot_receiver) = oneshot::channel();
let (control_sender, control_receiver) = mpsc::unbounded(); let (control_sender, control_receiver) = mpsc::unbounded();
let (system_theme_sender, system_theme_receiver) = oneshot::channel(); // Sender side currently not wired to a producer; receiver gets default.
let (_system_theme_sender, system_theme_receiver) = oneshot::channel();
let instance = Box::pin(run_instance::<P>( let instance = Box::pin(run_instance::<P>(
program, program,
@ -173,9 +179,6 @@ where
struct BootConfig { struct BootConfig {
sender: oneshot::Sender<()>, sender: oneshot::Sender<()>,
fonts: Vec<Cow<'static, [u8]>>,
graphics_settings: graphics::Settings,
is_wayland: bool,
} }
struct Runner<Message: 'static, F> { struct Runner<Message: 'static, F> {
instance: std::pin::Pin<Box<F>>, instance: std::pin::Pin<Box<F>>,
@ -185,15 +188,11 @@ where
sender: mpsc::UnboundedSender<Event<Message>>, sender: mpsc::UnboundedSender<Event<Message>>,
receiver: mpsc::UnboundedReceiver<Control>, receiver: mpsc::UnboundedReceiver<Control>,
error: Option<Error>, error: Option<Error>,
system_theme: Option<oneshot::Sender<theme::Mode>>,
#[cfg(feature = "a11y")] #[cfg(feature = "a11y")]
control_sender: mpsc::UnboundedSender<Control>, control_sender: mpsc::UnboundedSender<Control>,
#[cfg(feature = "a11y")] #[cfg(feature = "a11y")]
adapters: std::collections::HashMap< adapters: std::collections::HashMap<window::Id, (u64, iced_accessibility::accesskit_winit::Adapter)>,
window::Id,
(u64, iced_accessibility::accesskit_winit::Adapter),
>,
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
is_booted: std::rc::Rc<std::cell::RefCell<bool>>, is_booted: std::rc::Rc<std::cell::RefCell<bool>>,
@ -206,9 +205,6 @@ where
context, context,
boot: Some(BootConfig { boot: Some(BootConfig {
sender: boot_sender, sender: boot_sender,
fonts: settings.fonts,
graphics_settings,
is_wayland,
}), }),
id: settings.id, id: settings.id,
sender: event_sender, sender: event_sender,
@ -216,7 +212,6 @@ where
#[cfg(feature = "a11y")] #[cfg(feature = "a11y")]
control_sender: control_sender.clone(), control_sender: control_sender.clone(),
error: None, error: None,
system_theme: Some(system_theme_sender),
#[cfg(feature = "a11y")] #[cfg(feature = "a11y")]
adapters: Default::default(), adapters: Default::default(),
@ -319,12 +314,7 @@ where
event_loop: &dyn winit::event_loop::ActiveEventLoop, event_loop: &dyn winit::event_loop::ActiveEventLoop,
) { ) {
// create initial window // create initial window
let Some(BootConfig { let Some(BootConfig { sender }) = self.boot.take()
sender,
fonts,
graphics_settings,
is_wayland,
}) = self.boot.take()
else { else {
return; return;
}; };
@ -362,6 +352,8 @@ where
where where
F: Future<Output = ()>, F: Future<Output = ()>,
{ {
// TODO(yoda): migrate try_next → try_recv (futures-channel API change).
#[allow(deprecated)]
fn process_event( fn process_event(
&mut self, &mut self,
event_loop: &dyn winit::event_loop::ActiveEventLoop, event_loop: &dyn winit::event_loop::ActiveEventLoop,
@ -500,15 +492,9 @@ where
} }
}; };
} }
let window: Arc< let window: Arc<dyn winit::window::Window + 'static> = Arc::from(window);
dyn winit::window::Window + 'static,
> = Arc::from(window);
#[cfg(feature = "a11y")] #[cfg(feature = "a11y")]
self.init_adapter( self.init_adapter(event_loop, id, window.clone());
event_loop,
id,
window.clone(),
);
self.process_event( self.process_event(
event_loop, event_loop,
@ -577,13 +563,11 @@ where
.expect("Send event"); .expect("Send event");
} }
Control::Winit(id, e) => { Control::Winit(id, e) => {
#[cfg(all( #[cfg(all(feature = "cctk", target_os = "linux"))]
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(
@ -626,7 +610,7 @@ where
#[cfg(feature = "a11y")] #[cfg(feature = "a11y")]
Control::Cleanup(id) => { Control::Cleanup(id) => {
_ = self.adapters.remove(&id); _ = self.adapters.remove(&id);
} },
}, },
_ => { _ => {
break; break;
@ -641,21 +625,16 @@ where
} }
#[cfg(feature = "a11y")] #[cfg(feature = "a11y")]
fn init_adapter( fn init_adapter(&mut self, event_loop: &(dyn winit::event_loop::ActiveEventLoop + 'static), id: core::window::Id, window: Arc<dyn winit::window::Window + 'static>) {
&mut self,
event_loop: &(dyn winit::event_loop::ActiveEventLoop + 'static),
id: core::window::Id,
window: Arc<dyn winit::window::Window + 'static>,
) {
use crate::a11y::*; use crate::a11y::*;
use iced_accessibility::accesskit::{
ActivationHandler, Node, NodeId, Role, Tree, TreeUpdate,
};
use iced_accessibility::accesskit_winit::Adapter; use iced_accessibility::accesskit_winit::Adapter;
let node_id = iced_runtime::core::id::window_node_id(); let node_id =
iced_runtime::core::id::window_node_id();
let activation_handler = WinitActivationHandler { let activation_handler =
WinitActivationHandler {
proxy: self.control_sender.clone(), proxy: self.control_sender.clone(),
title: String::new(), title: String::new(),
}; };
@ -665,7 +644,8 @@ where
proxy: self.control_sender.clone(), proxy: self.control_sender.clone(),
}; };
let deactivation_handler = WinitDeactivationHandler { let deactivation_handler =
WinitDeactivationHandler {
proxy: self.control_sender.clone(), proxy: self.control_sender.clone(),
}; };
@ -682,6 +662,8 @@ where
), ),
), ),
); );
} }
} }
@ -699,6 +681,7 @@ where
} }
} }
#[allow(hidden_glob_reexports)] // intentional: internal Event shadows winit::event::Event from `pub use winit`
enum Event<Message: 'static> { enum Event<Message: 'static> {
WindowCreated { WindowCreated {
id: window::Id, id: window::Id,
@ -806,9 +789,6 @@ async fn run_instance<P>(
> = None; > = None;
let mut dnd_surface_id: Option<window::Id> = None; let mut dnd_surface_id: Option<window::Id> = None;
#[cfg(feature = "a11y")]
let mut a11y_enabled = false;
#[cfg(all(feature = "linux-theme-detection", target_os = "linux"))] #[cfg(all(feature = "linux-theme-detection", target_os = "linux"))]
let mut system_theme = { let mut system_theme = {
let to_mode = |color_scheme| match color_scheme { let to_mode = |color_scheme| match color_scheme {
@ -844,28 +824,6 @@ async fn run_instance<P>(
log::info!("System theme: {system_theme:?}"); log::info!("System theme: {system_theme:?}");
macro_rules! run_action {
($action:expr) => {
run_action(
$action,
&program,
&mut runtime,
&mut compositor,
&mut events,
&mut messages,
&mut clipboard,
&mut control_sender,
&mut user_interfaces,
&mut window_manager,
&mut ui_caches,
&mut is_window_opening,
&mut system_theme,
&mut platform_specific_handler,
is_daemon,
)
};
}
'next_event: loop { 'next_event: loop {
// Empty the queue if possible // Empty the queue if possible
let event = if let Ok(event) = event_receiver.try_recv() { let event = if let Ok(event) = event_receiver.try_recv() {
@ -885,13 +843,13 @@ 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),
); );
if compositor.is_none() {
match create_compositor::<P>( match create_compositor::<P>(
window.clone(), window.clone(),
CreateCompositor { CreateCompositor {
@ -916,7 +874,6 @@ async fn run_instance<P>(
continue; continue;
} }
} }
}
let window_theme = window let window_theme = window
.theme() .theme()
@ -942,7 +899,7 @@ async fn run_instance<P>(
compositor, compositor,
exit_on_close_request, exit_on_close_request,
system_theme, system_theme,
resize_border, resize_border
); );
window.raw.set_theme(conversion::window_theme( window.raw.set_theme(conversion::window_theme(
@ -1028,7 +985,22 @@ async fn run_instance<P>(
} }
} }
Event::UserEvent(action) => { Event::UserEvent(action) => {
let exited = run_action!(action); let exited = run_action(
action,
&program,
&mut runtime,
&mut compositor,
&mut events,
&mut messages,
&mut clipboard,
&mut control_sender,
&mut user_interfaces,
&mut window_manager,
&mut ui_caches,
&mut is_window_opening,
&mut system_theme,
&mut platform_specific_handler,
);
if exited { if exited {
runtime.track(None.into_iter()); runtime.track(None.into_iter());
} }
@ -1058,8 +1030,8 @@ async fn run_instance<P>(
// 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),
@ -1170,7 +1142,22 @@ async fn run_instance<P>(
continue; continue;
} }
_ = run_action!(action); _ = run_action(
action,
&program,
&mut runtime,
&mut compositor,
&mut events,
&mut messages,
&mut clipboard,
&mut control_sender,
&mut user_interfaces,
&mut window_manager,
&mut ui_caches,
&mut is_window_opening,
&mut system_theme,
&mut platform_specific_handler,
);
} }
for (window_id, window) in window_manager.iter_mut() { for (window_id, window) in window_manager.iter_mut() {
@ -1325,7 +1312,9 @@ async fn run_instance<P>(
continue; continue;
}; };
// Initiates a drag resize window state when found. // Initiates a drag resize window state when found.
if let Some(func) = window.drag_resize_window_func.as_mut() { if let Some(func) =
window.drag_resize_window_func.as_mut()
{
if func(window.raw.as_ref(), &event) { if func(window.raw.as_ref(), &event) {
continue; continue;
} }
@ -1351,9 +1340,22 @@ async fn run_instance<P>(
if matches!(event, winit::event::WindowEvent::CloseRequested) if matches!(event, winit::event::WindowEvent::CloseRequested)
&& window.exit_on_close_request && window.exit_on_close_request
{ {
_ = run_action!(Action::Window( _ = run_action(
runtime::window::Action::Close(id) Action::Window(runtime::window::Action::Close(id)),
)); &program,
&mut runtime,
&mut compositor,
&mut events,
&mut messages,
&mut clipboard,
&mut control_sender,
&mut user_interfaces,
&mut window_manager,
&mut ui_caches,
&mut is_window_opening,
&mut system_theme,
&mut platform_specific_handler,
);
} else { } else {
window.state.update(&program, window.raw.as_ref(), &event); window.state.update(&program, window.raw.as_ref(), &event);
@ -1379,7 +1381,6 @@ async fn run_instance<P>(
} }
let mut uis_stale = false; let mut uis_stale = false;
let mut resized = false;
for (id, window) in window_manager.iter_mut() { for (id, window) in window_manager.iter_mut() {
if skip && !window.resize_enabled { if skip && !window.resize_enabled {
continue; continue;
@ -1432,7 +1433,6 @@ async fn run_instance<P>(
// FIXME what to do when we are stuck in a configure event/resize request loop // FIXME what to do when we are stuck in a configure event/resize request loop
// We don't have control over how winit handles this. // We don't have control over how winit handles this.
window.resize_enabled = true; window.resize_enabled = true;
resized = true;
needs_redraw = true; needs_redraw = true;
let s = winit::dpi::Size::Logical( let s = winit::dpi::Size::Logical(
requested_size.cast(), requested_size.cast(),
@ -1531,7 +1531,22 @@ async fn run_instance<P>(
)); ));
for action in actions { for action in actions {
let exited = run_action!(action); let exited = run_action(
action,
&program,
&mut runtime,
&mut compositor,
&mut events,
&mut messages,
&mut clipboard,
&mut control_sender,
&mut user_interfaces,
&mut window_manager,
&mut ui_caches,
&mut is_window_opening,
&mut system_theme,
&mut platform_specific_handler,
);
if exited { if exited {
runtime.track(None.into_iter()); runtime.track(None.into_iter());
} }
@ -1632,8 +1647,8 @@ async fn run_instance<P>(
events.push((Some(id), conversion::a11y(action_request))); events.push((Some(id), conversion::a11y(action_request)));
} }
#[cfg(feature = "a11y")] #[cfg(feature = "a11y")]
Event::AccessibilityEnabled(enabled) => { Event::AccessibilityEnabled(_enabled) => {
a11y_enabled = enabled; // a11y enable signal currently unused at this layer
} }
Event::PlatformSpecific(e) => { Event::PlatformSpecific(e) => {
crate::platform_specific::handle_event( crate::platform_specific::handle_event(
@ -1741,6 +1756,7 @@ async fn run_instance<P>(
}; };
// search windows for widget with operation // search windows for widget with operation
if result.is_none() { if result.is_none() {
log::warn!( log::warn!(
"start_dnd: widget {:?} not found; drag will fail", "start_dnd: widget {:?} not found; drag will fail",
@ -2074,7 +2090,6 @@ fn run_action<'a, P, C>(
is_window_opening: &mut bool, is_window_opening: &mut bool,
system_theme: &mut theme::Mode, system_theme: &mut theme::Mode,
platform_specific: &mut crate::platform_specific::PlatformSpecific, platform_specific: &mut crate::platform_specific::PlatformSpecific,
is_daemon: bool,
) -> bool ) -> bool
where where
P: Program, P: Program,
@ -2159,7 +2174,7 @@ where
)); ));
} }
if window_manager.is_empty() && !is_daemon { if window_manager.is_empty() {
*compositor = None; *compositor = None;
} }
} }

View file

@ -1,14 +1,11 @@
//! Wayland specific shell //! Wayland specific shell
//! //!
use std::{borrow::Cow, collections::HashMap, sync::Arc}; use std::collections::HashMap;
#[cfg(all(feature = "cctk", target_os = "linux"))] use iced_graphics::compositor;
use cctk::sctk::reexports::client::Connection;
use iced_graphics::{Compositor, compositor};
use iced_runtime::{ use iced_runtime::{
core::{Vector, window}, core::{Vector, window}, user_interface,
platform_specific, user_interface,
}; };
use winit::raw_window_handle::HasWindowHandle; use winit::raw_window_handle::HasWindowHandle;
@ -17,8 +14,6 @@ pub mod wayland;
#[cfg(all(feature = "cctk", target_os = "linux"))] #[cfg(all(feature = "cctk", target_os = "linux"))]
pub use wayland::*; pub use wayland::*;
#[cfg(all(feature = "cctk", target_os = "linux"))]
use wayland_backend::client::Backend;
use crate::{CreateCompositor, Program, WindowManager}; use crate::{CreateCompositor, Program, WindowManager};

View file

@ -1,8 +1,8 @@
use crate::core::window::Id as SurfaceId; use crate::core::window::Id as SurfaceId;
use iced_runtime::{ use iced_runtime::{
self, Action, Task, self,
platform_specific::{self, wayland}, platform_specific::{self, wayland},
task, task, Action, Task,
}; };
pub fn request_token( pub fn request_token(

View file

@ -1,17 +0,0 @@
use iced_futures::core::window;
use iced_runtime::{
self, Action, Task,
platform_specific::{self, wayland},
task,
};
pub fn blur(
id: window::Id,
blur: Option<Vec<iced_runtime::core::Rectangle>>,
) -> Task<()> {
task::oneshot(|_| {
Action::PlatformSpecific(platform_specific::Action::Wayland(
wayland::Action::BlurSurface(id, blur),
))
})
}

View file

@ -1,11 +1,11 @@
use iced_futures::core::window; use iced_futures::core::window;
use iced_runtime::{ use iced_runtime::{
self, Action, Task, self,
platform_specific::{ platform_specific::{
self, self,
wayland::{self, CornerRadius}, wayland::{self, CornerRadius},
}, },
task, task, Action, Task,
}; };
pub fn corner_radius( pub fn corner_radius(

View file

@ -1,7 +1,7 @@
use iced_runtime::{ use iced_runtime::{
self, Action, Task, self,
platform_specific::{self, wayland}, platform_specific::{self, wayland},
task, task, Action, Task,
}; };
pub fn inhibit_shortcuts(inhibit: bool) -> Task<()> { pub fn inhibit_shortcuts(inhibit: bool) -> Task<()> {

View file

@ -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, Action, Task, self,
platform_specific::{ platform_specific::{
self, self,
wayland::{ wayland::{
@ -10,7 +10,7 @@ use iced_runtime::{
layer_surface::{IcedMargin, SctkLayerSurfaceSettings}, layer_surface::{IcedMargin, SctkLayerSurfaceSettings},
}, },
}, },
task, task, Action, Task,
}; };
pub use cctk::sctk::shell::wlr_layer::{Anchor, KeyboardInteractivity, Layer}; pub use cctk::sctk::shell::wlr_layer::{Anchor, KeyboardInteractivity, Layer};
@ -114,14 +114,3 @@ pub fn set_layer<Message>(id: SurfaceId, layer: Layer) -> Task<Message> {
)), )),
)) ))
} }
pub fn set_padding<Message>(
id: SurfaceId,
padding: IcedMargin,
) -> Task<Message> {
task::effect(Action::PlatformSpecific(
platform_specific::Action::Wayland(wayland::Action::LayerSurface(
wayland::layer_surface::Action::Padding { id, padding },
)),
))
}

View file

@ -1,7 +1,6 @@
//! Interact with the wayland objects of your application. //! Interact with the wayland objects of your application.
pub mod activation; pub mod activation;
pub mod blur;
pub mod corner_radius; pub mod corner_radius;
pub mod keyboard_shortcuts_inhibit; pub mod keyboard_shortcuts_inhibit;
pub mod layer_surface; pub mod layer_surface;

View file

@ -1,8 +1,7 @@
use iced_futures::core::window::Id; use iced_futures::core::window::Id;
use iced_runtime::{ use iced_runtime::{
Action, Task,
platform_specific::{self, wayland}, platform_specific::{self, wayland},
task, task, Action, Task,
}; };
/// Request subscription for overlap notification events on the surface /// Request subscription for overlap notification events on the surface

View file

@ -1,15 +1,12 @@
//! Interact with the popups of your application. //! Interact with the popups of your application.
use crate::core::window::Id as SurfaceId; use crate::core::window::Id as SurfaceId;
use iced_runtime::{ use iced_runtime::{
self, Action, Task, self,
platform_specific::{ platform_specific::{
self, self,
wayland::{ wayland::{self, popup::SctkPopupSettings},
self,
popup::{SctkPopupSettings, SctkPositioner},
}, },
}, task, 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>
@ -35,18 +32,6 @@ pub fn set_size<Message>(
)) ))
} }
/// <https://wayland.app/protocols/xdg-shell#xdg_popup:request:reposition>
pub fn reposition<Message>(
id: SurfaceId,
positioner: SctkPositioner,
) -> Task<Message> {
task::effect(Action::PlatformSpecific(
platform_specific::Action::Wayland(wayland::Action::Popup(
wayland::popup::Action::Reposition { id, positioner },
)),
))
}
/// <https://wayland.app/protocols/xdg-shell#xdg_popup:request:destroy> /// <https://wayland.app/protocols/xdg-shell#xdg_popup:request:destroy>
pub fn destroy_popup<Message>(id: SurfaceId) -> Task<Message> { pub fn destroy_popup<Message>(id: SurfaceId) -> Task<Message> {
task::effect(Action::PlatformSpecific( task::effect(Action::PlatformSpecific(

View file

@ -1,9 +1,9 @@
use crate::core::window::Id as SurfaceId; use crate::core::window::Id as SurfaceId;
use cctk::sctk::reexports::client::protocol::wl_output::WlOutput; use cctk::sctk::reexports::client::protocol::wl_output::WlOutput;
use iced_runtime::{ use iced_runtime::{
self, Action, Task, self,
platform_specific::{self, wayland}, platform_specific::{self, wayland},
task, task, Action, Task,
}; };
pub fn lock<Message>() -> Task<Message> { pub fn lock<Message>() -> Task<Message> {

View file

@ -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, Action, Task, self,
platform_specific::{ platform_specific::{
self, self,
wayland::{self, subsurface::SctkSubsurfaceSettings}, wayland::{self, subsurface::SctkSubsurfaceSettings},
}, },
task, task, Action, Task,
}; };
pub fn get_subsurface<Message>( pub fn get_subsurface<Message>(

View file

@ -1,56 +0,0 @@
/// Set by the user callback given to the [`EventLoop::run`] method.
///
/// Indicates the desired behavior of the event loop after [`Event::RedrawEventsCleared`] is emitted.
///
/// Defaults to [`Poll`].
///
/// ## Persistency
///
/// Almost every change is persistent between multiple calls to the event loop closure within a
/// given run loop. The only exception to this is [`ExitWithCode`] which, once set, cannot be unset.
/// Changes are **not** persistent between multiple calls to `run_return` - issuing a new call will
/// reset the control flow to [`Poll`].
///
/// [`ExitWithCode`]: Self::ExitWithCode
/// [`Poll`]: Self::Poll
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ControlFlow {
/// When the current loop iteration finishes, immediately begin a new iteration regardless of
/// whether or not new events are available to process.
///
/// ## Platform-specific
///
/// - **Web:** Events are queued and usually sent when `requestAnimationFrame` fires but sometimes
/// the events in the queue may be sent before the next `requestAnimationFrame` callback, for
/// example when the scaling of the page has changed. This should be treated as an implementation
/// detail which should not be relied on.
Poll,
/// When the current loop iteration finishes, suspend the thread until another event arrives.
Wait,
/// When the current loop iteration finishes, suspend the thread until either another event
/// arrives or the given time is reached.
///
/// Useful for implementing efficient timers. Applications which want to render at the display's
/// native refresh rate should instead use [`Poll`] and the VSync functionality of a graphics API
/// to reduce odds of missed frames.
///
/// [`Poll`]: Self::Poll
WaitUntil(std::time::Instant),
/// Send a [`LoopDestroyed`] event and stop the event loop. This variant is *sticky* - once set,
/// `control_flow` cannot be changed from `ExitWithCode`, and any future attempts to do so will
/// result in the `control_flow` parameter being reset to `ExitWithCode`.
///
/// The contained number will be used as exit code. The [`Exit`] constant is a shortcut for this
/// with exit code 0.
///
/// ## Platform-specific
///
/// - **Android / iOS / WASM:** The supplied exit code is unused.
/// - **Unix:** On most Unix-like platforms, only the 8 least significant bits will be used,
/// which can cause surprises with negative exit values (`-42` would end up as `214`). See
/// [`std::process::exit`].
///
/// [`LoopDestroyed`]: Event::LoopDestroyed
/// [`Exit`]: ControlFlow::Exit
ExitWithCode(i32),
}

View file

@ -1,16 +1,9 @@
pub mod control_flow;
pub mod proxy;
pub mod state; pub mod state;
#[cfg(feature = "a11y")]
use crate::platform_specific::SurfaceIdWrapper;
use crate::{ use crate::{
Control, Control,
futures::futures::channel::mpsc, futures::futures::channel::mpsc,
handlers::{ handlers::{overlap::OverlapNotifyV1, text_input::TextInputManager},
ext_background_effect, overlap::OverlapNotifyV1,
text_input::TextInputManager,
},
platform_specific::wayland::{ platform_specific::wayland::{
handlers::{ handlers::{
wp_fractional_scaling::FractionalScalingManager, wp_fractional_scaling::FractionalScalingManager,
@ -69,7 +62,7 @@ pub struct SctkEventLoop {
pub(crate) state: SctkState, pub(crate) state: SctkState,
} }
#[derive(Debug)] #[allow(dead_code)] // payloads kept for Debug; not inspected programmatically
pub enum Error { pub enum Error {
Calloop(calloop::Error), Calloop(calloop::Error),
Global(GlobalError), Global(GlobalError),
@ -127,7 +120,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| state.corner_radii.get(&id)) .map(|w| w.corner_radius.as_ref())
.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));
} }
@ -136,7 +129,7 @@ impl SctkEventLoop {
window, window,
id, id,
) => { ) => {
state.windows.push(SctkWindow { window, id }); state.windows.push(SctkWindow { window, id, corner_radius: Default::default() });
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)));
} }
@ -348,7 +341,7 @@ impl SctkEventLoop {
), ),
corner_radius_manager: registry_state.bind_one::<CosmicCornerRadiusManagerV1, _, _>( corner_radius_manager: registry_state.bind_one::<CosmicCornerRadiusManagerV1, _, _>(
&qh, &qh,
1..=2, 1..=1,
(), (),
).ok(), ).ok(),
toplevel_manager: ToplevelManagerState::try_new( toplevel_manager: ToplevelManagerState::try_new(
@ -361,12 +354,12 @@ impl SctkEventLoop {
(), (),
).ok(), ).ok(),
text_input_manager: TextInputManager::try_new(&registry_state, &qh), text_input_manager: TextInputManager::try_new(&registry_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,
@ -374,8 +367,6 @@ 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(),
@ -401,7 +392,6 @@ 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(),
}; };
@ -488,23 +478,6 @@ 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
@ -549,6 +522,22 @@ 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();
} }

View file

@ -1,66 +0,0 @@
use cctk::sctk::reexports::calloop;
use iced_futures::futures::{
Sink,
channel::mpsc,
task::{Context, Poll},
};
use std::pin::Pin;
/// An event loop proxy that implements `Sink`.
#[derive(Debug)]
pub struct Proxy<Message: 'static> {
raw: calloop::channel::Sender<Message>,
}
impl<Message: 'static> Clone for Proxy<Message> {
fn clone(&self) -> Self {
Self {
raw: self.raw.clone(),
}
}
}
impl<Message: 'static> Proxy<Message> {
/// Creates a new [`Proxy`] from an `EventLoopProxy`.
pub fn new(raw: calloop::channel::Sender<Message>) -> Self {
Self { raw }
}
/// send an event
pub fn send_event(&self, message: Message) {
let _ = self.raw.send(message);
}
}
impl<Message: 'static> Sink<Message> for Proxy<Message> {
type Error = mpsc::SendError;
fn poll_ready(
self: Pin<&mut Self>,
_cx: &mut Context<'_>,
) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn start_send(
self: Pin<&mut Self>,
message: Message,
) -> Result<(), Self::Error> {
let _ = self.raw.send(message);
Ok(())
}
fn poll_flush(
self: Pin<&mut Self>,
_cx: &mut Context<'_>,
) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn poll_close(
self: Pin<&mut Self>,
_cx: &mut Context<'_>,
) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
}

View file

@ -2,9 +2,7 @@ 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::{
@ -27,7 +25,7 @@ use iced_futures::{
}; };
use raw_window_handle::HasWindowHandle; use raw_window_handle::HasWindowHandle;
use std::{ use std::{
collections::{HashMap, HashSet, hash_map::Entry}, collections::{HashMap, HashSet},
convert::Infallible, convert::Infallible,
fmt::Debug, fmt::Debug,
sync::{Arc, Mutex, atomic::AtomicU32}, sync::{Arc, Mutex, atomic::AtomicU32},
@ -35,13 +33,16 @@ use std::{
}; };
use wayland_backend::client::ObjectId; use wayland_backend::client::ObjectId;
use winit::{ use winit::{
dpi::{LogicalPosition, LogicalSize}, dpi::LogicalSize,
platform::wayland::WindowExtWayland, platform::wayland::WindowExtWayland,
}; };
use cctk::{ use cctk::{
cosmic_protocols::{ cosmic_protocols::{
corner_radius::v1::client::cosmic_corner_radius_manager_v1::CosmicCornerRadiusManagerV1, corner_radius::v1::client::{
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::{
@ -63,12 +64,11 @@ 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, Modifiers}, keyboard::KeyEvent,
pointer::{CursorIcon, PointerData, ThemedPointer}, pointer::{CursorIcon, PointerData, ThemedPointer},
touch::TouchData, touch::TouchData,
}, },
@ -94,6 +94,7 @@ 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::{
@ -105,10 +106,6 @@ 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::{
@ -232,9 +229,6 @@ impl CommonSurface {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Common { pub struct Common {
pub(crate) fractional_scale: Option<f64>, pub(crate) fractional_scale: Option<f64>,
pub(crate) has_focus: bool,
pub(crate) ime_pos: LogicalPosition<u32>,
pub(crate) ime_size: LogicalSize<u32>,
pub(crate) size: LogicalSize<u32>, pub(crate) size: LogicalSize<u32>,
pub(crate) requested_size: (Option<u32>, Option<u32>), pub(crate) requested_size: (Option<u32>, Option<u32>),
pub(crate) wp_viewport: Option<WpViewport>, pub(crate) wp_viewport: Option<WpViewport>,
@ -244,9 +238,6 @@ impl Default for Common {
fn default() -> Self { fn default() -> Self {
Self { Self {
fractional_scale: Default::default(), fractional_scale: Default::default(),
has_focus: Default::default(),
ime_pos: Default::default(),
ime_size: Default::default(),
size: LogicalSize::new(1, 1), size: LogicalSize::new(1, 1),
requested_size: (None, None), requested_size: (None, None),
wp_viewport: None, wp_viewport: None,
@ -339,15 +330,24 @@ pub struct SctkPopupData {
pub(crate) parent: PopupParent, pub(crate) parent: PopupParent,
pub(crate) toplevel: WlSurface, pub(crate) toplevel: WlSurface,
pub(crate) positioner: Arc<XdgPositioner>, pub(crate) positioner: Arc<XdgPositioner>,
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<CornerRadiusWrapper>); pub struct SctkCornerRadius(Arc<MyCosmicCornerRadiusToplevelV1>);
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 {
@ -434,8 +434,6 @@ 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
@ -478,7 +476,6 @@ 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>>>,
@ -489,7 +486,6 @@ 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>>,
@ -501,10 +497,6 @@ 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),
@ -525,10 +517,6 @@ 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,
@ -545,10 +533,6 @@ 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),
@ -673,7 +657,9 @@ 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,
@ -687,18 +673,6 @@ 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)
{ {
@ -840,7 +814,6 @@ impl SctkState {
) )
} }
}; };
let toplevel = toplevel.clone();
popup.xdg_surface().set_window_geometry( popup.xdg_surface().set_window_geometry(
0, 0,
@ -873,10 +846,7 @@ 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 =
@ -900,7 +870,6 @@ impl SctkState {
parent: parent.clone(), parent: parent.clone(),
toplevel: toplevel.clone(), toplevel: toplevel.clone(),
positioner: positioner.clone(), positioner: positioner.clone(),
grab: settings.grab,
}, },
last_configure: None, last_configure: None,
_pending_requests: Default::default(), _pending_requests: Default::default(),
@ -909,13 +878,6 @@ 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(),
@ -950,15 +912,6 @@ 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()
@ -1011,9 +964,7 @@ impl SctkState {
layer_surface.set_input_region(Some(&region)); layer_surface.set_input_region(Some(&region));
region.destroy(); region.destroy();
} }
if let Some(blur) = self.pending_blur.remove(&id) { layer_surface.commit();
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)
@ -1047,10 +998,6 @@ 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(
@ -1058,14 +1005,6 @@ 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);
@ -1139,20 +1078,9 @@ 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(..)
@ -1174,7 +1102,6 @@ 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(),
@ -1249,14 +1176,6 @@ 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 } => {
@ -1329,7 +1248,7 @@ impl SctkState {
let timer = cctk::sctk::reexports::calloop::timer::Timer::from_duration(Duration::from_millis(30)); let timer = cctk::sctk::reexports::calloop::timer::Timer::from_duration(Duration::from_millis(30));
let queue_handle = self.queue_handle.clone(); let queue_handle = self.queue_handle.clone();
_ = self.loop_handle.insert_source(timer, move |_, _, state| { _ = self.loop_handle.insert_source(timer, move |_, _, state| {
let Some((mut popup, attempt)) = state.pending_popup.take() else { let Some((popup, attempt)) = state.pending_popup.take() else {
return TimeoutAction::Drop; return TimeoutAction::Drop;
}; };
@ -1361,6 +1280,7 @@ 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) {
@ -1414,12 +1334,6 @@ 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(..)
@ -1455,48 +1369,6 @@ 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 } => {
@ -1573,7 +1445,6 @@ 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);
} }
} }
@ -1584,11 +1455,6 @@ 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(..)
@ -1628,7 +1494,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 if !enabled { } else {
_ = self.overlap_notifications.remove(&my_id); _ = self.overlap_notifications.remove(&my_id);
} }
} else { } else {
@ -1645,7 +1511,6 @@ impl SctkState {
SctkEvent::SubsurfaceEvent (crate::sctk_event::SubsurfaceEventVariant::Created{ SctkEvent::SubsurfaceEvent (crate::sctk_event::SubsurfaceEventVariant::Created{
parent_id, parent_id,
parent, parent,
surface: subsurface,
qh: self.queue_handle.clone(), qh: self.queue_handle.clone(),
common_surface, common_surface,
surface_id: subsurface_settings.id, surface_id: subsurface_settings.id,
@ -1660,7 +1525,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(), subsurface.id)); destroyed.push((subsurface.instance.wl_surface.clone(), subsurface.instance.parent.clone()));
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();
@ -1668,12 +1533,7 @@ impl SctkState {
SctkEvent::SubsurfaceEvent( crate::sctk_event::SubsurfaceEventVariant::Destroyed(subsurface.instance) ) SctkEvent::SubsurfaceEvent( crate::sctk_event::SubsurfaceEventVariant::Destroyed(subsurface.instance) )
); );
} }
for (destroyed, parent, id) in destroyed { for (destroyed, parent) 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))) {
@ -1701,26 +1561,9 @@ 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() {
enum Surface { if let Some(w) = self.windows.iter_mut().find(|w| w.id == id) {
Xdg(XdgSurface, Option<XdgToplevel>), let geo_size: LogicalSize<f64> = w.window.surface_size().cast::<f64>().to_logical(w.window.scale_factor());
Wlr(ZwlrLayerSurfaceV1), let half_min_dim = (geo_size.width as u32).min(geo_size.height as u32) / 2 ;
}
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 {
@ -1729,73 +1572,37 @@ 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)) = self.corner_radii.get_mut(&id) { if let Some((protocol_object, corner_radii)) = w.corner_radius.as_mut() {
if *corner_radii != Some(adjusted_radii) { if *corner_radii != Some(adjusted_radii) {
match protocol_object.0.as_ref() { protocol_object.0.0.set_radius(
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,
), );
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()); *corner_radii = Some(adjusted_radii.clone());
} }
} else { } else {
let toplevel = w.xdg_toplevel(&self.connection);
let protocol_object = match s { let protocol_object = manager.get_corner_radius(&toplevel, &self.queue_handle, ());
Surface::Xdg(s, w) => {
if manager.version() == 1 { protocol_object.set_radius(
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,
), );
CornerRadiusWrapper::Wlr(protocol_object) => protocol_object.set_radius( w.corner_radius = Some((SctkCornerRadius(Arc::new(MyCosmicCornerRadiusToplevelV1( protocol_object))), Some(adjusted_radii.clone())));
adjusted_radii.top_left,
adjusted_radii.top_right,
adjusted_radii.bottom_right,
adjusted_radii.bottom_left,
)
};
_ = self.corner_radii.insert(id, (SctkCornerRadius(Arc::new(protocol_object)), Some(adjusted_radii.clone())));
} }
} else { } else {
if let Some(old) = self.corner_radii.get_mut(&id) { if let Some(old) = w.corner_radius.as_mut() {
match old.0.0.as_ref() { old.0.0.as_ref().0.unset_radius();
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);
@ -1803,95 +1610,10 @@ 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(&region));
}
(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(&region));
_ = vacant_entry.insert(blur_surface);
}
}
}
pub fn get_subsurface( pub fn get_subsurface(
&mut self, &mut self,
settings: SctkSubsurfaceSettings, settings: SctkSubsurfaceSettings,
@ -1905,17 +1627,9 @@ impl SctkState {
), ),
SubsurfaceCreationError, SubsurfaceCreationError,
> { > {
let Some(subsurface_state) = self.subsurface_state.clone() else { let Some(subsurface_state) = self.subsurface_state.as_ref() 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.;
@ -2021,9 +1735,7 @@ impl SctkState {
region.destroy(); region.destroy();
} }
if let Some(blur) = self.pending_blur.remove(&settings.id) { wl_surface.commit();
_ = 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,
@ -2099,11 +1811,7 @@ 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(),

View file

@ -3,7 +3,6 @@ use cctk::sctk::{
delegate_activation, delegate_activation,
reexports::client::protocol::{wl_seat::WlSeat, wl_surface::WlSurface}, reexports::client::protocol::{wl_seat::WlSeat, wl_surface::WlSurface},
}; };
use iced_futures::futures::channel::oneshot::Sender;
use crate::platform_specific::wayland::event_loop::state::SctkState; use crate::platform_specific::wayland::event_loop::state::SctkState;

View file

@ -1,115 +0,0 @@
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);

View file

@ -1,7 +1,6 @@
// 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;
@ -13,14 +12,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;

View file

@ -4,46 +4,10 @@ use crate::platform_specific::wayland::{
}; };
use cctk::sctk::{ use cctk::sctk::{
delegate_keyboard, delegate_keyboard,
seat::keyboard::{KeyboardHandler, Keysym, Modifiers}, seat::keyboard::{KeyboardHandler, Keysym},
}; };
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,
@ -53,9 +17,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, modifiers) = { let (i, mut is_active, seat) = {
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) {
@ -77,11 +41,9 @@ 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, modifiers) (i, is_active, seat)
}; };
if !is_active && self.seats[0].kbd_focus.is_none() { if !is_active && self.seats[0].kbd_focus.is_none() {
@ -111,12 +73,6 @@ 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(),
});
} }
} }
} }
@ -170,7 +126,6 @@ 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),
@ -182,12 +137,6 @@ 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(),
}) })
} }
} }
@ -299,7 +248,6 @@ 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() {

View file

@ -4,9 +4,10 @@ use crate::platform_specific::wayland::{
}; };
use cctk::sctk::{ use cctk::sctk::{
delegate_seat, delegate_seat,
reexports::client::{Proxy, protocol::wl_keyboard::WlKeyboard}, reexports::client::{protocol::wl_keyboard::WlKeyboard, Proxy},
seat::{SeatHandler, keyboard::Modifiers, pointer::ThemeSpec}, seat::{pointer::ThemeSpec, SeatHandler},
}; };
use iced_runtime::keyboard::Modifiers;
use std::sync::Arc; use std::sync::Arc;
impl SeatHandler for SctkState { impl SeatHandler for SctkState {
@ -17,7 +18,7 @@ impl SeatHandler for SctkState {
fn new_seat( fn new_seat(
&mut self, &mut self,
_conn: &cctk::sctk::reexports::client::Connection, _conn: &cctk::sctk::reexports::client::Connection,
qh: &cctk::sctk::reexports::client::QueueHandle<Self>, _qh: &cctk::sctk::reexports::client::QueueHandle<Self>,
seat: cctk::sctk::reexports::client::protocol::wl_seat::WlSeat, seat: cctk::sctk::reexports::client::protocol::wl_seat::WlSeat,
) { ) {
self.sctk_events.push(SctkEvent::SeatEvent { self.sctk_events.push(SctkEvent::SeatEvent {

View file

@ -9,12 +9,12 @@ use crate::{
use cctk::sctk::{ use cctk::sctk::{
delegate_touch, delegate_touch,
reexports::client::{ reexports::client::{
Connection, Proxy, QueueHandle,
protocol::{wl_surface::WlSurface, wl_touch::WlTouch}, protocol::{wl_surface::WlSurface, wl_touch::WlTouch},
Connection, Proxy, QueueHandle,
}, },
seat::touch::TouchHandler, seat::touch::TouchHandler,
}; };
use iced_runtime::core::{Point, touch}; use iced_runtime::core::{touch, Point};
impl TouchHandler for SctkState { impl TouchHandler for SctkState {
fn down( fn down(

View file

@ -1,9 +1,6 @@
use crate::{ use crate::{
event_loop::state::CommonSurface, event_loop::state::CommonSurface,
platform_specific::wayland::{ platform_specific::wayland::{handlers::SctkState, sctk_event::SctkEvent},
event_loop::state::receive_frame, handlers::SctkState,
sctk_event::SctkEvent,
},
}; };
use cctk::sctk::{ use cctk::sctk::{
delegate_session_lock, delegate_session_lock,
@ -70,10 +67,6 @@ 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);
} }
} }

View file

@ -1,30 +1,14 @@
use cctk::{ use cctk::{sctk, cosmic_protocols::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,
}, }};
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;
#[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(
_state: &mut Self, _state: &mut Self,
@ -33,13 +17,17 @@ impl Dispatch<CosmicCornerRadiusManagerV1, ()> for SctkState {
_data: &(), _data: &(),
_conn: &Connection, _conn: &Connection,
_qhandle: &sctk::reexports::client::QueueHandle<Self>, _qhandle: &sctk::reexports::client::QueueHandle<Self>,
) { ) {}
}
} }
impl Dispatch<CosmicCornerRadiusToplevelV1, ()> for SctkState { impl
Dispatch<
CosmicCornerRadiusToplevelV1,
(),
> for SctkState
{
fn event( fn event(
state: &mut Self, _state: &mut Self,
_proxy: &CosmicCornerRadiusToplevelV1, _proxy: &CosmicCornerRadiusToplevelV1,
event: <CosmicCornerRadiusToplevelV1 as Proxy>::Event, event: <CosmicCornerRadiusToplevelV1 as Proxy>::Event,
_data: &(), _data: &(),
@ -47,22 +35,7 @@ impl Dispatch<CosmicCornerRadiusToplevelV1, ()> for SctkState {
_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!(),
} }
} }
} }

View file

@ -1,13 +1,13 @@
use crate::platform_specific::wayland::{ use crate::platform_specific::wayland::{
event_loop::state::{SctkState, receive_frame}, event_loop::state::{receive_frame, SctkState},
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::{
WaylandSurface,
wlr_layer::{Anchor, KeyboardInteractivity, LayerShellHandler}, wlr_layer::{Anchor, KeyboardInteractivity, LayerShellHandler},
WaylandSurface,
}, },
}; };
use std::fmt::Debug; use std::fmt::Debug;

View file

@ -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;

View file

@ -69,10 +69,6 @@ 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,

View file

@ -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::{
Connection, Proxy, QueueHandle, delegate_dispatch, delegate_dispatch, Connection, Proxy, QueueHandle,
}; };
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;

View file

@ -16,12 +16,10 @@ use cctk::sctk::reexports::client::protocol::wl_surface::WlSurface;
use cctk::sctk::seat::keyboard::Modifiers; use cctk::sctk::seat::keyboard::Modifiers;
use cursor_icon::CursorIcon; use cursor_icon::CursorIcon;
use iced_futures::futures::channel::mpsc; use iced_futures::futures::channel::mpsc;
use iced_graphics::{Compositor, compositor}; use iced_graphics::compositor;
use iced_runtime::core::{Vector, window}; use iced_runtime::core::{Vector, window};
use raw_window_handle::{DisplayHandle, HasDisplayHandle, HasWindowHandle}; use raw_window_handle::{HasDisplayHandle, HasWindowHandle, 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;
@ -107,7 +105,7 @@ impl PlatformSpecific {
display: OwnedDisplayHandle, display: OwnedDisplayHandle,
) -> Self { ) -> Self {
self.wayland.winit_event_sender = Some(tx); self.wayland.winit_event_sender = Some(tx);
self.wayland.conn = match display.raw_display_handle() { self.wayland.conn = match display.display_handle().map(|h| h.as_raw()) {
Ok(raw_window_handle::RawDisplayHandle::Wayland( Ok(raw_window_handle::RawDisplayHandle::Wayland(
wayland_display_handle, wayland_display_handle,
)) => { )) => {
@ -129,6 +127,7 @@ 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(),
@ -189,12 +188,12 @@ impl WaylandSpecific {
winit_event_sender, winit_event_sender,
proxy, proxy,
sender, sender,
display_handle, display_handle: _,
conn, conn: _,
surface_ids, surface_ids,
modifiers, modifiers,
subsurface_state, subsurface_state,
surface_subsurfaces, surface_subsurfaces: _,
} = self; } = self;
match e { match e {
@ -237,7 +236,7 @@ impl WaylandSpecific {
&mut self, &mut self,
keep: F, keep: F,
) { ) {
self.surface_subsurfaces.retain(|k, v| keep(*k)) self.surface_subsurfaces.retain(|k, _v| keep(*k))
} }
pub(crate) fn clear_subsurface_list(&mut self) { pub(crate) fn clear_subsurface_list(&mut self) {
@ -250,7 +249,7 @@ impl WaylandSpecific {
wl_surface: &WlSurface, wl_surface: &WlSurface,
) { ) {
let subsurfaces = crate::subsurface_widget::take_subsurfaces(); let subsurfaces = crate::subsurface_widget::take_subsurfaces();
let mut entry = self.surface_subsurfaces.entry(id); let entry = self.surface_subsurfaces.entry(id);
let surface_subsurfaces = entry.or_default(); let surface_subsurfaces = entry.or_default();
let Some(subsurface_state) = self.subsurface_state.as_mut() else { let Some(subsurface_state) = self.subsurface_state.as_mut() else {
return; return;

View file

@ -4,7 +4,7 @@ use crate::{
SurfaceIdWrapper, UserInterfaces, SurfaceIdWrapper, UserInterfaces,
wayland::{ wayland::{
conversion::{ conversion::{
modifiers_to_native, pointer_axis_to_native, modifiers_to_native,
pointer_button_to_native, pointer_button_to_native,
}, },
keymap::{self, keysym_to_key}, keymap::{self, keysym_to_key},
@ -25,10 +25,9 @@ use iced_futures::{
}, },
}, },
}, },
event,
futures::{SinkExt, channel::mpsc}, futures::{SinkExt, channel::mpsc},
}; };
use iced_graphics::{Compositor, compositor}; use iced_graphics::compositor;
use iced_runtime::{ use iced_runtime::{
core::{ core::{
Point, Point,
@ -40,9 +39,7 @@ use iced_runtime::{
user_interface, user_interface,
}; };
use cctk::{ use cctk::sctk::{
cosmic_protocols::overlap_notify::v1::client::zcosmic_overlap_notification_v1,
sctk::{
output::OutputInfo, output::OutputInfo,
reexports::{ reexports::{
calloop::channel, calloop::channel,
@ -67,8 +64,7 @@ use cctk::{
wlr_layer::{Layer, LayerSurfaceConfigure}, wlr_layer::{Layer, LayerSurfaceConfigure},
xdg::{popup::PopupConfigure, window::WindowConfigure}, xdg::{popup::PopupConfigure, window::WindowConfigure},
}, },
}, };
};
use std::{ use std::{
collections::HashMap, collections::HashMap,
num::NonZeroU32, num::NonZeroU32,
@ -96,7 +92,9 @@ 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,
@ -118,6 +116,11 @@ 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,
@ -158,6 +161,10 @@ pub enum SctkEvent {
}, },
SubsurfaceEvent(SubsurfaceEventVariant), SubsurfaceEvent(SubsurfaceEventVariant),
//
// output events
//
NewOutput { NewOutput {
id: WlOutput, id: WlOutput,
info: Option<OutputInfo>, info: Option<OutputInfo>,
@ -167,7 +174,9 @@ pub enum SctkEvent {
info: OutputInfo, info: OutputInfo,
}, },
RemovedOutput(WlOutput), RemovedOutput(WlOutput),
//
// compositor events
//
ScaleFactorChanged { ScaleFactorChanged {
factor: f64, factor: f64,
id: WlOutput, id: WlOutput,
@ -273,7 +282,6 @@ pub enum SubsurfaceEventVariant {
Created { Created {
parent_id: window::Id, parent_id: window::Id,
parent: WlSurface, parent: WlSurface,
surface: WlSurface,
qh: QueueHandle<SctkState>, qh: QueueHandle<SctkState>,
common_surface: CommonSurface, common_surface: CommonSurface,
surface_id: SurfaceId, surface_id: SurfaceId,
@ -365,12 +373,6 @@ 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(
@ -501,7 +503,7 @@ impl SctkEvent {
), ),
), ),
), ),
SurfaceIdWrapper::Window(id) => { SurfaceIdWrapper::Window(_id) => {
Some(iced_runtime::core::Event::Window( Some(iced_runtime::core::Event::Window(
window::Event::Unfocused, window::Event::Unfocused,
)) ))
@ -529,7 +531,7 @@ impl SctkEvent {
), ),
), ),
), ),
SurfaceIdWrapper::Subsurface(id) => None, SurfaceIdWrapper::Subsurface(_id) => None,
}) })
{ {
events.push(( events.push((
@ -563,7 +565,7 @@ impl SctkEvent {
), ),
), ),
), ),
SurfaceIdWrapper::Window(id) => { SurfaceIdWrapper::Window(_id) => {
Some(iced_runtime::core::Event::Window( Some(iced_runtime::core::Event::Window(
window::Event::Focused, window::Event::Focused,
)) ))
@ -1500,7 +1502,6 @@ impl SctkEvent {
common, common,
z, z,
parent, parent,
surface: _,
qh, qh,
surface_id, surface_id,
display, display,
@ -1723,12 +1724,6 @@ impl SctkEvent {
), ),
), ),
)), )),
SctkEvent::Blur => events.push((
None,
iced_runtime::core::Event::PlatformSpecific(
PlatformSpecific::Wayland(wayland::Event::BlurEnabled),
),
)),
} }
} }
} }

View file

@ -57,9 +57,7 @@ use wayland_protocols::wp::{
}; };
use winit::window::WindowId; use winit::window::WindowId;
use crate::platform_specific::{ use crate::platform_specific::event_loop::state::SctkState;
SurfaceIdWrapper, event_loop::state::SctkState,
};
#[derive(Debug)] #[derive(Debug)]
pub struct Plane { pub struct Plane {
@ -625,7 +623,7 @@ impl Drop for SubsurfaceState {
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub(crate) struct SubsurfaceInstance { pub struct SubsurfaceInstance {
pub(crate) wl_surface: WlSurface, pub(crate) wl_surface: WlSurface,
pub(crate) wl_subsurface: WlSubsurface, pub(crate) wl_subsurface: WlSubsurface,
pub(crate) wp_viewport: WpViewport, pub(crate) wp_viewport: WpViewport,

View file

@ -1,9 +1,9 @@
use crate::platform_specific::wayland::Action; use crate::platform_specific::wayland::Action;
use cctk::sctk::reexports::{ use cctk::sctk::reexports::{
calloop::{LoopHandle, channel}, calloop::channel,
client::{ client::{
Proxy, QueueHandle, Proxy, QueueHandle,
protocol::{wl_display::WlDisplay, wl_surface::WlSurface}, protocol::wl_display::WlDisplay,
}, },
}; };
use raw_window_handle::HandleError; use raw_window_handle::HandleError;
@ -91,7 +91,7 @@ impl winit::window::Window for SctkWinitWindow {
} }
} }
fn set_cursor_visible(&self, visible: bool) { fn set_cursor_visible(&self, _visible: bool) {
// TODO // TODO
} }
@ -203,35 +203,35 @@ impl winit::window::Window for SctkWinitWindow {
// TODO refer to winit for implementation // TODO refer to winit for implementation
} }
fn set_outer_position(&self, position: winit::dpi::Position) {} fn set_outer_position(&self, _position: winit::dpi::Position) {}
fn outer_size(&self) -> winit::dpi::PhysicalSize<u32> { fn outer_size(&self) -> winit::dpi::PhysicalSize<u32> {
// XXX not applicable to wrapped surfaces // XXX not applicable to wrapped surfaces
Default::default() Default::default()
} }
fn set_min_surface_size(&self, min_size: Option<winit::dpi::Size>) { fn set_min_surface_size(&self, _min_size: Option<winit::dpi::Size>) {
// XXX not applicable to wrapped surfaces // XXX not applicable to wrapped surfaces
} }
fn set_max_surface_size(&self, max_size: Option<winit::dpi::Size>) { fn set_max_surface_size(&self, _max_size: Option<winit::dpi::Size>) {
// XXX not applicable to wrapped surfaces // XXX not applicable to wrapped surfaces
} }
fn set_surface_resize_increments( fn set_surface_resize_increments(
&self, &self,
increments: Option<winit::dpi::Size>, _increments: Option<winit::dpi::Size>,
) { ) {
log::warn!( log::warn!(
"`set_surface_resize_increments` is not implemented for Wayland" "`set_surface_resize_increments` is not implemented for Wayland"
) )
} }
fn set_title(&self, title: &str) { fn set_title(&self, _title: &str) {
// XXX not applicable to wrapped surfaces // XXX not applicable to wrapped surfaces
} }
fn set_transparent(&self, transparent: bool) { fn set_transparent(&self, _transparent: bool) {
todo!() todo!()
} }
@ -286,37 +286,23 @@ impl winit::window::Window for SctkWinitWindow {
_ = self.tx.send(Action::SetImePurpose(purpose)); _ = self.tx.send(Action::SetImePurpose(purpose));
} }
fn set_blur(&self, blur: bool) { fn set_blur(&self, _blur: bool) {
_ = self.tx.send(Action::Action( // TODO
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) {}
fn is_visible(&self) -> Option<bool> { fn is_visible(&self) -> Option<bool> {
None None
} }
fn set_resizable(&self, resizable: bool) {} fn set_resizable(&self, _resizable: bool) {}
fn is_resizable(&self) -> bool { fn is_resizable(&self) -> bool {
false false
} }
fn set_enabled_buttons(&self, buttons: winit::window::WindowButtons) { fn set_enabled_buttons(&self, _buttons: winit::window::WindowButtons) {
// TODO v5 of xdg_shell. // TODO v5 of xdg_shell.
} }
@ -324,7 +310,7 @@ impl winit::window::Window for SctkWinitWindow {
WindowButtons::all() WindowButtons::all()
} }
fn set_minimized(&self, minimized: bool) { fn set_minimized(&self, _minimized: bool) {
// XXX not applicable to the wrapped surfaces // XXX not applicable to the wrapped surfaces
} }
@ -333,7 +319,7 @@ impl winit::window::Window for SctkWinitWindow {
None None
} }
fn set_maximized(&self, maximized: bool) { fn set_maximized(&self, _maximized: bool) {
// XXX can't minimize the wrapped surfaces // XXX can't minimize the wrapped surfaces
} }
@ -344,7 +330,7 @@ impl winit::window::Window for SctkWinitWindow {
fn set_fullscreen( fn set_fullscreen(
&self, &self,
fullscreen: Option<winit_core::monitor::Fullscreen>, _fullscreen: Option<winit_core::monitor::Fullscreen>,
) { ) {
// XXX can't fullscreen the wrapped surfaces // XXX can't fullscreen the wrapped surfaces
} }
@ -354,7 +340,7 @@ impl winit::window::Window for SctkWinitWindow {
None None
} }
fn set_decorations(&self, decorations: bool) { fn set_decorations(&self, _decorations: bool) {
// XXX no decorations supported for the wrapped surfaces // XXX no decorations supported for the wrapped surfaces
} }
@ -362,26 +348,26 @@ impl winit::window::Window for SctkWinitWindow {
false false
} }
fn set_window_level(&self, level: winit::window::WindowLevel) {} fn set_window_level(&self, _level: winit::window::WindowLevel) {}
fn set_window_icon(&self, window_icon: Option<winit_core::icon::Icon>) {} fn set_window_icon(&self, _window_icon: Option<winit_core::icon::Icon>) {}
fn focus_window(&self) {} fn focus_window(&self) {}
fn request_user_attention( fn request_user_attention(
&self, &self,
request_type: Option<winit::window::UserAttentionType>, _request_type: Option<winit::window::UserAttentionType>,
) { ) {
// XXX can't request attention on wrapped surfaces // XXX can't request attention on wrapped surfaces
} }
fn set_theme(&self, theme: Option<winit::window::Theme>) {} fn set_theme(&self, _theme: Option<winit::window::Theme>) {}
fn theme(&self) -> Option<winit::window::Theme> { fn theme(&self) -> Option<winit::window::Theme> {
None None
} }
fn set_content_protected(&self, protected: bool) {} fn set_content_protected(&self, _protected: bool) {}
fn title(&self) -> String { fn title(&self) -> String {
String::new() String::new()
@ -434,14 +420,14 @@ impl winit::window::Window for SctkWinitWindow {
fn set_cursor_position( fn set_cursor_position(
&self, &self,
position: winit::dpi::Position, _position: winit::dpi::Position,
) -> Result<(), winit::error::RequestError> { ) -> Result<(), winit::error::RequestError> {
todo!() todo!()
} }
fn set_cursor_grab( fn set_cursor_grab(
&self, &self,
mode: winit::window::CursorGrabMode, _mode: winit::window::CursorGrabMode,
) -> Result<(), winit::error::RequestError> { ) -> Result<(), winit::error::RequestError> {
todo!() todo!()
} }
@ -452,7 +438,7 @@ impl winit::window::Window for SctkWinitWindow {
fn request_ime_update( fn request_ime_update(
&self, &self,
request: winit::window::ImeRequest, _request: winit::window::ImeRequest,
) -> Result<(), winit::window::ImeRequestError> { ) -> Result<(), winit::window::ImeRequestError> {
todo!() todo!()
} }

View file

@ -8,7 +8,6 @@ use crate::futures::futures::{
use crate::graphics::shell; use crate::graphics::shell;
use crate::runtime::Action; use crate::runtime::Action;
use crate::runtime::window; use crate::runtime::window;
use std::hash::DefaultHasher;
use std::pin::Pin; use std::pin::Pin;
/// An event loop proxy with backpressure that implements `Sink`. /// An event loop proxy with backpressure that implements `Sink`.
@ -34,7 +33,7 @@ impl<T: 'static> Proxy<T> {
const MAX_SIZE: usize = 100; const MAX_SIZE: usize = 100;
/// Creates a new [`Proxy`] from an `EventLoopProxy`. /// Creates a new [`Proxy`] from an `EventLoopProxy`.
pub fn new( pub(crate) fn new(
raw: winit::event_loop::EventLoopProxy, raw: winit::event_loop::EventLoopProxy,
event_sender: mpsc::UnboundedSender<Event<T>>, event_sender: mpsc::UnboundedSender<Event<T>>,
) -> (Self, impl Future<Output = ()>) { ) -> (Self, impl Future<Output = ()>) {

View file

@ -14,7 +14,7 @@ use crate::core::text;
use crate::core::theme::{self, Base}; use crate::core::theme::{self, Base};
use crate::core::time::Instant; use crate::core::time::Instant;
use crate::core::{ use crate::core::{
Color, Element, InputMethod, Padding, Point, Rectangle, Size, Text, Vector, Color, InputMethod, Padding, Point, Rectangle, Size, Text, Vector,
}; };
use crate::graphics::Compositor; use crate::graphics::Compositor;
use crate::program::{self, Program}; use crate::program::{self, Program};
@ -26,10 +26,6 @@ use winit::monitor::MonitorHandle;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::sync::Arc; use std::sync::Arc;
pub(crate) type ViewFn<M, T, R> = Arc<
Box<dyn Fn() -> Option<Element<'static, M, T, R>> + Send + Sync + 'static>,
>;
pub struct WindowManager<P, C> pub struct WindowManager<P, C>
where where
P: Program, P: Program,
@ -325,6 +321,10 @@ where
} }
} }
// TODO(yoda): migrate to Window::request_ime_update(ImeRequest::*).
// Legacy set_ime_* still functional but deprecated; full migration
// requires ImeCapabilities + ImeRequestData reshape.
#[allow(deprecated)]
fn enable_ime( fn enable_ime(
&mut self, &mut self,
cursor: Rectangle, cursor: Rectangle,
@ -335,9 +335,19 @@ 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));
@ -345,6 +355,7 @@ where
} }
} }
#[allow(deprecated)] // see TODO on enable_ime
fn disable_ime(&mut self) { fn disable_ime(&mut self) {
if self.ime_state.is_some() { if self.ime_state.is_some() {
self.raw.set_ime_allowed(false); self.raw.set_ime_allowed(false);