mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-24 02:01:18 +07:00
screenshot-ui: Animate opening
This commit is contained in:
@@ -571,6 +571,8 @@ pub struct Animations {
|
|||||||
pub window_resize: WindowResizeAnim,
|
pub window_resize: WindowResizeAnim,
|
||||||
#[knuffel(child, default)]
|
#[knuffel(child, default)]
|
||||||
pub config_notification_open_close: ConfigNotificationOpenCloseAnim,
|
pub config_notification_open_close: ConfigNotificationOpenCloseAnim,
|
||||||
|
#[knuffel(child, default)]
|
||||||
|
pub screenshot_ui_open: ScreenshotUiOpenAnim,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Animations {
|
impl Default for Animations {
|
||||||
@@ -585,6 +587,7 @@ impl Default for Animations {
|
|||||||
window_close: Default::default(),
|
window_close: Default::default(),
|
||||||
window_resize: Default::default(),
|
window_resize: Default::default(),
|
||||||
config_notification_open_close: 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)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub struct Animation {
|
pub struct Animation {
|
||||||
pub off: bool,
|
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 {
|
impl Animation {
|
||||||
pub fn new_off() -> Self {
|
pub fn new_off() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|||||||
+1
-1
@@ -2569,7 +2569,7 @@ mod tests {
|
|||||||
let comp_mod = CompositorMod::Super;
|
let comp_mod = CompositorMod::Super;
|
||||||
let mut suppressed_keys = HashSet::new();
|
let mut suppressed_keys = HashSet::new();
|
||||||
|
|
||||||
let screenshot_ui = ScreenshotUi::new();
|
let screenshot_ui = ScreenshotUi::new(Default::default());
|
||||||
let disable_power_key_handling = false;
|
let disable_power_key_handling = false;
|
||||||
|
|
||||||
// The key_code we pick is arbitrary, the only thing
|
// The key_code we pick is arbitrary, the only thing
|
||||||
|
|||||||
+5
-1
@@ -1589,7 +1589,7 @@ impl Niri {
|
|||||||
let mods_with_finger_scroll_binds =
|
let mods_with_finger_scroll_binds =
|
||||||
mods_with_finger_scroll_binds(backend.mod_key(), &config_.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 config_error_notification = ConfigErrorNotification::new(config.clone());
|
||||||
|
|
||||||
let mut hotkey_overlay = HotkeyOverlay::new(config.clone(), backend.mod_key());
|
let mut hotkey_overlay = HotkeyOverlay::new(config.clone(), backend.mod_key());
|
||||||
@@ -2973,6 +2973,10 @@ impl Niri {
|
|||||||
state.unfinished_animations_remain |=
|
state.unfinished_animations_remain |=
|
||||||
self.config_error_notification.are_animations_ongoing();
|
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.
|
// Also keep redrawing if the current cursor is animated.
|
||||||
state.unfinished_animations_remain |= self
|
state.unfinished_animations_remain |= self
|
||||||
.cursor_manager
|
.cursor_manager
|
||||||
|
|||||||
+52
-20
@@ -1,11 +1,13 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
use std::cmp::{max, min};
|
use std::cmp::{max, min};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::iter::zip;
|
use std::iter::zip;
|
||||||
use std::mem;
|
use std::rc::Rc;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use arrayvec::ArrayVec;
|
use arrayvec::ArrayVec;
|
||||||
use niri_config::Action;
|
use niri_config::{Action, Config};
|
||||||
use pango::{Alignment, FontDescription};
|
use pango::{Alignment, FontDescription};
|
||||||
use pangocairo::cairo::{self, ImageSurface};
|
use pangocairo::cairo::{self, ImageSurface};
|
||||||
use smithay::backend::allocator::Fourcc;
|
use smithay::backend::allocator::Fourcc;
|
||||||
@@ -18,6 +20,7 @@ use smithay::input::keyboard::{Keysym, ModifiersState};
|
|||||||
use smithay::output::{Output, WeakOutput};
|
use smithay::output::{Output, WeakOutput};
|
||||||
use smithay::utils::{Physical, Point, Rectangle, Scale, Size, Transform};
|
use smithay::utils::{Physical, Point, Rectangle, Scale, Size, Transform};
|
||||||
|
|
||||||
|
use crate::animation::Animation;
|
||||||
use crate::niri_render_elements;
|
use crate::niri_render_elements;
|
||||||
use crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement;
|
use crate::render_helpers::primary_gpu_texture::PrimaryGpuTextureRenderElement;
|
||||||
use crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement};
|
use crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderElement};
|
||||||
@@ -42,15 +45,19 @@ const TEXT_SHOW_P: &str =
|
|||||||
// allows only single-output selections for now.
|
// allows only single-output selections for now.
|
||||||
//
|
//
|
||||||
// As a consequence of this, selection coordinates are in output-local coordinate space.
|
// As a consequence of this, selection coordinates are in output-local coordinate space.
|
||||||
|
#[allow(clippy::large_enum_variant)]
|
||||||
pub enum ScreenshotUi {
|
pub enum ScreenshotUi {
|
||||||
Closed {
|
Closed {
|
||||||
last_selection: Option<(WeakOutput, Rectangle<i32, Physical>)>,
|
last_selection: Option<(WeakOutput, Rectangle<i32, Physical>)>,
|
||||||
|
config: Rc<RefCell<Config>>,
|
||||||
},
|
},
|
||||||
Open {
|
Open {
|
||||||
selection: (Output, Point<i32, Physical>, Point<i32, Physical>),
|
selection: (Output, Point<i32, Physical>, Point<i32, Physical>),
|
||||||
output_data: HashMap<Output, OutputData>,
|
output_data: HashMap<Output, OutputData>,
|
||||||
mouse_down: bool,
|
mouse_down: bool,
|
||||||
show_pointer: bool,
|
show_pointer: bool,
|
||||||
|
open_anim: Animation,
|
||||||
|
config: Rc<RefCell<Config>>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,9 +86,10 @@ niri_render_elements! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ScreenshotUi {
|
impl ScreenshotUi {
|
||||||
pub fn new() -> Self {
|
pub fn new(config: Rc<RefCell<Config>>) -> Self {
|
||||||
Self::Closed {
|
Self::Closed {
|
||||||
last_selection: None,
|
last_selection: None,
|
||||||
|
config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +104,11 @@ impl ScreenshotUi {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let Self::Closed { last_selection } = self else {
|
let Self::Closed {
|
||||||
|
last_selection,
|
||||||
|
config,
|
||||||
|
} = self
|
||||||
|
else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -167,11 +179,18 @@ impl ScreenshotUi {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
let open_anim = {
|
||||||
|
let c = config.borrow();
|
||||||
|
Animation::new(0., 1., 0., c.animations.screenshot_ui_open.0)
|
||||||
|
};
|
||||||
|
|
||||||
*self = Self::Open {
|
*self = Self::Open {
|
||||||
selection,
|
selection,
|
||||||
output_data,
|
output_data,
|
||||||
mouse_down: false,
|
mouse_down: false,
|
||||||
show_pointer: true,
|
show_pointer: true,
|
||||||
|
open_anim,
|
||||||
|
config: config.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.update_buffers();
|
self.update_buffers();
|
||||||
@@ -180,13 +199,11 @@ impl ScreenshotUi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn close(&mut self) -> bool {
|
pub fn close(&mut self) -> bool {
|
||||||
let selection = match mem::take(self) {
|
let Self::Open {
|
||||||
Self::Open { selection, .. } => selection,
|
selection, config, ..
|
||||||
closed @ Self::Closed { .. } => {
|
} = self
|
||||||
// Put it back.
|
else {
|
||||||
*self = closed;
|
return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let last_selection = Some((
|
let last_selection = Some((
|
||||||
@@ -194,7 +211,10 @@ impl ScreenshotUi {
|
|||||||
rect_from_corner_points(selection.1, selection.2),
|
rect_from_corner_points(selection.1, selection.2),
|
||||||
));
|
));
|
||||||
|
|
||||||
*self = Self::Closed { last_selection };
|
*self = Self::Closed {
|
||||||
|
last_selection,
|
||||||
|
config: config.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@@ -209,6 +229,22 @@ impl ScreenshotUi {
|
|||||||
matches!(self, ScreenshotUi::Open { .. })
|
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) {
|
fn update_buffers(&mut self) {
|
||||||
let Self::Open {
|
let Self::Open {
|
||||||
selection,
|
selection,
|
||||||
@@ -293,6 +329,7 @@ impl ScreenshotUi {
|
|||||||
output_data,
|
output_data,
|
||||||
show_pointer,
|
show_pointer,
|
||||||
mouse_down,
|
mouse_down,
|
||||||
|
open_anim,
|
||||||
..
|
..
|
||||||
} = self
|
} = self
|
||||||
else {
|
else {
|
||||||
@@ -306,6 +343,7 @@ impl ScreenshotUi {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let scale = output_data.scale;
|
let scale = output_data.scale;
|
||||||
|
let progress = open_anim.clamped_value().clamp(0., 1.) as f32;
|
||||||
|
|
||||||
// The help panel goes on top.
|
// The help panel goes on top.
|
||||||
if let Some((show, hide)) = &output_data.panel {
|
if let Some((show, hide)) = &output_data.panel {
|
||||||
@@ -324,7 +362,7 @@ impl ScreenshotUi {
|
|||||||
let elem = PrimaryGpuTextureRenderElement(TextureRenderElement::from_texture_buffer(
|
let elem = PrimaryGpuTextureRenderElement(TextureRenderElement::from_texture_buffer(
|
||||||
buffer.clone(),
|
buffer.clone(),
|
||||||
location,
|
location,
|
||||||
alpha,
|
alpha * progress,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
Kind::Unspecified,
|
Kind::Unspecified,
|
||||||
@@ -337,7 +375,7 @@ impl ScreenshotUi {
|
|||||||
SolidColorRenderElement::from_buffer(
|
SolidColorRenderElement::from_buffer(
|
||||||
buffer,
|
buffer,
|
||||||
loc.to_f64().to_logical(scale),
|
loc.to_f64().to_logical(scale),
|
||||||
1.,
|
progress,
|
||||||
Kind::Unspecified,
|
Kind::Unspecified,
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
@@ -530,12 +568,6 @@ impl ScreenshotUi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ScreenshotUi {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OutputScreenshot {
|
impl OutputScreenshot {
|
||||||
pub fn from_textures(
|
pub fn from_textures(
|
||||||
renderer: &mut GlesRenderer,
|
renderer: &mut GlesRenderer,
|
||||||
|
|||||||
@@ -45,6 +45,11 @@ animations {
|
|||||||
config-notification-open-close {
|
config-notification-open-close {
|
||||||
spring damping-ratio=0.6 stiffness=1000 epsilon=0.001
|
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
|
### Synchronized Animations
|
||||||
|
|
||||||
<sup>Since: 0.1.5</sup>
|
<sup>Since: 0.1.5</sup>
|
||||||
|
|||||||
Reference in New Issue
Block a user