aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/handlers/xdg_shell.rs39
-rw-r--r--src/input.rs47
-rw-r--r--src/window/mapped.rs14
3 files changed, 84 insertions, 16 deletions
diff --git a/src/handlers/xdg_shell.rs b/src/handlers/xdg_shell.rs
index afd0bab9..4be323af 100644
--- a/src/handlers/xdg_shell.rs
+++ b/src/handlers/xdg_shell.rs
@@ -7,7 +7,7 @@ use smithay::input::pointer::Focus;
use smithay::output::Output;
use smithay::reexports::wayland_protocols::xdg::decoration::zv1::server::zxdg_toplevel_decoration_v1;
use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_positioner::ConstraintAdjustment;
-use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel::{self, ResizeEdge};
+use smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel::{self};
use smithay::reexports::wayland_server::protocol::wl_output;
use smithay::reexports::wayland_server::protocol::wl_seat::WlSeat;
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
@@ -30,9 +30,11 @@ use smithay::{
delegate_kde_decoration, delegate_xdg_decoration, delegate_xdg_foreign, delegate_xdg_shell,
};
+use crate::input::DOUBLE_CLICK_TIME;
use crate::layout::workspace::ColumnWidth;
use crate::niri::{PopupGrabState, State};
use crate::resize_grab::ResizeGrab;
+use crate::utils::{get_monotonic_time, ResizeEdge};
use crate::window::{InitialConfigureState, ResolvedWindowRules, Unmapped, WindowRef};
impl XdgShellHandler for State {
@@ -65,7 +67,7 @@ impl XdgShellHandler for State {
surface: ToplevelSurface,
_seat: WlSeat,
serial: Serial,
- edges: ResizeEdge,
+ edges: xdg_toplevel::ResizeEdge,
) {
let pointer = self.niri.seat.get_pointer().unwrap();
if !pointer.has_grab(serial) {
@@ -89,16 +91,37 @@ impl XdgShellHandler for State {
return;
};
+ let edges = ResizeEdge::from(edges);
let window = mapped.window.clone();
- if !self
- .niri
- .layout
- .interactive_resize_begin(window.clone(), edges.into())
- {
+
+ // See if we got a double resize-click gesture.
+ let time = get_monotonic_time();
+ let last_cell = mapped.last_interactive_resize_start();
+ let last = last_cell.get();
+ last_cell.set(Some((time, edges)));
+ if let Some((last_time, last_edges)) = last {
+ if time.saturating_sub(last_time) <= DOUBLE_CLICK_TIME {
+ let intersection = edges.intersection(last_edges);
+ if intersection.intersects(ResizeEdge::LEFT_RIGHT) {
+ // FIXME: don't activate once we can pass specific windows to actions.
+ self.niri.layout.activate_window(&window);
+ self.niri.layout.toggle_full_width();
+ }
+ if intersection.intersects(ResizeEdge::TOP_BOTTOM) {
+ // FIXME: don't activate once we can pass specific windows to actions.
+ self.niri.layout.activate_window(&window);
+ self.niri.layout.reset_window_height();
+ }
+ return;
+ }
+ }
+
+ let grab = ResizeGrab::new(start_data, window.clone());
+
+ if !self.niri.layout.interactive_resize_begin(window, edges) {
return;
}
- let grab = ResizeGrab::new(start_data, window);
pointer.set_grab(self, grab, serial, Focus::Clear);
self.niri.interactive_resize_ongoing = true;
}
diff --git a/src/input.rs b/src/input.rs
index 208d48f0..cacbaaca 100644
--- a/src/input.rs
+++ b/src/input.rs
@@ -31,7 +31,9 @@ use crate::niri::State;
use crate::resize_grab::ResizeGrab;
use crate::ui::screenshot_ui::ScreenshotUi;
use crate::utils::spawning::spawn;
-use crate::utils::{center, get_monotonic_time};
+use crate::utils::{center, get_monotonic_time, ResizeEdge};
+
+pub const DOUBLE_CLICK_TIME: Duration = Duration::from_millis(400);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CompositorMod {
@@ -1076,7 +1078,6 @@ impl State {
if ButtonState::Pressed == button_state {
if let Some(mapped) = self.niri.window_under_cursor() {
let window = mapped.window.clone();
- self.niri.layout.activate_window(&window);
// Check if we need to start an interactive resize.
if event.button() == Some(MouseButton::Right) && !pointer.is_grabbed() {
@@ -1093,23 +1094,55 @@ impl State {
.layout
.resize_edges_under(output, pos_within_output)
.unwrap();
- if self
- .niri
- .layout
- .interactive_resize_begin(window.clone(), edges)
+
+ // See if we got a double resize-click gesture.
+ // FIXME: deduplicate with resize_request in xdg-shell somehow.
+ let time = get_monotonic_time();
+ let last_cell = mapped.last_interactive_resize_start();
+ let last = last_cell.get();
+ last_cell.set(Some((time, edges)));
+ let mut did_gesture = false;
+ if let Some((last_time, last_edges)) = last {
+ if time.saturating_sub(last_time) <= DOUBLE_CLICK_TIME {
+ let intersection = edges.intersection(last_edges);
+ if intersection.intersects(ResizeEdge::LEFT_RIGHT) {
+ // FIXME: don't activate once we can pass specific windows to
+ // actions.
+ self.niri.layout.activate_window(&window);
+ self.niri.layout.toggle_full_width();
+ }
+ if intersection.intersects(ResizeEdge::TOP_BOTTOM) {
+ // FIXME: don't activate once we can pass specific windows to
+ // actions.
+ self.niri.layout.activate_window(&window);
+ self.niri.layout.reset_window_height();
+ }
+ did_gesture = true;
+ }
+ }
+
+ self.niri.layout.activate_window(&window);
+
+ if !did_gesture
+ && self
+ .niri
+ .layout
+ .interactive_resize_begin(window.clone(), edges)
{
let start_data = PointerGrabStartData {
focus: None,
button: event.button_code(),
location,
};
- let grab = ResizeGrab::new(start_data, window);
+ let grab = ResizeGrab::new(start_data, window.clone());
pointer.set_grab(self, grab, serial, Focus::Clear);
self.niri.interactive_resize_ongoing = true;
}
}
}
+ self.niri.layout.activate_window(&window);
+
// FIXME: granular.
self.niri.queue_redraw_all();
} else if let Some(output) = self.niri.output_under_cursor() {
diff --git a/src/window/mapped.rs b/src/window/mapped.rs
index 952a2bf3..4b31da22 100644
--- a/src/window/mapped.rs
+++ b/src/window/mapped.rs
@@ -1,5 +1,6 @@
-use std::cell::RefCell;
+use std::cell::{Cell, RefCell};
use std::cmp::{max, min};
+use std::time::Duration;
use niri_config::WindowRule;
use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement};
@@ -27,6 +28,7 @@ use crate::render_helpers::renderer::NiriRenderer;
use crate::render_helpers::snapshot::RenderSnapshot;
use crate::render_helpers::surface::render_snapshot_from_surface_tree;
use crate::render_helpers::{BakedBuffer, RenderTarget, SplitElements};
+use crate::utils::ResizeEdge;
#[derive(Debug)]
pub struct Mapped {
@@ -61,6 +63,11 @@ pub struct Mapped {
/// State of an ongoing interactive resize.
interactive_resize: Option<InteractiveResize>,
+
+ /// Last time interactive resize was started.
+ ///
+ /// Used for double-resize-click tracking.
+ last_interactive_resize_start: Cell<Option<(Duration, ResizeEdge)>>,
}
/// Interactive resize state.
@@ -100,6 +107,7 @@ impl Mapped {
animate_serials: Vec::new(),
animation_snapshot: None,
interactive_resize: None,
+ last_interactive_resize_start: Cell::new(None),
}
}
@@ -199,6 +207,10 @@ impl Mapped {
pub fn store_animation_snapshot(&mut self, renderer: &mut GlesRenderer) {
self.animation_snapshot = Some(self.render_snapshot(renderer));
}
+
+ pub fn last_interactive_resize_start(&self) -> &Cell<Option<(Duration, ResizeEdge)>> {
+ &self.last_interactive_resize_start
+ }
}
impl Drop for Mapped {