screenshot-ui: Animate opening

This commit is contained in:
Ivan Molodetskikh 2024-07-08 11:24:08 +04:00
parent 092cf6cfaf
commit 4513663084
5 changed files with 111 additions and 22 deletions

View File

@ -571,6 +571,8 @@ pub struct Animations {
pub window_resize: WindowResizeAnim,
#[knuffel(child, default)]
pub config_notification_open_close: ConfigNotificationOpenCloseAnim,
#[knuffel(child, default)]
pub screenshot_ui_open: ScreenshotUiOpenAnim,
}
impl Default for Animations {
@ -585,6 +587,7 @@ impl Default for Animations {
window_close: Default::default(),
window_resize: Default::default(),
config_notification_open_close: Default::default(),
screenshot_ui_open: Default::default(),
}
}
}
@ -717,6 +720,21 @@ impl Default for ConfigNotificationOpenCloseAnim {
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ScreenshotUiOpenAnim(pub Animation);
impl Default for ScreenshotUiOpenAnim {
fn default() -> Self {
Self(Animation {
off: false,
kind: AnimationKind::Easing(EasingParams {
duration_ms: 200,
curve: AnimationCurve::EaseOutQuad,
}),
})
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Animation {
pub off: bool,
@ -1802,6 +1820,21 @@ where
}
}
impl<S> knuffel::Decode<S> for ScreenshotUiOpenAnim
where
S: knuffel::traits::ErrorSpan,
{
fn decode_node(
node: &knuffel::ast::SpannedNode<S>,
ctx: &mut knuffel::decode::Context<S>,
) -> Result<Self, DecodeError<S>> {
let default = Self::default().0;
Ok(Self(Animation::decode_node(node, ctx, default, |_, _| {
Ok(false)
})?))
}
}
impl Animation {
pub fn new_off() -> Self {
Self {

View File

@ -2569,7 +2569,7 @@ mod tests {
let comp_mod = CompositorMod::Super;
let mut suppressed_keys = HashSet::new();
let screenshot_ui = ScreenshotUi::new();
let screenshot_ui = ScreenshotUi::new(Default::default());
let disable_power_key_handling = false;
// The key_code we pick is arbitrary, the only thing

View File

@ -1589,7 +1589,7 @@ impl Niri {
let mods_with_finger_scroll_binds =
mods_with_finger_scroll_binds(backend.mod_key(), &config_.binds);
let screenshot_ui = ScreenshotUi::new();
let screenshot_ui = ScreenshotUi::new(config.clone());
let config_error_notification = ConfigErrorNotification::new(config.clone());
let mut hotkey_overlay = HotkeyOverlay::new(config.clone(), backend.mod_key());
@ -2973,6 +2973,10 @@ impl Niri {
state.unfinished_animations_remain |=
self.config_error_notification.are_animations_ongoing();
self.screenshot_ui
.advance_animations(target_presentation_time);
state.unfinished_animations_remain |= self.screenshot_ui.are_animations_ongoing();
// Also keep redrawing if the current cursor is animated.
state.unfinished_animations_remain |= self
.cursor_manager

View File

@ -1,11 +1,13 @@
use std::cell::RefCell;
use std::cmp::{max, min};
use std::collections::HashMap;
use std::iter::zip;
use std::mem;
use std::rc::Rc;
use std::time::Duration;
use anyhow::Context;
use arrayvec::ArrayVec;
use niri_config::Action;
use niri_config::{Action, Config};
use pango::{Alignment, FontDescription};
use pangocairo::cairo::{self, ImageSurface};
use smithay::backend::allocator::Fourcc;
@ -18,6 +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::niri_render_elements;
use crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement;
use crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement};
@ -42,15 +45,19 @@ const TEXT_SHOW_P: &str =
// allows only single-output selections for now.
//
// As a consequence of this, selection coordinates are in output-local coordinate space.
#[allow(clippy::large_enum_variant)]
pub enum ScreenshotUi {
Closed {
last_selection: Option<(WeakOutput, Rectangle<i32, Physical>)>,
config: Rc<RefCell<Config>>,
},
Open {
selection: (Output, Point<i32, Physical>, Point<i32, Physical>),
output_data: HashMap<Output, OutputData>,
mouse_down: bool,
show_pointer: bool,
open_anim: Animation,
config: Rc<RefCell<Config>>,
},
}
@ -79,9 +86,10 @@ niri_render_elements! {
}
impl ScreenshotUi {
pub fn new() -> Self {
pub fn new(config: Rc<RefCell<Config>>) -> Self {
Self::Closed {
last_selection: None,
config,
}
}
@ -96,7 +104,11 @@ impl ScreenshotUi {
return false;
}
let Self::Closed { last_selection } = self else {
let Self::Closed {
last_selection,
config,
} = self
else {
return false;
};
@ -167,11 +179,18 @@ impl ScreenshotUi {
})
.collect();
let open_anim = {
let c = config.borrow();
Animation::new(0., 1., 0., c.animations.screenshot_ui_open.0)
};
*self = Self::Open {
selection,
output_data,
mouse_down: false,
show_pointer: true,
open_anim,
config: config.clone(),
};
self.update_buffers();
@ -180,13 +199,11 @@ impl ScreenshotUi {
}
pub fn close(&mut self) -> bool {
let selection = match mem::take(self) {
Self::Open { selection, .. } => selection,
closed @ Self::Closed { .. } => {
// Put it back.
*self = closed;
return false;
}
let Self::Open {
selection, config, ..
} = self
else {
return false;
};
let last_selection = Some((
@ -194,7 +211,10 @@ impl ScreenshotUi {
rect_from_corner_points(selection.1, selection.2),
));
*self = Self::Closed { last_selection };
*self = Self::Closed {
last_selection,
config: config.clone(),
};
true
}
@ -209,6 +229,22 @@ impl ScreenshotUi {
matches!(self, ScreenshotUi::Open { .. })
}
pub fn advance_animations(&mut self, current_time: Duration) {
let Self::Open { open_anim, .. } = self else {
return;
};
open_anim.set_current_time(current_time);
}
pub fn are_animations_ongoing(&self) -> bool {
let Self::Open { open_anim, .. } = self else {
return false;
};
!open_anim.is_done()
}
fn update_buffers(&mut self) {
let Self::Open {
selection,
@ -293,6 +329,7 @@ impl ScreenshotUi {
output_data,
show_pointer,
mouse_down,
open_anim,
..
} = self
else {
@ -306,6 +343,7 @@ impl ScreenshotUi {
};
let scale = output_data.scale;
let progress = open_anim.clamped_value().clamp(0., 1.) as f32;
// The help panel goes on top.
if let Some((show, hide)) = &output_data.panel {
@ -324,7 +362,7 @@ impl ScreenshotUi {
let elem = PrimaryGpuTextureRenderElement(TextureRenderElement::from_texture_buffer(
buffer.clone(),
location,
alpha,
alpha * progress,
None,
None,
Kind::Unspecified,
@ -337,7 +375,7 @@ impl ScreenshotUi {
SolidColorRenderElement::from_buffer(
buffer,
loc.to_f64().to_logical(scale),
1.,
progress,
Kind::Unspecified,
)
.into()
@ -530,12 +568,6 @@ impl ScreenshotUi {
}
}
impl Default for ScreenshotUi {
fn default() -> Self {
Self::new()
}
}
impl OutputScreenshot {
pub fn from_textures(
renderer: &mut GlesRenderer,

View File

@ -45,6 +45,11 @@ animations {
config-notification-open-close {
spring damping-ratio=0.6 stiffness=1000 epsilon=0.001
}
screenshot-ui-open {
duration-ms 200
curve "ease-out-quad"
}
}
```
@ -354,6 +359,21 @@ animations {
}
```
#### `screenshot-ui-open`
<sup>Since: 0.1.8</sup>
The open (fade-in) animation of the screenshot UI.
```
animations {
screenshot-ui-open {
duration-ms 200
curve "ease-out-quad"
}
}
```
### Synchronized Animations
<sup>Since: 0.1.5</sup>