egui_charts/ui/drawing_toolbar/toolbar/
floating.rs1use super::super::{components::render_svg_at_rect_themed, icons};
4use super::DrawingToolbar;
5use crate::drawings::DrawingToolType;
6use crate::icons::icons as embedded_icons;
7use crate::localization as l10n;
8use crate::styles::icons as icon_sizes;
9use crate::theme::ThemeExt;
10use crate::tokens::DESIGN_TOKENS;
11use egui::{Color32, Id, Pos2, Rect, Sense, Ui, Vec2};
12
13impl DrawingToolbar {
14 pub fn show_floating(&mut self, ui: &mut Ui, position: Pos2) -> Option<DrawingToolType> {
30 let pass = ui.ctx().cumulative_pass_nr();
31 if self.strip_pass == Some(pass) {
32 return None;
33 }
34 let tools: Vec<_> = self
35 .state
36 .favorites
37 .iter()
38 .copied()
39 .filter(|tool| self.registry.availability(*tool).visible)
40 .collect();
41 if !self.state.floating_visible || tools.is_empty() {
42 return None;
43 }
44 self.strip_pass = Some(pass);
45
46 let mut new_selection = None;
47 let mut moved = Vec2::ZERO;
48 let area = egui::Area::new(Id::NULL.with("floating_drawing_toolbar"))
49 .enabled(ui.is_enabled())
50 .current_pos(self.state.floating_pos.unwrap_or(position))
51 .show(ui.ctx(), |ui| {
52 egui::Frame::new()
53 .fill(ui.visuals().panel_fill)
54 .stroke(ui.style().visuals.window_stroke)
55 .corner_radius(ui.active_theme().radius.md)
56 .show(ui, |ui| {
57 ui.horizontal(|ui| {
58 moved = Self::strip_grip(ui);
59 for tool in tools {
60 let is_sel = self.state.sel_tool == Some(tool);
61 let enabled = self.registry.availability(tool).enabled;
62 let (rect, response) = ui.allocate_exact_size(
63 Vec2::splat(DESIGN_TOKENS.sizing.button_md),
64 Sense::click(),
65 );
66 let translated_label = l10n::text(ui.ctx(), tool.as_str());
67 response.widget_info(|| {
68 egui::WidgetInfo::selected(
69 egui::Role::Button,
70 enabled,
71 is_sel,
72 &translated_label,
73 )
74 });
75 crate::styles::focus::draw_focus_ring(
76 ui,
77 &response,
78 ui.active_theme().radius.sm,
79 );
80
81 let bg = if is_sel {
82 ui.active_theme().ui.accent
83 } else if response.hovered() {
84 ui.visuals().widgets.hovered.bg_fill
85 } else {
86 Color32::TRANSPARENT
87 };
88 ui.painter()
89 .rect_filled(rect, ui.active_theme().radius.sm, bg);
90
91 let icon_rect = Rect::from_center_size(
92 rect.center(),
93 Vec2::splat(icon_sizes::MD),
94 );
95 render_svg_at_rect_themed(
96 ui,
97 icons::get_icon(tool),
98 icon_rect,
99 response.hovered(),
100 is_sel,
101 );
102
103 if response.on_hover_text(translated_label).clicked() && enabled {
104 new_selection = Some(tool);
105 }
106 }
107
108 ui.separator();
109
110 if crate::ui_kit::buttons::Button::icon(&embedded_icons::CLOSE)
111 .ghost()
112 .tooltip("Hide favorite tools")
113 .show(ui)
114 .clicked()
115 {
116 self.state.floating_visible = false;
117 }
118 });
119 });
120 });
121 self.state.floating_pos = Some(area.response.rect.min + moved);
122 if moved != Vec2::ZERO {
123 ui.ctx().request_repaint();
124 }
125
126 if let Some(tool) = new_selection {
127 self.select_tool(tool);
128 }
129
130 new_selection
131 }
132
133 fn strip_grip(ui: &mut Ui) -> Vec2 {
135 let size = Vec2::new(icon_sizes::SM, DESIGN_TOKENS.sizing.button_md);
136 let (rect, response) = ui.allocate_exact_size(size, Sense::drag());
137 let label = l10n::text(ui.ctx(), "Move favorite tools");
138 response.widget_info(|| egui::WidgetInfo::labeled(egui::Role::Button, true, &label));
139 let response = response.on_hover_cursor(if ui.input(|i| i.pointer.any_down()) {
140 egui::CursorIcon::Grabbing
141 } else {
142 egui::CursorIcon::Grab
143 });
144 let color = if response.hovered() || response.dragged() {
145 ui.visuals().text_color()
146 } else {
147 ui.visuals().weak_text_color()
148 };
149 for col in [-2.0, 2.0] {
150 for row in [-5.0, 0.0, 5.0] {
151 ui.painter()
152 .circle_filled(rect.center() + Vec2::new(col, row), 1.0, color);
153 }
154 }
155 response.drag_delta()
156 }
157}