mirror of
https://github.com/YaLTeR/niri.git
synced 2024-09-11 12:35:58 +03:00
Add focus change animations
This commit is contained in:
parent
c65a4f1624
commit
0e3b800337
@ -10,6 +10,7 @@ edition = "2021"
|
||||
anyhow = { version = "1.0.72", features = ["backtrace"] }
|
||||
bitflags = "2.3.3"
|
||||
clap = { version = "4.3.21", features = ["derive"] }
|
||||
keyframe = { version = "1.1.1", default-features = false }
|
||||
profiling = "1.0.9"
|
||||
smithay-drm-extras = { version = "0.1.0", path = "../smithay/smithay-drm-extras" }
|
||||
tracing = "0.1.37"
|
||||
|
46
src/animation.rs
Normal file
46
src/animation.rs
Normal file
@ -0,0 +1,46 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use keyframe::functions::EaseOutCubic;
|
||||
use keyframe::EasingFunction;
|
||||
|
||||
use crate::utils::get_monotonic_time;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Animation {
|
||||
from: f64,
|
||||
to: f64,
|
||||
duration: Duration,
|
||||
start_time: Duration,
|
||||
current_time: Duration,
|
||||
}
|
||||
|
||||
impl Animation {
|
||||
pub fn new(from: f64, to: f64, over: Duration) -> Self {
|
||||
// FIXME: ideally we shouldn't use current time here because animations started within the
|
||||
// same frame cycle should have the same start time to be synchronized.
|
||||
let now = get_monotonic_time();
|
||||
|
||||
Self {
|
||||
from,
|
||||
to,
|
||||
duration: over,
|
||||
start_time: now,
|
||||
current_time: now,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_current_time(&mut self, time: Duration) {
|
||||
self.current_time = time;
|
||||
}
|
||||
|
||||
pub fn is_done(&self) -> bool {
|
||||
self.current_time >= self.start_time + self.duration
|
||||
}
|
||||
|
||||
pub fn value(&self) -> f64 {
|
||||
let passed = (self.current_time - self.start_time).as_secs_f64();
|
||||
let total = self.duration.as_secs_f64();
|
||||
let x = (passed / total).clamp(0., 1.);
|
||||
EaseOutCubic.y(x) * (self.to - self.from) + self.from
|
||||
}
|
||||
}
|
@ -44,6 +44,8 @@ use smithay::utils::{Logical, Point, Rectangle, Scale, Size};
|
||||
use smithay::wayland::compositor::{with_states, SurfaceData};
|
||||
use smithay::wayland::shell::xdg::XdgToplevelSurfaceData;
|
||||
|
||||
use crate::animation::Animation;
|
||||
|
||||
const PADDING: i32 = 16;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@ -114,6 +116,9 @@ pub struct Workspace<W: LayoutElement> {
|
||||
|
||||
/// Offset of the view computed from the active column.
|
||||
view_offset: i32,
|
||||
|
||||
/// Animation of the view offset, if one is currently ongoing.
|
||||
view_offset_anim: Option<Animation>,
|
||||
}
|
||||
|
||||
/// Width of a column.
|
||||
@ -770,6 +775,20 @@ impl<W: LayoutElement> MonitorSet<W> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn workspace_for_output_mut(&mut self, output: &Output) -> Option<&mut Workspace<W>> {
|
||||
let MonitorSet::Normal { monitors, .. } = self else {
|
||||
return None;
|
||||
};
|
||||
|
||||
monitors.iter_mut().find_map(|monitor| {
|
||||
if &monitor.output == output {
|
||||
Some(&mut monitor.workspaces[monitor.active_workspace_idx])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn window_under(
|
||||
&self,
|
||||
output: &Output,
|
||||
@ -868,6 +887,20 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
columns: vec![],
|
||||
active_column_idx: 0,
|
||||
view_offset: 0,
|
||||
view_offset_anim: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn advance_animations(&mut self, current_time: Duration) {
|
||||
match &mut self.view_offset_anim {
|
||||
Some(anim) => {
|
||||
anim.set_current_time(current_time);
|
||||
self.view_offset = anim.value().round() as i32;
|
||||
if anim.is_done() {
|
||||
self.view_offset_anim = None;
|
||||
}
|
||||
}
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
|
||||
@ -918,6 +951,25 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
}
|
||||
}
|
||||
|
||||
fn activate_column(&mut self, idx: usize) {
|
||||
if self.active_column_idx == idx {
|
||||
return;
|
||||
}
|
||||
|
||||
let current_x = self.view_pos();
|
||||
|
||||
self.active_column_idx = idx;
|
||||
|
||||
self.view_offset = 0;
|
||||
let new_x = self.view_pos();
|
||||
|
||||
self.view_offset_anim = Some(Animation::new(
|
||||
(current_x - new_x) as f64,
|
||||
0.,
|
||||
Duration::from_millis(250),
|
||||
));
|
||||
}
|
||||
|
||||
fn has_windows(&self) -> bool {
|
||||
self.windows().next().is_some()
|
||||
}
|
||||
@ -954,7 +1006,7 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
self.columns.insert(idx, column);
|
||||
|
||||
if activate {
|
||||
self.active_column_idx = idx;
|
||||
self.activate_column(idx);
|
||||
}
|
||||
}
|
||||
|
||||
@ -978,7 +1030,7 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
return;
|
||||
}
|
||||
|
||||
self.active_column_idx = min(self.active_column_idx, self.columns.len() - 1);
|
||||
self.activate_column(min(self.active_column_idx, self.columns.len() - 1));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1004,22 +1056,24 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
let column = &mut self.columns[column_idx];
|
||||
|
||||
column.activate_window(window);
|
||||
self.active_column_idx = column_idx;
|
||||
self.activate_column(column_idx);
|
||||
}
|
||||
|
||||
fn verify_invariants(&self) {
|
||||
assert!(self.view_size.w > 0);
|
||||
assert!(self.view_size.h > 0);
|
||||
|
||||
assert!(self.columns.is_empty() || self.active_column_idx < self.columns.len());
|
||||
if !self.columns.is_empty() {
|
||||
assert!(self.active_column_idx < self.columns.len());
|
||||
|
||||
for column in &self.columns {
|
||||
column.verify_invariants();
|
||||
for column in &self.columns {
|
||||
column.verify_invariants();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn focus_left(&mut self) {
|
||||
self.active_column_idx = self.active_column_idx.saturating_sub(1);
|
||||
self.activate_column(self.active_column_idx.saturating_sub(1));
|
||||
}
|
||||
|
||||
fn focus_right(&mut self) {
|
||||
@ -1027,7 +1081,7 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
return;
|
||||
}
|
||||
|
||||
self.active_column_idx = min(self.active_column_idx + 1, self.columns.len() - 1);
|
||||
self.activate_column(min(self.active_column_idx + 1, self.columns.len() - 1));
|
||||
}
|
||||
|
||||
fn focus_down(&mut self) {
|
||||
@ -1053,7 +1107,7 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
}
|
||||
|
||||
self.columns.swap(self.active_column_idx, new_idx);
|
||||
self.active_column_idx = new_idx;
|
||||
self.activate_column(new_idx);
|
||||
}
|
||||
|
||||
fn move_right(&mut self) {
|
||||
@ -1067,7 +1121,7 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
}
|
||||
|
||||
self.columns.swap(self.active_column_idx, new_idx);
|
||||
self.active_column_idx = new_idx;
|
||||
self.activate_column(new_idx);
|
||||
}
|
||||
|
||||
fn move_down(&mut self) {
|
||||
|
@ -3,6 +3,7 @@ extern crate tracing;
|
||||
|
||||
mod handlers;
|
||||
|
||||
mod animation;
|
||||
mod backend;
|
||||
mod frame_clock;
|
||||
mod grabs;
|
||||
|
@ -302,11 +302,14 @@ impl Niri {
|
||||
fn redraw(&mut self, backend: &mut dyn Backend, output: &Output) {
|
||||
let _span = tracy_client::span!("redraw");
|
||||
let state = self.output_state.get_mut(output).unwrap();
|
||||
let presentation_time = state.frame_clock.next_presentation_time();
|
||||
|
||||
assert!(state.queued_redraw.take().is_some());
|
||||
assert!(!state.waiting_for_vblank);
|
||||
|
||||
let elements = self.monitor_set.render_elements(backend.renderer(), output);
|
||||
let ws = self.monitor_set.workspace_for_output_mut(output).unwrap();
|
||||
ws.advance_animations(presentation_time);
|
||||
let elements = ws.render_elements(backend.renderer());
|
||||
|
||||
let output_pos = self.global_space.output_geometry(output).unwrap().loc;
|
||||
let pointer_pos = self.seat.get_pointer().unwrap().current_location() - output_pos.to_f64();
|
||||
|
Loading…
Reference in New Issue
Block a user