mirror of
https://github.com/YaLTeR/niri.git
synced 2025-10-06 00:23:14 +02:00
Refactor animations to take explicit current time
This commit is contained in:
@@ -7,9 +7,9 @@ use niri::render_helpers::border::BorderRenderElement;
|
||||
use niri_config::{Color, CornerRadius, GradientInterpolation};
|
||||
use smithay::backend::renderer::element::RenderElement;
|
||||
use smithay::backend::renderer::gles::GlesRenderer;
|
||||
use smithay::utils::{Logical, Physical, Rectangle, Size};
|
||||
use smithay::utils::{Physical, Rectangle, Size};
|
||||
|
||||
use super::TestCase;
|
||||
use super::{Args, TestCase};
|
||||
|
||||
pub struct GradientAngle {
|
||||
angle: f32,
|
||||
@@ -17,7 +17,7 @@ pub struct GradientAngle {
|
||||
}
|
||||
|
||||
impl GradientAngle {
|
||||
pub fn new(_size: Size<i32, Logical>) -> Self {
|
||||
pub fn new(_args: Args) -> Self {
|
||||
Self {
|
||||
angle: 0.,
|
||||
prev_time: Duration::ZERO,
|
||||
|
@@ -8,9 +8,9 @@ use niri::render_helpers::border::BorderRenderElement;
|
||||
use niri_config::{Color, CornerRadius, FloatOrInt, GradientInterpolation};
|
||||
use smithay::backend::renderer::element::RenderElement;
|
||||
use smithay::backend::renderer::gles::GlesRenderer;
|
||||
use smithay::utils::{Logical, Physical, Point, Rectangle, Size};
|
||||
use smithay::utils::{Physical, Point, Rectangle, Size};
|
||||
|
||||
use super::TestCase;
|
||||
use super::{Args, TestCase};
|
||||
|
||||
pub struct GradientArea {
|
||||
progress: f32,
|
||||
@@ -19,7 +19,7 @@ pub struct GradientArea {
|
||||
}
|
||||
|
||||
impl GradientArea {
|
||||
pub fn new(_size: Size<i32, Logical>) -> Self {
|
||||
pub fn new(_args: Args) -> Self {
|
||||
let border = FocusRing::new(niri_config::FocusRing {
|
||||
off: false,
|
||||
width: FloatOrInt(1.),
|
||||
|
@@ -4,16 +4,16 @@ use niri_config::{
|
||||
};
|
||||
use smithay::backend::renderer::element::RenderElement;
|
||||
use smithay::backend::renderer::gles::GlesRenderer;
|
||||
use smithay::utils::{Logical, Physical, Rectangle, Size};
|
||||
use smithay::utils::{Physical, Rectangle, Size};
|
||||
|
||||
use super::TestCase;
|
||||
use super::{Args, TestCase};
|
||||
|
||||
pub struct GradientOklab {
|
||||
gradient_format: GradientInterpolation,
|
||||
}
|
||||
|
||||
impl GradientOklab {
|
||||
pub fn new(_size: Size<i32, Logical>) -> Self {
|
||||
pub fn new(_args: Args) -> Self {
|
||||
Self {
|
||||
gradient_format: GradientInterpolation {
|
||||
color_space: GradientColorSpace::Oklab,
|
||||
|
@@ -2,16 +2,16 @@ use niri::render_helpers::border::BorderRenderElement;
|
||||
use niri_config::{Color, CornerRadius, GradientColorSpace, GradientInterpolation};
|
||||
use smithay::backend::renderer::element::RenderElement;
|
||||
use smithay::backend::renderer::gles::GlesRenderer;
|
||||
use smithay::utils::{Logical, Physical, Rectangle, Size};
|
||||
use smithay::utils::{Physical, Rectangle, Size};
|
||||
|
||||
use super::TestCase;
|
||||
use super::{Args, TestCase};
|
||||
|
||||
pub struct GradientOklabAlpha {
|
||||
gradient_format: GradientInterpolation,
|
||||
}
|
||||
|
||||
impl GradientOklabAlpha {
|
||||
pub fn new(_size: Size<i32, Logical>) -> Self {
|
||||
pub fn new(_args: Args) -> Self {
|
||||
Self {
|
||||
gradient_format: GradientInterpolation {
|
||||
color_space: GradientColorSpace::Oklab,
|
||||
|
@@ -4,16 +4,16 @@ use niri_config::{
|
||||
};
|
||||
use smithay::backend::renderer::element::RenderElement;
|
||||
use smithay::backend::renderer::gles::GlesRenderer;
|
||||
use smithay::utils::{Logical, Physical, Rectangle, Size};
|
||||
use smithay::utils::{Physical, Rectangle, Size};
|
||||
|
||||
use super::TestCase;
|
||||
use super::{Args, TestCase};
|
||||
|
||||
pub struct GradientOklchAlpha {
|
||||
gradient_format: GradientInterpolation,
|
||||
}
|
||||
|
||||
impl GradientOklchAlpha {
|
||||
pub fn new(_size: Size<i32, Logical>) -> Self {
|
||||
pub fn new(_args: Args) -> Self {
|
||||
Self {
|
||||
gradient_format: GradientInterpolation {
|
||||
color_space: GradientColorSpace::Oklch,
|
||||
|
@@ -4,16 +4,16 @@ use niri_config::{
|
||||
};
|
||||
use smithay::backend::renderer::element::RenderElement;
|
||||
use smithay::backend::renderer::gles::GlesRenderer;
|
||||
use smithay::utils::{Logical, Physical, Rectangle, Size};
|
||||
use smithay::utils::{Physical, Rectangle, Size};
|
||||
|
||||
use super::TestCase;
|
||||
use super::{Args, TestCase};
|
||||
|
||||
pub struct GradientOklchDecreasing {
|
||||
gradient_format: GradientInterpolation,
|
||||
}
|
||||
|
||||
impl GradientOklchDecreasing {
|
||||
pub fn new(_size: Size<i32, Logical>) -> Self {
|
||||
pub fn new(_args: Args) -> Self {
|
||||
Self {
|
||||
gradient_format: GradientInterpolation {
|
||||
color_space: GradientColorSpace::Oklch,
|
||||
|
@@ -4,16 +4,16 @@ use niri_config::{
|
||||
};
|
||||
use smithay::backend::renderer::element::RenderElement;
|
||||
use smithay::backend::renderer::gles::GlesRenderer;
|
||||
use smithay::utils::{Logical, Physical, Rectangle, Size};
|
||||
use smithay::utils::{Physical, Rectangle, Size};
|
||||
|
||||
use super::TestCase;
|
||||
use super::{Args, TestCase};
|
||||
|
||||
pub struct GradientOklchIncreasing {
|
||||
gradient_format: GradientInterpolation,
|
||||
}
|
||||
|
||||
impl GradientOklchIncreasing {
|
||||
pub fn new(_size: Size<i32, Logical>) -> Self {
|
||||
pub fn new(_args: Args) -> Self {
|
||||
Self {
|
||||
gradient_format: GradientInterpolation {
|
||||
color_space: GradientColorSpace::Oklch,
|
||||
|
@@ -4,16 +4,16 @@ use niri_config::{
|
||||
};
|
||||
use smithay::backend::renderer::element::RenderElement;
|
||||
use smithay::backend::renderer::gles::GlesRenderer;
|
||||
use smithay::utils::{Logical, Physical, Rectangle, Size};
|
||||
use smithay::utils::{Physical, Rectangle, Size};
|
||||
|
||||
use super::TestCase;
|
||||
use super::{Args, TestCase};
|
||||
|
||||
pub struct GradientOklchLonger {
|
||||
gradient_format: GradientInterpolation,
|
||||
}
|
||||
|
||||
impl GradientOklchLonger {
|
||||
pub fn new(_size: Size<i32, Logical>) -> Self {
|
||||
pub fn new(_args: Args) -> Self {
|
||||
Self {
|
||||
gradient_format: GradientInterpolation {
|
||||
color_space: GradientColorSpace::Oklch,
|
||||
|
@@ -4,16 +4,16 @@ use niri_config::{
|
||||
};
|
||||
use smithay::backend::renderer::element::RenderElement;
|
||||
use smithay::backend::renderer::gles::GlesRenderer;
|
||||
use smithay::utils::{Logical, Physical, Rectangle, Size};
|
||||
use smithay::utils::{Physical, Rectangle, Size};
|
||||
|
||||
use super::TestCase;
|
||||
use super::{Args, TestCase};
|
||||
|
||||
pub struct GradientOklchShorter {
|
||||
gradient_format: GradientInterpolation,
|
||||
}
|
||||
|
||||
impl GradientOklchShorter {
|
||||
pub fn new(_size: Size<i32, Logical>) -> Self {
|
||||
pub fn new(_args: Args) -> Self {
|
||||
Self {
|
||||
gradient_format: GradientInterpolation {
|
||||
color_space: GradientColorSpace::Oklch,
|
||||
|
@@ -4,16 +4,16 @@ use niri_config::{
|
||||
};
|
||||
use smithay::backend::renderer::element::RenderElement;
|
||||
use smithay::backend::renderer::gles::GlesRenderer;
|
||||
use smithay::utils::{Logical, Physical, Rectangle, Size};
|
||||
use smithay::utils::{Physical, Rectangle, Size};
|
||||
|
||||
use super::TestCase;
|
||||
use super::{Args, TestCase};
|
||||
|
||||
pub struct GradientSrgb {
|
||||
gradient_format: GradientInterpolation,
|
||||
}
|
||||
|
||||
impl GradientSrgb {
|
||||
pub fn new(_size: Size<i32, Logical>) -> Self {
|
||||
pub fn new(_args: Args) -> Self {
|
||||
Self {
|
||||
gradient_format: GradientInterpolation {
|
||||
color_space: GradientColorSpace::Srgb,
|
||||
|
@@ -2,16 +2,16 @@ use niri::render_helpers::border::BorderRenderElement;
|
||||
use niri_config::{Color, CornerRadius, GradientColorSpace, GradientInterpolation};
|
||||
use smithay::backend::renderer::element::RenderElement;
|
||||
use smithay::backend::renderer::gles::GlesRenderer;
|
||||
use smithay::utils::{Logical, Physical, Rectangle, Size};
|
||||
use smithay::utils::{Physical, Rectangle, Size};
|
||||
|
||||
use super::TestCase;
|
||||
use super::{Args, TestCase};
|
||||
|
||||
pub struct GradientSrgbAlpha {
|
||||
gradient_format: GradientInterpolation,
|
||||
}
|
||||
|
||||
impl GradientSrgbAlpha {
|
||||
pub fn new(_size: Size<i32, Logical>) -> Self {
|
||||
pub fn new(_args: Args) -> Self {
|
||||
Self {
|
||||
gradient_format: GradientInterpolation {
|
||||
color_space: GradientColorSpace::Srgb,
|
||||
|
@@ -4,16 +4,16 @@ use niri_config::{
|
||||
};
|
||||
use smithay::backend::renderer::element::RenderElement;
|
||||
use smithay::backend::renderer::gles::GlesRenderer;
|
||||
use smithay::utils::{Logical, Physical, Rectangle, Size};
|
||||
use smithay::utils::{Physical, Rectangle, Size};
|
||||
|
||||
use super::TestCase;
|
||||
use super::{Args, TestCase};
|
||||
|
||||
pub struct GradientSrgbLinear {
|
||||
gradient_format: GradientInterpolation,
|
||||
}
|
||||
|
||||
impl GradientSrgbLinear {
|
||||
pub fn new(_size: Size<i32, Logical>) -> Self {
|
||||
pub fn new(_args: Args) -> Self {
|
||||
Self {
|
||||
gradient_format: GradientInterpolation {
|
||||
color_space: GradientColorSpace::SrgbLinear,
|
||||
|
@@ -2,16 +2,16 @@ use niri::render_helpers::border::BorderRenderElement;
|
||||
use niri_config::{Color, CornerRadius, GradientColorSpace, GradientInterpolation};
|
||||
use smithay::backend::renderer::element::RenderElement;
|
||||
use smithay::backend::renderer::gles::GlesRenderer;
|
||||
use smithay::utils::{Logical, Physical, Rectangle, Size};
|
||||
use smithay::utils::{Physical, Rectangle, Size};
|
||||
|
||||
use super::TestCase;
|
||||
use super::{Args, TestCase};
|
||||
|
||||
pub struct GradientSrgbLinearAlpha {
|
||||
gradient_format: GradientInterpolation,
|
||||
}
|
||||
|
||||
impl GradientSrgbLinearAlpha {
|
||||
pub fn new(_size: Size<i32, Logical>) -> Self {
|
||||
pub fn new(_args: Args) -> Self {
|
||||
Self {
|
||||
gradient_format: GradientInterpolation {
|
||||
color_space: GradientColorSpace::SrgbLinear,
|
||||
|
@@ -1,18 +1,18 @@
|
||||
use std::collections::HashMap;
|
||||
use std::time::Duration;
|
||||
|
||||
use niri::animation::Clock;
|
||||
use niri::layout::workspace::ColumnWidth;
|
||||
use niri::layout::{LayoutElement as _, Options};
|
||||
use niri::render_helpers::RenderTarget;
|
||||
use niri::utils::get_monotonic_time;
|
||||
use niri_config::{Color, FloatOrInt, OutputName};
|
||||
use smithay::backend::renderer::element::RenderElement;
|
||||
use smithay::backend::renderer::gles::GlesRenderer;
|
||||
use smithay::desktop::layer_map_for_output;
|
||||
use smithay::output::{Mode, Output, PhysicalProperties, Subpixel};
|
||||
use smithay::utils::{Logical, Physical, Size};
|
||||
use smithay::utils::{Physical, Size};
|
||||
|
||||
use super::TestCase;
|
||||
use super::{Args, TestCase};
|
||||
use crate::test_window::TestWindow;
|
||||
|
||||
type DynStepFn = Box<dyn FnOnce(&mut Layout)>;
|
||||
@@ -20,13 +20,16 @@ type DynStepFn = Box<dyn FnOnce(&mut Layout)>;
|
||||
pub struct Layout {
|
||||
output: Output,
|
||||
windows: Vec<TestWindow>,
|
||||
clock: Clock,
|
||||
layout: niri::layout::Layout<TestWindow>,
|
||||
start_time: Duration,
|
||||
steps: HashMap<Duration, DynStepFn>,
|
||||
}
|
||||
|
||||
impl Layout {
|
||||
pub fn new(size: Size<i32, Logical>) -> Self {
|
||||
pub fn new(args: Args) -> Self {
|
||||
let Args { size, clock } = args;
|
||||
|
||||
let output = Output::new(
|
||||
String::new(),
|
||||
PhysicalProperties {
|
||||
@@ -63,20 +66,23 @@ impl Layout {
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
let mut layout = niri::layout::Layout::with_options(options);
|
||||
let mut layout = niri::layout::Layout::with_options(clock.clone(), options);
|
||||
layout.add_output(output.clone());
|
||||
|
||||
let start_time = clock.now();
|
||||
|
||||
Self {
|
||||
output,
|
||||
windows: Vec::new(),
|
||||
clock,
|
||||
layout,
|
||||
start_time: get_monotonic_time(),
|
||||
start_time,
|
||||
steps: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_in_between(size: Size<i32, Logical>) -> Self {
|
||||
let mut rv = Self::new(size);
|
||||
pub fn open_in_between(args: Args) -> Self {
|
||||
let mut rv = Self::new(args);
|
||||
|
||||
rv.add_window(TestWindow::freeform(0), Some(ColumnWidth::Proportion(0.3)));
|
||||
rv.add_window(TestWindow::freeform(1), Some(ColumnWidth::Proportion(0.3)));
|
||||
@@ -91,8 +97,8 @@ impl Layout {
|
||||
rv
|
||||
}
|
||||
|
||||
pub fn open_multiple_quickly(size: Size<i32, Logical>) -> Self {
|
||||
let mut rv = Self::new(size);
|
||||
pub fn open_multiple_quickly(args: Args) -> Self {
|
||||
let mut rv = Self::new(args);
|
||||
|
||||
for delay in [100, 200, 300] {
|
||||
rv.add_step(delay, move |l| {
|
||||
@@ -105,8 +111,8 @@ impl Layout {
|
||||
rv
|
||||
}
|
||||
|
||||
pub fn open_multiple_quickly_big(size: Size<i32, Logical>) -> Self {
|
||||
let mut rv = Self::new(size);
|
||||
pub fn open_multiple_quickly_big(args: Args) -> Self {
|
||||
let mut rv = Self::new(args);
|
||||
|
||||
for delay in [100, 200, 300] {
|
||||
rv.add_step(delay, move |l| {
|
||||
@@ -119,8 +125,8 @@ impl Layout {
|
||||
rv
|
||||
}
|
||||
|
||||
pub fn open_to_the_left(size: Size<i32, Logical>) -> Self {
|
||||
let mut rv = Self::new(size);
|
||||
pub fn open_to_the_left(args: Args) -> Self {
|
||||
let mut rv = Self::new(args);
|
||||
|
||||
rv.add_window(TestWindow::freeform(0), Some(ColumnWidth::Proportion(0.3)));
|
||||
rv.add_window(TestWindow::freeform(1), Some(ColumnWidth::Proportion(0.3)));
|
||||
@@ -135,8 +141,8 @@ impl Layout {
|
||||
rv
|
||||
}
|
||||
|
||||
pub fn open_to_the_left_big(size: Size<i32, Logical>) -> Self {
|
||||
let mut rv = Self::new(size);
|
||||
pub fn open_to_the_left_big(args: Args) -> Self {
|
||||
let mut rv = Self::new(args);
|
||||
|
||||
rv.add_window(TestWindow::freeform(0), Some(ColumnWidth::Proportion(0.3)));
|
||||
rv.add_window(TestWindow::freeform(1), Some(ColumnWidth::Proportion(0.8)));
|
||||
@@ -201,21 +207,23 @@ impl TestCase for Layout {
|
||||
self.layout.are_animations_ongoing(Some(&self.output)) || !self.steps.is_empty()
|
||||
}
|
||||
|
||||
fn advance_animations(&mut self, mut current_time: Duration) {
|
||||
fn advance_animations(&mut self, current_time: Duration) {
|
||||
let run = self
|
||||
.steps
|
||||
.keys()
|
||||
.copied()
|
||||
.filter(|delay| self.start_time + *delay <= current_time)
|
||||
.collect::<Vec<_>>();
|
||||
for key in &run {
|
||||
let f = self.steps.remove(key).unwrap();
|
||||
for delay in &run {
|
||||
let now = self.start_time + *delay;
|
||||
self.clock.set_time_override(Some(now));
|
||||
self.layout.advance_animations(now);
|
||||
|
||||
let f = self.steps.remove(delay).unwrap();
|
||||
f(self);
|
||||
}
|
||||
if !run.is_empty() {
|
||||
current_time = get_monotonic_time();
|
||||
}
|
||||
|
||||
self.clock.set_time_override(None);
|
||||
self.layout.advance_animations(current_time);
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,9 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use niri::animation::Clock;
|
||||
use smithay::backend::renderer::element::RenderElement;
|
||||
use smithay::backend::renderer::gles::GlesRenderer;
|
||||
use smithay::utils::{Physical, Size};
|
||||
use smithay::utils::{Logical, Physical, Size};
|
||||
|
||||
pub mod gradient_angle;
|
||||
pub mod gradient_area;
|
||||
@@ -21,6 +22,11 @@ pub mod layout;
|
||||
pub mod tile;
|
||||
pub mod window;
|
||||
|
||||
pub struct Args {
|
||||
pub size: Size<i32, Logical>,
|
||||
pub clock: Clock,
|
||||
}
|
||||
|
||||
pub trait TestCase {
|
||||
fn resize(&mut self, _width: i32, _height: i32) {}
|
||||
fn are_animations_ongoing(&self) -> bool {
|
||||
|
@@ -6,9 +6,9 @@ use niri::render_helpers::RenderTarget;
|
||||
use niri_config::{Color, FloatOrInt};
|
||||
use smithay::backend::renderer::element::RenderElement;
|
||||
use smithay::backend::renderer::gles::GlesRenderer;
|
||||
use smithay::utils::{Logical, Physical, Point, Rectangle, Scale, Size};
|
||||
use smithay::utils::{Physical, Point, Rectangle, Scale, Size};
|
||||
|
||||
use super::TestCase;
|
||||
use super::{Args, TestCase};
|
||||
use crate::test_window::TestWindow;
|
||||
|
||||
pub struct Tile {
|
||||
@@ -17,53 +17,44 @@ pub struct Tile {
|
||||
}
|
||||
|
||||
impl Tile {
|
||||
pub fn freeform(size: Size<i32, Logical>) -> Self {
|
||||
pub fn freeform(args: Args) -> Self {
|
||||
let window = TestWindow::freeform(0);
|
||||
let mut rv = Self::with_window(window);
|
||||
rv.tile.request_tile_size(size.to_f64(), false, None);
|
||||
rv.window.communicate();
|
||||
rv
|
||||
Self::with_window(args, window)
|
||||
}
|
||||
|
||||
pub fn fixed_size(size: Size<i32, Logical>) -> Self {
|
||||
pub fn fixed_size(args: Args) -> Self {
|
||||
let window = TestWindow::fixed_size(0);
|
||||
let mut rv = Self::with_window(window);
|
||||
rv.tile.request_tile_size(size.to_f64(), false, None);
|
||||
rv.window.communicate();
|
||||
rv
|
||||
Self::with_window(args, window)
|
||||
}
|
||||
|
||||
pub fn fixed_size_with_csd_shadow(size: Size<i32, Logical>) -> Self {
|
||||
pub fn fixed_size_with_csd_shadow(args: Args) -> Self {
|
||||
let window = TestWindow::fixed_size(0);
|
||||
window.set_csd_shadow_width(64);
|
||||
let mut rv = Self::with_window(window);
|
||||
rv.tile.request_tile_size(size.to_f64(), false, None);
|
||||
rv.window.communicate();
|
||||
rv
|
||||
Self::with_window(args, window)
|
||||
}
|
||||
|
||||
pub fn freeform_open(size: Size<i32, Logical>) -> Self {
|
||||
let mut rv = Self::freeform(size);
|
||||
pub fn freeform_open(args: Args) -> Self {
|
||||
let mut rv = Self::freeform(args);
|
||||
rv.window.set_color([0.1, 0.1, 0.1, 1.]);
|
||||
rv.tile.start_open_animation();
|
||||
rv
|
||||
}
|
||||
|
||||
pub fn fixed_size_open(size: Size<i32, Logical>) -> Self {
|
||||
let mut rv = Self::fixed_size(size);
|
||||
pub fn fixed_size_open(args: Args) -> Self {
|
||||
let mut rv = Self::fixed_size(args);
|
||||
rv.window.set_color([0.1, 0.1, 0.1, 1.]);
|
||||
rv.tile.start_open_animation();
|
||||
rv
|
||||
}
|
||||
|
||||
pub fn fixed_size_with_csd_shadow_open(size: Size<i32, Logical>) -> Self {
|
||||
let mut rv = Self::fixed_size_with_csd_shadow(size);
|
||||
pub fn fixed_size_with_csd_shadow_open(args: Args) -> Self {
|
||||
let mut rv = Self::fixed_size_with_csd_shadow(args);
|
||||
rv.window.set_color([0.1, 0.1, 0.1, 1.]);
|
||||
rv.tile.start_open_animation();
|
||||
rv
|
||||
}
|
||||
|
||||
pub fn with_window(window: TestWindow) -> Self {
|
||||
pub fn with_window(args: Args, window: TestWindow) -> Self {
|
||||
let options = Options {
|
||||
focus_ring: niri_config::FocusRing {
|
||||
off: true,
|
||||
@@ -77,7 +68,13 @@ impl Tile {
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
let tile = niri::layout::tile::Tile::new(window.clone(), 1., Rc::new(options));
|
||||
|
||||
let mut tile =
|
||||
niri::layout::tile::Tile::new(window.clone(), 1., args.clock, Rc::new(options));
|
||||
|
||||
tile.request_tile_size(args.size.to_f64(), false, None);
|
||||
window.communicate();
|
||||
|
||||
Self { window, tile }
|
||||
}
|
||||
}
|
||||
|
@@ -2,9 +2,9 @@ use niri::layout::LayoutElement;
|
||||
use niri::render_helpers::RenderTarget;
|
||||
use smithay::backend::renderer::element::RenderElement;
|
||||
use smithay::backend::renderer::gles::GlesRenderer;
|
||||
use smithay::utils::{Logical, Physical, Point, Scale, Size};
|
||||
use smithay::utils::{Physical, Point, Scale, Size};
|
||||
|
||||
use super::TestCase;
|
||||
use super::{Args, TestCase};
|
||||
use crate::test_window::TestWindow;
|
||||
|
||||
pub struct Window {
|
||||
@@ -12,24 +12,24 @@ pub struct Window {
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn freeform(size: Size<i32, Logical>) -> Self {
|
||||
pub fn freeform(args: Args) -> Self {
|
||||
let mut window = TestWindow::freeform(0);
|
||||
window.request_size(size, false, None);
|
||||
window.request_size(args.size, false, None);
|
||||
window.communicate();
|
||||
Self { window }
|
||||
}
|
||||
|
||||
pub fn fixed_size(size: Size<i32, Logical>) -> Self {
|
||||
pub fn fixed_size(args: Args) -> Self {
|
||||
let mut window = TestWindow::fixed_size(0);
|
||||
window.request_size(size, false, None);
|
||||
window.request_size(args.size, false, None);
|
||||
window.communicate();
|
||||
Self { window }
|
||||
}
|
||||
|
||||
pub fn fixed_size_with_csd_shadow(size: Size<i32, Logical>) -> Self {
|
||||
pub fn fixed_size_with_csd_shadow(args: Args) -> Self {
|
||||
let mut window = TestWindow::fixed_size(0);
|
||||
window.set_csd_shadow_width(64);
|
||||
window.request_size(size, false, None);
|
||||
window.request_size(args.size, false, None);
|
||||
window.communicate();
|
||||
Self { window }
|
||||
}
|
||||
|
@@ -5,12 +5,12 @@ use std::env;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use adw::prelude::{AdwApplicationWindowExt, NavigationPageExt};
|
||||
use cases::Args;
|
||||
use gtk::prelude::{
|
||||
AdjustmentExt, ApplicationExt, ApplicationExtManual, BoxExt, GtkWindowExt, WidgetExt,
|
||||
};
|
||||
use gtk::{gdk, gio, glib};
|
||||
use niri::animation::ANIMATION_SLOWDOWN;
|
||||
use smithay::utils::{Logical, Size};
|
||||
use smithay_view::SmithayView;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
@@ -72,11 +72,7 @@ fn build_ui(app: &adw::Application) {
|
||||
}
|
||||
|
||||
impl S {
|
||||
fn add<T: TestCase + 'static>(
|
||||
&self,
|
||||
make: impl Fn(Size<i32, Logical>) -> T + 'static,
|
||||
title: &str,
|
||||
) {
|
||||
fn add<T: TestCase + 'static>(&self, make: impl Fn(Args) -> T + 'static, title: &str) {
|
||||
let view = SmithayView::new(make);
|
||||
self.stack.add_titled(&view, None, title);
|
||||
}
|
||||
|
@@ -1,8 +1,8 @@
|
||||
use gtk::glib;
|
||||
use gtk::subclass::prelude::*;
|
||||
use smithay::utils::{Logical, Size};
|
||||
use smithay::utils::Size;
|
||||
|
||||
use crate::cases::TestCase;
|
||||
use crate::cases::{Args, TestCase};
|
||||
|
||||
mod imp {
|
||||
use std::cell::{Cell, OnceCell, RefCell};
|
||||
@@ -11,6 +11,7 @@ mod imp {
|
||||
use anyhow::{ensure, Context};
|
||||
use gtk::gdk;
|
||||
use gtk::prelude::*;
|
||||
use niri::animation::Clock;
|
||||
use niri::render_helpers::{resources, shaders};
|
||||
use niri::utils::get_monotonic_time;
|
||||
use smithay::backend::egl::ffi::egl;
|
||||
@@ -21,7 +22,7 @@ mod imp {
|
||||
|
||||
use super::*;
|
||||
|
||||
type DynMakeTestCase = Box<dyn Fn(Size<i32, Logical>) -> Box<dyn TestCase>>;
|
||||
type DynMakeTestCase = Box<dyn Fn(Args) -> Box<dyn TestCase>>;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SmithayView {
|
||||
@@ -30,6 +31,7 @@ mod imp {
|
||||
renderer: RefCell<Option<Result<GlesRenderer, ()>>>,
|
||||
pub make_test_case: OnceCell<DynMakeTestCase>,
|
||||
test_case: RefCell<Option<Box<dyn TestCase>>>,
|
||||
clock: RefCell<Clock>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
@@ -129,7 +131,11 @@ mod imp {
|
||||
let mut case = self.test_case.borrow_mut();
|
||||
let case = case.get_or_insert_with(|| {
|
||||
let make = self.make_test_case.get().unwrap();
|
||||
make(Size::from(size))
|
||||
let args = Args {
|
||||
size: Size::from(size),
|
||||
clock: self.clock.borrow().clone(),
|
||||
};
|
||||
make(args)
|
||||
});
|
||||
|
||||
case.advance_animations(get_monotonic_time());
|
||||
@@ -232,12 +238,10 @@ glib::wrapper! {
|
||||
}
|
||||
|
||||
impl SmithayView {
|
||||
pub fn new<T: TestCase + 'static>(
|
||||
make_test_case: impl Fn(Size<i32, Logical>) -> T + 'static,
|
||||
) -> Self {
|
||||
pub fn new<T: TestCase + 'static>(make_test_case: impl Fn(Args) -> T + 'static) -> Self {
|
||||
let obj: Self = glib::Object::builder().build();
|
||||
|
||||
let make = move |size| Box::new(make_test_case(size)) as Box<dyn TestCase>;
|
||||
let make = move |args| Box::new(make_test_case(args)) as Box<dyn TestCase>;
|
||||
let make_test_case = Box::new(make) as _;
|
||||
let _ = obj.imp().make_test_case.set(make_test_case);
|
||||
|
||||
|
41
src/animation/clock.rs
Normal file
41
src/animation/clock.rs
Normal file
@@ -0,0 +1,41 @@
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::utils::get_monotonic_time;
|
||||
|
||||
/// Clock that can have its time value overridden.
|
||||
///
|
||||
/// Can be cloned to share the same clock.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct Clock {
|
||||
time_override: Rc<Cell<Option<Duration>>>,
|
||||
}
|
||||
|
||||
impl Clock {
|
||||
/// Creates a new [`Clock`] with time override in place.
|
||||
pub fn with_override(time: Duration) -> Self {
|
||||
Self {
|
||||
time_override: Rc::new(Cell::new(Some(time))),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the current time override.
|
||||
pub fn set_time_override(&mut self, time: Option<Duration>) {
|
||||
self.time_override.set(time);
|
||||
}
|
||||
|
||||
/// Gets the current time.
|
||||
#[inline]
|
||||
pub fn now(&self) -> Duration {
|
||||
self.time_override.get().unwrap_or_else(get_monotonic_time)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Clock {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
Rc::ptr_eq(&self.time_override, &other.time_override)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Clock {}
|
@@ -4,11 +4,12 @@ use keyframe::functions::{EaseOutCubic, EaseOutQuad};
|
||||
use keyframe::EasingFunction;
|
||||
use portable_atomic::{AtomicF64, Ordering};
|
||||
|
||||
use crate::utils::get_monotonic_time;
|
||||
|
||||
mod spring;
|
||||
pub use spring::{Spring, SpringParams};
|
||||
|
||||
mod clock;
|
||||
pub use clock::Clock;
|
||||
|
||||
pub static ANIMATION_SLOWDOWN: AtomicF64 = AtomicF64::new(1.);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -48,11 +49,24 @@ pub enum Curve {
|
||||
}
|
||||
|
||||
impl Animation {
|
||||
pub fn new(from: f64, to: f64, initial_velocity: f64, config: niri_config::Animation) -> Self {
|
||||
pub fn new(
|
||||
current_time: Duration,
|
||||
from: f64,
|
||||
to: f64,
|
||||
initial_velocity: f64,
|
||||
config: niri_config::Animation,
|
||||
) -> Self {
|
||||
// Scale the velocity by slowdown to keep the touchpad gestures feeling right.
|
||||
let initial_velocity = initial_velocity * ANIMATION_SLOWDOWN.load(Ordering::Relaxed);
|
||||
|
||||
let mut rv = Self::ease(from, to, initial_velocity, 0, Curve::EaseOutCubic);
|
||||
let mut rv = Self::ease(
|
||||
current_time,
|
||||
from,
|
||||
to,
|
||||
initial_velocity,
|
||||
0,
|
||||
Curve::EaseOutCubic,
|
||||
);
|
||||
if config.off {
|
||||
rv.is_off = true;
|
||||
return rv;
|
||||
@@ -83,10 +97,11 @@ impl Animation {
|
||||
initial_velocity: self.initial_velocity,
|
||||
params,
|
||||
};
|
||||
*self = Self::spring(spring);
|
||||
*self = Self::spring(current_time, spring);
|
||||
}
|
||||
niri_config::AnimationKind::Easing(p) => {
|
||||
*self = Self::ease(
|
||||
current_time,
|
||||
self.from,
|
||||
self.to,
|
||||
self.initial_velocity,
|
||||
@@ -101,7 +116,13 @@ impl Animation {
|
||||
}
|
||||
|
||||
/// Restarts the animation using the previous config.
|
||||
pub fn restarted(&self, from: f64, to: f64, initial_velocity: f64) -> Self {
|
||||
pub fn restarted(
|
||||
&self,
|
||||
current_time: Duration,
|
||||
from: f64,
|
||||
to: f64,
|
||||
initial_velocity: f64,
|
||||
) -> Self {
|
||||
if self.is_off {
|
||||
return self.clone();
|
||||
}
|
||||
@@ -111,6 +132,7 @@ impl Animation {
|
||||
|
||||
match self.kind {
|
||||
Kind::Easing { curve } => Self::ease(
|
||||
current_time,
|
||||
from,
|
||||
to,
|
||||
initial_velocity,
|
||||
@@ -124,23 +146,32 @@ impl Animation {
|
||||
initial_velocity: self.initial_velocity,
|
||||
params: spring.params,
|
||||
};
|
||||
Self::spring(spring)
|
||||
Self::spring(current_time, spring)
|
||||
}
|
||||
Kind::Deceleration {
|
||||
initial_velocity,
|
||||
deceleration_rate,
|
||||
} => {
|
||||
let threshold = 0.001; // FIXME
|
||||
Self::decelerate(from, initial_velocity, deceleration_rate, threshold)
|
||||
Self::decelerate(
|
||||
current_time,
|
||||
from,
|
||||
initial_velocity,
|
||||
deceleration_rate,
|
||||
threshold,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ease(from: f64, to: f64, initial_velocity: f64, duration_ms: u64, curve: Curve) -> 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();
|
||||
|
||||
pub fn ease(
|
||||
current_time: Duration,
|
||||
from: f64,
|
||||
to: f64,
|
||||
initial_velocity: f64,
|
||||
duration_ms: u64,
|
||||
curve: Curve,
|
||||
) -> Self {
|
||||
let duration = Duration::from_millis(duration_ms);
|
||||
let kind = Kind::Easing { curve };
|
||||
|
||||
@@ -152,19 +183,15 @@ impl Animation {
|
||||
duration,
|
||||
// Our current curves never overshoot.
|
||||
clamped_duration: duration,
|
||||
start_time: now,
|
||||
current_time: now,
|
||||
start_time: current_time,
|
||||
current_time,
|
||||
kind,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spring(spring: Spring) -> Self {
|
||||
pub fn spring(current_time: Duration, spring: Spring) -> Self {
|
||||
let _span = tracy_client::span!("Animation::spring");
|
||||
|
||||
// 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();
|
||||
|
||||
let duration = spring.duration();
|
||||
let clamped_duration = spring.clamped_duration().unwrap_or(duration);
|
||||
let kind = Kind::Spring(spring);
|
||||
@@ -176,22 +203,19 @@ impl Animation {
|
||||
is_off: false,
|
||||
duration,
|
||||
clamped_duration,
|
||||
start_time: now,
|
||||
current_time: now,
|
||||
start_time: current_time,
|
||||
current_time,
|
||||
kind,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decelerate(
|
||||
current_time: Duration,
|
||||
from: f64,
|
||||
initial_velocity: f64,
|
||||
deceleration_rate: f64,
|
||||
threshold: f64,
|
||||
) -> 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();
|
||||
|
||||
let duration_s = if initial_velocity == 0. {
|
||||
0.
|
||||
} else {
|
||||
@@ -214,8 +238,8 @@ impl Animation {
|
||||
is_off: false,
|
||||
duration,
|
||||
clamped_duration: duration,
|
||||
start_time: now,
|
||||
current_time: now,
|
||||
start_time: current_time,
|
||||
current_time,
|
||||
kind,
|
||||
}
|
||||
}
|
||||
|
@@ -3024,6 +3024,7 @@ pub fn mods_with_finger_scroll_binds(comp_mod: CompositorMod, binds: &Binds) ->
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::animation::Clock;
|
||||
|
||||
#[test]
|
||||
fn bindings_suppress_keys() {
|
||||
@@ -3042,7 +3043,7 @@ mod tests {
|
||||
let comp_mod = CompositorMod::Super;
|
||||
let mut suppressed_keys = HashSet::new();
|
||||
|
||||
let screenshot_ui = ScreenshotUi::new(Default::default());
|
||||
let screenshot_ui = ScreenshotUi::new(Clock::default(), Default::default());
|
||||
let disable_power_key_handling = false;
|
||||
|
||||
// The key_code we pick is arbitrary, the only thing
|
||||
|
@@ -142,8 +142,7 @@ impl ClosingWindow {
|
||||
match &mut self.anim_state {
|
||||
AnimationState::Waiting { blocker, anim } => {
|
||||
if blocker.state() != BlockerState::Pending {
|
||||
let mut anim = anim.restarted(0., 1., 0.);
|
||||
anim.set_current_time(current_time);
|
||||
let anim = anim.restarted(current_time, 0., 1., 0.);
|
||||
self.anim_state = AnimationState::Animating(anim);
|
||||
}
|
||||
}
|
||||
|
@@ -52,6 +52,7 @@ use workspace::WorkspaceId;
|
||||
pub use self::monitor::MonitorRenderElement;
|
||||
use self::monitor::{Monitor, WorkspaceSwitch};
|
||||
use self::workspace::{compute_working_area, Column, ColumnWidth, InsertHint, OutputId, Workspace};
|
||||
use crate::animation::Clock;
|
||||
use crate::layout::workspace::InsertPosition;
|
||||
use crate::niri_render_elements;
|
||||
use crate::render_helpers::renderer::NiriRenderer;
|
||||
@@ -216,6 +217,8 @@ pub struct Layout<W: LayoutElement> {
|
||||
last_active_workspace_id: HashMap<String, WorkspaceId>,
|
||||
/// Ongoing interactive move.
|
||||
interactive_move: Option<InteractiveMoveState<W>>,
|
||||
/// Clock for driving animations.
|
||||
clock: Clock,
|
||||
/// Configurable properties of the layout.
|
||||
options: Rc<Options>,
|
||||
}
|
||||
@@ -433,27 +436,30 @@ impl Options {
|
||||
}
|
||||
|
||||
impl<W: LayoutElement> Layout<W> {
|
||||
pub fn new(config: &Config) -> Self {
|
||||
Self::with_options_and_workspaces(config, Options::from_config(config))
|
||||
pub fn new(clock: Clock, config: &Config) -> Self {
|
||||
Self::with_options_and_workspaces(clock, config, Options::from_config(config))
|
||||
}
|
||||
|
||||
pub fn with_options(options: Options) -> Self {
|
||||
pub fn with_options(clock: Clock, options: Options) -> Self {
|
||||
Self {
|
||||
monitor_set: MonitorSet::NoOutputs { workspaces: vec![] },
|
||||
is_active: true,
|
||||
last_active_workspace_id: HashMap::new(),
|
||||
interactive_move: None,
|
||||
clock,
|
||||
options: Rc::new(options),
|
||||
}
|
||||
}
|
||||
|
||||
fn with_options_and_workspaces(config: &Config, options: Options) -> Self {
|
||||
fn with_options_and_workspaces(clock: Clock, config: &Config, options: Options) -> Self {
|
||||
let opts = Rc::new(options);
|
||||
|
||||
let workspaces = config
|
||||
.workspaces
|
||||
.iter()
|
||||
.map(|ws| Workspace::new_with_config_no_outputs(Some(ws.clone()), opts.clone()))
|
||||
.map(|ws| {
|
||||
Workspace::new_with_config_no_outputs(Some(ws.clone()), clock.clone(), opts.clone())
|
||||
})
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
@@ -461,6 +467,7 @@ impl<W: LayoutElement> Layout<W> {
|
||||
is_active: true,
|
||||
last_active_workspace_id: HashMap::new(),
|
||||
interactive_move: None,
|
||||
clock,
|
||||
options: opts,
|
||||
}
|
||||
}
|
||||
@@ -522,13 +529,18 @@ impl<W: LayoutElement> Layout<W> {
|
||||
}
|
||||
|
||||
// Make sure there's always an empty workspace.
|
||||
workspaces.push(Workspace::new(output.clone(), self.options.clone()));
|
||||
workspaces.push(Workspace::new(
|
||||
output.clone(),
|
||||
self.clock.clone(),
|
||||
self.options.clone(),
|
||||
));
|
||||
|
||||
for ws in &mut workspaces {
|
||||
ws.set_output(Some(output.clone()));
|
||||
}
|
||||
|
||||
let mut monitor = Monitor::new(output, workspaces, self.options.clone());
|
||||
let mut monitor =
|
||||
Monitor::new(output, workspaces, self.clock.clone(), self.options.clone());
|
||||
monitor.active_workspace_idx = active_workspace_idx.unwrap_or(0);
|
||||
monitors.push(monitor);
|
||||
|
||||
@@ -540,7 +552,11 @@ impl<W: LayoutElement> Layout<W> {
|
||||
}
|
||||
MonitorSet::NoOutputs { mut workspaces } => {
|
||||
// We know there are no empty workspaces there, so add one.
|
||||
workspaces.push(Workspace::new(output.clone(), self.options.clone()));
|
||||
workspaces.push(Workspace::new(
|
||||
output.clone(),
|
||||
self.clock.clone(),
|
||||
self.options.clone(),
|
||||
));
|
||||
|
||||
let ws_id_to_activate = self.last_active_workspace_id.remove(&output.name());
|
||||
let mut active_workspace_idx = 0;
|
||||
@@ -553,7 +569,8 @@ impl<W: LayoutElement> Layout<W> {
|
||||
}
|
||||
}
|
||||
|
||||
let mut monitor = Monitor::new(output, workspaces, self.options.clone());
|
||||
let mut monitor =
|
||||
Monitor::new(output, workspaces, self.clock.clone(), self.options.clone());
|
||||
monitor.active_workspace_idx = active_workspace_idx;
|
||||
|
||||
MonitorSet::Normal {
|
||||
@@ -785,7 +802,10 @@ impl<W: LayoutElement> Layout<W> {
|
||||
let ws = if let Some(ws) = workspaces.get_mut(0) {
|
||||
ws
|
||||
} else {
|
||||
workspaces.push(Workspace::new_no_outputs(self.options.clone()));
|
||||
workspaces.push(Workspace::new_no_outputs(
|
||||
self.clock.clone(),
|
||||
self.options.clone(),
|
||||
));
|
||||
&mut workspaces[0]
|
||||
};
|
||||
ws.add_window(None, window, true, width, is_full_width);
|
||||
@@ -1992,6 +2012,8 @@ impl<W: LayoutElement> Layout<W> {
|
||||
move_win_id = Some(window_id.clone());
|
||||
}
|
||||
InteractiveMoveState::Moving(move_) => {
|
||||
assert_eq!(self.clock, move_.tile.clock);
|
||||
|
||||
let scale = move_.output.current_scale().fractional_scale();
|
||||
let options = Options::clone(&self.options).adjusted_for_scale(scale);
|
||||
assert_eq!(
|
||||
@@ -2026,6 +2048,8 @@ impl<W: LayoutElement> Layout<W> {
|
||||
"with no outputs there cannot be empty unnamed workspaces"
|
||||
);
|
||||
|
||||
assert_eq!(self.clock, workspace.clock);
|
||||
|
||||
assert_eq!(
|
||||
workspace.base_options, self.options,
|
||||
"workspace base options must be synchronized with layout"
|
||||
@@ -2070,6 +2094,7 @@ impl<W: LayoutElement> Layout<W> {
|
||||
);
|
||||
assert!(monitor.active_workspace_idx < monitor.workspaces.len());
|
||||
|
||||
assert_eq!(self.clock, monitor.clock);
|
||||
assert_eq!(
|
||||
monitor.options, self.options,
|
||||
"monitor options must be synchronized with layout"
|
||||
@@ -2135,6 +2160,8 @@ impl<W: LayoutElement> Layout<W> {
|
||||
// exists.
|
||||
|
||||
for workspace in &monitor.workspaces {
|
||||
assert_eq!(self.clock, workspace.clock);
|
||||
|
||||
assert_eq!(
|
||||
workspace.base_options, self.options,
|
||||
"workspace options must be synchronized with layout"
|
||||
@@ -2326,6 +2353,7 @@ impl<W: LayoutElement> Layout<W> {
|
||||
return;
|
||||
}
|
||||
|
||||
let clock = self.clock.clone();
|
||||
let options = self.options.clone();
|
||||
|
||||
match &mut self.monitor_set {
|
||||
@@ -2349,6 +2377,7 @@ impl<W: LayoutElement> Layout<W> {
|
||||
let ws = Workspace::new_with_config(
|
||||
mon.output.clone(),
|
||||
Some(ws_config.clone()),
|
||||
clock,
|
||||
options,
|
||||
);
|
||||
mon.workspaces.insert(0, ws);
|
||||
@@ -2357,7 +2386,8 @@ impl<W: LayoutElement> Layout<W> {
|
||||
mon.clean_up_workspaces();
|
||||
}
|
||||
MonitorSet::NoOutputs { workspaces } => {
|
||||
let ws = Workspace::new_with_config_no_outputs(Some(ws_config.clone()), options);
|
||||
let ws =
|
||||
Workspace::new_with_config_no_outputs(Some(ws_config.clone()), clock, options);
|
||||
workspaces.insert(0, ws);
|
||||
}
|
||||
}
|
||||
@@ -3161,7 +3191,10 @@ impl<W: LayoutElement> Layout<W> {
|
||||
let ws = if let Some(ws) = workspaces.get_mut(0) {
|
||||
ws
|
||||
} else {
|
||||
workspaces.push(Workspace::new_no_outputs(self.options.clone()));
|
||||
workspaces.push(Workspace::new_no_outputs(
|
||||
self.clock.clone(),
|
||||
self.options.clone(),
|
||||
));
|
||||
&mut workspaces[0]
|
||||
};
|
||||
|
||||
@@ -3620,7 +3653,7 @@ mod tests {
|
||||
|
||||
impl<W: LayoutElement> Default for Layout<W> {
|
||||
fn default() -> Self {
|
||||
Self::with_options(Default::default())
|
||||
Self::with_options(Clock::with_override(Duration::ZERO), Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3854,6 +3887,16 @@ mod tests {
|
||||
prop_oneof![Just(1.), Just(1.5), Just(2.),]
|
||||
}
|
||||
|
||||
fn arbitrary_msec_delta() -> impl Strategy<Value = i32> {
|
||||
prop_oneof![
|
||||
1 => Just(-1000),
|
||||
2 => Just(-10),
|
||||
1 => Just(0),
|
||||
2 => Just(10),
|
||||
6 => Just(1000),
|
||||
]
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Arbitrary)]
|
||||
enum Op {
|
||||
AddOutput(#[proptest(strategy = "1..=5usize")] usize),
|
||||
@@ -3997,6 +4040,10 @@ mod tests {
|
||||
Refresh {
|
||||
is_active: bool,
|
||||
},
|
||||
AdvanceAnimations {
|
||||
#[proptest(strategy = "arbitrary_msec_delta()")]
|
||||
msec_delta: i32,
|
||||
},
|
||||
MoveWorkspaceToOutput(#[proptest(strategy = "1..=5u8")] u8),
|
||||
ViewOffsetGestureBegin {
|
||||
#[proptest(strategy = "1..=5usize")]
|
||||
@@ -4505,6 +4552,16 @@ mod tests {
|
||||
Op::Refresh { is_active } => {
|
||||
layout.refresh(is_active);
|
||||
}
|
||||
Op::AdvanceAnimations { msec_delta } => {
|
||||
let mut now = layout.clock.now();
|
||||
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);
|
||||
}
|
||||
Op::MoveWorkspaceToOutput(id) => {
|
||||
let name = format!("output{id}");
|
||||
let Some(output) = layout.outputs().find(|o| o.name() == name).cloned() else {
|
||||
@@ -4617,7 +4674,7 @@ mod tests {
|
||||
|
||||
#[track_caller]
|
||||
fn check_ops_with_options(options: Options, ops: &[Op]) {
|
||||
let mut layout = Layout::with_options(options);
|
||||
let mut layout = Layout::with_options(Clock::with_override(Duration::ZERO), options);
|
||||
|
||||
for op in ops {
|
||||
op.apply(&mut layout);
|
||||
@@ -5440,7 +5497,7 @@ mod tests {
|
||||
config.layout.border.off = false;
|
||||
config.layout.border.width = FloatOrInt(2.);
|
||||
|
||||
let mut layout = Layout::new(&config);
|
||||
let mut layout = Layout::new(Clock::default(), &config);
|
||||
|
||||
Op::AddWindow {
|
||||
id: 1,
|
||||
@@ -5460,7 +5517,7 @@ mod tests {
|
||||
let mut config = Config::default();
|
||||
config.layout.preset_window_heights = vec![PresetSize::Fixed(1), PresetSize::Fixed(2)];
|
||||
|
||||
let mut layout = Layout::new(&config);
|
||||
let mut layout = Layout::new(Clock::default(), &config);
|
||||
|
||||
let ops = [
|
||||
Op::AddOutput(1),
|
||||
@@ -5752,6 +5809,37 @@ mod tests {
|
||||
check_ops(&ops);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interactive_move_onto_last_workspace() {
|
||||
let ops = [
|
||||
Op::AddOutput(1),
|
||||
Op::AddWindow {
|
||||
id: 0,
|
||||
bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)),
|
||||
min_max_size: Default::default(),
|
||||
},
|
||||
Op::InteractiveMoveBegin {
|
||||
window: 0,
|
||||
output_idx: 1,
|
||||
px: 0.,
|
||||
py: 0.,
|
||||
},
|
||||
Op::InteractiveMoveUpdate {
|
||||
window: 0,
|
||||
dx: 1000.,
|
||||
dy: 0.,
|
||||
output_idx: 1,
|
||||
px: 0.,
|
||||
py: 0.,
|
||||
},
|
||||
Op::FocusWorkspaceDown,
|
||||
Op::AdvanceAnimations { msec_delta: 1000 },
|
||||
Op::InteractiveMoveEnd { window: 0 },
|
||||
];
|
||||
|
||||
check_ops(&ops);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn output_active_workspace_is_preserved() {
|
||||
let ops = [
|
||||
|
@@ -15,7 +15,7 @@ use super::workspace::{
|
||||
WorkspaceRenderElement,
|
||||
};
|
||||
use super::{LayoutElement, Options};
|
||||
use crate::animation::Animation;
|
||||
use crate::animation::{Animation, Clock};
|
||||
use crate::input::swipe_tracker::SwipeTracker;
|
||||
use crate::render_helpers::renderer::NiriRenderer;
|
||||
use crate::render_helpers::RenderTarget;
|
||||
@@ -45,6 +45,8 @@ pub struct Monitor<W: LayoutElement> {
|
||||
pub(super) previous_workspace_id: Option<WorkspaceId>,
|
||||
/// In-progress switch between workspaces.
|
||||
pub(super) workspace_switch: Option<WorkspaceSwitch>,
|
||||
/// Clock for driving animations.
|
||||
pub(super) clock: Clock,
|
||||
/// Configurable properties of the layout.
|
||||
pub(super) options: Rc<Options>,
|
||||
}
|
||||
@@ -94,7 +96,12 @@ impl WorkspaceSwitch {
|
||||
}
|
||||
|
||||
impl<W: LayoutElement> Monitor<W> {
|
||||
pub fn new(output: Output, workspaces: Vec<Workspace<W>>, options: Rc<Options>) -> Self {
|
||||
pub fn new(
|
||||
output: Output,
|
||||
workspaces: Vec<Workspace<W>>,
|
||||
clock: Clock,
|
||||
options: Rc<Options>,
|
||||
) -> Self {
|
||||
Self {
|
||||
output_name: output.name(),
|
||||
output,
|
||||
@@ -102,6 +109,7 @@ impl<W: LayoutElement> Monitor<W> {
|
||||
active_workspace_idx: 0,
|
||||
previous_workspace_id: None,
|
||||
workspace_switch: None,
|
||||
clock,
|
||||
options,
|
||||
}
|
||||
}
|
||||
@@ -151,7 +159,11 @@ impl<W: LayoutElement> Monitor<W> {
|
||||
}
|
||||
|
||||
pub fn add_workspace_bottom(&mut self) {
|
||||
let ws = Workspace::new(self.output.clone(), self.options.clone());
|
||||
let ws = Workspace::new(
|
||||
self.output.clone(),
|
||||
self.clock.clone(),
|
||||
self.options.clone(),
|
||||
);
|
||||
self.workspaces.push(ws);
|
||||
}
|
||||
|
||||
@@ -172,6 +184,7 @@ impl<W: LayoutElement> Monitor<W> {
|
||||
self.active_workspace_idx = idx;
|
||||
|
||||
self.workspace_switch = Some(WorkspaceSwitch::Animation(Animation::new(
|
||||
self.clock.now(),
|
||||
current_idx,
|
||||
idx as f64,
|
||||
0.,
|
||||
@@ -1099,6 +1112,7 @@ impl<W: LayoutElement> Monitor<W> {
|
||||
|
||||
self.active_workspace_idx = new_idx;
|
||||
self.workspace_switch = Some(WorkspaceSwitch::Animation(Animation::new(
|
||||
self.clock.now(),
|
||||
gesture.current_idx,
|
||||
new_idx as f64,
|
||||
velocity,
|
||||
|
@@ -13,7 +13,7 @@ use super::{
|
||||
LayoutElement, LayoutElementRenderElement, LayoutElementRenderSnapshot, Options,
|
||||
RESIZE_ANIMATION_THRESHOLD,
|
||||
};
|
||||
use crate::animation::Animation;
|
||||
use crate::animation::{Animation, Clock};
|
||||
use crate::niri_render_elements;
|
||||
use crate::render_helpers::border::BorderRenderElement;
|
||||
use crate::render_helpers::clipped_surface::{ClippedSurfaceRenderElement, RoundedCornerDamage};
|
||||
@@ -76,6 +76,9 @@ pub struct Tile<W: LayoutElement> {
|
||||
/// Scale of the output the tile is on (and rounds its sizes to).
|
||||
scale: f64,
|
||||
|
||||
/// Clock for driving animations.
|
||||
pub(super) clock: Clock,
|
||||
|
||||
/// Configurable properties of the layout.
|
||||
pub(super) options: Rc<Options>,
|
||||
}
|
||||
@@ -110,7 +113,7 @@ struct MoveAnimation {
|
||||
}
|
||||
|
||||
impl<W: LayoutElement> Tile<W> {
|
||||
pub fn new(window: W, scale: f64, options: Rc<Options>) -> Self {
|
||||
pub fn new(window: W, scale: f64, clock: Clock, options: Rc<Options>) -> Self {
|
||||
let rules = window.rules();
|
||||
let border_config = rules.border.resolve_against(options.border);
|
||||
let focus_ring_config = rules.focus_ring.resolve_against(options.focus_ring.into());
|
||||
@@ -130,6 +133,7 @@ impl<W: LayoutElement> Tile<W> {
|
||||
unmap_snapshot: None,
|
||||
rounded_corner_damage: Default::default(),
|
||||
scale,
|
||||
clock,
|
||||
options,
|
||||
}
|
||||
}
|
||||
@@ -180,7 +184,13 @@ impl<W: LayoutElement> Tile<W> {
|
||||
let change = self.window.size().to_f64().to_point() - size_from.to_point();
|
||||
let change = f64::max(change.x.abs(), change.y.abs());
|
||||
if change > RESIZE_ANIMATION_THRESHOLD {
|
||||
let anim = Animation::new(0., 1., 0., self.options.animations.window_resize.anim);
|
||||
let anim = Animation::new(
|
||||
self.clock.now(),
|
||||
0.,
|
||||
1.,
|
||||
0.,
|
||||
self.options.animations.window_resize.anim,
|
||||
);
|
||||
self.resize_animation = Some(ResizeAnimation {
|
||||
anim,
|
||||
size_from,
|
||||
@@ -316,6 +326,7 @@ impl<W: LayoutElement> Tile<W> {
|
||||
|
||||
pub fn start_open_animation(&mut self) {
|
||||
self.open_animation = Some(OpenAnimation::new(Animation::new(
|
||||
self.clock.now(),
|
||||
0.,
|
||||
1.,
|
||||
0.,
|
||||
@@ -342,8 +353,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(1., 0., 0.))
|
||||
.unwrap_or_else(|| Animation::new(1., 0., 0., config));
|
||||
.map(|anim| anim.restarted(self.clock.now(), 1., 0., 0.))
|
||||
.unwrap_or_else(|| Animation::new(self.clock.now(), 1., 0., 0., config));
|
||||
|
||||
self.move_x_animation = Some(MoveAnimation {
|
||||
anim,
|
||||
@@ -361,8 +372,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(1., 0., 0.))
|
||||
.unwrap_or_else(|| Animation::new(1., 0., 0., config));
|
||||
.map(|anim| anim.restarted(self.clock.now(), 1., 0., 0.))
|
||||
.unwrap_or_else(|| Animation::new(self.clock.now(), 1., 0., 0., config));
|
||||
|
||||
self.move_y_animation = Some(MoveAnimation {
|
||||
anim,
|
||||
|
@@ -19,7 +19,7 @@ use super::closing_window::{ClosingWindow, ClosingWindowRenderElement};
|
||||
use super::insert_hint_element::{InsertHintElement, InsertHintRenderElement};
|
||||
use super::tile::{Tile, TileRenderElement, TileRenderSnapshot};
|
||||
use super::{ConfigureIntent, InteractiveResizeData, LayoutElement, Options, RemovedTile};
|
||||
use crate::animation::Animation;
|
||||
use crate::animation::{Animation, Clock};
|
||||
use crate::input::swipe_tracker::SwipeTracker;
|
||||
use crate::niri_render_elements;
|
||||
use crate::render_helpers::renderer::NiriRenderer;
|
||||
@@ -113,6 +113,9 @@ pub struct Workspace<W: LayoutElement> {
|
||||
/// Insert hint element for rendering.
|
||||
insert_hint_element: InsertHintElement,
|
||||
|
||||
/// Clock for driving animations.
|
||||
pub(super) clock: Clock,
|
||||
|
||||
/// Configurable properties of the layout as received from the parent monitor.
|
||||
pub(super) base_options: Rc<Options>,
|
||||
|
||||
@@ -293,6 +296,9 @@ pub struct Column<W: LayoutElement> {
|
||||
/// Scale of the output the column is on (and rounds its sizes to).
|
||||
scale: f64,
|
||||
|
||||
/// Clock for driving animations.
|
||||
clock: Clock,
|
||||
|
||||
/// Configurable properties of the layout.
|
||||
options: Rc<Options>,
|
||||
}
|
||||
@@ -403,13 +409,14 @@ impl TileData {
|
||||
}
|
||||
|
||||
impl<W: LayoutElement> Workspace<W> {
|
||||
pub fn new(output: Output, options: Rc<Options>) -> Self {
|
||||
Self::new_with_config(output, None, options)
|
||||
pub fn new(output: Output, clock: Clock, options: Rc<Options>) -> Self {
|
||||
Self::new_with_config(output, None, clock, options)
|
||||
}
|
||||
|
||||
pub fn new_with_config(
|
||||
output: Output,
|
||||
config: Option<WorkspaceConfig>,
|
||||
clock: Clock,
|
||||
base_options: Rc<Options>,
|
||||
) -> Self {
|
||||
let original_output = config
|
||||
@@ -442,6 +449,7 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
closing_windows: vec![],
|
||||
insert_hint: None,
|
||||
insert_hint_element: InsertHintElement::new(options.insert_hint),
|
||||
clock,
|
||||
base_options,
|
||||
options,
|
||||
name: config.map(|c| c.name.0),
|
||||
@@ -451,6 +459,7 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
|
||||
pub fn new_with_config_no_outputs(
|
||||
config: Option<WorkspaceConfig>,
|
||||
clock: Clock,
|
||||
base_options: Rc<Options>,
|
||||
) -> Self {
|
||||
let original_output = OutputId(
|
||||
@@ -482,6 +491,7 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
closing_windows: vec![],
|
||||
insert_hint: None,
|
||||
insert_hint_element: InsertHintElement::new(options.insert_hint),
|
||||
clock,
|
||||
base_options,
|
||||
options,
|
||||
name: config.map(|c| c.name.0),
|
||||
@@ -489,8 +499,8 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_no_outputs(options: Rc<Options>) -> Self {
|
||||
Self::new_with_config_no_outputs(None, options)
|
||||
pub fn new_no_outputs(clock: Clock, options: Rc<Options>) -> Self {
|
||||
Self::new_with_config_no_outputs(None, clock, options)
|
||||
}
|
||||
|
||||
pub fn id(&self) -> WorkspaceId {
|
||||
@@ -941,6 +951,7 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
|
||||
// FIXME: also compute and use current velocity.
|
||||
self.view_offset_adj = Some(ViewOffsetAdjustment::Animation(Animation::new(
|
||||
self.clock.now(),
|
||||
self.view_offset,
|
||||
new_view_offset,
|
||||
0.,
|
||||
@@ -1100,7 +1111,12 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
width: ColumnWidth,
|
||||
is_full_width: bool,
|
||||
) {
|
||||
let tile = Tile::new(window, self.scale.fractional_scale(), self.options.clone());
|
||||
let tile = Tile::new(
|
||||
window,
|
||||
self.scale.fractional_scale(),
|
||||
self.clock.clone(),
|
||||
self.options.clone(),
|
||||
);
|
||||
self.add_tile(col_idx, tile, activate, width, is_full_width, None);
|
||||
}
|
||||
|
||||
@@ -1118,7 +1134,6 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
self.view_size,
|
||||
self.working_area,
|
||||
self.scale.fractional_scale(),
|
||||
self.options.clone(),
|
||||
width,
|
||||
is_full_width,
|
||||
true,
|
||||
@@ -1682,7 +1697,13 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
) {
|
||||
let output_scale = Scale::from(self.scale.fractional_scale());
|
||||
|
||||
let anim = Animation::new(0., 1., 0., self.options.animations.window_close.anim);
|
||||
let anim = Animation::new(
|
||||
self.clock.now(),
|
||||
0.,
|
||||
1.,
|
||||
0.,
|
||||
self.options.animations.window_close.anim,
|
||||
);
|
||||
|
||||
let blocker = if self.options.disable_transactions {
|
||||
TransactionBlocker::completed()
|
||||
@@ -1725,6 +1746,7 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
|
||||
for (column, data) in zip(&self.columns, &self.data) {
|
||||
assert!(Rc::ptr_eq(&self.options, &column.options));
|
||||
assert_eq!(self.clock, column.clock);
|
||||
assert_eq!(self.scale.fractional_scale(), column.scale);
|
||||
column.verify_invariants();
|
||||
|
||||
@@ -2619,7 +2641,6 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
self.view_size,
|
||||
self.working_area,
|
||||
self.scale.fractional_scale(),
|
||||
self.options.clone(),
|
||||
removed.width,
|
||||
removed.is_full_width,
|
||||
false,
|
||||
@@ -2969,6 +2990,7 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
let target_view_offset = target_snap.view_pos - new_col_x;
|
||||
|
||||
self.view_offset_adj = Some(ViewOffsetAdjustment::Animation(Animation::new(
|
||||
self.clock.now(),
|
||||
current_view_offset + delta,
|
||||
target_view_offset,
|
||||
velocity,
|
||||
@@ -3174,7 +3196,6 @@ impl<W: LayoutElement> Column<W> {
|
||||
view_size: Size<f64, Logical>,
|
||||
working_area: Rectangle<f64, Logical>,
|
||||
scale: f64,
|
||||
options: Rc<Options>,
|
||||
width: ColumnWidth,
|
||||
is_full_width: bool,
|
||||
animate_resize: bool,
|
||||
@@ -3190,7 +3211,8 @@ impl<W: LayoutElement> Column<W> {
|
||||
view_size,
|
||||
working_area,
|
||||
scale,
|
||||
options,
|
||||
clock: tile.clock.clone(),
|
||||
options: tile.options.clone(),
|
||||
};
|
||||
|
||||
let is_pending_fullscreen = tile.window().is_pending_fullscreen();
|
||||
@@ -3313,6 +3335,7 @@ impl<W: LayoutElement> Column<W> {
|
||||
let current_offset = self.move_animation.as_ref().map_or(0., Animation::value);
|
||||
|
||||
self.move_animation = Some(Animation::new(
|
||||
self.clock.now(),
|
||||
from_x_offset + current_offset,
|
||||
0.,
|
||||
0.,
|
||||
@@ -3716,6 +3739,7 @@ impl<W: LayoutElement> Column<W> {
|
||||
let mut total_min_height = 0.;
|
||||
for (tile, data) in zip(&self.tiles, &self.data) {
|
||||
assert!(Rc::ptr_eq(&self.options, &tile.options));
|
||||
assert_eq!(self.clock, tile.clock);
|
||||
assert_eq!(self.scale, tile.scale());
|
||||
assert_eq!(self.is_fullscreen, tile.window().is_pending_fullscreen());
|
||||
|
||||
|
15
src/niri.rs
15
src/niri.rs
@@ -100,6 +100,7 @@ use smithay::wayland::virtual_keyboard::VirtualKeyboardManagerState;
|
||||
use smithay::wayland::xdg_activation::XdgActivationState;
|
||||
use smithay::wayland::xdg_foreign::XdgForeignState;
|
||||
|
||||
use crate::animation::Clock;
|
||||
use crate::backend::tty::SurfaceDmabufFeedback;
|
||||
use crate::backend::{Backend, RenderResult, Tty, Winit};
|
||||
use crate::cursor::{CursorManager, CursorTextureCache, RenderCursor, XCursor};
|
||||
@@ -179,6 +180,9 @@ pub struct Niri {
|
||||
/// Whether the at-startup=true window rules are active.
|
||||
pub is_at_startup: bool,
|
||||
|
||||
/// Clock for driving animations.
|
||||
pub clock: Clock,
|
||||
|
||||
// Each workspace corresponds to a Space. Each workspace generally has one Output mapped to it,
|
||||
// however it may have none (when there are no outputs connected) or multiple (when mirroring).
|
||||
pub layout: Layout<Mapped>,
|
||||
@@ -1671,7 +1675,8 @@ impl Niri {
|
||||
let config_ = config.borrow();
|
||||
let config_file_output_config = config_.outputs.clone();
|
||||
|
||||
let layout = Layout::new(&config_);
|
||||
let clock = Clock::default();
|
||||
let layout = Layout::new(clock.clone(), &config_);
|
||||
|
||||
let (blocker_cleared_tx, blocker_cleared_rx) = mpsc::channel();
|
||||
|
||||
@@ -1799,8 +1804,8 @@ impl Niri {
|
||||
let mods_with_finger_scroll_binds =
|
||||
mods_with_finger_scroll_binds(backend.mod_key(), &config_.binds);
|
||||
|
||||
let screenshot_ui = ScreenshotUi::new(config.clone());
|
||||
let config_error_notification = ConfigErrorNotification::new(config.clone());
|
||||
let screenshot_ui = ScreenshotUi::new(clock.clone(), config.clone());
|
||||
let config_error_notification = ConfigErrorNotification::new(clock.clone(), config.clone());
|
||||
|
||||
let mut hotkey_overlay = HotkeyOverlay::new(config.clone(), backend.mod_key());
|
||||
if !config_.hotkey_overlay.skip_at_startup {
|
||||
@@ -1895,6 +1900,7 @@ impl Niri {
|
||||
display_handle,
|
||||
start_time: Instant::now(),
|
||||
is_at_startup: true,
|
||||
clock,
|
||||
|
||||
layout,
|
||||
global_space: Space::default(),
|
||||
@@ -3044,6 +3050,7 @@ impl Niri {
|
||||
|
||||
for state in self.output_state.values_mut() {
|
||||
if let Some(transition) = &mut state.screen_transition {
|
||||
// Screen transition uses real time so that it's not affected by animation slowdown.
|
||||
transition.advance_animations(target_time);
|
||||
if transition.is_done() {
|
||||
state.screen_transition = None;
|
||||
@@ -4819,6 +4826,8 @@ impl Niri {
|
||||
let delay = delay_ms.map_or(screen_transition::DELAY, |d| {
|
||||
Duration::from_millis(u64::from(d))
|
||||
});
|
||||
|
||||
// Screen transition uses real time so that it's not affected by animation slowdown.
|
||||
let start_at = get_monotonic_time() + delay;
|
||||
for (output, from_texture) in textures {
|
||||
let state = self.output_state.get_mut(&output).unwrap();
|
||||
|
@@ -14,7 +14,7 @@ use smithay::output::Output;
|
||||
use smithay::reexports::gbm::Format as Fourcc;
|
||||
use smithay::utils::{Point, Transform};
|
||||
|
||||
use crate::animation::Animation;
|
||||
use crate::animation::{Animation, Clock};
|
||||
use crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement;
|
||||
use crate::render_helpers::renderer::NiriRenderer;
|
||||
use crate::render_helpers::texture::{TextureBuffer, TextureRenderElement};
|
||||
@@ -35,6 +35,7 @@ pub struct ConfigErrorNotification {
|
||||
// notification.
|
||||
created_path: Option<PathBuf>,
|
||||
|
||||
clock: Clock,
|
||||
config: Rc<RefCell<Config>>,
|
||||
}
|
||||
|
||||
@@ -46,18 +47,25 @@ enum State {
|
||||
}
|
||||
|
||||
impl ConfigErrorNotification {
|
||||
pub fn new(config: Rc<RefCell<Config>>) -> Self {
|
||||
pub fn new(clock: Clock, config: Rc<RefCell<Config>>) -> Self {
|
||||
Self {
|
||||
state: State::Hidden,
|
||||
buffers: RefCell::new(HashMap::new()),
|
||||
created_path: None,
|
||||
clock,
|
||||
config,
|
||||
}
|
||||
}
|
||||
|
||||
fn animation(&self, from: f64, to: f64) -> Animation {
|
||||
let c = self.config.borrow();
|
||||
Animation::new(from, to, 0., c.animations.config_notification_open_close.0)
|
||||
Animation::new(
|
||||
self.clock.now(),
|
||||
from,
|
||||
to,
|
||||
0.,
|
||||
c.animations.config_notification_open_close.0,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn show_created(&mut self, created_path: PathBuf) {
|
||||
|
@@ -20,7 +20,7 @@ use smithay::input::keyboard::{Keysym, ModifiersState};
|
||||
use smithay::output::{Output, WeakOutput};
|
||||
use smithay::utils::{Physical, Point, Rectangle, Scale, Size, Transform};
|
||||
|
||||
use crate::animation::Animation;
|
||||
use crate::animation::{Animation, Clock};
|
||||
use crate::niri_render_elements;
|
||||
use crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement;
|
||||
use crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement};
|
||||
@@ -49,6 +49,7 @@ const TEXT_SHOW_P: &str =
|
||||
pub enum ScreenshotUi {
|
||||
Closed {
|
||||
last_selection: Option<(WeakOutput, Rectangle<i32, Physical>)>,
|
||||
clock: Clock,
|
||||
config: Rc<RefCell<Config>>,
|
||||
},
|
||||
Open {
|
||||
@@ -57,6 +58,7 @@ pub enum ScreenshotUi {
|
||||
mouse_down: bool,
|
||||
show_pointer: bool,
|
||||
open_anim: Animation,
|
||||
clock: Clock,
|
||||
config: Rc<RefCell<Config>>,
|
||||
},
|
||||
}
|
||||
@@ -86,9 +88,10 @@ niri_render_elements! {
|
||||
}
|
||||
|
||||
impl ScreenshotUi {
|
||||
pub fn new(config: Rc<RefCell<Config>>) -> Self {
|
||||
pub fn new(clock: Clock, config: Rc<RefCell<Config>>) -> Self {
|
||||
Self::Closed {
|
||||
last_selection: None,
|
||||
clock,
|
||||
config,
|
||||
}
|
||||
}
|
||||
@@ -106,6 +109,7 @@ impl ScreenshotUi {
|
||||
|
||||
let Self::Closed {
|
||||
last_selection,
|
||||
clock,
|
||||
config,
|
||||
} = self
|
||||
else {
|
||||
@@ -181,7 +185,7 @@ impl ScreenshotUi {
|
||||
|
||||
let open_anim = {
|
||||
let c = config.borrow();
|
||||
Animation::new(0., 1., 0., c.animations.screenshot_ui_open.0)
|
||||
Animation::new(clock.now(), 0., 1., 0., c.animations.screenshot_ui_open.0)
|
||||
};
|
||||
|
||||
*self = Self::Open {
|
||||
@@ -190,6 +194,7 @@ impl ScreenshotUi {
|
||||
mouse_down: false,
|
||||
show_pointer: true,
|
||||
open_anim,
|
||||
clock: clock.clone(),
|
||||
config: config.clone(),
|
||||
};
|
||||
|
||||
@@ -200,7 +205,10 @@ impl ScreenshotUi {
|
||||
|
||||
pub fn close(&mut self) -> bool {
|
||||
let Self::Open {
|
||||
selection, config, ..
|
||||
selection,
|
||||
clock,
|
||||
config,
|
||||
..
|
||||
} = self
|
||||
else {
|
||||
return false;
|
||||
@@ -213,6 +221,7 @@ impl ScreenshotUi {
|
||||
|
||||
*self = Self::Closed {
|
||||
last_selection,
|
||||
clock: clock.clone(),
|
||||
config: config.clone(),
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user