1
0
Fork 0
forked from wry/wry
wry/src/tree/placeholder.rs
2025-10-01 14:05:33 +02:00

286 lines
8.1 KiB
Rust

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<bool>,
update_textures_scheduled: Cell<bool>,
state: Rc<State>,
location: Cell<Option<NodeLocation>>,
pub textures: RefCell<SmallMapMut<Scale, TextTexture, 2>>,
}
pub async fn placeholder_render_textures(state: Rc<State>) {
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<State>, node: Rc<dyn ToplevelNode>, slf: &Weak<Self>) -> 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<State>, slf: &Weak<Self>) -> 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<Self>) {
if !self.update_textures_scheduled.replace(true) {
self.state
.pending_placeholder_render_textures
.push(self.clone());
}
}
fn update_texture_phase1(&self) -> Rc<AsyncEvent> {
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<Self>, 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<Rc<OutputNode>> {
self.toplevel.output_opt()
}
fn node_location(&self) -> Option<NodeLocation> {
self.location.get()
}
fn node_layer(&self) -> NodeLayerLink {
self.toplevel.node_layer()
}
fn node_do_focus(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, _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<FoundNode>,
_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<Rc<Client>> {
self.toplevel.client.clone()
}
fn node_toplevel(self: Rc<Self>) -> Option<Rc<dyn ToplevelNode>> {
Some(self)
}
fn node_make_visible(self: Rc<Self>) {
self.toplevel.make_visible(&*self);
}
fn node_on_pointer_enter(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, _x: Fixed, _y: Fixed) {
seat.pointer_cursor().set_known(KnownCursor::Default);
seat.enter_toplevel(self.clone());
}
fn node_into_toplevel(self: Rc<Self>) -> Option<Rc<dyn ToplevelNode>> {
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<WorkspaceNode>) {
self.location.set(ws.node_location());
}
fn tl_change_extents_impl(self: Rc<Self>, 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<Self>) {
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<Self>) -> Rc<dyn ToplevelNode> {
self
}
fn tl_admits_children(&self) -> bool {
false
}
fn tl_tile_drag_destination(
self: Rc<Self>,
source: NodeId,
split: Option<ContainerSplit>,
abs_bounds: Rect,
x: i32,
y: i32,
) -> Option<TileDragDestination> {
default_tile_drag_destination(self, source, split, abs_bounds, x, y)
}
}