From 292fa50fe703d217ee02c5c8dfe2b867e1717785 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Sun, 20 Feb 2022 21:53:46 +0100 Subject: [PATCH] autocommit 2022-02-20 21:53:46 CET --- default-config/src/lib.rs | 4 + i4config/src/_private/client.rs | 4 + i4config/src/_private/ipc.rs | 3 + i4config/src/lib.rs | 4 + src/config/handler.rs | 61 +++- src/ifs/wl_seat.rs | 4 + src/ifs/wl_surface.rs | 44 +-- src/ifs/wl_surface/xdg_surface.rs | 10 + .../wl_surface/xdg_surface/xdg_toplevel.rs | 113 +++----- src/main.rs | 10 +- src/render/renderer/renderer.rs | 70 +++-- src/state.rs | 73 ++++- src/theme.rs | 16 +- src/tree/container.rs | 41 ++- src/tree/float.rs | 260 ++++++++++++++++++ src/tree/mod.rs | 120 +------- src/tree/walker.rs | 31 ++- src/tree/workspace.rs | 6 - 18 files changed, 594 insertions(+), 280 deletions(-) create mode 100644 src/tree/float.rs diff --git a/default-config/src/lib.rs b/default-config/src/lib.rs index 00cbe3cd..11ecce9f 100644 --- a/default-config/src/lib.rs +++ b/default-config/src/lib.rs @@ -58,6 +58,10 @@ fn configure_seat(s: Seat) { s.focus_parent(); }); + s.bind(MOD | SHIFT | SYM_f, move || { + s.toggle_floating(); + }); + s.bind(MOD | SHIFT | SYM_h, move || s.move_(Left)); s.bind(MOD | SHIFT | SYM_j, move || s.move_(Down)); s.bind(MOD | SHIFT | SYM_k, move || s.move_(Up)); diff --git a/i4config/src/_private/client.rs b/i4config/src/_private/client.rs index fbf49e03..b39b98b9 100644 --- a/i4config/src/_private/client.rs +++ b/i4config/src/_private/client.rs @@ -196,6 +196,10 @@ impl Client { } } + pub fn toggle_floating(&self, seat: Seat) { + self.send(&ClientMessage::ToggleFloating { seat }); + } + pub fn set_title_color(&self, color: Color) { self.send(&ClientMessage::SetTitleColor { color }); } diff --git a/i4config/src/_private/ipc.rs b/i4config/src/_private/ipc.rs index d1cbdb71..7c4b17d7 100644 --- a/i4config/src/_private/ipc.rs +++ b/i4config/src/_private/ipc.rs @@ -122,6 +122,9 @@ pub enum ClientMessage<'a> { FocusParent { seat: Seat, }, + ToggleFloating { + seat: Seat, + }, } #[derive(Encode, Decode, Debug)] diff --git a/i4config/src/lib.rs b/i4config/src/lib.rs index 13a04fd3..32428a76 100644 --- a/i4config/src/lib.rs +++ b/i4config/src/lib.rs @@ -138,6 +138,10 @@ impl Seat { pub fn focus_parent(self) { get!().focus_parent(self); } + + pub fn toggle_floating(self) { + get!().toggle_floating(self); + } } pub fn get_seats() -> Vec { diff --git a/src/config/handler.rs b/src/config/handler.rs index 89f582ed..228e593d 100644 --- a/src/config/handler.rs +++ b/src/config/handler.rs @@ -1,8 +1,8 @@ use crate::backend::{KeyboardId, MouseId}; use crate::ifs::wl_seat::{SeatId, WlSeatGlobal}; use crate::state::DeviceHandlerData; -use crate::tree::walker::visit_containers; -use crate::tree::{ContainerSplit, Node}; +use crate::tree::walker::{NodeVisitorBase}; +use crate::tree::{ContainerNode, ContainerSplit, FloatNode, Node}; use crate::utils::copyhashmap::CopyHashMap; use crate::utils::debug_fn::debug_fn; use crate::utils::stack::Stack; @@ -348,6 +348,42 @@ impl ConfigProxyHandler { Ok(()) } + fn handle_toggle_floating(&self, seat: Seat) -> Result<(), FocusParentError> { + let seat = self.get_seat(seat)?; + seat.toggle_floating(); + Ok(()) + } + + fn spaces_change(&self) { + struct V; + impl NodeVisitorBase for V { + fn visit_container(&mut self, node: &Rc) { + node.on_spaces_changed(); + node.visit_children(self); + } + fn visit_float(&mut self, node: &Rc) { + node.on_spaces_changed(); + node.visit_children(self); + } + } + self.state.root.clone().visit(&mut V); + } + + fn colors_change(&self) { + struct V; + impl NodeVisitorBase for V { + fn visit_container(&mut self, node: &Rc) { + node.on_colors_changed(); + node.visit_children(self); + } + fn visit_float(&mut self, node: &Rc) { + node.on_colors_changed(); + node.visit_children(self); + } + } + self.state.root.clone().visit(&mut V); + } + fn handle_set_title_height(&self, height: i32) -> Result<(), SetTitleHeightError> { if height < 0 { return Err(SetTitleHeightError::Negative(height)); @@ -356,10 +392,7 @@ impl ConfigProxyHandler { return Err(SetTitleHeightError::Excessive(height)); } self.state.theme.title_height.set(height); - self.state - .root - .clone() - .visit(&mut visit_containers(|c| c.on_theme_changed())); + self.spaces_change(); Ok(()) } @@ -371,15 +404,13 @@ impl ConfigProxyHandler { return Err(SetBorderWidthError::Excessive(width)); } self.state.theme.border_width.set(width); - self.state - .root - .clone() - .visit(&mut visit_containers(|c| c.on_theme_changed())); + self.spaces_change(); Ok(()) } fn handle_set_title_color(&self, color: i4config::theme::Color) { self.state.theme.title_color.set(color.into()); + self.colors_change(); } fn handle_set_border_color(&self, color: i4config::theme::Color) { @@ -450,6 +481,7 @@ impl ConfigProxyHandler { ClientMessage::GetBorderWidth => self.handle_get_border_width(), ClientMessage::CreateSplit { seat, axis } => self.handle_create_split(seat, axis)?, ClientMessage::FocusParent { seat } => self.handle_focus_parent(seat)?, + ClientMessage::ToggleFloating { seat } => self.handle_toggle_floating(seat)?, } Ok(()) } @@ -487,6 +519,8 @@ enum CphError { CreateSplitError(#[from] CreateSplitError), #[error("Could not process a `focus_parent` request")] FocusParentError(#[from] FocusParentError), + #[error("Could not process a `toggle_floating` request")] + ToggleFloatingError(#[from] ToggleFloatingError), #[error("Could not process a `set_title_height` request")] SetTitleHeightError(#[from] SetTitleHeightError), #[error("Could not process a `set_border_width` request")] @@ -625,3 +659,10 @@ enum FocusParentError { CphError(#[from] Box), } efrom!(FocusParentError, CphError); + +#[derive(Debug, Error)] +enum ToggleFloatingError { + #[error(transparent)] + CphError(#[from] Box), +} +efrom!(ToggleFloatingError, CphError); diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index 9c3e5ed6..d3760378 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -203,6 +203,10 @@ impl WlSeatGlobal { self.keyboard_node.get().focus_parent(self); } + pub fn toggle_floating(self: &Rc) { + self.keyboard_node.get().toggle_floating(self); + } + pub fn get_rate(&self) -> (i32, i32) { self.repeat_rate.get() } diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index 0d1b2f8c..599b284c 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -579,12 +579,6 @@ dedicated_add_obj!(WlSurface, WlSurfaceId, surfaces); tree_id!(SurfaceNodeId); impl Node for WlSurface { - fn focus_parent(&self, seat: &Rc) { - if let Some(xdg) = self.xdg.get() { - xdg.focus_parent(seat); - } - } - fn id(&self) -> NodeId { self.node_id.into() } @@ -617,6 +611,19 @@ impl Node for WlSurface { self.seat_state.destroy_node(self); } + fn visit(self: Rc, visitor: &mut dyn NodeVisitor) { + visitor.visit_surface(&self); + } + + fn visit_children(&self, visitor: &mut dyn NodeVisitor) { + let children = self.children.borrow_mut(); + if let Some(c) = children.deref() { + for child in c.subsurfaces.values() { + visitor.visit_surface(&child.surface); + } + } + } + fn get_parent_split(&self) -> Option { self.xdg.get().and_then(|x| x.get_split()) } @@ -678,6 +685,18 @@ impl Node for WlSurface { seat.focus_surface(&self); } + fn focus_parent(&self, seat: &Rc) { + if let Some(xdg) = self.xdg.get() { + xdg.focus_parent(seat); + } + } + + fn toggle_floating(self: Rc, seat: &Rc) { + if let Some(xdg) = self.xdg.get() { + xdg.toggle_floating(seat); + } + } + fn unfocus(&self, seat: &WlSeatGlobal) { seat.unfocus_surface(self); } @@ -721,19 +740,6 @@ impl Node for WlSurface { fn dnd_motion(&self, dnd: &Dnd, x: Fixed, y: Fixed) { dnd.seat.dnd_surface_motion(self, dnd, x, y); } - - fn visit(self: Rc, visitor: &mut dyn NodeVisitor) { - visitor.visit_surface(&self); - } - - fn visit_children(&self, visitor: &mut dyn NodeVisitor) { - let children = self.children.borrow_mut(); - if let Some(c) = children.deref() { - for child in c.subsurfaces.values() { - visitor.visit_surface(&child.surface); - } - } - } } #[derive(Debug, Error)] diff --git a/src/ifs/wl_surface/xdg_surface.rs b/src/ifs/wl_surface/xdg_surface.rs index 9ef7ef94..eb2ec6ef 100644 --- a/src/ifs/wl_surface/xdg_surface.rs +++ b/src/ifs/wl_surface/xdg_surface.rs @@ -79,6 +79,10 @@ pub trait XdgSurfaceExt: Debug { let _ = seat; } + fn toggle_floating(self: Rc, seat: &Rc) { + let _ = seat; + } + fn get_split(&self) -> Option { None } @@ -221,6 +225,12 @@ impl XdgSurface { } } + pub fn toggle_floating(&self, seat: &Rc) { + if let Some(ext) = self.ext.get() { + ext.toggle_floating(seat); + } + } + pub fn focus_surface(&self, seat: &WlSeatGlobal) -> Rc { self.focus_surface .get(&seat.id()) diff --git a/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs b/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs index 7679da5b..b9ab648c 100644 --- a/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs +++ b/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs @@ -10,7 +10,7 @@ use crate::rect::Rect; use crate::render::Renderer; use crate::tree::walker::NodeVisitor; use crate::tree::{ContainerNode, ContainerSplit, FindTreeResult}; -use crate::tree::{FloatNode, FoundNode, Node, NodeId, ToplevelNodeId, WorkspaceNode}; +use crate::tree::{FoundNode, Node, NodeId, ToplevelNodeId, WorkspaceNode}; use crate::utils::buffd::MsgParser; use crate::utils::buffd::MsgParserError; use crate::utils::clonecell::CloneCell; @@ -311,6 +311,20 @@ impl XdgToplevel { Ok(()) } + fn toggle_floating(self: &Rc) { + let parent = match self.parent_node.get() { + Some(p) => p, + _ => return, + }; + if parent.is_float() { + parent.remove_child(&**self); + self.map_tiled(); + } else if let Some(ws) = self.xdg.workspace.get() { + parent.remove_child(&**self); + self.map_floating(&ws); + } + } + fn notify_parent(&self) { let parent = match self.parent_node.get() { Some(p) => p, @@ -322,90 +336,22 @@ impl XdgToplevel { parent.child_title_changed(self, self.title.borrow_mut().deref()); } - fn map_child(self: &Rc, parent: &XdgToplevel) { - let workspace = match parent.xdg.workspace.get() { - Some(w) => w, - _ => return self.map_tiled(), - }; - self.xdg.set_workspace(&workspace); - let output = workspace.output.get(); - let output_rect = output.position.get(); - log::info!("or = {:?}", output_rect); + fn map_floating(self: &Rc, workspace: &Rc) { let extents = self.xdg.extents.get(); - let position = { - let width = extents.width(); - let height = extents.height(); - let mut x1 = output_rect.x1(); - let mut y1 = output_rect.y1(); - if width < output_rect.width() { - x1 += (output_rect.width() - width) as i32 / 2; - } - if height < output_rect.height() { - y1 += (output_rect.height() - height) as i32 / 2; - } - Rect::new_sized(x1, y1, width, height).unwrap() - }; - log::info!("pos = {:?}", position); let state = &self.xdg.surface.client.state; - let floater = Rc::new(FloatNode { - id: state.node_ids.next(), - visible: Cell::new(true), - position: Cell::new(position), - display: output.display.clone(), - display_link: Cell::new(None), - workspace_link: Cell::new(None), - workspace: CloneCell::new(workspace.clone()), - child: CloneCell::new(Some(self.clone())), - seat_state: Default::default(), - }); - self.parent_node.set(Some(floater.clone())); - floater - .display_link - .set(Some(state.root.stacked.add_last(floater.clone()))); - floater - .workspace_link - .set(Some(workspace.stacked.add_last(floater.clone()))); - self.notify_parent(); + state.map_floating(self.clone(), extents.width(), extents.height(), workspace); + } + + fn map_child(self: &Rc, parent: &XdgToplevel) { + match parent.xdg.workspace.get() { + Some(w) => self.map_floating(&w), + _ => self.map_tiled(), + } } fn map_tiled(self: &Rc) { let state = &self.xdg.surface.client.state; - let seat = state.seat_queue.last(); - if let Some(seat) = seat { - if let Some(prev) = seat.last_tiled_keyboard_toplevel() { - if let Some(container) = prev.parent_node.get() { - if let Some(container) = container.into_container() { - container.add_child_after(&*prev, self.clone()); - self.parent_node.set(Some(container)); - return; - } - } - } - } - let output = { - let outputs = state.root.outputs.lock(); - outputs.values().next().cloned() - }; - if let Some(output) = output { - if let Some(workspace) = output.workspace.get() { - if let Some(container) = workspace.container.get() { - container.append_child(self.clone()); - self.parent_node.set(Some(container)); - } else { - let container = Rc::new(ContainerNode::new( - state, - &workspace, - workspace.clone(), - self.clone(), - ContainerSplit::Horizontal, - )); - workspace.set_container(&container); - self.parent_node.set(Some(container)); - }; - return; - } - } - todo!("map_tiled"); + state.map_tiled(self.clone()); } } @@ -526,6 +472,10 @@ impl XdgSurfaceExt for XdgToplevel { self.parent_node.get().map(|p| p.focus_self(seat)); } + fn toggle_floating(self: Rc, _seat: &Rc) { + Self::toggle_floating(&self); + } + fn get_split(&self) -> Option { self.parent_node.get().and_then(|p| p.get_split()) } @@ -543,14 +493,13 @@ impl XdgSurfaceExt for XdgToplevel { Some(pn) => pn, _ => return, }; - let cn = Rc::new(ContainerNode::new( + let cn = ContainerNode::new( &self.xdg.surface.client.state, &ws, pn.clone(), self.clone(), split, - )); - self.parent_node.set(Some(cn.clone())); + ); pn.replace_child(&*self, cn); } diff --git a/src/main.rs b/src/main.rs index a8b5ac6b..9508e19e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,7 +26,7 @@ use crate::ifs::zxdg_decoration_manager_v1::ZxdgDecorationManagerV1Global; use crate::render::RenderError; use crate::sighand::SighandError; use crate::state::State; -use crate::tree::{container_layout, render_titles, DisplayNode, NodeIds}; +use crate::tree::{container_layout, container_titles, DisplayNode, float_layout, float_titles, NodeIds}; use crate::utils::clonecell::CloneCell; use crate::utils::errorfmt::ErrorFmt; use crate::utils::numcell::NumCell; @@ -158,6 +158,8 @@ fn main_() -> Result<(), MainError> { theme: Default::default(), pending_container_layout: Default::default(), pending_container_titles: Default::default(), + pending_float_layout: Default::default(), + pending_float_titles: Default::default(), }); forker.install(&state); let backend = XorgBackend::new(&state)?; @@ -166,8 +168,10 @@ fn main_() -> Result<(), MainError> { state.config.set(Some(Rc::new(config))); let _global_event_handler = engine.spawn(tasks::handle_backend_events(state.clone())); let _slow_client_handler = engine.spawn(tasks::handle_slow_clients(state.clone())); - let _do_layout = engine.spawn2(Phase::Layout, container_layout(state.clone())); - let _render_titles = engine.spawn2(Phase::PostLayout, render_titles(state.clone())); + let _container_do_layout = engine.spawn2(Phase::Layout, container_layout(state.clone())); + let _container_render_titles = engine.spawn2(Phase::PostLayout, container_titles(state.clone())); + let _float_do_layout = engine.spawn2(Phase::Layout, float_layout(state.clone())); + let _float_render_titles = engine.spawn2(Phase::PostLayout, float_titles(state.clone())); let socket_path = Acceptor::install(&state)?; forker.setenv(b"WAYLAND_DISPLAY", socket_path.as_bytes()); el.run()?; diff --git a/src/render/renderer/renderer.rs b/src/render/renderer/renderer.rs index 561d0fbe..d6d35e0c 100644 --- a/src/render/renderer/renderer.rs +++ b/src/render/renderer/renderer.rs @@ -20,12 +20,13 @@ use crate::State; use std::ops::Deref; use std::rc::Rc; use std::slice; +use crate::theme::Color; -const NON_COLOR: (f32, f32, f32) = (0.2, 0.2, 0.2); -const CHILD_COLOR: (f32, f32, f32) = (0.8, 0.8, 0.8); -const YES_COLOR: (f32, f32, f32) = (0.0, 0.0, 1.0); +const NON_COLOR: Color = Color::from_rgbaf(0.2, 0.2, 0.2, 1.0); +const CHILD_COLOR: Color = Color::from_rgbaf(0.8, 0.8, 0.8, 1.0); +const YES_COLOR: Color = Color::from_rgbaf(0.0, 0.0, 1.0, 1.0); -fn focus_color(focus: ContainerFocus) -> (f32, f32, f32) { +fn focus_color(focus: ContainerFocus) -> Color { match focus { ContainerFocus::None => NON_COLOR, ContainerFocus::Child => CHILD_COLOR, @@ -64,7 +65,7 @@ impl Renderer<'_> { 2.0 * (y as f32 / self.fb.height as f32) - 1.0 } - fn fill_boxes(&self, boxes: &[Rect], r: f32, g: f32, b: f32, a: f32) { + fn fill_boxes(&self, boxes: &[Rect], color: &Color) { if boxes.is_empty() { return; } @@ -87,7 +88,7 @@ impl Renderer<'_> { } unsafe { glUseProgram(self.ctx.fill_prog.prog); - glUniform4f(self.ctx.fill_prog_color, r, g, b, a); + glUniform4f(self.ctx.fill_prog_color, color.r, color.g, color.b, color.a); glVertexAttribPointer( self.ctx.fill_prog_pos as _, 2, @@ -116,13 +117,13 @@ impl Renderer<'_> { let space_per_child = cwidth / num_children as i32; let mut rem = cwidth % num_children as i32; let mut pos = x; - let (r, g, b) = focus_color(ContainerFocus::None); - self.fill_boxes(slice::from_ref(&title_rect), r, g, b, 1.0); + let color = focus_color(ContainerFocus::None); + self.fill_boxes(slice::from_ref(&title_rect), &color); let c = self.state.theme.border_color.get(); - self.fill_boxes(slice::from_ref(&underline_rect), c.r, c.g, c.b, c.a); + self.fill_boxes(slice::from_ref(&underline_rect), &c); for child in container.children.iter() { let focus = child.focus.get(); - let (r, g, b) = focus_color(focus); + let color = focus_color(focus); let mut width = space_per_child; if rem > 0 { rem -= 1; @@ -133,7 +134,7 @@ impl Renderer<'_> { } if focus != ContainerFocus::None { let rect = Rect::new_sized(pos, y, width, title_height).unwrap(); - self.fill_boxes(slice::from_ref(&rect), r, g, b, 1.0); + self.fill_boxes(slice::from_ref(&rect), &color); } pos += width as i32; } @@ -208,13 +209,13 @@ impl Renderer<'_> { } { let c = self.state.theme.title_color.get(); - self.fill_boxes(&title_rects, c.r, c.g, c.b, c.a); + self.fill_boxes(&title_rects, &c); let c = self.state.theme.active_title_color.get(); - self.fill_boxes(&active_rects, c.r, c.g, c.b, c.a); + self.fill_boxes(&active_rects, &c); let c = self.state.theme.underline_color.get(); - self.fill_boxes(&underline_rects, c.r, c.g, c.b, c.a); + self.fill_boxes(&underline_rects, &c); let c = self.state.theme.border_color.get(); - self.fill_boxes(&border_rects, c.r, c.g, c.b, c.a); + self.fill_boxes(&border_rects, &c); for (tx, ty, tex) in titles { self.render_texture(&tex, x + tx, y + ty, ARGB8888); } @@ -349,8 +350,43 @@ impl Renderer<'_> { } pub fn render_floating(&mut self, floating: &FloatNode, x: i32, y: i32) { - if let Some(child) = floating.child.get() { - child.render(self, x, y) + let child = match floating.child.get() { + Some(c) => c, + _ => return, + }; + let pos = floating.position.get(); + let theme = &self.state.theme; + let th = theme.title_height.get(); + let bw = theme.border_width.get(); + let bc = theme.border_color.get(); + let tc = match floating.active.get() { + true => theme.active_title_color.get(), + false => theme.title_color.get(), + }; + let uc = theme.underline_color.get(); + let borders = [ + Rect::new_sized(x, y, pos.width(), bw).unwrap(), + Rect::new_sized(x, y, bw, pos.height()).unwrap(), + Rect::new_sized(x + pos.width() - bw, y, bw, pos.height()).unwrap(), + Rect::new_sized(x, y + pos.height() - bw, pos.width(), bw).unwrap(), + ]; + self.fill_boxes(&borders, &bc); + let title = [ + Rect::new_sized(x + bw, y + bw, pos.width() - 2 * bw, th).unwrap(), + ]; + self.fill_boxes(&title, &tc); + let title_underline = [ + Rect::new_sized(x + bw, y + bw + th, pos.width() - 2 * bw, 1).unwrap(), + ]; + self.fill_boxes(&title_underline, &uc); + if let Some(title) = floating.title_texture.get() { + self.render_texture(&title, x + bw, y + bw, ARGB8888); + } + let body = Rect::new_sized(x + bw, y + bw + th + 1, pos.width() - 2 * bw, pos.height() - 2 * bw - th - 1).unwrap(); + unsafe { + with_scissor(&body, || { + child.render(self, body.x1(), body.y1()); + }); } } } diff --git a/src/state.rs b/src/state.rs index 4e288176..d1938060 100644 --- a/src/state.rs +++ b/src/state.rs @@ -12,9 +12,12 @@ use crate::globals::{Globals, GlobalsError, WaylandGlobal}; use crate::ifs::wl_output::WlOutputGlobal; use crate::ifs::wl_seat::{SeatIds, WlSeatGlobal}; use crate::ifs::wl_surface::NoneSurfaceExt; +use crate::rect::Rect; use crate::render::RenderContext; use crate::theme::Theme; -use crate::tree::{ContainerNode, DisplayNode, NodeIds}; +use crate::tree::{ + ContainerNode, ContainerSplit, DisplayNode, FloatNode, Node, NodeIds, WorkspaceNode, +}; use crate::utils::clonecell::CloneCell; use crate::utils::copyhashmap::CopyHashMap; use crate::utils::linkedlist::LinkedList; @@ -58,6 +61,8 @@ pub struct State { pub theme: Theme, pub pending_container_layout: AsyncQueue>, pub pending_container_titles: AsyncQueue>, + pub pending_float_layout: AsyncQueue>, + pub pending_float_titles: AsyncQueue>, } pub struct MouseData { @@ -107,4 +112,70 @@ impl State { seat.trigger_tree_changed(); } } + + pub fn map_tiled(self: &Rc, node: Rc) { + let seat = self.seat_queue.last(); + if let Some(seat) = seat { + if let Some(prev) = seat.last_tiled_keyboard_toplevel() { + if let Some(container) = prev.parent_node.get() { + if let Some(container) = container.into_container() { + container.add_child_after(&*prev, node); + return; + } + } + } + } + let output = { + let outputs = self.root.outputs.lock(); + outputs.values().next().cloned() + }; + if let Some(output) = output { + if let Some(workspace) = output.workspace.get() { + if let Some(container) = workspace.container.get() { + container.append_child(node); + } else { + let container = ContainerNode::new( + self, + &workspace, + workspace.clone(), + node, + ContainerSplit::Horizontal, + ); + workspace.set_container(&container); + }; + return; + } + } + todo!("map_tiled"); + } + + pub fn map_floating( + self: &Rc, + node: Rc, + mut width: i32, + mut height: i32, + workspace: &Rc, + ) { + node.clone().set_workspace(workspace); + width += 2 * self.theme.border_width.get(); + height += 2 * self.theme.border_width.get() + self.theme.title_height.get(); + let output = workspace.output.get(); + let output_rect = output.position.get(); + let position = { + let mut x1 = output_rect.x1(); + let mut y1 = output_rect.y1(); + if width < output_rect.width() { + x1 += (output_rect.width() - width) as i32 / 2; + } else { + width = output_rect.width(); + } + if height < output_rect.height() { + y1 += (output_rect.height() - height) as i32 / 2; + } else { + height = output_rect.height(); + } + Rect::new_sized(x1, y1, width, height).unwrap() + }; + FloatNode::new(self, workspace, position, node); + } } diff --git a/src/theme.rs b/src/theme.rs index bb0a189c..d51ecc16 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -9,13 +9,6 @@ pub struct Color { } impl Color { - pub const RED: Self = Self { - r: 1.0, - g: 0.0, - b: 0.0, - a: 1.0, - }; - pub const GREY: Self = Self { r: 0.8, g: 0.8, @@ -37,6 +30,15 @@ impl Color { a: to_f32(a), } } + + pub const fn from_rgbaf(r: f32, g: f32, b: f32, a: f32) -> Self { + Self { + r, + g, + b, + a, + } + } } impl From for Color { diff --git a/src/tree/container.rs b/src/tree/container.rs index 3ad5ca87..0ffa5357 100644 --- a/src/tree/container.rs +++ b/src/tree/container.rs @@ -1,4 +1,4 @@ -use crate::backend::{KeyState}; +use crate::backend::KeyState; use crate::cursor::KnownCursor; use crate::fixed::Fixed; use crate::ifs::wl_seat::{NodeSeatState, SeatId, WlSeatGlobal, BTN_LEFT}; @@ -19,10 +19,6 @@ use std::mem; use std::ops::{Deref, DerefMut, Sub}; use std::rc::Rc; - - - - #[allow(dead_code)] #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum ContainerSplit { @@ -135,14 +131,14 @@ impl ContainerNode { parent: Rc, child: Rc, split: ContainerSplit, - ) -> Self { + ) -> Rc { child.clone().set_workspace(workspace); let children = LinkedList::new(); let mut child_nodes = AHashMap::new(); child_nodes.insert( child.id(), children.add_last(ContainerChild { - node: child, + node: child.clone(), active: Cell::new(false), body: Cell::new(Default::default()), content: Cell::new(Default::default()), @@ -152,7 +148,7 @@ impl ContainerNode { title_texture: Default::default(), }), ); - Self { + let slf = Rc::new(Self { id: state.node_ids.next(), parent: CloneCell::new(parent), active: Cell::new(false), @@ -177,7 +173,9 @@ impl ContainerNode { workspace: CloneCell::new(workspace.clone()), seats: RefCell::new(Default::default()), state: state.clone(), - } + }); + child.set_parent(slf.clone()); + slf } pub fn num_children(&self) -> usize { @@ -283,11 +281,15 @@ impl ContainerNode { } } - pub fn on_theme_changed(self: &Rc) { + pub fn on_spaces_changed(self: &Rc) { self.update_content_size(); self.schedule_layout(); } + pub fn on_colors_changed(self: &Rc) { + self.schedule_render_titles(); + } + fn schedule_layout(self: &Rc) { if !self.layout_scheduled.replace(true) { self.state.pending_container_layout.push(self.clone()); @@ -500,6 +502,7 @@ impl ContainerNode { } fn render_titles(&self) { + self.render_titles_scheduled.set(false); let theme = &self.state.theme; let th = theme.title_height.get(); let font = theme.font.borrow_mut(); @@ -523,7 +526,6 @@ impl ContainerNode { }; c.title_texture.set(Some(texture)); } - self.render_titles_scheduled.set(false); } } @@ -547,7 +549,7 @@ pub async fn container_layout(state: Rc) { } } -pub async fn render_titles(state: Rc) { +pub async fn container_titles(state: Rc) { loop { let container = state.pending_container_titles.pop().await; if container.render_titles_scheduled.get() { @@ -835,6 +837,21 @@ impl Node for ContainerNode { self.parent.get().focus_self(seat); } + fn toggle_floating(self: Rc, _seat: &Rc) { + let parent = self.parent.get(); + parent.clone().remove_child(&*self); + if parent.is_float() { + self.state.map_tiled(self.clone()); + } else { + self.state.map_floating( + self.clone(), + self.width.get(), + self.height.get(), + &self.workspace.get(), + ); + } + } + fn find_tree_at(&self, x: i32, y: i32, tree: &mut Vec) -> FindTreeResult { let mut recurse = |content: Rect, child: NodeRef| { if content.contains(x, y) { diff --git a/src/tree/float.rs b/src/tree/float.rs new file mode 100644 index 00000000..f2477a3a --- /dev/null +++ b/src/tree/float.rs @@ -0,0 +1,260 @@ +use std::cell::{Cell, RefCell}; +use std::fmt::{Debug, Formatter}; +use std::ops::Deref; +use std::rc::Rc; +use i4config::Direction; +use crate::{CloneCell, ErrorFmt, State, text}; +use crate::backend::{KeyState, ScrollAxis}; +use crate::client::{Client, ClientId}; +use crate::cursor::KnownCursor; +use crate::fixed::Fixed; +use crate::ifs::wl_seat::{Dnd, NodeSeatState, WlSeatGlobal}; +use crate::ifs::wl_surface::WlSurface; +use crate::rect::Rect; +use crate::render::{Renderer, Texture}; +use crate::theme::Color; +use crate::tree::{ContainerNode, ContainerSplit, FindTreeResult, FoundNode, Node, NodeId, WorkspaceNode}; +use crate::tree::walker::NodeVisitor; +use crate::utils::linkedlist::LinkedNode; +use crate::xkbcommon::ModifierState; + +tree_id!(FloatNodeId); +pub struct FloatNode { + pub id: FloatNodeId, + pub state: Rc, + pub visible: Cell, + pub position: Cell, + pub display_link: Cell>>>, + pub workspace_link: Cell>>>, + pub workspace: CloneCell>, + pub child: CloneCell>>, + pub active: Cell, + pub seat_state: NodeSeatState, + pub layout_scheduled: Cell, + pub render_titles_scheduled: Cell, + pub title: RefCell, + pub title_texture: CloneCell>>, +} + +pub async fn float_layout(state: Rc) { + loop { + let node = state.pending_float_layout.pop().await; + if node.layout_scheduled.get() { + node.perform_layout(); + } + } +} + +pub async fn float_titles(state: Rc) { + loop { + let node = state.pending_float_titles.pop().await; + if node.render_titles_scheduled.get() { + node.render_title(); + } + } +} + +impl FloatNode { + pub fn new(state: &Rc, ws: &Rc, position: Rect, child: Rc) -> Rc { + let floater = Rc::new(FloatNode { + id: state.node_ids.next(), + state: state.clone(), + visible: Cell::new(true), + position: Cell::new(position), + display_link: Cell::new(None), + workspace_link: Cell::new(None), + workspace: CloneCell::new(ws.clone()), + child: CloneCell::new(Some(child.clone())), + active: Cell::new(false), + seat_state: Default::default(), + layout_scheduled: Cell::new(false), + render_titles_scheduled: Cell::new(false), + title: Default::default(), + title_texture: Default::default(), + }); + floater + .display_link + .set(Some(state.root.stacked.add_last(floater.clone()))); + floater + .workspace_link + .set(Some(ws.stacked.add_last(floater.clone()))); + child.clone().set_workspace(ws); + child.clone().set_parent(floater.clone()); + floater.schedule_layout(); + floater + } + + pub fn on_spaces_changed(self: &Rc) { + self.schedule_layout(); + } + + pub fn on_colors_changed(self: &Rc) { + self.schedule_render_titles(); + } + + fn schedule_layout(self: &Rc) { + if !self.layout_scheduled.replace(true) { + self.state.pending_float_layout.push(self.clone()); + } + } + + fn perform_layout(self: &Rc) { + let child = match self.child.get() { + Some(c) => c, + _ => return, + }; + let pos = self.position.get(); + let theme = &self.state.theme; + let bw = theme.border_width.get(); + let th = theme.title_height.get(); + let cpos = Rect::new_sized( + pos.x1() + bw, + pos.y1() + bw + th + 1, + (pos.width() - 2 * bw).max(0), + (pos.height() - 2 * bw - th - 1).max(0), + ).unwrap(); + child.clone().change_extents(&cpos); + self.layout_scheduled.set(false); + self.schedule_render_titles(); + } + + fn schedule_render_titles(self: &Rc) { + if !self.render_titles_scheduled.replace(true) { + self.state.pending_float_titles.push(self.clone()); + } + } + + fn render_title(&self) { + self.render_titles_scheduled.set(false); + let theme = &self.state.theme; + let th = theme.title_height.get(); + let font = theme.font.borrow_mut(); + let title = self.title.borrow_mut(); + self.title_texture.set(None); + let pos = self.position.get(); + let ctx = match self.state.render_ctx.get() { + Some(c) => c, + _ => return, + }; + let texture = match text::render(&ctx, pos.width(), th, &font, &title, Color::GREY) { + Ok(t) => t, + Err(e) => { + log::error!("Could not render title {}: {}", title, ErrorFmt(e)); + return; + } + }; + self.title_texture.set(Some(texture)); + } +} + +impl Debug for FloatNode { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("FloatNode").finish_non_exhaustive() + } +} + +impl Node for FloatNode { + fn id(&self) -> NodeId { + self.id.into() + } + + fn seat_state(&self) -> &NodeSeatState { + &self.seat_state + } + + fn destroy_node(&self, _detach: bool) { + let _v = self.display_link.take(); + let _v = self.workspace_link.take(); + if let Some(child) = self.child.get() { + child.destroy_node(false); + } + self.seat_state.destroy_node(self); + } + + fn visit(self: Rc, visitor: &mut dyn NodeVisitor) { + visitor.visit_float(&self); + } + + fn visit_children(&self, visitor: &mut dyn NodeVisitor) { + if let Some(c) = self.child.get() { + c.visit(visitor); + } + } + + fn absolute_position(&self) -> Rect { + self.position.get() + } + + fn find_tree_at(&self, x: i32, y: i32, tree: &mut Vec) -> FindTreeResult { + let theme = &self.state.theme; + let th = theme.title_height.get(); + let bw = theme.border_width.get(); + let pos = self.position.get(); + if x < bw || x >= pos.width() - bw { + return FindTreeResult::AcceptsInput; + } + if y < bw + th + 1 || x >= pos.height() - bw { + return FindTreeResult::AcceptsInput; + } + let child = match self.child.get() { + Some(c) => c, + _ => return FindTreeResult::Other, + }; + let x = x - bw; + let y = y - bw - th - 1; + tree.push(FoundNode { + node: child.clone(), + x, + y, + }); + child.find_tree_at(x, y, tree) + } + + fn remove_child(self: Rc, _child: &dyn Node) { + self.child.set(None); + self.display_link.set(None); + self.workspace_link.set(None); + } + + fn pointer_target(&self, seat: &Rc) { + seat.set_known_cursor(KnownCursor::Default); + } + + fn render(&self, renderer: &mut Renderer, x: i32, y: i32) { + renderer.render_floating(self, x, y) + } + + fn into_float(self: Rc) -> Option> { + Some(self) + } + + fn accepts_child(&self, _node: &dyn Node) -> bool { + true + } + + fn is_float(&self) -> bool { + true + } + + fn set_workspace(self: Rc, ws: &Rc) { + if let Some(c) = self.child.get() { + c.set_workspace(ws); + } + self.workspace_link + .set(Some(ws.stacked.add_last(self.clone()))); + self.workspace.set(ws.clone()); + } + + fn child_active_changed(&self, _child: &dyn Node, active: bool) { + self.active.set(active); + } + + fn child_title_changed(self: Rc, child: &dyn Node, title: &str) { + let mut t = self.title.borrow_mut(); + if t.deref() != title { + t.clear(); + t.push_str(title); + self.schedule_render_titles(); + } + } +} diff --git a/src/tree/mod.rs b/src/tree/mod.rs index 34f7af80..40dbd431 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -10,7 +10,7 @@ use crate::render::Renderer; use crate::tree::walker::NodeVisitor; use crate::utils::clonecell::CloneCell; use crate::utils::copyhashmap::CopyHashMap; -use crate::utils::linkedlist::{LinkedList, LinkedNode}; +use crate::utils::linkedlist::{LinkedList}; use crate::xkbcommon::ModifierState; use crate::NumCell; pub use container::*; @@ -20,10 +20,12 @@ use std::fmt::{Debug, Display, Formatter}; use std::ops::Deref; use std::rc::Rc; pub use workspace::*; +pub use float::*; mod container; pub mod walker; mod workspace; +mod float; pub struct NodeIds { next: NumCell, @@ -173,6 +175,10 @@ pub trait Node { let _ = seat; } + fn toggle_floating(self: Rc, seat: &Rc) { + let _ = seat; + } + fn unfocus(&self, seat: &WlSeatGlobal) { let _ = seat; } @@ -495,115 +501,3 @@ impl Node for OutputNode { } } } - -tree_id!(FloatNodeId); -pub struct FloatNode { - pub id: FloatNodeId, - pub visible: Cell, - pub position: Cell, - pub display: Rc, - pub display_link: Cell>>>, - pub workspace_link: Cell>>>, - pub workspace: CloneCell>, - pub child: CloneCell>>, - pub seat_state: NodeSeatState, -} - -impl Debug for FloatNode { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("FloatNode").finish_non_exhaustive() - } -} - -impl Node for FloatNode { - fn id(&self) -> NodeId { - self.id.into() - } - - fn seat_state(&self) -> &NodeSeatState { - &self.seat_state - } - - fn destroy_node(&self, _detach: bool) { - let _v = self.display_link.take(); - let _v = self.workspace_link.take(); - if let Some(child) = self.child.get() { - child.destroy_node(false); - } - self.seat_state.destroy_node(self); - } - - fn visit(self: Rc, visitor: &mut dyn NodeVisitor) { - visitor.visit_float(&self); - } - - fn visit_children(&self, visitor: &mut dyn NodeVisitor) { - if let Some(c) = self.child.get() { - c.visit(visitor); - } - } - - fn absolute_position(&self) -> Rect { - self.position.get() - } - - fn find_tree_at(&self, x: i32, y: i32, tree: &mut Vec) -> FindTreeResult { - let child = match self.child.get() { - Some(c) => c, - _ => return FindTreeResult::Other, - }; - tree.push(FoundNode { - node: child.clone(), - x, - y, - }); - child.find_tree_at(x, y, tree) - } - - fn remove_child(self: Rc, _child: &dyn Node) { - self.child.set(None); - self.display_link.set(None); - self.workspace_link.set(None); - } - - fn child_size_changed(&self, _child: &dyn Node, width: i32, height: i32) { - let pos = self.position.get(); - self.position - .set(Rect::new_sized(pos.x1(), pos.y1(), width, height).unwrap()); - } - - fn pointer_target(&self, seat: &Rc) { - seat.set_known_cursor(KnownCursor::Default); - } - - fn render(&self, renderer: &mut Renderer, x: i32, y: i32) { - renderer.render_floating(self, x, y) - } - - fn into_float(self: Rc) -> Option> { - Some(self) - } - - fn accepts_child(&self, _node: &dyn Node) -> bool { - true - } - - fn is_float(&self) -> bool { - true - } - - fn change_extents(self: Rc, rect: &Rect) { - if let Some(child) = self.child.get() { - child.change_extents(rect); - } - } - - fn set_workspace(self: Rc, ws: &Rc) { - if let Some(c) = self.child.get() { - c.set_workspace(ws); - } - self.workspace_link - .set(Some(ws.stacked.add_last(self.clone()))); - self.workspace.set(ws.clone()); - } -} diff --git a/src/tree/walker.rs b/src/tree/walker.rs index 510bfd3a..9c3b381a 100644 --- a/src/tree/walker.rs +++ b/src/tree/walker.rs @@ -84,13 +84,24 @@ impl NodeVisitor for T { } } -pub fn visit_containers)>(f: F) -> impl NodeVisitor { - struct V(F); - impl)> NodeVisitorBase for V { - fn visit_container(&mut self, node: &Rc) { - (self.0)(node); - node.visit_children(self); - } - } - V(f) -} +// pub fn visit_containers)>(f: F) -> impl NodeVisitor { +// struct V(F); +// impl)> NodeVisitorBase for V { +// fn visit_container(&mut self, node: &Rc) { +// (self.0)(node); +// node.visit_children(self); +// } +// } +// V(f) +// } +// +// pub fn visit_floats)>(f: F) -> impl NodeVisitor { +// struct V(F); +// impl)> NodeVisitorBase for V { +// fn visit_float(&mut self, node: &Rc) { +// (self.0)(node); +// node.visit_children(self); +// } +// } +// V(f) +// } diff --git a/src/tree/workspace.rs b/src/tree/workspace.rs index fe9e4f55..8fbe7253 100644 --- a/src/tree/workspace.rs +++ b/src/tree/workspace.rs @@ -10,12 +10,6 @@ use crate::utils::linkedlist::LinkedList; use std::fmt::Debug; use std::rc::Rc; - - - - - - tree_id!(WorkspaceNodeId); pub struct WorkspaceNode {