diff --git a/src/compositor.rs b/src/compositor.rs index 8650d9ff..b6541586 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -369,6 +369,7 @@ fn create_dummy_output(state: &Rc) { state: state.clone(), is_dummy: true, status: Default::default(), + scroll: 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 30420f83..6b0e44cb 100644 --- a/src/it/tests.rs +++ b/src/it/tests.rs @@ -41,6 +41,7 @@ mod t0012_subsurface_focus; mod t0013_graphics_initialized; mod t0014_container_scroll_focus; mod t0015_scroll_partial; +mod t0016_scroll_ws; pub trait TestCase: Sync { fn name(&self) -> &'static str; @@ -74,5 +75,6 @@ pub fn tests() -> Vec<&'static dyn TestCase> { t0013_graphics_initialized, t0014_container_scroll_focus, t0015_scroll_partial, + t0016_scroll_ws, } } diff --git a/src/it/tests/t0016_scroll_ws.rs b/src/it/tests/t0016_scroll_ws.rs new file mode 100644 index 00000000..3671261d --- /dev/null +++ b/src/it/tests/t0016_scroll_ws.rs @@ -0,0 +1,37 @@ +use { + crate::it::{ + test_error::{TestErrorExt, 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 dss = client.get_default_seat().await?; + + let w1 = client.create_window().await?; + w1.map().await?; + + run.cfg.show_workspace(ds.seat.id(), "2")?; + + let w2 = client.create_window().await?; + w2.map().await?; + + let enters = dss.kb.enter.expect()?; + + ds.mouse.abs(&ds.connector, 0.0, 0.0); + ds.mouse.scroll(-1); + + client.sync().await; + + let enter = enters.next().with_context(|| "no enter")?; + tassert_eq!(enter.surface, w1.surface.id); + + Ok(()) +} diff --git a/src/tasks/connector.rs b/src/tasks/connector.rs index ad341aa9..74dd6bed 100644 --- a/src/tasks/connector.rs +++ b/src/tasks/connector.rs @@ -106,6 +106,7 @@ impl ConnectorHandler { state: self.state.clone(), is_dummy: false, status: self.state.status.clone(), + scroll: 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 e8d2e0c8..43652cce 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -4,7 +4,7 @@ use { cursor::KnownCursor, ifs::{ wl_output::WlOutputGlobal, - wl_seat::{collect_kb_foci2, NodeSeatState, WlSeatGlobal}, + wl_seat::{collect_kb_foci2, wl_pointer::PendingScroll, NodeSeatState, WlSeatGlobal}, wl_surface::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, zwlr_layer_shell_v1::{BACKGROUND, BOTTOM, OVERLAY, TOP}, }, @@ -14,7 +14,9 @@ use { text, theme::Color, tree::{walker::NodeVisitor, FindTreeResult, FoundNode, Node, NodeId, WorkspaceNode}, - utils::{clonecell::CloneCell, errorfmt::ErrorFmt, linkedlist::LinkedList}, + utils::{ + clonecell::CloneCell, errorfmt::ErrorFmt, linkedlist::LinkedList, scroller::Scroller, + }, }, jay_config::Direction, smallvec::SmallVec, @@ -38,6 +40,7 @@ pub struct OutputNode { pub state: Rc, pub is_dummy: bool, pub status: CloneCell>, + pub scroll: Scroller, } impl OutputNode { @@ -404,6 +407,42 @@ impl Node for OutputNode { renderer.render_output(self, x, y); } + fn node_on_axis_event(self: Rc, seat: &Rc, event: &PendingScroll) { + let steps = match self.scroll.handle(event) { + Some(e) => e, + _ => return, + }; + if steps == 0 { + return; + } + let ws = match self.workspace.get() { + Some(ws) => ws, + _ => return, + }; + let mut ws = 'ws: { + for r in self.workspaces.iter() { + if r.id == ws.id { + break 'ws r; + } + } + return; + }; + for _ in 0..steps.abs() { + let new = if steps < 0 { ws.prev() } else { ws.next() }; + ws = match new { + Some(n) => n, + None => break, + }; + } + if !self.show_workspace(&ws) { + return; + } + ws.deref() + .clone() + .node_do_focus(seat, Direction::Unspecified); + self.update_render_data(); + } + fn node_on_pointer_focus(&self, seat: &Rc) { // log::info!("output focus"); seat.set_known_cursor(KnownCursor::Default);