Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
context_menu.hpp
Go to the documentation of this file.
1#pragma once
2
14
15#include <pulp/view/view.hpp>
20#include <functional>
21#include <algorithm>
22#include <array>
23#include <memory>
24#include <optional>
25#include <string>
26#include <vector>
27
28namespace pulp::view {
29
37class ContextMenu : public View {
38public:
39 struct Item {
41 int id = 0;
42 std::string label;
43 bool enabled = true;
44 bool checked = false;
46 float normalized_value = 0.0f;
50 std::string value_label;
55 bool tab = false;
56 float normalized_step = 0.05f;
57 std::vector<std::string> choices;
58 std::size_t choice_index = 0;
59 std::function<void(const Item&)> on_change;
60 bool separator = false;
62 bool header = false;
63 bool has_submenu = false;
65 Item i;
66 i.separator = true;
67 return i;
68 }
69 static Item make_header(std::string title) {
70 Item i;
71 i.label = std::move(title);
72 i.enabled = false;
73 i.header = true;
74 return i;
75 }
76 static Item make_toggle(int id, std::string label, bool checked,
77 std::function<void(const Item&)> on_change) {
78 Item i;
79 i.id = id;
80 i.label = std::move(label);
81 i.checked = checked;
82 i.control_kind = ControlKind::toggle;
83 i.on_change = std::move(on_change);
84 return i;
85 }
86 static Item make_slider(int id, std::string label, float normalized, float step,
87 std::function<void(const Item&)> on_change) {
88 Item i;
89 i.id = id;
90 i.label = std::move(label);
91 i.control_kind = ControlKind::slider;
92 i.normalized_value = std::clamp(normalized, 0.0f, 1.0f);
93 i.normalized_step = step > 0.0f ? step : 0.05f;
94 i.on_change = std::move(on_change);
95 return i;
96 }
97 static Item make_choice(int id, std::string label, std::vector<std::string> choices,
98 std::size_t selected, std::function<void(const Item&)> on_change) {
99 Item i;
100 i.id = id;
101 i.label = std::move(label);
102 i.control_kind = ControlKind::choice;
103 i.choices = std::move(choices);
104 i.choice_index = i.choices.empty() ? 0 : std::min(selected, i.choices.size() - 1);
105 i.on_change = std::move(on_change);
106 return i;
107 }
108 void set_toggled(bool value) {
110 return;
111 checked = value;
112 if (on_change)
113 on_change(*this);
114 }
117 return;
118 const float next = std::clamp(value, 0.0f, 1.0f);
119 if (normalized_value == next)
120 return;
121 normalized_value = next;
122 if (on_change)
123 on_change(*this);
124 }
125 void set_choice_index(std::size_t value) {
126 if (control_kind != ControlKind::choice || choices.empty())
127 return;
128 const auto next = std::min(value, choices.size() - 1);
129 if (choice_index == next)
130 return;
131 choice_index = next;
132 if (on_change)
133 on_change(*this);
134 }
135 };
136
138
139 void set_items(std::vector<Item> items) {
140 if (item_change_dispatch_depth_ != 0) {
141 deferred_items_ = std::move(items);
142 return;
143 }
144 replace_items(std::move(items));
145 }
146
149 void set_min_width(float width) {
150 min_width_ = std::max(0.0f, width);
151 }
152 float min_width() const {
153 return min_width_;
154 }
155 std::vector<Item>& items() {
156 return items_;
157 }
158 const std::vector<Item>& items() const { return items_; }
159
163 std::function<void(std::optional<int>)> on_close;
164
167 void set_anchor(Point anchor) { anchor_ = anchor; }
168
169 int hovered_index() const { return hover_index_; }
170
174 static ContextMenu* show(View* root, Point pos, std::vector<Item> items,
175 std::function<void(std::optional<int>)> on_close);
176
177 void paint(canvas::Canvas& canvas) override;
178 void on_mouse_event(const MouseEvent& event) override;
179 bool on_key_event(const KeyEvent& event) override;
187 bool accepts_navigation_input() const override { return !closed_; }
188 View* hit_test(Point local_point) override; // whole overlay is hit (catch outside clicks)
189 bool wants_mouse_input() const override { return true; }
190
196 struct MenuLayout {
198 float border = 0.0f;
200 std::vector<Rect> rows{};
202 float content_height = 0.0f;
205 float max_scroll = 0.0f;
206 };
207
211 float scroll_offset() const {
212 return scroll_;
213 }
214
222
224 float measured_width() const { return layout().box.width; }
225
231 std::string label;
232 bool enabled = false;
233 float rate_normalized = 0.25f;
234 float depth_normalized = 0.0f;
235 std::vector<std::string> shapes;
236 std::size_t shape_index = 0;
237 std::function<void(const ModulationLane&)> on_change;
238 };
239
241 std::string target_label;
243 std::size_t lane_count = 2;
244 std::function<void()> on_reset;
245 std::function<void()> on_dismiss;
246 };
247
252 static std::vector<Item> make_modulation_items(const ModulationMenuSpec& spec);
253
258
259 private:
260 // Row index under a local point, or -1 if outside the box / on a non-row.
261 int row_at(Point local_point, const MenuLayout& lay) const;
262 // Keyboard nav must not move the hover somewhere the panel is not showing.
263 void scroll_row_into_view(int index);
264 void move_hover(int delta); // keyboard nav, skipping separators + disabled
265 void move_hover_to_edge(bool last); // Home / End
266 void adjust_control(Item& item, float delta);
267 bool set_control_from_pointer(Item& item, const Rect& row, Point point);
268 void fire_close(std::optional<int> result);
269 void replace_items(std::vector<Item> items);
270 void notify_item_change(Item& item);
271 void flush_deferred_items();
272
273 std::vector<Item> items_;
274 Point anchor_{0, 0};
275 int hover_index_ = -1;
276 int active_slider_index_ = -1;
277 int active_pointer_id_ = 0;
278 bool closed_ = false;
279 float scroll_ = 0.0f;
280 static float slider_track_right(const Item& item, const Rect& row);
281 static constexpr float kValueLabelWidth = 52.0f;
282 float min_width_ = 0.0f;
283 std::size_t item_change_dispatch_depth_ = 0;
284 std::optional<std::vector<Item>> deferred_items_;
285
286 static constexpr float kRowHeight = 24.0f;
287 static constexpr float kHPad = 34.0f; // total horizontal label padding
288 static constexpr float kMinWidth = 120.0f;
289 static constexpr float kRadius = 4.0f;
295 static constexpr float kSeparatorHeight = 7.0f;
296 static constexpr float kFontSize = 12.0f;
300 static constexpr float kEdgeMargin = 4.0f;
301};
302
309
310} // namespace pulp::view
Definition canvas.hpp:240
Floating, view-tree-drawn context / popup menu.
Definition context_menu.hpp:37
bool on_key_event(const KeyEvent &event) override
Key event with modifiers and up/down state.
ContextMenu()
Definition context_menu.hpp:137
static std::vector< Item > make_modulation_items(const ModulationMenuSpec &spec)
Build the canonical Spectr-style two-LFO menu.
void set_items(std::vector< Item > items)
Definition context_menu.hpp:139
std::vector< Item > & items()
Definition context_menu.hpp:155
void set_min_width(float width)
Reserve a width so a family of menus that rebuild their rows (a progressive-disclosure menu,...
Definition context_menu.hpp:149
void set_anchor(Point anchor)
Anchor (in the overlay/root LOCAL coordinate space) where the menu's top-left should appear; flips le...
Definition context_menu.hpp:167
MenuLayout layout() const
Compute the menu's own size and row positions, with no canvas and no paint.
int hovered_index() const
for tests; -1 = none
Definition context_menu.hpp:169
void paint(canvas::Canvas &canvas) override
static ContextMenu * show(View *root, Point pos, std::vector< Item > items, std::function< void(std::optional< int >)> on_close)
Mount items as a full-window overlay child of root at pos, returning a raw pointer to the live Contex...
std::function< void(std::optional< int >)> on_close
Fired with the chosen item id on select, or with std::nullopt on dismiss (Escape / outside-click).
Definition context_menu.hpp:163
const std::vector< Item > & items() const
Definition context_menu.hpp:158
bool wants_mouse_input() const override
True if this widget handles mouse down/drag through virtual methods even when it does not expose JS p...
Definition context_menu.hpp:189
void on_mouse_event(const MouseEvent &event) override
Mouse down with full event context.
View * hit_test(Point local_point) override
bool accepts_navigation_input() const override
An open menu owns the arrow keys, the same way an open ComboBox does.
Definition context_menu.hpp:187
float min_width() const
Definition context_menu.hpp:152
float scroll_offset() const
Current scroll offset in pixels, clamped to [0, max_scroll].
Definition context_menu.hpp:211
float measured_width() const
Convenience: the panel width layout() would choose.
Definition context_menu.hpp:224
static ContextMenu * show_modulation(View *root, Point pos, ModulationMenuSpec spec)
Show the canonical modulation popup at a root-local anchor.
@ row
Definition view.hpp:991
void set_focusable(bool f)
Definition view.hpp:669
Input event types with modifier keys and pointer IDs.
Definition affine_transform.hpp:24
Hover-cursor tracking for platform window hosts.
Definition fwd.hpp:27
@ value
the widget's primary value (Knob, Fader, RangeSlider, Toggle, ProgressBar)
Definition binding_diagnostics.hpp:19
@ i
Definition input_events.hpp:40
@ width
Definition css_animation.hpp:127
ContextMenu PopupMenu
Design-system alias.
Definition context_menu.hpp:308
Definition context_menu.hpp:39
bool separator
Definition context_menu.hpp:60
bool enabled
Definition context_menu.hpp:43
static Item make_choice(int id, std::string label, std::vector< std::string > choices, std::size_t selected, std::function< void(const Item &)> on_change)
Definition context_menu.hpp:97
void set_choice_index(std::size_t value)
Definition context_menu.hpp:125
bool header
A non-selectable title row that groups the items below it.
Definition context_menu.hpp:62
ControlKind control_kind
Definition context_menu.hpp:45
void set_toggled(bool value)
Definition context_menu.hpp:108
float normalized_value
Definition context_menu.hpp:46
static Item make_separator()
Definition context_menu.hpp:64
float normalized_step
Definition context_menu.hpp:56
int id
Definition context_menu.hpp:41
static Item make_header(std::string title)
Definition context_menu.hpp:69
std::size_t choice_index
Definition context_menu.hpp:58
void set_normalized_value(float value)
Definition context_menu.hpp:115
static Item make_slider(int id, std::string label, float normalized, float step, std::function< void(const Item &)> on_change)
Definition context_menu.hpp:86
std::vector< std::string > choices
Definition context_menu.hpp:57
ControlKind
Definition context_menu.hpp:40
@ choice
Definition context_menu.hpp:40
@ slider
Definition context_menu.hpp:40
@ action
Definition context_menu.hpp:40
@ toggle
Definition context_menu.hpp:40
std::function< void(const Item &)> on_change
Definition context_menu.hpp:59
std::string value_label
Text shown at the right end of a slider row ("2.50 Hz", "65%").
Definition context_menu.hpp:50
bool has_submenu
Definition context_menu.hpp:63
bool checked
Definition context_menu.hpp:44
std::string label
Definition context_menu.hpp:42
static Item make_toggle(int id, std::string label, bool checked, std::function< void(const Item &)> on_change)
Definition context_menu.hpp:76
bool tab
A toggle that is one of several sibling tabs (an "edit lane 1 /edit lane 2" pair): painted as a row w...
Definition context_menu.hpp:55
The menu's resolved geometry: the panel rect and one rect per row, in this view's LOCAL coordinates.
Definition context_menu.hpp:196
float content_height
Height the rows want, before the panel is capped to the overlay.
Definition context_menu.hpp:202
FontSpec font
the font the rows are measured/drawn with
Definition context_menu.hpp:199
Rect box
Definition context_menu.hpp:197
float border
panel-edge padding on all four sides
Definition context_menu.hpp:198
float max_scroll
content_height - box.height, clamped at 0.
Definition context_menu.hpp:205
std::vector< Rect > rows
one per item, in item order
Definition context_menu.hpp:200
A lane is the product-independent state needed by the standard two-LFO modulation popup.
Definition context_menu.hpp:230
bool enabled
Definition context_menu.hpp:232
std::vector< std::string > shapes
e.g. Sine, Triangle, Square
Definition context_menu.hpp:235
float depth_normalized
Definition context_menu.hpp:234
std::size_t shape_index
Definition context_menu.hpp:236
std::string label
e.g. "LFO 1"
Definition context_menu.hpp:231
float rate_normalized
Definition context_menu.hpp:233
std::function< void(const ModulationLane &)> on_change
Definition context_menu.hpp:237
Definition context_menu.hpp:240
std::size_t lane_count
Definition context_menu.hpp:243
ModulationLane lanes[2]
Definition context_menu.hpp:242
std::string target_label
Definition context_menu.hpp:241
std::function< void()> on_reset
Definition context_menu.hpp:244
std::function< void()> on_dismiss
Definition context_menu.hpp:245
A font request, as a delegate expresses it.
Definition widget_metrics.hpp:55
Rich key event with key code, modifiers, and repeat state.
Definition input_events.hpp:193
Rich mouse/pointer event with position, modifiers, button state, pointer ID, and stylus properties.
Definition input_events.hpp:104
Definition geometry.hpp:20
Axis-aligned rectangle in continuous (float) coordinates.
Definition geometry.hpp:52
float width
Definition geometry.hpp:53
A pluggable sizing delegate for Pulp's stock widgets — the sibling of WidgetPainter (widget_painter....
A pluggable paint delegate for Pulp's stock widgets.