mirror of
https://github.com/YaLTeR/niri.git
synced 2024-08-18 00:30:24 +03:00
Implement focus-follows-mouse max-scroll-amount
This commit is contained in:
parent
120eaa6c56
commit
1da99f4003
@ -75,7 +75,7 @@ pub struct Input {
|
||||
#[knuffel(child)]
|
||||
pub warp_mouse_to_focus: bool,
|
||||
#[knuffel(child)]
|
||||
pub focus_follows_mouse: bool,
|
||||
pub focus_follows_mouse: Option<FocusFollowsMouse>,
|
||||
#[knuffel(child)]
|
||||
pub workspace_auto_back_and_forth: bool,
|
||||
}
|
||||
@ -289,6 +289,15 @@ pub struct Touch {
|
||||
pub map_to_output: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
|
||||
pub struct FocusFollowsMouse {
|
||||
#[knuffel(property, str)]
|
||||
pub max_scroll_amount: Option<Percent>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct Percent(pub f64);
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq)]
|
||||
pub struct Outputs(pub Vec<Output>);
|
||||
|
||||
@ -1794,6 +1803,16 @@ where
|
||||
}
|
||||
|
||||
impl Animation {
|
||||
pub fn new_off() -> Self {
|
||||
Self {
|
||||
off: true,
|
||||
kind: AnimationKind::Easing(EasingParams {
|
||||
duration_ms: 0,
|
||||
curve: AnimationCurve::Linear,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_node<S: knuffel::traits::ErrorSpan>(
|
||||
node: &knuffel::ast::SpannedNode<S>,
|
||||
ctx: &mut knuffel::decode::Context<S>,
|
||||
@ -2418,6 +2437,23 @@ impl FromStr for TapButtonMap {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Percent {
|
||||
type Err = miette::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let Some((value, empty)) = s.split_once('%') else {
|
||||
return Err(miette!("value must end with '%'"));
|
||||
};
|
||||
|
||||
if !empty.is_empty() {
|
||||
return Err(miette!("trailing characters after '%' are not allowed"));
|
||||
}
|
||||
|
||||
let value: f64 = value.parse().map_err(|_| miette!("error parsing value"))?;
|
||||
Ok(Percent(value / 100.))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_miette_hook() -> Result<(), miette::InstallError> {
|
||||
miette::set_hook(Box::new(|_| Box::new(NarratableReportHandler::new())))
|
||||
}
|
||||
@ -2664,7 +2700,9 @@ mod tests {
|
||||
},
|
||||
disable_power_key_handling: true,
|
||||
warp_mouse_to_focus: true,
|
||||
focus_follows_mouse: true,
|
||||
focus_follows_mouse: Some(FocusFollowsMouse {
|
||||
max_scroll_amount: None,
|
||||
}),
|
||||
workspace_auto_back_and_forth: true,
|
||||
},
|
||||
outputs: Outputs(vec![Output {
|
||||
|
@ -44,7 +44,8 @@ input {
|
||||
// warp-mouse-to-focus
|
||||
|
||||
// Focus windows and outputs automatically when moving the mouse into them.
|
||||
// focus-follows-mouse
|
||||
// Setting max-scroll-amount="0%" makes it work only on windows already fully on screen.
|
||||
// focus-follows-mouse max-scroll-amount="0%"
|
||||
}
|
||||
|
||||
// You can configure outputs by their name, which you can find
|
||||
|
@ -947,6 +947,22 @@ impl<W: LayoutElement> Layout<W> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scroll_amount_to_activate(&self, window: &W::Id) -> f64 {
|
||||
let MonitorSet::Normal { monitors, .. } = &self.monitor_set else {
|
||||
return 0.;
|
||||
};
|
||||
|
||||
for mon in monitors {
|
||||
for ws in &mon.workspaces {
|
||||
if ws.has_window(window) {
|
||||
return ws.scroll_amount_to_activate(window);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
0.
|
||||
}
|
||||
|
||||
pub fn activate_window(&mut self, window: &W::Id) {
|
||||
let MonitorSet::Normal {
|
||||
monitors,
|
||||
|
@ -1391,6 +1391,37 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scroll_amount_to_activate(&self, window: &W::Id) -> f64 {
|
||||
let column_idx = self
|
||||
.columns
|
||||
.iter()
|
||||
.position(|col| col.contains(window))
|
||||
.unwrap();
|
||||
|
||||
if self.active_column_idx == column_idx {
|
||||
return 0.;
|
||||
}
|
||||
|
||||
let current_x = self.view_pos();
|
||||
let new_view_offset = self.compute_new_view_offset_for_column(
|
||||
current_x,
|
||||
column_idx,
|
||||
Some(self.active_column_idx),
|
||||
);
|
||||
|
||||
// Consider the end of an ongoing animation because that's what compute to fit does too.
|
||||
let final_x = if let Some(ViewOffsetAdjustment::Animation(anim)) = &self.view_offset_adj {
|
||||
current_x - self.view_offset + anim.to()
|
||||
} else {
|
||||
current_x
|
||||
};
|
||||
|
||||
let new_col_x = self.column_x(column_idx);
|
||||
let from_view_offset = final_x - new_col_x;
|
||||
|
||||
(from_view_offset - new_view_offset).abs() / self.working_area.size.w
|
||||
}
|
||||
|
||||
pub fn activate_window(&mut self, window: &W::Id) {
|
||||
let column_idx = self
|
||||
.columns
|
||||
|
10
src/niri.rs
10
src/niri.rs
@ -4140,9 +4140,9 @@ impl Niri {
|
||||
}
|
||||
|
||||
pub fn handle_focus_follows_mouse(&mut self, new_focus: &PointerFocus) {
|
||||
if !self.config.borrow().input.focus_follows_mouse {
|
||||
let Some(ffm) = self.config.borrow().input.focus_follows_mouse else {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let pointer = &self.seat.get_pointer().unwrap();
|
||||
if pointer.is_grabbed() {
|
||||
@ -4160,6 +4160,12 @@ impl Niri {
|
||||
|
||||
if let Some(window) = &new_focus.window {
|
||||
if current_focus.window.as_ref() != Some(window) {
|
||||
if let Some(threshold) = ffm.max_scroll_amount {
|
||||
if self.layout.scroll_amount_to_activate(window) > threshold.0 {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self.layout.activate_window(window);
|
||||
}
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ input {
|
||||
|
||||
// disable-power-key-handling
|
||||
// warp-mouse-to-focus
|
||||
// focus-follows-mouse
|
||||
// focus-follows-mouse max-scroll-amount="0%"
|
||||
// workspace-auto-back-and-forth
|
||||
}
|
||||
```
|
||||
@ -207,6 +207,24 @@ input {
|
||||
}
|
||||
```
|
||||
|
||||
<sup>Since: 0.1.8</sup> You can optionally set `max-scroll-amount`.
|
||||
Then, focus-follows-mouse won't focus a window if it will result in the view scrolling more than the set amount.
|
||||
The value is a percentage of the working area width.
|
||||
|
||||
```
|
||||
input {
|
||||
// Allow focus-follows-mouse when it results in scrolling at most 10% of the screen.
|
||||
focus-follows-mouse max-scroll-amount="10%"
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
input {
|
||||
// Allow focus-follows-mouse only when it will not scroll the view.
|
||||
focus-follows-mouse max-scroll-amount="0%"
|
||||
}
|
||||
```
|
||||
|
||||
#### `workspace-auto-back-and-forth`
|
||||
|
||||
Normally, switching to the same workspace by index twice will do nothing (since you're already on that workspace).
|
||||
|
Loading…
Reference in New Issue
Block a user