mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-23 02:05:33 +07:00
tab indicator: Animate opening
This commit is contained in:
@@ -641,6 +641,15 @@ impl<W: LayoutElement> FloatingSpace<W> {
|
|||||||
self.interactive_resize_end(Some(&id));
|
self.interactive_resize_end(Some(&id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn start_open_animation(&mut self, id: &W::Id) -> bool {
|
||||||
|
let Some(idx) = self.idx_of(id) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.tiles[idx].start_open_animation();
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
pub fn toggle_window_height(&mut self, id: Option<&W::Id>) {
|
pub fn toggle_window_height(&mut self, id: Option<&W::Id>) {
|
||||||
let Some(id) = id.or(self.active_window_id.as_ref()).cloned() else {
|
let Some(id) = id.or(self.active_window_id.as_ref()).cloned() else {
|
||||||
return;
|
return;
|
||||||
|
|||||||
+1
-4
@@ -4112,14 +4112,11 @@ impl<W: LayoutElement> Layout<W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for ws in self.workspaces_mut() {
|
for ws in self.workspaces_mut() {
|
||||||
for tile in ws.tiles_mut() {
|
if ws.start_open_animation(window) {
|
||||||
if tile.window().id() == window {
|
|
||||||
tile.start_open_animation();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn store_unmap_snapshot(&mut self, renderer: &mut GlesRenderer, window: &W::Id) {
|
pub fn store_unmap_snapshot(&mut self, renderer: &mut GlesRenderer, window: &W::Id) {
|
||||||
let _span = tracy_client::span!("Layout::store_unmap_snapshot");
|
let _span = tracy_client::span!("Layout::store_unmap_snapshot");
|
||||||
|
|||||||
+54
-1
@@ -1429,6 +1429,12 @@ impl<W: LayoutElement> ScrollingSpace<W> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn start_open_animation(&mut self, id: &W::Id) -> bool {
|
||||||
|
self.columns
|
||||||
|
.iter_mut()
|
||||||
|
.any(|col| col.start_open_animation(id))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn focus_left(&mut self) -> bool {
|
pub fn focus_left(&mut self) -> bool {
|
||||||
if self.active_column_idx == 0 {
|
if self.active_column_idx == 0 {
|
||||||
return false;
|
return false;
|
||||||
@@ -3432,6 +3438,17 @@ impl<W: LayoutElement> Column<W> {
|
|||||||
rv.set_fullscreen(true);
|
rv.set_fullscreen(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Animate the tab indicator for new columns.
|
||||||
|
if display_mode == ColumnDisplay::Tabbed
|
||||||
|
&& !rv.options.tab_indicator.hide_when_single_tab
|
||||||
|
&& !is_pending_fullscreen
|
||||||
|
{
|
||||||
|
// Usually new columns are created together with window movement actions. For new
|
||||||
|
// windows, we handle that in start_open_animation().
|
||||||
|
rv.tab_indicator
|
||||||
|
.start_open_animation(rv.clock.clone(), rv.options.animations.window_movement.0);
|
||||||
|
}
|
||||||
|
|
||||||
rv
|
rv
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3499,10 +3516,14 @@ impl<W: LayoutElement> Column<W> {
|
|||||||
for tile in &mut self.tiles {
|
for tile in &mut self.tiles {
|
||||||
tile.advance_animations();
|
tile.advance_animations();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.tab_indicator.advance_animations();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn are_animations_ongoing(&self) -> bool {
|
pub fn are_animations_ongoing(&self) -> bool {
|
||||||
self.move_animation.is_some() || self.tiles.iter().any(Tile::are_animations_ongoing)
|
self.move_animation.is_some()
|
||||||
|
|| self.tab_indicator.are_animations_ongoing()
|
||||||
|
|| self.tiles.iter().any(Tile::are_animations_ongoing)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_render_elements(&mut self, is_active: bool, view_rect: Rectangle<f64, Logical>) {
|
pub fn update_render_elements(&mut self, is_active: bool, view_rect: Rectangle<f64, Logical>) {
|
||||||
@@ -4375,6 +4396,14 @@ impl<W: LayoutElement> Column<W> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Animate the appearance of the tab indicator.
|
||||||
|
if display == ColumnDisplay::Tabbed {
|
||||||
|
self.tab_indicator.start_open_animation(
|
||||||
|
self.clock.clone(),
|
||||||
|
self.options.animations.window_movement.0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Now switch the display mode for real.
|
// Now switch the display mode for real.
|
||||||
self.display_mode = display;
|
self.display_mode = display;
|
||||||
self.update_tile_sizes(true);
|
self.update_tile_sizes(true);
|
||||||
@@ -4555,6 +4584,30 @@ impl<W: LayoutElement> Column<W> {
|
|||||||
Rectangle::new(self.tiles_origin(), area_size)
|
Rectangle::new(self.tiles_origin(), area_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn start_open_animation(&mut self, id: &W::Id) -> bool {
|
||||||
|
for tile in &mut self.tiles {
|
||||||
|
if tile.window().id() == id {
|
||||||
|
tile.start_open_animation();
|
||||||
|
|
||||||
|
// Animate the appearance of the tab indicator.
|
||||||
|
if self.display_mode == ColumnDisplay::Tabbed
|
||||||
|
&& !self.is_fullscreen
|
||||||
|
&& self.tiles.len() == 1
|
||||||
|
&& !self.tab_indicator.config().hide_when_single_tab
|
||||||
|
{
|
||||||
|
self.tab_indicator.start_open_animation(
|
||||||
|
self.clock.clone(),
|
||||||
|
self.options.animations.window_open.anim,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn verify_invariants(&self) {
|
fn verify_invariants(&self) {
|
||||||
assert!(!self.tiles.is_empty(), "columns can't be empty");
|
assert!(!self.tiles.is_empty(), "columns can't be empty");
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use smithay::utils::{Logical, Point, Rectangle, Size};
|
|||||||
|
|
||||||
use super::tile::Tile;
|
use super::tile::Tile;
|
||||||
use super::LayoutElement;
|
use super::LayoutElement;
|
||||||
|
use crate::animation::{Animation, Clock};
|
||||||
use crate::niri_render_elements;
|
use crate::niri_render_elements;
|
||||||
use crate::render_helpers::border::BorderRenderElement;
|
use crate::render_helpers::border::BorderRenderElement;
|
||||||
use crate::render_helpers::renderer::NiriRenderer;
|
use crate::render_helpers::renderer::NiriRenderer;
|
||||||
@@ -15,6 +16,7 @@ use crate::utils::{floor_logical_in_physical_max1, round_logical_in_physical};
|
|||||||
pub struct TabIndicator {
|
pub struct TabIndicator {
|
||||||
shader_locs: Vec<Point<f64, Logical>>,
|
shader_locs: Vec<Point<f64, Logical>>,
|
||||||
shaders: Vec<BorderRenderElement>,
|
shaders: Vec<BorderRenderElement>,
|
||||||
|
open_anim: Option<Animation>,
|
||||||
config: niri_config::TabIndicator,
|
config: niri_config::TabIndicator,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,6 +39,7 @@ impl TabIndicator {
|
|||||||
Self {
|
Self {
|
||||||
shader_locs: Vec::new(),
|
shader_locs: Vec::new(),
|
||||||
shaders: Vec::new(),
|
shaders: Vec::new(),
|
||||||
|
open_anim: None,
|
||||||
config,
|
config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -51,6 +54,22 @@ impl TabIndicator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn advance_animations(&mut self) {
|
||||||
|
if let Some(anim) = &mut self.open_anim {
|
||||||
|
if anim.is_done() {
|
||||||
|
self.open_anim = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn are_animations_ongoing(&self) -> bool {
|
||||||
|
self.open_anim.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start_open_animation(&mut self, clock: Clock, config: niri_config::Animation) {
|
||||||
|
self.open_anim = Some(Animation::new(clock, 0., 1., 0., config));
|
||||||
|
}
|
||||||
|
|
||||||
fn tab_rects(
|
fn tab_rects(
|
||||||
&self,
|
&self,
|
||||||
area: Rectangle<f64, Logical>,
|
area: Rectangle<f64, Logical>,
|
||||||
@@ -59,6 +78,8 @@ impl TabIndicator {
|
|||||||
) -> impl Iterator<Item = Rectangle<f64, Logical>> {
|
) -> impl Iterator<Item = Rectangle<f64, Logical>> {
|
||||||
let round = |logical: f64| round_logical_in_physical(scale, logical);
|
let round = |logical: f64| round_logical_in_physical(scale, logical);
|
||||||
|
|
||||||
|
let progress = self.open_anim.as_ref().map_or(1., |a| a.value().max(0.));
|
||||||
|
|
||||||
let width = round(self.config.width.0);
|
let width = round(self.config.width.0);
|
||||||
let gap = round(self.config.gap.0);
|
let gap = round(self.config.gap.0);
|
||||||
let gaps_between = round(self.config.gaps_between_tabs.0);
|
let gaps_between = round(self.config.gaps_between_tabs.0);
|
||||||
@@ -71,13 +92,20 @@ impl TabIndicator {
|
|||||||
let total_prop = self.config.length.total_proportion.unwrap_or(0.5);
|
let total_prop = self.config.length.total_proportion.unwrap_or(0.5);
|
||||||
let min_length = round(side * total_prop.clamp(0., 2.));
|
let min_length = round(side * total_prop.clamp(0., 2.));
|
||||||
|
|
||||||
|
// Compute px_per_tab before applying the animation to gaps_between in order to avoid it
|
||||||
|
// growing and shrinking over the duration of the animation.
|
||||||
let pixel = 1. / scale;
|
let pixel = 1. / scale;
|
||||||
let shortest_length = count as f64 * (pixel + gaps_between) - gaps_between;
|
let shortest_length = count as f64 * (pixel + gaps_between) - gaps_between;
|
||||||
let length = f64::max(min_length, shortest_length);
|
let length = f64::max(min_length, shortest_length);
|
||||||
let px_per_tab = (length + gaps_between) / count as f64 - gaps_between;
|
let px_per_tab = (length + gaps_between) / count as f64 - gaps_between;
|
||||||
|
|
||||||
|
let px_per_tab = px_per_tab * progress;
|
||||||
|
let gaps_between = round(self.config.gaps_between_tabs.0 * progress);
|
||||||
|
|
||||||
|
let length = count as f64 * (px_per_tab + gaps_between) - gaps_between;
|
||||||
let px_per_tab = floor_logical_in_physical_max1(scale, px_per_tab);
|
let px_per_tab = floor_logical_in_physical_max1(scale, px_per_tab);
|
||||||
let floored_length = count as f64 * (px_per_tab + gaps_between) - gaps_between;
|
let floored_length = count as f64 * (px_per_tab + gaps_between) - gaps_between;
|
||||||
let mut ones_left = ((length - floored_length) / pixel).max(0.).round() as usize;
|
let mut ones_left = ((length - floored_length) / pixel).round() as usize;
|
||||||
|
|
||||||
let mut shader_loc = Point::from((-gap - width, round((side - length) / 2.)));
|
let mut shader_loc = Point::from((-gap - width, round((side - length) / 2.)));
|
||||||
match position {
|
match position {
|
||||||
|
|||||||
@@ -1474,6 +1474,10 @@ impl<W: LayoutElement> Workspace<W> {
|
|||||||
.start_close_animation_for_tile(renderer, snapshot, tile_size, tile_pos, blocker);
|
.start_close_animation_for_tile(renderer, snapshot, tile_size, tile_pos, blocker);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn start_open_animation(&mut self, id: &W::Id) -> bool {
|
||||||
|
self.scrolling.start_open_animation(id) || self.floating.start_open_animation(id)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn window_under(&self, pos: Point<f64, Logical>) -> Option<(&W, HitType)> {
|
pub fn window_under(&self, pos: Point<f64, Logical>) -> Option<(&W, HitType)> {
|
||||||
// This logic is consistent with tiles_with_render_positions().
|
// This logic is consistent with tiles_with_render_positions().
|
||||||
if self.is_floating_visible() {
|
if self.is_floating_visible() {
|
||||||
|
|||||||
Reference in New Issue
Block a user