diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/input/mod.rs | 24 | ||||
| -rw-r--r-- | src/ui/screenshot_ui.rs | 175 |
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); |
