mirror of
https://github.com/YaLTeR/niri.git
synced 2024-10-26 20:04:05 +03:00
Compare commits
5 Commits
1735c4e462
...
6ad841cb5f
Author | SHA1 | Date | |
---|---|---|---|
|
6ad841cb5f | ||
|
711cad75e1 | ||
|
1da99f4003 | ||
|
120eaa6c56 | ||
|
fb636ef98d |
@ -75,7 +75,7 @@ pub struct Input {
|
|||||||
#[knuffel(child)]
|
#[knuffel(child)]
|
||||||
pub warp_mouse_to_focus: bool,
|
pub warp_mouse_to_focus: bool,
|
||||||
#[knuffel(child)]
|
#[knuffel(child)]
|
||||||
pub focus_follows_mouse: bool,
|
pub focus_follows_mouse: Option<FocusFollowsMouse>,
|
||||||
#[knuffel(child)]
|
#[knuffel(child)]
|
||||||
pub workspace_auto_back_and_forth: bool,
|
pub workspace_auto_back_and_forth: bool,
|
||||||
}
|
}
|
||||||
@ -289,6 +289,15 @@ pub struct Touch {
|
|||||||
pub map_to_output: Option<String>,
|
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)]
|
#[derive(Debug, Default, Clone, PartialEq)]
|
||||||
pub struct Outputs(pub Vec<Output>);
|
pub struct Outputs(pub Vec<Output>);
|
||||||
|
|
||||||
@ -1805,6 +1814,16 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Animation {
|
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>(
|
fn decode_node<S: knuffel::traits::ErrorSpan>(
|
||||||
node: &knuffel::ast::SpannedNode<S>,
|
node: &knuffel::ast::SpannedNode<S>,
|
||||||
ctx: &mut knuffel::decode::Context<S>,
|
ctx: &mut knuffel::decode::Context<S>,
|
||||||
@ -2429,6 +2448,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> {
|
pub fn set_miette_hook() -> Result<(), miette::InstallError> {
|
||||||
miette::set_hook(Box::new(|_| Box::new(NarratableReportHandler::new())))
|
miette::set_hook(Box::new(|_| Box::new(NarratableReportHandler::new())))
|
||||||
}
|
}
|
||||||
@ -2675,7 +2711,9 @@ mod tests {
|
|||||||
},
|
},
|
||||||
disable_power_key_handling: true,
|
disable_power_key_handling: true,
|
||||||
warp_mouse_to_focus: 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,
|
workspace_auto_back_and_forth: true,
|
||||||
},
|
},
|
||||||
outputs: Outputs(vec![Output {
|
outputs: Outputs(vec![Output {
|
||||||
|
@ -44,7 +44,8 @@ input {
|
|||||||
// warp-mouse-to-focus
|
// warp-mouse-to-focus
|
||||||
|
|
||||||
// Focus windows and outputs automatically when moving the mouse into them.
|
// 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
|
// 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) {
|
pub fn activate_window(&mut self, window: &W::Id) {
|
||||||
let MonitorSet::Normal {
|
let MonitorSet::Normal {
|
||||||
monitors,
|
monitors,
|
||||||
|
@ -660,8 +660,9 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_new_view_offset_for_column(&self, current_x: f64, idx: usize) -> f64 {
|
fn compute_new_view_offset_for_column_fit(&self, current_x: f64, idx: usize) -> f64 {
|
||||||
if self.columns[idx].is_fullscreen {
|
let col = &self.columns[idx];
|
||||||
|
if col.is_fullscreen {
|
||||||
return 0.;
|
return 0.;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -677,7 +678,7 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
final_x + self.working_area.loc.x,
|
final_x + self.working_area.loc.x,
|
||||||
self.working_area.size.w,
|
self.working_area.size.w,
|
||||||
new_col_x,
|
new_col_x,
|
||||||
self.columns[idx].width(),
|
col.width(),
|
||||||
self.options.gaps,
|
self.options.gaps,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -685,6 +686,71 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
new_offset - self.working_area.loc.x
|
new_offset - self.working_area.loc.x
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compute_new_view_offset_for_column_centered(&self, current_x: f64, idx: usize) -> f64 {
|
||||||
|
let col = &self.columns[idx];
|
||||||
|
if col.is_fullscreen {
|
||||||
|
return self.compute_new_view_offset_for_column_fit(current_x, idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
let width = col.width();
|
||||||
|
|
||||||
|
// Columns wider than the view are left-aligned (the fit code can deal with that).
|
||||||
|
if self.working_area.size.w <= width {
|
||||||
|
return self.compute_new_view_offset_for_column_fit(current_x, idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
-(self.working_area.size.w - width) / 2. - self.working_area.loc.x
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_new_view_offset_for_column(
|
||||||
|
&self,
|
||||||
|
current_x: f64,
|
||||||
|
idx: usize,
|
||||||
|
prev_idx: Option<usize>,
|
||||||
|
) -> f64 {
|
||||||
|
match self.options.center_focused_column {
|
||||||
|
CenterFocusedColumn::Always => {
|
||||||
|
self.compute_new_view_offset_for_column_centered(current_x, idx)
|
||||||
|
}
|
||||||
|
CenterFocusedColumn::OnOverflow => {
|
||||||
|
let Some(prev_idx) = prev_idx else {
|
||||||
|
return self.compute_new_view_offset_for_column_fit(current_x, idx);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Always take the left or right neighbor of the target as the source.
|
||||||
|
let source_idx = if prev_idx > idx {
|
||||||
|
min(idx + 1, self.columns.len() - 1)
|
||||||
|
} else {
|
||||||
|
idx.saturating_sub(1)
|
||||||
|
};
|
||||||
|
|
||||||
|
let source_x = self.column_x(source_idx);
|
||||||
|
let source_width = self.columns[source_idx].width();
|
||||||
|
|
||||||
|
let target_x = self.column_x(idx);
|
||||||
|
let target_width = self.columns[idx].width();
|
||||||
|
|
||||||
|
let total_width = if source_x < target_x {
|
||||||
|
// Source is left from target.
|
||||||
|
target_x - source_x + target_width
|
||||||
|
} else {
|
||||||
|
// Source is right from target.
|
||||||
|
source_x - target_x + source_width
|
||||||
|
} + self.options.gaps * 2.;
|
||||||
|
|
||||||
|
// If it fits together, do a normal animation, otherwise center the new column.
|
||||||
|
if total_width <= self.working_area.size.w {
|
||||||
|
self.compute_new_view_offset_for_column_fit(current_x, idx)
|
||||||
|
} else {
|
||||||
|
self.compute_new_view_offset_for_column_centered(current_x, idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CenterFocusedColumn::Never => {
|
||||||
|
self.compute_new_view_offset_for_column_fit(current_x, idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn animate_view_offset(&mut self, current_x: f64, idx: usize, new_view_offset: f64) {
|
fn animate_view_offset(&mut self, current_x: f64, idx: usize, new_view_offset: f64) {
|
||||||
self.animate_view_offset_with_config(
|
self.animate_view_offset_with_config(
|
||||||
current_x,
|
current_x,
|
||||||
@ -728,44 +794,24 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn animate_view_offset_to_column_fit(
|
|
||||||
&mut self,
|
|
||||||
current_x: f64,
|
|
||||||
idx: usize,
|
|
||||||
config: niri_config::Animation,
|
|
||||||
) {
|
|
||||||
let new_view_offset = self.compute_new_view_offset_for_column(current_x, idx);
|
|
||||||
self.animate_view_offset_with_config(current_x, idx, new_view_offset, config);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn animate_view_offset_to_column_centered(
|
fn animate_view_offset_to_column_centered(
|
||||||
&mut self,
|
&mut self,
|
||||||
current_x: f64,
|
current_x: f64,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
config: niri_config::Animation,
|
config: niri_config::Animation,
|
||||||
) {
|
) {
|
||||||
if self.columns.is_empty() {
|
let new_view_offset = self.compute_new_view_offset_for_column_centered(current_x, idx);
|
||||||
return;
|
self.animate_view_offset_with_config(current_x, idx, new_view_offset, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
let col = &self.columns[idx];
|
|
||||||
if col.is_fullscreen {
|
|
||||||
self.animate_view_offset_to_column_fit(current_x, idx, config);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let width = col.width();
|
|
||||||
|
|
||||||
// If the column is wider than the working area, then on commit it will be shifted to left
|
|
||||||
// edge alignment by the usual positioning code, so there's no use in trying to center it
|
|
||||||
// here.
|
|
||||||
if self.working_area.size.w <= width {
|
|
||||||
self.animate_view_offset_to_column_fit(current_x, idx, config);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let new_view_offset = -(self.working_area.size.w - width) / 2. - self.working_area.loc.x;
|
|
||||||
|
|
||||||
|
fn animate_view_offset_to_column_with_config(
|
||||||
|
&mut self,
|
||||||
|
current_x: f64,
|
||||||
|
idx: usize,
|
||||||
|
prev_idx: Option<usize>,
|
||||||
|
config: niri_config::Animation,
|
||||||
|
) {
|
||||||
|
let new_view_offset = self.compute_new_view_offset_for_column(current_x, idx, prev_idx);
|
||||||
self.animate_view_offset_with_config(current_x, idx, new_view_offset, config);
|
self.animate_view_offset_with_config(current_x, idx, new_view_offset, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -783,57 +829,6 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn animate_view_offset_to_column_with_config(
|
|
||||||
&mut self,
|
|
||||||
current_x: f64,
|
|
||||||
idx: usize,
|
|
||||||
prev_idx: Option<usize>,
|
|
||||||
config: niri_config::Animation,
|
|
||||||
) {
|
|
||||||
match self.options.center_focused_column {
|
|
||||||
CenterFocusedColumn::Always => {
|
|
||||||
self.animate_view_offset_to_column_centered(current_x, idx, config)
|
|
||||||
}
|
|
||||||
CenterFocusedColumn::OnOverflow => {
|
|
||||||
let Some(prev_idx) = prev_idx else {
|
|
||||||
self.animate_view_offset_to_column_fit(current_x, idx, config);
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Always take the left or right neighbor of the target as the source.
|
|
||||||
let source_idx = if prev_idx > idx {
|
|
||||||
min(idx + 1, self.columns.len() - 1)
|
|
||||||
} else {
|
|
||||||
idx.saturating_sub(1)
|
|
||||||
};
|
|
||||||
|
|
||||||
let source_x = self.column_x(source_idx);
|
|
||||||
let source_width = self.columns[source_idx].width();
|
|
||||||
|
|
||||||
let target_x = self.column_x(idx);
|
|
||||||
let target_width = self.columns[idx].width();
|
|
||||||
|
|
||||||
let total_width = if source_x < target_x {
|
|
||||||
// Source is left from target.
|
|
||||||
target_x - source_x + target_width
|
|
||||||
} else {
|
|
||||||
// Source is right from target.
|
|
||||||
source_x - target_x + source_width
|
|
||||||
} + self.options.gaps * 2.;
|
|
||||||
|
|
||||||
// If it fits together, do a normal animation, otherwise center the new column.
|
|
||||||
if total_width <= self.working_area.size.w {
|
|
||||||
self.animate_view_offset_to_column_fit(current_x, idx, config);
|
|
||||||
} else {
|
|
||||||
self.animate_view_offset_to_column_centered(current_x, idx, config);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CenterFocusedColumn::Never => {
|
|
||||||
self.animate_view_offset_to_column_fit(current_x, idx, config)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn activate_column(&mut self, idx: usize) {
|
fn activate_column(&mut self, idx: usize) {
|
||||||
self.activate_column_with_anim_config(
|
self.activate_column_with_anim_config(
|
||||||
idx,
|
idx,
|
||||||
@ -913,31 +908,24 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
is_full_width,
|
is_full_width,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
let width = column.width();
|
|
||||||
self.data.insert(col_idx, ColumnData::new(&column));
|
self.data.insert(col_idx, ColumnData::new(&column));
|
||||||
self.columns.insert(col_idx, column);
|
self.columns.insert(col_idx, column);
|
||||||
|
|
||||||
if activate {
|
if activate {
|
||||||
// If this is the first window on an empty workspace, skip the animation from whatever
|
// If this is the first window on an empty workspace, remove the effect of whatever
|
||||||
// view_offset was left over.
|
// view_offset was left over and skip the animation.
|
||||||
if was_empty {
|
if was_empty {
|
||||||
if self.options.center_focused_column == CenterFocusedColumn::Always {
|
self.view_offset = 0.;
|
||||||
self.view_offset =
|
|
||||||
-(self.working_area.size.w - width) / 2. - self.working_area.loc.x;
|
|
||||||
} else {
|
|
||||||
// Try to make the code produce a left-aligned offset, even in presence of left
|
|
||||||
// exclusive zones.
|
|
||||||
self.view_offset = self.compute_new_view_offset_for_column(self.column_x(0), 0);
|
|
||||||
}
|
|
||||||
self.view_offset_adj = None;
|
self.view_offset_adj = None;
|
||||||
|
self.view_offset =
|
||||||
|
self.compute_new_view_offset_for_column(self.view_pos(), col_idx, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let prev_offset = (!was_empty).then(|| self.static_view_offset());
|
let prev_offset = (!was_empty).then(|| self.static_view_offset());
|
||||||
|
|
||||||
self.activate_column_with_anim_config(
|
let anim_config =
|
||||||
col_idx,
|
anim_config.unwrap_or(self.options.animations.horizontal_view_movement.0);
|
||||||
anim_config.unwrap_or(self.options.animations.horizontal_view_movement.0),
|
self.activate_column_with_anim_config(col_idx, anim_config);
|
||||||
);
|
|
||||||
self.activate_prev_column_on_removal = prev_offset;
|
self.activate_prev_column_on_removal = prev_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1054,23 +1042,17 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
|
|
||||||
column.update_config(self.scale.fractional_scale(), self.options.clone());
|
column.update_config(self.scale.fractional_scale(), self.options.clone());
|
||||||
column.set_view_size(self.view_size, self.working_area);
|
column.set_view_size(self.view_size, self.working_area);
|
||||||
let width = column.width();
|
|
||||||
self.data.insert(idx, ColumnData::new(&column));
|
self.data.insert(idx, ColumnData::new(&column));
|
||||||
self.columns.insert(idx, column);
|
self.columns.insert(idx, column);
|
||||||
|
|
||||||
if activate {
|
if activate {
|
||||||
// If this is the first window on an empty workspace, skip the animation from whatever
|
// If this is the first window on an empty workspace, remove the effect of whatever
|
||||||
// view_offset was left over.
|
// view_offset was left over and skip the animation.
|
||||||
if was_empty {
|
if was_empty {
|
||||||
if self.options.center_focused_column == CenterFocusedColumn::Always {
|
self.view_offset = 0.;
|
||||||
self.view_offset =
|
|
||||||
-(self.working_area.size.w - width) / 2. - self.working_area.loc.x;
|
|
||||||
} else {
|
|
||||||
// Try to make the code produce a left-aligned offset, even in presence of left
|
|
||||||
// exclusive zones.
|
|
||||||
self.view_offset = self.compute_new_view_offset_for_column(self.column_x(0), 0);
|
|
||||||
}
|
|
||||||
self.view_offset_adj = None;
|
self.view_offset_adj = None;
|
||||||
|
self.view_offset =
|
||||||
|
self.compute_new_view_offset_for_column(self.view_pos(), idx, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let prev_offset = (!was_empty).then(|| self.static_view_offset());
|
let prev_offset = (!was_empty).then(|| self.static_view_offset());
|
||||||
@ -1409,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) {
|
pub fn activate_window(&mut self, window: &W::Id) {
|
||||||
let column_idx = self
|
let column_idx = self
|
||||||
.columns
|
.columns
|
||||||
@ -1947,6 +1960,10 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn center_column(&mut self) {
|
pub fn center_column(&mut self) {
|
||||||
|
if self.columns.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let center_x = self.view_pos();
|
let center_x = self.view_pos();
|
||||||
self.animate_view_offset_to_column_centered(
|
self.animate_view_offset_to_column_centered(
|
||||||
center_x,
|
center_x,
|
||||||
@ -1954,10 +1971,8 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
self.options.animations.horizontal_view_movement.0,
|
self.options.animations.horizontal_view_movement.0,
|
||||||
);
|
);
|
||||||
|
|
||||||
if !self.columns.is_empty() {
|
let col = &mut self.columns[self.active_column_idx];
|
||||||
let col = &mut self.columns[self.active_column_idx];
|
cancel_resize_for_column(&mut self.interactive_resize, col);
|
||||||
cancel_resize_for_column(&mut self.interactive_resize, col);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view_pos(&self) -> f64 {
|
fn view_pos(&self) -> f64 {
|
||||||
|
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) {
|
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;
|
return;
|
||||||
}
|
};
|
||||||
|
|
||||||
let pointer = &self.seat.get_pointer().unwrap();
|
let pointer = &self.seat.get_pointer().unwrap();
|
||||||
if pointer.is_grabbed() {
|
if pointer.is_grabbed() {
|
||||||
@ -4160,6 +4160,12 @@ impl Niri {
|
|||||||
|
|
||||||
if let Some(window) = &new_focus.window {
|
if let Some(window) = &new_focus.window {
|
||||||
if current_focus.window.as_ref() != Some(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);
|
self.layout.activate_window(window);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ input {
|
|||||||
|
|
||||||
// disable-power-key-handling
|
// disable-power-key-handling
|
||||||
// warp-mouse-to-focus
|
// warp-mouse-to-focus
|
||||||
// focus-follows-mouse
|
// focus-follows-mouse max-scroll-amount="0%"
|
||||||
// workspace-auto-back-and-forth
|
// 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`
|
#### `workspace-auto-back-and-forth`
|
||||||
|
|
||||||
Normally, switching to the same workspace by index twice will do nothing (since you're already on that workspace).
|
Normally, switching to the same workspace by index twice will do nothing (since you're already on that workspace).
|
||||||
|
@ -50,7 +50,7 @@ For this reason, most of the default keys use the `Mod` modifier.
|
|||||||
> Here, look at `sym: Left` and `sym: Right`: these are the key names.
|
> Here, look at `sym: Left` and `sym: Right`: these are the key names.
|
||||||
> I was pressing the left and the right arrow in this example.
|
> I was pressing the left and the right arrow in this example.
|
||||||
|
|
||||||
<sup>Since: 0.1.7</sup> Binds will repeat by default (i.e. holding down a bind will make it trigger repeatedly).
|
<sup>Since: 0.1.8</sup> Binds will repeat by default (i.e. holding down a bind will make it trigger repeatedly).
|
||||||
You can disable that for specific binds with `repeat=false`:
|
You can disable that for specific binds with `repeat=false`:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
Loading…
Reference in New Issue
Block a user