diff --git a/src/compositor.rs b/src/compositor.rs index 86188129..07f074dc 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -373,6 +373,7 @@ fn create_dummy_output(state: &Rc) { is_dummy: true, status: Default::default(), scroll: Default::default(), + pointer_positions: Default::default(), }); let dummy_workspace = Rc::new(WorkspaceNode { id: state.node_ids.next(), diff --git a/src/it/tests.rs b/src/it/tests.rs index e900b9d3..a67da9c3 100644 --- a/src/it/tests.rs +++ b/src/it/tests.rs @@ -43,6 +43,7 @@ mod t0014_container_scroll_focus; mod t0015_scroll_partial; mod t0016_scroll_ws; mod t0017_remove_unused_ws; +mod t0018_click_to_active_ws; pub trait TestCase: Sync { fn name(&self) -> &'static str; @@ -78,5 +79,6 @@ pub fn tests() -> Vec<&'static dyn TestCase> { t0015_scroll_partial, t0016_scroll_ws, t0017_remove_unused_ws, + t0018_click_to_active_ws, } } diff --git a/src/it/tests/t0018_click_to_active_ws.rs b/src/it/tests/t0018_click_to_active_ws.rs new file mode 100644 index 00000000..39a02a0e --- /dev/null +++ b/src/it/tests/t0018_click_to_active_ws.rs @@ -0,0 +1,46 @@ +use { + crate::{ + ifs::wl_seat::BTN_LEFT, + it::{test_error::TestResult, testrun::TestRun}, + }, + std::rc::Rc, +}; + +testcase!(); + +async fn test(run: Rc) -> TestResult { + let ds = run.create_default_setup().await?; + run.cfg.show_workspace(ds.seat.id(), "1")?; + + let client = run.create_client().await?; + + let win1 = client.create_window().await?; + win1.map().await?; + + run.cfg.show_workspace(ds.seat.id(), "2")?; + + let win2 = client.create_window().await?; + win2.map().await?; + + ds.mouse.abs(&ds.connector, 0.0, 0.0); + ds.mouse.click(BTN_LEFT); + + client.sync().await; + + let name = ds.output.workspace.get().map(|ws| ws.name.clone()); + tassert_eq!(name.as_deref(), Some("1")); + + let pos = { + let rd = ds.output.render_data.borrow_mut(); + rd.titles.last().map(|t| t.x1).unwrap_or(0) + }; + ds.mouse.abs(&ds.connector, pos as _, 0.0); + ds.mouse.click(BTN_LEFT); + + client.sync().await; + + let name = ds.output.workspace.get().map(|ws| ws.name.clone()); + tassert_eq!(name.as_deref(), Some("2")); + + Ok(()) +} diff --git a/src/render/renderer/renderer.rs b/src/render/renderer/renderer.rs index 883ac1d1..aaf603ec 100644 --- a/src/render/renderer/renderer.rs +++ b/src/render/renderer/renderer.rs @@ -114,10 +114,10 @@ impl Renderer<'_> { let c = theme.colors.unfocused_title_background.get(); self.fill_boxes2(&rd.inactive_workspaces, &c, x, y); for title in &rd.titles { - self.render_texture(&title.tex, x + title.x, y + title.y, ARGB8888); + self.render_texture(&title.tex, x + title.tex_x, y + title.tex_y, ARGB8888); } if let Some(status) = &rd.status { - self.render_texture(&status.tex, x + status.x, y + status.y, ARGB8888); + self.render_texture(&status.tex, x + status.tex_x, y + status.tex_y, ARGB8888); } } if let Some(ws) = output.workspace.get() { diff --git a/src/tasks/connector.rs b/src/tasks/connector.rs index 5dfbf1dd..4bd19aa6 100644 --- a/src/tasks/connector.rs +++ b/src/tasks/connector.rs @@ -118,6 +118,7 @@ impl ConnectorHandler { is_dummy: false, status: self.state.status.clone(), scroll: Default::default(), + pointer_positions: Default::default(), }); let mode = info.initial_mode; let output_data = Rc::new(OutputData { diff --git a/src/tree/output.rs b/src/tree/output.rs index 06c7ba95..9014826c 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -1,10 +1,14 @@ use { crate::{ - backend::Mode, + backend::{KeyState, Mode}, cursor::KnownCursor, + fixed::Fixed, ifs::{ wl_output::WlOutputGlobal, - wl_seat::{collect_kb_foci2, wl_pointer::PendingScroll, NodeSeatState, WlSeatGlobal}, + wl_seat::{ + collect_kb_foci2, wl_pointer::PendingScroll, NodeSeatState, SeatId, WlSeatGlobal, + BTN_LEFT, + }, wl_surface::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, zwlr_layer_shell_v1::{BACKGROUND, BOTTOM, OVERLAY, TOP}, }, @@ -16,7 +20,8 @@ use { walker::NodeVisitor, Direction, FindTreeResult, FoundNode, Node, NodeId, WorkspaceNode, }, utils::{ - clonecell::CloneCell, errorfmt::ErrorFmt, linkedlist::LinkedList, scroller::Scroller, + clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, + linkedlist::LinkedList, scroller::Scroller, }, }, smallvec::SmallVec, @@ -41,6 +46,7 @@ pub struct OutputNode { pub is_dummy: bool, pub status: CloneCell>, pub scroll: Scroller, + pub pointer_positions: CopyHashMap, } impl OutputNode { @@ -98,9 +104,12 @@ impl OutputNode { x = pos + (title_width - title.width()) / 2; } rd.titles.push(OutputTitle { - x, - y: 0, + x1: pos, + x2: pos + title_width, + tex_x: x, + tex_y: 0, tex: title, + ws: ws.deref().clone(), }); } } @@ -130,9 +139,9 @@ impl OutputNode { } }; let pos = width - title.width() - 1; - rd.status = Some(OutputTitle { - x: pos, - y: 0, + rd.status = Some(OutputStatus { + tex_x: pos, + tex_y: 0, tex: title, }); } @@ -295,11 +304,24 @@ impl OutputNode { self.status.set(status.clone()); self.update_render_data(); } + + fn pointer_move(self: &Rc, seat: &Rc, x: i32, y: i32) { + self.pointer_positions.set(seat.id(), (x, y)); + } } pub struct OutputTitle { - pub x: i32, - pub y: i32, + pub x1: i32, + pub x2: i32, + pub tex_x: i32, + pub tex_y: i32, + pub tex: Rc, + pub ws: Rc, +} + +pub struct OutputStatus { + pub tex_x: i32, + pub tex_y: i32, pub tex: Rc, } @@ -309,7 +331,7 @@ pub struct OutputRenderData { pub underline: Rect, pub inactive_workspaces: Vec, pub titles: Vec, - pub status: Option, + pub status: Option, } impl Debug for OutputNode { @@ -423,6 +445,37 @@ impl Node for OutputNode { renderer.render_output(self, x, y); } + fn node_on_button( + self: Rc, + seat: &Rc, + button: u32, + state: KeyState, + _serial: u32, + ) { + if state != KeyState::Pressed || button != BTN_LEFT { + return; + } + let (x, y) = match self.pointer_positions.get(&seat.id()) { + Some(p) => p, + _ => return, + }; + if y >= self.state.theme.sizes.title_height.get() { + return; + } + let ws = 'ws: { + let rd = self.render_data.borrow_mut(); + for title in &rd.titles { + if x >= title.x1 && x < title.x2 { + break 'ws title.ws.clone(); + } + } + return; + }; + self.show_workspace(&ws); + self.update_render_data(); + self.state.tree_changed(); + } + fn node_on_axis_event(self: Rc, seat: &Rc, event: &PendingScroll) { let steps = match self.scroll.handle(event) { Some(e) => e, @@ -460,11 +513,19 @@ impl Node for OutputNode { self.state.tree_changed(); } + fn node_on_pointer_enter(self: Rc, seat: &Rc, x: Fixed, y: Fixed) { + self.pointer_move(seat, x.round_down(), y.round_down()); + } + fn node_on_pointer_focus(&self, seat: &Rc) { // log::info!("output focus"); seat.set_known_cursor(KnownCursor::Default); } + fn node_on_pointer_motion(self: Rc, seat: &Rc, x: Fixed, y: Fixed) { + self.pointer_move(seat, x.round_down(), y.round_down()); + } + fn node_into_output(self: Rc) -> Option> { Some(self.clone()) } diff --git a/src/utils/clonecell.rs b/src/utils/clonecell.rs index 90160ff9..5d2c31ab 100644 --- a/src/utils/clonecell.rs +++ b/src/utils/clonecell.rs @@ -76,5 +76,8 @@ unsafe impl UnsafeCellCloneSafe for NodeRef {} unsafe impl UnsafeCellCloneSafe for () {} unsafe impl UnsafeCellCloneSafe for u64 {} +unsafe impl UnsafeCellCloneSafe for i32 {} + +unsafe impl UnsafeCellCloneSafe for (A, B) {} unsafe impl UnsafeCellCloneSafe for Modifiers {}