diff options
| author | Ivan Molodetskikh <yalterz@gmail.com> | 2025-08-22 08:50:52 +0300 |
|---|---|---|
| committer | Ivan Molodetskikh <yalterz@gmail.com> | 2025-08-22 08:57:08 +0300 |
| commit | 210d5e90fe00ae9add5d841e1752b7f8c4a639a7 (patch) | |
| tree | ef1431ecb1744984105e7b30520581cb5a1c5219 /src | |
| parent | 9d3beb49315ae4cde3d8edfb979fccda52af3fae (diff) | |
| download | niri-210d5e90fe00ae9add5d841e1752b7f8c4a639a7.tar.gz niri-210d5e90fe00ae9add5d841e1752b7f8c4a639a7.tar.bz2 niri-210d5e90fe00ae9add5d841e1752b7f8c4a639a7.zip | |
exit_confirm_dialog: Add open/close animation
Diffstat (limited to 'src')
| -rw-r--r-- | src/niri.rs | 4 | ||||
| -rw-r--r-- | src/ui/exit_confirm_dialog.rs | 110 |
2 files changed, 93 insertions, 21 deletions
diff --git a/src/niri.rs b/src/niri.rs index b4b4a665..c97153f1 100644 --- a/src/niri.rs +++ b/src/niri.rs @@ -2460,7 +2460,7 @@ impl Niri { hotkey_overlay.show(); } - let exit_confirm_dialog = ExitConfirmDialog::new(); + let exit_confirm_dialog = ExitConfirmDialog::new(animation_clock.clone(), config.clone()); event_loop .insert_source( @@ -4050,6 +4050,7 @@ impl Niri { self.layout.advance_animations(); self.config_error_notification.advance_animations(); + self.exit_confirm_dialog.advance_animations(); self.screenshot_ui.advance_animations(); for state in self.output_state.values_mut() { @@ -4400,6 +4401,7 @@ impl Niri { state.unfinished_animations_remain = self.layout.are_animations_ongoing(Some(output)); state.unfinished_animations_remain |= self.config_error_notification.are_animations_ongoing(); + state.unfinished_animations_remain |= self.exit_confirm_dialog.are_animations_ongoing(); state.unfinished_animations_remain |= self.screenshot_ui.are_animations_ongoing(); state.unfinished_animations_remain |= state.screen_transition.is_some(); diff --git a/src/ui/exit_confirm_dialog.rs b/src/ui/exit_confirm_dialog.rs index 9162118b..e0cc9398 100644 --- a/src/ui/exit_confirm_dialog.rs +++ b/src/ui/exit_confirm_dialog.rs @@ -1,16 +1,20 @@ use std::cell::RefCell; use std::collections::HashMap; +use std::rc::Rc; use std::sync::Mutex; use arrayvec::ArrayVec; +use niri_config::Config; use ordered_float::NotNan; use pangocairo::cairo::{self, ImageSurface}; use pangocairo::pango::{Alignment, FontDescription}; +use smithay::backend::renderer::element::utils::RescaleRenderElement; use smithay::backend::renderer::element::Kind; use smithay::output::Output; use smithay::reexports::gbm::Format as Fourcc; use smithay::utils::{Point, Transform}; +use crate::animation::{Animation, Clock}; use crate::niri_render_elements; use crate::render_helpers::memory::MemoryBuffer; use crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement; @@ -27,13 +31,16 @@ const BORDER: i32 = 8; const BACKDROP_COLOR: [f32; 4] = [0., 0., 0., 0.4]; pub struct ExitConfirmDialog { - is_open: bool, + state: State, buffers: RefCell<HashMap<NotNan<f64>, Option<MemoryBuffer>>>, + + clock: Clock, + config: Rc<RefCell<Config>>, } niri_render_elements! { ExitConfirmDialogRenderElement => { - Texture = PrimaryGpuTextureRenderElement, + Texture = RescaleRenderElement<PrimaryGpuTextureRenderElement>, SolidColor = SolidColorRenderElement, } } @@ -42,8 +49,15 @@ struct OutputData { backdrop: SolidColorBuffer, } +enum State { + Hidden, + Showing(Animation), + Visible, + Hiding(Animation), +} + impl ExitConfirmDialog { - pub fn new() -> Self { + pub fn new(clock: Clock, config: Rc<RefCell<Config>>) -> Self { let buffer = match render(1.) { Ok(x) => Some(x), Err(err) => { @@ -53,8 +67,10 @@ impl ExitConfirmDialog { }; Self { - is_open: false, + state: State::Hidden, buffers: RefCell::new(HashMap::from([(NotNan::new(1.).unwrap(), buffer)])), + clock, + config, } } @@ -64,26 +80,72 @@ impl ExitConfirmDialog { fallback.is_some() } + fn animation(&self, from: f64, to: f64) -> Animation { + let c = self.config.borrow(); + Animation::new( + self.clock.clone(), + from, + to, + 0., + c.animations.exit_confirmation_open_close.0, + ) + } + + fn value(&self) -> f64 { + match &self.state { + State::Hidden => 0., + State::Showing(anim) | State::Hiding(anim) => anim.value(), + State::Visible => 1., + } + } + + /// Returns true if the dialog will be shown (even if it is already shown). pub fn show(&mut self) -> bool { if !self.can_show() { return false; } - self.is_open = true; + if self.is_open() { + return true; + } + + self.state = State::Showing(self.animation(self.value(), 1.)); true } + /// Returns true if started the hide animation. pub fn hide(&mut self) -> bool { - if self.is_open { - self.is_open = false; - true - } else { - false + if !self.is_open() { + return false; } + + self.state = State::Hiding(self.animation(self.value(), 0.)); + true } pub fn is_open(&self) -> bool { - self.is_open + matches!(self.state, State::Showing(_) | State::Visible) + } + + pub fn advance_animations(&mut self) { + match &mut self.state { + State::Hidden => (), + State::Showing(anim) => { + if anim.is_done() { + self.state = State::Visible; + } + } + State::Visible => (), + State::Hiding(anim) => { + if anim.is_clamped_done() { + self.state = State::Hidden; + } + } + } + } + + pub fn are_animations_ongoing(&self) -> bool { + matches!(self.state, State::Showing(_) | State::Hiding(_)) } pub fn render<R: NiriRenderer>( @@ -93,9 +155,13 @@ impl ExitConfirmDialog { ) -> ArrayVec<ExitConfirmDialogRenderElement, 2> { let mut rv = ArrayVec::new(); - if !self.is_open { - return rv; - } + let (value, clamped_value) = match &self.state { + State::Hidden => return rv, + State::Showing(anim) | State::Hiding(anim) => (anim.value(), anim.clamped_value()), + State::Visible => (1., 1.), + }; + // Can be out of range when starting from past 0. or 1. from a spring bounce. + let clamped_value = clamped_value.clamp(0., 1.); let scale = output.current_scale().fractional_scale(); let output_size = output_size(output); @@ -117,7 +183,7 @@ impl ExitConfirmDialog { return rv; }; - let location = (output_size.to_f64().to_point() - size.to_point()).downscale(2.); + let location = (output_size.to_point() - size.to_point()).downscale(2.); let mut location = location.to_physical_precise_round(scale).to_logical(scale); location.x = f64::max(0., location.x); location.y = f64::max(0., location.y); @@ -125,14 +191,18 @@ impl ExitConfirmDialog { let elem = TextureRenderElement::from_texture_buffer( buffer, location, - 1., + clamped_value as f32, None, None, Kind::Unspecified, ); - rv.push(ExitConfirmDialogRenderElement::Texture( - PrimaryGpuTextureRenderElement(elem), - )); + let elem = PrimaryGpuTextureRenderElement(elem); + let elem = RescaleRenderElement::from_element( + elem, + (location + size.downscale(2.)).to_physical_precise_round(scale), + value.max(0.) * 0.2 + 0.8, + ); + rv.push(ExitConfirmDialogRenderElement::Texture(elem)); // Backdrop. let data = output.user_data().get_or_insert(|| { @@ -146,7 +216,7 @@ impl ExitConfirmDialog { let elem = SolidColorRenderElement::from_buffer( &data.backdrop, Point::new(0., 0.), - 1., + clamped_value as f32, Kind::Unspecified, ); rv.push(ExitConfirmDialogRenderElement::SolidColor(elem)); |
