merge: upstream/master (pre-epoch-1.8.0)
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
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
This commit is contained in:
commit
d12bb8e056
16 changed files with 839 additions and 351 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -3206,7 +3206,6 @@ dependencies = [
|
||||||
"resvg",
|
"resvg",
|
||||||
"rustc-hash 2.1.2",
|
"rustc-hash 2.1.2",
|
||||||
"rustix 0.38.44",
|
"rustix 0.38.44",
|
||||||
"smithay-client-toolkit 0.20.0",
|
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
"tiny-xlib",
|
"tiny-xlib",
|
||||||
"wayland-backend",
|
"wayland-backend",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
//! Load and operate on images.
|
//! Load and operate on images.
|
||||||
#[cfg(feature = "image")]
|
#[cfg(feature = "image")]
|
||||||
use crate::core::Bytes;
|
use crate::core::Bytes;
|
||||||
|
#[cfg(feature = "image")]
|
||||||
|
use crate::core::Size;
|
||||||
|
|
||||||
use crate::core::Rectangle;
|
use crate::core::Rectangle;
|
||||||
use crate::core::image;
|
use crate::core::image;
|
||||||
|
|
@ -178,3 +180,105 @@ fn to_error(error: ::image::ImageError) -> image::Error {
|
||||||
error => image::Error::Invalid(Arc::new(error)),
|
error => image::Error::Invalid(Arc::new(error)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// resample a raster image to this target
|
||||||
|
#[cfg(feature = "image")]
|
||||||
|
pub fn downsample_target(
|
||||||
|
native: Size<u32>,
|
||||||
|
bounds: Size<f32>,
|
||||||
|
) -> Option<Size<u32>> {
|
||||||
|
if !(bounds.width >= 1.0 && bounds.height >= 1.0) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Round up to 4px so an animated resize does not make a copy per pixel
|
||||||
|
// TODO: maybe find a better approach
|
||||||
|
let quantize = |length: f32| (length.ceil() as u32).div_ceil(4) * 4;
|
||||||
|
|
||||||
|
let target = Size::new(
|
||||||
|
quantize(bounds.width).min(native.width),
|
||||||
|
quantize(bounds.height).min(native.height),
|
||||||
|
);
|
||||||
|
|
||||||
|
let minified = native.width as f32 >= target.width as f32 * 1.25
|
||||||
|
|| native.height as f32 >= target.height as f32 * 1.25;
|
||||||
|
|
||||||
|
// If the target is big then Lanczos will be too expensive, and sampler is good enough
|
||||||
|
let small = u64::from(target.width) * u64::from(target.height) <= 1 << 18;
|
||||||
|
|
||||||
|
(minified && small).then_some(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resamples premultiplied pixels down to `target`.
|
||||||
|
#[cfg(feature = "image")]
|
||||||
|
pub fn downsample_premultiplied(
|
||||||
|
pixels: &[u8],
|
||||||
|
size: Size<u32>,
|
||||||
|
target: Size<u32>,
|
||||||
|
) -> Vec<u8> {
|
||||||
|
let image =
|
||||||
|
::image::RgbaImage::from_raw(size.width, size.height, pixels.to_vec())
|
||||||
|
.expect("pixels hold width * height RGBA pixels");
|
||||||
|
|
||||||
|
resize(&image, target).into_raw()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "image")]
|
||||||
|
fn resize(image: &::image::RgbaImage, target: Size<u32>) -> ::image::RgbaImage {
|
||||||
|
use ::image::imageops::{self, FilterType};
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
// if image is too big compared to target, box average it to double the target
|
||||||
|
// and then do Lanczos. The final result is almost the same, but lanczos is too
|
||||||
|
// expensive on large images.
|
||||||
|
let image = if image.width() >= target.width * 4
|
||||||
|
&& image.height() >= target.height * 4
|
||||||
|
{
|
||||||
|
Cow::Owned(imageops::thumbnail(
|
||||||
|
image,
|
||||||
|
target.width * 2,
|
||||||
|
target.height * 2,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Cow::Borrowed(image)
|
||||||
|
};
|
||||||
|
|
||||||
|
imageops::resize(&*image, target.width, target.height, FilterType::Lanczos3)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resamples RGBA pixels down to `target`.
|
||||||
|
///
|
||||||
|
/// Premultiplies the image, to avoid fringing the edges of an icon when interpolating
|
||||||
|
/// transparent pixels.
|
||||||
|
#[cfg(feature = "image")]
|
||||||
|
pub fn downsample(image: &Buffer, target: Size<u32>) -> ::image::RgbaImage {
|
||||||
|
let mut image = ::image::RgbaImage::from_raw(
|
||||||
|
image.width(),
|
||||||
|
image.height(),
|
||||||
|
image.as_raw().to_vec(),
|
||||||
|
)
|
||||||
|
.expect("buffer holds width * height RGBA pixels");
|
||||||
|
|
||||||
|
for pixel in image.pixels_mut() {
|
||||||
|
let alpha = u32::from(pixel[3]);
|
||||||
|
|
||||||
|
for channel in &mut pixel.0[..3] {
|
||||||
|
*channel = ((u32::from(*channel) * alpha + 127) / 255) as u8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut image = resize(&image, target);
|
||||||
|
|
||||||
|
for pixel in image.pixels_mut() {
|
||||||
|
let alpha = u32::from(pixel[3]);
|
||||||
|
|
||||||
|
if alpha > 0 {
|
||||||
|
for channel in &mut pixel.0[..3] {
|
||||||
|
*channel = ((u32::from(*channel) * 255 + alpha / 2) / alpha)
|
||||||
|
.min(255) as u8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
image
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,21 @@ impl Pipeline {
|
||||||
) {
|
) {
|
||||||
let mut cache = self.cache.borrow_mut();
|
let mut cache = self.cache.borrow_mut();
|
||||||
|
|
||||||
let Ok(mut image) = cache.allocate(handle) else {
|
let target = {
|
||||||
|
let Ok(image) = cache.allocate(handle) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
graphics::image::downsample_target(
|
||||||
|
Size::new(image.width(), image.height()),
|
||||||
|
bounds.size(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let Ok(mut image) = (match target {
|
||||||
|
Some(target) => cache.allocate_resampled(handle, target),
|
||||||
|
None => cache.allocate(handle),
|
||||||
|
}) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -110,6 +124,8 @@ impl Pipeline {
|
||||||
struct Cache {
|
struct Cache {
|
||||||
entries: FxHashMap<raster::Id, Option<Entry>>,
|
entries: FxHashMap<raster::Id, Option<Entry>>,
|
||||||
hits: FxHashSet<raster::Id>,
|
hits: FxHashSet<raster::Id>,
|
||||||
|
resampled: FxHashMap<(raster::Id, u32, u32), Entry>,
|
||||||
|
resampled_hits: FxHashSet<(raster::Id, u32, u32)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cache {
|
impl Cache {
|
||||||
|
|
@ -166,9 +182,59 @@ impl Cache {
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Like [`Self::allocate`], resampled to `target`. Call after
|
||||||
|
/// [`Self::allocate`] has decoded the image.
|
||||||
|
pub fn allocate_resampled(
|
||||||
|
&mut self,
|
||||||
|
handle: &raster::Handle,
|
||||||
|
target: Size<u32>,
|
||||||
|
) -> Result<tiny_skia::PixmapRef<'_>, raster::Error> {
|
||||||
|
let key = (handle.id(), target.width, target.height);
|
||||||
|
|
||||||
|
if !self.resampled.contains_key(&key) {
|
||||||
|
let native = self
|
||||||
|
.entries
|
||||||
|
.get(&handle.id())
|
||||||
|
.and_then(Option::as_ref)
|
||||||
|
.ok_or(raster::Error::Empty)?;
|
||||||
|
|
||||||
|
// Stored pixels are already premultiplied, so resample them as is.
|
||||||
|
let pixels = graphics::image::downsample_premultiplied(
|
||||||
|
bytemuck::cast_slice(&native.pixels),
|
||||||
|
Size::new(native.width, native.height),
|
||||||
|
target,
|
||||||
|
);
|
||||||
|
|
||||||
|
let _ = self.resampled.insert(
|
||||||
|
key,
|
||||||
|
Entry {
|
||||||
|
width: target.width,
|
||||||
|
height: target.height,
|
||||||
|
pixels: pixels
|
||||||
|
.chunks_exact(4)
|
||||||
|
.map(|p| u32::from_ne_bytes([p[0], p[1], p[2], p[3]]))
|
||||||
|
.collect(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = self.resampled_hits.insert(key);
|
||||||
|
let entry = &self.resampled[&key];
|
||||||
|
|
||||||
|
Ok(tiny_skia::PixmapRef::from_bytes(
|
||||||
|
bytemuck::cast_slice(&entry.pixels),
|
||||||
|
entry.width,
|
||||||
|
entry.height,
|
||||||
|
)
|
||||||
|
.expect("Build pixmap from image bytes"))
|
||||||
|
}
|
||||||
|
|
||||||
fn trim(&mut self) {
|
fn trim(&mut self) {
|
||||||
self.entries.retain(|key, _| self.hits.contains(key));
|
self.entries.retain(|key, _| self.hits.contains(key));
|
||||||
|
self.resampled
|
||||||
|
.retain(|key, _| self.resampled_hits.contains(key));
|
||||||
self.hits.clear();
|
self.hits.clear();
|
||||||
|
self.resampled_hits.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ use crate::image::atlas::{self, Atlas};
|
||||||
#[cfg(all(feature = "image", not(target_arch = "wasm32")))]
|
#[cfg(all(feature = "image", not(target_arch = "wasm32")))]
|
||||||
use worker::Worker;
|
use worker::Worker;
|
||||||
|
|
||||||
|
#[cfg(feature = "image")]
|
||||||
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
#[cfg(feature = "image")]
|
#[cfg(feature = "image")]
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
|
@ -38,6 +40,9 @@ impl Cache {
|
||||||
raster: Raster {
|
raster: Raster {
|
||||||
cache: crate::image::raster::Cache::default(),
|
cache: crate::image::raster::Cache::default(),
|
||||||
pending: HashMap::new(),
|
pending: HashMap::new(),
|
||||||
|
resampled: FxHashMap::default(),
|
||||||
|
resampled_hits: FxHashSet::default(),
|
||||||
|
should_trim: false,
|
||||||
belt: wgpu::util::StagingBelt::new(
|
belt: wgpu::util::StagingBelt::new(
|
||||||
device.clone(),
|
device.clone(),
|
||||||
2 * 1024 * 1024,
|
2 * 1024 * 1024,
|
||||||
|
|
@ -209,7 +214,9 @@ impl Cache {
|
||||||
encoder: &mut wgpu::CommandEncoder,
|
encoder: &mut wgpu::CommandEncoder,
|
||||||
belt: &mut wgpu::util::StagingBelt,
|
belt: &mut wgpu::util::StagingBelt,
|
||||||
handle: &core::image::Handle,
|
handle: &core::image::Handle,
|
||||||
|
bounds: Size<f32>,
|
||||||
) -> Option<(&atlas::Entry, &Arc<wgpu::BindGroup>)> {
|
) -> Option<(&atlas::Entry, &Arc<wgpu::BindGroup>)> {
|
||||||
|
use crate::graphics::image::{downsample, downsample_target, load};
|
||||||
use crate::image::raster::Memory;
|
use crate::image::raster::Memory;
|
||||||
|
|
||||||
self.receive();
|
self.receive();
|
||||||
|
|
@ -223,6 +230,34 @@ impl Cache {
|
||||||
None,
|
None,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
if let Some(target) = downsample_target(memory.dimensions(), bounds) {
|
||||||
|
let key = (handle.id(), target.width, target.height);
|
||||||
|
|
||||||
|
if !self.raster.resampled.contains_key(&key) {
|
||||||
|
let image = memory.host().or_else(|| load(handle).ok())?;
|
||||||
|
let image = downsample(&image, target);
|
||||||
|
|
||||||
|
let entry = self.atlas.upload(
|
||||||
|
device,
|
||||||
|
encoder,
|
||||||
|
belt,
|
||||||
|
target.width,
|
||||||
|
target.height,
|
||||||
|
&image,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let _ = self.raster.resampled.insert(key, entry);
|
||||||
|
self.raster.should_trim = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = self.raster.resampled_hits.insert(key);
|
||||||
|
|
||||||
|
return Some((
|
||||||
|
self.raster.resampled.get(&key)?,
|
||||||
|
self.atlas.bind_group(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
if let Memory::Device {
|
if let Memory::Device {
|
||||||
entry, bind_group, ..
|
entry, bind_group, ..
|
||||||
} = memory
|
} = memory
|
||||||
|
|
@ -303,6 +338,23 @@ impl Cache {
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
self.worker.drop(_bind_group);
|
self.worker.drop(_bind_group);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if self.raster.should_trim {
|
||||||
|
let hits = &self.raster.resampled_hits;
|
||||||
|
let atlas = &mut self.atlas;
|
||||||
|
|
||||||
|
self.raster.resampled.retain(|key, entry| {
|
||||||
|
let retain = hits.contains(key);
|
||||||
|
|
||||||
|
if !retain {
|
||||||
|
atlas.remove(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
retain
|
||||||
|
});
|
||||||
|
self.raster.resampled_hits.clear();
|
||||||
|
self.raster.should_trim = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "svg")]
|
#[cfg(feature = "svg")]
|
||||||
|
|
@ -376,6 +428,9 @@ impl Drop for Cache {
|
||||||
struct Raster {
|
struct Raster {
|
||||||
cache: crate::image::raster::Cache,
|
cache: crate::image::raster::Cache,
|
||||||
pending: HashMap<core::image::Id, Vec<Callback>>,
|
pending: HashMap<core::image::Id, Vec<Callback>>,
|
||||||
|
resampled: FxHashMap<(core::image::Id, u32, u32), atlas::Entry>,
|
||||||
|
resampled_hits: FxHashSet<(core::image::Id, u32, u32)>,
|
||||||
|
should_trim: bool,
|
||||||
belt: wgpu::util::StagingBelt,
|
belt: wgpu::util::StagingBelt,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -267,7 +267,13 @@ impl State {
|
||||||
clip_bounds,
|
clip_bounds,
|
||||||
} => {
|
} => {
|
||||||
if let Some((atlas_entry, bind_group)) = cache
|
if let Some((atlas_entry, bind_group)) = cache
|
||||||
.upload_raster(device, encoder, belt, &image.handle)
|
.upload_raster(
|
||||||
|
device,
|
||||||
|
encoder,
|
||||||
|
belt,
|
||||||
|
&image.handle,
|
||||||
|
bounds.size() * scale,
|
||||||
|
)
|
||||||
{
|
{
|
||||||
match atlas.as_mut() {
|
match atlas.as_mut() {
|
||||||
None => {
|
None => {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
//! Interact with the window of your application.
|
//! Interact with the window of your application.
|
||||||
|
|
||||||
|
use crate::core::Rectangle;
|
||||||
use crate::core::window::Id as SurfaceId;
|
use crate::core::window::Id as SurfaceId;
|
||||||
use iced_runtime::{
|
use iced_runtime::{
|
||||||
self, Action, Task,
|
self, Action, Task,
|
||||||
|
|
@ -125,3 +126,15 @@ pub fn set_padding<Message>(
|
||||||
)),
|
)),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <https://wayland.app/protocols/wayland#wl_surface:request:set_input_region>
|
||||||
|
pub fn set_input_zone<Message>(
|
||||||
|
id: SurfaceId,
|
||||||
|
zone: Option<Vec<Rectangle>>,
|
||||||
|
) -> Task<Message> {
|
||||||
|
task::effect(Action::PlatformSpecific(
|
||||||
|
platform_specific::Action::Wayland(wayland::Action::LayerSurface(
|
||||||
|
wayland::layer_surface::Action::InputZone { id, zone },
|
||||||
|
)),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
pub mod control_flow;
|
pub mod control_flow;
|
||||||
|
pub mod popup;
|
||||||
pub mod proxy;
|
pub mod proxy;
|
||||||
pub mod state;
|
pub mod state;
|
||||||
|
|
||||||
|
|
@ -6,6 +7,7 @@ pub mod state;
|
||||||
use crate::platform_specific::SurfaceIdWrapper;
|
use crate::platform_specific::SurfaceIdWrapper;
|
||||||
use crate::{
|
use crate::{
|
||||||
Control,
|
Control,
|
||||||
|
event_loop::popup::PopupManager,
|
||||||
futures::futures::channel::mpsc,
|
futures::futures::channel::mpsc,
|
||||||
handlers::{
|
handlers::{
|
||||||
ext_background_effect, overlap::OverlapNotifyV1,
|
ext_background_effect, overlap::OverlapNotifyV1,
|
||||||
|
|
@ -177,13 +179,41 @@ impl SctkEventLoop {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
crate::platform_specific::Action::SetCursor(
|
crate::platform_specific::Action::SetCursor(
|
||||||
|
surface,
|
||||||
icon,
|
icon,
|
||||||
) => {
|
) => {
|
||||||
if let Some(seat) = state.seats.get_mut(0) {
|
state.record_cursor(surface.clone(), |r| {
|
||||||
|
r.icon = Some(icon);
|
||||||
|
r.hidden = false;
|
||||||
|
});
|
||||||
|
if let Some(seat) = state
|
||||||
|
.seats
|
||||||
|
.get_mut(0)
|
||||||
|
.filter(|s| s.is_over(&surface))
|
||||||
|
{
|
||||||
seat.icon = Some(icon);
|
seat.icon = Some(icon);
|
||||||
|
seat.hidden = false;
|
||||||
seat.set_cursor(&state.connection, icon);
|
seat.set_cursor(&state.connection, icon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
crate::platform_specific::Action::SetCursorVisible(
|
||||||
|
surface,
|
||||||
|
visible,
|
||||||
|
) => {
|
||||||
|
state.record_cursor(surface.clone(), |r| {
|
||||||
|
r.hidden = !visible;
|
||||||
|
});
|
||||||
|
if let Some(seat) = state
|
||||||
|
.seats
|
||||||
|
.get_mut(0)
|
||||||
|
.filter(|s| s.is_over(&surface))
|
||||||
|
{
|
||||||
|
seat.set_cursor_visible(
|
||||||
|
&state.connection,
|
||||||
|
visible,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
crate::platform_specific::Action::RequestRedraw(
|
crate::platform_specific::Action::RequestRedraw(
|
||||||
id,
|
id,
|
||||||
) => {
|
) => {
|
||||||
|
|
@ -377,12 +407,13 @@ impl SctkEventLoop {
|
||||||
blur_surfaces: HashMap::new(),
|
blur_surfaces: HashMap::new(),
|
||||||
corner_radii: HashMap::new(),
|
corner_radii: HashMap::new(),
|
||||||
layer_surfaces: Vec::new(),
|
layer_surfaces: Vec::new(),
|
||||||
popups: Vec::new(),
|
popmgr: PopupManager::default(),
|
||||||
lock_surfaces: Vec::new(),
|
lock_surfaces: Vec::new(),
|
||||||
subsurfaces: Vec::new(),
|
subsurfaces: Vec::new(),
|
||||||
touch_points: HashMap::new(),
|
touch_points: HashMap::new(),
|
||||||
sctk_events: Vec::new(),
|
sctk_events: Vec::new(),
|
||||||
frame_status: HashMap::new(),
|
frame_status: HashMap::new(),
|
||||||
|
cursor_requests: HashMap::new(),
|
||||||
fractional_scaling_manager,
|
fractional_scaling_manager,
|
||||||
viewporter_state,
|
viewporter_state,
|
||||||
compositor_updates: Default::default(),
|
compositor_updates: Default::default(),
|
||||||
|
|
@ -514,8 +545,8 @@ impl SctkEventLoop {
|
||||||
.chain(
|
.chain(
|
||||||
state
|
state
|
||||||
.state
|
.state
|
||||||
.popups
|
.popmgr
|
||||||
.iter()
|
.popups()
|
||||||
.map(|s| s.popup.wl_surface()),
|
.map(|s| s.popup.wl_surface()),
|
||||||
)
|
)
|
||||||
.chain(
|
.chain(
|
||||||
|
|
|
||||||
189
winit/src/platform_specific/wayland/event_loop/popup.rs
Normal file
189
winit/src/platform_specific/wayland/event_loop/popup.rs
Normal file
|
|
@ -0,0 +1,189 @@
|
||||||
|
use std::sync::Mutex;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use cctk::sctk::shell::xdg::popup::PopupConfigure;
|
||||||
|
use cctk::sctk::shell::xdg::popup::Popup;
|
||||||
|
use iced_runtime::{platform_specific, core::window::Id};
|
||||||
|
use wayland_client::protocol::wl_surface::WlSurface;
|
||||||
|
use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1;
|
||||||
|
|
||||||
|
use crate::event_loop::state::Common;
|
||||||
|
use crate::event_loop::state::SctkPopupData;
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct PopupManager {
|
||||||
|
chains: Vec<Vec<SctkPopup>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PopupManager {
|
||||||
|
pub(crate) fn popups(&self) -> impl Iterator<Item = &SctkPopup> {
|
||||||
|
self.chains.iter().map(|c| c.iter()).flatten()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn chain_for_popup_mut(
|
||||||
|
&mut self,
|
||||||
|
id: &WlSurface,
|
||||||
|
) -> Option<(&mut Vec<SctkPopup>, usize)> {
|
||||||
|
let pos = self.chains.iter().enumerate().find_map(move |(pos, c)| {
|
||||||
|
if let Some(pop_pos) =
|
||||||
|
c.into_iter().position(|p| p.popup.wl_surface() == id)
|
||||||
|
{
|
||||||
|
Some((pos, pop_pos))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
pos.map(|pos| (&mut self.chains[pos.0], pos.1))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn chain_for_popup(
|
||||||
|
&self,
|
||||||
|
id: &WlSurface,
|
||||||
|
) -> Option<(&Vec<SctkPopup>, usize)> {
|
||||||
|
let pos = self.chains.iter().enumerate().find_map(move |(pos, c)| {
|
||||||
|
if let Some(pop_pos) =
|
||||||
|
c.into_iter().position(|p| p.popup.wl_surface() == id)
|
||||||
|
{
|
||||||
|
Some((pos, pop_pos))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
pos.map(|pos| (&self.chains[pos.0], pos.1))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn popup(&self, id: &WlSurface) -> Option<&SctkPopup> {
|
||||||
|
self.chains.iter().find_map(move |c| {
|
||||||
|
c.into_iter().find(|p| p.popup.wl_surface() == id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn popup_mut(
|
||||||
|
&mut self,
|
||||||
|
id: &WlSurface,
|
||||||
|
) -> Option<&mut SctkPopup> {
|
||||||
|
self.chains.iter_mut().find_map(move |c| {
|
||||||
|
c.into_iter().find(|p| p.popup.wl_surface() == id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn popup_id(&self, id: Id) -> Option<&SctkPopup> {
|
||||||
|
self.chains
|
||||||
|
.iter()
|
||||||
|
.find_map(move |c| c.into_iter().find(|p| p.data.id == id))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn popup_id_mut(&mut self, id: Id) -> Option<&mut SctkPopup> {
|
||||||
|
self.chains
|
||||||
|
.iter_mut()
|
||||||
|
.find_map(move |c| c.into_iter().find(|p| p.data.id == id))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn active_grab(&self) -> Option<&SctkPopup> {
|
||||||
|
self.chains
|
||||||
|
.iter()
|
||||||
|
.find_map(|c| c.into_iter().rev().find(|p| p.data.grab))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn root_grab(&self) -> Option<&SctkPopup> {
|
||||||
|
self.chains
|
||||||
|
.iter()
|
||||||
|
.find_map(|c| c.into_iter().find(|p| p.data.grab))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn push(&mut self, popup: SctkPopup) {
|
||||||
|
if let Some((chain, _pos)) =
|
||||||
|
self.chain_for_popup_mut(popup.data.parent.wl_surface())
|
||||||
|
{
|
||||||
|
// TODO should we return an error if a popup is attempted to be added to a non-leaf popup of a chain?
|
||||||
|
chain.push(popup);
|
||||||
|
} else {
|
||||||
|
if let Some(empty) = self.chains.iter_mut().find(|c| c.is_empty()) {
|
||||||
|
empty.push(popup);
|
||||||
|
} else {
|
||||||
|
self.chains.push(vec![popup]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn remove(
|
||||||
|
&mut self,
|
||||||
|
popup: &WlSurface,
|
||||||
|
) -> Option<impl Iterator<Item = SctkPopup>> {
|
||||||
|
// must perform cleanup so that the popups are dropped in the correct order
|
||||||
|
let ret = if let Some((chain, mut pos)) =
|
||||||
|
self.chain_for_popup_mut(popup)
|
||||||
|
{
|
||||||
|
// TODO should we return an error if a popup is attempted to be added to a non-leaf popup of a chain?
|
||||||
|
while let Some(p) = pos.checked_sub(1).and_then(|p| chain.get(p)) {
|
||||||
|
if p.close_with_children {
|
||||||
|
pos -= 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(chain.drain(pos..).rev())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn remove_ignore_children(
|
||||||
|
&mut self,
|
||||||
|
popup: &WlSurface,
|
||||||
|
) -> Option<impl Iterator<Item = SctkPopup>> {
|
||||||
|
// must perform cleanup so that the popups are dropped in the correct order
|
||||||
|
let ret = if let Some((chain, pos)) = self.chain_for_popup_mut(popup) {
|
||||||
|
Some(chain.drain(pos..).rev())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SctkPopup {
|
||||||
|
pub(crate) popup: Popup,
|
||||||
|
pub(crate) last_configure: Option<PopupConfigure>,
|
||||||
|
pub(crate) _pending_requests:
|
||||||
|
Vec<platform_specific::wayland::popup::Action>,
|
||||||
|
pub(crate) data: SctkPopupData,
|
||||||
|
pub(crate) common: Arc<Mutex<Common>>,
|
||||||
|
pub(crate) wp_fractional_scale: Option<WpFractionalScaleV1>,
|
||||||
|
pub(crate) close_with_children: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SctkPopup {
|
||||||
|
pub(crate) fn set_size(&mut self, w: u32, h: u32, token: u32) {
|
||||||
|
let guard = self.common.lock().unwrap();
|
||||||
|
if guard.size.width == w && guard.size.height == h {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
drop(guard);
|
||||||
|
// update geometry
|
||||||
|
self.popup
|
||||||
|
.xdg_surface()
|
||||||
|
.set_window_geometry(0, 0, w as i32, h as i32);
|
||||||
|
self.update_viewport(w, h);
|
||||||
|
// update positioner
|
||||||
|
self.data.positioner.set_size(w as i32, h as i32);
|
||||||
|
self.popup.reposition(&self.data.positioner, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn update_viewport(&mut self, w: u32, h: u32) {
|
||||||
|
let common = self.common.lock().unwrap();
|
||||||
|
if common.size.width == w && common.size.height == h {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Some(viewport) = common.wp_viewport.as_ref() {
|
||||||
|
// Set inner size without the borders.
|
||||||
|
viewport.set_destination(w as i32, h as i32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
Control,
|
Control,
|
||||||
|
event_loop::popup::{self, PopupManager},
|
||||||
handlers::{
|
handlers::{
|
||||||
activation::IcedRequestData,
|
activation::IcedRequestData,
|
||||||
ext_background_effect,
|
ext_background_effect,
|
||||||
|
|
@ -82,10 +83,7 @@ use cctk::{
|
||||||
Anchor, KeyboardInteractivity, Layer, LayerShell, LayerSurface,
|
Anchor, KeyboardInteractivity, Layer, LayerShell, LayerSurface,
|
||||||
LayerSurfaceConfigure, SurfaceKind,
|
LayerSurfaceConfigure, SurfaceKind,
|
||||||
},
|
},
|
||||||
xdg::{
|
xdg::{XdgPositioner, XdgShell, popup::Popup},
|
||||||
XdgPositioner, XdgShell,
|
|
||||||
popup::{Popup, PopupConfigure},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
shm::{Shm, multi::MultiPool},
|
shm::{Shm, multi::MultiPool},
|
||||||
},
|
},
|
||||||
|
|
@ -94,6 +92,7 @@ use cctk::{
|
||||||
};
|
};
|
||||||
use iced_runtime::{
|
use iced_runtime::{
|
||||||
core::{self, Point, touch},
|
core::{self, Point, touch},
|
||||||
|
keyboard::key::Named::Settings,
|
||||||
platform_specific::{
|
platform_specific::{
|
||||||
self,
|
self,
|
||||||
wayland::{
|
wayland::{
|
||||||
|
|
@ -123,6 +122,13 @@ use wayland_protocols::{
|
||||||
|
|
||||||
pub static TOKEN_CTR: AtomicU32 = AtomicU32::new(0);
|
pub static TOKEN_CTR: AtomicU32 = AtomicU32::new(0);
|
||||||
|
|
||||||
|
/// The requested cursor for a surface
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||||
|
pub(crate) struct CursorRequest {
|
||||||
|
pub(crate) icon: Option<CursorIcon>,
|
||||||
|
pub(crate) hidden: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct SctkSeat {
|
pub(crate) struct SctkSeat {
|
||||||
pub(crate) seat: WlSeat,
|
pub(crate) seat: WlSeat,
|
||||||
|
|
@ -139,6 +145,8 @@ pub(crate) struct SctkSeat {
|
||||||
pub(crate) active_icon: Option<CursorIcon>,
|
pub(crate) active_icon: Option<CursorIcon>,
|
||||||
// Cursor icon set by application
|
// Cursor icon set by application
|
||||||
pub(crate) icon: Option<CursorIcon>,
|
pub(crate) icon: Option<CursorIcon>,
|
||||||
|
// Application asked for cursor to hide
|
||||||
|
pub(crate) hidden: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SctkSeat {
|
impl SctkSeat {
|
||||||
|
|
@ -148,6 +156,35 @@ impl SctkSeat {
|
||||||
self.active_icon = Some(icon);
|
self.active_icon = Some(icon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Is the pointer currently over `surface`.
|
||||||
|
pub(crate) fn is_over(&self, surface: &ObjectId) -> bool {
|
||||||
|
self.ptr_focus.as_ref().is_some_and(|s| &s.id() == surface)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set cursor visibility over this surfaces.
|
||||||
|
pub(crate) fn set_cursor_visible(
|
||||||
|
&mut self,
|
||||||
|
conn: &Connection,
|
||||||
|
visible: bool,
|
||||||
|
) {
|
||||||
|
let Some(ptr) = self.ptr.as_ref() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.hidden = !visible;
|
||||||
|
|
||||||
|
if visible {
|
||||||
|
let icon = self.icon.unwrap_or(CursorIcon::Default);
|
||||||
|
_ = ptr.set_cursor(conn, icon);
|
||||||
|
self.active_icon = Some(icon);
|
||||||
|
} else {
|
||||||
|
// Ignoring the error
|
||||||
|
// It only fails when the pointer is not on our surface (due to serial error)
|
||||||
|
_ = ptr.hide_cursor();
|
||||||
|
self.active_icon = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -263,47 +300,6 @@ impl From<LogicalSize<u32>> for Common {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct SctkPopup {
|
|
||||||
pub(crate) popup: Popup,
|
|
||||||
pub(crate) last_configure: Option<PopupConfigure>,
|
|
||||||
pub(crate) _pending_requests:
|
|
||||||
Vec<platform_specific::wayland::popup::Action>,
|
|
||||||
pub(crate) data: SctkPopupData,
|
|
||||||
pub(crate) common: Arc<Mutex<Common>>,
|
|
||||||
pub(crate) wp_fractional_scale: Option<WpFractionalScaleV1>,
|
|
||||||
pub(crate) close_with_children: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SctkPopup {
|
|
||||||
pub(crate) fn set_size(&mut self, w: u32, h: u32, token: u32) {
|
|
||||||
let guard = self.common.lock().unwrap();
|
|
||||||
if guard.size.width == w && guard.size.height == h {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
drop(guard);
|
|
||||||
// update geometry
|
|
||||||
self.popup
|
|
||||||
.xdg_surface()
|
|
||||||
.set_window_geometry(0, 0, w as i32, h as i32);
|
|
||||||
self.update_viewport(w, h);
|
|
||||||
// update positioner
|
|
||||||
self.data.positioner.set_size(w as i32, h as i32);
|
|
||||||
self.popup.reposition(&self.data.positioner, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn update_viewport(&mut self, w: u32, h: u32) {
|
|
||||||
let common = self.common.lock().unwrap();
|
|
||||||
if common.size.width == w && common.size.height == h {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if let Some(viewport) = common.wp_viewport.as_ref() {
|
|
||||||
// Set inner size without the borders.
|
|
||||||
viewport.set_destination(w as i32, h as i32);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SctkLockSurface {
|
pub struct SctkLockSurface {
|
||||||
pub(crate) id: core::window::Id,
|
pub(crate) id: core::window::Id,
|
||||||
|
|
@ -337,6 +333,7 @@ pub struct SctkSubsurface {
|
||||||
pub struct SctkPopupData {
|
pub struct SctkPopupData {
|
||||||
pub(crate) id: core::window::Id,
|
pub(crate) id: core::window::Id,
|
||||||
pub(crate) parent: PopupParent,
|
pub(crate) parent: PopupParent,
|
||||||
|
pub(crate) parent_window: core::window::Id,
|
||||||
pub(crate) toplevel: WlSurface,
|
pub(crate) toplevel: WlSurface,
|
||||||
pub(crate) positioner: Arc<XdgPositioner>,
|
pub(crate) positioner: Arc<XdgPositioner>,
|
||||||
pub(crate) grab: bool,
|
pub(crate) grab: bool,
|
||||||
|
|
@ -431,7 +428,7 @@ pub struct SctkState {
|
||||||
/// `WindowUpdate` or buffer on the associated with it `WindowHandle`.
|
/// `WindowUpdate` or buffer on the associated with it `WindowHandle`.
|
||||||
pub(crate) windows: Vec<SctkWindow>,
|
pub(crate) windows: Vec<SctkWindow>,
|
||||||
pub(crate) layer_surfaces: Vec<SctkLayerSurface>,
|
pub(crate) layer_surfaces: Vec<SctkLayerSurface>,
|
||||||
pub(crate) popups: Vec<SctkPopup>,
|
pub(crate) popmgr: PopupManager,
|
||||||
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) blur_surfaces: HashMap<core::window::Id, ExtBackgroundEffectSurfaceV1>,
|
||||||
|
|
@ -471,6 +468,7 @@ pub struct SctkState {
|
||||||
pub(crate) session_lock_state: SessionLockState,
|
pub(crate) session_lock_state: SessionLockState,
|
||||||
pub(crate) session_lock: Option<SessionLock>,
|
pub(crate) session_lock: Option<SessionLock>,
|
||||||
pub(crate) id_map: HashMap<ObjectId, core::window::Id>,
|
pub(crate) id_map: HashMap<ObjectId, core::window::Id>,
|
||||||
|
pub(crate) cursor_requests: HashMap<ObjectId, CursorRequest>,
|
||||||
pub(crate) to_commit: HashMap<core::window::Id, WlSurface>,
|
pub(crate) to_commit: HashMap<core::window::Id, WlSurface>,
|
||||||
pub(crate) destroyed: HashSet<core::window::Id>,
|
pub(crate) destroyed: HashSet<core::window::Id>,
|
||||||
pub(crate) pending_popup: Option<(SctkPopupSettings, usize)>,
|
pub(crate) pending_popup: Option<(SctkPopupSettings, usize)>,
|
||||||
|
|
@ -573,6 +571,17 @@ pub(crate) fn receive_frame(
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SctkState {
|
impl SctkState {
|
||||||
|
/// What a surface wants the pointer over it to be.
|
||||||
|
pub(crate) fn record_cursor(
|
||||||
|
&mut self,
|
||||||
|
surface: ObjectId,
|
||||||
|
amend: impl FnOnce(&mut CursorRequest),
|
||||||
|
) {
|
||||||
|
self.cursor_requests
|
||||||
|
.retain(|id, _| id == &surface || self.id_map.contains_key(id));
|
||||||
|
amend(self.cursor_requests.entry(surface).or_default());
|
||||||
|
}
|
||||||
|
|
||||||
pub fn request_redraw(&mut self, surface: &WlSurface) {
|
pub fn request_redraw(&mut self, surface: &WlSurface) {
|
||||||
let e = self
|
let e = self
|
||||||
.frame_status
|
.frame_status
|
||||||
|
|
@ -609,11 +618,7 @@ impl SctkState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(popup) = self
|
if let Some(popup) = self.popmgr.popup(surface) {
|
||||||
.popups
|
|
||||||
.iter_mut()
|
|
||||||
.find(|p| p.popup.wl_surface() == surface)
|
|
||||||
{
|
|
||||||
id = Some(popup.data.id);
|
id = Some(popup.data.id);
|
||||||
if legacy && popup.wp_fractional_scale.is_some() {
|
if legacy && popup.wp_fractional_scale.is_some() {
|
||||||
return;
|
return;
|
||||||
|
|
@ -689,7 +694,7 @@ impl SctkState {
|
||||||
> {
|
> {
|
||||||
if self.layer_surfaces.iter().any(|s| s.id == settings.id)
|
if self.layer_surfaces.iter().any(|s| s.id == settings.id)
|
||||||
|| self.windows.iter().any(|w| w.id == settings.id)
|
|| self.windows.iter().any(|w| w.id == settings.id)
|
||||||
|| self.popups.iter().any(|p| p.data.id == settings.id)
|
|| self.popmgr.popup_id(settings.id).is_some()
|
||||||
|| self.subsurfaces.iter().any(|s| s.id == settings.id)
|
|| self.subsurfaces.iter().any(|s| s.id == settings.id)
|
||||||
{
|
{
|
||||||
log::warn!(
|
log::warn!(
|
||||||
|
|
@ -713,12 +718,7 @@ impl SctkState {
|
||||||
PopupParent::Window(parent.wl_surface(&self.connection)),
|
PopupParent::Window(parent.wl_surface(&self.connection)),
|
||||||
parent.wl_surface(&self.connection),
|
parent.wl_surface(&self.connection),
|
||||||
)
|
)
|
||||||
} else if let Some(i) = self
|
} else if let Some(parent) = self.popmgr.popup_id(settings.parent) {
|
||||||
.popups
|
|
||||||
.iter()
|
|
||||||
.position(|p| p.data.id == settings.parent)
|
|
||||||
{
|
|
||||||
let parent = &self.popups[i];
|
|
||||||
(
|
(
|
||||||
PopupParent::Popup(parent.popup.wl_surface().clone()),
|
PopupParent::Popup(parent.popup.wl_surface().clone()),
|
||||||
parent.data.toplevel.clone(),
|
parent.data.toplevel.clone(),
|
||||||
|
|
@ -756,6 +756,7 @@ impl SctkState {
|
||||||
if positioner.version() >= 3 && settings.positioner.reactive {
|
if positioner.version() >= 3 && settings.positioner.reactive {
|
||||||
positioner.set_reactive();
|
positioner.set_reactive();
|
||||||
}
|
}
|
||||||
|
|
||||||
positioner.set_size(size.0 as i32, size.1 as i32);
|
positioner.set_size(size.0 as i32, size.1 as i32);
|
||||||
|
|
||||||
let grab = settings.grab;
|
let grab = settings.grab;
|
||||||
|
|
@ -820,10 +821,9 @@ impl SctkState {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
PopupParent::Popup(parent) => {
|
PopupParent::Popup(parent) => {
|
||||||
let Some(parent_xdg) = self.popups.iter().find_map(|p| {
|
let Some(parent_xdg) =
|
||||||
(p.popup.wl_surface() == parent)
|
self.popmgr.popup(parent).map(|p| p.popup.xdg_surface())
|
||||||
.then(|| p.popup.xdg_surface())
|
else {
|
||||||
}) else {
|
|
||||||
return Err(PopupCreationError::ParentMissing);
|
return Err(PopupCreationError::ParentMissing);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -898,11 +898,12 @@ impl SctkState {
|
||||||
let common = Arc::new(Mutex::new(common));
|
let common = Arc::new(Mutex::new(common));
|
||||||
let positioner = Arc::new(positioner);
|
let positioner = Arc::new(positioner);
|
||||||
|
|
||||||
self.popups.push(SctkPopup {
|
self.popmgr.push(popup::SctkPopup {
|
||||||
popup: popup.clone(),
|
popup: popup.clone(),
|
||||||
data: SctkPopupData {
|
data: SctkPopupData {
|
||||||
id: settings.id,
|
id: settings.id,
|
||||||
parent: parent.clone(),
|
parent: parent.clone(),
|
||||||
|
parent_window: settings.parent,
|
||||||
toplevel: toplevel.clone(),
|
toplevel: toplevel.clone(),
|
||||||
positioner: positioner.clone(),
|
positioner: positioner.clone(),
|
||||||
grab: settings.grab,
|
grab: settings.grab,
|
||||||
|
|
@ -957,7 +958,7 @@ impl SctkState {
|
||||||
|
|
||||||
if self.layer_surfaces.iter().any(|s| s.id == id)
|
if self.layer_surfaces.iter().any(|s| s.id == id)
|
||||||
|| self.windows.iter().any(|w| w.id == id)
|
|| self.windows.iter().any(|w| w.id == id)
|
||||||
|| self.popups.iter().any(|p| p.data.id == id)
|
|| self.popmgr.popup_id(id).is_some()
|
||||||
|| self.subsurfaces.iter().any(|s| s.id == id)
|
|| self.subsurfaces.iter().any(|s| s.id == id)
|
||||||
{
|
{
|
||||||
log::warn!("Layer surface with id {id:?} already exists");
|
log::warn!("Layer surface with id {id:?} already exists");
|
||||||
|
|
@ -1071,7 +1072,7 @@ impl SctkState {
|
||||||
) -> Option<(CommonSurface, Arc<Mutex<Common>>)> {
|
) -> Option<(CommonSurface, Arc<Mutex<Common>>)> {
|
||||||
if self.layer_surfaces.iter().any(|s| s.id == id)
|
if self.layer_surfaces.iter().any(|s| s.id == id)
|
||||||
|| self.windows.iter().any(|w| w.id == id)
|
|| self.windows.iter().any(|w| w.id == id)
|
||||||
|| self.popups.iter().any(|p| p.data.id == id)
|
|| self.popmgr.popup_id(id).is_some()
|
||||||
|| self.subsurfaces.iter().any(|s| s.id == id)
|
|| self.subsurfaces.iter().any(|s| s.id == id)
|
||||||
{
|
{
|
||||||
log::warn!("Lock surface with id {id:?} already exists");
|
log::warn!("Lock surface with id {id:?} already exists");
|
||||||
|
|
@ -1150,12 +1151,9 @@ impl SctkState {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
platform_specific::wayland::layer_surface::Action::Destroy(id) => {
|
platform_specific::wayland::layer_surface::Action::Destroy(id) => {
|
||||||
if let Some(p) = self
|
if let Some(id) = self.layer_surfaces.iter().find_map(|l| if l.id == id {self.popmgr.popups().find_map(|p| (p.data.parent.wl_surface() == l.surface.wl_surface()).then(|| p.data.id))} else {None}).clone() {
|
||||||
.popups
|
_ = self.handle_action(Action::Popup(platform_specific::wayland::popup::Action::Destroy { id }));
|
||||||
.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);
|
||||||
|
|
||||||
|
|
@ -1269,245 +1267,200 @@ impl SctkState {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Action::Popup(action) => match action {
|
Action::Popup(action) => {
|
||||||
platform_specific::wayland::popup::Action::Popup { popup: settings } => {
|
match action {
|
||||||
// first check existing popup
|
platform_specific::wayland::popup::Action::Popup { popup: settings } => {
|
||||||
if let Some(existing) = self.popups.iter().position(|p| p.data.id == settings.id
|
// first check existing popup
|
||||||
&& (
|
// if an existing popup with the same id exists, we just update the positioner.
|
||||||
self.popups.iter().any(|parent| parent.popup.wl_surface() == p.data.parent.wl_surface() && parent.data.id == settings.parent)
|
if let Some((chain, existing)) = self.popmgr.popup_id(settings.id).and_then(|p|
|
||||||
|| self.windows.iter().any(|w| w.id == settings.parent && *p.data.parent.wl_surface() == w.wl_surface(&self.connection))
|
(p.data.grab == settings.grab && self.popmgr.popups().any(|parent| parent.popup.wl_surface() == p.data.parent.wl_surface() && parent.data.id == settings.parent)
|
||||||
|| self.layer_surfaces.iter().any(|l| l.id == settings.parent && p.data.parent.wl_surface() == l.surface.wl_surface()))
|
|| self.windows.iter().any(|w| w.id == settings.parent && *p.data.parent.wl_surface() == w.wl_surface(&self.connection))
|
||||||
) {
|
|| self.layer_surfaces.iter().any(|l| l.id == settings.parent && p.data.parent.wl_surface() == l.surface.wl_surface())).then(||
|
||||||
let existing = &mut self.popups[existing];
|
{
|
||||||
let size = if let Some(size) = settings.positioner.size {
|
p.popup.wl_surface().clone()
|
||||||
size
|
})).and_then(|s| self.popmgr.chain_for_popup_mut(&s))
|
||||||
} else {
|
|
||||||
let guard = existing.common.lock().unwrap();
|
|
||||||
(guard.size.width, guard.size.height)
|
|
||||||
};
|
|
||||||
let Ok(positioner) = XdgPositioner::new(&self.xdg_shell_state)
|
|
||||||
.map_err(PopupCreationError::PositionerCreationFailed) else {
|
|
||||||
log::error!("Failed to create popup positioner");
|
|
||||||
return Ok(());
|
|
||||||
};
|
|
||||||
positioner.set_anchor(settings.positioner.anchor);
|
|
||||||
positioner.set_anchor_rect(
|
|
||||||
settings.positioner.anchor_rect.x,
|
|
||||||
settings.positioner.anchor_rect.y,
|
|
||||||
settings.positioner.anchor_rect.width,
|
|
||||||
settings.positioner.anchor_rect.height,
|
|
||||||
);
|
|
||||||
if let Ok(constraint_adjustment) =
|
|
||||||
settings.positioner.constraint_adjustment.try_into()
|
|
||||||
{
|
{
|
||||||
positioner.set_constraint_adjustment(constraint_adjustment);
|
let existing = &mut chain[existing];
|
||||||
}
|
let size = if let Some(size) = settings.positioner.size {
|
||||||
positioner.set_gravity(settings.positioner.gravity);
|
size
|
||||||
positioner.set_offset(
|
} else {
|
||||||
settings.positioner.offset.0,
|
let guard = existing.common.lock().unwrap();
|
||||||
settings.positioner.offset.1,
|
(guard.size.width, guard.size.height)
|
||||||
);
|
};
|
||||||
if settings.positioner.reactive {
|
let Ok(positioner) = XdgPositioner::new(&self.xdg_shell_state)
|
||||||
positioner.set_reactive();
|
.map_err(PopupCreationError::PositionerCreationFailed) else {
|
||||||
}
|
log::error!("Failed to create popup positioner");
|
||||||
positioner.set_size(size.0 as i32, size.1 as i32);
|
return Ok(());
|
||||||
existing.data.positioner = Arc::new(positioner);
|
|
||||||
existing.set_size(size.0, size.1, TOKEN_CTR.fetch_add(1, std::sync::atomic::Ordering::Relaxed));
|
|
||||||
_ = send_event(&self.events_sender, &self.proxy,
|
|
||||||
SctkEvent::PopupEvent { variant: crate::sctk_event::PopupEventVariant::Size(size.0, size.1), toplevel_id: existing.data.parent.wl_surface().clone(), parent_id: existing.data.parent.wl_surface().clone(), id: existing.popup.wl_surface().clone() });
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
let mut parent_mismatch = !self.popups.is_empty();
|
|
||||||
for p in &self.popups {
|
|
||||||
parent_mismatch = p.data.id != settings.parent;
|
|
||||||
}
|
|
||||||
if !self.destroyed.is_empty() || parent_mismatch {
|
|
||||||
if parent_mismatch {
|
|
||||||
let mut found = false;
|
|
||||||
for p in std::mem::take(&mut self.popups).into_iter().rev() {
|
|
||||||
let id = p.data.id;
|
|
||||||
self.popups.insert(0, p);
|
|
||||||
|
|
||||||
found |= id == settings.parent;
|
|
||||||
if !found {
|
|
||||||
_ = self.handle_action(Action::Popup(platform_specific::wayland::popup::Action::Destroy{id}));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if self.pending_popup.replace((settings, 0)).is_none() {
|
|
||||||
|
|
||||||
let timer = cctk::sctk::reexports::calloop::timer::Timer::from_duration(Duration::from_millis(30));
|
|
||||||
let queue_handle = self.queue_handle.clone();
|
|
||||||
_ = self.loop_handle.insert_source(timer, move |_, _, state| {
|
|
||||||
let Some((mut popup, attempt)) = state.pending_popup.take() else {
|
|
||||||
return TimeoutAction::Drop;
|
|
||||||
};
|
};
|
||||||
|
positioner.set_anchor(settings.positioner.anchor);
|
||||||
|
positioner.set_anchor_rect(
|
||||||
|
settings.positioner.anchor_rect.x,
|
||||||
|
settings.positioner.anchor_rect.y,
|
||||||
|
settings.positioner.anchor_rect.width,
|
||||||
|
settings.positioner.anchor_rect.height,
|
||||||
|
);
|
||||||
|
if let Ok(constraint_adjustment) =
|
||||||
|
settings.positioner.constraint_adjustment.try_into()
|
||||||
|
{
|
||||||
|
positioner.set_constraint_adjustment(constraint_adjustment);
|
||||||
|
}
|
||||||
|
positioner.set_gravity(settings.positioner.gravity);
|
||||||
|
positioner.set_offset(
|
||||||
|
settings.positioner.offset.0,
|
||||||
|
settings.positioner.offset.1,
|
||||||
|
);
|
||||||
|
if settings.positioner.reactive {
|
||||||
|
positioner.set_reactive();
|
||||||
|
}
|
||||||
|
positioner.set_size(size.0 as i32, size.1 as i32);
|
||||||
|
existing.data.positioner = Arc::new(positioner);
|
||||||
|
existing.set_size(size.0, size.1, TOKEN_CTR.fetch_add(1, std::sync::atomic::Ordering::Relaxed));
|
||||||
|
_ = send_event(&self.events_sender, &self.proxy,
|
||||||
|
SctkEvent::PopupEvent { variant: crate::sctk_event::PopupEventVariant::Size(size.0, size.1), toplevel_id: existing.data.parent.wl_surface().clone(), parent_id: existing.data.parent.wl_surface().clone(), id: existing.popup.wl_surface().clone(), parent_window: existing.data.parent_window });
|
||||||
|
return Ok(());
|
||||||
|
} else if !self.destroyed.is_empty() || self.popmgr.popup_id(settings.id).is_some() || self.popmgr.active_grab().is_some()
|
||||||
|
{
|
||||||
|
// must clean up popup chains so that there is just one grab chain...
|
||||||
|
let to_destroy = if settings.grab {
|
||||||
|
if let Some((grab_chain, p_i)) = self.popmgr.active_grab().and_then(|g| self.popmgr.chain_for_popup(g.popup.wl_surface())) {
|
||||||
|
|
||||||
if !state.destroyed.is_empty() || state.popups.last().is_some_and(|p| {
|
if let Some(pos) = grab_chain.iter().position(|p| p.data.id == settings.parent) {
|
||||||
state.id_map.get(&p.popup.wl_surface().id()).map_or(true, |p| *p != popup.parent)
|
// destroy children of parent
|
||||||
}) {
|
grab_chain.get(pos + 1).map(|g| g.data.id)
|
||||||
if attempt < 5 {
|
|
||||||
state.pending_popup = Some((popup, attempt+1));
|
|
||||||
TimeoutAction::ToDuration(Duration::from_millis(30))
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
TimeoutAction::Drop
|
// destroy whole chain...
|
||||||
|
grab_chain.get(0).map(|g| g.data.id)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match state.get_popup(popup) {
|
None
|
||||||
Ok((id, parent_id, toplevel_id, surface, common)) => {
|
|
||||||
let wl_surface = surface.wl_surface().clone();
|
|
||||||
receive_frame(&mut state.frame_status, &wl_surface);
|
|
||||||
send_event(&state.events_sender, &state.proxy,
|
|
||||||
SctkEvent::PopupEvent {
|
|
||||||
variant: crate::platform_specific::wayland::sctk_event::PopupEventVariant::Created(queue_handle.clone(), surface, id, common, state.connection.display()),
|
|
||||||
toplevel_id, parent_id, id: wl_surface });
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
log::error!("Failed to create popup. {err:?}");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
TimeoutAction::Drop
|
|
||||||
}
|
}
|
||||||
});
|
} else {
|
||||||
}
|
None
|
||||||
} else {
|
};
|
||||||
self.pending_popup = None;
|
if let Some(to_destroy) = to_destroy {
|
||||||
match self.get_popup(settings) {
|
_ = self.destroy_popup(to_destroy, true);
|
||||||
Ok((id, parent_id, toplevel_id, surface, common)) => {
|
|
||||||
let wl_surface = surface.wl_surface().clone();
|
|
||||||
send_event(&self.events_sender, &self.proxy,
|
|
||||||
SctkEvent::PopupEvent {
|
|
||||||
variant: crate::platform_specific::wayland::sctk_event::PopupEventVariant::Created(self.queue_handle.clone(), surface, id, common, self.connection.display()),
|
|
||||||
toplevel_id, parent_id, id: wl_surface });
|
|
||||||
}
|
}
|
||||||
Err(err) => {
|
|
||||||
log::error!("Failed to create popup. {err:?}");
|
if self.popmgr.popup_id(settings.id).is_some() {
|
||||||
|
let _ = self.handle_action(Action::Popup(platform_specific::wayland::popup::Action::Destroy { id: settings.id }));
|
||||||
|
}
|
||||||
|
if self.pending_popup.replace((settings, 0)).is_none() {
|
||||||
|
let timer = cctk::sctk::reexports::calloop::timer::Timer::from_duration(Duration::from_millis(30));
|
||||||
|
let queue_handle = self.queue_handle.clone();
|
||||||
|
_ = self.loop_handle.insert_source(timer, move |_, _, state| {
|
||||||
|
let Some((settings, attempt)) = state.pending_popup.take() else {
|
||||||
|
return TimeoutAction::Drop;
|
||||||
|
};
|
||||||
|
// check if the mismatch still exists...
|
||||||
|
if !state.destroyed.is_empty() || state.popmgr.popup_id(settings.id).is_some_and(|p|
|
||||||
|
state.popmgr.popups().any(|parent| parent.popup.wl_surface() == p.data.parent.wl_surface() && parent.data.id == settings.parent))
|
||||||
|
{
|
||||||
|
if attempt < 5 {
|
||||||
|
state.pending_popup = Some((settings, attempt+1));
|
||||||
|
TimeoutAction::ToDuration(Duration::from_millis(30))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
TimeoutAction::Drop
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let parent_window = settings.parent;
|
||||||
|
match state.get_popup(settings) {
|
||||||
|
Ok((id, parent_id, toplevel_id, surface, common)) => {
|
||||||
|
let wl_surface = surface.wl_surface().clone();
|
||||||
|
receive_frame(&mut state.frame_status, &wl_surface);
|
||||||
|
send_event(&state.events_sender, &state.proxy,
|
||||||
|
SctkEvent::PopupEvent {
|
||||||
|
variant: crate::platform_specific::wayland::sctk_event::PopupEventVariant::Created(queue_handle.clone(), surface, id, common, state.connection.display()),
|
||||||
|
toplevel_id, parent_id, id: wl_surface, parent_window });
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
log::error!("Failed to create popup. {err:?}");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
TimeoutAction::Drop
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let parent_window = settings.parent;
|
||||||
|
match self.get_popup(settings) {
|
||||||
|
Ok((id, parent_id, toplevel_id, surface, common)) => {
|
||||||
|
let wl_surface = surface.wl_surface().clone();
|
||||||
|
send_event(&self.events_sender, &self.proxy,
|
||||||
|
SctkEvent::PopupEvent {
|
||||||
|
variant: crate::platform_specific::wayland::sctk_event::PopupEventVariant::Created(
|
||||||
|
self.queue_handle.clone(), surface, id, common, self.connection.display()
|
||||||
|
),
|
||||||
|
toplevel_id, parent_id, id: wl_surface, parent_window }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
log::error!("Failed to create popup. {err:?}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
// XXX popup destruction must be done carefully
|
||||||
// XXX popup destruction must be done carefully
|
// first destroy the uppermost popup, then work down to the requested popup
|
||||||
// first destroy the uppermost popup, then work down to the requested popup
|
platform_specific::wayland::popup::Action::Destroy { id } => {
|
||||||
platform_specific::wayland::popup::Action::Destroy { id } => {
|
if let Some(value) = self.destroy_popup(id, false) {
|
||||||
let sctk_popup = match self
|
return value;
|
||||||
.popups
|
|
||||||
.iter()
|
|
||||||
.position(|s| s.data.id == id)
|
|
||||||
{
|
|
||||||
Some(p) => self.popups.remove(p),
|
|
||||||
None => {
|
|
||||||
log::info!("No popup to destroy");
|
|
||||||
return Ok(());
|
|
||||||
},
|
|
||||||
};
|
|
||||||
let mut to_destroy = vec![sctk_popup];
|
|
||||||
|
|
||||||
while let Some(popup_to_destroy_last) = to_destroy.last().and_then(|popup| self
|
|
||||||
.popups
|
|
||||||
.iter()
|
|
||||||
.position(|p| popup.data.parent.wl_surface() == p.popup.wl_surface() && p.close_with_children)) {
|
|
||||||
let popup_to_destroy_last = self.popups.remove(popup_to_destroy_last);
|
|
||||||
to_destroy.push(popup_to_destroy_last);
|
|
||||||
}
|
|
||||||
to_destroy.reverse();
|
|
||||||
|
|
||||||
while let Some(popup_to_destroy_first) = to_destroy.last().and_then(|popup| self
|
|
||||||
.popups
|
|
||||||
.iter()
|
|
||||||
.position(|p| p.data.parent.wl_surface() == popup.popup.wl_surface())) {
|
|
||||||
let popup_to_destroy_first = self.popups.remove(popup_to_destroy_first);
|
|
||||||
to_destroy.push(popup_to_destroy_first);
|
|
||||||
}
|
|
||||||
for popup in to_destroy.into_iter().rev() {
|
|
||||||
if let Some(id) = self.id_map.remove(&popup.popup.wl_surface().id()) {
|
|
||||||
_ = self.destroyed.insert(id);
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
if let Some(blurred) = self.blur_surfaces.remove(&id) {
|
platform_specific::wayland::popup::Action::Size { id, width, height } => {
|
||||||
blurred.destroy();
|
if let Some(sctk_popup) = self
|
||||||
}
|
.popmgr.popup_id_mut(id)
|
||||||
_ = self.corner_radii.remove(&id);
|
|
||||||
|
|
||||||
|
|
||||||
let (removed, remaining): (Vec<_>, Vec<_>) = self
|
|
||||||
.subsurfaces
|
|
||||||
.drain(..)
|
|
||||||
.partition(|s| {
|
|
||||||
s.instance.parent == *popup.popup.wl_surface()
|
|
||||||
});
|
|
||||||
|
|
||||||
self.subsurfaces = remaining;
|
|
||||||
for s in removed
|
|
||||||
{
|
{
|
||||||
crate::subsurface_widget::remove_iced_subsurface(
|
// update geometry
|
||||||
&s.instance.wl_surface,
|
// update positioner
|
||||||
);
|
sctk_popup.set_size(width, height, TOKEN_CTR.fetch_add(1, std::sync::atomic::Ordering::Relaxed));
|
||||||
send_event(&self.events_sender, &self.proxy,
|
let surface = sctk_popup.popup.wl_surface().clone();
|
||||||
SctkEvent::SubsurfaceEvent( crate::sctk_event::SubsurfaceEventVariant::Destroyed(s.instance) )
|
_ = send_event(&self.events_sender, &self.proxy,
|
||||||
);
|
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, parent_window: sctk_popup.data.parent_window });
|
||||||
}
|
}
|
||||||
_ = send_event(&self.events_sender, &self.proxy,
|
},
|
||||||
SctkEvent::PopupEvent { variant: crate::sctk_event::PopupEventVariant::Done, toplevel_id: popup.data.toplevel.clone(), parent_id: popup.data.parent.wl_surface().clone(), id: popup.popup.wl_surface().clone() });
|
platform_specific::wayland::popup::Action::Reposition { id, positioner } => {
|
||||||
}
|
if let Some(sctk_popup) = self
|
||||||
},
|
.popmgr.popup_id_mut(id)
|
||||||
platform_specific::wayland::popup::Action::Size { id, width, height } => {
|
|
||||||
if let Some(sctk_popup) = self
|
|
||||||
.popups
|
|
||||||
.iter_mut()
|
|
||||||
.find(|s| s.data.id == id)
|
|
||||||
{
|
|
||||||
// update geometry
|
|
||||||
// update positioner
|
|
||||||
sctk_popup.set_size(width, height, 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(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_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, parent_window: sctk_popup.data.parent_window });
|
||||||
}
|
}
|
||||||
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 } => {
|
||||||
|
|
@ -1718,7 +1671,7 @@ impl SctkState {
|
||||||
}
|
}
|
||||||
let s = if let Some(w) = self.windows.iter_mut().find(|w| w.id == id) {
|
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())))
|
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) {
|
} else if let Some(p) = self.popmgr.popup_id_mut(id) {
|
||||||
let guard = p.common.lock().unwrap();
|
let guard = p.common.lock().unwrap();
|
||||||
Some((Surface::Xdg(p.popup.xdg_surface().clone(), None), guard.size.cast::<f64>()))
|
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) {
|
} else if let Some(l) = self.layer_surfaces.iter_mut().find(|l| l.id == id) {
|
||||||
|
|
@ -1822,7 +1775,7 @@ impl SctkState {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let s = if let Some(s) = self.popups.iter().find(|s| s.data.id == id) {
|
let s = if let Some(s) = self.popmgr.popup_id(id) {
|
||||||
s.popup.wl_surface()
|
s.popup.wl_surface()
|
||||||
} else if let Some(s) = self.layer_surfaces.iter().find(|s| s.id == id) {
|
} else if let Some(s) = self.layer_surfaces.iter().find(|s| s.id == id) {
|
||||||
s.surface.wl_surface()
|
s.surface.wl_surface()
|
||||||
|
|
@ -1843,6 +1796,76 @@ impl SctkState {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn destroy_popup(
|
||||||
|
&mut self,
|
||||||
|
id: core::window::Id,
|
||||||
|
ignore_children: bool,
|
||||||
|
) -> Option<Result<(), Infallible>> {
|
||||||
|
let Some(to_destroy) = self.popmgr.popup_id(id) else {
|
||||||
|
if self.pending_popup.take().is_none_or(|p_id| p_id.0.id != id) {
|
||||||
|
log::warn!(
|
||||||
|
"Destroyed popup does not exist. {id:?}, existing: {:?}",
|
||||||
|
self.popmgr.popups().map(|p| p.data.id).collect::<Vec<_>>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Some(Ok(()));
|
||||||
|
};
|
||||||
|
let s = to_destroy.popup.wl_surface().clone();
|
||||||
|
let d: Option<Vec<_>> = if ignore_children {
|
||||||
|
self.popmgr.remove(&s).map(|v| v.collect())
|
||||||
|
} else {
|
||||||
|
self.popmgr.remove_ignore_children(&s).map(|v| v.collect())
|
||||||
|
};
|
||||||
|
let Some(to_destroy) = d else {
|
||||||
|
return Some(Ok(()));
|
||||||
|
};
|
||||||
|
|
||||||
|
for popup in to_destroy {
|
||||||
|
if let Some(id) = self.id_map.remove(&popup.popup.wl_surface().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
|
||||||
|
.subsurfaces
|
||||||
|
.drain(..)
|
||||||
|
.partition(|s| s.instance.parent == *popup.popup.wl_surface());
|
||||||
|
|
||||||
|
self.subsurfaces = remaining;
|
||||||
|
for s in removed {
|
||||||
|
crate::subsurface_widget::remove_iced_subsurface(
|
||||||
|
&s.instance.wl_surface,
|
||||||
|
);
|
||||||
|
send_event(
|
||||||
|
&self.events_sender,
|
||||||
|
&self.proxy,
|
||||||
|
SctkEvent::SubsurfaceEvent(
|
||||||
|
crate::sctk_event::SubsurfaceEventVariant::Destroyed(
|
||||||
|
s.instance,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ = send_event(
|
||||||
|
&self.events_sender,
|
||||||
|
&self.proxy,
|
||||||
|
SctkEvent::PopupEvent {
|
||||||
|
variant: crate::sctk_event::PopupEventVariant::Done,
|
||||||
|
parent_window: popup.data.parent_window,
|
||||||
|
toplevel_id: popup.data.toplevel.clone(),
|
||||||
|
parent_id: popup.data.parent.wl_surface().clone(),
|
||||||
|
id: popup.popup.wl_surface().clone(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn apply_blur(
|
fn apply_blur(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: core::window::Id,
|
id: core::window::Id,
|
||||||
|
|
@ -1922,7 +1945,7 @@ impl SctkState {
|
||||||
};
|
};
|
||||||
if self.layer_surfaces.iter().any(|s| s.id == settings.id)
|
if self.layer_surfaces.iter().any(|s| s.id == settings.id)
|
||||||
|| self.windows.iter().any(|w| w.id == settings.id)
|
|| self.windows.iter().any(|w| w.id == settings.id)
|
||||||
|| self.popups.iter().any(|p| p.data.id == settings.id)
|
|| self.popmgr.popup_id(settings.id).is_some()
|
||||||
|| self.subsurfaces.iter().any(|s| s.id == settings.id)
|
|| self.subsurfaces.iter().any(|s| s.id == settings.id)
|
||||||
{
|
{
|
||||||
log::warn!("Subsurface with id {:?} already exists", settings.id);
|
log::warn!("Subsurface with id {:?} already exists", settings.id);
|
||||||
|
|
@ -1977,12 +2000,7 @@ impl SctkState {
|
||||||
self.windows.iter().find(|w| w.id == settings.parent)
|
self.windows.iter().find(|w| w.id == settings.parent)
|
||||||
{
|
{
|
||||||
PopupParent::Window(parent.wl_surface(&self.connection))
|
PopupParent::Window(parent.wl_surface(&self.connection))
|
||||||
} else if let Some(i) = self
|
} else if let Some(parent) = self.popmgr.popup_id(settings.parent) {
|
||||||
.popups
|
|
||||||
.iter()
|
|
||||||
.position(|p| p.data.id == settings.parent)
|
|
||||||
{
|
|
||||||
let parent = &self.popups[i];
|
|
||||||
PopupParent::Popup(parent.popup.wl_surface().clone())
|
PopupParent::Popup(parent.popup.wl_surface().clone())
|
||||||
} else if let Some(i) = self
|
} else if let Some(i) = self
|
||||||
.lock_surfaces
|
.lock_surfaces
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ impl PointerHandler for SctkState {
|
||||||
|
|
||||||
// track events, but only forward for the active seat
|
// track events, but only forward for the active seat
|
||||||
for e in events {
|
for e in events {
|
||||||
if my_seat.active_icon != my_seat.icon {
|
if !my_seat.hidden && my_seat.active_icon != my_seat.icon {
|
||||||
// Restore cursor that was set by appliction, or default
|
// Restore cursor that was set by appliction, or default
|
||||||
my_seat.set_cursor(
|
my_seat.set_cursor(
|
||||||
conn,
|
conn,
|
||||||
|
|
@ -60,10 +60,19 @@ impl PointerHandler for SctkState {
|
||||||
match e.kind {
|
match e.kind {
|
||||||
PointerEventKind::Enter { .. } => {
|
PointerEventKind::Enter { .. } => {
|
||||||
_ = my_seat.ptr_focus.replace(e.surface.clone());
|
_ = my_seat.ptr_focus.replace(e.surface.clone());
|
||||||
|
if let Some(want) =
|
||||||
|
self.cursor_requests.get(&e.surface.id())
|
||||||
|
{
|
||||||
|
my_seat.icon = want.icon;
|
||||||
|
my_seat.set_cursor_visible(conn, !want.hidden);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
PointerEventKind::Leave { .. } => {
|
PointerEventKind::Leave { .. } => {
|
||||||
_ = my_seat.ptr_focus.take();
|
_ = my_seat.ptr_focus.take();
|
||||||
_ = my_seat.active_icon = None;
|
_ = my_seat.active_icon = None;
|
||||||
|
// The next surface the pointer lands on decides what it
|
||||||
|
// looks like there
|
||||||
|
my_seat.hidden = false;
|
||||||
}
|
}
|
||||||
PointerEventKind::Press {
|
PointerEventKind::Press {
|
||||||
time,
|
time,
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ impl SeatHandler for SctkState {
|
||||||
last_touch_down: None,
|
last_touch_down: None,
|
||||||
icon: None,
|
icon: None,
|
||||||
active_icon: None,
|
active_icon: None,
|
||||||
|
hidden: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,6 +66,7 @@ impl SeatHandler for SctkState {
|
||||||
last_touch_down: None,
|
last_touch_down: None,
|
||||||
icon: None,
|
icon: None,
|
||||||
active_icon: None,
|
active_icon: None,
|
||||||
|
hidden: false,
|
||||||
});
|
});
|
||||||
self.seats.last_mut().unwrap()
|
self.seats.last_mut().unwrap()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::event_loop::state::receive_frame;
|
use crate::event_loop::state::receive_frame;
|
||||||
use crate::platform_specific::wayland::{
|
use crate::platform_specific::wayland::{
|
||||||
event_loop::state::{self, PopupParent, SctkState},
|
event_loop::state::{PopupParent, SctkState},
|
||||||
sctk_event::{PopupEventVariant, SctkEvent},
|
sctk_event::{PopupEventVariant, SctkEvent},
|
||||||
};
|
};
|
||||||
use cctk::sctk::{
|
use cctk::sctk::{
|
||||||
|
|
@ -18,11 +18,11 @@ impl PopupHandler for SctkState {
|
||||||
configure: cctk::sctk::shell::xdg::popup::PopupConfigure,
|
configure: cctk::sctk::shell::xdg::popup::PopupConfigure,
|
||||||
) {
|
) {
|
||||||
self.request_redraw(popup.wl_surface());
|
self.request_redraw(popup.wl_surface());
|
||||||
let sctk_popup = match self.popups.iter_mut().find(|s| {
|
let sctk_popup = match self.popmgr.popup_mut(popup.wl_surface()) {
|
||||||
s.popup.wl_surface().clone() == popup.wl_surface().clone()
|
|
||||||
}) {
|
|
||||||
Some(p) => p,
|
Some(p) => p,
|
||||||
None => return,
|
None => {
|
||||||
|
return;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let first = sctk_popup.last_configure.is_none();
|
let first = sctk_popup.last_configure.is_none();
|
||||||
_ = sctk_popup.last_configure.replace(configure.clone());
|
_ = sctk_popup.last_configure.replace(configure.clone());
|
||||||
|
|
@ -38,6 +38,7 @@ impl PopupHandler for SctkState {
|
||||||
first,
|
first,
|
||||||
),
|
),
|
||||||
id: popup.wl_surface().clone(),
|
id: popup.wl_surface().clone(),
|
||||||
|
parent_window: sctk_popup.data.parent_window,
|
||||||
toplevel_id: sctk_popup.data.toplevel.clone(),
|
toplevel_id: sctk_popup.data.toplevel.clone(),
|
||||||
parent_id: match &sctk_popup.data.parent {
|
parent_id: match &sctk_popup.data.parent {
|
||||||
PopupParent::LayerSurface(s) => s.clone(),
|
PopupParent::LayerSurface(s) => s.clone(),
|
||||||
|
|
@ -53,35 +54,11 @@ impl PopupHandler for SctkState {
|
||||||
_qh: &cctk::sctk::reexports::client::QueueHandle<Self>,
|
_qh: &cctk::sctk::reexports::client::QueueHandle<Self>,
|
||||||
popup: &cctk::sctk::shell::xdg::popup::Popup,
|
popup: &cctk::sctk::shell::xdg::popup::Popup,
|
||||||
) {
|
) {
|
||||||
let sctk_popup = match self.popups.iter().position(|s| {
|
let Some(to_destroy) = self.popmgr.remove(popup.wl_surface()) else {
|
||||||
s.popup.wl_surface().clone() == popup.wl_surface().clone()
|
return;
|
||||||
}) {
|
|
||||||
Some(p) => self.popups.remove(p),
|
|
||||||
None => return,
|
|
||||||
};
|
};
|
||||||
let mut to_destroy = vec![sctk_popup];
|
|
||||||
while let Some(popup_to_destroy) = to_destroy.last() {
|
for popup in to_destroy {
|
||||||
match popup_to_destroy.data.parent.clone() {
|
|
||||||
state::PopupParent::LayerSurface(_)
|
|
||||||
| state::PopupParent::Window(_) => {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
state::PopupParent::Popup(popup_to_destroy_first) => {
|
|
||||||
let Some(popup_to_destroy_first) =
|
|
||||||
self.popups.iter().position(|p| {
|
|
||||||
p.popup.wl_surface() == &popup_to_destroy_first
|
|
||||||
})
|
|
||||||
else {
|
|
||||||
log::warn!("could not find popup to destroy first.");
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let popup_to_destroy_first =
|
|
||||||
self.popups.remove(popup_to_destroy_first);
|
|
||||||
to_destroy.push(popup_to_destroy_first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for popup in to_destroy.into_iter().rev() {
|
|
||||||
if let Some(id) = self.id_map.remove(&popup.popup.wl_surface().id())
|
if let Some(id) = self.id_map.remove(&popup.popup.wl_surface().id())
|
||||||
{
|
{
|
||||||
if let Some(blurred) = self.blur_surfaces.remove(&id) {
|
if let Some(blurred) = self.blur_surfaces.remove(&id) {
|
||||||
|
|
@ -94,6 +71,7 @@ impl PopupHandler for SctkState {
|
||||||
|
|
||||||
self.sctk_events.push(SctkEvent::PopupEvent {
|
self.sctk_events.push(SctkEvent::PopupEvent {
|
||||||
variant: PopupEventVariant::Done,
|
variant: PopupEventVariant::Done,
|
||||||
|
parent_window: popup.data.parent_window,
|
||||||
toplevel_id: popup.data.toplevel.clone(),
|
toplevel_id: popup.data.toplevel.clone(),
|
||||||
parent_id: popup.data.parent.wl_surface().clone(),
|
parent_id: popup.data.parent.wl_surface().clone(),
|
||||||
id: popup.popup.wl_surface().clone(),
|
id: popup.popup.wl_surface().clone(),
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,8 @@ use winit::window::ImePurpose;
|
||||||
|
|
||||||
pub(crate) enum Action {
|
pub(crate) enum Action {
|
||||||
Action(iced_runtime::platform_specific::wayland::Action),
|
Action(iced_runtime::platform_specific::wayland::Action),
|
||||||
SetCursor(CursorIcon),
|
SetCursor(ObjectId, CursorIcon),
|
||||||
|
SetCursorVisible(ObjectId, bool),
|
||||||
RequestRedraw(ObjectId),
|
RequestRedraw(ObjectId),
|
||||||
TrackWindow(Arc<dyn winit::window::Window>, window::Id),
|
TrackWindow(Arc<dyn winit::window::Window>, window::Id),
|
||||||
ResizeWindow(window::Id),
|
ResizeWindow(window::Id),
|
||||||
|
|
@ -48,9 +49,14 @@ impl std::fmt::Debug for Action {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Action(arg0) => f.debug_tuple("Action").field(arg0).finish(),
|
Self::Action(arg0) => f.debug_tuple("Action").field(arg0).finish(),
|
||||||
Self::SetCursor(arg0) => {
|
Self::SetCursor(arg0, arg1) => {
|
||||||
f.debug_tuple("SetCursor").field(arg0).finish()
|
f.debug_tuple("SetCursor").field(arg0).field(arg1).finish()
|
||||||
}
|
}
|
||||||
|
Self::SetCursorVisible(arg0, arg1) => f
|
||||||
|
.debug_tuple("SetCursorVisible")
|
||||||
|
.field(arg0)
|
||||||
|
.field(arg1)
|
||||||
|
.finish(),
|
||||||
Self::RequestRedraw(arg0) => {
|
Self::RequestRedraw(arg0) => {
|
||||||
f.debug_tuple("RequestRedraw").field(arg0).finish()
|
f.debug_tuple("RequestRedraw").field(arg0).finish()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -155,6 +155,9 @@ pub enum SctkEvent {
|
||||||
parent_id: WlSurface,
|
parent_id: WlSurface,
|
||||||
/// the id of this popup
|
/// the id of this popup
|
||||||
id: WlSurface,
|
id: WlSurface,
|
||||||
|
/// Window id of the parent surface, as passed in the popup settings. Needed to route
|
||||||
|
/// `Done` to the parent's widgets.
|
||||||
|
parent_window: SurfaceId,
|
||||||
},
|
},
|
||||||
|
|
||||||
SubsurfaceEvent(SubsurfaceEventVariant),
|
SubsurfaceEvent(SubsurfaceEventVariant),
|
||||||
|
|
@ -920,6 +923,7 @@ impl SctkEvent {
|
||||||
SctkEvent::PopupEvent {
|
SctkEvent::PopupEvent {
|
||||||
variant,
|
variant,
|
||||||
id: surface,
|
id: surface,
|
||||||
|
parent_window,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
match variant {
|
match variant {
|
||||||
|
|
@ -958,6 +962,8 @@ impl SctkEvent {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
|
// tell the parent's widgets so menus can reset when the compositor dismisses them.
|
||||||
|
events.push((Some(parent_window), e.1.clone()));
|
||||||
events.push(e)
|
events.push(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,10 @@ impl winit::window::Window for SctkWinitWindow {
|
||||||
fn set_cursor(&self, cursor: winit_core::cursor::Cursor) {
|
fn set_cursor(&self, cursor: winit_core::cursor::Cursor) {
|
||||||
match cursor {
|
match cursor {
|
||||||
winit_core::cursor::Cursor::Icon(icon) => {
|
winit_core::cursor::Cursor::Icon(icon) => {
|
||||||
_ = self.tx.send(Action::SetCursor(icon));
|
_ = self.tx.send(Action::SetCursor(
|
||||||
|
self.surface.wl_surface().id(),
|
||||||
|
icon,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
winit_core::cursor::Cursor::Custom(_) => {
|
winit_core::cursor::Cursor::Custom(_) => {
|
||||||
// TODO
|
// TODO
|
||||||
|
|
@ -92,7 +95,10 @@ impl winit::window::Window for SctkWinitWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_cursor_visible(&self, visible: bool) {
|
fn set_cursor_visible(&self, visible: bool) {
|
||||||
// TODO
|
_ = self.tx.send(Action::SetCursorVisible(
|
||||||
|
self.surface.wl_surface().id(),
|
||||||
|
visible,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn surface_size(&self) -> winit::dpi::PhysicalSize<u32> {
|
fn surface_size(&self) -> winit::dpi::PhysicalSize<u32> {
|
||||||
|
|
|
||||||
|
|
@ -291,11 +291,11 @@ where
|
||||||
pub fn update_mouse(&mut self, interaction: mouse::Interaction) {
|
pub fn update_mouse(&mut self, interaction: mouse::Interaction) {
|
||||||
if interaction != self.mouse_interaction {
|
if interaction != self.mouse_interaction {
|
||||||
if let Some(icon) = conversion::mouse_interaction(interaction) {
|
if let Some(icon) = conversion::mouse_interaction(interaction) {
|
||||||
self.raw.set_cursor(icon.into());
|
|
||||||
|
|
||||||
if self.mouse_interaction == mouse::Interaction::Hidden {
|
if self.mouse_interaction == mouse::Interaction::Hidden {
|
||||||
self.raw.set_cursor_visible(true);
|
self.raw.set_cursor_visible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.raw.set_cursor(icon.into());
|
||||||
} else {
|
} else {
|
||||||
self.raw.set_cursor_visible(false);
|
self.raw.set_cursor_visible(false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue