src/gui/elems/midiIO/midiLearnerPack.cpp
src/gui/elems/fileBrowser.cpp
src/gui/elems/soundMeter.cpp
+ src/gui/elems/keyBinder.cpp
src/gui/elems/plugin/pluginBrowser.cpp
src/gui/elems/plugin/pluginParameter.cpp
src/gui/elems/plugin/pluginElement.cpp
src/gui/elems/config/tabAudio.cpp
src/gui/elems/config/tabBehaviors.cpp
src/gui/elems/config/tabPlugins.cpp
+ src/gui/elems/config/tabBindings.cpp
src/gui/elems/basics/scroll.cpp
src/gui/elems/basics/pack.cpp
src/gui/elems/basics/group.cpp
src/gui/elems/basics/split.cpp
src/gui/elems/basics/browser.cpp
src/gui/elems/basics/flex.cpp
+ src/gui/elems/basics/tabs.cpp
src/utils/log.cpp
src/utils/time.cpp
src/utils/math.cpp
#include <algorithm>
#include <cassert>
#include <cmath>
+#include <cstddef>
#include <unordered_map>
namespace giada::m
{
m_actions.updateEvent(id, e);
}
-} // namespace giada::m
\ No newline at end of file
+} // namespace giada::m
#include "core/actions/actions.h"
#include "core/midiEvent.h"
#include "core/types.h"
+#include <cstddef>
#include <unordered_set>
namespace giada::m::patch
/* -------------------------------------------------------------------------- */
-template struct MidiLighter<KernelMidi>;
+template class MidiLighter<KernelMidi>;
#ifdef WITH_TESTS
-template struct MidiLighter<KernelMidiMock>;
+template class MidiLighter<KernelMidiMock>;
#endif
} // namespace giada::m
\ No newline at end of file
MidiMapper<KernelMidiI>* m_midiMapper;
};
-extern template struct MidiLighter<KernelMidi>;
+extern template class MidiLighter<KernelMidi>;
#ifdef WITH_TESTS
-extern template struct MidiLighter<KernelMidiMock>;
+extern template class MidiLighter<KernelMidiMock>;
#endif
} // namespace giada::m
namespace giada::m
{
class Channel;
-class ChannelShared;
+struct ChannelShared;
class Sequencer;
/* SampleReactor
data.midiInVolumeOut = j.value(CONF_KEY_MIDI_IN_VOLUME_OUT, data.midiInVolumeOut);
data.midiInBeatDouble = j.value(CONF_KEY_MIDI_IN_BEAT_DOUBLE, data.midiInBeatDouble);
data.midiInBeatHalf = j.value(CONF_KEY_MIDI_IN_BEAT_HALF, data.midiInBeatHalf);
+
+ data.keyBindings[KEY_BIND_PLAY] = j.value(CONF_KEY_BIND_PLAY, 0);
+ data.keyBindings[KEY_BIND_REWIND] = j.value(CONF_KEY_BIND_REWIND, 0);
+ data.keyBindings[KEY_BIND_RECORD_ACTIONS] = j.value(CONF_KEY_BIND_RECORD_ACTIONS, 0);
+ data.keyBindings[KEY_BIND_RECORD_INPUT] = j.value(CONF_KEY_BIND_RECORD_INPUT, 0);
+ data.keyBindings[KEY_BIND_EXIT] = j.value(CONF_KEY_BIND_EXIT, 0);
+
#ifdef WITH_VST
data.pluginChooserX = j.value(CONF_KEY_PLUGIN_CHOOSER_X, data.pluginChooserX);
data.pluginChooserY = j.value(CONF_KEY_PLUGIN_CHOOSER_Y, data.pluginChooserY);
j[CONF_KEY_REC_TRIGGER_MODE] = static_cast<int>(data.recTriggerMode);
j[CONF_KEY_REC_TRIGGER_LEVEL] = data.recTriggerLevel;
j[CONF_KEY_INPUT_REC_MODE] = static_cast<int>(data.inputRecMode);
+
+ j[CONF_KEY_BIND_PLAY] = data.keyBindings.find(KEY_BIND_PLAY)->second;
+ j[CONF_KEY_BIND_REWIND] = data.keyBindings.find(KEY_BIND_REWIND)->second;
+ j[CONF_KEY_BIND_RECORD_ACTIONS] = data.keyBindings.find(KEY_BIND_RECORD_ACTIONS)->second;
+ j[CONF_KEY_BIND_RECORD_INPUT] = data.keyBindings.find(KEY_BIND_RECORD_INPUT)->second;
+ j[CONF_KEY_BIND_EXIT] = data.keyBindings.find(KEY_BIND_EXIT)->second;
+
#ifdef WITH_VST
j[CONF_KEY_PLUGIN_CHOOSER_X] = data.pluginChooserX;
j[CONF_KEY_PLUGIN_CHOOSER_Y] = data.pluginChooserY;
#include "core/types.h"
#include "utils/gui.h"
#include <string>
+#include <unordered_map>
namespace giada::m
{
class Conf final
{
public:
+ using KeyBindings = std::unordered_map<int, int>;
+
+ static constexpr int KEY_BIND_PLAY = 1;
+ static constexpr int KEY_BIND_REWIND = 2;
+ static constexpr int KEY_BIND_RECORD_ACTIONS = 3;
+ static constexpr int KEY_BIND_RECORD_INPUT = 4;
+ static constexpr int KEY_BIND_EXIT = 5;
+
struct Data
{
int logMode = LOG_MODE_MUTE;
int pluginSortMethod = 0;
#endif
+
+ KeyBindings keyBindings = {
+ {KEY_BIND_PLAY, ' '},
+ {KEY_BIND_REWIND, FL_BackSpace},
+ {KEY_BIND_RECORD_ACTIONS, FL_Enter},
+ {KEY_BIND_RECORD_INPUT, FL_End},
+ {KEY_BIND_EXIT, FL_Escape}};
};
Conf();
/* -- version --------------------------------------------------------------- */
constexpr auto G_APP_NAME = "Giada";
-constexpr auto G_VERSION_STR = "0.20.1";
+constexpr auto G_VERSION_STR = "0.21.0";
constexpr int G_VERSION_MAJOR = 0;
-constexpr int G_VERSION_MINOR = 20;
-constexpr int G_VERSION_PATCH = 1;
+constexpr int G_VERSION_MINOR = 21;
+constexpr int G_VERSION_PATCH = 0;
constexpr auto CONF_FILENAME = "giada.conf";
constexpr auto CONF_KEY_REC_TRIGGER_MODE = "rec_trigger_mode";
constexpr auto CONF_KEY_REC_TRIGGER_LEVEL = "rec_trigger_level";
constexpr auto CONF_KEY_INPUT_REC_MODE = "input_rec_mode";
+constexpr auto CONF_KEY_BIND_PLAY = "key_bind_play";
+constexpr auto CONF_KEY_BIND_REWIND = "key_bind_rewind";
+constexpr auto CONF_KEY_BIND_RECORD_ACTIONS = "key_bind_record_actions";
+constexpr auto CONF_KEY_BIND_RECORD_INPUT = "key_bind_record_input";
+constexpr auto CONF_KEY_BIND_EXIT = "key_bind_record_exit";
/* JSON midimaps keys */
#include "utils/log.h"
#include "utils/vector.h"
#include <cassert>
+#include <cstddef>
namespace giada::m
{
#include "core/conf.h"
#include "deps/rtaudio/RtAudio.h"
+#include <cstddef>
#include <functional>
#include <memory>
#include <string>
#include "utils/log.h"
#include "utils/math.h"
#include <cassert>
+#include <cstddef>
#include <vector>
namespace giada::m
#include "core/midiEvent.h"
#include "core/model/model.h"
#include "core/types.h"
+#include <cstddef>
#include <cstdint>
#include <functional>
* -------------------------------------------------------------------------- */
#include "midiLearnParam.h"
+#include <cstddef>
namespace giada::m
{
{
return m_index;
}
-} // namespace giada::m
\ No newline at end of file
+} // namespace giada::m
#define G_MIDI_LEARN_PARAM_H
#include "core/weakAtomic.h"
+#include <cstddef>
#include <atomic>
namespace giada::m
#include "utils/fs.h"
#include "utils/log.h"
#include "utils/string.h"
+#include <cstddef>
#include <cstring>
#include <filesystem>
#include <fstream>
#ifdef WITH_TESTS
template class MidiMapper<KernelMidiMock>;
#endif
-} // namespace giada::m
\ No newline at end of file
+} // namespace giada::m
void Model::reset()
{
+ get() = {};
m_shared = {};
get().sequencer.shared = &m_shared.sequencerShared;
LoadState load(const Patch::Data& patch)
{
- DataLock lock = g_engine.model.lockData();
+ DataLock lock = g_engine.model.lockData(SwapType::NONE);
/* Clear and re-initialize channels first. */
/* -------------------------------------------------------------------------- */
-bool Plugin::acceptsMidi() const
+bool Plugin::isInstrument() const
{
if (!valid)
return false;
- return m_plugin->acceptsMidi();
+ return m_plugin->acceptsMidi() && m_plugin->getTotalNumInputChannels() == 0;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-void Plugin::process(juce::AudioBuffer<float>& out, juce::MidiBuffer m)
+const Plugin::Buffer& Plugin::process(const Plugin::Buffer& out, juce::MidiBuffer m)
{
- /* If this is not an instrument (i.e. doesn't accept MIDI), copy the
- incoming buffer data into the temporary one. This way FXes will process
- existing audio data. Conversely, if the plug-in is an instrument, it
- generates its own audio data inside a clean m_buffer and we can play more
- than one plug-in instrument in the same stack, driven by the same set of
- MIDI events. */
-
- const bool isInstrument = m_plugin->acceptsMidi();
-
- if (!isInstrument)
- m_buffer = out;
- else
- m_buffer.clear();
+ /* Copy the incoming buffer data into the temporary one. This way FXes will
+ process existing audio data on the private buffer. This is needed later on
+ when merging it back into the incoming buffer. */
+ m_buffer = out;
m_plugin->processBlock(m_buffer, m);
-
- /* The local buffer is now filled. Let's try to fill the 'out' one as well
- by taking into account the bus layout - many plug-ins might have mono output
- and we have a stereo buffer to fill. */
-
- for (int i = 0, j = 0; i < out.getNumChannels(); i++)
- {
- if (isInstrument)
- out.addFrom(i, 0, m_buffer, j, 0, m_buffer.getNumSamples());
- else
- out.copyFrom(i, 0, m_buffer, j, 0, m_buffer.getNumSamples());
- if (i < countMainOutChannels() - 1)
- j++;
- }
+ return m_buffer;
}
/* -------------------------------------------------------------------------- */
class Plugin : private juce::ComponentListener
{
public:
+ using Buffer = juce::AudioBuffer<float>;
+
/* Plugin (1)
Constructs an invalid plug-in. */
std::string getParameterLabel(int index) const;
bool isSuspended() const;
bool isBypassed() const;
+ bool isInstrument() const;
int getNumPrograms() const;
int getCurrentProgram() const;
std::string getProgramName(int index) const;
void setParameter(int index, float value) const;
void setCurrentProgram(int index) const;
- bool acceptsMidi() const;
PluginState getState() const;
juce::AudioProcessorEditor* createEditor() const;
+ /* countMainOutChannels
+ Returns the current channel layout for the main output bus. */
+
+ int countMainOutChannels() const;
+
/* process
- Process the plug-in with audio and MIDI data. The audio buffer is a reference:
- it has to be altered by the plug-in itself. Conversely, the MIDI buffer must
- be passed by copy: each plug-in must receive its own copy of the event set, so
- that any attempt to change/clear the MIDI buffer will only modify the local
- copy. */
+ Process the plug-in with audio and MIDI data. The audio buffer is a
+ reference, while the MIDI buffer must be passed by copy: each plug-in must
+ receive its own copy of the event set, so that any attempt to change/clear
+ the MIDI buffer will only modify the local copy. Returns a reference of the
+ local buffer filled with processed data. */
- void process(juce::AudioBuffer<float>& b, juce::MidiBuffer m);
+ const Buffer& process(const Buffer& b, juce::MidiBuffer m);
void setState(PluginState p);
void setBypass(bool b);
juce::AudioProcessor::Bus* getMainBus(BusType b) const;
- /* countMainOutChannels
- Returns the current channel layout for the main output bus. */
-
- int countMainOutChannels() const;
-
std::unique_ptr<juce::AudioPluginInstance> m_plugin;
std::unique_ptr<PluginHost::Info> m_playHead;
- juce::AudioBuffer<float> m_buffer;
+ Buffer m_buffer;
std::atomic<bool> m_bypass;
#include "utils/log.h"
#include "utils/vector.h"
#include <cassert>
+#include <cstddef>
#include <memory>
namespace giada::m
{
assert(outBuf.countFrames() == m_audioBuffer.getNumSamples());
- /* If events are null: Audio stack processing (master in, master out or
- sample channels. No need for MIDI events.
- If events are not null: MIDI stack (MIDI channels). MIDI channels must not
- process the current buffer: give them an empty and clean one. */
+ giadaToJuceTempBuf(outBuf);
if (events == nullptr)
{
- giadaToJuceTempBuf(outBuf);
juce::MidiBuffer dummyEvents; // empty
processPlugins(plugins, dummyEvents);
}
else
- {
- m_audioBuffer.clear();
processPlugins(plugins, *events);
- }
+
juceToGiadaOutBuf(outBuf);
}
{
if (!p->valid || p->isSuspended() || p->isBypassed())
continue;
- p->process(m_audioBuffer, events);
+ processPlugin(p, events);
}
events.clear();
}
+
+/* -------------------------------------------------------------------------- */
+
+void PluginHost::processPlugin(Plugin* p, const juce::MidiBuffer& events)
+{
+ const Plugin::Buffer& pluginBuffer = p->process(m_audioBuffer, events);
+ const bool isInstrument = p->isInstrument();
+
+ /* Merge the plugin buffer back into the local one. Special care is needed
+ if audio channels mismatch. */
+
+ for (int i = 0, j = 0; i < m_audioBuffer.getNumChannels(); i++)
+ {
+ /* If instrument (i.e. a plug-in that accepts MIDI and produces audio
+ out of it), SUM the local working buffer to the main one. This allows
+ multiple plug-in instruments to play simultaneously on a given set of
+ MIDI events. If it's a normal FX instead (!isInstrument), the local
+ working buffer is simply copied over the main one. */
+
+ if (isInstrument)
+ m_audioBuffer.addFrom(i, 0, pluginBuffer, j, 0, pluginBuffer.getNumSamples());
+ else
+ m_audioBuffer.copyFrom(i, 0, pluginBuffer, j, 0, pluginBuffer.getNumSamples());
+ if (i < p->countMainOutChannels() - 1)
+ j++;
+ }
+}
} // namespace giada::m
#endif // #ifdef WITH_VST
void toggleBypass(ID pluginId);
private:
+ /* giadaToJuceTempBuf
+ Copies the Giada buffer 'outBuf' to the private JUCE buffer for local
+ processing. */
+
void giadaToJuceTempBuf(const mcl::AudioBuffer& outBuf);
/* juceToGiadaOutBuf
- Converts buffer from Juce to Giada. A note for the future: if we overwrite (=)
- (as we do now) it's SEND, if we add (+) it's INSERT. */
+ Copies the private JUCE buffer to Giada buffer 'outBuf'. */
void juceToGiadaOutBuf(mcl::AudioBuffer& outBuf) const;
- void processPlugins(const std::vector<Plugin*>& plugins, juce::MidiBuffer& events);
+ void processPlugins(const std::vector<Plugin*>&, juce::MidiBuffer& events);
+
+ void processPlugin(Plugin*, const juce::MidiBuffer& events);
model::Model& m_model;
#include "utils/log.h"
#include "utils/string.h"
#include <cassert>
+#include <cstddef>
#include <memory>
namespace giada::m
#include "pluginState.h"
#include "core/const.h"
+#include <cstddef>
namespace giada::m
{
#define G_PLUGIN_STATE_H
#include "deps/juce-config.h"
+#include <cstddef>
#include <string>
namespace giada::m
#endif
-#endif // #ifdef WITH_VST
\ No newline at end of file
+#endif // #ifdef WITH_VST
#ifndef G_QUEUE_H
#define G_QUEUE_H
+#include <cstddef>
#include <array>
#include <atomic>
#ifndef G_RING_BUFFER_H
#define G_RING_BUFFER_H
+#include <cstddef>
#include <array>
namespace giada
#include "utils/fs.h"
#include "utils/vector.h"
#include <FL/Fl_Tooltip.H>
+#include <cstddef>
extern giada::v::Ui g_ui;
extern giada::m::Engine g_engine;
configWin->tabPlugins->rebuild();
}
#endif
-} // namespace giada::c::config
\ No newline at end of file
+} // namespace giada::c::config
/* -------------------------------------------------------------------------- */
+void stopActionRecording()
+{
+ if (g_engine.kernelAudio.isReady() && g_engine.recorder.isRecordingAction())
+ g_engine.recorder.stopActionRec(g_engine.actionRecorder);
+}
+
+/* -------------------------------------------------------------------------- */
+
void toggleActionRecording()
{
if (!g_engine.kernelAudio.isReady())
g_engine.recorder.prepareActionRec(g_engine.conf.data.recTriggerMode);
}
+/* -------------------------------------------------------------------------- */
+
+void stopInputRecording()
+{
+ if (g_engine.kernelAudio.isReady() && g_engine.recorder.isRecordingInput())
+ g_engine.recorder.stopInputRec(g_engine.conf.data.inputRecMode, g_engine.kernelAudio.getSampleRate());
+}
+
+/* -------------------------------------------------------------------------- */
+
void toggleInputRecording()
{
if (!g_engine.kernelAudio.isReady() || !g_engine.kernelAudio.isInputEnabled() || !g_engine.mixerHandler.hasInputRecordableChannels())
void stopSequencer(Thread t);
void toggleSequencer(Thread t);
void rewindSequencer(Thread t);
+void stopActionRecording();
void toggleActionRecording();
+void stopInputRecording();
void toggleInputRecording();
/* Plug-ins. */
g_ui.rebuildSubWindow(WID_MIDI_INPUT);
g_ui.rebuildSubWindow(WID_MIDI_OUTPUT);
}
+
+/* -------------------------------------------------------------------------- */
+
+bool isValidKey_(int key)
+{
+ if (strlen(Fl::event_text()) == 0)
+ return false;
+ for (const auto& [_, val] : g_engine.conf.data.keyBindings)
+ if (key == val)
+ return false;
+ return true;
+}
} // namespace
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-void channel_setKey(ID channelId, int k)
+bool channel_setKey(ID channelId, int k)
{
+ if (!isValidKey_(k))
+ return false;
g_engine.model.get().getChannel(channelId).key = k;
g_engine.model.swap(m::model::SwapType::HARD);
+ return true;
}
/* -------------------------------------------------------------------------- */
void channel_setMidiOutputFilter(ID channelId, int c);
/* channel_setKey
-Set key 'k' to Sample Channel 'channelId'. Used for keyboard bindings. */
+Set key 'k' to Sample Channel 'channelId'. Used for keyboard bindings. Returns
+false if the key is not valid (because used for global bindings). */
-void channel_setKey(ID channelId, int k);
+bool channel_setKey(ID channelId, int k);
/* MIDI Learning functions. */
#include "core/engine.h"
#include "core/patch.h"
#include "core/sequencer.h"
+#include "glue/channel.h"
#include "glue/config.h"
+#include "glue/io.h"
#include "glue/storage.h"
#include "gui/dialogs/about.h"
#include "gui/dialogs/actionEditor/midiActionEditor.h"
/* -------------------------------------------------------------------------- */
-void openKeyGrabberWindow(const c::channel::Data& data)
+void openKeyGrabberWindow(int key, std::function<bool(int)> f)
{
- g_ui.openSubWindow(*g_ui.mainWindow.get(), new v::gdKeyGrabber(data), WID_KEY_GRABBER);
+ v::gdKeyGrabber* keyGrabber = new v::gdKeyGrabber(key);
+
+ keyGrabber->onSetKey = f;
+
+ g_ui.openSubWindow(*g_ui.mainWindow.get(), keyGrabber, WID_KEY_GRABBER);
}
/* -------------------------------------------------------------------------- */
#define G_GLUE_LAYOUT_H
#include "core/types.h"
+#include <functional>
#include <string>
namespace giada::m
void openBrowserForSampleLoad(ID channelId);
void openBrowserForSampleSave(ID channelId);
void openAboutWindow();
-void openKeyGrabberWindow(const c::channel::Data&);
+void openKeyGrabberWindow(int key, std::function<bool(int)>);
void openBpmWindow(std::string bpmValue);
void openBeatsWindow(int beats, int bars);
void openConfigWindow();
if (!v::gdConfirmWin("Warning", "Close project: are you sure?"))
return;
g_engine.mixer.disable();
- g_engine.reset();
g_ui.reset();
+ g_engine.reset();
g_engine.mixer.enable();
}
p.setProgress(v);
};
+ /* Close all sub-windows first, in case there are VST editors visible. VST
+ editors must be closed before deleting their plug-in processors. */
+
+ g_ui.closeAllSubwindows();
+
m::LoadState state = g_engine.load(projectPath, patchPath, progressCb);
if (state.patch != G_PATCH_OK)
m_barTop.add(&zoomOutBtn);
m_barTop.resizable(m_barPadding);
- m_actionType.add("Key press");
- m_actionType.add("Key release");
- m_actionType.add("Stop sample");
- m_actionType.value(0);
+ m_actionType.addItem("Key press");
+ m_actionType.addItem("Key release");
+ m_actionType.addItem("Stop sample");
+ m_actionType.showItem(0);
m_actionType.copy_tooltip("Action type to add");
if (!canChangeActionType())
m_actionType.deactivate();
int gdSampleActionEditor::getActionType() const
{
- if (m_actionType.value() == 0)
+ if (m_actionType.getSelectedId() == 0)
return m::MidiEvent::NOTE_ON;
- else if (m_actionType.value() == 1)
+ else if (m_actionType.getSelectedId() == 1)
return m::MidiEvent::NOTE_OFF;
- else if (m_actionType.value() == 2)
+ else if (m_actionType.getSelectedId() == 2)
return m::MidiEvent::NOTE_KILL;
assert(false);
*
* -------------------------------------------------------------------------- */
-#include "config.h"
+#include "gui/dialogs/config.h"
#include "core/conf.h"
#include "core/const.h"
+#include "gui/elems/basics/box.h"
#include "gui/elems/basics/boxtypes.h"
#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/flex.h"
+#include "gui/elems/basics/tabs.h"
#include "gui/elems/config/tabAudio.h"
#include "gui/elems/config/tabBehaviors.h"
+#include "gui/elems/config/tabBindings.h"
#include "gui/elems/config/tabMidi.h"
#include "gui/elems/config/tabMisc.h"
#include "gui/elems/config/tabPlugins.h"
#include "utils/gui.h"
-#include <FL/Fl_Tabs.H>
namespace giada::v
{
gdConfig::gdConfig(int w, int h, m::Conf::Data& conf)
-: gdWindow(u::gui::centerWindowX(w), u::gui::centerWindowY(h), w, h, "Configuration")
+: gdWindow(u::gui::getCenterWinBounds(w, h), "Configuration")
{
- begin();
+ const geompp::Rect<int> bounds = getContentBounds().reduced(G_GUI_OUTER_MARGIN);
- Fl_Tabs* tabs = new Fl_Tabs(8, 8, w - 16, h - 44);
- tabs->box(G_CUSTOM_BORDER_BOX);
- tabs->labelcolor(G_COLOR_LIGHT_2);
- tabs->begin();
-
- tabAudio = new geTabAudio(tabs->x() + 10, tabs->y() + 20, tabs->w() - 20, tabs->h() - 40);
- tabMidi = new geTabMidi(tabs->x() + 10, tabs->y() + 20, tabs->w() - 20, tabs->h() - 40);
- tabBehaviors = new geTabBehaviors(tabs->x() + 10, tabs->y() + 20, tabs->w() - 20, tabs->h() - 40, conf);
- tabMisc = new geTabMisc(tabs->x() + 10, tabs->y() + 20, tabs->w() - 20);
+ geFlex* container = new geFlex(bounds, Direction::VERTICAL, G_GUI_OUTER_MARGIN);
+ {
+ geTabs* tabs = new geTabs(bounds);
+ {
+ tabAudio = new geTabAudio(bounds);
+ tabMidi = new geTabMidi(bounds);
+ tabBehaviors = new geTabBehaviors(bounds, conf);
+ tabMisc = new geTabMisc(bounds);
+ tabBindings = new geTabBindings(bounds, conf);
+#ifdef WITH_VST
+ tabPlugins = new geTabPlugins(bounds);
+#endif
+ tabs->add(tabAudio);
+ tabs->add(tabMidi);
+ tabs->add(tabBehaviors);
#ifdef WITH_VST
- tabPlugins = new geTabPlugins(tabs->x() + 10, tabs->y() + 20, tabs->w() - 20, tabs->h() - 40);
+ tabs->add(tabPlugins);
#endif
+ tabs->add(tabBindings);
+ tabs->add(tabMisc);
+ }
- tabs->end();
+ geFlex* footer = new geFlex(Direction::HORIZONTAL, G_GUI_OUTER_MARGIN);
+ {
+ geButton* save = new geButton("Save");
+ geButton* cancel = new geButton("Cancel");
+ save->onClick = [this]() { saveConfig(); };
+ cancel->onClick = [this]() { do_callback(); };
- save = new geButton(w - 88, h - 28, 80, 20, "Save");
- cancel = new geButton(w - 176, h - 28, 80, 20, "Cancel");
+ footer->add(new geBox()); // Spacer
+ footer->add(cancel, 80);
+ footer->add(save, 80);
+ footer->end();
+ }
- end();
+ container->add(tabs);
+ container->add(footer, G_GUI_UNIT);
+ container->end();
+ }
- save->callback(cb_save_config, (void*)this);
- cancel->callback(cb_cancel, (void*)this);
+ add(container);
+ resizable(container);
+ size_range(w, h);
u::gui::setFavicon(this);
setId(WID_CONFIG);
/* -------------------------------------------------------------------------- */
-void gdConfig::cb_save_config(Fl_Widget* /*w*/, void* p) { ((gdConfig*)p)->cb_save_config(); }
-void gdConfig::cb_cancel(Fl_Widget* /*w*/, void* p) { ((gdConfig*)p)->cb_cancel(); }
-
-/* -------------------------------------------------------------------------- */
-
-void gdConfig::cb_save_config()
+void gdConfig::saveConfig()
{
tabAudio->save();
tabBehaviors->save();
#endif
do_callback();
}
-
-/* -------------------------------------------------------------------------- */
-
-void gdConfig::cb_cancel()
-{
- do_callback();
-}
} // namespace giada::v
#include "core/conf.h"
#include "window.h"
-class geCheck;
-class geInput;
-class geBox;
-
namespace giada::v
{
class geButton;
-class geChoice;
class geTabAudio;
class geTabBehaviors;
class geTabMidi;
class geTabMisc;
+class geTabBindings;
#ifdef WITH_VST
class geTabPlugins;
#endif
geTabBehaviors* tabBehaviors;
geTabMidi* tabMidi;
geTabMisc* tabMisc;
+ geTabBindings* tabBindings;
#ifdef WITH_VST
geTabPlugins* tabPlugins;
#endif
- geButton* save;
- geButton* cancel;
private:
- static void cb_save_config(Fl_Widget* /*w*/, void* p);
- static void cb_cancel(Fl_Widget* /*w*/, void* p);
- void cb_save_config();
- void cb_cancel();
+ void saveConfig();
};
} // namespace giada::v
*
* -------------------------------------------------------------------------- */
-#include "keyGrabber.h"
-#include "config.h"
+#include "gui/dialogs/keyGrabber.h"
#include "core/conf.h"
-#include "core/model/model.h"
+#include "glue/channel.h"
#include "glue/io.h"
#include "gui/elems/basics/box.h"
-#include "gui/elems/mainWindow/keyboard/channel.h"
-#include "gui/elems/mainWindow/keyboard/channelButton.h"
-#include "gui/elems/mainWindow/keyboard/keyboard.h"
-#include "mainWindow.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/flex.h"
#include "utils/gui.h"
#include "utils/log.h"
#include "utils/string.h"
#include <cassert>
-extern giada::v::gdMainWindow* mainWin;
-
-namespace giada
-{
-namespace v
+namespace giada::v
{
-gdKeyGrabber::gdKeyGrabber(const c::channel::Data& d)
+gdKeyGrabber::gdKeyGrabber(int key)
: gdWindow(300, 126, "Key configuration")
-, m_data(d)
+, onSetKey(nullptr)
+, m_key(key)
{
- begin();
- m_text = new geBox(8, 8, 284, 80, "");
- m_clear = new geButton(w() - 88, m_text->y() + m_text->h() + 8, 80, 20, "Clear");
- m_cancel = new geButton(m_clear->x() - 88, m_clear->y(), 80, 20, "Close");
- end();
+ geFlex* container = new geFlex(getContentBounds().reduced({G_GUI_OUTER_MARGIN}), Direction::VERTICAL, G_GUI_OUTER_MARGIN);
+ {
+ m_text = new geBox();
- m_clear->callback(cb_clear, (void*)this);
- m_cancel->callback(cb_cancel, (void*)this);
+ geFlex* footer = new geFlex(Direction::HORIZONTAL, G_GUI_OUTER_MARGIN);
+ {
+ m_clear = new geButton("Clear");
+ m_cancel = new geButton("Close");
- updateText(m_data.key);
+ footer->add(new geBox()); // Spacer
+ footer->add(m_clear, 80);
+ footer->add(m_cancel, 80);
+ footer->end();
+ }
- u::gui::setFavicon(this);
- set_modal();
- show();
-}
+ container->add(m_text);
+ container->add(footer, G_GUI_UNIT);
+ container->end();
+ }
-/* -------------------------------------------------------------------------- */
+ add(container);
-void gdKeyGrabber::rebuild()
-{
- updateText(m_data.key);
-}
+ m_clear->onClick = [this]() {
+ assert(onSetKey != nullptr);
-/* -------------------------------------------------------------------------- */
+ m_key = 0;
+ onSetKey(m_key);
+ rebuild();
+ };
-void gdKeyGrabber::cb_clear(Fl_Widget* /*w*/, void* p) { ((gdKeyGrabber*)p)->cb_clear(); }
-void gdKeyGrabber::cb_cancel(Fl_Widget* /*w*/, void* p) { ((gdKeyGrabber*)p)->cb_cancel(); }
+ m_cancel->onClick = [this]() {
+ do_callback();
+ };
-/* -------------------------------------------------------------------------- */
+ rebuild();
-void gdKeyGrabber::cb_cancel()
-{
- do_callback();
+ u::gui::setFavicon(this);
+ set_modal();
+ show();
}
/* -------------------------------------------------------------------------- */
-void gdKeyGrabber::cb_clear()
+void gdKeyGrabber::rebuild()
{
- updateText(0);
- setButtonLabel(0);
+ std::string tmp = "Press a key.\n\nCurrent binding: " + u::gui::keyToString(m_key);
+ m_text->copy_label(tmp.c_str());
}
/* -------------------------------------------------------------------------- */
-void gdKeyGrabber::setButtonLabel(int key)
+int gdKeyGrabber::handle(int e)
{
- c::io::channel_setKey(m_data.id, key);
-}
-
-/* -------------------------------------------------------------------------- */
+ if (e != FL_KEYUP)
+ return Fl_Group::handle(e);
-void gdKeyGrabber::updateText(int key)
-{
- std::string tmp = "Press a key.\n\nCurrent binding: ";
- if (key != 0)
- tmp += static_cast<wchar_t>(key);
- else
- tmp += "[none]";
- m_text->copy_label(tmp.c_str());
-}
+ assert(onSetKey != nullptr);
-/* -------------------------------------------------------------------------- */
+ const int newKey = Fl::event_key();
-int gdKeyGrabber::handle(int e)
-{
- int ret = Fl_Group::handle(e);
- switch (e)
- {
- case FL_KEYUP:
+ if (!onSetKey(newKey))
{
- int x = Fl::event_key();
- if (strlen(Fl::event_text()) != 0 && x != FL_BackSpace && x != FL_Enter && x != FL_Delete && x != FL_Tab && x != FL_End && x != ' ')
- {
- u::log::print("set key '%c' (%d) for channel ID=%d\n", x, x, m_data.id);
- setButtonLabel(x);
- updateText(x);
- break;
- }
- else
- u::log::print("invalid key\n");
+ u::log::print("Invalid key\n");
+ return 1;
}
- }
- return (ret);
-}
-} // namespace v
-} // namespace giada
+ m_key = newKey;
+ rebuild();
+
+ u::log::print("Set key '%c' (%d)\n", m_key, m_key);
+
+ return 1;
+}
+} // namespace giada::v
#ifndef GD_KEYGRABBER_H
#define GD_KEYGRABBER_H
+#include "core/conf.h"
#include "window.h"
#include <FL/Fl.H>
-
-class geBox;
+#include <functional>
namespace giada::c::channel
{
namespace giada::v
{
+class geBox;
class geButton;
class gdKeyGrabber : public gdWindow
{
public:
- gdKeyGrabber(const c::channel::Data& d);
+ gdKeyGrabber(int key);
int handle(int e) override;
void rebuild() override;
-private:
- static void cb_clear(Fl_Widget* /*w*/, void* p);
- static void cb_cancel(Fl_Widget* /*w*/, void* p);
- void cb_clear();
- void cb_cancel();
+ /* onSetKey
+ Callback fired when this widget has grabbed an event. Returns a boolean value
+ to inform the widget if the key is valid. */
- void setButtonLabel(int key);
- void updateText(int key);
+ std::function<bool(int key)> onSetKey;
- const c::channel::Data& m_data;
+private:
+ int m_key;
geBox* m_text;
geButton* m_clear;
add(zone2);
add(zone3);
add(keyboard);
+
callback([](Fl_Widget* /*w*/, void* /*v*/) {
+ if (Fl::event() == FL_SHORTCUT && Fl::event_key() == FL_Escape)
+ return; // ignore Escape
m::init::closeMainWindow();
});
u::gui::setFavicon(this);
#include "utils/log.h"
#include <FL/Fl_Pack.H>
#include <cassert>
+#include <cstddef>
#ifdef WITH_VST
#include "core/plugins/plugin.h"
#endif
m_ok->callback(cb_close, (void*)this);
m_enable->callback(cb_enable, (void*)this);
- m_channel->add("Channel (any)");
- m_channel->add("Channel 1");
- m_channel->add("Channel 2");
- m_channel->add("Channel 3");
- m_channel->add("Channel 4");
- m_channel->add("Channel 5");
- m_channel->add("Channel 6");
- m_channel->add("Channel 7");
- m_channel->add("Channel 8");
- m_channel->add("Channel 9");
- m_channel->add("Channel 10");
- m_channel->add("Channel 11");
- m_channel->add("Channel 12");
- m_channel->add("Channel 13");
- m_channel->add("Channel 14");
- m_channel->add("Channel 15");
- m_channel->add("Channel 16");
- m_channel->callback(cb_setChannel, (void*)this);
+ m_channel->addItem("Channel (any)");
+ m_channel->addItem("Channel 1");
+ m_channel->addItem("Channel 2");
+ m_channel->addItem("Channel 3");
+ m_channel->addItem("Channel 4");
+ m_channel->addItem("Channel 5");
+ m_channel->addItem("Channel 6");
+ m_channel->addItem("Channel 7");
+ m_channel->addItem("Channel 8");
+ m_channel->addItem("Channel 9");
+ m_channel->addItem("Channel 10");
+ m_channel->addItem("Channel 11");
+ m_channel->addItem("Channel 12");
+ m_channel->addItem("Channel 13");
+ m_channel->addItem("Channel 14");
+ m_channel->addItem("Channel 15");
+ m_channel->addItem("Channel 16");
+ m_channel->onChange = [this](ID id) {
+ c::io::channel_setMidiInputFilter(m_data.channelId, id == 0 ? -1 : id - 1);
+ };
m_veloAsVol->callback(cb_veloAsVol, (void*)this);
static_cast<gePluginLearnerPack*>(m_container->getChild(i++))->update(plugin, m_data.enabled);
#endif
- m_channel->value(m_data.filter == -1 ? 0 : m_data.filter + 1);
+ m_channel->showItem(m_data.filter == -1 ? 0 : m_data.filter + 1);
if (m_data.enabled)
{
/* -------------------------------------------------------------------------- */
void gdMidiInputChannel::cb_enable(Fl_Widget* /*w*/, void* p) { ((gdMidiInputChannel*)p)->cb_enable(); }
-void gdMidiInputChannel::cb_setChannel(Fl_Widget* /*w*/, void* p) { ((gdMidiInputChannel*)p)->cb_setChannel(); }
void gdMidiInputChannel::cb_veloAsVol(Fl_Widget* /*w*/, void* p) { ((gdMidiInputChannel*)p)->cb_veloAsVol(); }
/* -------------------------------------------------------------------------- */
{
c::io::channel_enableVelocityAsVol(m_data.channelId, m_veloAsVol->value());
}
-
-/* -------------------------------------------------------------------------- */
-
-void gdMidiInputChannel::cb_setChannel()
-{
- c::io::channel_setMidiInputFilter(m_data.channelId,
- m_channel->value() == 0 ? -1 : m_channel->value() - 1);
-}
-} // namespace giada::v
\ No newline at end of file
+} // namespace giada::v
private:
static void cb_enable(Fl_Widget* /*w*/, void* p);
- static void cb_setChannel(Fl_Widget* /*w*/, void* p);
static void cb_veloAsVol(Fl_Widget* /*w*/, void* p);
void cb_enable();
- void cb_setChannel();
void cb_veloAsVol();
ID m_channelId;
m_ok->callback(cb_close, (void*)this);
m_enable->callback(cb_enable, (void*)this);
- m_channel->add("Channel (any)");
- m_channel->add("Channel 1");
- m_channel->add("Channel 2");
- m_channel->add("Channel 3");
- m_channel->add("Channel 4");
- m_channel->add("Channel 5");
- m_channel->add("Channel 6");
- m_channel->add("Channel 7");
- m_channel->add("Channel 8");
- m_channel->add("Channel 9");
- m_channel->add("Channel 10");
- m_channel->add("Channel 11");
- m_channel->add("Channel 12");
- m_channel->add("Channel 13");
- m_channel->add("Channel 14");
- m_channel->add("Channel 15");
- m_channel->add("Channel 16");
- m_channel->callback(cb_setChannel, (void*)this);
+ m_channel->addItem("Channel (any)");
+ m_channel->addItem("Channel 1");
+ m_channel->addItem("Channel 2");
+ m_channel->addItem("Channel 3");
+ m_channel->addItem("Channel 4");
+ m_channel->addItem("Channel 5");
+ m_channel->addItem("Channel 6");
+ m_channel->addItem("Channel 7");
+ m_channel->addItem("Channel 8");
+ m_channel->addItem("Channel 9");
+ m_channel->addItem("Channel 10");
+ m_channel->addItem("Channel 11");
+ m_channel->addItem("Channel 12");
+ m_channel->addItem("Channel 13");
+ m_channel->addItem("Channel 14");
+ m_channel->addItem("Channel 15");
+ m_channel->addItem("Channel 16");
+ m_channel->onChange = [](ID id) {
+ c::io::master_setMidiFilter(id == 0 ? -1 : id - 1);
+ };
u::gui::setFavicon(this);
m_data = c::io::master_getInputData();
m_enable->value(m_data.enabled);
- m_channel->value(m_data.filter - 1 ? 0 : m_data.filter + 1);
+ m_channel->showItem(m_data.filter - 1 ? 0 : m_data.filter + 1);
m_learners->update(m_data);
m_data.enabled ? m_channel->activate() : m_channel->deactivate();
/* -------------------------------------------------------------------------- */
void gdMidiInputMaster::cb_enable(Fl_Widget* /*w*/, void* p) { ((gdMidiInputMaster*)p)->cb_enable(); }
-void gdMidiInputMaster::cb_setChannel(Fl_Widget* /*w*/, void* p) { ((gdMidiInputMaster*)p)->cb_setChannel(); }
/* -------------------------------------------------------------------------- */
{
c::io::master_enableMidiLearn(m_enable->value());
}
-
-/* -------------------------------------------------------------------------- */
-
-void gdMidiInputMaster::cb_setChannel()
-{
- c::io::master_setMidiFilter(m_channel->value() == 0 ? -1 : m_channel->value() - 1);
-}
} // namespace giada::v
\ No newline at end of file
private:
static void cb_enable(Fl_Widget* /*w*/, void* p);
- static void cb_setChannel(Fl_Widget* /*w*/, void* p);
void cb_enable();
- void cb_setChannel();
c::io::Master_InputData m_data;
#include "utils/gui.h"
#include <FL/Fl_Pack.H>
-namespace giada
-{
-namespace v
+namespace giada::v
{
gdMidiOutputMidiCh::gdMidiOutputMidiCh(ID channelId)
: gdMidiOutputBase(300, 168, channelId)
add(m_learners);
add(m_close);
- m_chanListOut->add("Channel 1");
- m_chanListOut->add("Channel 2");
- m_chanListOut->add("Channel 3");
- m_chanListOut->add("Channel 4");
- m_chanListOut->add("Channel 5");
- m_chanListOut->add("Channel 6");
- m_chanListOut->add("Channel 7");
- m_chanListOut->add("Channel 8");
- m_chanListOut->add("Channel 9");
- m_chanListOut->add("Channel 10");
- m_chanListOut->add("Channel 11");
- m_chanListOut->add("Channel 12");
- m_chanListOut->add("Channel 13");
- m_chanListOut->add("Channel 14");
- m_chanListOut->add("Channel 15");
- m_chanListOut->add("Channel 16");
- m_chanListOut->value(0);
-
- m_chanListOut->callback(cb_setChannel, (void*)this);
+ m_chanListOut->addItem("Channel 1");
+ m_chanListOut->addItem("Channel 2");
+ m_chanListOut->addItem("Channel 3");
+ m_chanListOut->addItem("Channel 4");
+ m_chanListOut->addItem("Channel 5");
+ m_chanListOut->addItem("Channel 6");
+ m_chanListOut->addItem("Channel 7");
+ m_chanListOut->addItem("Channel 8");
+ m_chanListOut->addItem("Channel 9");
+ m_chanListOut->addItem("Channel 10");
+ m_chanListOut->addItem("Channel 11");
+ m_chanListOut->addItem("Channel 12");
+ m_chanListOut->addItem("Channel 13");
+ m_chanListOut->addItem("Channel 14");
+ m_chanListOut->addItem("Channel 15");
+ m_chanListOut->addItem("Channel 16");
+ m_chanListOut->showItem(0);
+ m_chanListOut->onChange = [this](ID id) {
+ c::io::channel_setMidiOutputFilter(m_channelId, id);
+ };
+
m_enableOut->callback(cb_enableOut, (void*)this);
m_enableLightning->callback(cb_enableLightning, (void*)this);
m_close->callback(cb_close, (void*)this);
assert(m_data.output.has_value());
m_learners->update(m_data);
- m_chanListOut->value(m_data.output->filter);
+ m_chanListOut->showItem(m_data.output->filter);
m_enableOut->value(m_data.output->enabled);
m_data.output->enabled ? m_chanListOut->activate() : m_chanListOut->deactivate();
/* -------------------------------------------------------------------------- */
void gdMidiOutputMidiCh::cb_enableOut(Fl_Widget* /*w*/, void* p) { ((gdMidiOutputMidiCh*)p)->cb_enableOut(); }
-void gdMidiOutputMidiCh::cb_setChannel(Fl_Widget* /*w*/, void* p) { ((gdMidiOutputMidiCh*)p)->cb_setChannel(); }
/* -------------------------------------------------------------------------- */
{
c::io::channel_enableMidiOutput(m_channelId, m_enableOut->value());
}
-
-/* -------------------------------------------------------------------------- */
-
-void gdMidiOutputMidiCh::cb_setChannel()
-{
- c::io::channel_setMidiOutputFilter(m_channelId, m_chanListOut->value());
-}
-} // namespace v
-} // namespace giada
+} // namespace giada::v
#include "midiOutputBase.h"
-namespace giada
-{
-namespace v
+namespace giada::v
{
class geChoice;
class gdMidiOutputMidiCh : public gdMidiOutputBase
void rebuild() override;
- private:
+private:
static void cb_enableOut(Fl_Widget* /*w*/, void* p);
- static void cb_setChannel(Fl_Widget* /*w*/, void* p);
void cb_enableOut();
- void cb_setChannel();
geCheck* m_enableOut;
geChoice* m_chanListOut;
};
-} // namespace v
-} // namespace giada
+} // namespace giada::v
#endif
/* top area */
Fl_Group* group_top = new Fl_Group(8, 8, w() - 16, 20);
- sortMethod = new geChoice(group_top->x() + 45, group_top->y(), 100, 20, "Sort by");
+ sortMethod = new geChoice(group_top->x(), group_top->y(), 180, 20, "Sort by", 0);
geBox* b1 = new geBox(sortMethod->x() + sortMethod->w(), group_top->y(), 100, 20); // spacer window border <-> menu
group_top->resizable(b1);
group_top->end();
end();
- sortMethod->add("Name");
- sortMethod->add("Category");
- sortMethod->add("Manufacturer");
- sortMethod->add("Format");
- sortMethod->callback(cb_sort, (void*)this);
- sortMethod->value(m_conf.pluginSortMethod);
+ sortMethod->addItem("Name");
+ sortMethod->addItem("Category");
+ sortMethod->addItem("Manufacturer");
+ sortMethod->addItem("Format");
+ sortMethod->showItem(m_conf.pluginSortMethod);
+ sortMethod->onChange = [this](ID id) {
+ c::plugin::sortPlugins(static_cast<m::PluginManager::SortMethod>(id));
+ browser->refresh();
+ };
addBtn->callback(cb_add, (void*)this);
addBtn->shortcut(FL_Enter);
m_conf.pluginChooserY = y();
m_conf.pluginChooserW = w();
m_conf.pluginChooserH = h();
- m_conf.pluginSortMethod = sortMethod->value();
+ m_conf.pluginSortMethod = sortMethod->getSelectedId();
}
/* -------------------------------------------------------------------------- */
void gdPluginChooser::cb_close(Fl_Widget* /*w*/, void* p) { ((gdPluginChooser*)p)->cb_close(); }
void gdPluginChooser::cb_add(Fl_Widget* /*w*/, void* p) { ((gdPluginChooser*)p)->cb_add(); }
-void gdPluginChooser::cb_sort(Fl_Widget* /*w*/, void* p) { ((gdPluginChooser*)p)->cb_sort(); }
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-void gdPluginChooser::cb_sort()
-{
- c::plugin::sortPlugins(static_cast<m::PluginManager::SortMethod>(sortMethod->value()));
- browser->refresh();
-}
-
-/* -------------------------------------------------------------------------- */
-
void gdPluginChooser::cb_add()
{
int pluginIndex = browser->value() - 3; // subtract header lines
private:
static void cb_close(Fl_Widget* /*w*/, void* p);
static void cb_add(Fl_Widget* /*w*/, void* p);
- static void cb_sort(Fl_Widget* /*w*/, void* p);
void cb_close();
void cb_add();
- void cb_sort();
m::Conf::Data& m_conf;
#include "window.h"
-class geBox;
class geSlider;
namespace giada::c::plugin
{
struct Plugin;
}
+
namespace giada::m
{
class Plugin;
}
+
namespace giada::v
{
+class geBox;
class geLiquidScroll;
class gdPluginWindow : public gdWindow
{
void gdPluginWindowGUI::closeEditor()
{
+ m_plugin.setResizeCallback(nullptr);
m_editor.reset();
}
} // namespace giada::v
m_conf.sampleEditorY = y();
m_conf.sampleEditorW = w();
m_conf.sampleEditorH = h();
- m_conf.sampleEditorGridVal = atoi(grid->text());
+ m_conf.sampleEditorGridVal = grid->getSelectedId();
m_conf.sampleEditorGridOn = snap->value();
c::sampleEditor::stopPreview();
reload->callback(cb_reload, (void*)this);
- grid->add("(off)");
- grid->add("2");
- grid->add("3");
- grid->add("4");
- grid->add("6");
- grid->add("8");
- grid->add("16");
- grid->add("32");
- grid->add("64");
+ grid->addItem("(off)");
+ grid->addItem("2");
+ grid->addItem("3");
+ grid->addItem("4");
+ grid->addItem("6");
+ grid->addItem("8");
+ grid->addItem("16");
+ grid->addItem("32");
+ grid->addItem("64");
grid->copy_tooltip("Grid frequency");
-
- if (m_conf.sampleEditorGridVal == 0)
- grid->value(0);
- else
- grid->value(grid->find_item(u::string::iToString(m_conf.sampleEditorGridVal).c_str()));
- grid->callback(cb_changeGrid, (void*)this);
+ grid->showItem(m_conf.sampleEditorGridVal);
+ grid->onChange = [this](ID) {
+ waveTools->waveform->setGridLevel(std::stoi(grid->getSelectedLabel()));
+ };
snap->value(m_conf.sampleEditorGridOn);
snap->copy_tooltip("Snap to grid");
void gdSampleEditor::cb_reload(Fl_Widget* /*w*/, void* p) { ((gdSampleEditor*)p)->cb_reload(); }
void gdSampleEditor::cb_zoomIn(Fl_Widget* /*w*/, void* p) { ((gdSampleEditor*)p)->cb_zoomIn(); }
void gdSampleEditor::cb_zoomOut(Fl_Widget* /*w*/, void* p) { ((gdSampleEditor*)p)->cb_zoomOut(); }
-void gdSampleEditor::cb_changeGrid(Fl_Widget* /*w*/, void* p) { ((gdSampleEditor*)p)->cb_changeGrid(); }
void gdSampleEditor::cb_enableSnap(Fl_Widget* /*w*/, void* p) { ((gdSampleEditor*)p)->cb_enableSnap(); }
void gdSampleEditor::cb_togglePreview(Fl_Widget* /*w*/, void* p) { ((gdSampleEditor*)p)->cb_togglePreview(); }
void gdSampleEditor::cb_rewindPreview(Fl_Widget* /*w*/, void* p) { ((gdSampleEditor*)p)->cb_rewindPreview(); }
/* -------------------------------------------------------------------------- */
-void gdSampleEditor::cb_changeGrid()
-{
- waveTools->waveform->setGridLevel(atoi(grid->text()));
-}
-
-/* -------------------------------------------------------------------------- */
-
void gdSampleEditor::updateInfo()
{
std::string bitDepth = m_data.waveBits != 0 ? u::string::iToString(m_data.waveBits) : "(unknown)";
#include "window.h"
class geCheck;
-class geBox;
class geStatusButton;
namespace giada::m
namespace giada::v
{
+class geBox;
class geButton;
class geChoice;
class gePack;
static void cb_reload(Fl_Widget* /*w*/, void* p);
static void cb_zoomIn(Fl_Widget* /*w*/, void* p);
static void cb_zoomOut(Fl_Widget* /*w*/, void* p);
- static void cb_changeGrid(Fl_Widget* /*w*/, void* p);
static void cb_enableSnap(Fl_Widget* /*w*/, void* p);
static void cb_togglePreview(Fl_Widget* /*w*/, void* p);
static void cb_rewindPreview(Fl_Widget* /*w*/, void* p);
void cb_reload();
void cb_zoomIn();
void cb_zoomOut();
- void cb_changeGrid();
void cb_enableSnap();
void cb_togglePreview();
void cb_rewindPreview();
#include "window.h"
#include "utils/log.h"
+#include <FL/Fl.H>
namespace giada::v
{
void gdWindow::cb_closeChild(Fl_Widget* w, void* /*p*/)
{
+ /* Disable default FLTK behavior where 'escape' closes the window. */
+ if (Fl::event() == FL_SHORTCUT && Fl::event_key() == FL_Escape)
+ return;
+
gdWindow* child = (gdWindow*)w;
if (child->getParent() != nullptr)
(child->getParent())->delSubWindow(child);
namespace giada::v
{
-Dispatcher::Dispatcher()
-: m_backspace(false)
-, m_end(false)
-, m_enter(false)
-, m_space(false)
-, m_esc(false)
-, m_key(false)
+Dispatcher::Dispatcher(const m::Conf::KeyBindings& k)
+: m_keyBindings(k)
+, m_keyPressed(-1)
{
}
if (event == FL_KEYDOWN)
{
- if (Fl::event_key() == FL_BackSpace && !m_backspace)
- {
- m_backspace = true;
+ if (m_keyPressed == Fl::event_key()) // Avoid key retrig
+ return;
+
+ m_keyPressed = Fl::event_key();
+
+ if (m_keyPressed == m_keyBindings.at(m::Conf::KEY_BIND_PLAY))
+ c::events::toggleSequencer(Thread::MAIN);
+ else if (m_keyPressed == m_keyBindings.at(m::Conf::KEY_BIND_REWIND))
c::events::rewindSequencer(Thread::MAIN);
- }
- else if (Fl::event_key() == FL_End && !m_end)
- {
- m_end = true;
- c::events::toggleInputRecording();
- }
- else if (Fl::event_key() == FL_Enter && !m_enter)
- {
- m_enter = true;
+ else if (m_keyPressed == m_keyBindings.at(m::Conf::KEY_BIND_RECORD_ACTIONS))
c::events::toggleActionRecording();
- }
- else if (Fl::event_key() == ' ' && !m_space)
- {
- m_space = true;
- c::events::toggleSequencer(Thread::MAIN);
- }
- else if (Fl::event_key() == FL_Escape && !m_esc)
+ else if (m_keyPressed == m_keyBindings.at(m::Conf::KEY_BIND_RECORD_INPUT))
+ c::events::toggleInputRecording();
+ else if (m_keyPressed == m_keyBindings.at(m::Conf::KEY_BIND_EXIT))
{
- m_esc = true;
- m::init::closeMainWindow();
+ c::events::stopActionRecording();
+ c::events::stopInputRecording();
}
- else if (!m_key)
+ else
{
- m_key = true;
onEventOccured();
dispatchChannels(event);
}
}
else if (event == FL_KEYUP)
{
- if (Fl::event_key() == FL_BackSpace)
- m_backspace = false;
- else if (Fl::event_key() == FL_End)
- m_end = false;
- else if (Fl::event_key() == ' ')
- m_space = false;
- else if (Fl::event_key() == FL_Enter)
- m_enter = false;
- else if (Fl::event_key() == FL_Escape)
- m_esc = false;
- else
- {
- m_key = false;
- dispatchChannels(event);
- }
+ m_keyPressed = -1;
+ dispatchChannels(event);
}
}
#ifndef G_V_DISPATCHER_H
#define G_V_DISPATCHER_H
+#include "core/conf.h"
#include "core/types.h"
#include <functional>
class Dispatcher final
{
public:
- Dispatcher();
+ Dispatcher(const m::Conf::KeyBindings& m_keyBindings);
/* dispatchKey
Processes a key pressed on the physical keyboard. */
void dispatchChannels(int event) const;
- bool m_backspace;
- bool m_end;
- bool m_enter;
- bool m_space;
- bool m_esc;
- bool m_key;
+ const m::Conf::KeyBindings& m_keyBindings;
+
+ int m_keyPressed;
};
} // namespace giada::v
* -------------------------------------------------------------------------- */
#include "drawing.h"
+#include "utils/gui.h"
#include <FL/Fl.H>
+#include <cassert>
namespace giada::v
{
fl_color(c);
fl_line(l.x1, l.y1, l.x2, l.y2);
}
+
+/* -------------------------------------------------------------------------- */
+
+void drawText(const std::string& s, geompp::Rect<int> b, Fl_Color c, int alignment)
+{
+ assert(!s.empty());
+
+ fl_color(c);
+ fl_draw(u::gui::truncate(s, b.w - 16).c_str(), b.x, b.y, b.w, b.h, alignment);
+}
} // namespace giada::v
#include "deps/geompp/src/rect.hpp"
#include <FL/fl_draw.H>
+#include <string>
namespace giada::v
{
-void drawRectf(geompp::Rect<int> r, Fl_Color c);
-void drawRect(geompp::Rect<int> r, Fl_Color c);
-void drawLine(geompp::Line<int> l, Fl_Color c);
+void drawRectf(geompp::Rect<int>, Fl_Color);
+void drawRect(geompp::Rect<int>, Fl_Color);
+void drawLine(geompp::Line<int>, Fl_Color);
+void drawText(const std::string&, geompp::Rect<int>, Fl_Color c, int alignment = FL_ALIGN_CENTER);
} // namespace giada::v
#endif
\ No newline at end of file
, m_conf(c)
{
gridType = new geChoice(x, y, 40, 20);
- gridType->add("1");
- gridType->add("2");
- gridType->add("3");
- gridType->add("4");
- gridType->add("6");
- gridType->add("8");
- gridType->add("16");
- gridType->add("32");
- gridType->value(0);
- gridType->callback(cb_changeType, (void*)this);
+ gridType->addItem("1");
+ gridType->addItem("2");
+ gridType->addItem("3");
+ gridType->addItem("4");
+ gridType->addItem("6");
+ gridType->addItem("8");
+ gridType->addItem("16");
+ gridType->addItem("32");
+ gridType->showItem(0);
+ gridType->onChange = [this](ID) {
+ window()->redraw();
+ };
active = new geCheck(gridType->x() + gridType->w() + 4, y, 20, 20);
- gridType->value(m_conf.actionEditorGridVal);
+ gridType->showItem(m_conf.actionEditorGridVal);
active->value(m_conf.actionEditorGridOn);
end();
geGridTool::~geGridTool()
{
- m_conf.actionEditorGridVal = gridType->value();
+ m_conf.actionEditorGridVal = gridType->getSelectedId();
m_conf.actionEditorGridOn = active->value();
}
/* -------------------------------------------------------------------------- */
-void geGridTool::cb_changeType(Fl_Widget* /*w*/, void* p) { ((geGridTool*)p)->cb_changeType(); }
-
-/* -------------------------------------------------------------------------- */
-
-void geGridTool::cb_changeType()
-{
- window()->redraw();
-}
-
-/* -------------------------------------------------------------------------- */
-
bool geGridTool::isOn() const
{
return active->value();
int geGridTool::getValue() const
{
- switch (gridType->value())
+ switch (gridType->getSelectedId())
{
case 0:
return 1;
geChoice* gridType;
geCheck* active;
-
- static void cb_changeType(Fl_Widget* /*w*/, void* p);
- void cb_changeType();
};
} // namespace giada::v
#include "utils/gui.h"
#include <FL/fl_draw.H>
+namespace giada::v
+{
geBox::geBox(int x, int y, int w, int h, const char* l, Fl_Align al)
: Fl_Box(x, y, w, h)
{
draw_label(); // draw_label also paints image, if any
else if (label() != nullptr)
{
- fl_color(G_COLOR_LIGHT_2);
+ fl_color(active() ? G_COLOR_LIGHT_2 : G_COLOR_GREY_4);
fl_font(FL_HELVETICA, G_GUI_FONT_SIZE_BASE);
- fl_draw(giada::u::gui::truncate(label(), w() - 8).c_str(), x() + 4, y(), w() - 4, h(), align());
+ fl_draw(giada::u::gui::truncate(label(), w()).c_str(), x(), y(), w(), h(), align());
}
-}
\ No newline at end of file
+}
+} // namespace giada::v
\ No newline at end of file
#include <FL/Fl_Box.H>
+namespace giada::v
+{
class geBox : public Fl_Box
{
public:
void draw() override;
};
+} // namespace giada::v
#endif
*
* -------------------------------------------------------------------------- */
-#include "choice.h"
+#include "gui/elems/basics/choice.h"
#include "core/const.h"
+#include "gui/drawing.h"
#include "utils/gui.h"
#include "utils/vector.h"
#include <FL/fl_draw.H>
#include <cassert>
-#include <string>
namespace giada::v
{
-geChoice::geChoice(int x, int y, int w, int h, const char* l, bool ang)
-: Fl_Choice(x, y, w, h, l)
-, m_angle(ang)
+geChoice::geMenu::geMenu(int x, int y, int w, int h)
+: Fl_Choice(x, y, w, h)
{
labelsize(G_GUI_FONT_SIZE_BASE);
labelcolor(G_COLOR_LIGHT_2);
/* -------------------------------------------------------------------------- */
-void geChoice::cb_onChange(Fl_Widget* /*w*/, void* p) { (static_cast<geChoice*>(p))->cb_onChange(); }
+void geChoice::geMenu::draw()
+{
+ geompp::Rect<int> bounds(x(), y(), w(), h());
+
+ drawRectf(bounds, G_COLOR_GREY_2); // background
+ drawRect(bounds, static_cast<Fl_Color>(G_COLOR_GREY_4)); // border
+ fl_polygon(x() + w() - 8, y() + h() - 1, x() + w() - 1, y() + h() - 8, x() + w() - 1, y() + h() - 1);
+ if (value() != -1)
+ drawText(text(value()), bounds, active() ? G_COLOR_LIGHT_2 : G_COLOR_GREY_4);
+}
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-void geChoice::cb_onChange()
+geChoice::geChoice(int x, int y, int w, int h, const char* l, int labelWidth)
+: geFlex(x, y, w, h, Direction::HORIZONTAL, G_GUI_INNER_MARGIN)
+, m_text(nullptr)
+, m_menu(nullptr)
{
- if (onChange != nullptr)
- onChange(getSelectedId());
+ if (l != nullptr)
+ {
+ m_text = new geBox(l, FL_ALIGN_RIGHT);
+ add(m_text, labelWidth != 0 ? labelWidth : u::gui::getStringRect(l).w);
+ }
+ m_menu = new geMenu(x, y, w, h);
+ add(m_menu);
+ end();
}
/* -------------------------------------------------------------------------- */
-void geChoice::draw()
+geChoice::geChoice(const char* l, int labelWidth)
+: geChoice(0, 0, 0, 0, l, labelWidth)
{
- fl_rectf(x(), y(), w(), h(), G_COLOR_GREY_2); // bg
- fl_rect(x(), y(), w(), h(), static_cast<Fl_Color>(G_COLOR_GREY_4)); // border
- if (m_angle)
- fl_polygon(x() + w() - 8, y() + h() - 1, x() + w() - 1, y() + h() - 8, x() + w() - 1, y() + h() - 1);
+}
- /* pick up the text() from the selected item (value()) and print it in
- * the box and avoid overflows */
+/* -------------------------------------------------------------------------- */
- fl_color(!active() ? G_COLOR_GREY_4 : G_COLOR_LIGHT_2);
- if (value() != -1)
- fl_draw(u::gui::truncate(text(value()), w() - 16).c_str(), x(), y(), w(), h(), FL_ALIGN_CENTER);
+void geChoice::cb_onChange(Fl_Widget* /*w*/, void* p) { (static_cast<geChoice*>(p))->cb_onChange(); }
+
+/* -------------------------------------------------------------------------- */
+
+void geChoice::cb_onChange()
+{
+ if (onChange != nullptr)
+ onChange(getSelectedId());
}
/* -------------------------------------------------------------------------- */
ID geChoice::getSelectedId() const
{
- return value() == -1 ? -1 : m_ids.at(value());
+ return m_menu->value() == -1 ? -1 : m_ids.at(m_menu->value());
}
/* -------------------------------------------------------------------------- */
void geChoice::addItem(const std::string& label, ID id)
{
- Fl_Choice::add(label.c_str(), 0, cb_onChange, static_cast<void*>(this));
+ m_menu->add(label.c_str(), 0, cb_onChange, static_cast<void*>(this));
if (id != -1)
m_ids.push_back(id);
void geChoice::showItem(const std::string& label)
{
- value(find_index(label.c_str()));
+ m_menu->value(m_menu->find_index(label.c_str()));
}
void geChoice::showItem(ID id)
{
- value(u::vector::indexOf(m_ids, id));
+ m_menu->value(u::vector::indexOf(m_ids, id));
+}
+
+/* -------------------------------------------------------------------------- */
+
+void geChoice::activate()
+{
+ geFlex::activate();
+ m_menu->activate();
+ if (m_text != nullptr)
+ m_text->activate();
+}
+
+void geChoice::deactivate()
+{
+ geFlex::deactivate();
+ m_menu->deactivate();
+ if (m_text != nullptr)
+ m_text->deactivate();
+}
+
+/* -------------------------------------------------------------------------- */
+
+std::string geChoice::getSelectedLabel() const
+{
+ return m_menu->text();
+}
+
+/* -------------------------------------------------------------------------- */
+
+std::size_t geChoice::countItems() const
+{
+ return m_ids.size();
}
/* -------------------------------------------------------------------------- */
void geChoice::clear()
{
- Fl_Choice::clear();
+ m_menu->clear();
m_ids.clear();
}
#define GE_CHOICE_H
#include "core/types.h"
+#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/flex.h"
#include <FL/Fl_Choice.H>
#include <functional>
#include <string>
namespace giada::v
{
-class geChoice : public Fl_Choice
+class geChoice : public geFlex
{
public:
- geChoice(int x, int y, int w, int h, const char* l = 0, bool angle = true);
- void draw() override;
+ /* geChoice
+ Constructors. If label is != nullptr but labelWidth is not specified, the
+ label width is automatically computed and adjusted accordingly. */
- ID getSelectedId() const;
+ geChoice(int x, int y, int w, int h, const char* l = nullptr, int labelWidth = 0);
+ geChoice(const char* l = nullptr, int labelWidth = 0);
+
+ ID getSelectedId() const;
+ std::string getSelectedLabel() const;
+ std::size_t countItems() const;
/* addItem
- Adds a new item with a certain ID. Pass id = -1 to auto-increment it. */
+ Adds a new item with a certain ID. Pass id = -1 to auto-increment it (ID
+ starts from 0). */
void addItem(const std::string& label, ID id = -1);
void showItem(const std::string& label);
- void showItem(ID id);
+ void showItem(ID);
+ void activate();
+ void deactivate();
+
void clear();
std::function<void(ID)> onChange = nullptr;
private:
+ class geMenu : public Fl_Choice
+ {
+ public:
+ geMenu(int x, int y, int w, int h);
+ void draw() override;
+ };
+
static void cb_onChange(Fl_Widget* w, void* p);
void cb_onChange();
- bool m_angle;
+ geBox* m_text;
+ geMenu* m_menu;
std::vector<ID> m_ids;
};
} // namespace giada::v
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
#include "flex.h"
+#include <cstddef>
#include <numeric>
namespace giada::v
Fl_Group::end();
resize(x(), y(), w(), h());
}
-} // namespace giada::v
\ No newline at end of file
+} // namespace giada::v
* -------------------------------------------------------------------------- */
#include "group.h"
+#include <cstddef>
#include <algorithm>
namespace giada
return m_widgets.at(m_widgets.size() - 1); // Throws std::out_of_range in case
}
} // namespace v
-} // namespace giada
\ No newline at end of file
+} // namespace giada
#define GE_GROUP_H
#include <FL/Fl_Group.H>
+#include <cstddef>
#include <vector>
namespace giada
/* -------------------------------------------------------------------------- */
+geLiquidScroll::geLiquidScroll(geompp::Rect<int> r, Direction d)
+: geLiquidScroll(r.x, r.y, r.w, r.h, d)
+{
+}
+
+/* -------------------------------------------------------------------------- */
+
void geLiquidScroll::resize(int X, int Y, int W, int H)
{
const int nc = children() - 2; // skip hscrollbar and vscrollbar
#define GE_LIQUID_SCROLL_H
#include "core/const.h"
+#include "deps/geompp/src/rect.hpp"
#include "gui/types.h"
#include "scroll.h"
{
public:
geLiquidScroll(int x, int y, int w, int h, Direction d);
+ geLiquidScroll(geompp::Rect<int>, Direction d);
void resize(int x, int y, int w, int h) override;
#include <FL/Fl_Group.H>
#include <FL/fl_draw.H>
#include <cassert>
+#include <cstddef>
#include <vector>
namespace giada::v
const int curr = m_direction == Direction::VERTICAL ? wd.h() : wd.w();
handleDrag(p - curr);
}
-} // namespace giada::v
\ No newline at end of file
+} // namespace giada::v
#include "boxtypes.h"
#include "core/const.h"
#include <cassert>
+#include <cstddef>
namespace giada
{
return m_widgets.at(m_widgets.size() - 1); // Throws std::out_of_range in case
}
} // namespace v
-} // namespace giada
\ No newline at end of file
+} // namespace giada
#include "gui/elems/basics/pack.h"
#include "gui/elems/basics/scroll.h"
+#include <cstddef>
namespace giada
{
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+#include "gui/elems/basics/tabs.h"
+#include "core/const.h"
+#include "gui/elems/basics/boxtypes.h"
+
+namespace giada::v
+{
+geTabs::geTabs(geompp::Rect<int> r)
+: Fl_Tabs(r.x, r.y, r.w, r.h)
+{
+ box(G_CUSTOM_BORDER_BOX);
+ labelcolor(G_COLOR_LIGHT_2);
+ end();
+}
+
+/* -------------------------------------------------------------------------- */
+
+void geTabs::add(Fl_Widget* wg)
+{
+ constexpr int TAB_HEIGHT = 25;
+
+ wg->resize(x(), y() + TAB_HEIGHT, w(), h() - TAB_HEIGHT);
+ wg->labelsize(G_GUI_FONT_SIZE_BASE);
+ wg->selection_color(G_COLOR_GREY_4);
+
+ Fl_Tabs::add(wg);
+ resizable(wg);// To keep the tab height constant during resizing
+}
+} // namespace giada::v
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+#ifndef GE_TABS_H
+#define GE_TABS_H
+
+#include "deps/geompp/src/rect.hpp"
+#include <FL/Fl_Tabs.H>
+
+namespace giada::v
+{
+class geTabs : public Fl_Tabs
+{
+public:
+ geTabs(geompp::Rect<int>);
+
+ void add(Fl_Widget*);
+};
+} // namespace giada::v
+
+#endif
\ No newline at end of file
#include "core/kernelAudio.h"
#include "deps/rtaudio/RtAudio.h"
#include "gui/elems/basics/box.h"
-#include "gui/elems/basics/button.h"
#include "gui/elems/basics/check.h"
+#include "gui/elems/basics/choice.h"
+#include "gui/elems/basics/flex.h"
#include "gui/elems/basics/input.h"
#include "utils/string.h"
#include <string>
+constexpr int LABEL_WIDTH = 110;
+
namespace giada::v
{
-geTabAudio::geDeviceMenu::geDeviceMenu(int x, int y, int w, int h, const char* l,
- const std::vector<c::config::AudioDeviceData>& devices)
-: geChoice(x, y, w, h, l)
+geTabAudio::geDeviceMenu::geDeviceMenu(const char* l, const std::vector<c::config::AudioDeviceData>& devices)
+: geChoice(l, LABEL_WIDTH)
{
if (devices.size() == 0)
{
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-geTabAudio::geChannelMenu::geChannelMenu(int x, int y, int w, int h, const char* l,
- const c::config::AudioDeviceData& data)
-: geChoice(x, y, w, h, l)
+geTabAudio::geChannelMenu::geChannelMenu(const char* l, const c::config::AudioDeviceData& data)
+: geChoice(l, LABEL_WIDTH)
, m_data(data)
{
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-geTabAudio::geTabAudio(int X, int Y, int W, int H)
-: Fl_Group(X, Y, W, H, "Sound System")
+geTabAudio::geTabAudio(geompp::Rect<int> bounds)
+: Fl_Group(bounds.x, bounds.y, bounds.w, bounds.h, "Audio")
, m_data(c::config::getAudioData())
, m_initialApi(m_data.api)
{
- begin();
- soundsys = new geChoice(x() + 114, y() + 9, 250, 20, "System");
- buffersize = new geChoice(x() + 114, y() + 37, 55, 20, "Buffer size");
- samplerate = new geChoice(x() + 304, y() + 37, 60, 20, "Sample rate");
- sounddevOut = new geDeviceMenu(x() + 114, y() + 65, 250, 20, "Output device", m_data.outputDevices);
- channelsOut = new geChannelMenu(x() + 114, y() + 93, 55, 20, "Output channels", m_data.outputDevice);
- limitOutput = new geCheck(x() + 177, y() + 93, 100, 20, "Limit output");
- sounddevIn = new geDeviceMenu(x() + 114, y() + 121, 234, 20, "Input device", m_data.inputDevices);
- enableIn = new geCheck(sounddevIn->x() + sounddevIn->w() + 4, sounddevIn->y(), 12, 20);
- channelsIn = new geChannelMenu(x() + 114, y() + 149, 55, 20, "Input channels", m_data.inputDevice);
- recTriggerLevel = new geInput(x() + 309, y() + 149, 55, 20, "Rec threshold (dB)");
- rsmpQuality = new geChoice(x() + 114, y() + 177, 250, 20, "Resampling");
- new geBox(x(), rsmpQuality->y() + rsmpQuality->h() + 8, w(), 92, "Restart Giada for the changes to take effect.");
end();
- labelsize(G_GUI_FONT_SIZE_BASE);
- selection_color(G_COLOR_GREY_4);
+ geFlex* body = new geFlex(bounds.reduced(G_GUI_OUTER_MARGIN), Direction::VERTICAL, G_GUI_OUTER_MARGIN);
+ {
+ soundsys = new geChoice("System", LABEL_WIDTH);
+
+ geFlex* line1 = new geFlex(Direction::HORIZONTAL, G_GUI_OUTER_MARGIN);
+ {
+ buffersize = new geChoice("Buffer size", LABEL_WIDTH);
+ samplerate = new geChoice("Sample rate", LABEL_WIDTH);
+
+ line1->add(buffersize, 180);
+ line1->add(samplerate, 180);
+ line1->end();
+ }
+
+ sounddevOut = new geDeviceMenu("Output device", m_data.outputDevices);
+
+ geFlex* line2 = new geFlex(Direction::HORIZONTAL, G_GUI_OUTER_MARGIN);
+ {
+ channelsOut = new geChannelMenu("Output channels", m_data.outputDevice);
+ limitOutput = new geCheck(x() + 177, y() + 93, 100, 20, "Limit output");
+
+ line2->add(channelsOut, 180);
+ line2->add(limitOutput);
+ line2->end();
+ }
+
+ geFlex* line3 = new geFlex(Direction::HORIZONTAL, G_GUI_OUTER_MARGIN);
+ {
+ sounddevIn = new geDeviceMenu("Input device", m_data.inputDevices);
+ enableIn = new geCheck(0, 0, 0, 0);
+
+ line3->add(sounddevIn);
+ line3->add(enableIn, 12);
+ line3->end();
+ }
+
+ geFlex* line4 = new geFlex(Direction::HORIZONTAL, G_GUI_OUTER_MARGIN);
+ {
+ channelsIn = new geChannelMenu("Input channels", m_data.inputDevice);
+ recTriggerLevel = new geInput(0, 0, 0, 0, "Rec threshold (dB)");
+
+ line4->add(channelsIn, 180);
+ line4->add(new geBox(), 132); // TODO - temporary hack for geInput's label
+ line4->add(recTriggerLevel, 40);
+ line4->end();
+ }
+
+ rsmpQuality = new geChoice("Resampling", LABEL_WIDTH);
+
+ body->add(soundsys, 20);
+ body->add(line1, 20);
+ body->add(sounddevOut, 20);
+ body->add(line2, 20);
+ body->add(line3, 20);
+ body->add(line4, 20);
+ body->add(rsmpQuality, 20);
+ body->add(new geBox("Restart Giada for the changes to take effect."));
+ body->end();
+ }
+
+ add(body);
+ resizable(body);
for (const auto& [key, value] : m_data.apis)
soundsys->addItem(value.c_str(), key);
#ifndef GE_TAB_AUDIO_H
#define GE_TAB_AUDIO_H
+#include "deps/geompp/src/rect.hpp"
#include "glue/config.h"
#include "gui/elems/basics/choice.h"
#include <FL/Fl_Group.H>
namespace giada::v
{
-class geButton;
class geTabAudio : public Fl_Group
{
public:
struct geDeviceMenu : public geChoice
{
- geDeviceMenu(int x, int y, int w, int h, const char* l, const std::vector<c::config::AudioDeviceData>&);
+ geDeviceMenu(const char* l, const std::vector<c::config::AudioDeviceData>&);
};
struct geChannelMenu : public geChoice
{
- geChannelMenu(int x, int y, int w, int h, const char* l, const c::config::AudioDeviceData&);
+ geChannelMenu(const char* l, const c::config::AudioDeviceData&);
int getChannelsCount() const;
int getChannelsStart() const;
c::config::AudioDeviceData m_data;
};
- geTabAudio(int x, int y, int w, int h);
+ geTabAudio(geompp::Rect<int>);
void save();
#include "core/const.h"
#include "gui/elems/basics/box.h"
#include "gui/elems/basics/check.h"
+#include "gui/elems/basics/flex.h"
#include <FL/Fl_Pack.H>
namespace giada::v
{
-geTabBehaviors::geTabBehaviors(int X, int Y, int W, int H, m::Conf::Data& c)
-: Fl_Group(X, Y, W, H)
-, m_container(X, Y + G_GUI_OUTER_MARGIN, Direction::VERTICAL, G_GUI_OUTER_MARGIN)
-, m_chansStopOnSeqHalt(0, 0, 280, 30, "Dynamic channels stop immediately when the sequencer\nis halted")
-, m_treatRecsAsLoops(0, 0, 280, 20, "Treat one shot channels with actions as loops")
-, m_inputMonitorDefaultOn(0, 0, 280, 20, "New sample channels have input monitor on by default")
-, m_overdubProtectionDefaultOn(0, 0, 280, 30, "New sample channels have overdub protection on\nby default")
+geTabBehaviors::geTabBehaviors(geompp::Rect<int> bounds, m::Conf::Data& c)
+: Fl_Group(bounds.x, bounds.y, bounds.w, bounds.h, "Behaviors")
, m_conf(c)
{
end();
- label("Behaviors");
- labelsize(G_GUI_FONT_SIZE_BASE);
- selection_color(G_COLOR_GREY_4);
+ geFlex* body = new geFlex(bounds.reduced(G_GUI_OUTER_MARGIN), Direction::VERTICAL, G_GUI_OUTER_MARGIN);
+ {
+ m_chansStopOnSeqHalt = new geCheck(0, 0, 0, 0, "Dynamic channels stop immediately when the sequencer\nis halted");
+ m_treatRecsAsLoops = new geCheck(0, 0, 0, 0, "Treat one shot channels with actions as loops");
+ m_inputMonitorDefaultOn = new geCheck(0, 0, 0, 0, "New sample channels have input monitor on by default");
+ m_overdubProtectionDefaultOn = new geCheck(0, 0, 0, 0, "New sample channels have overdub protection on\nby default");
- m_container.add(&m_chansStopOnSeqHalt);
- m_container.add(&m_treatRecsAsLoops);
- m_container.add(&m_inputMonitorDefaultOn);
- m_container.add(&m_overdubProtectionDefaultOn);
+ body->add(m_chansStopOnSeqHalt, 30);
+ body->add(m_treatRecsAsLoops, 20);
+ body->add(m_inputMonitorDefaultOn, 20);
+ body->add(m_overdubProtectionDefaultOn, 30);
+ body->end();
+ };
- add(m_container);
+ add(body);
+ resizable(body);
- m_chansStopOnSeqHalt.value(m_conf.chansStopOnSeqHalt);
- m_treatRecsAsLoops.value(m_conf.treatRecsAsLoops);
- m_inputMonitorDefaultOn.value(m_conf.inputMonitorDefaultOn);
- m_overdubProtectionDefaultOn.value(m_conf.overdubProtectionDefaultOn);
+ m_chansStopOnSeqHalt->value(m_conf.chansStopOnSeqHalt);
+ m_treatRecsAsLoops->value(m_conf.treatRecsAsLoops);
+ m_inputMonitorDefaultOn->value(m_conf.inputMonitorDefaultOn);
+ m_overdubProtectionDefaultOn->value(m_conf.overdubProtectionDefaultOn);
}
/* -------------------------------------------------------------------------- */
void geTabBehaviors::save()
{
- m_conf.chansStopOnSeqHalt = m_chansStopOnSeqHalt.value();
- m_conf.treatRecsAsLoops = m_treatRecsAsLoops.value();
- m_conf.inputMonitorDefaultOn = m_inputMonitorDefaultOn.value();
- m_conf.overdubProtectionDefaultOn = m_overdubProtectionDefaultOn.value();
+ m_conf.chansStopOnSeqHalt = m_chansStopOnSeqHalt->value();
+ m_conf.treatRecsAsLoops = m_treatRecsAsLoops->value();
+ m_conf.inputMonitorDefaultOn = m_inputMonitorDefaultOn->value();
+ m_conf.overdubProtectionDefaultOn = m_overdubProtectionDefaultOn->value();
}
} // namespace giada::v
\ No newline at end of file
#define GE_TAB_BEHAVIORS_H
#include "core/conf.h"
+#include "deps/geompp/src/rect.hpp"
#include "gui/elems/basics/check.h"
-#include "gui/elems/basics/pack.h"
#include <FL/Fl_Group.H>
namespace giada::v
class geTabBehaviors : public Fl_Group
{
public:
- geTabBehaviors(int x, int y, int w, int h, m::Conf::Data&);
+ geTabBehaviors(geompp::Rect<int>, m::Conf::Data&);
void save();
private:
- gePack m_container;
- geCheck m_chansStopOnSeqHalt;
- geCheck m_treatRecsAsLoops;
- geCheck m_inputMonitorDefaultOn;
- geCheck m_overdubProtectionDefaultOn;
+ geCheck* m_chansStopOnSeqHalt;
+ geCheck* m_treatRecsAsLoops;
+ geCheck* m_inputMonitorDefaultOn;
+ geCheck* m_overdubProtectionDefaultOn;
m::Conf::Data& m_conf;
};
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+#include "gui/elems/config/tabBindings.h"
+#include "core/const.h"
+#include "gui/elems/basics/liquidScroll.h"
+#include "gui/elems/keyBinder.h"
+#include "utils/gui.h"
+
+namespace giada::v
+{
+geTabBindings::geTabBindings(geompp::Rect<int> bounds, m::Conf::Data& conf)
+: Fl_Group(bounds.x, bounds.y, bounds.w, bounds.h, "Key Bindings")
+{
+ end();
+
+ geFlex* body = new geFlex(bounds.reduced(G_GUI_OUTER_MARGIN), Direction::VERTICAL, G_GUI_INNER_MARGIN);
+ {
+ play = new geKeyBinder("Play", conf.keyBindings.find(m::Conf::KEY_BIND_PLAY)->second);
+ rewind = new geKeyBinder("Rewind", conf.keyBindings.find(m::Conf::KEY_BIND_REWIND)->second);
+ recordActions = new geKeyBinder("Record actions", conf.keyBindings.find(m::Conf::KEY_BIND_RECORD_ACTIONS)->second);
+ recordInput = new geKeyBinder("Record audio", conf.keyBindings.find(m::Conf::KEY_BIND_RECORD_INPUT)->second);
+ exit = new geKeyBinder("Exit", conf.keyBindings.find(m::Conf::KEY_BIND_EXIT)->second);
+
+ body->add(play, G_GUI_UNIT);
+ body->add(rewind, G_GUI_UNIT);
+ body->add(recordActions, G_GUI_UNIT);
+ body->add(recordInput, G_GUI_UNIT);
+ body->add(exit, G_GUI_UNIT);
+ body->end();
+ }
+
+ add(body);
+ resizable(body);
+}
+} // namespace giada::v
\ No newline at end of file
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+#ifndef GE_CONFIG_TAB_BINDINGS_H
+#define GE_CONFIG_TAB_BINDINGS_H
+
+#include "core/conf.h"
+#include "deps/geompp/src/rect.hpp"
+#include <FL/Fl_Group.H>
+
+class geCheck;
+class geInput;
+
+namespace giada::v
+{
+class geKeyBinder;
+class geTabBindings : public Fl_Group
+{
+public:
+ geTabBindings(geompp::Rect<int>, m::Conf::Data&);
+
+private:
+ geKeyBinder* play;
+ geKeyBinder* rewind;
+ geKeyBinder* recordActions;
+ geKeyBinder* recordInput;
+ geKeyBinder* exit;
+};
+} // namespace giada::v
+
+#endif
#include "utils/gui.h"
#include <string>
+constexpr int LABEL_WIDTH = 120;
+
namespace giada::v
{
-geTabMidi::geMenu::geMenu(int x, int y, int w, int h, const char* l,
- const std::vector<std::string>& data, const std::string& msgIfNotFound)
-: geChoice(x, y, w, h, l)
+geTabMidi::geMenu::geMenu(const char* l, const std::vector<std::string>& data,
+ const std::string& msgIfNotFound)
+: geChoice(l, LABEL_WIDTH)
{
if (data.size() == 0)
{
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-geTabMidi::geTabMidi(int X, int Y, int W, int H)
-: Fl_Group(X, Y, W, H, "MIDI")
+geTabMidi::geTabMidi(geompp::Rect<int> bounds)
+: Fl_Group(bounds.x, bounds.y, bounds.w, bounds.h, "MIDI")
, m_data(c::config::getMidiData())
, m_initialApi(m_data.api)
{
- begin();
- system = new geChoice(x() + w() - 250, y() + 9, 250, 20, "System");
- portOut = new geMenu(x() + w() - 250, system->y() + system->h() + 8, 234, 20, "Output port", m_data.outPorts, "-- no ports found --");
- enableOut = new geCheck(portOut->x() + portOut->w() + 4, portOut->y(), 12, 20);
- portIn = new geMenu(x() + w() - 250, portOut->y() + portOut->h() + 8, 234, 20, "Input port", m_data.inPorts, "-- no ports found --");
- enableIn = new geCheck(portIn->x() + portIn->w() + 4, portIn->y(), 12, 20);
- midiMap = new geMenu(x() + w() - 250, portIn->y() + portIn->h() + 8, 250, 20, "Output Midi Map", m_data.midiMaps, "(no MIDI maps available)");
- sync = new geChoice(x() + w() - 250, midiMap->y() + midiMap->h() + 8, 250, 20, "Sync");
- new geBox(x(), sync->y() + sync->h() + 8, w(), h() - 150, "Restart Giada for the changes to take effect.");
end();
- labelsize(G_GUI_FONT_SIZE_BASE);
- selection_color(G_COLOR_GREY_4);
+ geFlex* body = new geFlex(bounds.reduced(G_GUI_OUTER_MARGIN), Direction::VERTICAL, G_GUI_OUTER_MARGIN);
+ {
+ system = new geChoice("System", LABEL_WIDTH);
+
+ geFlex* line1 = new geFlex(Direction::HORIZONTAL, G_GUI_OUTER_MARGIN);
+ {
+ portOut = new geMenu("Output port", m_data.outPorts, "-- no ports found --");
+ enableOut = new geCheck(0, 0, 0, 0);
+
+ line1->add(portOut);
+ line1->add(enableOut, 12);
+ line1->end();
+ }
+
+ geFlex* line2 = new geFlex(Direction::HORIZONTAL, G_GUI_OUTER_MARGIN);
+ {
+ portIn = new geMenu("Input port", m_data.inPorts, "-- no ports found --");
+ enableIn = new geCheck(0, 0, 0, 0);
+
+ line2->add(portIn);
+ line2->add(enableIn, 12);
+ line2->end();
+ }
+
+ midiMap = new geMenu("Output Midi Map", m_data.midiMaps, "(no MIDI maps available)");
+ sync = new geChoice("Sync", LABEL_WIDTH);
+
+ body->add(system, 20);
+ body->add(line1, 20);
+ body->add(line2, 20);
+ body->add(midiMap, 20);
+ body->add(sync, 20);
+ body->add(new geBox("Restart Giada for the changes to take effect."));
+ body->end();
+ }
+
+ add(body);
+ resizable(body);
for (const auto& [key, value] : m_data.apis)
system->addItem(value.c_str(), key);
enableOut->onChange = [this](bool b) {
if (b)
{
- m_data.outPort = portOut->value();
+ m_data.outPort = portOut->getSelectedId();
portOut->activate();
}
else
enableIn->onChange = [this](bool b) {
if (b)
{
- m_data.inPort = portIn->value();
+ m_data.inPort = portIn->getSelectedId();
portIn->activate();
}
else
#ifndef GE_TAB_MIDI_H
#define GE_TAB_MIDI_H
+#include "deps/geompp/src/rect.hpp"
#include "glue/config.h"
#include "gui/elems/basics/choice.h"
#include <FL/Fl_Group.H>
public:
struct geMenu : public geChoice
{
- geMenu(int x, int y, int w, int h, const char* l, const std::vector<std::string>&,
- const std::string& msgIfNotFound);
+ geMenu(const char* l, const std::vector<std::string>&, const std::string& msgIfNotFound);
};
- geTabMidi(int x, int y, int w, int h);
+ geTabMidi(geompp::Rect<int>);
void save() const;
#include "tabMisc.h"
#include "core/const.h"
+#include "gui/elems/basics/choice.h"
+
+constexpr int LABEL_WIDTH = 120;
namespace giada::v
{
-geTabMisc::geTabMisc(int X, int Y, int W)
-: geGroup(X, Y)
+geTabMisc::geTabMisc(geompp::Rect<int> bounds)
+: Fl_Group(bounds.x, bounds.y, bounds.w, bounds.h, "Misc")
, m_data(c::config::getMiscData())
-, m_debugMsg(W - 230, 9, 230, 20, "Debug messages")
-, m_tooltips(W - 230, 37, 230, 20, "Tooltips")
{
- add(&m_debugMsg);
- add(&m_tooltips);
+ end();
+
+ geFlex* body = new geFlex(bounds.reduced(G_GUI_OUTER_MARGIN), Direction::VERTICAL, G_GUI_OUTER_MARGIN);
+ {
+ m_debugMsg = new geChoice("Debug messages", LABEL_WIDTH);
+ m_tooltips = new geChoice("Tooltips", LABEL_WIDTH);
- m_debugMsg.addItem("Disabled");
- m_debugMsg.addItem("To standard output");
- m_debugMsg.addItem("To file");
- m_debugMsg.onChange = [this](ID id) { m_data.logMode = id; };
+ body->add(m_debugMsg, 20);
+ body->add(m_tooltips, 20);
+ body->end();
+ }
- m_tooltips.addItem("Disabled");
- m_tooltips.addItem("Enabled");
- m_tooltips.onChange = [this](ID id) { m_data.showTooltips = id; };
+ add(body);
+ resizable(body);
- m_debugMsg.showItem(m_data.logMode);
- m_tooltips.showItem(m_data.showTooltips);
+ m_debugMsg->addItem("Disabled");
+ m_debugMsg->addItem("To standard output");
+ m_debugMsg->addItem("To file");
+ m_debugMsg->showItem(m_data.logMode);
+ m_debugMsg->onChange = [this](ID id) { m_data.logMode = id; };
- copy_label("Misc");
- labelsize(G_GUI_FONT_SIZE_BASE);
- selection_color(G_COLOR_GREY_4);
+ m_tooltips->addItem("Disabled");
+ m_tooltips->addItem("Enabled");
+ m_tooltips->showItem(m_data.showTooltips);
+ m_tooltips->onChange = [this](ID id) { m_data.showTooltips = id; };
}
/* -------------------------------------------------------------------------- */
#ifndef GE_TAB_MISC_H
#define GE_TAB_MISC_H
+#include "deps/geompp/src/rect.hpp"
#include "glue/config.h"
-#include "gui/elems/basics/choice.h"
-#include "gui/elems/basics/group.h"
+#include <FL/Fl_Group.H>
namespace giada::v
{
-class geTabMisc : public geGroup
+class geChoice;
+class geTabMisc : public Fl_Group
{
public:
- geTabMisc(int x, int y, int w);
+ geTabMisc(geompp::Rect<int>);
void save();
private:
c::config::MiscData m_data;
- geChoice m_debugMsg;
- geChoice m_tooltips;
+ geChoice* m_debugMsg;
+ geChoice* m_tooltips;
};
} // namespace giada::v
#include "gui/elems/basics/box.h"
#include "gui/elems/basics/button.h"
#include "gui/elems/basics/check.h"
+#include "gui/elems/basics/flex.h"
#include "gui/elems/basics/input.h"
#include "utils/gui.h"
#include "utils/string.h"
namespace giada::v
{
-geTabPlugins::geTabPlugins(int X, int Y, int W, int H)
-: Fl_Group(X, Y, W, H, "Plug-ins")
-, m_browse(x() + w() - G_GUI_UNIT, y() + 9, G_GUI_UNIT, G_GUI_UNIT, "", zoomInOff_xpm, zoomInOn_xpm)
-, m_folderPath(m_browse.x() - 258, y() + 9, 250, G_GUI_UNIT)
-, m_scanButton(x() + w() - 150, m_folderPath.y() + m_folderPath.h() + 8, 150, G_GUI_UNIT)
-, m_info(x(), m_scanButton.y() + m_scanButton.h() + 8, w(), 240)
+geTabPlugins::geTabPlugins(geompp::Rect<int> bounds)
+: Fl_Group(bounds.x, bounds.y, bounds.w, bounds.h, "Plug-ins")
{
end();
- labelsize(G_GUI_FONT_SIZE_BASE);
- selection_color(G_COLOR_GREY_4);
+ geFlex* body = new geFlex(bounds.reduced(G_GUI_OUTER_MARGIN), Direction::VERTICAL, G_GUI_OUTER_MARGIN);
+ {
+ geFlex* line1 = new geFlex(Direction::HORIZONTAL, G_GUI_OUTER_MARGIN);
+ {
+ m_folderPath = new geInput(0, 0, 0, 0);
+ m_browse = new geButton("", zoomInOff_xpm, zoomInOn_xpm);
- m_info.hide();
+ line1->add(new geBox(), 80); // TODO - temporary hack for geInput's label
+ line1->add(m_folderPath);
+ line1->add(m_browse, 20);
+ line1->end();
+ }
- m_folderPath.label("Plug-ins folder");
- m_folderPath.onChange = [this](const std::string& v) {
+ m_scanButton = new geButton();
+ m_info = new geBox();
+
+ body->add(line1, 20);
+ body->add(m_scanButton, 20);
+ body->add(m_info);
+ body->end();
+ }
+
+ add(body);
+ resizable(body);
+
+ m_info->hide();
+
+ m_folderPath->label("Plug-ins folder");
+ m_folderPath->onChange = [this](const std::string& v) {
m_data.pluginPath = v;
};
- m_browse.callback(cb_browse, (void*)this);
- m_scanButton.callback(cb_scan, (void*)this);
+ m_browse->onClick = [this]() {
+ c::layout::openBrowserForPlugins(*static_cast<v::gdWindow*>(top_window()));
+ };
+
+ m_scanButton->onClick = [this]() {
+ std::function<void(float)> callback = [this](float progress) {
+ std::string l = "Scan in progress (" + std::to_string((int)(progress * 100)) + "%). Please wait...";
+ m_info->label(l.c_str());
+ Fl::wait();
+ };
+
+ m_info->show();
+ c::config::scanPlugins(m_folderPath->value(), callback);
+ m_info->hide();
+ rebuild();
+ };
rebuild();
}
m_data = c::config::getPluginData();
const std::string scanLabel = "Scan (" + std::to_string(m_data.numAvailablePlugins) + " found)";
- m_scanButton.copy_label(scanLabel.c_str());
-
- m_folderPath.value(m_data.pluginPath.c_str());
- m_folderPath.redraw();
-}
-
-/* -------------------------------------------------------------------------- */
-
-void geTabPlugins::cb_scan(Fl_Widget* /*w*/, void* p) { ((geTabPlugins*)p)->cb_scan(); }
-void geTabPlugins::cb_browse(Fl_Widget* /*w*/, void* p) { ((geTabPlugins*)p)->cb_browse(); }
+ m_scanButton->copy_label(scanLabel.c_str());
-/* -------------------------------------------------------------------------- */
-
-void geTabPlugins::cb_browse()
-{
- c::layout::openBrowserForPlugins(*static_cast<v::gdWindow*>(top_window()));
-}
-
-/* -------------------------------------------------------------------------- */
-
-void geTabPlugins::cb_scan()
-{
- std::function<void(float)> callback = [this](float progress) {
- std::string l = "Scan in progress (" + std::to_string((int)(progress * 100)) + "%). Please wait...";
- m_info.label(l.c_str());
- Fl::wait();
- };
-
- m_info.show();
- c::config::scanPlugins(m_folderPath.value(), callback);
- m_info.hide();
- rebuild();
+ m_folderPath->value(m_data.pluginPath.c_str());
+ m_folderPath->redraw();
}
/* -------------------------------------------------------------------------- */
#ifdef WITH_VST
+#include "deps/geompp/src/rect.hpp"
#include "glue/config.h"
-#include "gui/elems/basics/box.h"
-#include "gui/elems/basics/button.h"
-#include "gui/elems/basics/input.h"
#include <FL/Fl_Group.H>
+class geInput;
+class geBox;
+
namespace giada::v
{
+class geBox;
+class geButton;
class geTabPlugins : public Fl_Group
{
public:
- geTabPlugins(int x, int y, int w, int h);
+ geTabPlugins(geompp::Rect<int>);
void save();
void rebuild();
private:
- static void cb_scan(Fl_Widget* /*w*/, void* p);
- static void cb_browse(Fl_Widget* /*w*/, void* p);
- void cb_scan();
- void cb_browse();
-
c::config::PluginData m_data;
- geButton m_browse;
- geInput m_folderPath;
- geButton m_scanButton;
- geBox m_info;
+ geButton* m_browse;
+ geInput* m_folderPath;
+ geButton* m_scanButton;
+ geBox* m_info;
};
} // namespace giada::v
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+#include "gui/elems/keyBinder.h"
+#include "core/const.h"
+#include "glue/layout.h"
+#include "gui/dialogs/keyGrabber.h"
+#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/boxtypes.h"
+#include "gui/elems/basics/button.h"
+#include "utils/gui.h"
+
+namespace giada::v
+{
+geKeyBinder::geKeyBinder(const std::string& l, int& keyRef)
+: geFlex(Direction::HORIZONTAL, G_GUI_INNER_MARGIN)
+{
+ m_labelBox = new geBox(l.c_str());
+ m_keyBox = new geBox(u::gui::keyToString(keyRef).c_str());
+ m_bindBtn = new geButton("Bind");
+ m_clearBtn = new geButton("Clear");
+
+ add(m_labelBox);
+ add(m_keyBox, 100);
+ add(m_bindBtn, 50);
+ add(m_clearBtn, 50);
+ end();
+
+ m_labelBox->box(G_CUSTOM_BORDER_BOX);
+ m_keyBox->box(G_CUSTOM_BORDER_BOX);
+
+ m_bindBtn->onClick = [&keyRef, this]() {
+ c::layout::openKeyGrabberWindow(keyRef, [&keyRef, this](int newKey) {
+ keyRef = newKey;
+ m_keyBox->copy_label(u::gui::keyToString(keyRef).c_str());
+ return true;
+ });
+ };
+
+ m_clearBtn->onClick = [&keyRef, this]() {
+ keyRef = 0;
+ m_keyBox->copy_label(u::gui::keyToString(keyRef).c_str());
+ };
+}
+
+} // namespace giada::v
\ No newline at end of file
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+#ifndef GE_KEY_BINDER_H
+#define GE_KEY_BINDER_H
+
+#include "gui/elems/basics/flex.h"
+#include <string>
+
+namespace giada::v
+{
+class geBox;
+class geButton;
+class geKeyBinder : public geFlex
+{
+public:
+ geKeyBinder(const std::string& l, int& keyRef);
+
+private:
+ geBox* m_labelBox;
+ geBox* m_keyBox;
+ geButton* m_bindBtn;
+ geButton* m_clearBtn;
+};
+} // namespace giada::v
+
+#endif
c::recorder::clearAllActions(data.id);
break;
case Menu::SETUP_KEYBOARD_INPUT:
- c::layout::openKeyGrabberWindow(data);
+ c::layout::openKeyGrabberWindow(data.key, [channelId = data.id](int key) {
+ return c::io::channel_setKey(channelId, key);
+ });
break;
case Menu::SETUP_MIDI_INPUT:
c::layout::openChannelMidiInputWindow(data.id);
}
case Menu::SETUP_KEYBOARD_INPUT:
{
- c::layout::openKeyGrabberWindow(data);
+ c::layout::openKeyGrabberWindow(data.key, [channelId = data.id](int key) {
+ return c::io::channel_setKey(channelId, key);
+ });
break;
}
case Menu::SETUP_MIDI_INPUT:
{
m_bpm = new geButton(0, 0, 60, G_GUI_UNIT);
m_meter = new geButton(0, 0, 60, G_GUI_UNIT);
- m_quantizer = new geChoice(0, 0, 60, G_GUI_UNIT, "", false);
+ m_quantizer = new geChoice(0, 0, 60, G_GUI_UNIT);
m_multiplier = new geButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, "", multiplyOff_xpm, multiplyOn_xpm);
m_divider = new geButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, "", divideOff_xpm, divideOn_xpm);
add(m_quantizer);
void geMainTimer::setQuantizer(int q)
{
- m_quantizer->value(q);
+ m_quantizer->showItem(q);
}
/* -------------------------------------------------------------------------- */
*
* -------------------------------------------------------------------------- */
-#include "midiLearner.h"
+#include "gui/elems/midiIO/midiLearner.h"
+#include "core/const.h"
#include "gui/elems/basics/box.h"
#include "gui/elems/basics/boxtypes.h"
#include "gui/elems/basics/button.h"
#include "utils/string.h"
#include <cassert>
-namespace giada
+namespace giada::v
{
-namespace v
-{
-geMidiLearner::geMidiLearner(int x, int y, std::string l, int param)
-: gePack(x, y, Direction::HORIZONTAL)
+geMidiLearner::geMidiLearner(int x, int y, int w, int h, std::string l, int param)
+: geFlex(x, y, w, h, Direction::HORIZONTAL, G_GUI_INNER_MARGIN)
, onStartLearn(nullptr)
, onStopLearn(nullptr)
, onClearLearn(nullptr)
, m_param(param)
-, m_text(0, 0, 146, 20, l.c_str())
-, m_valueBtn(0, 0, 80, 20)
-, m_button(0, 0, 50, 20, "learn")
{
- add(&m_text);
- add(&m_valueBtn);
- add(&m_button);
-
- m_text.box(G_CUSTOM_BORDER_BOX);
-
- m_valueBtn.box(G_CUSTOM_BORDER_BOX);
- m_valueBtn.callback(cb_value, (void*)this);
- m_valueBtn.when(FL_WHEN_RELEASE);
-
- m_button.type(FL_TOGGLE_BUTTON);
- m_button.callback(cb_button, (void*)this);
+ m_text = new geBox(l.c_str());
+ m_valueBtn = new geButton();
+ m_button = new geButton("learn");
+
+ add(m_text);
+ add(m_valueBtn, 80);
+ add(m_button, 50);
+ end();
+
+ m_text->box(G_CUSTOM_BORDER_BOX);
+
+ m_valueBtn->box(G_CUSTOM_BORDER_BOX);
+ m_valueBtn->when(FL_WHEN_RELEASE);
+ m_valueBtn->onClick = [this]() {
+ assert(onClearLearn != nullptr);
+
+ if (Fl::event_button() == FL_RIGHT_MOUSE)
+ onClearLearn(m_param);
+ };
+
+ m_button->type(FL_TOGGLE_BUTTON);
+ m_button->onClick = [this]() {
+ assert(onStartLearn != nullptr);
+ assert(onStopLearn != nullptr);
+
+ if (m_button->value() == 1)
+ onStartLearn(m_param);
+ else
+ onStopLearn();
+ };
}
/* -------------------------------------------------------------------------- */
-void geMidiLearner::cb_button(Fl_Widget* /*w*/, void* p) { ((geMidiLearner*)p)->onLearn(); }
-void geMidiLearner::cb_value(Fl_Widget* /*w*/, void* p) { ((geMidiLearner*)p)->onReset(); }
-
-/* -------------------------------------------------------------------------- */
-
void geMidiLearner::update(uint32_t value)
{
std::string tmp = "(not set)";
tmp.pop_back(); // Remove last two digits, useless in MIDI messages
}
- m_valueBtn.copy_label(tmp.c_str());
- m_button.value(0);
+ m_valueBtn->copy_label(tmp.c_str());
+ m_button->value(0);
}
/* -------------------------------------------------------------------------- */
-void geMidiLearner::activate()
-{
- Fl_Group::activate();
- m_valueBtn.activate();
- m_button.activate();
-}
-
-void geMidiLearner::deactivate()
+void geMidiLearner::update(const std::string& s)
{
- Fl_Group::deactivate();
- m_valueBtn.deactivate();
- m_button.deactivate();
+ m_valueBtn->copy_label(s.c_str());
+ m_button->value(0);
}
/* -------------------------------------------------------------------------- */
-void geMidiLearner::onLearn() const
+void geMidiLearner::activate()
{
- assert(onStartLearn != nullptr);
- assert(onStopLearn != nullptr);
-
- if (m_button.value() == 1)
- onStartLearn(m_param);
- else
- onStopLearn();
+ Fl_Group::activate();
+ m_valueBtn->activate();
+ m_button->activate();
}
-/* -------------------------------------------------------------------------- */
-
-void geMidiLearner::onReset() const
+void geMidiLearner::deactivate()
{
- assert(onClearLearn != nullptr);
-
- if (Fl::event_button() == FL_RIGHT_MOUSE)
- onClearLearn(m_param);
+ Fl_Group::deactivate();
+ m_valueBtn->deactivate();
+ m_button->deactivate();
}
-} // namespace v
-} // namespace giada
+} // namespace giada::v
\ No newline at end of file
#ifndef GE_MIDI_LEARNER_H
#define GE_MIDI_LEARNER_H
-#include "gui/elems/basics/box.h"
-#include "gui/elems/basics/button.h"
-#include "gui/elems/basics/pack.h"
+#include "gui/elems/basics/flex.h"
#include <functional>
#include <string>
-class geBox;
-
-namespace giada
-{
-namespace v
+namespace giada::v
{
+class geBox;
class geButton;
-class geMidiLearner : public gePack
+class geMidiLearner : public geFlex
{
public:
- geMidiLearner(int x, int y, std::string l, int param);
+ geMidiLearner(int x, int y, int w, int h, std::string l, int param);
/* update
Updates and repaints the label widget with value 'value'. */
void update(uint32_t value);
+ /* update (1)
+ Just sets the label widget with a string value (no parsing done as in (1)). */
+
+ void update(const std::string&);
+
void activate();
void deactivate();
int m_param;
- geBox m_text;
- geButton m_valueBtn;
- geButton m_button;
-
-private:
- static void cb_button(Fl_Widget* /*w*/, void* p);
- static void cb_value(Fl_Widget* /*w*/, void* p);
-
- void onLearn() const;
- void onReset() const;
+ geBox* m_text;
+ geButton* m_valueBtn;
+ geButton* m_button;
};
-} // namespace v
-} // namespace giada
+} // namespace giada::v
#endif
void geMidiLearnerPack::addMidiLearner(std::string label, int param, bool visible)
{
- geMidiLearner* l = new geMidiLearner(0, 0, label, param);
+ geMidiLearner* l = new geMidiLearner(0, 0, LEARNER_WIDTH, G_GUI_UNIT, label, param);
l->onStartLearn = m_onStartLearn;
l->onClearLearn = m_onClearLearn;
#include <cassert>
#include <string>
-namespace giada
-{
-namespace v
+namespace giada::v
{
gePluginElement::gePluginElement(int x, int y, c::plugin::Plugin data)
: gePack(x, y, Direction::HORIZONTAL)
button.copy_label(m_plugin.name.c_str());
button.callback(cb_openPluginWindow, (void*)this);
- program.callback(cb_setProgram, (void*)this);
+ program.onChange = [pluginId = m_plugin.id](ID id) {
+ c::plugin::setProgram(pluginId, id);
+ };
for (const auto& p : m_plugin.programs)
- program.add(u::gui::removeFltkChars(p.name).c_str());
+ program.addItem(u::gui::removeFltkChars(p.name));
- if (program.size() == 0)
+ if (program.countItems() == 0)
{
- program.add("-- no programs --\0");
+ program.addItem("-- no programs --\0");
program.deactivate();
}
else
- program.value(m_plugin.currentProgram);
+ program.showItem(m_plugin.currentProgram);
bypass.callback(cb_setBypass, (void*)this);
bypass.type(FL_TOGGLE_BUTTON);
void gePluginElement::cb_setBypass(Fl_Widget* /*w*/, void* p) { ((gePluginElement*)p)->cb_setBypass(); }
void gePluginElement::cb_shiftUp(Fl_Widget* /*w*/, void* p) { ((gePluginElement*)p)->cb_shiftUp(); }
void gePluginElement::cb_shiftDown(Fl_Widget* /*w*/, void* p) { ((gePluginElement*)p)->cb_shiftDown(); }
-void gePluginElement::cb_setProgram(Fl_Widget* /*w*/, void* p) { ((gePluginElement*)p)->cb_setProgram(); }
/* -------------------------------------------------------------------------- */
{
c::plugin::toggleBypass(m_plugin.id);
}
-
-/* -------------------------------------------------------------------------- */
-
-void gePluginElement::cb_setProgram()
-{
- c::plugin::setProgram(m_plugin.id, program.value());
-}
-} // namespace v
-} // namespace giada
+} // namespace giada::v
#endif // #ifdef WITH_VST
#include "gui/elems/basics/choice.h"
#include "gui/elems/basics/pack.h"
-namespace giada
-{
-namespace v
+namespace giada::v
{
class gePluginElement : public gePack
{
geButton shiftDown;
geButton remove;
- private:
+private:
static void cb_removePlugin(Fl_Widget* /*w*/, void* p);
static void cb_openPluginWindow(Fl_Widget* /*w*/, void* p);
static void cb_setBypass(Fl_Widget* /*w*/, void* p);
static void cb_shiftUp(Fl_Widget* /*w*/, void* p);
static void cb_shiftDown(Fl_Widget* /*w*/, void* p);
- static void cb_setProgram(Fl_Widget* /*w*/, void* p);
void cb_removePlugin();
void cb_openPluginWindow();
void cb_setBypass();
void cb_shiftUp();
void cb_shiftDown();
- void cb_setProgram();
c::plugin::Plugin m_plugin;
};
-} // namespace v
-} // namespace giada
+} // namespace giada::v
#endif
#include "glue/plugin.h"
#include <FL/Fl_Group.H>
-class geBox;
class geSlider;
namespace giada::v
{
+class geBox;
class gePluginParameter : public Fl_Group
{
public:
void update(const c::plugin::Param& p, bool changeSlider);
- private:
+private:
static void cb_setValue(Fl_Widget* /*w*/, void* p);
void cb_setValue();
#include "waveTools.h"
#include <FL/Fl.H>
-namespace giada
-{
-namespace v
+namespace giada::v
{
geBoostTool::geBoostTool(int X, int Y)
: Fl_Pack(X, Y, 220, G_GUI_UNIT)
void geBoostTool::cb_normalize()
{
}
-
-} // namespace v
-} // namespace giada
+} // namespace giada::v
#include <FL/Fl_Pack.H>
class geInput;
-class geBox;
-namespace giada
-{
-namespace v
+namespace giada::v
{
+class geBox;
class geDial;
class geButton;
class geBoostTool : public Fl_Pack
geInput* input;
geButton* normalize;
};
-} // namespace v
-} // namespace giada
+} // namespace giada::v
#endif
namespace giada::v
{
-Ui::Ui(m::Recorder& recorder)
-: m_updater(*this)
+Ui::Ui(m::Recorder& recorder, const m::Conf::Data& conf)
+: dispatcher(conf.keyBindings)
+, m_updater(*this)
, m_blinker(0)
{
dispatcher.onEventOccured = [&recorder]() {
#ifndef G_V_UI_H
#define G_V_UI_H
+#include "core/conf.h"
#include "core/patch.h"
#include "gui/dialogs/mainWindow.h"
#include "gui/dispatcher.h"
class Ui final
{
public:
- Ui(m::Recorder&);
+ Ui(m::Recorder&, const m::Conf::Data&);
/* shouldBlink
Return whether is time to blink something or not. This is used to make
#include "gui/ui.h"
giada::m::Engine g_engine;
-giada::v::Ui g_ui(g_engine.recorder);
+giada::v::Ui g_ui(g_engine.recorder, g_engine.conf.data);
int main(int argc, char** argv)
{
char buf[PATH_MAX];
snprintf(buf, PATH_MAX, "%s/.giada", getenv("HOME"));
+ return stdfs::path(buf).string();
#elif defined(G_OS_WINDOWS)
- char buf[MAX_PATH];
- snprintf(buf, MAX_PATH, ".");
+ return stdfs::current_path().string();
#elif defined(G_OS_MAC)
const char* home = pwd->pw_dir;
snprintf(buf, PATH_MAX, "%s/Library/Application Support/Giada", home);
-#endif
-
return stdfs::path(buf).string();
+
+#endif
}
/* -------------------------------------------------------------------------- */
#include <FL/Fl.H>
#include <FL/fl_draw.H>
+#include <cstddef>
#include <string>
#if defined(_WIN32)
#include "../ext/resource.h"
{
return (Fl::h() / 2) - (h / 2);
}
+
+/* -------------------------------------------------------------------------- */
+
+std::string keyToString(int key)
+{
+ // https://github.com/fltk/fltk/blob/570a05a33c9dc42a16caa5a1a11cf34d4df1c1f9/FL/Enumerations.H
+ // https://www.fltk.org/doc-1.3/group__fl__events.html#gafa17a5b4d8d9163631c88142e60447ed
+
+ if (key == 0)
+ return "[None]";
+
+ switch (key)
+ {
+ case ' ':
+ return "Space";
+ case FL_BackSpace:
+ return "Backspace";
+ case FL_Tab:
+ return "Tab";
+ case FL_Enter:
+ return "Enter";
+ case FL_Pause:
+ return "Pause";
+ case FL_Scroll_Lock:
+ return "Scroll lock";
+ case FL_Escape:
+ return "Escape";
+ case FL_Home:
+ return "Home";
+ case FL_Left:
+ return "Left";
+ case FL_Up:
+ return "Up";
+ case FL_Right:
+ return "Right";
+ case FL_Down:
+ return "Down";
+ case FL_Page_Up:
+ return "Page up";
+ case FL_Page_Down:
+ return "Page down";
+ case FL_End:
+ return "End";
+ case FL_Print:
+ return "Print";
+ case FL_Insert:
+ return "Insert";
+ case FL_Menu:
+ return "Menu";
+ case FL_Help:
+ return "Help";
+ case FL_Num_Lock:
+ return "Num lock";
+ case FL_KP: // TODO ?
+ return "";
+ case FL_KP_Enter:
+ return "KP Enter";
+ case FL_F + 1:
+ return "F1";
+ case FL_F + 2:
+ return "F2";
+ case FL_F + 3:
+ return "F3";
+ case FL_F + 4:
+ return "F4";
+ case FL_F + 5:
+ return "F5";
+ case FL_F + 6:
+ return "F6";
+ case FL_F + 7:
+ return "F7";
+ case FL_F + 8:
+ return "F8";
+ case FL_F + 9:
+ return "F9";
+ case FL_F + 10:
+ return "F10";
+ case FL_F + 11:
+ return "F11";
+ case FL_F + 12:
+ return "F12";
+ case FL_Shift_L:
+ return "Shift L";
+ case FL_Shift_R:
+ return "Shift R";
+ case FL_Control_L:
+ return "Control L";
+ case FL_Control_R:
+ return "Control R";
+ case FL_Caps_Lock:
+ return "Caps lock";
+ case FL_Meta_L:
+ return "Meta L";
+ case FL_Meta_R:
+ return "Meta R";
+ case FL_Alt_L:
+ return "Alt L";
+ case FL_Alt_R:
+ return "Alt R";
+ case FL_Delete:
+ return "Delete";
+ default:
+ return Fl::event_text();
+ }
+}
} // namespace giada::u::gui
int centerWindowX(int w);
int centerWindowY(int h);
+/* keyToString
+Translates an FLTK key event into a human-readable string. */
+
+std::string keyToString(int key);
+
/* makeMenuItem
Makes a new Fl_Menu_Item at compile time. Used to initialize pop-up menus. */
#include "core/const.h"
#include <climits>
#include <cstdarg>
+#include <cstddef>
#include <iomanip>
#include <memory>
#ifndef G_UTILS_VECTOR_H
#define G_UTILS_VECTOR_H
+#include <cstddef>
#include <algorithm>
#include <functional>
#include <vector>