1
0
mirror of https://github.com/emilk/egui.git synced 2026-09-01 06:10:06 -04:00
Files
egui/crates/egui_demo_lib/src/demo/pan_zoom.rs
Emil Ernerfeldt d58d13781d Store Margin using i8 to reduce its size (#5567)
Adds `Marginf` to fill the previous niche.

This is all in a pursuit to shrink the sizes of often-used structs, to
improve performance (less cache misses, less memcpy:s, etc).

* On the path towards https://github.com/emilk/egui/issues/4019
2025-01-02 16:05:52 +01:00

146 lines
5.4 KiB
Rust

use egui::emath::TSTransform;
use egui::TextWrapMode;
#[derive(Clone, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct PanZoom {
transform: TSTransform,
drag_value: f32,
}
impl Eq for PanZoom {}
impl crate::Demo for PanZoom {
fn name(&self) -> &'static str {
"🔍 Pan Zoom"
}
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
use crate::View as _;
let window = egui::Window::new("Pan Zoom")
.default_width(300.0)
.default_height(300.0)
.vscroll(false)
.open(open);
window.show(ctx, |ui| self.ui(ui));
}
}
impl crate::View for PanZoom {
fn ui(&mut self, ui: &mut egui::Ui) {
ui.label(
"Pan, zoom in, and zoom out with scrolling (see the plot demo for more instructions). \
Double click on the background to reset.",
);
ui.vertical_centered(|ui| {
ui.add(crate::egui_github_link_file!());
});
ui.separator();
let (id, rect) = ui.allocate_space(ui.available_size());
let response = ui.interact(rect, id, egui::Sense::click_and_drag());
// Allow dragging the background as well.
if response.dragged() {
self.transform.translation += response.drag_delta();
}
// Plot-like reset
if response.double_clicked() {
self.transform = TSTransform::default();
}
let transform =
TSTransform::from_translation(ui.min_rect().left_top().to_vec2()) * self.transform;
if let Some(pointer) = ui.ctx().input(|i| i.pointer.hover_pos()) {
// Note: doesn't catch zooming / panning if a button in this PanZoom container is hovered.
if response.hovered() {
let pointer_in_layer = transform.inverse() * pointer;
let zoom_delta = ui.ctx().input(|i| i.zoom_delta());
let pan_delta = ui.ctx().input(|i| i.smooth_scroll_delta);
// Zoom in on pointer:
self.transform = self.transform
* TSTransform::from_translation(pointer_in_layer.to_vec2())
* TSTransform::from_scaling(zoom_delta)
* TSTransform::from_translation(-pointer_in_layer.to_vec2());
// Pan:
self.transform = TSTransform::from_translation(pan_delta) * self.transform;
}
}
for (i, (pos, callback)) in [
(
egui::Pos2::new(0.0, 0.0),
Box::new(|ui: &mut egui::Ui, _: &mut Self| {
ui.button("top left").on_hover_text("Normal tooltip")
}) as Box<dyn Fn(&mut egui::Ui, &mut Self) -> egui::Response>,
),
(
egui::Pos2::new(0.0, 120.0),
Box::new(|ui: &mut egui::Ui, _| {
ui.button("bottom left").on_hover_text("Normal tooltip")
}),
),
(
egui::Pos2::new(120.0, 120.0),
Box::new(|ui: &mut egui::Ui, _| {
ui.button("right bottom")
.on_hover_text_at_pointer("Tooltip at pointer")
}),
),
(
egui::Pos2::new(120.0, 0.0),
Box::new(|ui: &mut egui::Ui, _| {
ui.button("right top")
.on_hover_text_at_pointer("Tooltip at pointer")
}),
),
(
egui::Pos2::new(60.0, 60.0),
Box::new(|ui, state| {
use egui::epaint::{pos2, CircleShape, Color32, QuadraticBezierShape, Stroke};
// Smiley face.
let painter = ui.painter();
painter.add(CircleShape::filled(pos2(0.0, -10.0), 1.0, Color32::YELLOW));
painter.add(CircleShape::filled(pos2(10.0, -10.0), 1.0, Color32::YELLOW));
painter.add(QuadraticBezierShape::from_points_stroke(
[pos2(0.0, 0.0), pos2(5.0, 3.0), pos2(10.0, 0.0)],
false,
Color32::TRANSPARENT,
Stroke::new(1.0, Color32::YELLOW),
));
ui.add(egui::Slider::new(&mut state.drag_value, 0.0..=100.0).text("My value"))
}),
),
]
.into_iter()
.enumerate()
{
let window_layer = ui.layer_id();
let id = egui::Area::new(id.with(("subarea", i)))
.default_pos(pos)
.order(egui::Order::Middle)
.constrain(false)
.show(ui.ctx(), |ui| {
ui.set_clip_rect(transform.inverse() * rect);
egui::Frame::default()
.rounding(egui::Rounding::same(4))
.inner_margin(egui::Margin::same(8))
.stroke(ui.ctx().style().visuals.window_stroke)
.fill(ui.style().visuals.panel_fill)
.show(ui, |ui| {
ui.style_mut().wrap_mode = Some(TextWrapMode::Extend);
callback(ui, self)
});
})
.response
.layer_id;
ui.ctx().set_transform_layer(id, transform);
ui.ctx().set_sublayer(window_layer, id);
}
}
}