use crate::client::Client;
use crate::cursor::KnownCursor;
use crate::fixed::Fixed;
use crate::ifs::wl_seat::NodeSeatState;
use crate::ifs::wl_seat::WlSeatGlobal;
use crate::rect::Rect;
use crate::renderer::Renderer;
use crate::scale::Scale;
use crate::state::GfxCtxChangedListener;
use crate::state::OutputEventListener;
use crate::state::ScalesChangedListener;
use crate::state::State;
use crate::text::TextTexture;
use crate::transactions::TransactionData;
use crate::transactions::Transactionable;
use crate::transactions::TransactionableExt;
use crate::tree::ContainerSplit;
use crate::tree::Direction;
use crate::tree::FindTreeResult;
use crate::tree::FindTreeUsecase;
use crate::tree::FoundNode;
use crate::tree::NodeBase;
use crate::tree::NodeId;
use crate::tree::NodeLayerLink;
use crate::tree::NodeLocation;
use crate::tree::NodeVisitor;
use crate::tree::OutputNode;
use crate::tree::TileDragDestination;
use crate::tree::ToplevelData;
use crate::tree::ToplevelDataTransactionOp;
use crate::tree::ToplevelNode;
use crate::tree::ToplevelNodeBase;
use crate::tree::ToplevelType;
use crate::tree::TreeTimeline;
use crate::tree::TreeTimeline::LiveTL;
use crate::tree::TreeTimeline::RenderTL;
use crate::tree::WorkspaceEventListener;
use crate::tree::WorkspaceNode;
use crate::tree::default_tile_drag_destination;
use crate::utils::asyncevent::AsyncEvent;
use crate::utils::errorfmt::ErrorFmt;
use crate::utils::event_listener::EventListener;
use crate::utils::fuse::fuse_inode::FuseInodeWithKey;
use crate::utils::on_drop_event::OnDropEvent;
use crate::utils::smallmap::SmallMapMut;
use jay_proc::GetLiveness;
use std::cell::Cell;
use std::cell::RefCell;
use std::ops::Deref;
use std::rc::Rc;
use std::rc::Weak;
use std::sync::Arc;
mod placeholder_dfs_g_fuse;
tree_id!(PlaceholderNodeId);
#[derive(GetLiveness)]
pub struct PlaceholderNode {
id: PlaceholderNodeId,
#[liveness]
toplevel: ToplevelData,
destroyed: Cell<bool>,
update_textures_scheduled: Cell<bool>,
state: Rc<State>,
location: Cell<Option<NodeLocation>>,
pub textures: RefCell<SmallMapMut<Scale, TextTexture, 2>>,
transaction_data: TransactionData<PlaceholderTransactionOp>,
_gfx_ctx_listener: EventListener<dyn GfxCtxChangedListener>,
_scales_listener: EventListener<dyn ScalesChangedListener>,
}
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(),
transaction_data: TransactionData::new(&state.tree),
_gfx_ctx_listener: EventListener::attached(slf.clone(), &state.gfx_ctx_changed),
_scales_listener: EventListener::attached(slf.clone(), &state.scales_changed),
}
}
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(),
transaction_data: TransactionData::new(&state.tree),
_gfx_ctx_listener: EventListener::attached(slf.clone(), &state.gfx_ctx_changed),
_scales_listener: EventListener::attached(slf.clone(), &state.scales_changed),
}
}
pub fn is_destroyed(&self) -> bool {
self.destroyed.get()
}
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.content_size.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::from(format!("monospace {}", width / 10).into_boxed_str());
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(RenderTL) {
self.state.damage(self.node_absolute_position(RenderTL));
}
}
}
impl NodeBase 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) {
}
fn node_visible(&self, tl: TreeTimeline) -> bool {
self.toplevel.visible[tl].get()
}
fn node_absolute_position(&self, _tl: TreeTimeline) -> Rect {
self.toplevel.content_size.get()
}
fn node_output(&self) -> Option<Rc<OutputNode>> {
self.toplevel.output_opt(LiveTL)
}
fn node_workspace(&self) -> Option<Rc<WorkspaceNode>> {
self.toplevel.workspace[LiveTL].get()
}
fn node_location(&self) -> Option<NodeLocation> {
self.location.get()
}
fn node_layer(&self) -> NodeLayerLink {
self.toplevel.node_layer()
}
fn node_debugfs(self: Rc<Self>) -> FuseInodeWithKey {
self.debugfs()
}
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.content_size.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) {
}
fn tl_destroy_impl(self: &Rc<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)
}
fn tl_schedule_data_op(self: Rc<Self>, op: ToplevelDataTransactionOp) {
self.add_transaction_op(PlaceholderTransactionOp::ToplevelData(op));
}
}
impl OutputEventListener for PlaceholderNode {
fn scale_changed(self: Rc<Self>, _on: &Rc<OutputNode>, _scale: Scale) {
self.toplevel.scale_changed();
}
}
impl WorkspaceEventListener for PlaceholderNode {
fn output_changed(
self: Rc<Self>,
_ws: &Rc<WorkspaceNode>,
old: &Rc<OutputNode>,
new: &Rc<OutputNode>,
) {
self.toplevel.workspace_output_changed(old, new);
}
}
impl GfxCtxChangedListener for PlaceholderNode {
fn handle_gfx_context_change(self: Rc<Self>) {
self.textures.borrow_mut().clear();
self.schedule_update_texture();
}
}
impl ScalesChangedListener for PlaceholderNode {
fn changed(self: Rc<Self>) {
self.textures.borrow_mut().clear();
self.schedule_update_texture();
}
}
pub enum PlaceholderTransactionOp {
ToplevelData(ToplevelDataTransactionOp),
}
impl Transactionable for PlaceholderNode {
type T = PlaceholderTransactionOp;
fn data(&self) -> &TransactionData<Self::T> {
&self.transaction_data
}
fn apply(self: &Rc<Self>, op: Self::T) {
match op {
PlaceholderTransactionOp::ToplevelData(v) => {
self.toplevel.run_op(v);
}
}
}
}