Vital
Loading...
Searching...
No Matches
wavetable_organizer.cpp
Go to the documentation of this file.
2
3#include "skin.h"
5
6// Internal callback triggered by organizer context menu actions.
7namespace {
8 void organizerCallback(int result, WavetableOrganizer* organizer) {
9 if (result == WavetableOrganizer::kCreate)
10 organizer->createKeyframeAtMenu();
11 else if (result == WavetableOrganizer::kRemove)
12 organizer->deleteSelectedKeyframes();
13 }
14}
15
16bool DraggableFrame::isInside(int x, int y) {
17 int x_distance = std::min(x, getWidth() - x);
18 int y_distance = std::min(y, getWidth() - y);
19 return x_distance + y_distance >= getWidth() / 2;
20}
21
22WavetableOrganizer::WavetableOrganizer(WavetableCreator* wavetable_creator, int max_frames) :
23 SynthSection("Wavetable Organizer"),
24 unselected_frame_quads_(kMaxKeyframes, Shaders::kDiamondFragment),
25 selected_frame_quads_(kMaxKeyframes, Shaders::kDiamondFragment),
26 active_rows_(WavetableComponentList::kMaxRows, Shaders::kColorFragment),
27 selection_quad_(Shaders::kColorFragment), playhead_quad_(Shaders::kColorFragment) {
28 // Constructor sets up OpenGL quads and initial states.
29 unselected_frame_quads_.setTargetComponent(this);
30 selected_frame_quads_.setTargetComponent(this);
31 active_rows_.setTargetComponent(this);
32
33 unselected_frame_quads_.setThickness(2.0f);
34 selected_frame_quads_.setThickness(2.0f);
35 addOpenGlComponent(&active_rows_);
36 addOpenGlComponent(&unselected_frame_quads_);
37 addOpenGlComponent(&selected_frame_quads_);
38 addOpenGlComponent(&selection_quad_);
39 addOpenGlComponent(&playhead_quad_);
40
41 wavetable_creator_ = wavetable_creator;
42 max_frames_ = max_frames;
43 frame_width_ = 0.0f;
44 currently_dragged_ = nullptr;
45 dragged_start_x_ = 0;
46 mouse_mode_ = kWaiting;
47 playhead_position_ = 0;
48 draw_vertical_offset_ = 0;
49
50 recreateVisibleFrames();
51}
52
54 listeners_.clear();
55 clearVisibleFrames();
56}
57
58// Draws background grid and sets colors for frame quads.
60 g.fillAll(findColour(Skin::kBackground, true));
61
62 Colour lighten = findColour(Skin::kLightenScreen, true);
63 g.setColour(lighten.withMultipliedAlpha(0.5f));
64 int half_handle = 0.5f * handleWidth();
65 g.fillRect(0, 0, half_handle, getHeight());
66 g.fillRect(getWidth() - half_handle, 0, half_handle, getHeight());
67 for (int i = kDrawSkip; i < max_frames_ - 1; i += kDrawSkip) {
68 if (i % kDrawSkipLarge == 0)
69 g.setColour(lighten);
70 else
71 g.setColour(lighten.withMultipliedAlpha(0.5f));
72
73 int x = i * frame_width_ + half_handle;
74 g.fillRect(x, 0, 1, getHeight());
75 }
76
77 g.setColour(lighten);
78
79 Colour edge = findColour(Skin::kBackground, true);
80 Colour primary = findColour(Skin::kWidgetPrimary1, true);
81 unselected_frame_quads_.setColor(edge);
82 unselected_frame_quads_.setAltColor(findColour(Skin::kWidgetPrimaryDisabled, true));
83 selected_frame_quads_.setColor(edge);
84 selected_frame_quads_.setAltColor(primary);
85 selection_quad_.setColor(lighten);
86 playhead_quad_.setColor(primary);
87 active_rows_.setColor(lighten);
88}
89
90// Repositions frames and updates visuals after size changes.
92 repositionVisibleFrames();
93 playheadMoved(playhead_position_);
94
95 setFrameQuads();
96 setRowQuads();
97}
98
99void WavetableOrganizer::deselect() {
100 for (WavetableKeyframe* keyframe : currently_selected_) {
101 if (frame_lookup_.count(keyframe))
102 frame_lookup_[keyframe]->select(false);
103 }
104 currently_selected_.clear();
105
106 for (Listener* listener : listeners_)
107 listener->frameSelected(nullptr);
108
109 setFrameQuads();
110}
111
112void WavetableOrganizer::deleteKeyframe(WavetableKeyframe* keyframe) {
113 keyframe->owner()->remove(keyframe);
114 frame_lookup_.erase(keyframe);
115
116 for (Listener* listener : listeners_)
117 listener->positionsUpdated();
118
119 setRowQuads();
120}
121
122void WavetableOrganizer::createKeyframeAtPosition(Point<int> position) {
123 int row_index = getRowFromY(position.y);
124 WavetableComponent* component = getComponentAtRow(row_index);
125 if (component == nullptr)
126 return;
127
128 int x_position = getPositionFromX(position.x - handleWidth() / 2);
129
130 WavetableKeyframe* new_keyframe = component->insertNewKeyframe(x_position);
131 std::unique_ptr<DraggableFrame> new_frame = std::make_unique<DraggableFrame>(!component->hasKeyframes());
132 int x = frame_width_ * new_keyframe->position();
133 new_frame->setBounds(x, row_index * handleWidth() + draw_vertical_offset_, handleWidth(), handleWidth());
134 addAndMakeVisible(new_frame.get());
135 frame_lookup_[new_keyframe] = std::move(new_frame);
136
137 selectFrame(new_keyframe);
138 for (Listener* listener : listeners_)
139 listener->positionsUpdated();
140
141 setFrameQuads();
142 setRowQuads();
143}
144
146 if (wavetable_creator_->numGroups()) {
147 WavetableGroup* group = wavetable_creator_->getGroup(0);
148 if (group->numComponents()) {
149 WavetableComponent* component = group->getComponent(0);
150 if (component->numFrames())
151 selectFrame(component->getFrameAt(0));
152 }
153 }
154}
155
156void WavetableOrganizer::selectFrame(WavetableKeyframe* keyframe) {
157 selectFrames({keyframe});
158
159 for (Listener* listener : listeners_)
160 listener->frameSelected(keyframe);
161}
162
163void WavetableOrganizer::selectFrames(std::vector<WavetableKeyframe*> keyframes) {
164 deselect();
165
166 for (WavetableKeyframe* keyframe : keyframes) {
167 if (frame_lookup_.count(keyframe)) {
168 DraggableFrame* frame = frame_lookup_[keyframe].get();
169 frame->select(true);
170 frame->toFront(false);
171 }
172
173 currently_selected_.push_back(keyframe);
174 }
175 setFrameQuads();
176}
177
178void WavetableOrganizer::positionSelectionBox(const MouseEvent& e) {
179 int position_end = getPositionFromX(e.x - handleWidth() / 2);
180 int position_start = getPositionFromX(mouse_down_position_.x - handleWidth() / 2);
181 int row_end = getRowFromY(e.y);
182 int row_start = getRowFromY(mouse_down_position_.y);
183
184 int position_left = std::min(position_start, position_end);
185 int position_right = std::max(position_start, position_end);
186 int row_top = std::min(row_start, row_end);
187 int row_bottom = std::max(row_start, row_end) + 1;
188
189 int x = std::roundf(position_left * frame_width_);
190 int y = std::roundf(row_top * handleWidth()) + draw_vertical_offset_ + 1;
191 int width = std::roundf(position_right * frame_width_) - x;
192 int height = std::roundf(row_bottom * handleWidth()) + draw_vertical_offset_ - y;
193
194 selection_quad_.setBounds(x + handleWidth() / 2 - 1, y, width + 2, height);
195}
196
197void WavetableOrganizer::setRowQuads() {
198 float height = getHeight();
199 int handle_width = handleWidth();
200 float row_height = handle_width * 2.0f / height;
201 float buffer = 2.0f / height;
202 float y = 1.0f - draw_vertical_offset_ * 2.0f / height - buffer;
203
204 int index = 0;
205 for (int g = 0; g < wavetable_creator_->numGroups(); ++g) {
206 int num_components = wavetable_creator_->getGroup(g)->numComponents();
207 for (int n = 0; n < num_components; ++n) {
208 active_rows_.setQuad(index, -1.0f, y - row_height + buffer, 2.0f, row_height - 2.0f * buffer);
209 y -= row_height;
210 index++;
211 }
212 y -= row_height;
213 }
214
215 active_rows_.setNumQuads(index);
216}
217
218void WavetableOrganizer::setFrameQuads() {
219 int num_selected = 0;
220 int num_unselected = 0;
221 float gl_width_scale = 2.0f / getWidth();
222 float gl_height_scale = 2.0f / getHeight();
223 for (auto& frame : frame_lookup_) {
224 if (frame.second == nullptr)
225 continue;
226
227 Rectangle<int> bounds = frame.second->getBounds();
228 float x = bounds.getX() * gl_width_scale - 1.0f;
229 float y = 1.0f - bounds.getBottom() * gl_height_scale;
230 float width = bounds.getWidth() * gl_width_scale;
231 float height = bounds.getHeight() * gl_height_scale;
232 if (frame.second->selected()) {
233 selected_frame_quads_.setQuad(num_selected, x, y, width, height);
234 num_selected++;
235 }
236 else {
237 unselected_frame_quads_.setQuad(num_unselected, x, y, width, height);
238 num_unselected++;
239 }
240 }
241
242 selected_frame_quads_.setNumQuads(num_selected);
243 unselected_frame_quads_.setNumQuads(num_unselected);
244}
245
247 return 1 + 2 * (int)(getHeight() * kHandleHeightPercent * 0.5f);
248}
249
251 std::vector<WavetableKeyframe*> selected = currently_selected_;
252 deselect();
253 for (WavetableKeyframe* keyframe : selected)
254 deleteKeyframe(keyframe);
255
256 setFrameQuads();
257}
258
260 createKeyframeAtPosition(menu_created_position_);
261}
262
263int WavetableOrganizer::getRowFromY(int y) {
264 return std::max(0.0f, (y - draw_vertical_offset_) * 1.0f / handleWidth());
265}
266
267int WavetableOrganizer::getPositionFromX(int x) {
268 return std::min(std::max(0, getUnclampedPositionFromX(x)), max_frames_ - 1);
269}
270
271int WavetableOrganizer::getUnclampedPositionFromX(int x) {
272 return x / frame_width_;
273}
274
275bool WavetableOrganizer::isSelected(WavetableKeyframe* keyframe) {
276 auto found = std::find(currently_selected_.begin(), currently_selected_.end(), keyframe);
277 return found != currently_selected_.end();
278}
279
280void WavetableOrganizer::clearVisibleFrames() {
281 frame_lookup_.clear();
282}
283
284void WavetableOrganizer::recreateVisibleFrames() {
285 clearVisibleFrames();
286
287 for (int g = 0; g < wavetable_creator_->numGroups(); ++g) {
288 WavetableGroup* group = wavetable_creator_->getGroup(g);
289
290 for (int i = 0; i < group->numComponents(); ++i) {
291 WavetableComponent* component = group->getComponent(i);
292
293 for (int f = 0; f < component->numFrames(); ++f) {
294 WavetableKeyframe* keyframe = component->getFrameAt(f);
295 std::unique_ptr<DraggableFrame> frame = std::make_unique<DraggableFrame>(!component->hasKeyframes());
296 addAndMakeVisible(frame.get());
297 frame_lookup_[keyframe] = std::move(frame);
298 }
299 }
300 }
301 repositionVisibleFrames();
302 if (currently_selected_.size() == 1)
303 selectFrame(currently_selected_[0]);
304 else if (currently_selected_.size() > 1)
305 selectFrames(currently_selected_);
306}
307
308void WavetableOrganizer::repositionVisibleFrames() {
309 frame_width_ = (getWidth() - handleWidth() + 1) / (max_frames_ - 1.0f);
310
311 int y = draw_vertical_offset_;
312 for (int g = 0; g < wavetable_creator_->numGroups(); ++g) {
313 WavetableGroup* group = wavetable_creator_->getGroup(g);
314
315 for (int i = 0; i < group->numComponents(); ++i) {
316 WavetableComponent* component = group->getComponent(i);
317
318 for (int f = 0; f < component->numFrames(); ++f) {
319 WavetableKeyframe* keyframe = component->getFrameAt(f);
320 int x = frame_width_ * keyframe->position();
321 DraggableFrame* frame = frame_lookup_[keyframe].get();
322 if (frame) {
323 if (frame->fullFrame())
324 frame->setBounds(0, y, getWidth(), handleWidth());
325 else
326 frame->setBounds(x, y, handleWidth(), handleWidth());
327 }
328 }
329
330 y += handleWidth();
331 }
332
333 y += handleWidth();
334 }
335 setFrameQuads();
336}
337
338WavetableComponent* WavetableOrganizer::getComponentAtRow(int row) {
339 int internal_row = row;
340 for (int g = 0; g < wavetable_creator_->numGroups() && internal_row >= 0; ++g) {
341 WavetableGroup* group = wavetable_creator_->getGroup(g);
342
343 if (internal_row < group->numComponents())
344 return group->getComponent(internal_row);
345
346 internal_row -= group->numComponents() + 1;
347 }
348 return nullptr;
349}
350
351WavetableKeyframe* WavetableOrganizer::getFrameAtMouseEvent(const MouseEvent& e) {
352 WavetableComponent* component = getComponentAtRow(getRowFromY(e.y));
353 if (component == nullptr)
354 return nullptr;
355
356 int position = getUnclampedPositionFromX(e.x - handleWidth());
357 if (!component->hasKeyframes())
358 return component->getFrameAtPosition(-1);
359
360 WavetableKeyframe* keyframe = component->getFrameAtPosition(position);
361 DraggableFrame* frame = frame_lookup_[keyframe].get();
362
363 if (frame && frame->isInside(e.x - frame->getX(), e.y - frame->getY()))
364 return keyframe;
365
366 return nullptr;
367}
368
369void WavetableOrganizer::mouseDown(const MouseEvent& e) {
370 // Handles selection, dragging initiation, and context menu starting positions.
371 Component::mouseDown(e);
372 mouse_down_position_ = e.position.toInt();
373
374 WavetableKeyframe* keyframe = getFrameAtMouseEvent(e);
375 if (keyframe) {
376 DraggableFrame* frame = frame_lookup_[keyframe].get();
377
378 mouse_mode_ = kDragging;
379 currently_dragged_ = keyframe;
380 dragged_start_x_ = frame->getX();
381
382 if (!isSelected(keyframe))
383 selectFrame(keyframe);
384 }
385}
386
387void WavetableOrganizer::mouseDrag(const MouseEvent& e) {
388 // Handles dragging frames or updating selection boxes.
389 Component::mouseDrag(e);
390
391 int x_movement = e.x - mouse_down_position_.x;
392 if (mouse_mode_ == kWaiting) {
393 if (x_movement) {
394 selection_quad_.setVisible(true);
395 mouse_mode_ = kSelecting;
396 positionSelectionBox(e);
397 }
398 }
399 else if (mouse_mode_ == kDragging) {
400 int new_frame_position = getUnclampedPositionFromX(dragged_start_x_ + x_movement);
401 int delta_frame_position = new_frame_position - currently_dragged_->position();
402
403 for (WavetableKeyframe* keyframe : currently_selected_) {
404 if (!keyframe->owner()->hasKeyframes())
405 continue;
406
407 DraggableFrame* frame = frame_lookup_[keyframe].get();
408 int frame_position = keyframe->position() + delta_frame_position;
409 int show_frame_position = vital::utils::iclamp(frame_position, 0, max_frames_ - 1);
410 int x = show_frame_position * frame_width_;
411
412 keyframe->setPosition(show_frame_position);
413 frame->setTopLeftPosition(x, frame->getY());
414 }
415
416 int clamped_position = getPositionFromX(dragged_start_x_ + x_movement);
417
418 for (Listener* listener : listeners_)
419 listener->frameDragged(currently_dragged_, clamped_position);
420
421 setFrameQuads();
422 }
423 else if (mouse_mode_ == kSelecting)
424 positionSelectionBox(e);
425}
426
427void WavetableOrganizer::mouseUp(const MouseEvent& e) {
428 // Finalizes drags, completes selection, and handles context menus.
429 Component::mouseUp(e);
430
431 if (mouse_mode_ == kWaiting)
432 deselect();
433 else if (mouse_mode_ == kSelecting) {
434 std::vector<WavetableKeyframe*> selection;
435 int row_start_index = getRowFromY(selection_quad_.getY());
436 int row_end_index = getRowFromY(selection_quad_.getBottom());
437
438 for (int row_index = row_start_index; row_index < row_end_index; ++row_index) {
439 WavetableComponent* component = getComponentAtRow(row_index);
440
441 if (component) {
442 int start_position = getPositionFromX(selection_quad_.getX() - handleWidth() / 2);
443 int end_position = getPositionFromX(selection_quad_.getRight() - handleWidth() / 2);
444 int start = component->getIndexFromPosition(start_position - 1);
445 int end = component->getIndexFromPosition(end_position);
446 for (int i = start; i < end; ++i)
447 selection.push_back(component->getFrameAt(i));
448 }
449 }
450 selectFrames(selection);
451 selection_quad_.setVisible(false);
452 }
453 else if (mouse_mode_ == kDragging) {
454 currently_dragged_ = nullptr;
455
456
457 int start_position = vital::WaveFrame::kWaveformSize - 1;
458 int end_position = 0;
459 for (WavetableKeyframe* keyframe : currently_selected_) {
460 if (!keyframe->owner()->hasKeyframes())
461 continue;
462
463 keyframe->setPosition(vital::utils::iclamp(keyframe->position(), 0, max_frames_ - 1));
464 keyframe->owner()->reposition(keyframe);
465 start_position = std::min(start_position, keyframe->position());
466 end_position = std::max(end_position, keyframe->position());
467 }
468
469 for (Listener* listener : listeners_)
470 listener->positionsUpdated();
471 }
472
473 if (e.mods.isPopupMenu()) {
474 int row_index = getRowFromY(e.y);
475 WavetableComponent* component = getComponentAtRow(row_index);
476 if (component == nullptr || !component->hasKeyframes()) {
477 mouse_mode_ = kWaiting;
478 return;
479 }
480
481 menu_created_position_ = e.getPosition();
482 PopupItems options;
483
484 if (!currently_selected_.empty()) {
485 if (currently_selected_.size() > 1)
486 options.addItem(kRemove, "Remove Keyframes");
487 else
488 options.addItem(kRemove, "Remove Keyframe");
489 }
490 else {
491 if (getComponentAtRow(row_index))
492 options.addItem(kCreate, "Create Keyframe");
493 }
494
495 showPopupSelector(this, e.getPosition(), options, [=](int selection) { organizerCallback(selection, this); });
496 }
497
498 mouse_mode_ = kWaiting;
499}
500
501void WavetableOrganizer::mouseDoubleClick(const MouseEvent& e) {
502 // Double-click can quickly add or remove a keyframe at a position.
503 int row_index = getRowFromY(e.y);
504 WavetableComponent* component = getComponentAtRow(row_index);
505 if (component == nullptr || !component->hasKeyframes())
506 return;
507
508 WavetableKeyframe* keyframe = getFrameAtMouseEvent(e);
509 if (keyframe) {
510 deselect();
511 deleteKeyframe(keyframe);
512 setFrameQuads();
513 }
514 else
515 createKeyframeAtPosition(e.getPosition());
516}
517
519 // Updates a visual marker to show where the playhead is currently located.
520 playhead_position_ = position;
521 int x = handleWidth() / 2 + position * frame_width_;
522 playhead_quad_.setBounds(x, 0, 1, getHeight());
523}
524
526 // Ensures the organizer updates selection if a component is removed.
527 std::vector<WavetableKeyframe*> new_selected;
528 for (WavetableKeyframe* selected : currently_selected_) {
529 if (selected->owner() != component)
530 new_selected.push_back(selected);
531 }
532 if (!new_selected.empty())
533 selectFrames(new_selected);
534 else
535 deselect();
536}
537
539 // Adds frames for the new component and selects its first frame if any.
540 recreateVisibleFrames();
541
542 if (component->numFrames())
543 selectFrame(component->getFrameAt(0));
544}
545
547 // Adjusts vertical offset after scrolling, repositioning frames accordingly.
548 draw_vertical_offset_ = offset;
549 repositionVisibleFrames();
550 setRowQuads();
551}
A visual frame representing a single wavetable keyframe, which can be dragged by the user.
Definition wavetable_organizer.h:17
bool fullFrame() const
Determines if the frame represents a full row or a small diamond.
Definition wavetable_organizer.h:41
force_inline bool selected() const
Checks if the frame is currently selected.
Definition wavetable_organizer.h:53
force_inline void select(bool selected)
Sets the frame's selection state.
Definition wavetable_organizer.h:47
bool isInside(int x, int y)
Checks if a point is inside the frame's clickable area.
Definition wavetable_organizer.cpp:16
void setThickness(float thickness, bool reset=false)
Sets the thickness used by some shaders and can reset to this thickness.
Definition open_gl_multi_quad.h:338
void setQuad(int i, float x, float y, float w, float h)
Sets the position and size of a quad in normalized device space.
Definition open_gl_multi_quad.h:313
void setTargetComponent(Component *target_component)
Sets a target component to help position the quads.
Definition open_gl_multi_quad.h:358
void setNumQuads(int num_quads)
Sets how many quads will actually be drawn (up to max_quads).
Definition open_gl_multi_quad.h:92
force_inline void setAltColor(Colour color)
Sets an alternate color, often used by custom shaders.
Definition open_gl_multi_quad.h:118
force_inline void setColor(Colour color)
Sets the base color for the quads.
Definition open_gl_multi_quad.h:102
Manages and provides access to vertex and fragment shaders used by the OpenGL rendering pipeline.
Definition shaders.h:19
@ kBackground
Definition skin.h:128
@ kWidgetPrimaryDisabled
Definition skin.h:167
@ kWidgetPrimary1
Definition skin.h:165
@ kLightenScreen
Definition skin.h:141
Base class for all synthesizer sections, providing UI layout, painting, and interaction logic.
Definition synth_section.h:193
void showPopupSelector(Component *source, Point< int > position, const PopupItems &options, std::function< void(int)> callback, std::function< void()> cancel={ })
Shows a popup selector with options.
Definition synth_section.cpp:119
void addOpenGlComponent(OpenGlComponent *open_gl_component, bool to_beginning=false)
Definition synth_section.cpp:489
A base class representing a component in a wavetable synthesis chain.
Definition wavetable_component.h:32
int numFrames() const
Gets the number of keyframes.
Definition wavetable_component.h:155
WavetableKeyframe * getFrameAt(int index) const
Gets a keyframe by index.
Definition wavetable_component.h:177
WavetableKeyframe * insertNewKeyframe(int position)
Inserts a new keyframe at the given position, creating and sorting it into the array.
Definition wavetable_component.cpp:8
WavetableKeyframe * getFrameAtPosition(int position)
Gets a keyframe by position if one matches exactly, or nullptr otherwise.
Definition wavetable_component.cpp:119
int getIndexFromPosition(int position) const
Finds the insertion index for a given position to keep keyframes sorted.
Definition wavetable_component.cpp:107
void remove(WavetableKeyframe *keyframe)
Removes a specific keyframe from the component.
Definition wavetable_component.cpp:31
virtual bool hasKeyframes()
Indicates whether this component relies on multiple keyframes.
Definition wavetable_component.h:108
void reposition(WavetableKeyframe *keyframe)
Repositions a keyframe in the keyframe list after its position changed.
Definition wavetable_component.cpp:21
A UI component that lists and manages the wavetable sources and modifiers.
Definition wavetable_component_list.h:65
A class responsible for creating complete wavetables from groups of wavetable components.
Definition wavetable_creator.h:27
WavetableGroup * getGroup(int index) const
Retrieves a WavetableGroup by index.
Definition wavetable_creator.h:71
int numGroups() const
Gets the total number of WavetableGroups.
Definition wavetable_creator.h:63
A class representing a group of WavetableComponents combined to form part of a wavetable.
Definition wavetable_group.h:24
WavetableComponent * getComponent(int index) const
Retrieves a component by index.
Definition wavetable_group.h:92
int numComponents() const
Gets the number of WavetableComponents in this group.
Definition wavetable_group.h:84
Represents a single state of a waveform at a specific position in a wavetable.
Definition wavetable_keyframe.h:35
int position() const
Gets the wavetable frame position of this keyframe.
Definition wavetable_keyframe.h:81
WavetableComponent * owner()
Gets the WavetableComponent that owns this keyframe.
Definition wavetable_keyframe.h:152
void setPosition(int position)
Sets the frame position of this keyframe.
Definition wavetable_keyframe.h:88
Interface for objects that need to respond to organizer events.
Definition wavetable_organizer.h:109
Manages the display and interaction of wavetable keyframes and groups on a timeline.
Definition wavetable_organizer.h:72
void componentsScrolled(int offset) override
Called when components are scrolled via the WavetableComponentList. Adjusts vertical offset of rows.
Definition wavetable_organizer.cpp:546
void createKeyframeAtMenu()
Shows a menu to create a keyframe at a specific position.
Definition wavetable_organizer.cpp:259
void mouseDrag(const MouseEvent &event) override
Called as the mouse is dragged. Used for dragging frames or adjusting selection rectangles.
Definition wavetable_organizer.cpp:387
void resized() override
Handles resizing and rearranges the displayed frames and rows.
Definition wavetable_organizer.cpp:91
void selectDefaultFrame()
Selects a default frame (usually the first one) if available.
Definition wavetable_organizer.cpp:145
void paintBackground(Graphics &g) override
Paints the background, including a grid and handle areas.
Definition wavetable_organizer.cpp:59
@ kDragging
Definition wavetable_organizer.h:97
@ kSelecting
Definition wavetable_organizer.h:96
@ kWaiting
Definition wavetable_organizer.h:95
static constexpr int kDrawSkipLarge
Definition wavetable_organizer.h:77
void componentAdded(WavetableComponent *component) override
Called when a component is added to the wavetable. Updates frame display accordingly.
Definition wavetable_organizer.cpp:538
static constexpr int kDrawSkip
Definition wavetable_organizer.h:76
WavetableOrganizer(WavetableCreator *wavetable_creator, int max_frames)
Constructs a WavetableOrganizer.
Definition wavetable_organizer.cpp:22
virtual ~WavetableOrganizer()
Definition wavetable_organizer.cpp:53
void playheadMoved(int position) override
Called when the playhead moves, updates the visual playhead position.
Definition wavetable_organizer.cpp:518
void mouseUp(const MouseEvent &event) override
Called when the mouse button is released. Finalizes drags, completes selections, or handles context m...
Definition wavetable_organizer.cpp:427
void deleteSelectedKeyframes()
Deletes all currently selected keyframes.
Definition wavetable_organizer.cpp:250
@ kCreate
Definition wavetable_organizer.h:86
@ kRemove
Definition wavetable_organizer.h:87
static constexpr float kHandleHeightPercent
Fraction of the total height used for the handle area.
Definition wavetable_organizer.h:75
void componentRemoved(WavetableComponent *component) override
Called when a component is removed from the wavetable. Ensures selected frames are updated accordingl...
Definition wavetable_organizer.cpp:525
void mouseDoubleClick(const MouseEvent &event) override
Called on a double-click. Used for quickly adding or removing keyframes.
Definition wavetable_organizer.cpp:501
void mouseDown(const MouseEvent &event) override
Called when the mouse button is pressed. Used for selecting frames, starting drags,...
Definition wavetable_organizer.cpp:369
int handleWidth() const
Gets the width of the handle area for rows.
Definition wavetable_organizer.cpp:246
static constexpr int kWaveformSize
The size of the waveform (number of samples per frame).
Definition wave_frame.h:21
force_inline int iclamp(int value, int min_val, int max_val)
Clamps an integer between [min_val, max_val].
Definition utils.h:250
A hierarchical structure of popup menu items for a selector component.
Definition synth_section.h:29
void addItem(int sub_id, const std::string &sub_name, bool sub_selected=false)
Adds a new item as a submenu entry.
Definition synth_section.h:51