1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
//! What every editor canvas does around its own contents: the zoomable scene
//! inside the room the scrollbars leave, one painter for all of it, the
//! middle button and the scrollbars moving the view.
use crate::canvas::scene_modification::SceneModded;
use crate::canvas::scrollbars::{SCROLLBAR_WIDTH, pan_view, panning, scrollbars};
use crate::canvas::shapes::canvas_painter;
use crate::canvas::view::SceneView;
use egui::emath::TSTransform;
use egui::{Id, Margin, Painter, Rect, Response, Sense, Ui, UiBuilder, Vec2};
/// What a canvas is.
#[derive(Debug, Clone, Copy)]
pub(crate) struct CanvasSpec {
/// Salted with the host layer, so two canvases in one window keep their
/// own widgets.
pub(crate) id_salt: &'static str,
/// The scene's extent, in scene units; it starts at the origin.
pub(crate) scene_size: Vec2,
/// How far in the user may zoom, in screen pixels per scene unit.
pub(crate) max_zoom: f32,
/// What the canvas reacts to; [`Sense::hover`] while a dialog floats over
/// it.
pub(crate) sense: Sense,
}
/// One frame of a canvas, as its contents see it.
pub(crate) struct CanvasFrame<'u> {
/// The ui covering everything visible of the scene, in scene units.
pub(crate) ui: &'u mut Ui,
/// The canvas as one widget: clicks and drags on it, and its context
/// menu. Its positions are in scene units.
pub(crate) response: Response,
/// The one layer the canvas paints into, in screen coordinates.
pub(crate) painter: Painter,
/// Scene units to screen pixels.
pub(crate) scene_transform: TSTransform,
}
/// What showing a canvas leaves behind.
#[derive(Debug)]
pub(crate) struct CanvasShown {
/// The zoom the view now shows the scene at.
pub(crate) zoom: f32,
/// Where the canvas is on screen, without the scrollbars.
pub(crate) clip_rect: Rect,
}
/// Shows a canvas in the room left in `ui`, looking where `view` says, and
/// moves `view` by the middle button and the scrollbars.
pub(crate) fn show_canvas(
ui: &mut Ui,
spec: CanvasSpec,
view: &mut SceneView,
contents: impl FnOnce(&mut CanvasFrame<'_>),
) -> CanvasShown {
// the scrollbars take a strip off the right and the bottom
let clip_rect = ui.available_rect_before_wrap()
- Margin {
right: SCROLLBAR_WIDTH as i8,
bottom: SCROLLBAR_WIDTH as i8,
..Default::default()
};
let host_layer = ui.layer_id();
let scene = SceneModded::new()
.scene_size(spec.scene_size)
.max_zoom(spec.max_zoom);
let pan = ui
.scope_builder(UiBuilder::new().max_rect(clip_rect), |ui| {
scene.show(ui, view, |parent_ui| {
// Everything on screen is canvas, not just the scene: the clip
// rect is the visible part of it, so clicking beside the scene
// still deselects and a thing dragged past its edge stays
// reachable.
let visible = parent_ui.clip_rect();
let mut child_ui = parent_ui.new_child(
UiBuilder::new()
.max_rect(visible)
.sense(spec.sense)
.id(Id::new((spec.id_salt, host_layer))),
);
child_ui.take_available_space();
let response = child_ui.response();
let painter = canvas_painter(clip_rect, &child_ui, host_layer);
let scene_transform = child_ui
.ctx()
.layer_transform_to_global(child_ui.layer_id())
.unwrap_or_default();
let mut frame = CanvasFrame {
ui: &mut child_ui,
response: response.clone(),
painter,
scene_transform,
};
contents(&mut frame);
// the drag the middle button took off the contents, in scene
// units
if panning(&child_ui) {
response.drag_delta()
} else {
Vec2::ZERO
}
})
})
.inner
.inner;
pan_view(view, pan);
scrollbars(ui, clip_rect, &scene, view);
CanvasShown {
zoom: scene.zoom_of(clip_rect.size(), view),
clip_rect,
}
}