spell_framework/wayland_adapter/window/
popup.rs1use slint::platform::WindowAdapter;
2use smithay_client_toolkit::{
3 reexports::{
4 client::{
5 QueueHandle,
6 protocol::{wl_shm, wl_surface::WlSurface},
7 },
8 protocols::xdg::shell::client::xdg_surface::XdgSurface,
9 },
10 shell::xdg::popup::Popup,
11 shm::slot::{Buffer, SlotPool},
12};
13use std::{
14 cell::{Cell, RefCell},
15 collections::HashMap,
16 rc::Rc,
17};
18use tracing::{info, warn};
19
20use crate::{
21 PopupSlint,
22 configure::{PopupConf, PopupCore},
23 slint_adapter::{ADAPTERS, SpellSkiaWinAdapter},
24 wayland_adapter::{
25 SpellWin,
26 fractional_scaling::{
27 FractionalScaleHandler, FractionalScaleState, delegate_fractional_scale,
28 },
29 viewporter::{ViewporterState, delegate_viewporter},
30 },
31};
32
33pub(super) struct PopupManager {
34 id_gen: u32,
35 popups: HashMap<u32, Box<dyn PopupSlint>>,
36 pool: Option<Rc<RefCell<SlotPool>>>,
37}
38
39impl PopupManager {
40 pub(super) fn new() -> Self {
41 PopupManager {
42 id_gen: 0,
43 popups: HashMap::new(),
44 pool: None,
45 }
46 }
47
48 pub(super) fn return_popup(&self, popup_inner: &Popup) -> Option<&dyn PopupSlint> {
49 for popup in self.popups.values() {
50 if popup_inner == popup.inner() {
51 return Some(popup.as_ref());
52 }
53 }
54 None
55 }
56
57 pub(super) fn set_pool(&mut self, pool: Rc<RefCell<SlotPool>>) {
58 self.pool = Some(pool);
59 }
60
61 pub(super) fn create_popup_core(
62 &mut self,
63 popup: Popup,
64 popup_conf: PopupConf,
65 _fractional_scale_state: &FractionalScaleState,
66 _viewporter_state: &ViewporterState,
67 _qh: &QueueHandle<SpellWin>,
68 ) -> PopupCore {
69 let stride = popup_conf.width as i32 * 4;
72 let (buffer, _) = self
73 .pool
74 .as_ref()
75 .unwrap()
76 .borrow_mut()
77 .create_buffer(
78 popup_conf.width as i32,
79 popup_conf.height as i32,
80 stride,
81 wl_shm::Format::Argb8888,
82 )
83 .expect("failed to create buffer for popup");
84 popup.wl_surface().commit();
96 PopupCore {
104 pool: self.pool.as_ref().unwrap().clone(),
105 popup,
106 popup_conf,
107 buffer,
108 }
110 }
111
112 pub(super) fn add_popup<T: PopupSlint + 'static>(&mut self, popup_instance: T) -> u32 {
113 self.popups.insert(self.id_gen, Box::new(popup_instance));
114 info!("[Popup Manager]: Popup added for rendering");
115 self.id_gen = self.id_gen.wrapping_add(1);
116 self.id_gen - 1
117 }
118
119 pub(super) fn redraw_popups(&self, qh: &QueueHandle<SpellWin>) {
120 for popup in self.popups.values() {
121 popup.converter_popup(popup.inner().wl_surface(), qh);
122 }
123 }
124
125 pub(super) fn return_adapter(
126 &self,
127 surface: &WlSurface,
128 ) -> Option<&std::rc::Rc<SpellSkiaWinAdapter>> {
129 for popup in self.popups.values() {
130 if popup.inner().wl_surface() == surface {
131 return Some(popup.adapter());
132 }
133 }
134 None
135 }
136
137 pub(super) fn call_ack(&self, xdg_surface: &XdgSurface, serial: u32) {
138 for popup in self.popups.values() {
139 if popup.inner().xdg_surface() == xdg_surface {
140 popup.inner().xdg_surface().ack_configure(serial);
141 }
142 }
143 }
144
145 pub(super) fn close_popup(&mut self, id: &u32) {
146 if let Some(rem_popup) = self.popups.remove(id) {
147 rem_popup.inner().xdg_popup().destroy();
148 info!("Removed Popup with id: {}", id);
149 } else {
150 warn!(
151 "[PopupManager]: trying to remove a non-existant popup with id: {}",
152 id
153 );
154 };
155 }
156}
157
158pub struct SpellXDGPopup {
165 adapter: Rc<SpellSkiaWinAdapter>,
166 popup: Popup,
167 buffer: Buffer,
168 first_configure: Cell<bool>,
169 }
171
172delegate_fractional_scale!(SpellXDGPopup);
173delegate_viewporter!(SpellXDGPopup);
174
175impl SpellXDGPopup {
176 pub fn new(popup_settings: PopupCore) -> Self {
178 let adapter_value: Rc<SpellSkiaWinAdapter> = SpellSkiaWinAdapter::new(
179 popup_settings.pool,
180 RefCell::new(popup_settings.buffer.slot()),
181 popup_settings.popup_conf.width,
182 popup_settings.popup_conf.height,
183 );
184 ADAPTERS.with_borrow_mut(|v| v.push(adapter_value.clone()));
185 SpellXDGPopup {
186 adapter: adapter_value,
187 popup: popup_settings.popup,
188 buffer: popup_settings.buffer,
189 first_configure: Cell::new(true),
190 }
192 }
193
194 pub fn popup(&self) -> &Popup {
196 &self.popup
197 }
198
199 pub fn first_configure(&self) -> bool {
201 if self.first_configure.get() {
202 self.first_configure.set(false);
203 true
204 } else {
205 false
206 }
207 }
208
209 pub fn adapter(&self) -> &std::rc::Rc<SpellSkiaWinAdapter> {
211 &self.adapter
212 }
213
214 pub fn converter_popup<'a>(&self, wl_surface: &'a WlSurface, qh: &'a QueueHandle<SpellWin>) {
216 slint::platform::update_timers_and_animations();
217 let width: u32 = self.adapter.as_ref().size.get().width;
218 let height: u32 = self.adapter.as_ref().size.get().height;
219 let window_adapter = self.adapter.clone();
220
221 let redraw_val: bool = window_adapter.draw_if_needed();
222 let buffer = &self.buffer;
223 if self.first_configure.get() || redraw_val {
224 wl_surface.damage_buffer(0, 0, width as i32, height as i32);
227 wl_surface.attach(Some(buffer.wl_buffer()), 0, 0);
245 wl_surface.frame(qh, wl_surface.clone());
246 wl_surface.commit();
247 } else {
248 wl_surface.commit();
249 }
250 }
251}
252
253impl FractionalScaleHandler for SpellXDGPopup {
254 fn preferred_scale(
255 &mut self,
256 _: &smithay_client_toolkit::reexports::client::Connection,
257 _: &QueueHandle<Self>,
258 _: &WlSurface,
259 scale: u32,
260 ) {
261 info!(
262 "Scale factor of popup changed, invoked from custom trait: {}",
263 scale
264 );
265 let _width_old = self.adapter.size_original.get().width;
267 let _height_old = self.adapter.size_original.get().height;
268 self.popup.wl_surface().damage_buffer(
269 0,
270 0,
271 self.adapter.size.get().width as i32,
272 self.adapter.size.get().height as i32,
273 );
274 let (buffer, _width, _height, scale_factor) = self.adapter.changed_scale_factor(scale);
276 self.buffer = buffer;
279 self.adapter
280 .try_dispatch_event(slint::platform::WindowEvent::ScaleFactorChanged { scale_factor })
281 .unwrap();
282 self.adapter.request_redraw();
292 self.popup.wl_surface().commit();
293 }
294}