From cd318f0e55c43163aa26e4739042bb95b198d46d Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Tue, 22 Apr 2025 12:42:24 +0300 Subject: [PATCH] feat: Add `Scene::drag_pan_buttons` option. Allows specifying which pointer buttons pan the scene by dragging. (#5892) This adds an option for specifying the set of pointer buttons that can be used to pan the scene via clicking and dragging. The original behaviour where all buttons can pan the scene by default is maintained. Addresses part of #5891. --- Edit: It looks like the failing test is unrelated and also appears on master: https://github.com/emilk/egui/actions/runs/14330259861/job/40164414607. --------- Co-authored-by: Emil Ernerfeldt --- crates/egui/src/containers/mod.rs | 2 +- crates/egui/src/containers/scene.rs | 47 +++++++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/crates/egui/src/containers/mod.rs b/crates/egui/src/containers/mod.rs index 3bd89e0d..31898838 100644 --- a/crates/egui/src/containers/mod.rs +++ b/crates/egui/src/containers/mod.rs @@ -29,7 +29,7 @@ pub use { panel::{CentralPanel, SidePanel, TopBottomPanel}, popup::*, resize::Resize, - scene::Scene, + scene::{DragPanButtons, Scene}, scroll_area::ScrollArea, sides::Sides, tooltip::*, diff --git a/crates/egui/src/containers/scene.rs b/crates/egui/src/containers/scene.rs index 5b8d9741..d023a4d3 100644 --- a/crates/egui/src/containers/scene.rs +++ b/crates/egui/src/containers/scene.rs @@ -3,7 +3,8 @@ use core::f32; use emath::{GuiRounding, Pos2}; use crate::{ - emath::TSTransform, InnerResponse, LayerId, Rangef, Rect, Response, Sense, Ui, UiBuilder, Vec2, + emath::TSTransform, InnerResponse, LayerId, PointerButton, Rangef, Rect, Response, Sense, Ui, + UiBuilder, Vec2, }; /// Creates a transformation that fits a given scene rectangle into the available screen size. @@ -45,6 +46,30 @@ fn fit_to_rect_in_scene( pub struct Scene { zoom_range: Rangef, max_inner_size: Vec2, + drag_pan_buttons: DragPanButtons, +} + +/// Specifies which pointer buttons can be used to pan the scene by dragging. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct DragPanButtons(u8); + +bitflags::bitflags! { + impl DragPanButtons: u8 { + /// [PointerButton::Primary] + const PRIMARY = 1 << 0; + + /// [PointerButton::Secondary] + const SECONDARY = 1 << 1; + + /// [PointerButton::Middle] + const MIDDLE = 1 << 2; + + /// [PointerButton::Extra1] + const EXTRA_1 = 1 << 3; + + /// [PointerButton::Extra2] + const EXTRA_2 = 1 << 4; + } } impl Default for Scene { @@ -52,6 +77,7 @@ impl Default for Scene { Self { zoom_range: Rangef::new(f32::EPSILON, 1.0), max_inner_size: Vec2::splat(1000.0), + drag_pan_buttons: DragPanButtons::all(), } } } @@ -82,6 +108,15 @@ impl Scene { self } + /// Specify which pointer buttons can be used to pan by clicking and dragging. + /// + /// By default, this is `DragPanButtons::all()`. + #[inline] + pub fn drag_pan_buttons(mut self, flags: DragPanButtons) -> Self { + self.drag_pan_buttons = flags; + self + } + /// `scene_rect` contains the view bounds of the inner [`Ui`]. /// /// `scene_rect` will be mutated by any panning/zooming done by the user. @@ -179,7 +214,15 @@ impl Scene { /// Helper function to handle pan and zoom interactions on a response. pub fn register_pan_and_zoom(&self, ui: &Ui, resp: &mut Response, to_global: &mut TSTransform) { - if resp.dragged() { + let dragged = self.drag_pan_buttons.iter().any(|button| match button { + DragPanButtons::PRIMARY => resp.dragged_by(PointerButton::Primary), + DragPanButtons::SECONDARY => resp.dragged_by(PointerButton::Secondary), + DragPanButtons::MIDDLE => resp.dragged_by(PointerButton::Middle), + DragPanButtons::EXTRA_1 => resp.dragged_by(PointerButton::Extra1), + DragPanButtons::EXTRA_2 => resp.dragged_by(PointerButton::Extra2), + _ => false, + }); + if dragged { to_global.translation += to_global.scaling * resp.drag_delta(); resp.mark_changed(); }