Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pdvg.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@
#include "src/Slider.hpp"
#include "src/Subpatch.hpp"
#include "src/Knob.hpp"
#include "src/Popmenu.hpp"
126 changes: 124 additions & 2 deletions src/ExtraEventHandlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ struct PDToggleEventHandler::PrivateData
widget(w),
callback(other->callback),
isDown(other->isDown)

{
}

Expand Down Expand Up @@ -682,7 +681,6 @@ struct PDRadioEventHandler::PrivateData

bool mouseEvent(const Widget::MouseEvent &ev)
{

if (ev.button != 1)
return false;

Expand Down Expand Up @@ -1655,5 +1653,129 @@ bool PDKnobEventHandler::scrollEvent(const Widget::ScrollEvent &ev)
return pData->scrollEvent(ev);
}
// end knob
// --------------------------------------------------------------------------------------------------------------------

// begin popmenu
struct PDPopmenuEventHandler::PrivateData
{
PDPopmenuEventHandler *const self;
SubWidget *const widget;
PDPopmenuEventHandler::Callback *callback;

uint length;
uint value;

PrivateData(PDPopmenuEventHandler *const s, SubWidget *const w)
: self(s),
widget(w),
callback(nullptr),
length(0),
value(0)
{
}

PrivateData(PDPopmenuEventHandler *const s, SubWidget *const w, PrivateData *const other)
: self(s),
widget(w),
callback(other->callback),
length(other->length),
value(other->value)
{
}

void assignFrom(PrivateData *const other)
{
callback = other->callback;
length = other->length;
value = other->value;
}

bool mouseEvent(const Widget::MouseEvent &ev)
{
if (ev.button != 1)
return false;

PDWidget* pdWidget = dynamic_cast<PDWidget*>(widget);
if (ev.press && pdWidget->contains(ev.pos))
{
return true;
}
return false;
}

bool motionEvent(const Widget::MotionEvent &ev)
{
return false;
}

bool setValue(const uint value2, const bool sendCallback)
{
if (value != value2)
{
value = value2;
if (sendCallback && callback != nullptr)
{
try
{
callback->popmenuClicked(widget, value);
}
DISTRHO_SAFE_EXCEPTION("PDPopmenuEventHandler::setValue");
}
return true;
}
return false;
}
};

// --------------------------------------------------------------------------------------------------------------------

PDPopmenuEventHandler::PDPopmenuEventHandler(SubWidget *const self)
: pData(new PrivateData(this, self)) {}

PDPopmenuEventHandler::PDPopmenuEventHandler(SubWidget *const self, const PDPopmenuEventHandler &other)
: pData(new PrivateData(this, self, other.pData)) {}

PDPopmenuEventHandler &PDPopmenuEventHandler::operator=(const PDPopmenuEventHandler &other)
{
pData->assignFrom(other.pData);
return *this;
}

PDPopmenuEventHandler::~PDPopmenuEventHandler()
{
delete pData;
}

uint PDPopmenuEventHandler::getValue() const noexcept
{
return pData->value;
}

bool PDPopmenuEventHandler::setValue(const int value, const bool sendCallback) noexcept
{
return pData->setValue(static_cast<uint>(value), sendCallback);
}

void PDPopmenuEventHandler::setLength(int length) noexcept
{
pData->length = length;
}

void PDPopmenuEventHandler::setCallback(Callback *const callback) noexcept
{
pData->callback = callback;
}

bool PDPopmenuEventHandler::mouseEvent(const Widget::MouseEvent &ev)
{
return pData->mouseEvent(ev);
}

bool PDPopmenuEventHandler::motionEvent(const Widget::MotionEvent &ev)
{
return pData->motionEvent(ev);
}

// end popmenu

END_NAMESPACE_DGL
33 changes: 33 additions & 0 deletions src/ExtraEventHandlers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,39 @@ class PDKnobEventHandler
DISTRHO_LEAK_DETECTOR(PDKnobEventHandler)
};

class PDPopmenuEventHandler
{
public:
class Callback
{
public:
virtual ~Callback() {}
virtual void popmenuClicked(SubWidget *widget, uint index) = 0;
};

explicit PDPopmenuEventHandler(SubWidget *self);
explicit PDPopmenuEventHandler(SubWidget *self, const PDPopmenuEventHandler &other);
PDPopmenuEventHandler &operator=(const PDPopmenuEventHandler &other);
~PDPopmenuEventHandler();

uint getValue() const noexcept;

virtual bool setValue(int value, bool sendCallback = false) noexcept;

void setLength(int length) noexcept;
void setCallback(Callback *callback) noexcept;

bool mouseEvent(const Widget::MouseEvent &ev);
bool motionEvent(const Widget::MotionEvent &ev);

protected:
private:
struct PrivateData;
PrivateData *const pData;

DISTRHO_LEAK_DETECTOR(PDPopmenuEventHandler)
};

// --------------------------------------------------------------------------------------------------------------------

END_NAMESPACE_DGL
103 changes: 103 additions & 0 deletions src/Popmenu.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* Copyright (C) 2026 Wasted Audio
* SPDX-License-Identifier: ISC
*/

#include "nanovg.h"

#include "Common.hpp"
#include "Popmenu.hpp"
#include "Fonts/InterRegular.hpp"

START_NAMESPACE_DISTRHO

PDPopmenu::PDPopmenu(NanoSubWidget* parent, PDPopmenuEventHandler::Callback* cb)
: PDWidget(parent),
PDPopmenuEventHandler(this)
{
PDPopmenuEventHandler::setCallback(cb);

using namespace InterRegular;
NanoVG:FontId interId = createFontFromMemory("inter", (const uchar *)InterRegularData, InterRegularDataSize, 0);
fFontId = interId;
}

void PDPopmenu::renderText(NVGcontext* nvg, DGL::Rectangle<float> bounds)
{
beginPath();

auto fAlign = NVG_ALIGN_TOP;
auto value = (int) getValue();
auto fText = options[value].c_str();

fontFaceId(fFontId);
fontSize(fFontSize * 1.5f);
fillColor(Colors::cnvTextColor);
textAlign(fAlign);
text(bounds.getX(), bounds.getY(), fText, NULL);
closePath();
}

void PDPopmenu::onNanoDisplay()
{
const float scaleFactor = getTopLevelWidget()->getScaleFactor();
const DGL::Rectangle<float> b(0.0f, 0.0f, getWidth(), getHeight());

NVGcontext* nvg = getContext();

drawRoundedRect(nvg, b.getX(), b.getY(), b.getWidth(), b.getHeight(), bgColor, Colors::outColor, Corners::objectCornerRadius);

auto textBounds = translateRectangle(reduceRectangle(b, 2 * scaleFactor), 2 * scaleFactor, 0);
renderText(nvg, textBounds);

auto const triangleBounds = resizeCentered(removeFromRight(b, 20), 20, std::min((int)b.getHeight(), 12));

auto const triangleBoundsCenterX = triangleBounds.getX() + triangleBounds.getWidth() / 2.0f;
auto const triangleBoundsBottom = triangleBounds.getY() + triangleBounds.getHeight();

nvgStrokeColor(nvg, fgColor);
nvgBeginPath(nvg);
nvgMoveTo(nvg, triangleBoundsCenterX - 3 * scaleFactor, triangleBounds.getY() + 3 * scaleFactor);
nvgLineTo(nvg, triangleBoundsCenterX, triangleBounds.getY());
nvgLineTo(nvg, triangleBoundsCenterX + 3 * scaleFactor, triangleBounds.getY() + 3 * scaleFactor);
nvgStroke(nvg);

nvgBeginPath(nvg);
nvgMoveTo(nvg, triangleBoundsCenterX - 3 * scaleFactor, triangleBoundsBottom - 3 * scaleFactor);
nvgLineTo(nvg, triangleBoundsCenterX, triangleBoundsBottom);
nvgLineTo(nvg, triangleBoundsCenterX + 3 * scaleFactor, triangleBoundsBottom - 3 * scaleFactor);
nvgStroke(nvg);
}

bool PDPopmenu::onMouse(const MouseEvent &ev)
{
return PDPopmenuEventHandler::mouseEvent(ev);
}

bool PDPopmenu::onMotion(const MotionEvent &ev)
{
return PDPopmenuEventHandler::motionEvent(ev);
}

void PDPopmenu::setColors(NVGcolor bgColor, NVGcolor fgColor)
{
this->bgColor = bgColor;
this->fgColor = fgColor;
}

void PDPopmenu::setNoSelectLabel(std::string label)
{
this->noSelectLabel = label;
}

void PDPopmenu::setOptions(std::vector<std::string> options)
{
this->options = options;
}

void PDPopmenu::setFontSize(int fontSize)
{
this->fFontSize = fontSize;
}

END_NAMESPACE_DISTRHO
46 changes: 46 additions & 0 deletions src/Popmenu.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright (C) 2026 Wasted Audio
* SPDX-License-Identifier: ISC
*/

#pragma once

#include "NanoVG.hpp"
#include "nanovg.h"

#include "ExtraEventHandlers.hpp"
#include "Label.hpp"
#include "PDWidget.hpp"

START_NAMESPACE_DISTRHO

class PDPopmenu : public PDWidget,
public PDPopmenuEventHandler
{
public:
explicit PDPopmenu(NanoSubWidget* parent, PDPopmenuEventHandler::Callback* cb);

void setColors(NVGcolor bgColor, NVGcolor fgColor);
void setNoSelectLabel(std::string label);
void setOptions(std::vector<std::string> options);
void setFontSize(int size);

protected:
bool onMouse(const MouseEvent &ev) override;
bool onMotion(const MotionEvent &ev) override;
void renderText(NVGcontext* nvg, DGL::Rectangle<float> bounds);
void onNanoDisplay() override;

private:
NVGcolor fgColor;
NVGcolor bgColor;
int fFontSize;
std::string noSelectLabel;
std::vector<std::string> options;

NanoVG::FontId fFontId;

DISTRHO_LEAK_DETECTOR(PDPopmenu)
};

END_NAMESPACE_DISTRHO