From 596ae5a2ba332da9e13bbcbda31dbda5047cff0e Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Tue, 14 Nov 2023 16:32:49 +0100 Subject: [PATCH] floating: Animate tiling state changes --- src/shell/element/mod.rs | 32 ++++++ src/shell/layout/floating/mod.rs | 185 +++++++++++++++++++++++++++---- src/shell/workspace.rs | 2 + 3 files changed, 199 insertions(+), 20 deletions(-) diff --git a/src/shell/element/mod.rs b/src/shell/element/mod.rs index d21b3263..e6a50968 100644 --- a/src/shell/element/mod.rs +++ b/src/shell/element/mod.rs @@ -1154,6 +1154,12 @@ where TiledOverlay( RelocateRenderElement>>, ), + MovingStack( + RelocateRenderElement>>, + ), + MovingWindow( + RelocateRenderElement>>, + ), GrabbedStack(RescaleRenderElement>), GrabbedWindow(RescaleRenderElement>), FocusIndicator(PixelShaderElement), @@ -1175,6 +1181,8 @@ where CosmicMappedRenderElement::TiledStack(elem) => elem.id(), CosmicMappedRenderElement::TiledWindow(elem) => elem.id(), CosmicMappedRenderElement::TiledOverlay(elem) => elem.id(), + CosmicMappedRenderElement::MovingStack(elem) => elem.id(), + CosmicMappedRenderElement::MovingWindow(elem) => elem.id(), CosmicMappedRenderElement::GrabbedStack(elem) => elem.id(), CosmicMappedRenderElement::GrabbedWindow(elem) => elem.id(), CosmicMappedRenderElement::FocusIndicator(elem) => elem.id(), @@ -1192,6 +1200,8 @@ where CosmicMappedRenderElement::TiledStack(elem) => elem.current_commit(), CosmicMappedRenderElement::TiledWindow(elem) => elem.current_commit(), CosmicMappedRenderElement::TiledOverlay(elem) => elem.current_commit(), + CosmicMappedRenderElement::MovingStack(elem) => elem.current_commit(), + CosmicMappedRenderElement::MovingWindow(elem) => elem.current_commit(), CosmicMappedRenderElement::GrabbedStack(elem) => elem.current_commit(), CosmicMappedRenderElement::GrabbedWindow(elem) => elem.current_commit(), CosmicMappedRenderElement::FocusIndicator(elem) => elem.current_commit(), @@ -1209,6 +1219,8 @@ where CosmicMappedRenderElement::TiledStack(elem) => elem.src(), CosmicMappedRenderElement::TiledWindow(elem) => elem.src(), CosmicMappedRenderElement::TiledOverlay(elem) => elem.src(), + CosmicMappedRenderElement::MovingStack(elem) => elem.src(), + CosmicMappedRenderElement::MovingWindow(elem) => elem.src(), CosmicMappedRenderElement::GrabbedStack(elem) => elem.src(), CosmicMappedRenderElement::GrabbedWindow(elem) => elem.src(), CosmicMappedRenderElement::FocusIndicator(elem) => elem.src(), @@ -1226,6 +1238,8 @@ where CosmicMappedRenderElement::TiledStack(elem) => elem.geometry(scale), CosmicMappedRenderElement::TiledWindow(elem) => elem.geometry(scale), CosmicMappedRenderElement::TiledOverlay(elem) => elem.geometry(scale), + CosmicMappedRenderElement::MovingStack(elem) => elem.geometry(scale), + CosmicMappedRenderElement::MovingWindow(elem) => elem.geometry(scale), CosmicMappedRenderElement::GrabbedStack(elem) => elem.geometry(scale), CosmicMappedRenderElement::GrabbedWindow(elem) => elem.geometry(scale), CosmicMappedRenderElement::FocusIndicator(elem) => elem.geometry(scale), @@ -1243,6 +1257,8 @@ where CosmicMappedRenderElement::TiledStack(elem) => elem.location(scale), CosmicMappedRenderElement::TiledWindow(elem) => elem.location(scale), CosmicMappedRenderElement::TiledOverlay(elem) => elem.location(scale), + CosmicMappedRenderElement::MovingStack(elem) => elem.location(scale), + CosmicMappedRenderElement::MovingWindow(elem) => elem.location(scale), CosmicMappedRenderElement::GrabbedStack(elem) => elem.location(scale), CosmicMappedRenderElement::GrabbedWindow(elem) => elem.location(scale), CosmicMappedRenderElement::FocusIndicator(elem) => elem.location(scale), @@ -1260,6 +1276,8 @@ where CosmicMappedRenderElement::TiledStack(elem) => elem.transform(), CosmicMappedRenderElement::TiledWindow(elem) => elem.transform(), CosmicMappedRenderElement::TiledOverlay(elem) => elem.transform(), + CosmicMappedRenderElement::MovingStack(elem) => elem.transform(), + CosmicMappedRenderElement::MovingWindow(elem) => elem.transform(), CosmicMappedRenderElement::GrabbedStack(elem) => elem.transform(), CosmicMappedRenderElement::GrabbedWindow(elem) => elem.transform(), CosmicMappedRenderElement::FocusIndicator(elem) => elem.transform(), @@ -1281,6 +1299,8 @@ where CosmicMappedRenderElement::TiledStack(elem) => elem.damage_since(scale, commit), CosmicMappedRenderElement::TiledWindow(elem) => elem.damage_since(scale, commit), CosmicMappedRenderElement::TiledOverlay(elem) => elem.damage_since(scale, commit), + CosmicMappedRenderElement::MovingStack(elem) => elem.damage_since(scale, commit), + CosmicMappedRenderElement::MovingWindow(elem) => elem.damage_since(scale, commit), CosmicMappedRenderElement::GrabbedStack(elem) => elem.damage_since(scale, commit), CosmicMappedRenderElement::GrabbedWindow(elem) => elem.damage_since(scale, commit), CosmicMappedRenderElement::FocusIndicator(elem) => elem.damage_since(scale, commit), @@ -1300,6 +1320,8 @@ where CosmicMappedRenderElement::TiledStack(elem) => elem.opaque_regions(scale), CosmicMappedRenderElement::TiledWindow(elem) => elem.opaque_regions(scale), CosmicMappedRenderElement::TiledOverlay(elem) => elem.opaque_regions(scale), + CosmicMappedRenderElement::MovingStack(elem) => elem.opaque_regions(scale), + CosmicMappedRenderElement::MovingWindow(elem) => elem.opaque_regions(scale), CosmicMappedRenderElement::GrabbedStack(elem) => elem.opaque_regions(scale), CosmicMappedRenderElement::GrabbedWindow(elem) => elem.opaque_regions(scale), CosmicMappedRenderElement::FocusIndicator(elem) => elem.opaque_regions(scale), @@ -1317,6 +1339,8 @@ where CosmicMappedRenderElement::TiledStack(elem) => elem.alpha(), CosmicMappedRenderElement::TiledWindow(elem) => elem.alpha(), CosmicMappedRenderElement::TiledOverlay(elem) => elem.alpha(), + CosmicMappedRenderElement::MovingStack(elem) => elem.alpha(), + CosmicMappedRenderElement::MovingWindow(elem) => elem.alpha(), CosmicMappedRenderElement::GrabbedStack(elem) => elem.alpha(), CosmicMappedRenderElement::GrabbedWindow(elem) => elem.alpha(), CosmicMappedRenderElement::FocusIndicator(elem) => elem.alpha(), @@ -1344,6 +1368,8 @@ impl RenderElement for CosmicMappedRenderElement { CosmicMappedRenderElement::TiledOverlay(elem) => { RenderElement::::draw(elem, frame, src, dst, damage) } + CosmicMappedRenderElement::MovingStack(elem) => elem.draw(frame, src, dst, damage), + CosmicMappedRenderElement::MovingWindow(elem) => elem.draw(frame, src, dst, damage), CosmicMappedRenderElement::GrabbedStack(elem) => elem.draw(frame, src, dst, damage), CosmicMappedRenderElement::GrabbedWindow(elem) => elem.draw(frame, src, dst, damage), CosmicMappedRenderElement::FocusIndicator(elem) => { @@ -1369,6 +1395,8 @@ impl RenderElement for CosmicMappedRenderElement { CosmicMappedRenderElement::TiledStack(elem) => elem.underlying_storage(renderer), CosmicMappedRenderElement::TiledWindow(elem) => elem.underlying_storage(renderer), CosmicMappedRenderElement::TiledOverlay(elem) => elem.underlying_storage(renderer), + CosmicMappedRenderElement::MovingStack(elem) => elem.underlying_storage(renderer), + CosmicMappedRenderElement::MovingWindow(elem) => elem.underlying_storage(renderer), CosmicMappedRenderElement::GrabbedStack(elem) => elem.underlying_storage(renderer), CosmicMappedRenderElement::GrabbedWindow(elem) => elem.underlying_storage(renderer), CosmicMappedRenderElement::FocusIndicator(elem) => elem.underlying_storage(renderer), @@ -1401,6 +1429,8 @@ impl<'a, 'b> RenderElement> RenderElement::::draw(elem, frame.glow_frame_mut(), src, dst, damage) .map_err(|err| GlMultiError::Render(err)) } + CosmicMappedRenderElement::MovingStack(elem) => elem.draw(frame, src, dst, damage), + CosmicMappedRenderElement::MovingWindow(elem) => elem.draw(frame, src, dst, damage), CosmicMappedRenderElement::GrabbedStack(elem) => elem.draw(frame, src, dst, damage), CosmicMappedRenderElement::GrabbedWindow(elem) => elem.draw(frame, src, dst, damage), CosmicMappedRenderElement::FocusIndicator(elem) => { @@ -1435,6 +1465,8 @@ impl<'a, 'b> RenderElement> CosmicMappedRenderElement::TiledOverlay(elem) => { elem.underlying_storage(renderer.glow_renderer_mut()) } + CosmicMappedRenderElement::MovingStack(elem) => elem.underlying_storage(renderer), + CosmicMappedRenderElement::MovingWindow(elem) => elem.underlying_storage(renderer), CosmicMappedRenderElement::GrabbedStack(elem) => elem.underlying_storage(renderer), CosmicMappedRenderElement::GrabbedWindow(elem) => elem.underlying_storage(renderer), CosmicMappedRenderElement::FocusIndicator(elem) => { diff --git a/src/shell/layout/floating/mod.rs b/src/shell/layout/floating/mod.rs index 9998d297..0eff452f 100644 --- a/src/shell/layout/floating/mod.rs +++ b/src/shell/layout/floating/mod.rs @@ -1,16 +1,24 @@ // SPDX-License-Identifier: GPL-3.0-only -use std::sync::atomic::Ordering; +use std::{ + collections::HashMap, + sync::atomic::{AtomicBool, Ordering}, + time::{Duration, Instant}, +}; +use keyframe::{ease, functions::EaseInOutCubic}; use smithay::{ backend::renderer::{ - element::{AsRenderElements, RenderElement}, + element::{ + utils::{Relocate, RelocateRenderElement, RescaleRenderElement}, + AsRenderElements, RenderElement, + }, ImportAll, ImportMem, Renderer, }, desktop::{layer_map_for_output, space::SpaceElement, PopupKind, Space, WindowSurfaceType}, input::{pointer::GrabStartData as PointerGrabStartData, Seat}, output::Output, - utils::{IsAlive, Logical, Point, Rectangle, Size}, + utils::{IsAlive, Logical, Point, Rectangle, Scale, Size}, wayland::seat::WaylandFocus, }; @@ -28,17 +36,21 @@ use crate::{ CosmicSurface, Direction, FocusResult, MoveResult, ResizeDirection, ResizeMode, }, state::State, - utils::prelude::*, + utils::{prelude::*, tween::EaseRectangle}, wayland::handlers::xdg_shell::popup::get_popup_toplevel, }; mod grabs; pub use self::grabs::*; +pub const ANIMATION_DURATION: Duration = Duration::from_millis(200); + #[derive(Debug, Default)] pub struct FloatingLayout { pub(crate) space: Space, spawn_order: Vec, + tiling_animations: HashMap)>, + dirty: AtomicBool, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -746,11 +758,47 @@ impl FloatingLayout { let (new_pos, new_size) = (new_geo.loc, new_geo.size); focused.set_tiled(true); // TODO: More fine grained? + let start_rectangle = if let Some((previous_start, previous_rect)) = + self.tiling_animations.remove(focused) + { + if let Some(target_rect) = tiled_state + .as_ref() + .map(|state| state.relative_geometry(output_geometry)) + { + ease( + EaseInOutCubic, + EaseRectangle(previous_rect), + EaseRectangle(target_rect), + Instant::now() + .duration_since(previous_start) + .max(ANIMATION_DURATION) + .as_secs_f64() + / ANIMATION_DURATION.as_secs_f64(), + ) + .unwrap() + } else { + self.space + .element_geometry(focused) + .map(RectExt::as_local) + .unwrap() + } + } else { + self.space + .element_geometry(focused) + .map(RectExt::as_local) + .unwrap() + }; + + if tiled_state.is_none() { + *focused.last_geometry.lock().unwrap() = + self.space.element_geometry(focused).map(RectExt::as_local); + } + + self.tiling_animations + .insert(focused.clone(), (Instant::now(), start_rectangle)); *tiled_state = Some(new_state); std::mem::drop(tiled_state); - *focused.last_geometry.lock().unwrap() = - self.space.element_geometry(focused).map(RectExt::as_local); focused.moved_since_mapped.store(true, Ordering::SeqCst); let focused = focused.clone(); self.map_internal(focused, Some(new_pos), Some(new_size.as_logical())); @@ -773,9 +821,11 @@ impl FloatingLayout { puffin::profile_function!(); self.space.refresh(); + if let Some(pos) = self.spawn_order.iter().position(|w| !w.alive()) { self.spawn_order.truncate(pos); } + for element in self .space .elements() @@ -790,6 +840,18 @@ impl FloatingLayout { } } + pub fn animations_going(&self) -> bool { + self.dirty.swap(false, Ordering::SeqCst) || !self.tiling_animations.is_empty() + } + + pub fn update_animation_state(&mut self) { + self.tiling_animations + .retain(|_, (start, _)| Instant::now().duration_since(*start) < ANIMATION_DURATION); + if self.tiling_animations.is_empty() { + self.dirty.store(true, Ordering::SeqCst); + } + } + pub fn merge(&mut self, other: FloatingLayout) { for element in other.space.elements() { let elem_loc = other @@ -826,37 +888,120 @@ impl FloatingLayout { puffin::profile_function!(); let output = self.space.outputs().next().unwrap(); + let output_geometry = { + let layers = layer_map_for_output(output); + layers.non_exclusive_zone() + }; let output_scale = output.current_scale().fractional_scale(); let mut window_elements = Vec::new(); let mut popup_elements = Vec::new(); self.space.elements().rev().for_each(|elem| { - let render_location = self.space.element_location(elem).unwrap() - elem.geometry().loc; - let (w_elements, p_elements) = elem.split_render_elements( + let mut geometry = self + .tiling_animations + .get(elem) + .map(|(_, rect)| *rect) + .unwrap_or_else(|| self.space.element_geometry(elem).unwrap().as_local()); + + let render_location = geometry.loc - elem.geometry().loc.as_local(); + let (mut w_elements, p_elements) = elem.split_render_elements( renderer, - render_location.to_physical_precise_round(output_scale), + render_location + .as_logical() + .to_physical_precise_round(output_scale), output_scale.into(), alpha, ); - if focused == Some(elem) && !elem.is_maximized(false) { - let mut indicator_geometry = Rectangle::from_loc_and_size( - self.space.element_location(elem).unwrap(), - elem.geometry().size, - ) - .as_local(); + if let Some((start, original_geo)) = self.tiling_animations.get(elem) { + if let Some(target_rect) = elem + .floating_tiled + .lock() + .unwrap() + .as_ref() + .map(|state| state.relative_geometry(output_geometry)) + { + geometry = ease( + EaseInOutCubic, + EaseRectangle(original_geo.clone()), + EaseRectangle(target_rect), + Instant::now() + .duration_since(*start) + .min(ANIMATION_DURATION) + .as_millis() as f32 + / ANIMATION_DURATION.as_millis() as f32, + ) + .unwrap(); + + let buffer_size = elem.geometry().size; + let scale = Scale { + x: geometry.size.w as f64 / buffer_size.w as f64, + y: geometry.size.h as f64 / buffer_size.h as f64, + }; + w_elements = w_elements + .into_iter() + .map(|element| match element { + CosmicMappedRenderElement::Stack(elem) => { + CosmicMappedRenderElement::MovingStack({ + let rescaled = RescaleRenderElement::from_element( + elem, + original_geo + .loc + .as_logical() + .to_physical_precise_round(output_scale), + scale, + ); + let relocated = RelocateRenderElement::from_element( + rescaled, + (geometry.loc - original_geo.loc) + .as_logical() + .to_physical_precise_round(output_scale), + Relocate::Relative, + ); + relocated + }) + } + CosmicMappedRenderElement::Window(elem) => { + CosmicMappedRenderElement::MovingWindow({ + let rescaled = RescaleRenderElement::from_element( + elem, + original_geo + .loc + .as_logical() + .to_physical_precise_round(output_scale), + scale, + ); + let relocated = RelocateRenderElement::from_element( + rescaled, + (geometry.loc - original_geo.loc) + .as_logical() + .to_physical_precise_round(output_scale), + Relocate::Relative, + ); + relocated + }) + } + x => x, + }) + .collect(); + } + } + + if focused == Some(elem) && !elem.is_maximized(false) { if let Some((mode, resize)) = resize_indicator.as_mut() { - indicator_geometry.loc -= (18, 18).into(); - indicator_geometry.size += (36, 36).into(); - resize.resize(indicator_geometry.size.as_logical()); + let mut resize_geometry = geometry.clone(); + resize_geometry.loc -= (18, 18).into(); + resize_geometry.size += (36, 36).into(); + + resize.resize(resize_geometry.size.as_logical()); resize.output_enter(output, Rectangle::default() /* unused */); window_elements.extend( resize .render_elements::>( renderer, - indicator_geometry + resize_geometry .loc .as_logical() .to_physical_precise_round(output_scale), @@ -874,7 +1019,7 @@ impl FloatingLayout { let element = IndicatorShader::focus_element( renderer, Key::Window(Usage::FocusIndicator, elem.clone()), - indicator_geometry, + geometry, indicator_thickness, output_scale, alpha, diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index 916d274f..06927de5 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -260,6 +260,7 @@ impl Workspace { pub fn animations_going(&self) -> bool { self.tiling_layer.animations_going() + || self.floating_layer.animations_going() || self .fullscreen .as_ref() @@ -310,6 +311,7 @@ impl Workspace { } clients.extend(self.tiling_layer.update_animation_state()); + self.floating_layer.update_animation_state(); clients }