use { crate::{ client::Client, cursor::KnownCursor, fixed::Fixed, ifs::wl_seat::{NodeSeatState, WlSeatGlobal}, rect::Rect, renderer::Renderer, scale::Scale, state::State, text::TextTexture, tree::{ ContainerSplit, Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId, NodeLayerLink, NodeLocation, NodeVisitor, OutputNode, TileDragDestination, ToplevelData, ToplevelNode, ToplevelNodeBase, ToplevelType, WorkspaceNode, default_tile_drag_destination, }, utils::{ asyncevent::AsyncEvent, errorfmt::ErrorFmt, on_drop_event::OnDropEvent, smallmap::SmallMapMut, }, }, std::{ cell::{Cell, RefCell}, ops::Deref, rc::{Rc, Weak}, sync::Arc, }, }; tree_id!(PlaceholderNodeId); pub struct PlaceholderNode { id: PlaceholderNodeId, toplevel: ToplevelData, destroyed: Cell, update_textures_scheduled: Cell, state: Rc, location: Cell>, pub textures: RefCell>, } pub async fn placeholder_render_textures(state: Rc) { loop { let container = state.pending_placeholder_render_textures.pop().await; container.update_textures_scheduled.set(false); container.update_texture_phase1().triggered().await; container.update_texture_phase2(); } } impl PlaceholderNode { pub fn new_for(state: &Rc, node: Rc, slf: &Weak) -> Self { let id = state.node_ids.next(); Self { id, toplevel: ToplevelData::new( state, node.tl_data().title.borrow().clone(), node.node_client(), ToplevelType::Placeholder(Some(node.tl_data().identifier.get())), id, slf, ), destroyed: Default::default(), update_textures_scheduled: Cell::new(false), state: state.clone(), location: Cell::new(node.node_location()), textures: Default::default(), } } pub fn new_empty(state: &Rc, slf: &Weak) -> Self { let id = state.node_ids.next(); Self { id, toplevel: ToplevelData::new( state, String::new(), None, ToplevelType::Placeholder(None), id, slf, ), destroyed: Default::default(), update_textures_scheduled: Default::default(), state: state.clone(), location: Default::default(), textures: Default::default(), } } pub fn is_destroyed(&self) -> bool { self.destroyed.get() } pub fn schedule_update_texture(self: &Rc) { if !self.update_textures_scheduled.replace(true) { self.state .pending_placeholder_render_textures .push(self.clone()); } } fn update_texture_phase1(&self) -> Rc { let on_completed = Rc::new(OnDropEvent::default()); let Some(ctx) = self.toplevel.state.render_ctx.get() else { return on_completed.event(); }; let scales = self.toplevel.state.scales.lock(); let rect = self.toplevel.pos.get(); let mut textures = self.textures.borrow_mut(); for (scale, _) in scales.iter() { let tex = textures.get_or_insert_with(*scale, || TextTexture::new(&self.state, &ctx)); let mut width = rect.width(); let mut height = rect.height(); if *scale != 1 { let scale = scale.to_f64(); width = (width as f64 * scale).round() as _; height = (height as f64 * scale).round() as _; } if width != 0 && height != 0 { let font = Arc::new(format!("monospace {}", width / 10)); tex.schedule_render_fitting( on_completed.clone(), Some(height), &font, "Fullscreen", self.toplevel.state.theme.colors.unfocused_title_text.get(), false, None, ); } } on_completed.event() } fn update_texture_phase2(&self) { let textures = &*self.textures.borrow(); for (_, texture) in textures { if let Err(e) = texture.flip() { log::warn!("Could not render fullscreen texture: {}", ErrorFmt(e)); } } if self.node_visible() { self.state.damage(self.node_absolute_position()); } } } impl Node for PlaceholderNode { fn node_id(&self) -> NodeId { self.id.into() } fn node_seat_state(&self) -> &NodeSeatState { &self.toplevel.seat_state } fn node_visit(self: Rc, visitor: &mut dyn NodeVisitor) { visitor.visit_placeholder(&self); } fn node_visit_children(&self, _visitor: &mut dyn NodeVisitor) { // nothing } fn node_visible(&self) -> bool { self.toplevel.visible.get() } fn node_absolute_position(&self) -> Rect { self.toplevel.pos.get() } fn node_output(&self) -> Option> { self.toplevel.output_opt() } fn node_location(&self) -> Option { self.location.get() } fn node_layer(&self) -> NodeLayerLink { self.toplevel.node_layer() } fn node_do_focus(self: Rc, seat: &Rc, _direction: Direction) { seat.focus_toplevel(self.clone()); } fn node_active_changed(&self, active: bool) { self.toplevel.update_self_active(self, active); } fn node_find_tree_at( &self, _x: i32, _y: i32, _tree: &mut Vec, _usecase: FindTreeUsecase, ) -> FindTreeResult { FindTreeResult::AcceptsInput } fn node_render(&self, renderer: &mut Renderer, x: i32, y: i32, bounds: Option<&Rect>) { renderer.render_placeholder(self, x, y, bounds); } fn node_client(&self) -> Option> { self.toplevel.client.clone() } fn node_toplevel(self: Rc) -> Option> { Some(self) } fn node_make_visible(self: Rc) { self.toplevel.make_visible(&*self); } fn node_on_pointer_enter(self: Rc, seat: &Rc, _x: Fixed, _y: Fixed) { seat.pointer_cursor().set_known(KnownCursor::Default); seat.enter_toplevel(self.clone()); } fn node_into_toplevel(self: Rc) -> Option> { Some(self) } fn node_is_placeholder(&self) -> bool { true } } impl ToplevelNodeBase for PlaceholderNode { fn tl_data(&self) -> &ToplevelData { &self.toplevel } fn tl_set_workspace_ext(&self, ws: &Rc) { self.location.set(ws.node_location()); } fn tl_change_extents_impl(self: Rc, rect: &Rect) { self.toplevel.pos.set(*rect); if let Some(p) = self.toplevel.parent.get() { p.node_child_size_changed(self.deref(), rect.width(), rect.height()); } self.schedule_update_texture(); } fn tl_close(self: Rc) { let slf = self.clone(); self.toplevel.state.run_toplevel.schedule(move || { slf.tl_destroy(); }); } fn tl_set_visible_impl(&self, _visible: bool) { // nothing } fn tl_destroy_impl(&self) { self.destroyed.set(true); } fn tl_last_active_child(self: Rc) -> Rc { self } fn tl_admits_children(&self) -> bool { false } fn tl_tile_drag_destination( self: Rc, source: NodeId, split: Option, abs_bounds: Rect, x: i32, y: i32, ) -> Option { default_tile_drag_destination(self, source, split, abs_bounds, x, y) } }