From: Dennis Braun Date: Wed, 4 May 2022 15:29:26 +0000 (+0200) Subject: New upstream version 0.21.0 X-Git-Tag: archive/raspbian/0.22.0-1+rpi1~1^2~19^2~1 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=6be2fd4da5ca72688c5363886c6d0842c9edb54e;p=giada.git New upstream version 0.21.0 --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 4aeb5e3..5ec69d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -132,6 +132,7 @@ list(APPEND SOURCES 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 @@ -175,6 +176,7 @@ list(APPEND SOURCES 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 @@ -194,6 +196,7 @@ list(APPEND SOURCES 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 diff --git a/src/core/actions/actionRecorder.cpp b/src/core/actions/actionRecorder.cpp index 9043034..12cdaa4 100644 --- a/src/core/actions/actionRecorder.cpp +++ b/src/core/actions/actionRecorder.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include namespace giada::m @@ -351,4 +352,4 @@ void ActionRecorder::updateEvent(ID id, MidiEvent e) { m_actions.updateEvent(id, e); } -} // namespace giada::m \ No newline at end of file +} // namespace giada::m diff --git a/src/core/actions/actionRecorder.h b/src/core/actions/actionRecorder.h index ebbe27b..157dc05 100644 --- a/src/core/actions/actionRecorder.h +++ b/src/core/actions/actionRecorder.h @@ -30,6 +30,7 @@ #include "core/actions/actions.h" #include "core/midiEvent.h" #include "core/types.h" +#include #include namespace giada::m::patch diff --git a/src/core/channels/midiLighter.cpp b/src/core/channels/midiLighter.cpp index 45056e8..d21415f 100644 --- a/src/core/channels/midiLighter.cpp +++ b/src/core/channels/midiLighter.cpp @@ -121,8 +121,8 @@ void MidiLighter::send(uint32_t learnt, const MidiMap::Message& msg /* -------------------------------------------------------------------------- */ -template struct MidiLighter; +template class MidiLighter; #ifdef WITH_TESTS -template struct MidiLighter; +template class MidiLighter; #endif } // namespace giada::m \ No newline at end of file diff --git a/src/core/channels/midiLighter.h b/src/core/channels/midiLighter.h index a58b39f..8d540da 100644 --- a/src/core/channels/midiLighter.h +++ b/src/core/channels/midiLighter.h @@ -68,9 +68,9 @@ private: MidiMapper* m_midiMapper; }; -extern template struct MidiLighter; +extern template class MidiLighter; #ifdef WITH_TESTS -extern template struct MidiLighter; +extern template class MidiLighter; #endif } // namespace giada::m diff --git a/src/core/channels/sampleReactor.h b/src/core/channels/sampleReactor.h index 8761f2e..0915779 100644 --- a/src/core/channels/sampleReactor.h +++ b/src/core/channels/sampleReactor.h @@ -39,7 +39,7 @@ class Model; namespace giada::m { class Channel; -class ChannelShared; +struct ChannelShared; class Sequencer; /* SampleReactor diff --git a/src/core/conf.cpp b/src/core/conf.cpp index cf8437d..ed38bf8 100644 --- a/src/core/conf.cpp +++ b/src/core/conf.cpp @@ -142,6 +142,13 @@ bool Conf::read() 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); @@ -238,6 +245,13 @@ bool Conf::write() const j[CONF_KEY_REC_TRIGGER_MODE] = static_cast(data.recTriggerMode); j[CONF_KEY_REC_TRIGGER_LEVEL] = data.recTriggerLevel; j[CONF_KEY_INPUT_REC_MODE] = static_cast(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; diff --git a/src/core/conf.h b/src/core/conf.h index 20e032d..8541878 100644 --- a/src/core/conf.h +++ b/src/core/conf.h @@ -31,12 +31,21 @@ #include "core/types.h" #include "utils/gui.h" #include +#include namespace giada::m { class Conf final { public: + using KeyBindings = std::unordered_map; + + 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; @@ -133,6 +142,13 @@ public: 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(); diff --git a/src/core/const.h b/src/core/const.h index e77db0c..db9bc5b 100644 --- a/src/core/const.h +++ b/src/core/const.h @@ -59,10 +59,10 @@ /* -- 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"; @@ -444,6 +444,11 @@ constexpr auto CONF_KEY_PLUGIN_SORT_METHOD = "plugin_sort_method"; 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 */ diff --git a/src/core/kernelAudio.cpp b/src/core/kernelAudio.cpp index 8c10d8e..4fa059f 100644 --- a/src/core/kernelAudio.cpp +++ b/src/core/kernelAudio.cpp @@ -32,6 +32,7 @@ #include "utils/log.h" #include "utils/vector.h" #include +#include namespace giada::m { diff --git a/src/core/kernelAudio.h b/src/core/kernelAudio.h index ce3c57b..e509e91 100644 --- a/src/core/kernelAudio.h +++ b/src/core/kernelAudio.h @@ -29,6 +29,7 @@ #include "core/conf.h" #include "deps/rtaudio/RtAudio.h" +#include #include #include #include diff --git a/src/core/midiDispatcher.cpp b/src/core/midiDispatcher.cpp index 0d06521..2cff699 100644 --- a/src/core/midiDispatcher.cpp +++ b/src/core/midiDispatcher.cpp @@ -38,6 +38,7 @@ #include "utils/log.h" #include "utils/math.h" #include +#include #include namespace giada::m diff --git a/src/core/midiDispatcher.h b/src/core/midiDispatcher.h index 2a52c0d..8bef6a4 100644 --- a/src/core/midiDispatcher.h +++ b/src/core/midiDispatcher.h @@ -31,6 +31,7 @@ #include "core/midiEvent.h" #include "core/model/model.h" #include "core/types.h" +#include #include #include diff --git a/src/core/midiLearnParam.cpp b/src/core/midiLearnParam.cpp index 83c820a..8166aee 100644 --- a/src/core/midiLearnParam.cpp +++ b/src/core/midiLearnParam.cpp @@ -25,6 +25,7 @@ * -------------------------------------------------------------------------- */ #include "midiLearnParam.h" +#include namespace giada::m { @@ -56,4 +57,4 @@ std::size_t MidiLearnParam::getIndex() const { return m_index; } -} // namespace giada::m \ No newline at end of file +} // namespace giada::m diff --git a/src/core/midiLearnParam.h b/src/core/midiLearnParam.h index f2e7738..bf51f80 100644 --- a/src/core/midiLearnParam.h +++ b/src/core/midiLearnParam.h @@ -28,6 +28,7 @@ #define G_MIDI_LEARN_PARAM_H #include "core/weakAtomic.h" +#include #include namespace giada::m diff --git a/src/core/midiMapper.cpp b/src/core/midiMapper.cpp index bee78b8..839d69d 100644 --- a/src/core/midiMapper.cpp +++ b/src/core/midiMapper.cpp @@ -31,6 +31,7 @@ #include "utils/fs.h" #include "utils/log.h" #include "utils/string.h" +#include #include #include #include @@ -275,4 +276,4 @@ template class MidiMapper; #ifdef WITH_TESTS template class MidiMapper; #endif -} // namespace giada::m \ No newline at end of file +} // namespace giada::m diff --git a/src/core/model/model.cpp b/src/core/model/model.cpp index ca27e4c..f7b7daa 100644 --- a/src/core/model/model.cpp +++ b/src/core/model/model.cpp @@ -111,6 +111,7 @@ Model::Model() void Model::reset() { + get() = {}; m_shared = {}; get().sequencer.shared = &m_shared.sequencerShared; diff --git a/src/core/model/storage.cpp b/src/core/model/storage.cpp index c0cc3c8..1065b91 100644 --- a/src/core/model/storage.cpp +++ b/src/core/model/storage.cpp @@ -116,7 +116,7 @@ void store(Conf::Data& conf) 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. */ diff --git a/src/core/plugins/plugin.cpp b/src/core/plugins/plugin.cpp index 23c07cd..23e1dde 100644 --- a/src/core/plugins/plugin.cpp +++ b/src/core/plugins/plugin.cpp @@ -191,11 +191,11 @@ bool Plugin::isSuspended() const /* -------------------------------------------------------------------------- */ -bool Plugin::acceptsMidi() const +bool Plugin::isInstrument() const { if (!valid) return false; - return m_plugin->acceptsMidi(); + return m_plugin->acceptsMidi() && m_plugin->getTotalNumInputChannels() == 0; } /* -------------------------------------------------------------------------- */ @@ -216,37 +216,15 @@ void Plugin::setBypass(bool b) { m_bypass.store(b); } /* -------------------------------------------------------------------------- */ -void Plugin::process(juce::AudioBuffer& 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; } /* -------------------------------------------------------------------------- */ diff --git a/src/core/plugins/plugin.h b/src/core/plugins/plugin.h index 6f1758d..0609327 100644 --- a/src/core/plugins/plugin.h +++ b/src/core/plugins/plugin.h @@ -42,6 +42,8 @@ namespace giada::m class Plugin : private juce::ComponentListener { public: + using Buffer = juce::AudioBuffer; + /* Plugin (1) Constructs an invalid plug-in. */ @@ -73,23 +75,28 @@ public: 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& b, juce::MidiBuffer m); + const Buffer& process(const Buffer& b, juce::MidiBuffer m); void setState(PluginState p); void setBypass(bool b); @@ -131,14 +138,9 @@ private: juce::AudioProcessor::Bus* getMainBus(BusType b) const; - /* countMainOutChannels - Returns the current channel layout for the main output bus. */ - - int countMainOutChannels() const; - std::unique_ptr m_plugin; std::unique_ptr m_playHead; - juce::AudioBuffer m_buffer; + Buffer m_buffer; std::atomic m_bypass; diff --git a/src/core/plugins/pluginHost.cpp b/src/core/plugins/pluginHost.cpp index 76c58e5..4d8c724 100644 --- a/src/core/plugins/pluginHost.cpp +++ b/src/core/plugins/pluginHost.cpp @@ -36,6 +36,7 @@ #include "utils/log.h" #include "utils/vector.h" #include +#include #include namespace giada::m @@ -89,22 +90,16 @@ void PluginHost::processStack(mcl::AudioBuffer& outBuf, const std::vector& plugins, juce::MidiB { 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 diff --git a/src/core/plugins/pluginHost.h b/src/core/plugins/pluginHost.h index 9e19817..2663690 100644 --- a/src/core/plugins/pluginHost.h +++ b/src/core/plugins/pluginHost.h @@ -112,15 +112,20 @@ public: 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& plugins, juce::MidiBuffer& events); + void processPlugins(const std::vector&, juce::MidiBuffer& events); + + void processPlugin(Plugin*, const juce::MidiBuffer& events); model::Model& m_model; diff --git a/src/core/plugins/pluginManager.cpp b/src/core/plugins/pluginManager.cpp index e58c406..8ffbf6e 100644 --- a/src/core/plugins/pluginManager.cpp +++ b/src/core/plugins/pluginManager.cpp @@ -35,6 +35,7 @@ #include "utils/log.h" #include "utils/string.h" #include +#include #include namespace giada::m diff --git a/src/core/plugins/pluginState.cpp b/src/core/plugins/pluginState.cpp index 14a5403..17cb8f1 100644 --- a/src/core/plugins/pluginState.cpp +++ b/src/core/plugins/pluginState.cpp @@ -28,6 +28,7 @@ #include "pluginState.h" #include "core/const.h" +#include namespace giada::m { diff --git a/src/core/plugins/pluginState.h b/src/core/plugins/pluginState.h index 7a5d440..753e66d 100644 --- a/src/core/plugins/pluginState.h +++ b/src/core/plugins/pluginState.h @@ -30,6 +30,7 @@ #define G_PLUGIN_STATE_H #include "deps/juce-config.h" +#include #include namespace giada::m @@ -52,4 +53,4 @@ private: #endif -#endif // #ifdef WITH_VST \ No newline at end of file +#endif // #ifdef WITH_VST diff --git a/src/core/queue.h b/src/core/queue.h index 7b07395..811a50d 100644 --- a/src/core/queue.h +++ b/src/core/queue.h @@ -27,6 +27,7 @@ #ifndef G_QUEUE_H #define G_QUEUE_H +#include #include #include diff --git a/src/core/ringBuffer.h b/src/core/ringBuffer.h index 9e7032c..04690a5 100644 --- a/src/core/ringBuffer.h +++ b/src/core/ringBuffer.h @@ -27,6 +27,7 @@ #ifndef G_RING_BUFFER_H #define G_RING_BUFFER_H +#include #include namespace giada diff --git a/src/glue/config.cpp b/src/glue/config.cpp index 8a962ea..db2c926 100644 --- a/src/glue/config.cpp +++ b/src/glue/config.cpp @@ -41,6 +41,7 @@ #include "utils/fs.h" #include "utils/vector.h" #include +#include extern giada::v::Ui g_ui; extern giada::m::Engine g_engine; @@ -328,4 +329,4 @@ void setPluginPathCb(void* data) configWin->tabPlugins->rebuild(); } #endif -} // namespace giada::c::config \ No newline at end of file +} // namespace giada::c::config diff --git a/src/glue/events.cpp b/src/glue/events.cpp index 2d8b4ca..baa91d1 100644 --- a/src/glue/events.cpp +++ b/src/glue/events.cpp @@ -251,6 +251,14 @@ void rewindSequencer(Thread t) /* -------------------------------------------------------------------------- */ +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()) @@ -261,6 +269,16 @@ void toggleActionRecording() 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()) diff --git a/src/glue/events.h b/src/glue/events.h index f1ca0d2..6897d98 100644 --- a/src/glue/events.h +++ b/src/glue/events.h @@ -69,7 +69,9 @@ void startSequencer(Thread t); void stopSequencer(Thread t); void toggleSequencer(Thread t); void rewindSequencer(Thread t); +void stopActionRecording(); void toggleActionRecording(); +void stopInputRecording(); void toggleInputRecording(); /* Plug-ins. */ diff --git a/src/glue/io.cpp b/src/glue/io.cpp index eb3184d..30e2d4f 100644 --- a/src/glue/io.cpp +++ b/src/glue/io.cpp @@ -65,6 +65,18 @@ void rebuildMidiWindows_() 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 /* -------------------------------------------------------------------------- */ @@ -212,10 +224,13 @@ void channel_setMidiOutputFilter(ID channelId, int ch) /* -------------------------------------------------------------------------- */ -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; } /* -------------------------------------------------------------------------- */ diff --git a/src/glue/io.h b/src/glue/io.h index 74eca91..3a0cf0b 100644 --- a/src/glue/io.h +++ b/src/glue/io.h @@ -131,9 +131,10 @@ void channel_setMidiInputFilter(ID channelId, int c); 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. */ diff --git a/src/glue/layout.cpp b/src/glue/layout.cpp index f3b9fc1..76e7357 100644 --- a/src/glue/layout.cpp +++ b/src/glue/layout.cpp @@ -29,7 +29,9 @@ #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" @@ -101,9 +103,13 @@ void openAboutWindow() /* -------------------------------------------------------------------------- */ -void openKeyGrabberWindow(const c::channel::Data& data) +void openKeyGrabberWindow(int key, std::function 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); } /* -------------------------------------------------------------------------- */ diff --git a/src/glue/layout.h b/src/glue/layout.h index 8526350..cdc9f5a 100644 --- a/src/glue/layout.h +++ b/src/glue/layout.h @@ -28,6 +28,7 @@ #define G_GLUE_LAYOUT_H #include "core/types.h" +#include #include namespace giada::m @@ -52,7 +53,7 @@ void openBrowserForProjectSave(); void openBrowserForSampleLoad(ID channelId); void openBrowserForSampleSave(ID channelId); void openAboutWindow(); -void openKeyGrabberWindow(const c::channel::Data&); +void openKeyGrabberWindow(int key, std::function); void openBpmWindow(std::string bpmValue); void openBeatsWindow(int beats, int bars); void openConfigWindow(); diff --git a/src/glue/main.cpp b/src/glue/main.cpp index fd981bc..dca6339 100644 --- a/src/glue/main.cpp +++ b/src/glue/main.cpp @@ -279,8 +279,8 @@ void closeProject() 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(); } diff --git a/src/glue/storage.cpp b/src/glue/storage.cpp index 1f18190..8a7ab66 100644 --- a/src/glue/storage.cpp +++ b/src/glue/storage.cpp @@ -74,6 +74,11 @@ void loadProject(void* data) 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) diff --git a/src/gui/dialogs/actionEditor/sampleActionEditor.cpp b/src/gui/dialogs/actionEditor/sampleActionEditor.cpp index 0f013d6..944d4c4 100644 --- a/src/gui/dialogs/actionEditor/sampleActionEditor.cpp +++ b/src/gui/dialogs/actionEditor/sampleActionEditor.cpp @@ -52,10 +52,10 @@ gdSampleActionEditor::gdSampleActionEditor(ID channelId, m::Conf::Data& conf) 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(); @@ -103,11 +103,11 @@ void gdSampleActionEditor::rebuild() 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); diff --git a/src/gui/dialogs/config.cpp b/src/gui/dialogs/config.cpp index 3ee0661..29048a8 100644 --- a/src/gui/dialogs/config.cpp +++ b/src/gui/dialogs/config.cpp @@ -24,48 +24,72 @@ * * -------------------------------------------------------------------------- */ -#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 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 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); @@ -74,12 +98,7 @@ gdConfig::gdConfig(int w, int h, m::Conf::Data& conf) /* -------------------------------------------------------------------------- */ -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(); @@ -90,11 +109,4 @@ void gdConfig::cb_save_config() #endif do_callback(); } - -/* -------------------------------------------------------------------------- */ - -void gdConfig::cb_cancel() -{ - do_callback(); -} } // namespace giada::v diff --git a/src/gui/dialogs/config.h b/src/gui/dialogs/config.h index 23dabf8..9c4e5c2 100644 --- a/src/gui/dialogs/config.h +++ b/src/gui/dialogs/config.h @@ -30,18 +30,14 @@ #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 @@ -54,17 +50,13 @@ public: 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 diff --git a/src/gui/dialogs/keyGrabber.cpp b/src/gui/dialogs/keyGrabber.cpp index bfdc433..51fae3c 100644 --- a/src/gui/dialogs/keyGrabber.cpp +++ b/src/gui/dialogs/keyGrabber.cpp @@ -24,116 +24,96 @@ * * -------------------------------------------------------------------------- */ -#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 -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(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 diff --git a/src/gui/dialogs/keyGrabber.h b/src/gui/dialogs/keyGrabber.h index 789e170..6c23efc 100644 --- a/src/gui/dialogs/keyGrabber.h +++ b/src/gui/dialogs/keyGrabber.h @@ -27,10 +27,10 @@ #ifndef GD_KEYGRABBER_H #define GD_KEYGRABBER_H +#include "core/conf.h" #include "window.h" #include - -class geBox; +#include namespace giada::c::channel { @@ -39,25 +39,24 @@ struct Data; 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 onSetKey; - const c::channel::Data& m_data; +private: + int m_key; geBox* m_text; geButton* m_clear; diff --git a/src/gui/dialogs/mainWindow.cpp b/src/gui/dialogs/mainWindow.cpp index da46a86..c0c6582 100644 --- a/src/gui/dialogs/mainWindow.cpp +++ b/src/gui/dialogs/mainWindow.cpp @@ -126,7 +126,10 @@ gdMainWindow::gdMainWindow(int W, int H, const char* title, int argc, char** arg 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); diff --git a/src/gui/dialogs/midiIO/midiInputChannel.cpp b/src/gui/dialogs/midiIO/midiInputChannel.cpp index 214a957..b41afff 100644 --- a/src/gui/dialogs/midiIO/midiInputChannel.cpp +++ b/src/gui/dialogs/midiIO/midiInputChannel.cpp @@ -30,6 +30,7 @@ #include "utils/log.h" #include #include +#include #ifdef WITH_VST #include "core/plugins/plugin.h" #endif @@ -155,24 +156,26 @@ gdMidiInputChannel::gdMidiInputChannel(ID channelId, m::Conf::Data& c) 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); @@ -210,7 +213,7 @@ void gdMidiInputChannel::rebuild() static_cast(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) { @@ -228,7 +231,6 @@ void gdMidiInputChannel::rebuild() /* -------------------------------------------------------------------------- */ 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(); } /* -------------------------------------------------------------------------- */ @@ -244,12 +246,4 @@ void gdMidiInputChannel::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 diff --git a/src/gui/dialogs/midiIO/midiInputChannel.h b/src/gui/dialogs/midiIO/midiInputChannel.h index a9e4e3f..142ebe3 100644 --- a/src/gui/dialogs/midiIO/midiInputChannel.h +++ b/src/gui/dialogs/midiIO/midiInputChannel.h @@ -73,10 +73,8 @@ public: 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; diff --git a/src/gui/dialogs/midiIO/midiInputMaster.cpp b/src/gui/dialogs/midiIO/midiInputMaster.cpp index 8360e1f..2a79e12 100644 --- a/src/gui/dialogs/midiIO/midiInputMaster.cpp +++ b/src/gui/dialogs/midiIO/midiInputMaster.cpp @@ -97,24 +97,26 @@ gdMidiInputMaster::gdMidiInputMaster(m::Conf::Data& c) 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); @@ -130,7 +132,7 @@ void gdMidiInputMaster::rebuild() 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(); @@ -139,7 +141,6 @@ void gdMidiInputMaster::rebuild() /* -------------------------------------------------------------------------- */ 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(); } /* -------------------------------------------------------------------------- */ @@ -147,11 +148,4 @@ void gdMidiInputMaster::cb_enable() { 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 diff --git a/src/gui/dialogs/midiIO/midiInputMaster.h b/src/gui/dialogs/midiIO/midiInputMaster.h index 512f8ef..865236f 100644 --- a/src/gui/dialogs/midiIO/midiInputMaster.h +++ b/src/gui/dialogs/midiIO/midiInputMaster.h @@ -56,9 +56,7 @@ public: 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; diff --git a/src/gui/dialogs/midiIO/midiOutputMidiCh.cpp b/src/gui/dialogs/midiIO/midiOutputMidiCh.cpp index 997cbbe..ca31890 100644 --- a/src/gui/dialogs/midiIO/midiOutputMidiCh.cpp +++ b/src/gui/dialogs/midiIO/midiOutputMidiCh.cpp @@ -33,9 +33,7 @@ #include "utils/gui.h" #include -namespace giada -{ -namespace v +namespace giada::v { gdMidiOutputMidiCh::gdMidiOutputMidiCh(ID channelId) : gdMidiOutputBase(300, 168, channelId) @@ -60,25 +58,27 @@ gdMidiOutputMidiCh::gdMidiOutputMidiCh(ID 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); @@ -99,7 +99,7 @@ void gdMidiOutputMidiCh::rebuild() 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(); @@ -108,7 +108,6 @@ void gdMidiOutputMidiCh::rebuild() /* -------------------------------------------------------------------------- */ 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(); } /* -------------------------------------------------------------------------- */ @@ -116,12 +115,4 @@ void gdMidiOutputMidiCh::cb_enableOut() { 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 diff --git a/src/gui/dialogs/midiIO/midiOutputMidiCh.h b/src/gui/dialogs/midiIO/midiOutputMidiCh.h index 53f8a95..1abc715 100644 --- a/src/gui/dialogs/midiIO/midiOutputMidiCh.h +++ b/src/gui/dialogs/midiIO/midiOutputMidiCh.h @@ -31,9 +31,7 @@ #include "midiOutputBase.h" -namespace giada -{ -namespace v +namespace giada::v { class geChoice; class gdMidiOutputMidiCh : public gdMidiOutputBase @@ -43,16 +41,13 @@ public: 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 diff --git a/src/gui/dialogs/pluginChooser.cpp b/src/gui/dialogs/pluginChooser.cpp index b170aa7..1f06e17 100644 --- a/src/gui/dialogs/pluginChooser.cpp +++ b/src/gui/dialogs/pluginChooser.cpp @@ -46,7 +46,7 @@ gdPluginChooser::gdPluginChooser(int X, int Y, int W, int H, ID channelId, m::Co /* 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(); @@ -64,12 +64,15 @@ gdPluginChooser::gdPluginChooser(int X, int Y, int W, int H, ID channelId, m::Co 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(id)); + browser->refresh(); + }; addBtn->callback(cb_add, (void*)this); addBtn->shortcut(FL_Enter); @@ -88,14 +91,13 @@ gdPluginChooser::~gdPluginChooser() 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(); } /* -------------------------------------------------------------------------- */ @@ -106,14 +108,6 @@ void gdPluginChooser::cb_close() /* -------------------------------------------------------------------------- */ -void gdPluginChooser::cb_sort() -{ - c::plugin::sortPlugins(static_cast(sortMethod->value())); - browser->refresh(); -} - -/* -------------------------------------------------------------------------- */ - void gdPluginChooser::cb_add() { int pluginIndex = browser->value() - 3; // subtract header lines diff --git a/src/gui/dialogs/pluginChooser.h b/src/gui/dialogs/pluginChooser.h index e3d2f84..80d4389 100644 --- a/src/gui/dialogs/pluginChooser.h +++ b/src/gui/dialogs/pluginChooser.h @@ -50,10 +50,8 @@ public: 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; diff --git a/src/gui/dialogs/pluginWindow.h b/src/gui/dialogs/pluginWindow.h index 0b82548..875d821 100644 --- a/src/gui/dialogs/pluginWindow.h +++ b/src/gui/dialogs/pluginWindow.h @@ -31,19 +31,21 @@ #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 { diff --git a/src/gui/dialogs/pluginWindowGUI.cpp b/src/gui/dialogs/pluginWindowGUI.cpp index f06126d..5568c57 100644 --- a/src/gui/dialogs/pluginWindowGUI.cpp +++ b/src/gui/dialogs/pluginWindowGUI.cpp @@ -107,6 +107,7 @@ void gdPluginWindowGUI::openEditor() void gdPluginWindowGUI::closeEditor() { + m_plugin.setResizeCallback(nullptr); m_editor.reset(); } } // namespace giada::v diff --git a/src/gui/dialogs/sampleEditor.cpp b/src/gui/dialogs/sampleEditor.cpp index 9a80c58..194748f 100644 --- a/src/gui/dialogs/sampleEditor.cpp +++ b/src/gui/dialogs/sampleEditor.cpp @@ -103,7 +103,7 @@ gdSampleEditor::~gdSampleEditor() 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(); @@ -152,22 +152,20 @@ gePack* gdSampleEditor::createUpperBar() 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"); @@ -262,7 +260,6 @@ gePack* gdSampleEditor::createBottomBar(int x, int y, int h) 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(); } @@ -315,13 +312,6 @@ void gdSampleEditor::cb_zoomOut() /* -------------------------------------------------------------------------- */ -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)"; diff --git a/src/gui/dialogs/sampleEditor.h b/src/gui/dialogs/sampleEditor.h index 81c9074..fa48b03 100644 --- a/src/gui/dialogs/sampleEditor.h +++ b/src/gui/dialogs/sampleEditor.h @@ -33,7 +33,6 @@ #include "window.h" class geCheck; -class geBox; class geStatusButton; namespace giada::m @@ -43,6 +42,7 @@ class Wave; namespace giada::v { +class geBox; class geButton; class geChoice; class gePack; @@ -96,14 +96,12 @@ private: 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(); diff --git a/src/gui/dialogs/window.cpp b/src/gui/dialogs/window.cpp index 58754ad..9b0196f 100644 --- a/src/gui/dialogs/window.cpp +++ b/src/gui/dialogs/window.cpp @@ -26,6 +26,7 @@ #include "window.h" #include "utils/log.h" +#include namespace giada::v { @@ -72,6 +73,10 @@ gdWindow::~gdWindow() 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); diff --git a/src/gui/dispatcher.cpp b/src/gui/dispatcher.cpp index 75b48c2..423f75f 100644 --- a/src/gui/dispatcher.cpp +++ b/src/gui/dispatcher.cpp @@ -38,13 +38,9 @@ extern giada::v::Ui g_ui; 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) { } @@ -89,55 +85,34 @@ void Dispatcher::dispatchKey(int event) 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); } } diff --git a/src/gui/dispatcher.h b/src/gui/dispatcher.h index e31d121..81387c6 100644 --- a/src/gui/dispatcher.h +++ b/src/gui/dispatcher.h @@ -27,6 +27,7 @@ #ifndef G_V_DISPATCHER_H #define G_V_DISPATCHER_H +#include "core/conf.h" #include "core/types.h" #include @@ -36,7 +37,7 @@ class geChannel; class Dispatcher final { public: - Dispatcher(); + Dispatcher(const m::Conf::KeyBindings& m_keyBindings); /* dispatchKey Processes a key pressed on the physical keyboard. */ @@ -62,12 +63,9 @@ private: 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 diff --git a/src/gui/drawing.cpp b/src/gui/drawing.cpp index 3bc616b..15d192f 100644 --- a/src/gui/drawing.cpp +++ b/src/gui/drawing.cpp @@ -25,7 +25,9 @@ * -------------------------------------------------------------------------- */ #include "drawing.h" +#include "utils/gui.h" #include +#include namespace giada::v { @@ -48,4 +50,14 @@ void drawLine(geompp::Line l, Fl_Color c) fl_color(c); fl_line(l.x1, l.y1, l.x2, l.y2); } + +/* -------------------------------------------------------------------------- */ + +void drawText(const std::string& s, geompp::Rect 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 diff --git a/src/gui/drawing.h b/src/gui/drawing.h index 6eb3b61..2882a0f 100644 --- a/src/gui/drawing.h +++ b/src/gui/drawing.h @@ -29,12 +29,14 @@ #include "deps/geompp/src/rect.hpp" #include +#include namespace giada::v { -void drawRectf(geompp::Rect r, Fl_Color c); -void drawRect(geompp::Rect r, Fl_Color c); -void drawLine(geompp::Line l, Fl_Color c); +void drawRectf(geompp::Rect, Fl_Color); +void drawRect(geompp::Rect, Fl_Color); +void drawLine(geompp::Line, Fl_Color); +void drawText(const std::string&, geompp::Rect, Fl_Color c, int alignment = FL_ALIGN_CENTER); } // namespace giada::v #endif \ No newline at end of file diff --git a/src/gui/elems/actionEditor/gridTool.cpp b/src/gui/elems/actionEditor/gridTool.cpp index ff30eb5..97ce87f 100644 --- a/src/gui/elems/actionEditor/gridTool.cpp +++ b/src/gui/elems/actionEditor/gridTool.cpp @@ -38,20 +38,22 @@ geGridTool::geGridTool(Pixel x, Pixel y, m::Conf::Data& c) , 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(); @@ -64,23 +66,12 @@ geGridTool::geGridTool(Pixel x, Pixel y, m::Conf::Data& c) 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(); @@ -90,7 +81,7 @@ bool geGridTool::isOn() const int geGridTool::getValue() const { - switch (gridType->value()) + switch (gridType->getSelectedId()) { case 0: return 1; diff --git a/src/gui/elems/actionEditor/gridTool.h b/src/gui/elems/actionEditor/gridTool.h index 5481045..ffa9660 100644 --- a/src/gui/elems/actionEditor/gridTool.h +++ b/src/gui/elems/actionEditor/gridTool.h @@ -57,9 +57,6 @@ private: geChoice* gridType; geCheck* active; - - static void cb_changeType(Fl_Widget* /*w*/, void* p); - void cb_changeType(); }; } // namespace giada::v diff --git a/src/gui/elems/basics/box.cpp b/src/gui/elems/basics/box.cpp index 2e92b01..cab44a4 100644 --- a/src/gui/elems/basics/box.cpp +++ b/src/gui/elems/basics/box.cpp @@ -29,6 +29,8 @@ #include "utils/gui.h" #include +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) { @@ -58,8 +60,9 @@ void geBox::draw() 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 diff --git a/src/gui/elems/basics/box.h b/src/gui/elems/basics/box.h index c2ec4ed..6cc6eda 100644 --- a/src/gui/elems/basics/box.h +++ b/src/gui/elems/basics/box.h @@ -29,6 +29,8 @@ #include +namespace giada::v +{ class geBox : public Fl_Box { public: @@ -37,5 +39,6 @@ public: void draw() override; }; +} // namespace giada::v #endif diff --git a/src/gui/elems/basics/choice.cpp b/src/gui/elems/basics/choice.cpp index f0065ba..c2a41e9 100644 --- a/src/gui/elems/basics/choice.cpp +++ b/src/gui/elems/basics/choice.cpp @@ -24,19 +24,18 @@ * * -------------------------------------------------------------------------- */ -#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 #include -#include 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); @@ -48,45 +47,67 @@ geChoice::geChoice(int x, int y, int w, int h, const char* l, bool ang) /* -------------------------------------------------------------------------- */ -void geChoice::cb_onChange(Fl_Widget* /*w*/, void* p) { (static_cast(p))->cb_onChange(); } +void geChoice::geMenu::draw() +{ + geompp::Rect bounds(x(), y(), w(), h()); + + drawRectf(bounds, G_COLOR_GREY_2); // background + drawRect(bounds, static_cast(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(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(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(this)); + m_menu->add(label.c_str(), 0, cb_onChange, static_cast(this)); if (id != -1) m_ids.push_back(id); @@ -98,19 +119,51 @@ void geChoice::addItem(const std::string& label, ID 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(); } diff --git a/src/gui/elems/basics/choice.h b/src/gui/elems/basics/choice.h index 4485e83..f96feac 100644 --- a/src/gui/elems/basics/choice.h +++ b/src/gui/elems/basics/choice.h @@ -28,6 +28,8 @@ #define GE_CHOICE_H #include "core/types.h" +#include "gui/elems/basics/box.h" +#include "gui/elems/basics/flex.h" #include #include #include @@ -35,30 +37,48 @@ 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 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 m_ids; }; } // namespace giada::v diff --git a/src/gui/elems/basics/flex.cpp b/src/gui/elems/basics/flex.cpp index 4bf1421..070be45 100644 --- a/src/gui/elems/basics/flex.cpp +++ b/src/gui/elems/basics/flex.cpp @@ -1,4 +1,31 @@ +/* ----------------------------------------------------------------------------- + * + * 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 + * . + * + * -------------------------------------------------------------------------- */ + #include "flex.h" +#include #include namespace giada::v @@ -133,4 +160,4 @@ void geFlex::end() Fl_Group::end(); resize(x(), y(), w(), h()); } -} // namespace giada::v \ No newline at end of file +} // namespace giada::v diff --git a/src/gui/elems/basics/group.cpp b/src/gui/elems/basics/group.cpp index 08fd58e..fc50cd2 100644 --- a/src/gui/elems/basics/group.cpp +++ b/src/gui/elems/basics/group.cpp @@ -28,6 +28,7 @@ * -------------------------------------------------------------------------- */ #include "group.h" +#include #include namespace giada @@ -86,4 +87,4 @@ Fl_Widget* geGroup::getLastChild() 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 diff --git a/src/gui/elems/basics/group.h b/src/gui/elems/basics/group.h index 49a3202..1460911 100644 --- a/src/gui/elems/basics/group.h +++ b/src/gui/elems/basics/group.h @@ -31,6 +31,7 @@ #define GE_GROUP_H #include +#include #include namespace giada diff --git a/src/gui/elems/basics/liquidScroll.cpp b/src/gui/elems/basics/liquidScroll.cpp index 64f0903..2544c0d 100644 --- a/src/gui/elems/basics/liquidScroll.cpp +++ b/src/gui/elems/basics/liquidScroll.cpp @@ -43,6 +43,13 @@ geLiquidScroll::geLiquidScroll(int x, int y, int w, int h, Direction d) /* -------------------------------------------------------------------------- */ +geLiquidScroll::geLiquidScroll(geompp::Rect 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 diff --git a/src/gui/elems/basics/liquidScroll.h b/src/gui/elems/basics/liquidScroll.h index d90a28c..f56e78b 100644 --- a/src/gui/elems/basics/liquidScroll.h +++ b/src/gui/elems/basics/liquidScroll.h @@ -33,6 +33,7 @@ #define GE_LIQUID_SCROLL_H #include "core/const.h" +#include "deps/geompp/src/rect.hpp" #include "gui/types.h" #include "scroll.h" @@ -42,6 +43,7 @@ class geLiquidScroll : public geScroll { public: geLiquidScroll(int x, int y, int w, int h, Direction d); + geLiquidScroll(geompp::Rect, Direction d); void resize(int x, int y, int w, int h) override; diff --git a/src/gui/elems/basics/resizerBar.cpp b/src/gui/elems/basics/resizerBar.cpp index f4dacc6..8ec0827 100644 --- a/src/gui/elems/basics/resizerBar.cpp +++ b/src/gui/elems/basics/resizerBar.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include namespace giada::v @@ -229,4 +230,4 @@ void geResizerBar::moveTo(int p) 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 diff --git a/src/gui/elems/basics/scrollPack.cpp b/src/gui/elems/basics/scrollPack.cpp index 22a3171..0b5892a 100644 --- a/src/gui/elems/basics/scrollPack.cpp +++ b/src/gui/elems/basics/scrollPack.cpp @@ -28,6 +28,7 @@ #include "boxtypes.h" #include "core/const.h" #include +#include namespace giada { @@ -78,4 +79,4 @@ Fl_Widget* geScrollPack::getLastChild() 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 diff --git a/src/gui/elems/basics/scrollPack.h b/src/gui/elems/basics/scrollPack.h index 95d946e..14dfe4d 100644 --- a/src/gui/elems/basics/scrollPack.h +++ b/src/gui/elems/basics/scrollPack.h @@ -29,6 +29,7 @@ #include "gui/elems/basics/pack.h" #include "gui/elems/basics/scroll.h" +#include namespace giada { diff --git a/src/gui/elems/basics/tabs.cpp b/src/gui/elems/basics/tabs.cpp new file mode 100644 index 0000000..b2aeca9 --- /dev/null +++ b/src/gui/elems/basics/tabs.cpp @@ -0,0 +1,54 @@ +/* ----------------------------------------------------------------------------- + * + * 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 + * . + * + * -------------------------------------------------------------------------- */ + +#include "gui/elems/basics/tabs.h" +#include "core/const.h" +#include "gui/elems/basics/boxtypes.h" + +namespace giada::v +{ +geTabs::geTabs(geompp::Rect 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 diff --git a/src/gui/elems/basics/tabs.h b/src/gui/elems/basics/tabs.h new file mode 100644 index 0000000..37229f2 --- /dev/null +++ b/src/gui/elems/basics/tabs.h @@ -0,0 +1,44 @@ +/* ----------------------------------------------------------------------------- + * + * 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 + * . + * + * -------------------------------------------------------------------------- */ + +#ifndef GE_TABS_H +#define GE_TABS_H + +#include "deps/geompp/src/rect.hpp" +#include + +namespace giada::v +{ +class geTabs : public Fl_Tabs +{ +public: + geTabs(geompp::Rect); + + void add(Fl_Widget*); +}; +} // namespace giada::v + +#endif \ No newline at end of file diff --git a/src/gui/elems/config/tabAudio.cpp b/src/gui/elems/config/tabAudio.cpp index 7894597..aaac8de 100644 --- a/src/gui/elems/config/tabAudio.cpp +++ b/src/gui/elems/config/tabAudio.cpp @@ -29,17 +29,19 @@ #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 +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& devices) -: geChoice(x, y, w, h, l) +geTabAudio::geDeviceMenu::geDeviceMenu(const char* l, const std::vector& devices) +: geChoice(l, LABEL_WIDTH) { if (devices.size() == 0) { @@ -56,9 +58,8 @@ geTabAudio::geDeviceMenu::geDeviceMenu(int x, int y, int w, int h, const char* l /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -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) { } @@ -111,28 +112,75 @@ void geTabAudio::geChannelMenu::rebuild(const c::config::AudioDeviceData& data) /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -geTabAudio::geTabAudio(int X, int Y, int W, int H) -: Fl_Group(X, Y, W, H, "Sound System") +geTabAudio::geTabAudio(geompp::Rect 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); diff --git a/src/gui/elems/config/tabAudio.h b/src/gui/elems/config/tabAudio.h index b2f77bc..52d3dee 100644 --- a/src/gui/elems/config/tabAudio.h +++ b/src/gui/elems/config/tabAudio.h @@ -27,6 +27,7 @@ #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 @@ -36,18 +37,17 @@ class geInput; 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&); + geDeviceMenu(const char* l, const std::vector&); }; 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; @@ -60,7 +60,7 @@ public: c::config::AudioDeviceData m_data; }; - geTabAudio(int x, int y, int w, int h); + geTabAudio(geompp::Rect); void save(); diff --git a/src/gui/elems/config/tabBehaviors.cpp b/src/gui/elems/config/tabBehaviors.cpp index c15c2f7..bcf2494 100644 --- a/src/gui/elems/config/tabBehaviors.cpp +++ b/src/gui/elems/config/tabBehaviors.cpp @@ -29,45 +29,47 @@ #include "core/const.h" #include "gui/elems/basics/box.h" #include "gui/elems/basics/check.h" +#include "gui/elems/basics/flex.h" #include 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 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 diff --git a/src/gui/elems/config/tabBehaviors.h b/src/gui/elems/config/tabBehaviors.h index f000fb3..4e488a7 100644 --- a/src/gui/elems/config/tabBehaviors.h +++ b/src/gui/elems/config/tabBehaviors.h @@ -28,8 +28,8 @@ #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 namespace giada::v @@ -37,16 +37,15 @@ namespace giada::v class geTabBehaviors : public Fl_Group { public: - geTabBehaviors(int x, int y, int w, int h, m::Conf::Data&); + geTabBehaviors(geompp::Rect, 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; }; diff --git a/src/gui/elems/config/tabBindings.cpp b/src/gui/elems/config/tabBindings.cpp new file mode 100644 index 0000000..59c07b5 --- /dev/null +++ b/src/gui/elems/config/tabBindings.cpp @@ -0,0 +1,59 @@ +/* ----------------------------------------------------------------------------- + * + * 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 + * . + * + * -------------------------------------------------------------------------- */ + +#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 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 diff --git a/src/gui/elems/config/tabBindings.h b/src/gui/elems/config/tabBindings.h new file mode 100644 index 0000000..fc6aafe --- /dev/null +++ b/src/gui/elems/config/tabBindings.h @@ -0,0 +1,54 @@ +/* ----------------------------------------------------------------------------- + * + * 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 + * . + * + * -------------------------------------------------------------------------- */ + +#ifndef GE_CONFIG_TAB_BINDINGS_H +#define GE_CONFIG_TAB_BINDINGS_H + +#include "core/conf.h" +#include "deps/geompp/src/rect.hpp" +#include + +class geCheck; +class geInput; + +namespace giada::v +{ +class geKeyBinder; +class geTabBindings : public Fl_Group +{ +public: + geTabBindings(geompp::Rect, m::Conf::Data&); + +private: + geKeyBinder* play; + geKeyBinder* rewind; + geKeyBinder* recordActions; + geKeyBinder* recordInput; + geKeyBinder* exit; +}; +} // namespace giada::v + +#endif diff --git a/src/gui/elems/config/tabMidi.cpp b/src/gui/elems/config/tabMidi.cpp index 3ca5f11..c34e628 100644 --- a/src/gui/elems/config/tabMidi.cpp +++ b/src/gui/elems/config/tabMidi.cpp @@ -31,11 +31,13 @@ #include "utils/gui.h" #include +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& data, const std::string& msgIfNotFound) -: geChoice(x, y, w, h, l) +geTabMidi::geMenu::geMenu(const char* l, const std::vector& data, + const std::string& msgIfNotFound) +: geChoice(l, LABEL_WIDTH) { if (data.size() == 0) { @@ -54,24 +56,51 @@ geTabMidi::geMenu::geMenu(int x, int y, int w, int h, const char* l, /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ -geTabMidi::geTabMidi(int X, int Y, int W, int H) -: Fl_Group(X, Y, W, H, "MIDI") +geTabMidi::geTabMidi(geompp::Rect 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); @@ -93,7 +122,7 @@ geTabMidi::geTabMidi(int X, int Y, int W, int H) enableOut->onChange = [this](bool b) { if (b) { - m_data.outPort = portOut->value(); + m_data.outPort = portOut->getSelectedId(); portOut->activate(); } else @@ -108,7 +137,7 @@ geTabMidi::geTabMidi(int X, int Y, int W, int H) enableIn->onChange = [this](bool b) { if (b) { - m_data.inPort = portIn->value(); + m_data.inPort = portIn->getSelectedId(); portIn->activate(); } else diff --git a/src/gui/elems/config/tabMidi.h b/src/gui/elems/config/tabMidi.h index 0fce166..3add0b5 100644 --- a/src/gui/elems/config/tabMidi.h +++ b/src/gui/elems/config/tabMidi.h @@ -27,6 +27,7 @@ #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 @@ -40,11 +41,10 @@ class geTabMidi : public Fl_Group public: struct geMenu : public geChoice { - geMenu(int x, int y, int w, int h, const char* l, const std::vector&, - const std::string& msgIfNotFound); + geMenu(const char* l, const std::vector&, const std::string& msgIfNotFound); }; - geTabMidi(int x, int y, int w, int h); + geTabMidi(geompp::Rect); void save() const; diff --git a/src/gui/elems/config/tabMisc.cpp b/src/gui/elems/config/tabMisc.cpp index 81f8bad..8cf108e 100644 --- a/src/gui/elems/config/tabMisc.cpp +++ b/src/gui/elems/config/tabMisc.cpp @@ -26,33 +26,41 @@ #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 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; }; } /* -------------------------------------------------------------------------- */ diff --git a/src/gui/elems/config/tabMisc.h b/src/gui/elems/config/tabMisc.h index 727e4b5..f2d3c5a 100644 --- a/src/gui/elems/config/tabMisc.h +++ b/src/gui/elems/config/tabMisc.h @@ -27,24 +27,25 @@ #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 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); void save(); private: c::config::MiscData m_data; - geChoice m_debugMsg; - geChoice m_tooltips; + geChoice* m_debugMsg; + geChoice* m_tooltips; }; } // namespace giada::v diff --git a/src/gui/elems/config/tabPlugins.cpp b/src/gui/elems/config/tabPlugins.cpp index 3c4a3f1..43f5507 100644 --- a/src/gui/elems/config/tabPlugins.cpp +++ b/src/gui/elems/config/tabPlugins.cpp @@ -36,6 +36,7 @@ #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" @@ -44,27 +45,59 @@ 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 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(top_window())); + }; + + m_scanButton->onClick = [this]() { + std::function 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(); } @@ -76,38 +109,10 @@ void geTabPlugins::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(top_window())); -} - -/* -------------------------------------------------------------------------- */ - -void geTabPlugins::cb_scan() -{ - std::function 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(); } /* -------------------------------------------------------------------------- */ diff --git a/src/gui/elems/config/tabPlugins.h b/src/gui/elems/config/tabPlugins.h index 91ad111..2eea680 100644 --- a/src/gui/elems/config/tabPlugins.h +++ b/src/gui/elems/config/tabPlugins.h @@ -29,34 +29,32 @@ #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 +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); 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 diff --git a/src/gui/elems/keyBinder.cpp b/src/gui/elems/keyBinder.cpp new file mode 100644 index 0000000..93d2c3f --- /dev/null +++ b/src/gui/elems/keyBinder.cpp @@ -0,0 +1,69 @@ +/* ----------------------------------------------------------------------------- + * + * 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 + * . + * + * -------------------------------------------------------------------------- */ + +#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 diff --git a/src/gui/elems/keyBinder.h b/src/gui/elems/keyBinder.h new file mode 100644 index 0000000..d91eb75 --- /dev/null +++ b/src/gui/elems/keyBinder.h @@ -0,0 +1,50 @@ +/* ----------------------------------------------------------------------------- + * + * 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 + * . + * + * -------------------------------------------------------------------------- */ + +#ifndef GE_KEY_BINDER_H +#define GE_KEY_BINDER_H + +#include "gui/elems/basics/flex.h" +#include + +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 diff --git a/src/gui/elems/mainWindow/keyboard/midiChannel.cpp b/src/gui/elems/mainWindow/keyboard/midiChannel.cpp index 0859e0f..3273c85 100644 --- a/src/gui/elems/mainWindow/keyboard/midiChannel.cpp +++ b/src/gui/elems/mainWindow/keyboard/midiChannel.cpp @@ -82,7 +82,9 @@ void menuCallback(Fl_Widget* w, void* v) 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); diff --git a/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp b/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp index d868e61..f2759e4 100644 --- a/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp +++ b/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp @@ -102,7 +102,9 @@ void menuCallback(Fl_Widget* w, void* v) } 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: diff --git a/src/gui/elems/mainWindow/mainTimer.cpp b/src/gui/elems/mainWindow/mainTimer.cpp index 3293285..737b475 100644 --- a/src/gui/elems/mainWindow/mainTimer.cpp +++ b/src/gui/elems/mainWindow/mainTimer.cpp @@ -42,7 +42,7 @@ geMainTimer::geMainTimer(int x, int y) { 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); @@ -144,7 +144,7 @@ void geMainTimer::setLock(bool v) void geMainTimer::setQuantizer(int q) { - m_quantizer->value(q); + m_quantizer->showItem(q); } /* -------------------------------------------------------------------------- */ diff --git a/src/gui/elems/midiIO/midiLearner.cpp b/src/gui/elems/midiIO/midiLearner.cpp index 988da84..e730c23 100644 --- a/src/gui/elems/midiIO/midiLearner.cpp +++ b/src/gui/elems/midiIO/midiLearner.cpp @@ -24,48 +24,57 @@ * * -------------------------------------------------------------------------- */ -#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 -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)"; @@ -77,47 +86,31 @@ void geMidiLearner::update(uint32_t value) 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 diff --git a/src/gui/elems/midiIO/midiLearner.h b/src/gui/elems/midiIO/midiLearner.h index 7e97b66..393c005 100644 --- a/src/gui/elems/midiIO/midiLearner.h +++ b/src/gui/elems/midiIO/midiLearner.h @@ -27,29 +27,29 @@ #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 #include -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(); @@ -63,18 +63,10 @@ protected: 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 diff --git a/src/gui/elems/midiIO/midiLearnerPack.cpp b/src/gui/elems/midiIO/midiLearnerPack.cpp index 65558a8..c79b1ef 100644 --- a/src/gui/elems/midiIO/midiLearnerPack.cpp +++ b/src/gui/elems/midiIO/midiLearnerPack.cpp @@ -63,7 +63,7 @@ void geMidiLearnerPack::setCallbacks(std::function s, std::funct 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; diff --git a/src/gui/elems/plugin/pluginElement.cpp b/src/gui/elems/plugin/pluginElement.cpp index a521fa8..34eecda 100644 --- a/src/gui/elems/plugin/pluginElement.cpp +++ b/src/gui/elems/plugin/pluginElement.cpp @@ -42,9 +42,7 @@ #include #include -namespace giada -{ -namespace v +namespace giada::v { gePluginElement::gePluginElement(int x, int y, c::plugin::Plugin data) : gePack(x, y, Direction::HORIZONTAL) @@ -80,18 +78,20 @@ gePluginElement::gePluginElement(int x, int y, c::plugin::Plugin data) 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); @@ -120,7 +120,6 @@ void gePluginElement::cb_openPluginWindow(Fl_Widget* /*w*/, void* p) { ((gePlugi 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(); } /* -------------------------------------------------------------------------- */ @@ -186,14 +185,6 @@ void gePluginElement::cb_setBypass() { 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 diff --git a/src/gui/elems/plugin/pluginElement.h b/src/gui/elems/plugin/pluginElement.h index cf667d6..9277e4d 100644 --- a/src/gui/elems/plugin/pluginElement.h +++ b/src/gui/elems/plugin/pluginElement.h @@ -34,9 +34,7 @@ #include "gui/elems/basics/choice.h" #include "gui/elems/basics/pack.h" -namespace giada -{ -namespace v +namespace giada::v { class gePluginElement : public gePack { @@ -53,24 +51,21 @@ public: 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 diff --git a/src/gui/elems/plugin/pluginParameter.h b/src/gui/elems/plugin/pluginParameter.h index 92e1d33..1db9f93 100644 --- a/src/gui/elems/plugin/pluginParameter.h +++ b/src/gui/elems/plugin/pluginParameter.h @@ -33,11 +33,11 @@ #include "glue/plugin.h" #include -class geBox; class geSlider; namespace giada::v { +class geBox; class gePluginParameter : public Fl_Group { public: @@ -45,7 +45,7 @@ public: void update(const c::plugin::Param& p, bool changeSlider); - private: +private: static void cb_setValue(Fl_Widget* /*w*/, void* p); void cb_setValue(); diff --git a/src/gui/elems/sampleEditor/boostTool.cpp b/src/gui/elems/sampleEditor/boostTool.cpp index bcfe7a8..5c8dea0 100644 --- a/src/gui/elems/sampleEditor/boostTool.cpp +++ b/src/gui/elems/sampleEditor/boostTool.cpp @@ -39,9 +39,7 @@ #include "waveTools.h" #include -namespace giada -{ -namespace v +namespace giada::v { geBoostTool::geBoostTool(int X, int Y) : Fl_Pack(X, Y, 220, G_GUI_UNIT) @@ -106,6 +104,4 @@ void geBoostTool::cb_setBoostNum() void geBoostTool::cb_normalize() { } - -} // namespace v -} // namespace giada +} // namespace giada::v diff --git a/src/gui/elems/sampleEditor/boostTool.h b/src/gui/elems/sampleEditor/boostTool.h index b0ad40b..a01f9c3 100644 --- a/src/gui/elems/sampleEditor/boostTool.h +++ b/src/gui/elems/sampleEditor/boostTool.h @@ -30,12 +30,10 @@ #include class geInput; -class geBox; -namespace giada -{ -namespace v +namespace giada::v { +class geBox; class geDial; class geButton; class geBoostTool : public Fl_Pack @@ -58,7 +56,6 @@ private: geInput* input; geButton* normalize; }; -} // namespace v -} // namespace giada +} // namespace giada::v #endif diff --git a/src/gui/ui.cpp b/src/gui/ui.cpp index acdc051..1b1e732 100644 --- a/src/gui/ui.cpp +++ b/src/gui/ui.cpp @@ -44,8 +44,9 @@ 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]() { diff --git a/src/gui/ui.h b/src/gui/ui.h index ca25a1e..116eaf0 100644 --- a/src/gui/ui.h +++ b/src/gui/ui.h @@ -27,6 +27,7 @@ #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" @@ -45,7 +46,7 @@ namespace giada::v 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 diff --git a/src/main.cpp b/src/main.cpp index ff7829c..065002f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -28,7 +28,7 @@ #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) { diff --git a/src/utils/fs.cpp b/src/utils/fs.cpp index ecec279..6f19795 100644 --- a/src/utils/fs.cpp +++ b/src/utils/fs.cpp @@ -148,11 +148,11 @@ std::string getHomePath() 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) @@ -166,9 +166,9 @@ std::string getHomePath() const char* home = pwd->pw_dir; snprintf(buf, PATH_MAX, "%s/Library/Application Support/Giada", home); -#endif - return stdfs::path(buf).string(); + +#endif } /* -------------------------------------------------------------------------- */ diff --git a/src/utils/gui.cpp b/src/utils/gui.cpp index 406279e..da5b7a5 100644 --- a/src/utils/gui.cpp +++ b/src/utils/gui.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #if defined(_WIN32) #include "../ext/resource.h" @@ -147,4 +148,109 @@ int centerWindowY(int 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 diff --git a/src/utils/gui.h b/src/utils/gui.h index 86d4dc4..dec6721 100644 --- a/src/utils/gui.h +++ b/src/utils/gui.h @@ -75,6 +75,11 @@ std::string truncate(const std::string& s, Pixel width); 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. */ diff --git a/src/utils/string.cpp b/src/utils/string.cpp index e5d5ac4..f50c858 100644 --- a/src/utils/string.cpp +++ b/src/utils/string.cpp @@ -30,6 +30,7 @@ #include "core/const.h" #include #include +#include #include #include diff --git a/src/utils/vector.h b/src/utils/vector.h index e3353e7..dfbcace 100644 --- a/src/utils/vector.h +++ b/src/utils/vector.h @@ -27,6 +27,7 @@ #ifndef G_UTILS_VECTOR_H #define G_UTILS_VECTOR_H +#include #include #include #include