aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/input/mod.rs24
-rw-r--r--src/ui/screenshot_ui.rs175
2 files changed, 166 insertions, 33 deletions
diff --git a/src/input/mod.rs b/src/input/mod.rs
index a23c2e13..1f790f16 100644
--- a/src/input/mod.rs
+++ b/src/input/mod.rs
@@ -2561,8 +2561,12 @@ impl State {
self.niri.queue_redraw_all();
}
}
- } else if self.niri.screenshot_ui.pointer_up(None) {
- self.niri.queue_redraw_all();
+ } else if let Some(capture) = self.niri.screenshot_ui.pointer_up(None) {
+ if capture {
+ self.confirm_screenshot(true);
+ } else {
+ self.niri.queue_redraw_all();
+ }
}
}
@@ -3129,8 +3133,12 @@ impl State {
}
}
TabletToolTipState::Up => {
- if self.niri.screenshot_ui.pointer_up(None) {
- self.niri.queue_redraw_all();
+ if let Some(capture) = self.niri.screenshot_ui.pointer_up(None) {
+ if capture {
+ self.confirm_screenshot(true);
+ } else {
+ self.niri.queue_redraw_all();
+ }
}
tool.tip_up(event.time_msec());
@@ -3639,8 +3647,12 @@ impl State {
};
let slot = evt.slot();
- if self.niri.screenshot_ui.pointer_up(Some(slot)) {
- self.niri.queue_redraw_all();
+ if let Some(capture) = self.niri.screenshot_ui.pointer_up(Some(slot)) {
+ if capture {
+ self.confirm_screenshot(true);
+ } else {
+ self.niri.queue_redraw_all();
+ }
}
let serial = SERIAL_COUNTER.next_serial();
diff --git a/src/ui/screenshot_ui.rs b/src/ui/screenshot_ui.rs
index 00ea69d9..e3b0ee89 100644
--- a/src/ui/screenshot_ui.rs
+++ b/src/ui/screenshot_ui.rs
@@ -1,6 +1,7 @@
use std::cell::RefCell;
use std::cmp::{max, min};
use std::collections::HashMap;
+use std::f64::consts::TAU;
use std::iter::zip;
use std::rc::Rc;
@@ -18,7 +19,7 @@ use smithay::backend::renderer::gles::{GlesRenderer, GlesTexture};
use smithay::backend::renderer::{ExportMem, Texture as _};
use smithay::input::keyboard::{Keysym, ModifiersState};
use smithay::output::{Output, WeakOutput};
-use smithay::utils::{Physical, Point, Rectangle, Scale, Size, Transform};
+use smithay::utils::{Buffer, Physical, Point, Rectangle, Scale, Size, Transform};
use crate::animation::{Animation, Clock};
use crate::layout::floating::DIRECTIONAL_MOVE_PX;
@@ -32,6 +33,7 @@ use crate::utils::to_physical_precise_round;
const SELECTION_BORDER: i32 = 2;
const PADDING: i32 = 8;
+const RADIUS: i32 = 16;
const FONT: &str = "sans 14px";
const BORDER: i32 = 4;
const TEXT_HIDE_P: &str =
@@ -64,10 +66,13 @@ pub enum ScreenshotUi {
},
}
-#[derive(Clone, Copy)]
-enum Button {
+pub enum Button {
Up,
- Down { touch_slot: Option<TouchSlot> },
+ Down {
+ touch_slot: Option<TouchSlot>,
+ on_capture_button: bool,
+ last_pos: (Output, Point<i32, Physical>),
+ },
}
pub struct OutputData {
@@ -98,6 +103,16 @@ impl Button {
fn is_down(&self) -> bool {
matches!(self, Self::Down { .. })
}
+
+ fn is_dragging_selection(&self) -> bool {
+ matches!(
+ self,
+ Self::Down {
+ on_capture_button: false,
+ ..
+ }
+ )
+ }
}
impl ScreenshotUi {
@@ -521,17 +536,15 @@ impl ScreenshotUi {
// The help panel goes on top.
if let Some((show, hide)) = &output_data.panel {
let buffer = if *show_pointer { hide } else { show };
-
- let size = buffer.texture().size();
- let padding: i32 = to_physical_precise_round(scale, PADDING);
- let x = max(0, (output_data.size.w - size.w) / 2);
- let y = max(0, output_data.size.h - size.h - padding * 2);
- let location = Point::<_, Physical>::from((x, y))
+ let alpha = if button.is_dragging_selection() {
+ 0.3
+ } else {
+ 0.9
+ };
+ let location = panel_location(output_data, buffer.texture().size())
.to_f64()
.to_logical(scale);
- let alpha = if button.is_down() { 0.3 } else { 0.9 };
-
let elem = PrimaryGpuTextureRenderElement(TextureRenderElement::from_texture_buffer(
buffer.clone(),
location,
@@ -675,7 +688,12 @@ impl ScreenshotUi {
pub fn pointer_motion(&mut self, point: Point<i32, Physical>, slot: Option<TouchSlot>) {
let Self::Open {
selection,
- button: Button::Down { touch_slot },
+ button:
+ Button::Down {
+ touch_slot,
+ on_capture_button,
+ last_pos,
+ },
..
} = self
else {
@@ -686,6 +704,12 @@ impl ScreenshotUi {
return;
}
+ last_pos.1 = point;
+
+ if *on_capture_button {
+ return;
+ }
+
selection.2 = point;
self.update_buffers();
}
@@ -711,11 +735,30 @@ impl ScreenshotUi {
return false;
}
- if !output_data.contains_key(&output) {
+ let Some(output_data) = output_data.get(&output) else {
return false;
+ };
+
+ if let Some((show, hide)) = &output_data.panel {
+ let buffer = if *show_pointer { hide } else { show };
+ let panel_size = buffer.texture().size();
+ let location = panel_location(output_data, panel_size);
+
+ if is_within_capture_button(output_data.scale, panel_size, point - location) {
+ *button = Button::Down {
+ touch_slot: slot,
+ on_capture_button: true,
+ last_pos: (output, point),
+ };
+ return false;
+ }
}
- *button = Button::Down { touch_slot: slot };
+ *button = Button::Down {
+ touch_slot: slot,
+ on_capture_button: false,
+ last_pos: (output.clone(), point),
+ };
*selection = (output, point, point);
self.update_buffers();
@@ -723,27 +766,54 @@ impl ScreenshotUi {
true
}
- pub fn pointer_up(&mut self, slot: Option<TouchSlot>) -> bool {
+ pub fn pointer_up(&mut self, slot: Option<TouchSlot>) -> Option<bool> {
let Self::Open {
selection,
output_data,
button,
+ show_pointer,
..
} = self
else {
- return false;
+ return None;
};
- let Button::Down { touch_slot } = *button else {
- return false;
+ let Button::Down {
+ touch_slot,
+ on_capture_button,
+ ref last_pos,
+ } = *button
+ else {
+ return None;
};
if touch_slot != slot {
- return false;
+ return None;
}
+ let last_pos = last_pos.clone();
*button = Button::Up;
+ // Check if we released still on the capture button.
+ if on_capture_button {
+ let (output, point) = last_pos;
+
+ #[allow(clippy::question_mark)]
+ let Some(output_data) = output_data.get(&output) else {
+ return None;
+ };
+
+ if let Some((show, hide)) = &output_data.panel {
+ let buffer = if *show_pointer { hide } else { show };
+ let panel_size = buffer.texture().size();
+ let location = panel_location(output_data, panel_size);
+
+ if is_within_capture_button(output_data.scale, panel_size, point - location) {
+ return Some(true);
+ }
+ }
+ }
+
// Check if the resulting selection is zero-sized, and try to come up with a small
// default rectangle.
let (output, a, b) = selection;
@@ -762,7 +832,7 @@ impl ScreenshotUi {
self.update_buffers();
- true
+ Some(false)
}
}
@@ -853,6 +923,29 @@ pub fn rect_from_corner_points(
Rectangle::from_extremities((x1, y1), (x2 + 1, y2 + 1))
}
+fn panel_location(output_data: &OutputData, panel_size: Size<i32, Buffer>) -> Point<i32, Physical> {
+ let scale = output_data.scale;
+ let padding: i32 = to_physical_precise_round(scale, PADDING);
+ let x = max(0, (output_data.size.w - panel_size.w) / 2);
+ let y = max(0, output_data.size.h - panel_size.h - padding * 2);
+ Point::from((x, y))
+}
+
+fn is_within_capture_button(
+ scale: f64,
+ panel_size: Size<i32, Buffer>,
+ pos_within_panel: Point<i32, Physical>,
+) -> bool {
+ let padding: i32 = to_physical_precise_round(scale, PADDING);
+ let radius = to_physical_precise_round::<i32>(scale, RADIUS) - 2;
+
+ let xc = padding + radius;
+ let yc = panel_size.h / 2;
+ let pos = pos_within_panel;
+
+ (pos.x - xc) * (pos.x - xc) + (pos.y - yc) * (pos.y - yc) <= radius * radius
+}
+
fn render_panel(
renderer: &mut GlesRenderer,
scale: f64,
@@ -861,6 +954,11 @@ fn render_panel(
let _span = tracy_client::span!("screenshot_ui::render_panel");
let padding: i32 = to_physical_precise_round(scale, PADDING);
+ // Keep the border width even to avoid blurry edges.
+ let border_width = (f64::from(BORDER) / 2. * scale).round() * 2.;
+ let half_border_width = (border_width / 2.) as i32;
+ let radius: i32 = to_physical_precise_round(scale, RADIUS);
+ let circle_stroke: f64 = to_physical_precise_round(scale, 2.);
// Add 2 px of spacing to separate the backgrounds of the "Space" and "P" keys.
let spacing = to_physical_precise_round::<i32>(scale, 2) * 1024;
@@ -873,12 +971,14 @@ fn render_panel(
let layout = pangocairo::functions::create_layout(&cr);
layout.context().set_round_glyph_positions(false);
layout.set_font_description(Some(&font));
- layout.set_alignment(Alignment::Center);
+ layout.set_alignment(Alignment::Left);
layout.set_markup(text);
layout.set_spacing(spacing);
let (mut width, mut height) = layout.pixel_size();
- width += padding * 2;
+
+ width += padding + radius * 2 + padding - half_border_width + padding;
+ height = max(height, radius * 2);
height += padding * 2;
let surface = ImageSurface::create(cairo::Format::ARgb32, width, height)?;
@@ -886,11 +986,33 @@ fn render_panel(
cr.set_source_rgb(0.1, 0.1, 0.1);
cr.paint()?;
- cr.move_to(padding.into(), padding.into());
+ let padding = f64::from(padding);
+ let half_border_width = f64::from(half_border_width);
+ let r = f64::from(radius);
+
+ let yc = f64::from(height / 2);
+
+ cr.new_sub_path();
+ cr.arc(padding + r, yc, r, 0., TAU);
+ cr.set_source_rgb(1., 1., 1.);
+ cr.fill()?;
+
+ cr.new_sub_path();
+ cr.arc(padding + r, yc, r - circle_stroke, 0., TAU);
+ cr.set_source_rgb(0.1, 0.1, 0.1);
+ cr.fill()?;
+
+ cr.new_sub_path();
+ cr.arc(padding + r, yc, r - circle_stroke * 2., 0., TAU);
+ cr.set_source_rgb(1., 1., 1.);
+ cr.fill()?;
+
+ cr.move_to(padding + r * 2. + padding - half_border_width, padding);
+
let layout = pangocairo::functions::create_layout(&cr);
layout.context().set_round_glyph_positions(false);
layout.set_font_description(Some(&font));
- layout.set_alignment(Alignment::Center);
+ layout.set_alignment(Alignment::Left);
layout.set_markup(text);
layout.set_spacing(spacing);
@@ -903,8 +1025,7 @@ fn render_panel(
cr.line_to(0., height.into());
cr.line_to(0., 0.);
cr.set_source_rgb(0.3, 0.3, 0.3);
- // Keep the border width even to avoid blurry edges.
- cr.set_line_width((f64::from(BORDER) / 2. * scale).round() * 2.);
+ cr.set_line_width(border_width);
cr.stroke()?;
drop(cr);