From 2ede8fd710c661529e73a35dfa866f6e99550777 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Wed, 3 Feb 2021 02:05:50 +0100 Subject: [PATCH] User interface with CC knobs --- editor/layout/main.fl | 19 ++- editor/src/editor/Editor.cpp | 57 +++++++- editor/src/editor/GUIComponents.cpp | 206 ++++++++++++++++++++++++++++ editor/src/editor/GUIComponents.h | 58 ++++++++ editor/src/editor/layout/main.hpp | 5 +- 5 files changed, 327 insertions(+), 18 deletions(-) diff --git a/editor/layout/main.fl b/editor/layout/main.fl index 59ed5298..b96960f6 100644 --- a/editor/layout/main.fl +++ b/editor/layout/main.fl @@ -3,7 +3,7 @@ version 1.0305 header_name {.h} code_name {.cxx} widget_class mainView {open - xywh {576 416 800 475} type Double + xywh {571 362 800 475} type Double class LogicalGroup visible } { Fl_Box {} { @@ -197,23 +197,22 @@ widget_class mainView {open } } } - Fl_Group {subPanels_[kPanelControls]} { - xywh {5 110 790 285} hide + Fl_Group {subPanels_[kPanelControls]} {open + xywh {5 110 790 285} class LogicalGroup } { Fl_Group {} {open xywh {5 110 790 285} box ROUNDED_BOX class RoundedGroup } { - Fl_Box {} { - label {Controls not available} - xywh {5 110 790 285} labelsize 40 - class Label - } + Fl_Group controlsPanel_ {open selected + xywh {5 110 790 285} box THIN_DOWN_FRAME + class ControlsPanel + } {} } } Fl_Group {subPanels_[kPanelSettings]} {open - xywh {5 109 790 316} + xywh {5 109 790 316} hide class LogicalGroup } { Fl_Group {} { @@ -305,7 +304,7 @@ widget_class mainView {open } } Fl_Group userFilesGroup_ { - label Files open selected + label Files open xywh {620 270 139 100} box ROUNDED_BOX labelsize 12 align 17 class TitleGroup } { diff --git a/editor/src/editor/Editor.cpp b/editor/src/editor/Editor.cpp index 72c99ed7..3390e290 100644 --- a/editor/src/editor/Editor.cpp +++ b/editor/src/editor/Editor.cpp @@ -106,6 +106,8 @@ struct Editor::Impl : EditorController::Receiver, IControlListener { SPiano* piano_ = nullptr; + SControlsPanel* controlsPanel_ = nullptr; + void uiReceiveValue(EditId id, const EditValue& v) override; void uiReceiveMessage(const char* path, const char* sig, const sfizz_arg_t* args) override; @@ -156,6 +158,11 @@ struct Editor::Impl : EditorController::Receiver, IControlListener { void updateCCValue(unsigned cc, float value); void updateCCLabel(unsigned cc, const char* label); + // edition of CC by UI + void performCCValueChange(unsigned cc, float value); + void performCCBeginEdit(unsigned cc); + void performCCEndEdit(unsigned cc); + void setActivePanel(unsigned panelId); static void formatLabel(CTextLabel* label, const char* fmt, ...); @@ -533,6 +540,7 @@ void Editor::Impl::createFrameContents() typedef STextButton NextFileButton; typedef SPiano Piano; typedef SActionMenu ChevronDropDown; + typedef SControlsPanel ControlsPanel; auto createLogicalGroup = [](const CRect& bounds, int, const char*, CHoriTxtAlign, int) { CViewContainer* container = new CViewContainer(bounds); @@ -706,6 +714,10 @@ void Editor::Impl::createFrameContents() container->setBackground(background); return container; }; + auto createControlsPanel = [](const CRect& bounds, int, const char*, CHoriTxtAlign, int) { + auto* panel = new SControlsPanel(bounds); + return panel; + }; #include "layout/main.hpp" @@ -844,6 +856,18 @@ void Editor::Impl::createFrameContents() }; } + if (SControlsPanel* panel = controlsPanel_) { + panel->ValueChangeFunction = [this](uint32_t cc, float value) { + performCCValueChange(cc, value); + }; + panel->BeginEditFunction = [this](uint32_t cc) { + performCCBeginEdit(cc); + }; + panel->EndEditFunction = [this](uint32_t cc) { + performCCEndEdit(cc); + }; + } + /// CViewContainer* panel; activePanel_ = 0; @@ -1169,20 +1193,41 @@ void Editor::Impl::updateStretchedTuningLabel(float stretchedTuning) void Editor::Impl::updateCCUsed(unsigned cc, bool used) { - // TODO - fprintf(stderr, "CC%u used: %d\n", cc, used); + if (SControlsPanel* panel = controlsPanel_) + panel->setControlUsed(cc, used); } void Editor::Impl::updateCCValue(unsigned cc, float value) { - // TODO - fprintf(stderr, "CC%u value: %f\n", cc, value); + if (SControlsPanel* panel = controlsPanel_) + panel->setControlValue(cc, value); } void Editor::Impl::updateCCLabel(unsigned cc, const char* label) { - // TODO - fprintf(stderr, "CC%u label: %s\n", cc, label); + if (SControlsPanel* panel = controlsPanel_) + panel->setControlLabelText(cc, label); +} + +void Editor::Impl::performCCValueChange(unsigned cc, float value) +{ + // TODO(jpc) CC as parameters and automation + + char pathBuf[256]; + sprintf(pathBuf, "/cc%u/value", cc); + sfizz_arg_t args[1]; + args[0].f = value; + sendQueuedOSC(pathBuf, "f", args); +} + +void Editor::Impl::performCCBeginEdit(unsigned cc) +{ + // TODO(jpc) CC as parameters and automation +} + +void Editor::Impl::performCCEndEdit(unsigned cc) +{ + // TODO(jpc) CC as parameters and automation } void Editor::Impl::setActivePanel(unsigned panelId) diff --git a/editor/src/editor/GUIComponents.cpp b/editor/src/editor/GUIComponents.cpp index 16ad7dd9..5f7f7e0f 100644 --- a/editor/src/editor/GUIComponents.cpp +++ b/editor/src/editor/GUIComponents.cpp @@ -516,3 +516,209 @@ void SStyledKnob::draw(CDrawContext* dc) dc->drawLine(p1, p2); } } + +/// +SControlsPanel::SControlsPanel(const CRect& size) + : CScrollView( + size, CRect(), + CScrollView::kVerticalScrollbar|CScrollView::kDontDrawFrame|CScrollView::kAutoHideScrollbars), + listener_(new ControlSlotListener(this)) +{ + setBackgroundColor(CColor(0x00, 0x00, 0x00, 0x00)); + + setScrollbarWidth(10.0); +} + +void SControlsPanel::setControlUsed(uint32_t index, bool used) +{ + bool changed = false; + + if (used) { + if (index + 1 > slots_.size()) + slots_.resize(index + 1); + ControlSlot* slot = slots_[index].get(); + changed = !slot; + if (changed) { + slot = new ControlSlot; + slots_[index].reset(slot); + + // create controls etc... + CCoord knobWidth = 48.0; + CCoord knobHeight = 72.0; + CCoord labelWidth = 96.0; + CCoord labelHeight = 24.0; + CCoord verticalPadding = -12.0; + + CCoord totalWidth = std::max(knobWidth, labelWidth); + CCoord knobX = (totalWidth - knobWidth) / 2.0; + CCoord labelX = (totalWidth - labelWidth) / 2.0; + + CRect knobBounds(knobX, 0.0, knobX + knobWidth, knobHeight); + CRect labelBounds(labelX, knobHeight + verticalPadding, labelX + labelWidth, knobHeight + verticalPadding + labelHeight); + CRect boxBounds = CRect(knobBounds).unite(labelBounds); + + SharedPointer knob = owned(new SStyledKnob(knobBounds, listener_.get(), index)); + SharedPointer label = owned(new CTextLabel(labelBounds)); + SharedPointer box = owned(new CViewContainer(boxBounds)); + + box->addView(knob); + knob->remember(); + box->addView(label); + label->remember(); + + box->setBackgroundColor(CColor(0x00, 0x00, 0x00, 0x00)); + label->setStyle(CTextLabel::kRoundRectStyle); + label->setRoundRectRadius(5.0); + label->setBackColor(CColor(0x2e, 0x34, 0x36)); + label->setText(("CC " + std::to_string(index)).c_str()); + knob->setActiveTrackColor(CColor(0x00, 0xb6, 0x2a)); + knob->setInactiveTrackColor(CColor(0x30, 0x30, 0x30)); + knob->setLineIndicatorColor(CColor(0x00, 0x00, 0x00)); + + slot->knob = knob; + slot->label = label; + slot->box = box; + } + } + else { + if (index < slots_.size() && slots_[index]) { + changed = true; + slots_[index].reset(); + } + } + + if (changed) + updateLayout(); +} + +void SControlsPanel::setControlValue(uint32_t index, float value) +{ + if (index >= slots_.size()) + return; + + ControlSlot* slot = slots_[index].get(); + if (!slot) + return; + + slot->knob->setValue(value); +} + +void SControlsPanel::setControlLabelText(uint32_t index, UTF8StringPtr text) +{ + if (index >= slots_.size()) + return; + + ControlSlot* slot = slots_[index].get(); + if (!slot) + return; + + slot->label->setText(text); +} + +void SControlsPanel::recalculateSubViews() +{ + CScrollView::recalculateSubViews(); + + // maybe the operation just created the scroll bar + if (CScrollbar* vsb = getVerticalScrollbar()) { + // update scrollbar style + vsb->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00)); + vsb->setBackgroundColor(CColor(0x00, 0x00, 0x00, 0x00)); + vsb->setScrollerColor(CColor(0x00, 0x00, 0x00, 0x80)); + } +} + +void SControlsPanel::updateLayout() +{ + removeAll(); + + const CRect viewBounds = getViewSize(); + + bool isFirstSlot = true; + CCoord itemWidth {}; + CCoord itemHeight {}; + CCoord itemOffsetX {}; + + int numColumns {}; + const CCoord horizontalPadding = 24.0; + const CCoord verticalPadding = 5.0; + + int currentRow = 0; + int currentColumn = 0; + int containerBottom = 0; + + uint32_t numSlots = static_cast(slots_.size()); + for (uint32_t i = 0; i < numSlots; ++i) { + ControlSlot* slot = slots_[i].get(); + if (!slot) + continue; + + CViewContainer* box = slot->box; + + if (isFirstSlot) { + itemWidth = box->getWidth(); + itemHeight = box->getHeight(); + isFirstSlot = false; + numColumns = int((viewBounds.getWidth() - horizontalPadding) / + (itemWidth + horizontalPadding)); + numColumns = std::max(1, numColumns); + itemOffsetX = (viewBounds.getWidth() - horizontalPadding - + numColumns * (itemWidth + horizontalPadding)) / 2.0; + } + + CRect itemBounds = box->getViewSize(); + itemBounds.moveTo( + itemOffsetX + horizontalPadding + currentColumn * (horizontalPadding + itemWidth), + verticalPadding + currentRow * (verticalPadding + itemHeight)); + + box->setViewSize(itemBounds); + + containerBottom = itemBounds.bottom; + + addView(box); + box->remember(); + + if (++currentColumn == numColumns) { + currentColumn = 0; + ++currentRow; + } + } + + CRect containerSize = getContainerSize(); + containerSize.bottom = containerBottom; + setContainerSize(CRect(0.0, 0.0, viewBounds.getWidth(), containerBottom + verticalPadding)); +} + +void SControlsPanel::ControlSlotListener::valueChanged(CControl* pControl) +{ + if (panel_->ValueChangeFunction) + panel_->ValueChangeFunction(pControl->getTag(), pControl->getValue()); +} + +void SControlsPanel::ControlSlotListener::controlBeginEdit(CControl* pControl) +{ + if (panel_->BeginEditFunction) + panel_->BeginEditFunction(pControl->getTag()); +} + +void SControlsPanel::ControlSlotListener::controlEndEdit(CControl* pControl) +{ + if (panel_->EndEditFunction) + panel_->EndEditFunction(pControl->getTag()); +} + +/// +SPlaceHolder::SPlaceHolder(const CRect& size, const CColor& color) + : CView(size), color_(color) +{ +} + +void SPlaceHolder::draw(CDrawContext* dc) +{ + const CRect bounds = getViewSize(); + dc->setDrawMode(kAliasing); + dc->setFrameColor(color_); + dc->drawRect(bounds); + dc->drawLine(bounds.getTopLeft(), bounds.getBottomRight()); + dc->drawLine(bounds.getTopRight(), bounds.getBottomLeft()); +} diff --git a/editor/src/editor/GUIComponents.h b/editor/src/editor/GUIComponents.h index 8f497a15..a5899550 100644 --- a/editor/src/editor/GUIComponents.h +++ b/editor/src/editor/GUIComponents.h @@ -6,6 +6,9 @@ #pragma once #include +#include +#include +#include #include "utility/vstgui_before.h" #include "vstgui/lib/controls/cslider.h" @@ -13,8 +16,10 @@ #include "vstgui/lib/controls/ctextlabel.h" #include "vstgui/lib/controls/cbuttons.h" #include "vstgui/lib/controls/coptionmenu.h" +#include "vstgui/lib/controls/cscrollbar.h" #include "vstgui/lib/controls/icontrollistener.h" #include "vstgui/lib/cviewcontainer.h" +#include "vstgui/lib/cscrollview.h" #include "vstgui/lib/ccolor.h" #include "vstgui/lib/dragging.h" #include "utility/vstgui_after.h" @@ -210,3 +215,56 @@ private: CColor inactiveTrackColor_; CColor lineIndicatorColor_; }; + +/// +class SControlsPanel : public CScrollView { +public: + explicit SControlsPanel(const CRect& size); + + void setControlUsed(uint32_t index, bool used); + void setControlValue(uint32_t index, float value); + void setControlLabelText(uint32_t index, UTF8StringPtr text); + + std::function ValueChangeFunction; + std::function BeginEditFunction; + std::function EndEditFunction; + +protected: + void recalculateSubViews() override; + +private: + void updateLayout(); + +private: + struct ControlSlot { + SharedPointer knob; + SharedPointer label; + SharedPointer box; + }; + + class ControlSlotListener : public IControlListener { + public: + explicit ControlSlotListener(SControlsPanel* panel) : panel_(panel) {} + void valueChanged(CControl* pControl) override; + void controlBeginEdit(CControl* pControl) override; + void controlEndEdit(CControl* pControl) override; + + private: + SControlsPanel* panel_ = nullptr; + }; + + std::vector> slots_; + std::unique_ptr listener_; +}; + +/// +class SPlaceHolder : public CView { +public: + explicit SPlaceHolder(const CRect& size, const CColor& color = {0xff, 0x00, 0x00, 0xff}); + +protected: + void draw(CDrawContext* dc) override; + +private: + CColor color_; +}; diff --git a/editor/src/editor/layout/main.hpp b/editor/src/editor/layout/main.hpp index 6f4da9e3..6ea0a316 100644 --- a/editor/src/editor/layout/main.hpp +++ b/editor/src/editor/layout/main.hpp @@ -100,14 +100,15 @@ view__29->addView(view__39); LogicalGroup* const view__40 = createLogicalGroup(CRect(5, 110, 795, 395), -1, "", kCenterText, 14); subPanels_[kPanelControls] = view__40; view__0->addView(view__40); -view__40->setVisible(false); RoundedGroup* const view__41 = createRoundedGroup(CRect(0, 0, 790, 285), -1, "", kCenterText, 14); view__40->addView(view__41); -Label* const view__42 = createLabel(CRect(0, 0, 790, 285), -1, "Controls not available", kCenterText, 40); +ControlsPanel* const view__42 = createControlsPanel(CRect(0, 0, 790, 285), -1, "", kCenterText, 14); +controlsPanel_ = view__42; view__41->addView(view__42); LogicalGroup* const view__43 = createLogicalGroup(CRect(5, 109, 795, 425), -1, "", kCenterText, 14); subPanels_[kPanelSettings] = view__43; view__0->addView(view__43); +view__43->setVisible(false); TitleGroup* const view__44 = createTitleGroup(CRect(255, 26, 535, 126), -1, "Engine", kCenterText, 12); view__43->addView(view__44); ValueMenu* const view__45 = createValueMenu(CRect(25, 60, 85, 85), kTagSetNumVoices, "", kCenterText, 12);