Skip to content

Commit 6075871

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

File tree

5 files changed

+377
-7
lines changed

5 files changed

+377
-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: 78 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,29 @@ 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));
121+
122+
_createHiddenButton(fBtnFilterType, pFilterType, Size<uint>(45, 10 + 2), Point<int>(514, 96 - 4));
123+
_createHiddenButton(fBtnFilterMode, pFilterMode, Size<uint>(45, 10 + 2), Point<int>(514 + 48, 96 - 4));
124+
125+
_createHiddenButton(fBtnLfo1Waveform, pLfo1Wave, Size<uint>(45, 10 + 2), Point<int>(534 + 48, 316 - 4));
126+
127+
_createHiddenButton(fBtnArpMode, pArpMode, Size<uint>(45, 10 + 2), Point<int>(767, 316 - 4));
128+
129+
_createHiddenButton(fBtnMod1Src, pMod1Src, Size<uint>(45, 10 + 2), Point<int>(10, 426 - 4));
130+
_createHiddenButton(fBtnMod2Src, pMod2Src, Size<uint>(45, 10 + 2), Point<int>(230, 426 - 4));
131+
_createHiddenButton(fBtnMod3Src, pMod3Src, Size<uint>(45, 10 + 2), Point<int>(450, 426 - 4));
132+
_createHiddenButton(fBtnMod4Src, pMod4Src, Size<uint>(45, 10 + 2), Point<int>(670, 426 - 4));
110133
}
111134

112135
void CCetoneUI::parameterChanged(uint32_t index, float value)
@@ -349,14 +372,62 @@ void CCetoneUI::parameterChanged(uint32_t index, float value)
349372

350373
void CCetoneUI::imageButtonClicked(ImageButton* button, int)
351374
{
352-
#if 0
353-
switch (button->getId()) {
354-
case BTN_PANIC: {
355-
panic();
356-
break;
357-
}
375+
DISTRHO_SAFE_ASSERT_RETURN(fImGuiInstance, )
376+
377+
switch (button->getId())
378+
{
379+
case BTN_ABOUT:
380+
{
381+
fImGuiInstance->isAboutWindowOpen = !fImGuiInstance->isAboutWindowOpen;
382+
break;
383+
}
384+
case pOsc1Wave:
385+
{
386+
fImGuiInstance->menuPos = ImVec2(fBtnOsc1Waveform->getAbsolutePos().getX(), fBtnOsc1Waveform->getAbsolutePos().getY() + fBtnOsc1Waveform->getHeight());
387+
fImGuiInstance->requestMenuId = pOsc1Wave;
388+
break;
389+
}
390+
case pOsc2Wave:
391+
{
392+
fImGuiInstance->menuPos = ImVec2(fBtnOsc2Waveform->getAbsolutePos().getX(), fBtnOsc2Waveform->getAbsolutePos().getY() + fBtnOsc2Waveform->getHeight());
393+
fImGuiInstance->requestMenuId = pOsc2Wave;
394+
break;
395+
}
396+
case pOsc3Wave:
397+
{
398+
fImGuiInstance->menuPos = ImVec2(fBtnOsc3Waveform->getAbsolutePos().getX(), fBtnOsc3Waveform->getAbsolutePos().getY() + fBtnOsc3Waveform->getHeight());
399+
fImGuiInstance->requestMenuId = pOsc3Wave;
400+
break;
401+
}
402+
case pFilterType:
403+
{
404+
fImGuiInstance->menuPos = ImVec2(fBtnFilterType->getAbsolutePos().getX(), fBtnFilterType->getAbsolutePos().getY() + fBtnFilterType->getHeight());
405+
fImGuiInstance->requestMenuId = pFilterType;
406+
break;
407+
}
408+
case pFilterMode:
409+
{
410+
fImGuiInstance->menuPos = ImVec2(fBtnFilterMode->getAbsolutePos().getX(), fBtnFilterMode->getAbsolutePos().getY() + fBtnFilterMode->getHeight());
411+
fImGuiInstance->requestMenuId = pFilterMode;
412+
break;
413+
}
414+
case pLfo1Wave:
415+
{
416+
fImGuiInstance->menuPos = ImVec2(fBtnLfo1Waveform->getAbsolutePos().getX(), fBtnLfo1Waveform->getAbsolutePos().getY() + fBtnLfo1Waveform->getHeight());
417+
fImGuiInstance->requestMenuId = pLfo1Wave;
418+
break;
419+
}
420+
// NOTICE: For those buttons below, no need to specify menu position. Let Dear ImGui decide menu's position.
421+
case pArpMode:
422+
case pMod1Src:
423+
case pMod2Src:
424+
case pMod3Src:
425+
case pMod4Src:
426+
{
427+
fImGuiInstance->requestMenuId = button->getId();
428+
break;
429+
}
358430
}
359-
#endif
360431
}
361432

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

src/CetoneUI.hpp

Lines changed: 21 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,18 @@ 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+
ScopedPointer<ImageButton> fBtnFilterType, fBtnFilterMode;
111+
ScopedPointer<ImageButton> fBtnLfo1Waveform;
112+
ScopedPointer<ImageButton> fBtnArpMode;
113+
114+
ScopedPointer<ImageButton> fBtnMod1Src, fBtnMod2Src, fBtnMod3Src, fBtnMod4Src;
115+
96116
// -------------------------------------------------------------------
97117
// Helpers
98118

@@ -127,5 +147,6 @@ class CCetoneUI : public DISTRHO::UI,
127147
// Button IDs
128148

129149
constexpr uint BTN_PANIC = d_cconst('p', 'n', 'i', 'c');
150+
constexpr uint BTN_ABOUT = d_cconst('a', 'b', 't', '.');
130151

131152
// -----------------------------------------------------------------------

src/Widgets/ImGui_UI.cpp

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
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("Cetone Synth Light");
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+
case pFilterType:
69+
ImGui::OpenPopup("menu_filter_type");
70+
requestMenuId = 0;
71+
break;
72+
case pFilterMode:
73+
ImGui::OpenPopup("menu_filter_mode");
74+
requestMenuId = 0;
75+
break;
76+
case pLfo1Wave:
77+
ImGui::OpenPopup("menu_lfo1_wave");
78+
requestMenuId = 0;
79+
break;
80+
case pArpMode:
81+
ImGui::OpenPopup("menu_arp_mode");
82+
requestMenuId = 0;
83+
break;
84+
85+
case pMod1Src:
86+
case pMod2Src:
87+
case pMod3Src:
88+
case pMod4Src:
89+
ImGui::OpenPopup("menu_mod_src");
90+
_requestedModParam = requestMenuId;
91+
requestMenuId = 0;
92+
break;
93+
94+
default:
95+
requestMenuId = 0;
96+
}
97+
98+
//
99+
// Create popup menus
100+
// [NOTICE] ImGui::BeginPopup() MUST be put after ImGui::OpenPopup(), otherwise popup won't show!
101+
//
102+
103+
ImGui::SetNextWindowPos(menuPos); // Specify menu position
104+
105+
if (ImGui::BeginPopup("menu_osc1_wave"))
106+
{
107+
ImGui::SeparatorText("Waveform");
108+
if (ImGui::MenuItem("Saw")) { _triggerParamUpdate(pOsc1Wave, ui->_pi2f(OWAVE_SAW, OWAVE_MAX)); }
109+
if (ImGui::MenuItem("Pulse")) { _triggerParamUpdate(pOsc1Wave, ui->_pi2f(OWAVE_PULSE, OWAVE_MAX)); }
110+
if (ImGui::MenuItem("Triangle")) { _triggerParamUpdate(pOsc1Wave, ui->_pi2f(OWAVE_TRI, OWAVE_MAX)); }
111+
if (ImGui::MenuItem("Sine")) { _triggerParamUpdate(pOsc1Wave, ui->_pi2f(OWAVE_SINE, OWAVE_MAX)); }
112+
if (ImGui::MenuItem("C64 Noise")) { _triggerParamUpdate(pOsc1Wave, ui->_pi2f(OWAVE_C64NOISE, OWAVE_MAX)); }
113+
ImGui::EndPopup();
114+
}
115+
116+
ImGui::SetNextWindowPos(menuPos); // Specify menu position
117+
118+
if (ImGui::BeginPopup("menu_osc2_wave"))
119+
{
120+
ImGui::SeparatorText("Waveform");
121+
if (ImGui::MenuItem("Saw")) { _triggerParamUpdate(pOsc2Wave, ui->_pi2f(OWAVE_SAW, OWAVE_MAX)); }
122+
if (ImGui::MenuItem("Pulse")) { _triggerParamUpdate(pOsc2Wave, ui->_pi2f(OWAVE_PULSE, OWAVE_MAX)); }
123+
if (ImGui::MenuItem("Triangle")) { _triggerParamUpdate(pOsc2Wave, ui->_pi2f(OWAVE_TRI, OWAVE_MAX)); }
124+
if (ImGui::MenuItem("Sine")) { _triggerParamUpdate(pOsc2Wave, ui->_pi2f(OWAVE_SINE, OWAVE_MAX)); }
125+
if (ImGui::MenuItem("C64 Noise")) { _triggerParamUpdate(pOsc2Wave, ui->_pi2f(OWAVE_C64NOISE, OWAVE_MAX)); }
126+
ImGui::EndPopup();
127+
}
128+
129+
ImGui::SetNextWindowPos(menuPos); // Specify menu position
130+
131+
if (ImGui::BeginPopup("menu_osc3_wave"))
132+
{
133+
ImGui::SeparatorText("Waveform");
134+
if (ImGui::MenuItem("Saw")) { _triggerParamUpdate(pOsc3Wave, ui->_pi2f(OWAVE_SAW, OWAVE_MAX)); }
135+
if (ImGui::MenuItem("Pulse")) { _triggerParamUpdate(pOsc3Wave, ui->_pi2f(OWAVE_PULSE, OWAVE_MAX)); }
136+
if (ImGui::MenuItem("Triangle")) { _triggerParamUpdate(pOsc3Wave, ui->_pi2f(OWAVE_TRI, OWAVE_MAX)); }
137+
if (ImGui::MenuItem("Sine")) { _triggerParamUpdate(pOsc3Wave, ui->_pi2f(OWAVE_SINE, OWAVE_MAX)); }
138+
if (ImGui::MenuItem("C64 Noise")) { _triggerParamUpdate(pOsc3Wave, ui->_pi2f(OWAVE_C64NOISE, OWAVE_MAX)); }
139+
ImGui::EndPopup();
140+
}
141+
142+
ImGui::SetNextWindowPos(menuPos); // Specify menu position
143+
144+
if (ImGui::BeginPopup("menu_filter_type"))
145+
{
146+
ImGui::SeparatorText("Filter Type");
147+
if (ImGui::MenuItem("Dirty")) { _triggerParamUpdate(pFilterType, ui->_pi2f(FTYPE_DIRTY, FTYPE_MAX)); }
148+
if (ImGui::MenuItem("Moog")) { _triggerParamUpdate(pFilterType, ui->_pi2f(FTYPE_MOOG, FTYPE_MAX)); }
149+
if (ImGui::MenuItem("Moog 2")) { _triggerParamUpdate(pFilterType, ui->_pi2f(FTYPE_MOOG2, FTYPE_MAX)); }
150+
if (ImGui::MenuItem("Ch12db")) { _triggerParamUpdate(pFilterType, ui->_pi2f(FTYPE_CH12DB, FTYPE_MAX)); }
151+
if (ImGui::MenuItem("x0x (303)")) { _triggerParamUpdate(pFilterType, ui->_pi2f(FTYPE_303, FTYPE_MAX)); }
152+
if (ImGui::MenuItem("8580")) { _triggerParamUpdate(pFilterType, ui->_pi2f(FTYPE_8580, FTYPE_MAX)); }
153+
if (ImGui::MenuItem("Bi12db (Budda)")) { _triggerParamUpdate(pFilterType, ui->_pi2f(FTYPE_BUDDA, FTYPE_MAX)); }
154+
ImGui::Separator();
155+
if (ImGui::MenuItem("No Filter")) { _triggerParamUpdate(pFilterType, ui->_pi2f(FTYPE_NONE, FTYPE_MAX)); }
156+
ImGui::EndPopup();
157+
}
158+
159+
ImGui::SetNextWindowPos(menuPos);
160+
161+
if (ImGui::BeginPopup("menu_filter_mode"))
162+
{
163+
ImGui::SeparatorText("Filter Mode");
164+
if (ImGui::MenuItem("Low pass")) { _triggerParamUpdate(pFilterMode, ui->_pi2f(FMODE_LOW, FMODE_MAX)); }
165+
if (ImGui::MenuItem("Band pass")) { _triggerParamUpdate(pFilterMode, ui->_pi2f(FMODE_BAND, FMODE_MAX)); }
166+
if (ImGui::MenuItem("High pass")) { _triggerParamUpdate(pFilterMode, ui->_pi2f(FMODE_HIGH, FMODE_MAX)); }
167+
if (ImGui::MenuItem("Notch")) { _triggerParamUpdate(pFilterMode, ui->_pi2f(FMODE_NOTCH, FMODE_MAX)); }
168+
ImGui::EndPopup();
169+
}
170+
171+
ImGui::SetNextWindowPos(menuPos); // Specify menu position
172+
173+
if (ImGui::BeginPopup("menu_lfo1_wave"))
174+
{
175+
ImGui::SeparatorText("Waveform");
176+
if (ImGui::MenuItem("Saw")) { _triggerParamUpdate(pLfo1Wave, ui->_pi2f(OWAVE_SAW, OWAVE_MAX)); }
177+
if (ImGui::MenuItem("Pulse")) { _triggerParamUpdate(pLfo1Wave, ui->_pi2f(OWAVE_PULSE, OWAVE_MAX)); }
178+
if (ImGui::MenuItem("Triangle")) { _triggerParamUpdate(pLfo1Wave, ui->_pi2f(OWAVE_TRI, OWAVE_MAX)); }
179+
if (ImGui::MenuItem("Sine")) { _triggerParamUpdate(pLfo1Wave, ui->_pi2f(OWAVE_SINE, OWAVE_MAX)); }
180+
if (ImGui::MenuItem("C64 Noise")) { _triggerParamUpdate(pLfo1Wave, ui->_pi2f(OWAVE_C64NOISE, OWAVE_MAX)); }
181+
ImGui::EndPopup();
182+
}
183+
184+
// NOTICE:
185+
// For menus below, no need to specify menu position. Let Dear ImGui decide menu's position.
186+
// Otherwise, menu will partially show on the screen due to insufficient space.
187+
188+
if (ImGui::BeginPopup("menu_arp_mode"))
189+
{
190+
ImGui::SeparatorText("Arp Mode");
191+
if (ImGui::MenuItem("Minor")) { _triggerParamUpdate(pArpMode, ui->_pi2f(0 + 1, ARP_MAX + 1)); }
192+
if (ImGui::MenuItem("Major")) { _triggerParamUpdate(pArpMode, ui->_pi2f(1 + 1, ARP_MAX + 1)); }
193+
if (ImGui::MenuItem("Minor + 1 Octave")) { _triggerParamUpdate(pArpMode, ui->_pi2f(2 + 1, ARP_MAX + 1)); }
194+
if (ImGui::MenuItem("Major + 1 Octave")) { _triggerParamUpdate(pArpMode, ui->_pi2f(3 + 1, ARP_MAX + 1)); }
195+
if (ImGui::MenuItem("1 Octave")) { _triggerParamUpdate(pArpMode, ui->_pi2f(4 + 1, ARP_MAX + 1)); }
196+
if (ImGui::MenuItem("2 Octaves")) { _triggerParamUpdate(pArpMode, ui->_pi2f(5 + 1, ARP_MAX + 1)); }
197+
if (ImGui::MenuItem("Quint")) { _triggerParamUpdate(pArpMode, ui->_pi2f(6 + 1, ARP_MAX + 1)); }
198+
if (ImGui::MenuItem("Quint 2")) { _triggerParamUpdate(pArpMode, ui->_pi2f(7 + 1, ARP_MAX + 1)); }
199+
ImGui::Separator();
200+
if (ImGui::MenuItem("Off")) { _triggerParamUpdate(pArpMode, ui->_pi2f(-1 + 1, ARP_MAX + 1)); }
201+
ImGui::EndPopup();
202+
}
203+
204+
if (ImGui::BeginPopup("menu_mod_src"))
205+
{
206+
ImGui::SeparatorText("Modulator Source");
207+
if (ImGui::MenuItem("Velocity")) { _triggerParamUpdate(_requestedModParam, ui->_pi2f(MOD_SRC_VEL, MOD_SRC_MAX)); }
208+
if (ImGui::MenuItem("Controller 1")) { _triggerParamUpdate(_requestedModParam, ui->_pi2f(MOD_SRC_CTRL1, MOD_SRC_MAX)); }
209+
if (ImGui::MenuItem("Modulation Envelope")) { _triggerParamUpdate(_requestedModParam, ui->_pi2f(MOD_SRC_MENV1, MOD_SRC_MAX)); }
210+
if (ImGui::MenuItem("LFO")) { _triggerParamUpdate(_requestedModParam, ui->_pi2f(MOD_SRC_LFO1, MOD_SRC_MAX)); }
211+
if (ImGui::MenuItem("Modulation Env. x LFO")) { _triggerParamUpdate(_requestedModParam, ui->_pi2f(MOD_SRC_MENV1xLFO1, MOD_SRC_MAX)); }
212+
ImGui::Separator();
213+
if (ImGui::MenuItem("Disable")) { _triggerParamUpdate(_requestedModParam, ui->_pi2f(MOD_SRC_NONE, MOD_SRC_MAX)); }
214+
ImGui::EndPopup();
215+
}
216+
}
217+
218+
void ImGuiUI::_triggerParamUpdate(uint32_t paramId, float newValue)
219+
{
220+
ui->setParameterValue(paramId, newValue); // Tell the DSP to update parameter value
221+
ui->parameterChanged(paramId, newValue); // Request UI refresh
222+
}

0 commit comments

Comments
 (0)