Skip to main content

nice_plug_egui/
resizable_window.rs

1//! Resizable window wrapper for Egui editor.
2
3use egui::emath::GuiRounding;
4use egui::{CentralPanel, Id, Rect, Response, Sense, Ui, Vec2, pos2};
5use egui::{InnerResponse, UiBuilder};
6
7use crate::EguiState;
8
9/// Adds a corner to the plugin window that can be dragged in order to resize it.
10/// Resizing happens through plugin API, hence a custom implementation is needed.
11pub struct ResizableWindow {
12    id: Id,
13    min_size: Vec2,
14}
15
16impl ResizableWindow {
17    pub fn new(id_source: impl std::hash::Hash) -> Self {
18        Self {
19            id: Id::new(id_source),
20            min_size: Vec2::splat(16.0),
21        }
22    }
23
24    /// Won't shrink to smaller than this
25    #[inline]
26    pub fn min_size(mut self, min_size: impl Into<Vec2>) -> Self {
27        self.min_size = min_size.into();
28        self
29    }
30
31    pub fn show<R>(
32        self,
33        ui: &mut Ui,
34        egui_state: &EguiState,
35        add_contents: impl FnOnce(&mut Ui) -> R,
36    ) -> InnerResponse<R> {
37        CentralPanel::default().show_inside(ui, move |ui| {
38            let ui_rect = ui.clip_rect();
39            let mut content_ui =
40                ui.new_child(UiBuilder::new().max_rect(ui_rect).layout(*ui.layout()));
41
42            let ret = add_contents(&mut content_ui);
43
44            let corner_size = Vec2::splat(ui.visuals().resize_corner_size);
45            let corner_rect = Rect::from_min_size(ui_rect.max - corner_size, corner_size);
46
47            let corner_response = ui.interact(corner_rect, self.id.with("corner"), Sense::drag());
48
49            if let Some(pointer_pos) = corner_response.interact_pointer_pos() {
50                let desired_size = (pointer_pos - ui_rect.min + 0.5 * corner_response.rect.size())
51                    .max(self.min_size);
52
53                if corner_response.dragged() {
54                    egui_state.set_requested_size((
55                        desired_size.x.round() as u32,
56                        desired_size.y.round() as u32,
57                    ));
58                }
59            }
60
61            paint_resize_corner(&content_ui, &corner_response);
62
63            ret
64        })
65    }
66}
67
68pub fn paint_resize_corner(ui: &Ui, response: &Response) {
69    let stroke = ui.style().interact(response).fg_stroke;
70
71    let painter = ui.painter();
72    let rect = response.rect.translate(-Vec2::splat(2.0)); // move away from the corner
73    let cp = rect.max.round_to_pixels(painter.pixels_per_point());
74
75    let mut w = 2.0;
76
77    while w <= rect.width() && w <= rect.height() {
78        painter.line_segment([pos2(cp.x - w, cp.y), pos2(cp.x, cp.y - w)], stroke);
79        w += 4.0;
80    }
81}