aboutsummaryrefslogtreecommitdiff
path: root/src/layout
diff options
context:
space:
mode:
Diffstat (limited to 'src/layout')
-rw-r--r--src/layout/closing_window.rs7
-rw-r--r--src/layout/mod.rs28
-rw-r--r--src/layout/monitor.rs9
-rw-r--r--src/layout/opening_window.rs5
-rw-r--r--src/layout/tile.rs19
-rw-r--r--src/layout/workspace.rs339
6 files changed, 204 insertions, 203 deletions
diff --git a/src/layout/closing_window.rs b/src/layout/closing_window.rs
index 744c099e..12a77f92 100644
--- a/src/layout/closing_window.rs
+++ b/src/layout/closing_window.rs
@@ -1,5 +1,4 @@
use std::collections::HashMap;
-use std::time::Duration;
use anyhow::Context as _;
use glam::{Mat3, Vec2};
@@ -138,15 +137,15 @@ impl ClosingWindow {
})
}
- pub fn advance_animations(&mut self, current_time: Duration) {
+ pub fn advance_animations(&mut self) {
match &mut self.anim_state {
AnimationState::Waiting { blocker, anim } => {
if blocker.state() != BlockerState::Pending {
- let anim = anim.restarted(current_time, 0., 1., 0.);
+ let anim = anim.restarted(0., 1., 0.);
self.anim_state = AnimationState::Animating(anim);
}
}
- AnimationState::Animating(anim) => anim.set_current_time(current_time),
+ AnimationState::Animating(_anim) => (),
}
}
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index f4e59af3..45228038 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -219,6 +219,8 @@ pub struct Layout<W: LayoutElement> {
interactive_move: Option<InteractiveMoveState<W>>,
/// Clock for driving animations.
clock: Clock,
+ /// Time that we last updated render elements for.
+ update_render_elements_time: Duration,
/// Configurable properties of the layout.
options: Rc<Options>,
}
@@ -447,6 +449,7 @@ impl<W: LayoutElement> Layout<W> {
last_active_workspace_id: HashMap::new(),
interactive_move: None,
clock,
+ update_render_elements_time: Duration::ZERO,
options: Rc::new(options),
}
}
@@ -468,6 +471,7 @@ impl<W: LayoutElement> Layout<W> {
last_active_workspace_id: HashMap::new(),
interactive_move: None,
clock,
+ update_render_elements_time: Duration::ZERO,
options: opts,
}
}
@@ -2194,22 +2198,22 @@ impl<W: LayoutElement> Layout<W> {
}
}
- pub fn advance_animations(&mut self, current_time: Duration) {
+ pub fn advance_animations(&mut self) {
let _span = tracy_client::span!("Layout::advance_animations");
if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move {
- move_.tile.advance_animations(current_time);
+ move_.tile.advance_animations();
}
match &mut self.monitor_set {
MonitorSet::Normal { monitors, .. } => {
for mon in monitors {
- mon.advance_animations(current_time);
+ mon.advance_animations();
}
}
MonitorSet::NoOutputs { workspaces, .. } => {
for ws in workspaces {
- ws.advance_animations(current_time);
+ ws.advance_animations();
}
}
}
@@ -2242,6 +2246,8 @@ impl<W: LayoutElement> Layout<W> {
pub fn update_render_elements(&mut self, output: Option<&Output>) {
let _span = tracy_client::span!("Layout::update_render_elements");
+ self.update_render_elements_time = self.clock.now();
+
if let Some(InteractiveMoveState::Moving(move_)) = &mut self.interactive_move {
if output.map_or(true, |output| move_.output == *output) {
let pos_within_output = move_.tile_render_location();
@@ -3475,6 +3481,10 @@ impl<W: LayoutElement> Layout<W> {
output: &Output,
target: RenderTarget,
) -> impl Iterator<Item = TileRenderElement<R>> {
+ if self.update_render_elements_time != self.clock.now() {
+ error!("clock moved between updating render elements and rendering");
+ }
+
let mut rv = None;
if let Some(InteractiveMoveState::Moving(move_)) = &self.interactive_move {
@@ -3653,7 +3663,7 @@ mod tests {
impl<W: LayoutElement> Default for Layout<W> {
fn default() -> Self {
- Self::with_options(Clock::with_override(Duration::ZERO), Default::default())
+ Self::with_options(Clock::with_time(Duration::ZERO), Default::default())
}
}
@@ -4553,14 +4563,14 @@ mod tests {
layout.refresh(is_active);
}
Op::AdvanceAnimations { msec_delta } => {
- let mut now = layout.clock.now();
+ let mut now = layout.clock.now_unadjusted();
if msec_delta >= 0 {
now = now.saturating_add(Duration::from_millis(msec_delta as u64));
} else {
now = now.saturating_sub(Duration::from_millis(-msec_delta as u64));
}
- layout.clock.set_time_override(Some(now));
- layout.advance_animations(now);
+ layout.clock.set_unadjusted(now);
+ layout.advance_animations();
}
Op::MoveWorkspaceToOutput(id) => {
let name = format!("output{id}");
@@ -4674,7 +4684,7 @@ mod tests {
#[track_caller]
fn check_ops_with_options(options: Options, ops: &[Op]) {
- let mut layout = Layout::with_options(Clock::with_override(Duration::ZERO), options);
+ let mut layout = Layout::with_options(Clock::with_time(Duration::ZERO), options);
for op in ops {
op.apply(&mut layout);
diff --git a/src/layout/monitor.rs b/src/layout/monitor.rs
index aad921bb..52e73cce 100644
--- a/src/layout/monitor.rs
+++ b/src/layout/monitor.rs
@@ -184,7 +184,7 @@ impl<W: LayoutElement> Monitor<W> {
self.active_workspace_idx = idx;
self.workspace_switch = Some(WorkspaceSwitch::Animation(Animation::new(
- self.clock.now(),
+ self.clock.clone(),
current_idx,
idx as f64,
0.,
@@ -734,9 +734,8 @@ impl<W: LayoutElement> Monitor<W> {
Some(column.tiles[column.active_tile_idx].window())
}
- pub fn advance_animations(&mut self, current_time: Duration) {
+ pub fn advance_animations(&mut self) {
if let Some(WorkspaceSwitch::Animation(anim)) = &mut self.workspace_switch {
- anim.set_current_time(current_time);
if anim.is_done() {
self.workspace_switch = None;
self.clean_up_workspaces();
@@ -744,7 +743,7 @@ impl<W: LayoutElement> Monitor<W> {
}
for ws in &mut self.workspaces {
- ws.advance_animations(current_time);
+ ws.advance_animations();
}
}
@@ -1112,7 +1111,7 @@ impl<W: LayoutElement> Monitor<W> {
self.active_workspace_idx = new_idx;
self.workspace_switch = Some(WorkspaceSwitch::Animation(Animation::new(
- self.clock.now(),
+ self.clock.clone(),
gesture.current_idx,
new_idx as f64,
velocity,
diff --git a/src/layout/opening_window.rs b/src/layout/opening_window.rs
index 90e50581..0a3d4973 100644
--- a/src/layout/opening_window.rs
+++ b/src/layout/opening_window.rs
@@ -1,5 +1,4 @@
use std::collections::HashMap;
-use std::time::Duration;
use anyhow::Context as _;
use glam::{Mat3, Vec2};
@@ -41,9 +40,7 @@ impl OpenAnimation {
}
}
- pub fn advance_animations(&mut self, current_time: Duration) {
- self.anim.set_current_time(current_time);
- }
+ pub fn advance_animations(&mut self) {}
pub fn is_done(&self) -> bool {
self.anim.is_done()
diff --git a/src/layout/tile.rs b/src/layout/tile.rs
index dd3d50af..6646999f 100644
--- a/src/layout/tile.rs
+++ b/src/layout/tile.rs
@@ -1,5 +1,4 @@
use std::rc::Rc;
-use std::time::Duration;
use niri_config::{Color, CornerRadius, GradientInterpolation};
use smithay::backend::allocator::Fourcc;
@@ -185,7 +184,7 @@ impl<W: LayoutElement> Tile<W> {
let change = f64::max(change.x.abs(), change.y.abs());
if change > RESIZE_ANIMATION_THRESHOLD {
let anim = Animation::new(
- self.clock.now(),
+ self.clock.clone(),
0.,
1.,
0.,
@@ -218,29 +217,25 @@ impl<W: LayoutElement> Tile<W> {
self.rounded_corner_damage.set_size(window_size);
}
- pub fn advance_animations(&mut self, current_time: Duration) {
+ pub fn advance_animations(&mut self) {
if let Some(open) = &mut self.open_animation {
- open.advance_animations(current_time);
if open.is_done() {
self.open_animation = None;
}
}
if let Some(resize) = &mut self.resize_animation {
- resize.anim.set_current_time(current_time);
if resize.anim.is_done() {
self.resize_animation = None;
}
}
if let Some(move_) = &mut self.move_x_animation {
- move_.anim.set_current_time(current_time);
if move_.anim.is_done() {
self.move_x_animation = None;
}
}
if let Some(move_) = &mut self.move_y_animation {
- move_.anim.set_current_time(current_time);
if move_.anim.is_done() {
self.move_y_animation = None;
}
@@ -326,7 +321,7 @@ impl<W: LayoutElement> Tile<W> {
pub fn start_open_animation(&mut self) {
self.open_animation = Some(OpenAnimation::new(Animation::new(
- self.clock.now(),
+ self.clock.clone(),
0.,
1.,
0.,
@@ -353,8 +348,8 @@ impl<W: LayoutElement> Tile<W> {
// Preserve the previous config if ongoing.
let anim = self.move_x_animation.take().map(|move_| move_.anim);
let anim = anim
- .map(|anim| anim.restarted(self.clock.now(), 1., 0., 0.))
- .unwrap_or_else(|| Animation::new(self.clock.now(), 1., 0., 0., config));
+ .map(|anim| anim.restarted(1., 0., 0.))
+ .unwrap_or_else(|| Animation::new(self.clock.clone(), 1., 0., 0., config));
self.move_x_animation = Some(MoveAnimation {
anim,
@@ -372,8 +367,8 @@ impl<W: LayoutElement> Tile<W> {
// Preserve the previous config if ongoing.
let anim = self.move_y_animation.take().map(|move_| move_.anim);
let anim = anim
- .map(|anim| anim.restarted(self.clock.now(), 1., 0., 0.))
- .unwrap_or_else(|| Animation::new(self.clock.now(), 1., 0., 0., config));
+ .map(|anim| anim.restarted(1., 0., 0.))
+ .unwrap_or_else(|| Animation::new(self.clock.clone(), 1., 0., 0., config));
self.move_y_animation = Some(MoveAnimation {
anim,
diff --git a/src/layout/workspace.rs b/src/layout/workspace.rs
index 6432ca8b..2b3a462c 100644
--- a/src/layout/workspace.rs
+++ b/src/layout/workspace.rs
@@ -84,10 +84,7 @@ pub struct Workspace<W: LayoutElement> {
/// Any gaps, including left padding from work area left exclusive zone, is handled
/// with this view offset (rather than added as a constant elsewhere in the code). This allows
/// for natural handling of fullscreen windows, which must ignore work area padding.
- view_offset: f64,
-
- /// Adjustment of the view offset, if one is currently ongoing.
- view_offset_adj: Option<ViewOffsetAdjustment>,
+ view_offset: ViewOffset,
/// Whether to activate the previous, rather than the next, column upon column removal.
///
@@ -188,8 +185,12 @@ struct ColumnData {
}
#[derive(Debug)]
-enum ViewOffsetAdjustment {
+enum ViewOffset {
+ /// The view offset is static.
+ Static(f64),
+ /// The view offset is animating.
Animation(Animation),
+ /// The view offset is controlled by the ongoing gesture.
Gesture(ViewGesture),
}
@@ -199,7 +200,7 @@ struct ViewGesture {
tracker: SwipeTracker,
delta_from_tracker: f64,
// The view offset we'll use if needed for activate_prev_column_on_removal.
- static_view_offset: f64,
+ stationary_view_offset: f64,
/// Whether the gesture is controlled by the touchpad.
is_touchpad: bool,
}
@@ -325,17 +326,70 @@ impl OutputId {
}
}
-impl ViewOffsetAdjustment {
+impl ViewOffset {
+ /// Returns the current view offset.
+ pub fn current(&self) -> f64 {
+ match self {
+ ViewOffset::Static(offset) => *offset,
+ ViewOffset::Animation(anim) => anim.value(),
+ ViewOffset::Gesture(gesture) => gesture.current_view_offset,
+ }
+ }
+
+ /// Returns the target view offset suitable for computing the new view offset.
+ pub fn target(&self) -> f64 {
+ match self {
+ ViewOffset::Static(offset) => *offset,
+ ViewOffset::Animation(anim) => anim.to(),
+ // This can be used for example if a gesture is interrupted.
+ ViewOffset::Gesture(gesture) => gesture.current_view_offset,
+ }
+ }
+
+ /// Returns a view offset value suitable for saving and later restoration.
+ ///
+ /// This means that it shouldn't return an in-progress animation or gesture value.
+ fn stationary(&self) -> f64 {
+ match self {
+ ViewOffset::Static(offset) => *offset,
+ // For animations we can return the final value.
+ ViewOffset::Animation(anim) => anim.to(),
+ ViewOffset::Gesture(gesture) => gesture.stationary_view_offset,
+ }
+ }
+
+ pub fn is_static(&self) -> bool {
+ matches!(self, Self::Static(_))
+ }
+
pub fn is_animation(&self) -> bool {
matches!(self, Self::Animation(_))
}
- pub fn target_view_offset(&self) -> f64 {
+ pub fn is_gesture(&self) -> bool {
+ matches!(self, Self::Gesture(_))
+ }
+
+ pub fn offset(&mut self, delta: f64) {
match self {
- ViewOffsetAdjustment::Animation(anim) => anim.to(),
- ViewOffsetAdjustment::Gesture(gesture) => gesture.current_view_offset,
+ ViewOffset::Static(offset) => *offset += delta,
+ ViewOffset::Animation(anim) => anim.offset(delta),
+ ViewOffset::Gesture(_gesture) => {
+ // Is this needed?
+ error!("cancel gesture before offsetting");
+ }
}
}
+
+ pub fn cancel_gesture(&mut self) {
+ if let ViewOffset::Gesture(gesture) = self {
+ *self = ViewOffset::Static(gesture.current_view_offset);
+ }
+ }
+
+ pub fn stop_anim_and_gesture(&mut self) {
+ *self = ViewOffset::Static(self.current());
+ }
}
impl ColumnData {
@@ -442,8 +496,7 @@ impl<W: LayoutElement> Workspace<W> {
data: vec![],
active_column_idx: 0,
interactive_resize: None,
- view_offset: 0.,
- view_offset_adj: None,
+ view_offset: ViewOffset::Static(0.),
activate_prev_column_on_removal: None,
view_offset_before_fullscreen: None,
closing_windows: vec![],
@@ -484,8 +537,7 @@ impl<W: LayoutElement> Workspace<W> {
data: vec![],
active_column_idx: 0,
interactive_resize: None,
- view_offset: 0.,
- view_offset_adj: None,
+ view_offset: ViewOffset::Static(0.),
activate_prev_column_on_removal: None,
view_offset_before_fullscreen: None,
closing_windows: vec![],
@@ -524,37 +576,31 @@ impl<W: LayoutElement> Workspace<W> {
|| (self.options.always_center_single_column && self.columns.len() <= 1)
}
- pub fn advance_animations(&mut self, current_time: Duration) {
- if let Some(ViewOffsetAdjustment::Animation(anim)) = &mut self.view_offset_adj {
- anim.set_current_time(current_time);
- self.view_offset = anim.value();
+ pub fn advance_animations(&mut self) {
+ if let ViewOffset::Animation(anim) = &self.view_offset {
if anim.is_done() {
- self.view_offset_adj = None;
+ self.view_offset = ViewOffset::Static(anim.to());
}
- } else if let Some(ViewOffsetAdjustment::Gesture(gesture)) = &self.view_offset_adj {
- self.view_offset = gesture.current_view_offset;
}
for col in &mut self.columns {
- col.advance_animations(current_time);
+ col.advance_animations();
}
self.closing_windows.retain_mut(|closing| {
- closing.advance_animations(current_time);
+ closing.advance_animations();
closing.are_animations_ongoing()
});
}
pub fn are_animations_ongoing(&self) -> bool {
- self.view_offset_adj
- .as_ref()
- .is_some_and(|s| s.is_animation())
+ self.view_offset.is_animation()
|| self.columns.iter().any(Column::are_animations_ongoing)
|| !self.closing_windows.is_empty()
}
pub fn are_transitions_ongoing(&self) -> bool {
- self.view_offset_adj.is_some()
+ !self.view_offset.is_static()
|| self.columns.iter().any(Column::are_animations_ongoing)
|| !self.closing_windows.is_empty()
}
@@ -786,7 +832,7 @@ impl<W: LayoutElement> Workspace<W> {
fn compute_new_view_offset_fit(
&self,
- current_x: f64,
+ target_x: Option<f64>,
col_x: f64,
width: f64,
is_fullscreen: bool,
@@ -795,14 +841,10 @@ impl<W: LayoutElement> Workspace<W> {
return 0.;
}
- let final_x = if let Some(ViewOffsetAdjustment::Animation(anim)) = &self.view_offset_adj {
- current_x - self.view_offset + anim.to()
- } else {
- current_x
- };
+ let target_x = target_x.unwrap_or_else(|| self.target_view_pos());
let new_offset = compute_new_view_offset(
- final_x + self.working_area.loc.x,
+ target_x + self.working_area.loc.x,
self.working_area.size.w,
col_x,
width,
@@ -815,37 +857,41 @@ impl<W: LayoutElement> Workspace<W> {
fn compute_new_view_offset_centered(
&self,
- current_x: f64,
+ target_x: Option<f64>,
col_x: f64,
width: f64,
is_fullscreen: bool,
) -> f64 {
if is_fullscreen {
- return self.compute_new_view_offset_fit(current_x, col_x, width, is_fullscreen);
+ return self.compute_new_view_offset_fit(target_x, col_x, width, is_fullscreen);
}
// 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_fit(current_x, col_x, width, is_fullscreen);
+ return self.compute_new_view_offset_fit(target_x, col_x, width, is_fullscreen);
}
-(self.working_area.size.w - width) / 2. - self.working_area.loc.x
}
- fn compute_new_view_offset_for_column_fit(&self, current_x: f64, idx: usize) -> f64 {
+ fn compute_new_view_offset_for_column_fit(&self, target_x: Option<f64>, idx: usize) -> f64 {
let col = &self.columns[idx];
self.compute_new_view_offset_fit(
- current_x,
+ target_x,
self.column_x(idx),
col.width(),
col.is_fullscreen,
)
}
- fn compute_new_view_offset_for_column_centered(&self, current_x: f64, idx: usize) -> f64 {
+ fn compute_new_view_offset_for_column_centered(
+ &self,
+ target_x: Option<f64>,
+ idx: usize,
+ ) -> f64 {
let col = &self.columns[idx];
self.compute_new_view_offset_centered(
- current_x,
+ target_x,
self.column_x(idx),
col.width(),
col.is_fullscreen,
@@ -854,21 +900,21 @@ impl<W: LayoutElement> Workspace<W> {
fn compute_new_view_offset_for_column(
&self,
- current_x: f64,
+ target_x: Option<f64>,
idx: usize,
prev_idx: Option<usize>,
) -> f64 {
if self.is_centering_focused_column() {
- return self.compute_new_view_offset_for_column_centered(current_x, idx);
+ return self.compute_new_view_offset_for_column_centered(target_x, idx);
}
match self.options.center_focused_column {
CenterFocusedColumn::Always => {
- self.compute_new_view_offset_for_column_centered(current_x, idx)
+ self.compute_new_view_offset_for_column_centered(target_x, idx)
}
CenterFocusedColumn::OnOverflow => {
let Some(prev_idx) = prev_idx else {
- return self.compute_new_view_offset_for_column_fit(current_x, idx);
+ return self.compute_new_view_offset_for_column_fit(target_x, idx);
};
// Always take the left or right neighbor of the target as the source.
@@ -878,36 +924,35 @@ impl<W: LayoutElement> Workspace<W> {
idx.saturating_sub(1)
};
- let source_x = self.column_x(source_idx);
- let source_width = self.columns[source_idx].width();
+ let source_col_x = self.column_x(source_idx);
+ let source_col_width = self.columns[source_idx].width();
- let target_x = self.column_x(idx);
- let target_width = self.columns[idx].width();
+ let target_col_x = self.column_x(idx);
+ let target_col_width = self.columns[idx].width();
- let total_width = if source_x < target_x {
+ let total_width = if source_col_x < target_col_x {
// Source is left from target.
- target_x - source_x + target_width
+ target_col_x - source_col_x + target_col_width
} else {
// Source is right from target.
- source_x - target_x + source_width
+ source_col_x - target_col_x + source_col_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)
+ self.compute_new_view_offset_for_column_fit(target_x, idx)
} else {
- self.compute_new_view_offset_for_column_centered(current_x, idx)
+ self.compute_new_view_offset_for_column_centered(target_x, idx)
}
}
CenterFocusedColumn::Never => {
- self.compute_new_view_offset_for_column_fit(current_x, idx)
+ self.compute_new_view_offset_for_column_fit(target_x, idx)
}
}
}
- fn animate_view_offset(&mut self, current_x: f64, idx: usize, new_view_offset: f64) {
+ fn animate_view_offset(&mut self, idx: usize, new_view_offset: f64) {
self.animate_view_offset_with_config(
- current_x,
idx,
new_view_offset,
self.options.animations.horizontal_view_movement.0,
@@ -916,78 +961,67 @@ impl<W: LayoutElement> Workspace<W> {
fn animate_view_offset_with_config(
&mut self,
- current_x: f64,
idx: usize,
new_view_offset: f64,
config: niri_config::Animation,
) {
+ self.view_offset.cancel_gesture();
+
let new_col_x = self.column_x(idx);
- let old_col_x = current_x - self.view_offset;
+ let old_col_x = self.column_x(self.active_column_idx);
let offset_delta = old_col_x - new_col_x;
- self.view_offset += offset_delta;
+ self.view_offset.offset(offset_delta);
let pixel = 1. / self.scale.fractional_scale();
- // If we're already animating towards that, don't restart it.
- if let Some(ViewOffsetAdjustment::Animation(anim)) = &mut self.view_offset_adj {
- // Offset the animation for the active column change.
- anim.offset(offset_delta);
-
- let to_diff = new_view_offset - anim.to();
- if (anim.value() - self.view_offset).abs() < pixel && to_diff.abs() < pixel {
- // Correct for any inaccuracy.
- anim.offset(to_diff);
- return;
- }
- }
-
- // If our view offset is already this, we don't need to do anything.
- if (self.view_offset - new_view_offset).abs() < pixel {
+ // If our view offset is already this or animating towards this, we don't need to do
+ // anything.
+ let to_diff = new_view_offset - self.view_offset.target();
+ if to_diff.abs() < pixel {
// Correct for any inaccuracy.
- self.view_offset = new_view_offset;
- self.view_offset_adj = None;
+ self.view_offset.offset(to_diff);
return;
}
// FIXME: also compute and use current velocity.
- self.view_offset_adj = Some(ViewOffsetAdjustment::Animation(Animation::new(
- self.clock.now(),
- self.view_offset,
+ self.view_offset = ViewOffset::Animation(Animation::new(
+ self.clock.clone(),
+ self.view_offset.current(),
new_view_offset,
0.,
config,
- )));
+ ));
}
fn animate_view_offset_to_column_centered(
&mut self,
- current_x: f64,
+ target_x: Option<f64>,
idx: usize,
config: niri_config::Animation,
) {
- let new_view_offset = self.compute_new_view_offset_for_column_centered(current_x, idx);
- self.animate_view_offset_with_config(current_x, idx, new_view_offset, config);
+ let new_view_offset = self.compute_new_view_offset_for_column_centered(target_x, idx);
+ self.animate_view_offset_with_config(idx, new_view_offset, config);
}
fn animate_view_offset_to_column_with_config(
&mut self,
- current_x: f64,
+ target_x: Option<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);
+ let new_view_offset = self.compute_new_view_offset_for_column(target_x, idx, prev_idx);
+ self.animate_view_offset_with_config(idx, new_view_offset, config);
}
fn animate_view_offset_to_column(
&mut self,
- current_x: f64,
+ target_x: Option<f64>,
idx: usize,
prev_idx: Option<usize>,
) {
self.animate_view_offset_to_column_with_config(
- current_x,
+ target_x,
idx,
prev_idx,
self.options.animations.horizontal_view_movement.0,
@@ -1006,9 +1040,8 @@ impl<W: LayoutElement> Workspace<W> {
return;
}
- let current_x = self.view_pos();
self.animate_view_offset_to_column_with_config(
- current_x,
+ None,
idx,
Some(self.active_column_idx),
config,
@@ -1240,14 +1273,13 @@ impl<W: LayoutElement> Workspace<W> {
// If this is the first window on an empty workspace, remove the effect of whatever
// view_offset was left over and skip the animation.
if was_empty {
- self.view_offset = 0.;
- self.view_offset_adj = None;
+ self.view_offset = ViewOffset::Static(0.);
self.view_offset =
- self.compute_new_view_offset_for_column(self.view_pos(), idx, None);
+ ViewOffset::Static(self.compute_new_view_offset_for_column(None, idx, None));
}
let prev_offset = (!was_empty && idx == self.active_column_idx + 1)
- .then(|| self.static_view_offset());
+ .then(|| self.view_offset.stationary());
let anim_config =
anim_config.unwrap_or(self.options.animations.horizontal_view_movement.0);
@@ -1416,15 +1448,13 @@ impl<W: LayoutElement> Workspace<W> {
// Restore the view offset but make sure to scroll the view in case the
// previous window had resized.
- let current_x = self.view_pos();
self.animate_view_offset_with_config(
- current_x,
self.active_column_idx,
prev_offset,
view_config,
);
self.animate_view_offset_to_column_with_config(
- current_x,
+ None,
self.active_column_idx,
None,
view_config,
@@ -1509,6 +1539,9 @@ impl<W: LayoutElement> Workspace<W> {
// the Resizing state, which can trigger this code path for a while.
let resize = if offset != 0. { resize } else { None };
if let Some(resize) = resize {
+ // Don't bother with the gesture.
+ self.view_offset.cancel_gesture();
+
// If this is an interactive resize commit of an active window, then we need to
// either preserve the view offset or adjust it accordingly.
let centered = self.is_centering_focused_column();
@@ -1518,33 +1551,24 @@ impl<W: LayoutElement> Workspace<W> {
// FIXME: when view_offset becomes fractional, this can be made additive too.
let new_offset =
-(self.working_area.size.w - width) / 2. - self.working_area.loc.x;
- new_offset - self.view_offset
+ new_offset - self.view_offset.target()
} else if resize.edges.contains(ResizeEdge::LEFT) {
-offset
} else {
0.
};
- self.view_offset += offset;
- if let Some(ViewOffsetAdjustment::Animation(anim)) = &mut self.view_offset_adj {
- anim.offset(offset);
- } else {
- // Don't bother with the gesture.
- self.view_offset_adj = None;
- }
+ self.view_offset.offset(offset);
}
- if self.interactive_resize.is_none()
- && !matches!(self.view_offset_adj, Some(ViewOffsetAdjustment::Gesture(_)))
- {
+ if self.interactive_resize.is_none() && !self.view_offset.is_gesture() {
// We might need to move the view to ensure the resized window is still visible.
- let current_x = self.view_pos();
// Upon unfullscreening, restore the view offset.
let is_fullscreen = self.columns[col_idx].tiles[tile_idx].is_fullscreen();
if was_fullscreen && !is_fullscreen {
if let Some(prev_offset) = self.view_offset_before_fullscreen.take() {
- self.animate_view_offset(current_x, col_idx, prev_offset);
+ self.animate_view_offset(col_idx, prev_offset);
}
}
@@ -1558,7 +1582,7 @@ impl<W: LayoutElement> Workspace<W> {
// FIXME: we will want to skip the animation in some cases here to make continuously
// resizing windows not look janky.
- self.animate_view_offset_to_column_with_config(current_x, col_idx, None, config);
+ self.animate_view_offset_to_column_with_config(None, col_idx, None, config);
}
}
}
@@ -1574,22 +1598,16 @@ impl<W: LayoutElement> Workspace<W> {
return 0.;
}
- let current_x = self.view_pos();
+ // Consider the end of an ongoing animation because that's what compute to fit does too.
+ let target_x = self.target_view_pos();
let new_view_offset = self.compute_new_view_offset_for_column(
- current_x,
+ Some(target_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;
+ let from_view_offset = target_x - new_col_x;
(from_view_offset - new_view_offset).abs() / self.working_area.size.w
}
@@ -1698,7 +1716,7 @@ impl<W: LayoutElement> Workspace<W> {
let output_scale = Scale::from(self.scale.fractional_scale());
let anim = Animation::new(
- self.clock.now(),
+ self.clock.clone(),
0.,
1.,
0.,
@@ -1870,10 +1888,8 @@ impl<W: LayoutElement> Workspace<W> {
// Preserve the camera position when moving to the left.
let view_offset_delta = -self.column_x(self.active_column_idx) + current_col_x;
- self.view_offset += view_offset_delta;
- if let Some(ViewOffsetAdjustment::Animation(anim)) = &mut self.view_offset_adj {
- anim.offset(view_offset_delta);
- }
+ self.view_offset.cancel_gesture();
+ self.view_offset.offset(view_offset_delta);
// The column we just moved is offset by the difference between its new and old position.
let new_col_x = self.column_x(new_idx);
@@ -1987,7 +2003,8 @@ impl<W: LayoutElement> Workspace<W> {
if source_tile_was_active {
// Make sure the previous (target) column is activated so the animation looks right.
- self.activate_prev_column_on_removal = Some(self.static_view_offset() + offset.x);
+ self.activate_prev_column_on_removal =
+ Some(self.view_offset.stationary() + offset.x);
}
offset.x += self.columns[source_col_idx].render_offset().x;
@@ -2204,9 +2221,8 @@ impl<W: LayoutElement> Workspace<W> {
return;
}
- let center_x = self.view_pos();
self.animate_view_offset_to_column_centered(
- center_x,
+ None,
self.active_column_idx,
self.options.animations.horizontal_view_movement.0,
);
@@ -2216,19 +2232,11 @@ impl<W: LayoutElement> Workspace<W> {
}
pub fn view_pos(&self) -> f64 {
- self.column_x(self.active_column_idx) + self.view_offset
+ self.column_x(self.active_column_idx) + self.view_offset.current()
}
- /// Returns a view offset value suitable for saving and later restoration.
- ///
- /// This means that it shouldn't return an in-progress animation or gesture value.
- fn static_view_offset(&self) -> f64 {
- match &self.view_offset_adj {
- // For animations we can return the final value.
- Some(ViewOffsetAdjustment::Animation(anim)) => anim.to(),
- Some(ViewOffsetAdjustment::Gesture(gesture)) => gesture.static_view_offset,
- _ => self.view_offset,
- }
+ pub fn target_view_pos(&self) -> f64 {
+ self.column_x(self.active_column_idx) + self.view_offset.target()
}
// HACK: pass a self.data iterator in manually as a workaround for the lack of method partial
@@ -2398,9 +2406,9 @@ impl<W: LayoutElement> Workspace<W> {
// effect here.
if self.columns.is_empty() {
let view_offset = if self.is_centering_focused_column() {
-