Как мне вложить эти классы и функцию FlexBox? - PullRequest
0 голосов
/ 02 сентября 2018

Я работаю в JUCE, чтобы попытаться создать гибкую структуру панели для использования синтезатора звука. В общем, цель состоит в том, чтобы создать что-то вроде этого:

Panel Look

Там, где есть маркированные группы помеченных ручек.

Я использую JUCE и C ++, которые имеют функцию FlexBox, которая позволяет автоматически организовывать элементы на экране, что идеально. У меня есть такой результат в настоящее время: FlexBox Simple Panel Working

Где все эти помеченные ручки гибко расположены на экране.

То, как это работает, заключается в том, что каждая ручка представляет собой «слайдер», который является встроенным в класс JUCE, и он помечен классом GroupComponent с именем «LabeledSlider». Затем собственный массив выбирает все объекты LabeledSlider, а встроенная функция FlexBox автоматически размещает этот массив на экране.

Это тот рабочий код, который включает в себя простой генератор синусоидальных сигналов, основанный на регуляторах частоты и амплитуды:

class LabeledSlider : public GroupComponent

{
    public:
    LabeledSlider (const String& name)
    {
        setText (name);
        setTextLabelPosition (Justification::centredTop);
        addAndMakeVisible (slider);
    }

    void resized() override
    {
        slider.setBounds (getLocalBounds().reduced (10));
    }

    Slider slider 
    { 
        Slider::RotaryHorizontalVerticalDrag, Slider::TextBoxBelow 
    };


};



class MainContentComponent : public AudioAppComponent
{
public:
    MainContentComponent()

    {

        LabeledSlider* control = new LabeledSlider("Frequency");
        control->slider.setRange(20.0, 20000.0);
        control->slider.setSkewFactorFromMidPoint(500.0);
        control->slider.setNumDecimalPlacesToDisplay(1);
        control->slider.setValue(currentFrequency, dontSendNotification);
        control->slider.onValueChange = [this] { targetFrequency = frequency.slider.getValue(); };
        control->slider.setTextBoxStyle(Slider::TextBoxBelow, false, 100, 20);
        control->slider.setRange(50.0, 5000.0);
        control->slider.setSkewFactorFromMidPoint(500.0);
        control->slider.setNumDecimalPlacesToDisplay(1);
        addAndMakeVisible(knobs.add(control));

        control = new LabeledSlider("Level");
        control->slider.setRange(0.0, 1.0);
        control->slider.onValueChange = [this] { targetLevel = (float)level.slider.getValue(); };
        addAndMakeVisible(knobs.add(control));

        control = new LabeledSlider("Dummy1");
        addAndMakeVisible(knobs.add(control));

        control = new LabeledSlider("Dummy2");
        addAndMakeVisible(knobs.add(control));

        control = new LabeledSlider("Dummy3");
        addAndMakeVisible(knobs.add(control));

        control = new LabeledSlider("Dummy4");
        addAndMakeVisible(knobs.add(control));

        control = new LabeledSlider("Dummy5");
        addAndMakeVisible(knobs.add(control));

        control = new LabeledSlider("Dummy6");
        addAndMakeVisible(knobs.add(control));

        control = new LabeledSlider("Dummy7");
        addAndMakeVisible(knobs.add(control));

        control = new LabeledSlider("Dummy8");
        addAndMakeVisible(knobs.add(control));

        control = new LabeledSlider("Dummy9");
        addAndMakeVisible(knobs.add(control));


    setSize (600, 600);
    setAudioChannels (0, 2); // no inputs, two outputs
    }

    ~MainContentComponent()
    {
        shutdownAudio();
    }

    void resized() override
    {

        //==============================================================================
        FlexBox knobBox;
        knobBox.flexWrap = FlexBox::Wrap::wrap;
        knobBox.justifyContent = FlexBox::JustifyContent::flexStart;
        knobBox.alignContent = FlexBox::AlignContent::flexStart;


        for (auto* k : knobs)
            knobBox.items.add(FlexItem(*k).withMinHeight(80.0f).withMinWidth(80.0f).withFlex(1));

        //==============================================================================
        FlexBox fb;
        fb.flexDirection = FlexBox::Direction::column;
        fb.items.add(FlexItem(knobBox).withFlex(2.5));
        fb.performLayout(getLocalBounds().toFloat());


    }

    inline void updateAngleDelta()
    {
        auto cyclesPerSample = currentFrequency / currentSampleRate;
        angleDelta = cyclesPerSample * 2.0 * MathConstants<double>::pi;
    }

    void prepareToPlay (int, double sampleRate) override
    {
        currentSampleRate = sampleRate;
        updateAngleDelta();
    }

    void releaseResources() override {}

    void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override
    {
        auto* leftBuffer  = bufferToFill.buffer->getWritePointer (0, bufferToFill.startSample);
        auto* rightBuffer = bufferToFill.buffer->getWritePointer (1, bufferToFill.startSample);

        auto localTargetFrequency = targetFrequency;

        if (targetFrequency != currentFrequency)
        {
            auto frequencyIncrement = (targetFrequency - currentFrequency) / bufferToFill.numSamples;

            for (auto sample = 0; sample < bufferToFill.numSamples; ++sample)
            {
                auto currentSample = (float) std::sin (currentAngle);
                currentFrequency += frequencyIncrement;
                updateAngleDelta();
                currentAngle += angleDelta;
                leftBuffer[sample]  = currentSample;
                rightBuffer[sample] = currentSample;
            }

            currentFrequency = localTargetFrequency;
        }
        else
        {
            for (auto sample = 0; sample < bufferToFill.numSamples; ++sample)
            {
                auto currentSample = (float) std::sin (currentAngle);
                currentAngle += angleDelta;
                leftBuffer[sample]  = currentSample;
                rightBuffer[sample] = currentSample;
            }
        }

        auto localTargetLevel = targetLevel;
        bufferToFill.buffer->applyGainRamp (bufferToFill.startSample, bufferToFill.numSamples, currentLevel, localTargetLevel);
        currentLevel = localTargetLevel;
    }

private:
    double currentSampleRate = 0.0, currentAngle = 0.0, angleDelta = 0.0;
    double currentFrequency = 500.0, targetFrequency = 500.0;
    float currentLevel = 0.1f, targetLevel = 0.1f;
    int rotaryDiam = 100;

    LabeledSlider frequency{ "Frequency" };
    LabeledSlider level{ "Level" };
    LabeledSlider dummy1{ "Dummy 1" };
    LabeledSlider dummy2{ "Dummy 2" };
    LabeledSlider dummy3{ "Dummy 3" };
    LabeledSlider dummy4{ "Dummy 4" };
    LabeledSlider dummy5{ "Dummy 5" };
    LabeledSlider dummy6{ "Dummy 6" };
    LabeledSlider dummy7{ "Dummy 7" };
    LabeledSlider dummy8{ "Dummy 8" };
    LabeledSlider dummy9{ "Dummy 9" };

    OwnedArray<LabeledSlider> knobs;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent)
};

Я бы хотел добавить второй слой FlexBox, сгруппировав ручки в больший класс с именем «LabeledGroup», чтобы каждый «LabeledSlider», когда он определен, принадлежал к LabeledGroup.

Затем эту же функцию FlexBox можно использовать для автоматической организации ручек внутри LabeledGroups, а также для организации самих LabeledGroups.

Я новичок во всем этом, хотя, хотя я уже часами читаю о вложенных классах, я не могу понять, как это сделать.

Вот моя грубая попытка реорганизации приведенного выше кода, хотя он, очевидно, не работает:

class LabeledGroup : public GroupComponent

{

public:

    LabeledGroup(const String& name)
    {
        setText(name);
        setTextLabelPosition(Justification::centredTop);


        class LabeledSlider : public GroupComponent
        {
        public:
            LabeledSlider(const String& name)
            {
                setText(name);
                setTextLabelPosition(Justification::centredTop);
                addAndMakeVisible(slider);
            }

            void resized() override
            {
                slider.setBounds(getLocalBounds().reduced(10));
            }

            Slider slider
            {
                Slider::RotaryHorizontalVerticalDrag, Slider::TextBoxBelow
            };

        };

    }

private:
    void resized() override
    {

        OwnedArray<LabeledSlider> knobs;

        //==============================================================================
        FlexBox knobBox;
        knobBox.flexWrap = FlexBox::Wrap::wrap;
        knobBox.justifyContent = FlexBox::JustifyContent::flexStart;
        knobBox.alignContent = FlexBox::AlignContent::flexStart;

        for (auto* k : knobs)
            knobBox.items.add(FlexItem(*k).withMinHeight(80.0f).withMinWidth(80.0f).withFlex(1));

        //==============================================================================
        FlexBox fb;
        fb.flexDirection = FlexBox::Direction::column;
        fb.items.add(FlexItem(knobBox).withFlex(2.5));
        fb.performLayout(getLocalBounds().toFloat());

    }

};




class MainContentComponent : public AudioAppComponent
{
public:
    MainContentComponent()

    {
        addAndMakeVisible(group1);

        LabeledSlider* control = new LabeledSlider("Frequency");
        control->slider.setRange(20.0, 20000.0);
        control->slider.setSkewFactorFromMidPoint(500.0);
        control->slider.setNumDecimalPlacesToDisplay(1);
        control->slider.setValue(currentFrequency, dontSendNotification);
        control->slider.onValueChange = [this] { targetFrequency = frequency.slider.getValue(); };
        control->slider.setTextBoxStyle(Slider::TextBoxBelow, false, 100, 20);
        control->slider.setRange(50.0, 5000.0);
        control->slider.setSkewFactorFromMidPoint(500.0);
        control->slider.setNumDecimalPlacesToDisplay(1);
        addAndMakeVisible(knobs.add(control));

        control = new LabeledSlider("Level");
        control->slider.setRange(0.0, 1.0);
        control->slider.onValueChange = [this] { targetLevel = (float)level.slider.getValue(); };
        addAndMakeVisible(knobs.add(control));

        control = new LabeledSlider("Dummy1");
        addAndMakeVisible(knobs.add(control));

        control = new LabeledSlider("Dummy2");
        addAndMakeVisible(knobs.add(control));

        control = new LabeledSlider("Dummy3");
        addAndMakeVisible(knobs.add(control));

        control = new LabeledSlider("Dummy4");
        addAndMakeVisible(knobs.add(control));

        control = new LabeledSlider("Dummy5");
        addAndMakeVisible(knobs.add(control));

        control = new LabeledSlider("Dummy6");
        addAndMakeVisible(knobs.add(control));

        control = new LabeledSlider("Dummy7");
        addAndMakeVisible(knobs.add(control));

        control = new LabeledSlider("Dummy8");
        addAndMakeVisible(knobs.add(control));

        control = new LabeledSlider("Dummy9");
        addAndMakeVisible(knobs.add(control));

    setSize (600, 600);
    setAudioChannels (0, 2); // no inputs, two outputs
    }

    ~MainContentComponent()
    {
        shutdownAudio();
    }

    void resized() override
    {

        //==============================================================================
        FlexBox knobBox;
        knobBox.flexWrap = FlexBox::Wrap::wrap;
        knobBox.justifyContent = FlexBox::JustifyContent::flexStart;
        knobBox.alignContent = FlexBox::AlignContent::flexStart;

        for (auto* k : knobgroup)
            knobBox.items.add(FlexItem(*k).withMinHeight(80.0f).withMinWidth(80.0f).withFlex(1));

        //==============================================================================
        FlexBox fb;
        fb.flexDirection = FlexBox::Direction::column;
        fb.items.add(FlexItem(knobBox).withFlex(2.5));
        fb.performLayout(getLocalBounds().toFloat());


    }

    inline void updateAngleDelta()
    {
        auto cyclesPerSample = currentFrequency / currentSampleRate;
        angleDelta = cyclesPerSample * 2.0 * MathConstants<double>::pi;
    }

    void prepareToPlay (int, double sampleRate) override
    {
        currentSampleRate = sampleRate;
        updateAngleDelta();
    }

    void releaseResources() override {}

    void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override
    {
        auto* leftBuffer  = bufferToFill.buffer->getWritePointer (0, bufferToFill.startSample);
        auto* rightBuffer = bufferToFill.buffer->getWritePointer (1, bufferToFill.startSample);

        auto localTargetFrequency = targetFrequency;

        if (targetFrequency != currentFrequency)
        {
            auto frequencyIncrement = (targetFrequency - currentFrequency) / bufferToFill.numSamples;

            for (auto sample = 0; sample < bufferToFill.numSamples; ++sample)
            {
                auto currentSample = (float) std::sin (currentAngle);
                currentFrequency += frequencyIncrement;
                updateAngleDelta();
                currentAngle += angleDelta;
                leftBuffer[sample]  = currentSample;
                rightBuffer[sample] = currentSample;
            }

            currentFrequency = localTargetFrequency;
        }
        else
        {
            for (auto sample = 0; sample < bufferToFill.numSamples; ++sample)
            {
                auto currentSample = (float) std::sin (currentAngle);
                currentAngle += angleDelta;
                leftBuffer[sample]  = currentSample;
                rightBuffer[sample] = currentSample;
            }
        }

        auto localTargetLevel = targetLevel;
        bufferToFill.buffer->applyGainRamp (bufferToFill.startSample, bufferToFill.numSamples, currentLevel, localTargetLevel);
        currentLevel = localTargetLevel;
    }

private:
    double currentSampleRate = 0.0, currentAngle = 0.0, angleDelta = 0.0;
    double currentFrequency = 500.0, targetFrequency = 500.0;
    float currentLevel = 0.1f, targetLevel = 0.1f;
    int rotaryDiam = 100;

    LabeledSlider frequency{ "Frequency" };
    LabeledSlider level{ "Level" };
    LabeledSlider dummy1{ "Dummy 1" };
    LabeledSlider dummy2{ "Dummy 2" };
    LabeledSlider dummy3{ "Dummy 3" };
    LabeledSlider dummy4{ "Dummy 4" };
    LabeledSlider dummy5{ "Dummy 5" };
    LabeledSlider dummy6{ "Dummy 6" };
    LabeledSlider dummy7{ "Dummy 7" };
    LabeledSlider dummy8{ "Dummy 8" };
    LabeledSlider dummy9{ "Dummy 9" };

    LabeledGroup group1{ "Group 1" };

    OwnedArray<LabeledGroup> knobgroup;
  JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent)

Я новичок в C ++ и JUCE, так что это сложно для меня.

Моя основная проблема в настоящее время (если принцип обоснован) заключается в том, что я не знаю, как заставить "ownarray" работать внутри класса LabeledGroup, чтобы он автоматически выбирал любые LabeledSliders, определенные в данной LabeledGroup. и, таким образом, организован встроенным FlexBox.

Я также не знаю, как определить ручки как LabeledSliders, которые принадлежат данной LabeledGroup. то есть. Как переписать объявления слайдера, чтобы обозначить группу.

Спасибо, если вы все это прочитали. Любая помощь будет принята с благодарностью. Подобные вещи сложно понять, когда вы новичок, и вы еще не видели реальных примеров применения.

...