Merge pull request #114 from mahkoh/jorth/double-click
tree: support toggling floating with double clicks
This commit is contained in:
commit
911591973e
11 changed files with 137 additions and 14 deletions
|
|
@ -465,6 +465,14 @@ impl Client {
|
||||||
*self.on_new_input_device.borrow_mut() = Some(Rc::new(f));
|
*self.on_new_input_device.borrow_mut() = Some(Rc::new(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_double_click_interval(&self, usec: u64) {
|
||||||
|
self.send(&ClientMessage::SetDoubleClickIntervalUsec { usec });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_double_click_distance(&self, dist: i32) {
|
||||||
|
self.send(&ClientMessage::SetDoubleClickDistance { dist });
|
||||||
|
}
|
||||||
|
|
||||||
pub fn connector_set_position(&self, connector: Connector, x: i32, y: i32) {
|
pub fn connector_set_position(&self, connector: Connector, x: i32, y: i32) {
|
||||||
self.send(&ClientMessage::ConnectorSetPosition { connector, x, y });
|
self.send(&ClientMessage::ConnectorSetPosition { connector, x, y });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -346,6 +346,12 @@ pub enum ClientMessage<'a> {
|
||||||
connector: Connector,
|
connector: Connector,
|
||||||
transform: Transform,
|
transform: Transform,
|
||||||
},
|
},
|
||||||
|
SetDoubleClickIntervalUsec {
|
||||||
|
usec: u64,
|
||||||
|
},
|
||||||
|
SetDoubleClickDistance {
|
||||||
|
dist: i32,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ use {
|
||||||
Axis, Direction, ModifiedKeySym, Workspace,
|
Axis, Direction, ModifiedKeySym, Workspace,
|
||||||
},
|
},
|
||||||
serde::{Deserialize, Serialize},
|
serde::{Deserialize, Serialize},
|
||||||
|
std::time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// An input device.
|
/// An input device.
|
||||||
|
|
@ -257,6 +258,8 @@ impl Seat {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Toggles whether the currently focused window is floating.
|
/// Toggles whether the currently focused window is floating.
|
||||||
|
///
|
||||||
|
/// You can do the same by double-clicking on the header.
|
||||||
pub fn toggle_floating(self) {
|
pub fn toggle_floating(self) {
|
||||||
get!().toggle_floating(self);
|
get!().toggle_floating(self);
|
||||||
}
|
}
|
||||||
|
|
@ -329,3 +332,28 @@ pub fn on_new_seat<F: Fn(Seat) + 'static>(f: F) {
|
||||||
pub fn on_new_input_device<F: Fn(InputDevice) + 'static>(f: F) {
|
pub fn on_new_input_device<F: Fn(InputDevice) + 'static>(f: F) {
|
||||||
get!().on_new_input_device(f)
|
get!().on_new_input_device(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the maximum time between two clicks to be registered as a double click by the
|
||||||
|
/// compositor.
|
||||||
|
///
|
||||||
|
/// This only affects interactions with the compositor UI and has no effect on
|
||||||
|
/// applications.
|
||||||
|
///
|
||||||
|
/// The default is 400 ms.
|
||||||
|
pub fn set_double_click_time(duration: Duration) {
|
||||||
|
let usec = duration.as_micros().min(u64::MAX as u128);
|
||||||
|
get!().set_double_click_interval(usec as u64)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the maximum distance between two clicks to be registered as a double click by the
|
||||||
|
/// compositor.
|
||||||
|
///
|
||||||
|
/// This only affects interactions with the compositor UI and has no effect on
|
||||||
|
/// applications.
|
||||||
|
///
|
||||||
|
/// Setting a negative distance disables double clicks.
|
||||||
|
///
|
||||||
|
/// The default is 5.
|
||||||
|
pub fn set_double_click_distance(distance: i32) {
|
||||||
|
get!().set_double_click_distance(distance)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -205,6 +205,8 @@ fn start_compositor2(
|
||||||
drm_feedback_ids: Default::default(),
|
drm_feedback_ids: Default::default(),
|
||||||
direct_scanout_enabled: Cell::new(true),
|
direct_scanout_enabled: Cell::new(true),
|
||||||
output_transforms: Default::default(),
|
output_transforms: Default::default(),
|
||||||
|
double_click_interval_usec: Cell::new(400 * 1000),
|
||||||
|
double_click_distance: Cell::new(5),
|
||||||
});
|
});
|
||||||
state.tracker.register(ClientId::from_raw(0));
|
state.tracker.register(ClientId::from_raw(0));
|
||||||
create_dummy_output(&state);
|
create_dummy_output(&state);
|
||||||
|
|
|
||||||
|
|
@ -615,6 +615,14 @@ impl ConfigProxyHandler {
|
||||||
self.state.default_workspace_capture.set(capture);
|
self.state.default_workspace_capture.set(capture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_set_double_click_interval_usec(&self, usec: u64) {
|
||||||
|
self.state.double_click_interval_usec.set(usec);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_set_double_click_distance(&self, dist: i32) {
|
||||||
|
self.state.double_click_distance.set(dist);
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_get_seat_workspace(&self, seat: Seat) -> Result<(), CphError> {
|
fn handle_get_seat_workspace(&self, seat: Seat) -> Result<(), CphError> {
|
||||||
let seat = self.get_seat(seat)?;
|
let seat = self.get_seat(seat)?;
|
||||||
let output = seat.get_output();
|
let output = seat.get_output();
|
||||||
|
|
@ -1355,6 +1363,12 @@ impl ConfigProxyHandler {
|
||||||
} => self
|
} => self
|
||||||
.handle_connector_set_transform(connector, transform)
|
.handle_connector_set_transform(connector, transform)
|
||||||
.wrn("connector_set_transform")?,
|
.wrn("connector_set_transform")?,
|
||||||
|
ClientMessage::SetDoubleClickIntervalUsec { usec } => {
|
||||||
|
self.handle_set_double_click_interval_usec(usec)
|
||||||
|
}
|
||||||
|
ClientMessage::SetDoubleClickDistance { dist } => {
|
||||||
|
self.handle_set_double_click_distance(dist)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ use {
|
||||||
time::now_usec,
|
time::now_usec,
|
||||||
tree::{
|
tree::{
|
||||||
generic_node_visitor, ContainerNode, ContainerSplit, Direction, FloatNode, FoundNode,
|
generic_node_visitor, ContainerNode, ContainerSplit, Direction, FloatNode, FoundNode,
|
||||||
Node, OutputNode, WorkspaceNode,
|
Node, OutputNode, ToplevelNode, WorkspaceNode,
|
||||||
},
|
},
|
||||||
utils::{
|
utils::{
|
||||||
asyncevent::AsyncEvent,
|
asyncevent::AsyncEvent,
|
||||||
|
|
@ -623,6 +623,10 @@ impl WlSeatGlobal {
|
||||||
Some(tl) => tl,
|
Some(tl) => tl,
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
self.set_tl_floating(tl, floating);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_tl_floating(self: &Rc<Self>, tl: Rc<dyn ToplevelNode>, floating: bool) {
|
||||||
let data = tl.tl_data();
|
let data = tl.tl_data();
|
||||||
if data.is_fullscreen.get() {
|
if data.is_fullscreen.get() {
|
||||||
return;
|
return;
|
||||||
|
|
@ -634,15 +638,13 @@ impl WlSeatGlobal {
|
||||||
Some(p) => p,
|
Some(p) => p,
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
if let Some(cn) = parent.node_into_containing_node() {
|
if !floating {
|
||||||
if !floating {
|
parent.cnode_remove_child2(tl.tl_as_node(), true);
|
||||||
cn.cnode_remove_child2(tl.tl_as_node(), true);
|
self.state.map_tiled(tl);
|
||||||
self.state.map_tiled(tl);
|
} else if let Some(ws) = data.workspace.get() {
|
||||||
} else if let Some(ws) = data.workspace.get() {
|
parent.cnode_remove_child2(tl.tl_as_node(), true);
|
||||||
cn.cnode_remove_child2(tl.tl_as_node(), true);
|
let (width, height) = data.float_size(&ws);
|
||||||
let (width, height) = data.float_size(&ws);
|
self.state.map_floating(tl, width, height, &ws, None);
|
||||||
self.state.map_floating(tl, width, height, &ws, None);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -154,6 +154,8 @@ pub struct State {
|
||||||
pub drm_feedback_ids: DrmFeedbackIds,
|
pub drm_feedback_ids: DrmFeedbackIds,
|
||||||
pub direct_scanout_enabled: Cell<bool>,
|
pub direct_scanout_enabled: Cell<bool>,
|
||||||
pub output_transforms: RefCell<AHashMap<Rc<OutputId>, Transform>>,
|
pub output_transforms: RefCell<AHashMap<Rc<OutputId>, Transform>>,
|
||||||
|
pub double_click_interval_usec: Cell<u64>,
|
||||||
|
pub double_click_distance: Cell<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl Drop for State {
|
// impl Drop for State {
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ use {
|
||||||
},
|
},
|
||||||
utils::{
|
utils::{
|
||||||
clonecell::CloneCell,
|
clonecell::CloneCell,
|
||||||
|
double_click_state::DoubleClickState,
|
||||||
errorfmt::ErrorFmt,
|
errorfmt::ErrorFmt,
|
||||||
linkedlist::{LinkedList, LinkedNode, NodeRef},
|
linkedlist::{LinkedList, LinkedNode, NodeRef},
|
||||||
numcell::NumCell,
|
numcell::NumCell,
|
||||||
|
|
@ -146,6 +147,7 @@ struct SeatState {
|
||||||
x: i32,
|
x: i32,
|
||||||
y: i32,
|
y: i32,
|
||||||
op: Option<SeatOp>,
|
op: Option<SeatOp>,
|
||||||
|
double_click_state: DoubleClickState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContainerChild {
|
impl ContainerChild {
|
||||||
|
|
@ -521,6 +523,7 @@ impl ContainerNode {
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
op: None,
|
op: None,
|
||||||
|
double_click_state: Default::default(),
|
||||||
});
|
});
|
||||||
let mut changed = false;
|
let mut changed = false;
|
||||||
changed |= mem::replace(&mut seat_state.x, x) != x;
|
changed |= mem::replace(&mut seat_state.x, x) != x;
|
||||||
|
|
@ -1176,7 +1179,7 @@ impl Node for ContainerNode {
|
||||||
fn node_on_button(
|
fn node_on_button(
|
||||||
self: Rc<Self>,
|
self: Rc<Self>,
|
||||||
seat: &Rc<WlSeatGlobal>,
|
seat: &Rc<WlSeatGlobal>,
|
||||||
_time_usec: u64,
|
time_usec: u64,
|
||||||
button: u32,
|
button: u32,
|
||||||
state: KeyState,
|
state: KeyState,
|
||||||
_serial: u32,
|
_serial: u32,
|
||||||
|
|
@ -1232,6 +1235,15 @@ impl Node for ContainerNode {
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
if seat_data
|
||||||
|
.double_click_state
|
||||||
|
.click(&self.state, time_usec, seat_data.x, seat_data.y)
|
||||||
|
&& kind == SeatOpKind::Move
|
||||||
|
{
|
||||||
|
drop(seat_datas);
|
||||||
|
seat.set_tl_floating(child.node.clone(), true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
seat_data.op = Some(SeatOp { child, kind })
|
seat_data.op = Some(SeatOp { child, kind })
|
||||||
} else if state == KeyState::Released {
|
} else if state == KeyState::Released {
|
||||||
let op = seat_data.op.take().unwrap();
|
let op = seat_data.op.take().unwrap();
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@ use {
|
||||||
StackedNode, ToplevelNode, WorkspaceNode,
|
StackedNode, ToplevelNode, WorkspaceNode,
|
||||||
},
|
},
|
||||||
utils::{
|
utils::{
|
||||||
clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt,
|
clonecell::CloneCell, copyhashmap::CopyHashMap, double_click_state::DoubleClickState,
|
||||||
linkedlist::LinkedNode,
|
errorfmt::ErrorFmt, linkedlist::LinkedNode,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
|
|
@ -57,6 +57,7 @@ struct SeatState {
|
||||||
op_active: bool,
|
op_active: bool,
|
||||||
dist_hor: i32,
|
dist_hor: i32,
|
||||||
dist_ver: i32,
|
dist_ver: i32,
|
||||||
|
double_click_state: DoubleClickState,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
|
@ -228,6 +229,7 @@ impl FloatNode {
|
||||||
op_active: false,
|
op_active: false,
|
||||||
dist_hor: 0,
|
dist_hor: 0,
|
||||||
dist_ver: 0,
|
dist_ver: 0,
|
||||||
|
double_click_state: Default::default(),
|
||||||
});
|
});
|
||||||
seat_state.x = x;
|
seat_state.x = x;
|
||||||
seat_state.y = y;
|
seat_state.y = y;
|
||||||
|
|
@ -462,7 +464,7 @@ impl Node for FloatNode {
|
||||||
fn node_on_button(
|
fn node_on_button(
|
||||||
self: Rc<Self>,
|
self: Rc<Self>,
|
||||||
seat: &Rc<WlSeatGlobal>,
|
seat: &Rc<WlSeatGlobal>,
|
||||||
_time_usec: u64,
|
time_usec: u64,
|
||||||
button: u32,
|
button: u32,
|
||||||
state: KeyState,
|
state: KeyState,
|
||||||
_serial: u32,
|
_serial: u32,
|
||||||
|
|
@ -479,6 +481,17 @@ impl Node for FloatNode {
|
||||||
if state != KeyState::Pressed {
|
if state != KeyState::Pressed {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if seat_data
|
||||||
|
.double_click_state
|
||||||
|
.click(&self.state, time_usec, seat_data.x, seat_data.y)
|
||||||
|
&& seat_data.op_type == OpType::Move
|
||||||
|
{
|
||||||
|
if let Some(tl) = self.child.get() {
|
||||||
|
drop(seat_datas);
|
||||||
|
seat.set_tl_floating(tl, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
seat_data.op_active = true;
|
seat_data.op_active = true;
|
||||||
let pos = self.position.get();
|
let pos = self.position.get();
|
||||||
match seat_data.op_type {
|
match seat_data.op_type {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ pub mod cell_ext;
|
||||||
pub mod clonecell;
|
pub mod clonecell;
|
||||||
pub mod copyhashmap;
|
pub mod copyhashmap;
|
||||||
pub mod debug_fn;
|
pub mod debug_fn;
|
||||||
|
pub mod double_click_state;
|
||||||
pub mod errorfmt;
|
pub mod errorfmt;
|
||||||
pub mod fdcloser;
|
pub mod fdcloser;
|
||||||
pub mod hex;
|
pub mod hex;
|
||||||
|
|
|
||||||
35
src/utils/double_click_state.rs
Normal file
35
src/utils/double_click_state.rs
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
use crate::state::State;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct DoubleClickState {
|
||||||
|
last_click: Option<(u64, i32, i32)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DoubleClickState {
|
||||||
|
pub fn click(&mut self, state: &State, time_usec: u64, x: i32, y: i32) -> bool {
|
||||||
|
let res = self.click_(state, time_usec, x, y);
|
||||||
|
if !res {
|
||||||
|
self.last_click = Some((time_usec, x, y));
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn click_(&mut self, state: &State, time_usec: u64, x: i32, y: i32) -> bool {
|
||||||
|
let Some((last_usec, last_x, last_y)) = self.last_click.take() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if time_usec.wrapping_sub(last_usec) > state.double_click_interval_usec.get() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let max_dist = state.double_click_distance.get();
|
||||||
|
if max_dist < 0 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let dist_x = last_x - x;
|
||||||
|
let dist_y = last_y - y;
|
||||||
|
if dist_x * dist_x + dist_y * dist_y > max_dist * max_dist {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue