Skip to content

Commit 5f686c4

Browse files
committed
(WIP) UI: Implement Dear ImGui UI part
1 parent 344d84f commit 5f686c4

File tree

5 files changed

+233
-7
lines changed

5 files changed

+233
-7
lines changed

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ add_subdirectory (dpf)
1212

1313
include_directories (
1414
src/
15+
dpf-widgets/opengl/
1516
)
1617

1718
set (SRC_BACKEND
@@ -42,5 +43,7 @@ dpf_add_plugin (${PROJECT_NAME}
4243
src/CetoneUIHelper.cpp
4344
src/Images/CetoneArtwork.cpp
4445
src/Fonts/CetoneFonts.cpp
46+
dpf-widgets/opengl/DearImGui.cpp
47+
src/Widgets/ImGui_UI.cpp
4548
)
4649

src/CetoneUI.cpp

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,17 @@ CCetoneUI::CCetoneUI()
107107
_createSwitchButton(fBtnGlideState, pPortaMode, 764, 150);
108108

109109
_createSwitchButton(fBtnLFOTrigger, pLfo1Trig, 677, 260);
110+
111+
/* ImGui instance (popup menus, subwindows, etc.) */
112+
fImGuiInstance = new ImGuiUI(getTopLevelWidget(), this);
113+
114+
/* "About" button (by clicking the plugin logo) */
115+
_createHiddenButton(fBtnAbout, BTN_ABOUT, Size<uint>(118, 25), Point<int>(0, 0));
116+
117+
/* Popup menu button on params which has constant value sets */
118+
_createHiddenButton(fBtnOsc1Waveform, pOsc1Wave, Size<uint>(45, 10 + 2), Point<int>(10 + 48 * 2, 200 + 2));
119+
_createHiddenButton(fBtnOsc2Waveform, pOsc2Wave, Size<uint>(45, 10 + 2), Point<int>(262 + 48 * 2, 200 + 2));
120+
_createHiddenButton(fBtnOsc3Waveform, pOsc3Wave, Size<uint>(45, 10 + 2), Point<int>(514 + 48 * 2, 200 + 2));
110121
}
111122

112123
void CCetoneUI::parameterChanged(uint32_t index, float value)
@@ -349,14 +360,34 @@ void CCetoneUI::parameterChanged(uint32_t index, float value)
349360

350361
void CCetoneUI::imageButtonClicked(ImageButton* button, int)
351362
{
352-
#if 0
353-
switch (button->getId()) {
354-
case BTN_PANIC: {
355-
panic();
356-
break;
357-
}
363+
DISTRHO_SAFE_ASSERT_RETURN(fImGuiInstance, )
364+
365+
switch (button->getId())
366+
{
367+
case BTN_ABOUT:
368+
{
369+
fImGuiInstance->isAboutWindowOpen = !fImGuiInstance->isAboutWindowOpen;
370+
break;
371+
}
372+
case pOsc1Wave:
373+
{
374+
fImGuiInstance->menuPos = ImVec2(fBtnOsc1Waveform->getAbsolutePos().getX(), fBtnOsc1Waveform->getAbsolutePos().getY() + fBtnOsc1Waveform->getHeight());
375+
fImGuiInstance->requestMenuId = pOsc1Wave;
376+
break;
377+
}
378+
case pOsc2Wave:
379+
{
380+
fImGuiInstance->menuPos = ImVec2(fBtnOsc2Waveform->getAbsolutePos().getX(), fBtnOsc2Waveform->getAbsolutePos().getY() + fBtnOsc2Waveform->getHeight());
381+
fImGuiInstance->requestMenuId = pOsc2Wave;
382+
break;
383+
}
384+
case pOsc3Wave:
385+
{
386+
fImGuiInstance->menuPos = ImVec2(fBtnOsc3Waveform->getAbsolutePos().getX(), fBtnOsc3Waveform->getAbsolutePos().getY() + fBtnOsc3Waveform->getHeight());
387+
fImGuiInstance->requestMenuId = pOsc3Wave;
388+
break;
389+
}
358390
}
359-
#endif
360391
}
361392

362393
void CCetoneUI::imageSwitchClicked(ImageSwitch* button, bool down)

src/CetoneUI.hpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#include "ImageWidgets.hpp"
55
#include "NanoVG.hpp"
66

7+
#include "Widgets/ImGui_UI.hpp"
8+
79
using DGL_NAMESPACE::ImageAboutWindow;
810
using DGL_NAMESPACE::ImageButton;
911
using DGL_NAMESPACE::ImageKnob;
@@ -55,6 +57,12 @@ class CCetoneUI : public DISTRHO::UI,
5557
NanoVG fNanoText;
5658
char fLabelBuffer[32 + 1];
5759

60+
// -------------------------------------------------------------------
61+
// Dear ImGui Instance
62+
63+
ScopedPointer<ImGuiUI> fImGuiInstance;
64+
friend class ImGuiUI;
65+
5866
// -------------------------------------------------------------------
5967
// Image resources
6068

@@ -93,6 +101,13 @@ class CCetoneUI : public DISTRHO::UI,
93101
ScopedPointer<ImageSwitch> fBtnGlideState;
94102
ScopedPointer<ImageSwitch> fBtnLFOTrigger;
95103

104+
// -------------------------------------------------------------------
105+
// Buttons
106+
107+
ScopedPointer<ImageButton> fBtnAbout;
108+
109+
ScopedPointer<ImageButton> fBtnOsc1Waveform, fBtnOsc2Waveform, fBtnOsc3Waveform;
110+
96111
// -------------------------------------------------------------------
97112
// Helpers
98113

@@ -127,5 +142,6 @@ class CCetoneUI : public DISTRHO::UI,
127142
// Button IDs
128143

129144
constexpr uint BTN_PANIC = d_cconst('p', 'n', 'i', 'c');
145+
constexpr uint BTN_ABOUT = d_cconst('a', 'b', 't', '.');
130146

131147
// -----------------------------------------------------------------------

src/Widgets/ImGui_UI.cpp

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
#include "ImGui_UI.hpp"
2+
#include "DistrhoPluginInfo.h"
3+
4+
#include "Structures.h"
5+
#include "Defines.h"
6+
7+
#include "CetoneUI.hpp" // For class CCetoneUI
8+
9+
void ImGuiUI::onImGuiDisplay()
10+
{
11+
double scaleFactor = getScaleFactor() * userScaling;
12+
const double initialSize = 800 * scaleFactor;
13+
14+
//
15+
// "About" Window
16+
//
17+
{
18+
ImGui::SetNextWindowPos(ImVec2(initialSize / 4, initialSize / 16), ImGuiCond_Once);
19+
ImGui::SetNextWindowSize(ImVec2(600, 230), ImGuiCond_Once);
20+
21+
if (isAboutWindowOpen)
22+
{
23+
ImGui::Begin("About " DISTRHO_PLUGIN_NAME, &isAboutWindowOpen, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize);
24+
{
25+
ImGui::SeparatorText(DISTRHO_PLUGIN_NAME);
26+
ImGui::Text("Light-weight monophonic analogue-style synthesizer, by Neotec Software.\n");
27+
28+
ImGui::SeparatorText("Authors");
29+
ImGui::BulletText("René 'Neotec' Jeschke - Original developer");
30+
ImGui::BulletText("AnClark Liu <[email protected]> - Ported to DPF, Further developments");
31+
32+
ImGui::SeparatorText("License");
33+
ImGui::BulletText("This project is licensed under GNU General Public License, version 3.");
34+
35+
ImGui::Text("\n\n");
36+
ImGui::Dummy(ImVec2(490, 0));
37+
ImGui::SameLine();
38+
if (ImGui::Button("OK", ImVec2(80, 0)))
39+
isAboutWindowOpen = false;
40+
}
41+
ImGui::End();
42+
}
43+
}
44+
45+
//
46+
// Handle menu opening requests
47+
//
48+
// Here, variable `requestTestMenuOpen` acts as an "event flag" to request ImGui to show the menu.
49+
//
50+
// Dear ImGui has its own mechanism to show popup menus, which does not require a flag to control its exisitance.
51+
// This is quite different from window (ImGui::Begin()).
52+
// So just call this function once, your popup will stick on the screen unless you do some operations.
53+
//
54+
switch (requestMenuId)
55+
{
56+
case pOsc1Wave:
57+
ImGui::OpenPopup("menu_osc1_wave");
58+
requestMenuId = 0;
59+
break;
60+
case pOsc2Wave:
61+
ImGui::OpenPopup("menu_osc2_wave");
62+
requestMenuId = 0;
63+
break;
64+
case pOsc3Wave:
65+
ImGui::OpenPopup("menu_osc3_wave");
66+
requestMenuId = 0;
67+
break;
68+
69+
default:
70+
requestMenuId = 0;
71+
}
72+
73+
//
74+
// Create popup menus
75+
// [NOTICE] ImGui::BeginPopup() MUST be put after ImGui::OpenPopup(), otherwise popup won't show!
76+
//
77+
78+
// Specify menu position
79+
ImGui::SetNextWindowPos(menuPos);
80+
81+
if (ImGui::BeginPopup("menu_osc1_wave"))
82+
{
83+
ImGui::SeparatorText("OSC1 Waveform");
84+
if (ImGui::MenuItem("Saw")) { _triggerParamUpdate(pOsc1Wave, ui->_pi2f(OWAVE_SAW, OWAVE_MAX)); }
85+
if (ImGui::MenuItem("Pulse")) { _triggerParamUpdate(pOsc1Wave, ui->_pi2f(OWAVE_PULSE, OWAVE_MAX)); }
86+
if (ImGui::MenuItem("Triangle")) { _triggerParamUpdate(pOsc1Wave, ui->_pi2f(OWAVE_TRI, OWAVE_MAX)); }
87+
if (ImGui::MenuItem("Sine")) { _triggerParamUpdate(pOsc1Wave, ui->_pi2f(OWAVE_SINE, OWAVE_MAX)); }
88+
if (ImGui::MenuItem("C64 Noise")) { _triggerParamUpdate(pOsc1Wave, ui->_pi2f(OWAVE_C64NOISE, OWAVE_MAX)); }
89+
ImGui::EndPopup();
90+
}
91+
92+
// Specify menu position
93+
ImGui::SetNextWindowPos(menuPos);
94+
95+
if (ImGui::BeginPopup("menu_osc2_wave"))
96+
{
97+
ImGui::SeparatorText("OSC2 Waveform");
98+
if (ImGui::MenuItem("Saw")) { _triggerParamUpdate(pOsc2Wave, ui->_pi2f(OWAVE_SAW, OWAVE_MAX)); }
99+
if (ImGui::MenuItem("Pulse")) { _triggerParamUpdate(pOsc2Wave, ui->_pi2f(OWAVE_PULSE, OWAVE_MAX)); }
100+
if (ImGui::MenuItem("Triangle")) { _triggerParamUpdate(pOsc2Wave, ui->_pi2f(OWAVE_TRI, OWAVE_MAX)); }
101+
if (ImGui::MenuItem("Sine")) { _triggerParamUpdate(pOsc2Wave, ui->_pi2f(OWAVE_SINE, OWAVE_MAX)); }
102+
if (ImGui::MenuItem("C64 Noise")) { _triggerParamUpdate(pOsc2Wave, ui->_pi2f(OWAVE_C64NOISE, OWAVE_MAX)); }
103+
ImGui::EndPopup();
104+
}
105+
106+
// Specify menu position
107+
ImGui::SetNextWindowPos(menuPos);
108+
109+
if (ImGui::BeginPopup("menu_osc3_wave"))
110+
{
111+
ImGui::SeparatorText("OSC3 Waveform");
112+
if (ImGui::MenuItem("Saw")) { _triggerParamUpdate(pOsc3Wave, ui->_pi2f(OWAVE_SAW, OWAVE_MAX)); }
113+
if (ImGui::MenuItem("Pulse")) { _triggerParamUpdate(pOsc3Wave, ui->_pi2f(OWAVE_PULSE, OWAVE_MAX)); }
114+
if (ImGui::MenuItem("Triangle")) { _triggerParamUpdate(pOsc3Wave, ui->_pi2f(OWAVE_TRI, OWAVE_MAX)); }
115+
if (ImGui::MenuItem("Sine")) { _triggerParamUpdate(pOsc3Wave, ui->_pi2f(OWAVE_SINE, OWAVE_MAX)); }
116+
if (ImGui::MenuItem("C64 Noise")) { _triggerParamUpdate(pOsc3Wave, ui->_pi2f(OWAVE_C64NOISE, OWAVE_MAX)); }
117+
ImGui::EndPopup();
118+
}
119+
}
120+
121+
void ImGuiUI::_triggerParamUpdate(uint32_t paramId, float newValue)
122+
{
123+
ui->setParameterValue(paramId, newValue); // Tell the DSP to update parameter value
124+
ui->parameterChanged(paramId, newValue); // Request UI refresh
125+
}

src/Widgets/ImGui_UI.hpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Inspector Window for DPF
3+
* Copyright (C) 2022-2025 Filipe Coelho <[email protected]>
4+
*
5+
* Permission to use, copy, modify, and/or distribute this software for any purpose with
6+
* or without fee is hereby granted, provided that the above copyright notice and this
7+
* permission notice appear in all copies.
8+
*
9+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
10+
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
11+
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
12+
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
13+
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14+
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15+
*/
16+
17+
#pragma once
18+
19+
#include "DearImGui.hpp"
20+
21+
// Forward decls.
22+
class CCetoneUI;
23+
24+
25+
// --------------------------------------------------------------------------------------------------------------------
26+
27+
class ImGuiUI : public ImGuiTopLevelWidget
28+
{
29+
30+
public:
31+
CCetoneUI *ui;
32+
33+
bool isAboutWindowOpen = false;
34+
uint16_t requestMenuId = 0;
35+
36+
ImVec2 menuPos{0, 0};
37+
38+
double userScaling = 1.0f;
39+
40+
ImGuiUI(TopLevelWidget* const tlw, CCetoneUI* const ui) :
41+
ImGuiTopLevelWidget(tlw->getWindow()),
42+
ui(ui)
43+
{
44+
}
45+
46+
protected:
47+
void onImGuiDisplay() override;
48+
49+
private:
50+
void _triggerParamUpdate(uint32_t paramId, float newValue);
51+
};

0 commit comments

Comments
 (0)