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;
24 unselected_frame_quads_(kMaxKeyframes,
Shaders::kDiamondFragment),
25 selected_frame_quads_(kMaxKeyframes,
Shaders::kDiamondFragment),
27 selection_quad_(
Shaders::kColorFragment), playhead_quad_(
Shaders::kColorFragment) {
41 wavetable_creator_ = wavetable_creator;
42 max_frames_ = max_frames;
44 currently_dragged_ =
nullptr;
47 playhead_position_ = 0;
48 draw_vertical_offset_ = 0;
50 recreateVisibleFrames();
63 g.setColour(lighten.withMultipliedAlpha(0.5f));
65 g.fillRect(0, 0, half_handle, getHeight());
66 g.fillRect(getWidth() - half_handle, 0, half_handle, getHeight());
71 g.setColour(lighten.withMultipliedAlpha(0.5f));
73 int x = i * frame_width_ + half_handle;
74 g.fillRect(x, 0, 1, getHeight());
81 unselected_frame_quads_.
setColor(edge);
83 selected_frame_quads_.
setColor(edge);
92 repositionVisibleFrames();
99void WavetableOrganizer::deselect() {
101 if (frame_lookup_.count(keyframe))
102 frame_lookup_[keyframe]->select(
false);
104 currently_selected_.clear();
106 for (Listener* listener : listeners_)
107 listener->frameSelected(
nullptr);
114 frame_lookup_.erase(keyframe);
116 for (Listener* listener : listeners_)
117 listener->positionsUpdated();
122void WavetableOrganizer::createKeyframeAtPosition(Point<int> position) {
123 int row_index = getRowFromY(position.y);
125 if (component ==
nullptr)
128 int x_position = getPositionFromX(position.x -
handleWidth() / 2);
131 std::unique_ptr<DraggableFrame> new_frame = std::make_unique<DraggableFrame>(!component->
hasKeyframes());
132 int x = frame_width_ * new_keyframe->
position();
134 addAndMakeVisible(new_frame.get());
135 frame_lookup_[new_keyframe] = std::move(new_frame);
137 selectFrame(new_keyframe);
138 for (Listener* listener : listeners_)
139 listener->positionsUpdated();
157 selectFrames({keyframe});
159 for (Listener* listener : listeners_)
160 listener->frameSelected(keyframe);
163void WavetableOrganizer::selectFrames(std::vector<WavetableKeyframe*> keyframes) {
167 if (frame_lookup_.count(keyframe)) {
170 frame->toFront(
false);
173 currently_selected_.push_back(keyframe);
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);
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;
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;
194 selection_quad_.setBounds(x +
handleWidth() / 2 - 1, y, width + 2, height);
197void WavetableOrganizer::setRowQuads() {
198 float height = getHeight();
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;
205 for (
int g = 0; g < wavetable_creator_->
numGroups(); ++g) {
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);
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)
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;
233 selected_frame_quads_.
setQuad(num_selected, x, y, width, height);
237 unselected_frame_quads_.
setQuad(num_unselected, x, y, width, height);
243 unselected_frame_quads_.
setNumQuads(num_unselected);
251 std::vector<WavetableKeyframe*> selected = currently_selected_;
254 deleteKeyframe(keyframe);
260 createKeyframeAtPosition(menu_created_position_);
263int WavetableOrganizer::getRowFromY(
int y) {
264 return std::max(0.0f, (y - draw_vertical_offset_) * 1.0f /
handleWidth());
267int WavetableOrganizer::getPositionFromX(
int x) {
268 return std::min(std::max(0, getUnclampedPositionFromX(x)), max_frames_ - 1);
271int WavetableOrganizer::getUnclampedPositionFromX(
int x) {
272 return x / frame_width_;
276 auto found = std::find(currently_selected_.begin(), currently_selected_.end(), keyframe);
277 return found != currently_selected_.end();
280void WavetableOrganizer::clearVisibleFrames() {
281 frame_lookup_.clear();
284void WavetableOrganizer::recreateVisibleFrames() {
285 clearVisibleFrames();
287 for (
int g = 0; g < wavetable_creator_->
numGroups(); ++g) {
293 for (
int f = 0; f < component->
numFrames(); ++f) {
295 std::unique_ptr<DraggableFrame> frame = std::make_unique<DraggableFrame>(!component->
hasKeyframes());
296 addAndMakeVisible(frame.get());
297 frame_lookup_[keyframe] = std::move(frame);
301 repositionVisibleFrames();
302 if (currently_selected_.size() == 1)
303 selectFrame(currently_selected_[0]);
304 else if (currently_selected_.size() > 1)
305 selectFrames(currently_selected_);
308void WavetableOrganizer::repositionVisibleFrames() {
309 frame_width_ = (getWidth() -
handleWidth() + 1) / (max_frames_ - 1.0f);
311 int y = draw_vertical_offset_;
312 for (
int g = 0; g < wavetable_creator_->
numGroups(); ++g) {
318 for (
int f = 0; f < component->
numFrames(); ++f) {
320 int x = frame_width_ * keyframe->
position();
339 int internal_row = row;
340 for (
int g = 0; g < wavetable_creator_->
numGroups() && internal_row >= 0; ++g) {
343 if (internal_row < group->numComponents())
351WavetableKeyframe* WavetableOrganizer::getFrameAtMouseEvent(
const MouseEvent& e) {
353 if (component ==
nullptr)
356 int position = getUnclampedPositionFromX(e.x -
handleWidth());
363 if (frame && frame->
isInside(e.x - frame->getX(), e.y - frame->getY()))
371 Component::mouseDown(e);
372 mouse_down_position_ = e.position.toInt();
379 currently_dragged_ = keyframe;
380 dragged_start_x_ = frame->getX();
382 if (!isSelected(keyframe))
383 selectFrame(keyframe);
389 Component::mouseDrag(e);
391 int x_movement = e.x - mouse_down_position_.x;
394 selection_quad_.setVisible(
true);
396 positionSelectionBox(e);
400 int new_frame_position = getUnclampedPositionFromX(dragged_start_x_ + x_movement);
401 int delta_frame_position = new_frame_position - currently_dragged_->
position();
408 int frame_position = keyframe->
position() + delta_frame_position;
410 int x = show_frame_position * frame_width_;
413 frame->setTopLeftPosition(x, frame->getY());
416 int clamped_position = getPositionFromX(dragged_start_x_ + x_movement);
418 for (
Listener* listener : listeners_)
419 listener->frameDragged(currently_dragged_, clamped_position);
424 positionSelectionBox(e);
429 Component::mouseUp(e);
434 std::vector<WavetableKeyframe*> selection;
435 int row_start_index = getRowFromY(selection_quad_.getY());
436 int row_end_index = getRowFromY(selection_quad_.getBottom());
438 for (
int row_index = row_start_index; row_index < row_end_index; ++row_index) {
442 int start_position = getPositionFromX(selection_quad_.getX() -
handleWidth() / 2);
443 int end_position = getPositionFromX(selection_quad_.getRight() -
handleWidth() / 2);
446 for (
int i = start; i < end; ++i)
447 selection.push_back(component->
getFrameAt(i));
450 selectFrames(selection);
451 selection_quad_.setVisible(
false);
454 currently_dragged_ =
nullptr;
458 int end_position = 0;
465 start_position = std::min(start_position, keyframe->
position());
466 end_position = std::max(end_position, keyframe->
position());
469 for (
Listener* listener : listeners_)
470 listener->positionsUpdated();
473 if (e.mods.isPopupMenu()) {
474 int row_index = getRowFromY(e.y);
476 if (component ==
nullptr || !component->
hasKeyframes()) {
481 menu_created_position_ = e.getPosition();
484 if (!currently_selected_.empty()) {
485 if (currently_selected_.size() > 1)
491 if (getComponentAtRow(row_index))
495 showPopupSelector(
this, e.getPosition(), options, [=](
int selection) { organizerCallback(selection, this); });
503 int row_index = getRowFromY(e.y);
511 deleteKeyframe(keyframe);
515 createKeyframeAtPosition(e.getPosition());
520 playhead_position_ = position;
521 int x =
handleWidth() / 2 + position * frame_width_;
522 playhead_quad_.setBounds(x, 0, 1, getHeight());
527 std::vector<WavetableKeyframe*> new_selected;
529 if (selected->owner() != component)
530 new_selected.push_back(selected);
532 if (!new_selected.empty())
533 selectFrames(new_selected);
540 recreateVisibleFrames();
548 draw_vertical_offset_ = offset;
549 repositionVisibleFrames();
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