diff --git a/editor/layout/main.fl b/editor/layout/main.fl index dffc20d7..fe3824c2 100644 --- a/editor/layout/main.fl +++ b/editor/layout/main.fl @@ -11,7 +11,7 @@ widget_class mainView {open class Background } Fl_Group {} { - comment {theme=darkTheme} + comment {theme=darkTheme} open xywh {0 0 800 110} class LogicalGroup } { @@ -44,16 +44,6 @@ widget_class mainView {open xywh {185 5 380 100} box ROUNDED_BOX class RoundedGroup } { - Fl_Box {} { - label {File:} - xywh {200 13 40 30} labelsize 16 - class Label - } - Fl_Box {} { - label {KS:} - xywh {200 45 40 30} labelsize 16 - class Label - } Fl_Box {} { label {Separator 1} xywh {195 41 360 5} box BORDER_BOX labeltype NO_LABEL @@ -66,12 +56,12 @@ widget_class mainView {open } Fl_Box sfzFileLabel_ { label {DefaultInstrument.sfz} - xywh {265 12 230 30} labelsize 20 + xywh {195 11 250 31} labelsize 20 align 20 class Label } Fl_Box {} { - label {Key switch} - xywh {265 44 230 30} labelsize 20 + label {Key switch:} + xywh {195 44 250 30} labelsize 20 align 20 class Label } Fl_Box {} { @@ -81,12 +71,12 @@ widget_class mainView {open } Fl_Button {} { comment {tag=kTagLoadSfzFile} - xywh {500 14 25 25} labelsize 24 + xywh {505 14 25 25} labelsize 24 class LoadFileButton } Fl_Button {} { comment {tag=kTagEditSfzFile} - xywh {525 14 25 25} labelsize 24 + xywh {530 14 25 25} labelsize 24 class EditFileButton } Fl_Box infoVoicesLabel_ { @@ -111,6 +101,16 @@ widget_class mainView {open xywh {500 76 50 25} labelsize 12 align 16 class Label } + Fl_Button {} { + comment {tag=kTagNextSfzFile} + xywh {480 14 25 25} labelsize 24 + class NextFileButton + } + Fl_Button {} { + comment {tag=kTagPreviousSfzFile} + xywh {455 14 25 25} labelsize 24 + class PreviousFileButton + } } Fl_Group {} {open xywh {570 5 225 100} box ROUNDED_BOX diff --git a/editor/src/editor/Editor.cpp b/editor/src/editor/Editor.cpp index 4c16773d..37bee6a6 100644 --- a/editor/src/editor/Editor.cpp +++ b/editor/src/editor/Editor.cpp @@ -11,8 +11,13 @@ #include "NativeHelpers.h" #include #include +#include #include #include +#include +#include +#include +#include #include #include @@ -46,6 +51,8 @@ struct Editor::Impl : EditorController::Receiver, IControlListener { enum { kTagLoadSfzFile, kTagEditSfzFile, + kTagPreviousSfzFile, + kTagNextSfzFile, kTagSetVolume, kTagSetNumVoices, kTagSetOversampling, @@ -101,9 +108,12 @@ struct Editor::Impl : EditorController::Receiver, IControlListener { void chooseSfzFile(); void changeSfzFile(const std::string& filePath); + void changeToNextSfzFile(long offset); void chooseScalaFile(); void changeScalaFile(const std::string& filePath); + static bool scanDirectoryFiles(const fs::path& dirPath, std::function filter, std::vector& fileNames); + static absl::string_view simplifiedFileName(absl::string_view path, absl::string_view removedSuffix, absl::string_view ifEmpty); void updateSfzFileLabel(const std::string& filePath); @@ -377,6 +387,8 @@ void Editor::Impl::createFrameContents() typedef STextButton HomeButton; typedef STextButton SettingsButton; typedef STextButton EditFileButton; + typedef STextButton PreviousFileButton; + typedef STextButton NextFileButton; typedef SPiano Piano; auto createLogicalGroup = [](const CRect& bounds, int, const char*, CHoriTxtAlign, int) { @@ -505,6 +517,12 @@ void Editor::Impl::createFrameContents() auto createLoadFileButton = [&createGlyphButton](const CRect& bounds, int tag, const char*, CHoriTxtAlign, int fontsize) { return createGlyphButton(u8"\ue1a3", bounds, tag, fontsize); }; + auto createPreviousFileButton = [&createGlyphButton](const CRect& bounds, int tag, const char*, CHoriTxtAlign, int fontsize) { + return createGlyphButton(u8"\ue0d9", bounds, tag, fontsize); + }; + auto createNextFileButton = [&createGlyphButton](const CRect& bounds, int tag, const char*, CHoriTxtAlign, int fontsize) { + return createGlyphButton(u8"\ue0da", bounds, tag, fontsize); + }; auto createPiano = [](const CRect& bounds, int, const char*, CHoriTxtAlign, int) { SPiano* piano = new SPiano(bounds); return piano; @@ -669,6 +687,56 @@ void Editor::Impl::changeSfzFile(const std::string& filePath) updateSfzFileLabel(filePath); } +void Editor::Impl::changeToNextSfzFile(long offset) +{ + if (currentSfzFile_.empty()) + return; + + const fs::path filePath = fs::u8path(currentSfzFile_); + const fs::path dirPath = filePath.parent_path(); + + // extract file names of regular files from the sfz directory + std::vector fileNames; + fileNames.reserve(64); + + auto fileFilter = [](const fs::path &name) -> bool { + std::string ext = name.extension().u8string(); + absl::AsciiStrToLower(&ext); + return ext == ".sfz"; + }; + + if (!scanDirectoryFiles(dirPath, fileFilter, fileNames)) + return; + + // sort file names + const size_t size = fileNames.size(); + if (size == 0) + return; + + std::sort(fileNames.begin(), fileNames.end()); + + // find our current position in the file name list + size_t currentIndex = 0; + const fs::path currentFileName = filePath.filename(); + + while (currentIndex + 1 < size && fileNames[currentIndex] < currentFileName) + ++currentIndex; + + // advance to the next or previous item + typedef typename std::make_signed::type signed_size_t; + + size_t newIndex = static_cast(currentIndex) + offset; + if (static_cast(newIndex) < 0) + newIndex = static_cast(newIndex) % + static_cast(size) + size; + newIndex %= size; + + if (newIndex != currentIndex) { + const fs::path newFilePath = dirPath / fileNames[newIndex]; + changeSfzFile(newFilePath.u8string()); + } +} + void Editor::Impl::chooseScalaFile() { SharedPointer fs = owned(CNewFileSelector::create(frame_)); @@ -694,6 +762,39 @@ void Editor::Impl::changeScalaFile(const std::string& filePath) updateScalaFileLabel(filePath); } +bool Editor::Impl::scanDirectoryFiles(const fs::path& dirPath, std::function filter, std::vector& fileNames) +{ + std::error_code ec; + fs::directory_iterator it { dirPath, ec }; + + if (ec) + return false; + + fileNames.clear(); + + while (!ec && it != fs::directory_iterator()) { + const fs::directory_entry& ent = *it; + + std::error_code fileEc; + const fs::file_status status = ent.status(fileEc); + if (fileEc) + continue; + + if (status.type() == fs::file_type::regular) { + fs::path fileName = ent.path().filename(); + if (!filter || filter(fileName)) + fileNames.push_back(std::move(fileName)); + } + + it.increment(ec); + } + + if (ec) + return false; + + return true; +} + absl::string_view Editor::Impl::simplifiedFileName(absl::string_view path, absl::string_view removedSuffix, absl::string_view ifEmpty) { if (path.empty()) @@ -892,6 +993,20 @@ void Editor::Impl::valueChanged(CControl* ctl) openFileInExternalEditor(currentSfzFile_.c_str()); break; + case kTagPreviousSfzFile: + if (value != 1) + break; + + Call::later([this]() { changeToNextSfzFile(-1); }); + break; + + case kTagNextSfzFile: + if (value != 1) + break; + + Call::later([this]() { changeToNextSfzFile(+1); }); + break; + case kTagLoadScalaFile: if (value != 1) break; diff --git a/editor/src/editor/layout/main.hpp b/editor/src/editor/layout/main.hpp index af348d36..f7121624 100644 --- a/editor/src/editor/layout/main.hpp +++ b/editor/src/editor/layout/main.hpp @@ -18,37 +18,37 @@ SettingsButton* const view__7 = createSettingsButton(CRect(107, 69, 132, 94), kT view__3->addView(view__7); RoundedGroup* const view__8 = createRoundedGroup(CRect(185, 5, 565, 105), -1, "", kCenterText, 14); view__2->addView(view__8); -Label* const view__9 = createLabel(CRect(15, 8, 55, 38), -1, "File:", kCenterText, 16); +HLine* const view__9 = createHLine(CRect(10, 36, 370, 41), -1, "", kCenterText, 14); view__8->addView(view__9); -Label* const view__10 = createLabel(CRect(15, 40, 55, 70), -1, "KS:", kCenterText, 16); +HLine* const view__10 = createHLine(CRect(10, 68, 370, 73), -1, "", kCenterText, 14); view__8->addView(view__10); -HLine* const view__11 = createHLine(CRect(10, 36, 370, 41), -1, "", kCenterText, 14); +Label* const view__11 = createLabel(CRect(10, 6, 260, 37), -1, "DefaultInstrument.sfz", kLeftText, 20); +sfzFileLabel_ = view__11; view__8->addView(view__11); -HLine* const view__12 = createHLine(CRect(10, 68, 370, 73), -1, "", kCenterText, 14); +Label* const view__12 = createLabel(CRect(10, 39, 260, 69), -1, "Key switch:", kLeftText, 20); view__8->addView(view__12); -Label* const view__13 = createLabel(CRect(80, 7, 310, 37), -1, "DefaultInstrument.sfz", kCenterText, 20); -sfzFileLabel_ = view__13; +Label* const view__13 = createLabel(CRect(10, 71, 70, 96), -1, "Voices:", kRightText, 12); view__8->addView(view__13); -Label* const view__14 = createLabel(CRect(80, 39, 310, 69), -1, "Key switch", kCenterText, 20); +LoadFileButton* const view__14 = createLoadFileButton(CRect(320, 9, 345, 34), kTagLoadSfzFile, "", kCenterText, 24); view__8->addView(view__14); -Label* const view__15 = createLabel(CRect(10, 71, 70, 96), -1, "Voices:", kRightText, 12); +EditFileButton* const view__15 = createEditFileButton(CRect(345, 9, 370, 34), kTagEditSfzFile, "", kCenterText, 24); view__8->addView(view__15); -LoadFileButton* const view__16 = createLoadFileButton(CRect(315, 9, 340, 34), kTagLoadSfzFile, "", kCenterText, 24); +Label* const view__16 = createLabel(CRect(75, 71, 125, 96), -1, "", kCenterText, 12); +infoVoicesLabel_ = view__16; view__8->addView(view__16); -EditFileButton* const view__17 = createEditFileButton(CRect(340, 9, 365, 34), kTagEditSfzFile, "", kCenterText, 24); +Label* const view__17 = createLabel(CRect(130, 71, 190, 96), -1, "Max:", kRightText, 12); view__8->addView(view__17); -Label* const view__18 = createLabel(CRect(75, 71, 125, 96), -1, "", kCenterText, 12); -infoVoicesLabel_ = view__18; +Label* const view__18 = createLabel(CRect(195, 71, 245, 96), -1, "", kCenterText, 12); +numVoicesLabel_ = view__18; view__8->addView(view__18); -Label* const view__19 = createLabel(CRect(130, 71, 190, 96), -1, "Max:", kRightText, 12); +Label* const view__19 = createLabel(CRect(250, 71, 310, 96), -1, "Memory:", kRightText, 12); view__8->addView(view__19); -Label* const view__20 = createLabel(CRect(195, 71, 245, 96), -1, "", kCenterText, 12); -numVoicesLabel_ = view__20; +Label* const view__20 = createLabel(CRect(315, 71, 365, 96), -1, "", kCenterText, 12); +memoryLabel_ = view__20; view__8->addView(view__20); -Label* const view__21 = createLabel(CRect(250, 71, 310, 96), -1, "Memory:", kRightText, 12); +NextFileButton* const view__21 = createNextFileButton(CRect(295, 9, 320, 34), kTagNextSfzFile, "", kCenterText, 24); view__8->addView(view__21); -Label* const view__22 = createLabel(CRect(315, 71, 365, 96), -1, "", kCenterText, 12); -memoryLabel_ = view__22; +PreviousFileButton* const view__22 = createPreviousFileButton(CRect(270, 9, 295, 34), kTagPreviousSfzFile, "", kCenterText, 24); view__8->addView(view__22); RoundedGroup* const view__23 = createRoundedGroup(CRect(570, 5, 795, 105), -1, "", kCenterText, 14); view__2->addView(view__23);