mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-23 02:05:33 +07:00
Implement window open animations
This commit is contained in:
@@ -0,0 +1,228 @@
|
||||
use std::collections::HashMap;
|
||||
use std::time::Duration;
|
||||
|
||||
use niri::layout::workspace::ColumnWidth;
|
||||
use niri::layout::Options;
|
||||
use niri::utils::get_monotonic_time;
|
||||
use niri_config::Color;
|
||||
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 super::TestCase;
|
||||
use crate::test_window::TestWindow;
|
||||
|
||||
type DynStepFn = Box<dyn FnOnce(&mut Layout)>;
|
||||
|
||||
pub struct Layout {
|
||||
output: Output,
|
||||
windows: Vec<TestWindow>,
|
||||
layout: niri::layout::Layout<TestWindow>,
|
||||
start_time: Duration,
|
||||
steps: HashMap<Duration, DynStepFn>,
|
||||
}
|
||||
|
||||
impl Layout {
|
||||
pub fn new(size: Size<i32, Logical>) -> Self {
|
||||
let output = Output::new(
|
||||
String::new(),
|
||||
PhysicalProperties {
|
||||
size: Size::from((size.w, size.h)),
|
||||
subpixel: Subpixel::Unknown,
|
||||
make: String::new(),
|
||||
model: String::new(),
|
||||
},
|
||||
);
|
||||
let mode = Some(Mode {
|
||||
size: size.to_physical(1),
|
||||
refresh: 60000,
|
||||
});
|
||||
output.change_current_state(mode, None, None, None);
|
||||
|
||||
let options = Options {
|
||||
focus_ring: niri_config::FocusRing {
|
||||
off: true,
|
||||
..Default::default()
|
||||
},
|
||||
border: niri_config::FocusRing {
|
||||
off: false,
|
||||
width: 4,
|
||||
active_color: Color::new(255, 163, 72, 255),
|
||||
inactive_color: Color::new(50, 50, 50, 255),
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
let mut layout = niri::layout::Layout::with_options(options);
|
||||
layout.add_output(output.clone());
|
||||
|
||||
Self {
|
||||
output,
|
||||
windows: Vec::new(),
|
||||
layout,
|
||||
start_time: get_monotonic_time(),
|
||||
steps: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_in_between(size: Size<i32, Logical>) -> Self {
|
||||
let mut rv = Self::new(size);
|
||||
|
||||
rv.add_window(TestWindow::freeform(0), Some(ColumnWidth::Proportion(0.3)));
|
||||
rv.add_window(TestWindow::freeform(1), Some(ColumnWidth::Proportion(0.3)));
|
||||
rv.layout.activate_window(&rv.windows[0]);
|
||||
|
||||
rv.add_step(500, |l| {
|
||||
let win = TestWindow::freeform(2);
|
||||
l.add_window(win.clone(), Some(ColumnWidth::Proportion(0.3)));
|
||||
l.layout.start_open_animation_for_window(&win);
|
||||
});
|
||||
|
||||
rv
|
||||
}
|
||||
|
||||
pub fn open_multiple_quickly(size: Size<i32, Logical>) -> Self {
|
||||
let mut rv = Self::new(size);
|
||||
|
||||
for delay in [100, 200, 300] {
|
||||
rv.add_step(delay, move |l| {
|
||||
let win = TestWindow::freeform(delay as usize);
|
||||
l.add_window(win.clone(), Some(ColumnWidth::Proportion(0.3)));
|
||||
l.layout.start_open_animation_for_window(&win);
|
||||
});
|
||||
}
|
||||
|
||||
rv
|
||||
}
|
||||
|
||||
pub fn open_multiple_quickly_big(size: Size<i32, Logical>) -> Self {
|
||||
let mut rv = Self::new(size);
|
||||
|
||||
for delay in [100, 200, 300] {
|
||||
rv.add_step(delay, move |l| {
|
||||
let win = TestWindow::freeform(delay as usize);
|
||||
l.add_window(win.clone(), Some(ColumnWidth::Proportion(0.5)));
|
||||
l.layout.start_open_animation_for_window(&win);
|
||||
});
|
||||
}
|
||||
|
||||
rv
|
||||
}
|
||||
|
||||
pub fn open_to_the_left(size: Size<i32, Logical>) -> Self {
|
||||
let mut rv = Self::new(size);
|
||||
|
||||
rv.add_window(TestWindow::freeform(0), Some(ColumnWidth::Proportion(0.3)));
|
||||
rv.add_window(TestWindow::freeform(1), Some(ColumnWidth::Proportion(0.3)));
|
||||
|
||||
rv.add_step(500, |l| {
|
||||
let win = TestWindow::freeform(2);
|
||||
let right_of = l.windows[0].clone();
|
||||
l.add_window_right_of(&right_of, win.clone(), Some(ColumnWidth::Proportion(0.3)));
|
||||
l.layout.start_open_animation_for_window(&win);
|
||||
});
|
||||
|
||||
rv
|
||||
}
|
||||
|
||||
pub fn open_to_the_left_big(size: Size<i32, Logical>) -> Self {
|
||||
let mut rv = Self::new(size);
|
||||
|
||||
rv.add_window(TestWindow::freeform(0), Some(ColumnWidth::Proportion(0.3)));
|
||||
rv.add_window(TestWindow::freeform(1), Some(ColumnWidth::Proportion(0.8)));
|
||||
|
||||
rv.add_step(500, |l| {
|
||||
let win = TestWindow::freeform(2);
|
||||
let right_of = l.windows[0].clone();
|
||||
l.add_window_right_of(&right_of, win.clone(), Some(ColumnWidth::Proportion(0.5)));
|
||||
l.layout.start_open_animation_for_window(&win);
|
||||
});
|
||||
|
||||
rv
|
||||
}
|
||||
|
||||
fn add_window(&mut self, window: TestWindow, width: Option<ColumnWidth>) {
|
||||
self.layout.add_window(window.clone(), width, false);
|
||||
if window.communicate() {
|
||||
self.layout.update_window(&window);
|
||||
}
|
||||
self.windows.push(window);
|
||||
}
|
||||
|
||||
fn add_window_right_of(
|
||||
&mut self,
|
||||
right_of: &TestWindow,
|
||||
window: TestWindow,
|
||||
width: Option<ColumnWidth>,
|
||||
) {
|
||||
self.layout
|
||||
.add_window_right_of(right_of, window.clone(), width, false);
|
||||
if window.communicate() {
|
||||
self.layout.update_window(&window);
|
||||
}
|
||||
self.windows.push(window);
|
||||
}
|
||||
|
||||
fn add_step(&mut self, delay_ms: u64, f: impl FnOnce(&mut Self) + 'static) {
|
||||
self.steps
|
||||
.insert(Duration::from_millis(delay_ms), Box::new(f) as _);
|
||||
}
|
||||
}
|
||||
|
||||
impl TestCase for Layout {
|
||||
fn resize(&mut self, width: i32, height: i32) {
|
||||
let mode = Some(Mode {
|
||||
size: Size::from((width, height)),
|
||||
refresh: 60000,
|
||||
});
|
||||
self.output.change_current_state(mode, None, None, None);
|
||||
layer_map_for_output(&self.output).arrange();
|
||||
self.layout.update_output_size(&self.output);
|
||||
for win in &self.windows {
|
||||
if win.communicate() {
|
||||
self.layout.update_window(win);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn are_animations_ongoing(&self) -> bool {
|
||||
self.layout
|
||||
.monitor_for_output(&self.output)
|
||||
.unwrap()
|
||||
.are_animations_ongoing()
|
||||
|| !self.steps.is_empty()
|
||||
}
|
||||
|
||||
fn advance_animations(&mut self, mut 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();
|
||||
f(self);
|
||||
}
|
||||
if !run.is_empty() {
|
||||
current_time = get_monotonic_time();
|
||||
}
|
||||
|
||||
self.layout.advance_animations(current_time);
|
||||
}
|
||||
|
||||
fn render(
|
||||
&mut self,
|
||||
renderer: &mut GlesRenderer,
|
||||
_size: Size<i32, Physical>,
|
||||
) -> Vec<Box<dyn RenderElement<GlesRenderer>>> {
|
||||
self.layout
|
||||
.monitor_for_output(&self.output)
|
||||
.unwrap()
|
||||
.render_elements(renderer)
|
||||
.into_iter()
|
||||
.map(|elem| Box::new(elem) as _)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ use smithay::backend::renderer::element::RenderElement;
|
||||
use smithay::backend::renderer::gles::GlesRenderer;
|
||||
use smithay::utils::{Physical, Size};
|
||||
|
||||
pub mod layout;
|
||||
pub mod tile;
|
||||
pub mod window;
|
||||
|
||||
|
||||
@@ -41,6 +41,27 @@ impl Tile {
|
||||
rv
|
||||
}
|
||||
|
||||
pub fn freeform_open(size: Size<i32, Logical>) -> Self {
|
||||
let mut rv = Self::freeform(size);
|
||||
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);
|
||||
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);
|
||||
rv.window.set_color([0.1, 0.1, 0.1, 1.]);
|
||||
rv.tile.start_open_animation();
|
||||
rv
|
||||
}
|
||||
|
||||
pub fn with_window(window: TestWindow) -> Self {
|
||||
let options = Options {
|
||||
focus_ring: niri_config::FocusRing {
|
||||
@@ -66,6 +87,10 @@ impl TestCase for Tile {
|
||||
self.window.communicate();
|
||||
}
|
||||
|
||||
fn are_animations_ongoing(&self) -> bool {
|
||||
self.tile.are_animations_ongoing()
|
||||
}
|
||||
|
||||
fn advance_animations(&mut self, current_time: Duration) {
|
||||
self.tile.advance_animations(current_time, true);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ use smithay::utils::{Logical, Size};
|
||||
use smithay_view::SmithayView;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
use crate::cases::layout::Layout;
|
||||
use crate::cases::TestCase;
|
||||
|
||||
mod cases;
|
||||
@@ -85,6 +86,27 @@ fn build_ui(app: &adw::Application) {
|
||||
Tile::fixed_size_with_csd_shadow,
|
||||
"Fixed Size Tile - CSD Shadow",
|
||||
);
|
||||
s.add(Tile::freeform_open, "Freeform Tile - Open");
|
||||
s.add(Tile::fixed_size_open, "Fixed Size Tile - Open");
|
||||
s.add(
|
||||
Tile::fixed_size_with_csd_shadow_open,
|
||||
"Fixed Size Tile - CSD Shadow - Open",
|
||||
);
|
||||
|
||||
s.add(Layout::open_in_between, "Layout - Open In-Between");
|
||||
s.add(
|
||||
Layout::open_multiple_quickly,
|
||||
"Layout - Open Multiple Quickly",
|
||||
);
|
||||
s.add(
|
||||
Layout::open_multiple_quickly_big,
|
||||
"Layout - Open Multiple Quickly - Big",
|
||||
);
|
||||
s.add(Layout::open_to_the_left, "Layout - Open To The Left");
|
||||
s.add(
|
||||
Layout::open_to_the_left_big,
|
||||
"Layout - Open To The Left - Big",
|
||||
);
|
||||
|
||||
let content_headerbar = adw::HeaderBar::new();
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::rc::Rc;
|
||||
use niri::layout::{LayoutElement, LayoutElementRenderElement};
|
||||
use niri::render_helpers::renderer::NiriRenderer;
|
||||
use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement};
|
||||
use smithay::backend::renderer::element::Kind;
|
||||
use smithay::backend::renderer::element::{Id, Kind};
|
||||
use smithay::output::Output;
|
||||
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
|
||||
use smithay::utils::{Logical, Point, Scale, Size, Transform};
|
||||
@@ -196,6 +196,8 @@ impl LayoutElement for TestWindow {
|
||||
|
||||
fn output_leave(&self, _output: &Output) {}
|
||||
|
||||
fn set_offscreen_element_id(&self, _id: Option<Id>) {}
|
||||
|
||||
fn is_fullscreen(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user