mirror of
https://github.com/LMMS/lmms.git
synced 2026-05-12 00:37:05 -04:00
Linked model groups (#4964)
Add labeled controls for different types with a common base class Implement a container for multiple equal groups of linked models and suiting views. Such groups are suited for representing mono effects where each Model occurs twice. A group provides Models for one mono processor and is visually represented with a group box. This concept is common for LADSPA and Lv2, and useful for any mono effect.
This commit is contained in:
133
include/ControlLayout.h
Normal file
133
include/ControlLayout.h
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* ControlLayout.h - layout for controls
|
||||
*
|
||||
* Copyright (c) 2019-2019 Johannes Lorenz <j.git$$$lorenz-ho.me, $$$=@>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This program 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 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** BSD License Usage
|
||||
** Alternatively, you may use this file under the terms of the BSD license
|
||||
** as follows:
|
||||
**
|
||||
** "Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are
|
||||
** met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in
|
||||
** the documentation and/or other materials provided with the
|
||||
** distribution.
|
||||
** * Neither the name of The Qt Company Ltd nor the names of its
|
||||
** contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef CONTROLLAYOUT_H
|
||||
#define CONTROLLAYOUT_H
|
||||
|
||||
#include <QLayout>
|
||||
#include <QMultiMap>
|
||||
#include <QStyle>
|
||||
class QLayoutItem;
|
||||
class QRect;
|
||||
class QString;
|
||||
|
||||
/**
|
||||
Layout for controls (models)
|
||||
|
||||
Originally token from Qt's FlowLayout example. Modified.
|
||||
|
||||
Features a search bar, as well as looking up widgets with string keys
|
||||
Keys have to be provided in the widgets' objectNames
|
||||
*/
|
||||
class ControlLayout : public QLayout
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ControlLayout(QWidget *parent,
|
||||
int margin = -1, int hSpacing = -1, int vSpacing = -1);
|
||||
~ControlLayout() override;
|
||||
|
||||
void addItem(QLayoutItem *item) override;
|
||||
int horizontalSpacing() const;
|
||||
int verticalSpacing() const;
|
||||
Qt::Orientations expandingDirections() const override;
|
||||
bool hasHeightForWidth() const override;
|
||||
int heightForWidth(int) const override;
|
||||
int count() const override;
|
||||
QLayoutItem *itemAt(int index) const override;
|
||||
QLayoutItem *itemByString(const QString& key) const;
|
||||
QSize minimumSize() const override;
|
||||
void setGeometry(const QRect &rect) override;
|
||||
QSize sizeHint() const override;
|
||||
QLayoutItem *takeAt(int index) override;
|
||||
|
||||
private slots:
|
||||
void onTextChanged(const QString&);
|
||||
|
||||
private:
|
||||
int doLayout(const QRect &rect, bool testOnly) const;
|
||||
int smartSpacing(QStyle::PixelMetric pm) const;
|
||||
QMap<QString, QLayoutItem *>::const_iterator pairAt(int index) const;
|
||||
|
||||
QMultiMap<QString, QLayoutItem *> m_itemMap;
|
||||
int m_hSpace;
|
||||
int m_vSpace;
|
||||
// relevant dimension is width, as later, heightForWidth() will be called
|
||||
// 400 looks good and is ~4 knobs in a row
|
||||
constexpr const static int m_minWidth = 400;
|
||||
class QLineEdit* m_searchBar;
|
||||
//! name of search bar, must be ASCII sorted before any alpha numerics
|
||||
static constexpr const char* s_searchBarName = "!!searchBar!!";
|
||||
};
|
||||
|
||||
#endif // CONTROLLAYOUT_H
|
||||
134
include/Controls.h
Normal file
134
include/Controls.h
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Controls.h - labeled control widgets
|
||||
*
|
||||
* Copyright (c) 2019-2019 Johannes Lorenz <j.git$$$lorenz-ho.me, $$$=@>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This program 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 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CONTROLS_H
|
||||
#define CONTROLS_H
|
||||
|
||||
|
||||
#include "Model.h"
|
||||
|
||||
// headers only required for covariance
|
||||
#include "AutomatableModel.h"
|
||||
#include "ComboBoxModel.h"
|
||||
|
||||
|
||||
class QString;
|
||||
class QWidget;
|
||||
class AutomatableModel;
|
||||
|
||||
|
||||
/**
|
||||
These classes provide
|
||||
- a control with a text label
|
||||
- a type safe way to set a model
|
||||
(justification: setting the wrong typed model to a widget will cause
|
||||
hard-to-find runtime errors)
|
||||
*/
|
||||
class Control
|
||||
{
|
||||
public:
|
||||
virtual QWidget* topWidget() = 0;
|
||||
virtual void setText(const QString& text) = 0;
|
||||
|
||||
virtual void setModel(AutomatableModel* model) = 0;
|
||||
virtual AutomatableModel* model() = 0;
|
||||
virtual class AutomatableModelView* modelView() = 0;
|
||||
|
||||
virtual ~Control();
|
||||
};
|
||||
|
||||
|
||||
class KnobControl : public Control
|
||||
{
|
||||
class Knob* m_knob;
|
||||
|
||||
public:
|
||||
void setText(const QString& text) override;
|
||||
QWidget* topWidget() override;
|
||||
|
||||
void setModel(AutomatableModel* model) override;
|
||||
FloatModel* model() override;
|
||||
class AutomatableModelView* modelView() override;
|
||||
|
||||
KnobControl(QWidget* parent = nullptr);
|
||||
~KnobControl() override;
|
||||
};
|
||||
|
||||
|
||||
class ComboControl : public Control
|
||||
{
|
||||
QWidget* m_widget;
|
||||
class ComboBox* m_combo;
|
||||
class QLabel* m_label;
|
||||
|
||||
public:
|
||||
void setText(const QString& text) override;
|
||||
QWidget* topWidget() override { return m_widget; }
|
||||
|
||||
void setModel(AutomatableModel* model) override;
|
||||
ComboBoxModel* model() override;
|
||||
class AutomatableModelView* modelView() override;
|
||||
|
||||
ComboControl(QWidget* parent = nullptr);
|
||||
~ComboControl() override;
|
||||
};
|
||||
|
||||
|
||||
class LcdControl : public Control
|
||||
{
|
||||
class LcdSpinBox* m_lcd;
|
||||
|
||||
public:
|
||||
void setText(const QString& text) override;
|
||||
QWidget* topWidget() override;
|
||||
|
||||
void setModel(AutomatableModel* model) override;
|
||||
IntModel* model() override;
|
||||
class AutomatableModelView* modelView() override;
|
||||
|
||||
LcdControl(int numDigits, QWidget* parent = nullptr);
|
||||
~LcdControl() override;
|
||||
};
|
||||
|
||||
|
||||
class CheckControl : public Control
|
||||
{
|
||||
QWidget* m_widget;
|
||||
class LedCheckBox* m_checkBox;
|
||||
QLabel* m_label;
|
||||
|
||||
public:
|
||||
void setText(const QString& text) override;
|
||||
QWidget* topWidget() override;
|
||||
|
||||
void setModel(AutomatableModel* model) override;
|
||||
BoolModel *model() override;
|
||||
class AutomatableModelView* modelView() override;
|
||||
|
||||
CheckControl(QWidget* parent = nullptr);
|
||||
~CheckControl() override;
|
||||
};
|
||||
|
||||
|
||||
#endif // CONTROLS_H
|
||||
105
include/LinkedModelGroupViews.h
Normal file
105
include/LinkedModelGroupViews.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* LinkedModelGroupViews.h - view for groups of linkable models
|
||||
*
|
||||
* Copyright (c) 2019-2019 Johannes Lorenz <j.git$$$lorenz-ho.me, $$$=@>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This program 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 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LINKEDMODELGROUPVIEWS_H
|
||||
#define LINKEDMODELGROUPVIEWS_H
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <QWidget>
|
||||
|
||||
|
||||
/**
|
||||
@file LinkedModelGroupViews.h
|
||||
See Lv2ViewBase.h for example usage
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
View for a representative processor
|
||||
|
||||
Features:
|
||||
* Remove button for removable models
|
||||
* Simple handling of adding, removing and model changing
|
||||
|
||||
@note Neither this class, nor any inheriting classes, shall inherit
|
||||
ModelView. The "view" in the name is just for consistency
|
||||
with LinkedModelGroupsView.
|
||||
*/
|
||||
class LinkedModelGroupView : public QWidget
|
||||
{
|
||||
public:
|
||||
/**
|
||||
@param colNum numbers of columns for the controls
|
||||
(link LEDs not counted)
|
||||
*/
|
||||
LinkedModelGroupView(QWidget *parent, class LinkedModelGroup* model,
|
||||
std::size_t colNum);
|
||||
~LinkedModelGroupView();
|
||||
|
||||
//! Reconnect models if model changed
|
||||
void modelChanged(class LinkedModelGroup *linkedModelGroup);
|
||||
|
||||
protected:
|
||||
//! Add a control to this widget
|
||||
//! @warning This widget will own this control, do not free it
|
||||
void addControl(class Control *ctrl, const std::string &id,
|
||||
const std::string& display, bool removable);
|
||||
|
||||
void removeControl(const QString &key);
|
||||
|
||||
private:
|
||||
class LinkedModelGroup* m_model;
|
||||
|
||||
//! column number in surrounding grid in LinkedModelGroupsView
|
||||
std::size_t m_colNum;
|
||||
class ControlLayout* m_layout;
|
||||
std::map<std::string, std::unique_ptr<class Control>> m_widgets;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
Container class for one LinkedModelGroupView
|
||||
|
||||
@note It's intended this class does not inherit from ModelView.
|
||||
Inheriting classes need to do that, see e.g. Lv2Instrument.h
|
||||
*/
|
||||
class LinkedModelGroupsView
|
||||
{
|
||||
protected:
|
||||
~LinkedModelGroupsView() = default;
|
||||
|
||||
//! Reconnect models if model changed; to be called by child virtuals
|
||||
void modelChanged(class LinkedModelGroups* ctrlBase);
|
||||
|
||||
private:
|
||||
//! The base class must return the adressed group view,
|
||||
//! which has the same value as "this"
|
||||
virtual LinkedModelGroupView* getGroupView() = 0;
|
||||
};
|
||||
|
||||
|
||||
#endif // LINKEDMODELGROUPVIEWS_H
|
||||
175
include/LinkedModelGroups.h
Normal file
175
include/LinkedModelGroups.h
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* LinkedModelGroups.h - base classes for groups of linked models
|
||||
*
|
||||
* Copyright (c) 2019-2019 Johannes Lorenz <j.git$$$lorenz-ho.me, $$$=@>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This program 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 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LINKEDMODELGROUPS_H
|
||||
#define LINKEDMODELGROUPS_H
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "Model.h"
|
||||
|
||||
|
||||
/**
|
||||
@file LinkedModelGroups.h
|
||||
See Lv2ControlBase.h and Lv2Proc.h for example usage
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
Base class for a group of linked models
|
||||
|
||||
See the LinkedModelGroup class for explenations
|
||||
|
||||
Features:
|
||||
* Models are stored by their QObject::objectName
|
||||
* Models are linked automatically
|
||||
*/
|
||||
class LinkedModelGroup : public Model
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/*
|
||||
Initialization
|
||||
*/
|
||||
//! @param parent model of the LinkedModelGroups class
|
||||
LinkedModelGroup(Model* parent) : Model(parent) {}
|
||||
|
||||
/*
|
||||
Linking (initially only)
|
||||
*/
|
||||
void linkControls(LinkedModelGroup *other);
|
||||
|
||||
/*
|
||||
Models
|
||||
*/
|
||||
struct ModelInfo
|
||||
{
|
||||
QString m_name;
|
||||
class AutomatableModel* m_model;
|
||||
ModelInfo() { /* hopefully no one will use this */ } // TODO: remove?
|
||||
ModelInfo(const QString& name, AutomatableModel* model)
|
||||
: m_name(name), m_model(model) {}
|
||||
};
|
||||
|
||||
// TODO: refactor those 2
|
||||
template<class Functor>
|
||||
void foreach_model(const Functor& ftor)
|
||||
{
|
||||
for (auto itr = m_models.begin(); itr != m_models.end(); ++itr)
|
||||
{
|
||||
ftor(itr->first, itr->second);
|
||||
}
|
||||
}
|
||||
|
||||
template<class Functor>
|
||||
void foreach_model(const Functor& ftor) const
|
||||
{
|
||||
for (auto itr = m_models.cbegin(); itr != m_models.cend(); ++itr)
|
||||
{
|
||||
ftor(itr->first, itr->second);
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t modelNum() const { return m_models.size(); }
|
||||
bool containsModel(const QString& name) const;
|
||||
void removeControl(AutomatableModel *);
|
||||
|
||||
/*
|
||||
Load/Save
|
||||
*/
|
||||
void saveValues(class QDomDocument& doc, class QDomElement& that);
|
||||
void loadValues(const class QDomElement& that);
|
||||
|
||||
signals:
|
||||
// NOTE: when separating core from UI, this will need to be removed
|
||||
// (who would kno if the client is Qt, i.e. it may not have slots at all)
|
||||
// In this case you'd e.g. send the UI something like
|
||||
// "/added <model meta info>"
|
||||
void modelAdded(AutomatableModel* added);
|
||||
void modelRemoved(AutomatableModel* removed);
|
||||
|
||||
public:
|
||||
AutomatableModel* getModel(const std::string& s)
|
||||
{
|
||||
auto itr = m_models.find(s);
|
||||
return (itr == m_models.end()) ? nullptr : itr->second.m_model;
|
||||
}
|
||||
|
||||
//! Register a further model
|
||||
void addModel(class AutomatableModel* model, const QString& name);
|
||||
//! Unregister a model, return true if a model was erased
|
||||
bool eraseModel(const QString& name);
|
||||
|
||||
//! Remove all models
|
||||
void clearModels();
|
||||
|
||||
private:
|
||||
//! models for the controls
|
||||
std::map<std::string, ModelInfo> m_models;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
Container for a group of linked models
|
||||
|
||||
Each group contains the same models and model types. The models are
|
||||
numbered, and equal numbered models are associated and always linked.
|
||||
|
||||
A typical application are two mono plugins making a stereo plugin.
|
||||
|
||||
@note Though this class can contain multiple model groups, a corresponding
|
||||
view ("LinkedModelGroupViews") will only display one group, as they all have
|
||||
the same values
|
||||
|
||||
@note Though called "container", this class does not contain, but only
|
||||
know the single groups. The inheriting classes are responsible for storage.
|
||||
*/
|
||||
class LinkedModelGroups
|
||||
{
|
||||
public:
|
||||
virtual ~LinkedModelGroups();
|
||||
|
||||
void linkAllModels();
|
||||
|
||||
/*
|
||||
Load/Save
|
||||
*/
|
||||
void saveSettings(class QDomDocument& doc, class QDomElement& that);
|
||||
void loadSettings(const class QDomElement& that);
|
||||
|
||||
/*
|
||||
General
|
||||
*/
|
||||
//! Derived classes must return the group with index @p idx,
|
||||
//! or nullptr if @p is out of range
|
||||
virtual LinkedModelGroup* getGroup(std::size_t idx) = 0;
|
||||
//! @see getGroup
|
||||
virtual const LinkedModelGroup* getGroup(std::size_t idx) const = 0;
|
||||
};
|
||||
|
||||
|
||||
#endif // LINKEDMODELGROUPS_H
|
||||
@@ -21,6 +21,13 @@ std::unique_ptr<T> make_unique(Args&&... args)
|
||||
{
|
||||
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
//! Overload for the case a deleter should be specified
|
||||
template<typename T, typename Deleter, typename... Args>
|
||||
std::unique_ptr<T, Deleter> make_unique(Args&&... args)
|
||||
{
|
||||
return std::unique_ptr<T, Deleter>(new T(std::forward<Args>(args)...));
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // include guard
|
||||
|
||||
@@ -31,6 +31,7 @@ set(LMMS_SRCS
|
||||
core/LadspaControl.cpp
|
||||
core/LadspaManager.cpp
|
||||
core/LfoController.cpp
|
||||
core/LinkedModelGroups.cpp
|
||||
core/LocklessAllocator.cpp
|
||||
core/MemoryHelper.cpp
|
||||
core/MemoryManager.cpp
|
||||
|
||||
185
src/core/LinkedModelGroups.cpp
Normal file
185
src/core/LinkedModelGroups.cpp
Normal file
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* LinkedModelGroups.cpp - base classes for groups of linked models
|
||||
*
|
||||
* Copyright (c) 2019-2019 Johannes Lorenz <j.git$$$lorenz-ho.me, $$$=@>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This program 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 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "LinkedModelGroups.h"
|
||||
|
||||
#include <QDomDocument>
|
||||
#include <QDomElement>
|
||||
|
||||
#include "AutomatableModel.h"
|
||||
#include "stdshims.h"
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
LinkedModelGroup
|
||||
*/
|
||||
|
||||
|
||||
void LinkedModelGroup::linkControls(LinkedModelGroup *other)
|
||||
{
|
||||
foreach_model([&other](const std::string& id, ModelInfo& inf)
|
||||
{
|
||||
auto itr2 = other->m_models.find(id);
|
||||
Q_ASSERT(itr2 != other->m_models.end());
|
||||
AutomatableModel::linkModels(inf.m_model, itr2->second.m_model);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void LinkedModelGroup::saveValues(QDomDocument &doc, QDomElement &that)
|
||||
{
|
||||
foreach_model([&doc, &that](const std::string& , ModelInfo& inf)
|
||||
{
|
||||
inf.m_model->saveSettings(doc, that, /*m_models[idx].m_name*/ inf.m_name); /* TODO: m_name useful */
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void LinkedModelGroup::loadValues(const QDomElement &that)
|
||||
{
|
||||
foreach_model([&that](const std::string& , ModelInfo& inf)
|
||||
{
|
||||
// try to load, if it fails, this will load a sane initial value
|
||||
inf.m_model->loadSettings(that, /*m_models()[idx].m_name*/ inf.m_name); /* TODO: m_name useful */
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void LinkedModelGroup::addModel(AutomatableModel *model, const QString &name)
|
||||
{
|
||||
model->setObjectName(name);
|
||||
m_models.emplace(std::string(name.toUtf8().data()), ModelInfo(name, model));
|
||||
connect(model, &AutomatableModel::destroyed,
|
||||
this, [this, model](jo_id_t){
|
||||
if(containsModel(model->objectName()))
|
||||
{
|
||||
emit modelRemoved(model);
|
||||
eraseModel(model->objectName());
|
||||
}
|
||||
},
|
||||
Qt::DirectConnection);
|
||||
|
||||
// View needs to create another child view, e.g. a new knob:
|
||||
emit modelAdded(model);
|
||||
emit dataChanged();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void LinkedModelGroup::removeControl(AutomatableModel* mdl)
|
||||
{
|
||||
if(containsModel(mdl->objectName()))
|
||||
{
|
||||
emit modelRemoved(mdl);
|
||||
eraseModel(mdl->objectName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool LinkedModelGroup::eraseModel(const QString& name)
|
||||
{
|
||||
return m_models.erase(name.toStdString()) > 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void LinkedModelGroup::clearModels()
|
||||
{
|
||||
m_models.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool LinkedModelGroup::containsModel(const QString &name) const
|
||||
{
|
||||
return m_models.find(name.toStdString()) != m_models.end();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
LinkedModelGroups
|
||||
*/
|
||||
|
||||
|
||||
|
||||
LinkedModelGroups::~LinkedModelGroups() {}
|
||||
|
||||
|
||||
|
||||
|
||||
void LinkedModelGroups::linkAllModels()
|
||||
{
|
||||
LinkedModelGroup* first = getGroup(0);
|
||||
LinkedModelGroup* cur;
|
||||
|
||||
for (std::size_t i = 1; (cur = getGroup(i)); ++i)
|
||||
{
|
||||
first->linkControls(cur);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void LinkedModelGroups::saveSettings(QDomDocument& doc, QDomElement& that)
|
||||
{
|
||||
LinkedModelGroup* grp0 = getGroup(0);
|
||||
if (grp0)
|
||||
{
|
||||
QDomElement models = doc.createElement("models");
|
||||
that.appendChild(models);
|
||||
grp0->saveValues(doc, models);
|
||||
}
|
||||
else { /* don't even add a "models" node */ }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void LinkedModelGroups::loadSettings(const QDomElement& that)
|
||||
{
|
||||
QDomElement models = that.firstChildElement("models");
|
||||
LinkedModelGroup* grp0;
|
||||
if (!models.isNull() && (grp0 = getGroup(0)))
|
||||
{
|
||||
// only load the first group, the others are linked to the first
|
||||
grp0->loadValues(models);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ SET(LMMS_SRCS
|
||||
gui/widgets/ComboBox.cpp
|
||||
gui/widgets/ControllerRackView.cpp
|
||||
gui/widgets/ControllerView.cpp
|
||||
gui/widgets/Controls.cpp
|
||||
gui/widgets/CPULoadWidget.cpp
|
||||
gui/widgets/EffectRackView.cpp
|
||||
gui/widgets/EffectView.cpp
|
||||
@@ -71,6 +72,8 @@ SET(LMMS_SRCS
|
||||
gui/widgets/LcdSpinBox.cpp
|
||||
gui/widgets/LcdWidget.cpp
|
||||
gui/widgets/LedCheckbox.cpp
|
||||
gui/widgets/ControlLayout.cpp
|
||||
gui/widgets/LinkedModelGroupViews.cpp
|
||||
gui/widgets/MeterDialog.cpp
|
||||
gui/widgets/MidiPortMenu.cpp
|
||||
gui/widgets/NStateButton.cpp
|
||||
|
||||
308
src/gui/widgets/ControlLayout.cpp
Normal file
308
src/gui/widgets/ControlLayout.cpp
Normal file
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
* ControlLayout.cpp - implementation for ControlLayout.h
|
||||
*
|
||||
* Copyright (c) 2019-2019 Johannes Lorenz <j.git$$$lorenz-ho.me, $$$=@>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This program 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 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** BSD License Usage
|
||||
** Alternatively, you may use this file under the terms of the BSD license
|
||||
** as follows:
|
||||
**
|
||||
** "Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are
|
||||
** met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in
|
||||
** the documentation and/or other materials provided with the
|
||||
** distribution.
|
||||
** * Neither the name of The Qt Company Ltd nor the names of its
|
||||
** contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#include "ControlLayout.h"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QLayoutItem>
|
||||
#include <QLineEdit>
|
||||
#include <QRect>
|
||||
#include <QString>
|
||||
|
||||
constexpr const int ControlLayout::m_minWidth;
|
||||
|
||||
ControlLayout::ControlLayout(QWidget *parent, int margin, int hSpacing, int vSpacing)
|
||||
: QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing),
|
||||
m_searchBar(new QLineEdit(parent))
|
||||
{
|
||||
setContentsMargins(margin, margin, margin, margin);
|
||||
m_searchBar->setPlaceholderText("filter");
|
||||
m_searchBar->setObjectName(s_searchBarName);
|
||||
connect(m_searchBar, SIGNAL(textChanged(const QString&)),
|
||||
this, SLOT(onTextChanged(const QString& )));
|
||||
addWidget(m_searchBar);
|
||||
m_searchBar->setHidden(true); // nothing to filter yet
|
||||
}
|
||||
|
||||
ControlLayout::~ControlLayout()
|
||||
{
|
||||
QLayoutItem *item;
|
||||
while ((item = takeAt(0))) { delete item; }
|
||||
}
|
||||
|
||||
void ControlLayout::onTextChanged(const QString&)
|
||||
{
|
||||
invalidate();
|
||||
update();
|
||||
}
|
||||
|
||||
void ControlLayout::addItem(QLayoutItem *item)
|
||||
{
|
||||
QWidget* widget = item->widget();
|
||||
const QString str = widget ? widget->objectName() : QString("unnamed");
|
||||
m_itemMap.insert(str, item);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
int ControlLayout::horizontalSpacing() const
|
||||
{
|
||||
if (m_hSpace >= 0) { return m_hSpace; }
|
||||
else
|
||||
{
|
||||
return smartSpacing(QStyle::PM_LayoutHorizontalSpacing);
|
||||
}
|
||||
}
|
||||
|
||||
int ControlLayout::verticalSpacing() const
|
||||
{
|
||||
if (m_vSpace >= 0) { return m_vSpace; }
|
||||
else
|
||||
{
|
||||
return smartSpacing(QStyle::PM_LayoutVerticalSpacing);
|
||||
}
|
||||
}
|
||||
|
||||
int ControlLayout::count() const
|
||||
{
|
||||
return m_itemMap.size() - 1;
|
||||
}
|
||||
|
||||
QMap<QString, QLayoutItem*>::const_iterator
|
||||
ControlLayout::pairAt(int index) const
|
||||
{
|
||||
if (index < 0) { return m_itemMap.cend(); }
|
||||
|
||||
auto skip = [&](QLayoutItem* item) -> bool
|
||||
{
|
||||
return item->widget()->objectName() == s_searchBarName;
|
||||
};
|
||||
|
||||
QMap<QString, QLayoutItem*>::const_iterator itr = m_itemMap.cbegin();
|
||||
for (; itr != m_itemMap.cend() && (index > 0 || skip(itr.value())); ++itr)
|
||||
{
|
||||
if(!skip(itr.value())) { index--; }
|
||||
}
|
||||
return itr;
|
||||
}
|
||||
|
||||
// linear time :-(
|
||||
QLayoutItem *ControlLayout::itemAt(int index) const
|
||||
{
|
||||
auto itr = pairAt(index);
|
||||
return (itr == m_itemMap.end()) ? nullptr : itr.value();
|
||||
}
|
||||
|
||||
QLayoutItem *ControlLayout::itemByString(const QString &key) const
|
||||
{
|
||||
auto itr = m_itemMap.find(key);
|
||||
return (itr == m_itemMap.end()) ? nullptr : *itr;
|
||||
}
|
||||
|
||||
// linear time :-(
|
||||
QLayoutItem *ControlLayout::takeAt(int index)
|
||||
{
|
||||
auto itr = pairAt(index);
|
||||
return (itr == m_itemMap.end()) ? nullptr : m_itemMap.take(itr.key());
|
||||
}
|
||||
|
||||
Qt::Orientations ControlLayout::expandingDirections() const
|
||||
{
|
||||
return Qt::Orientations();
|
||||
}
|
||||
|
||||
bool ControlLayout::hasHeightForWidth() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int ControlLayout::heightForWidth(int width) const
|
||||
{
|
||||
int height = doLayout(QRect(0, 0, width, 0), true);
|
||||
return height;
|
||||
}
|
||||
|
||||
void ControlLayout::setGeometry(const QRect &rect)
|
||||
{
|
||||
QLayout::setGeometry(rect);
|
||||
doLayout(rect, false);
|
||||
}
|
||||
|
||||
QSize ControlLayout::sizeHint() const
|
||||
{
|
||||
return minimumSize();
|
||||
}
|
||||
|
||||
QSize ControlLayout::minimumSize() const
|
||||
{
|
||||
// original formula from Qt's FlowLayout example:
|
||||
// get maximum height and width for all children.
|
||||
// as Qt will later call heightForWidth, only the width here really matters
|
||||
QSize size;
|
||||
for (const QLayoutItem *item : qAsConst(m_itemMap))
|
||||
{
|
||||
size = size.expandedTo(item->minimumSize());
|
||||
}
|
||||
const QMargins margins = contentsMargins();
|
||||
size += QSize(margins.left() + margins.right(), margins.top() + margins.bottom());
|
||||
|
||||
// the original formula would leed to ~1 widget per row
|
||||
// bash it at least to 400 so we have ~4 knobs per row
|
||||
size.setWidth(qMax(size.width(), m_minWidth));
|
||||
return size;
|
||||
}
|
||||
|
||||
int ControlLayout::doLayout(const QRect &rect, bool testOnly) const
|
||||
{
|
||||
int left, top, right, bottom;
|
||||
getContentsMargins(&left, &top, &right, &bottom);
|
||||
QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
|
||||
int x = effectiveRect.x();
|
||||
int y = effectiveRect.y();
|
||||
int lineHeight = 0;
|
||||
|
||||
const QString filterText = m_searchBar->text();
|
||||
bool first = true;
|
||||
|
||||
QMapIterator<QString, QLayoutItem*> itr(m_itemMap);
|
||||
while (itr.hasNext())
|
||||
{
|
||||
itr.next();
|
||||
QLayoutItem* item = itr.value();
|
||||
QWidget *wid = item->widget();
|
||||
if (wid)
|
||||
{
|
||||
if ( first || // do not filter search bar
|
||||
filterText.isEmpty() || // no filter - pass all
|
||||
itr.key().contains(filterText, Qt::CaseInsensitive))
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
// for the search bar, only show it if there are at least
|
||||
// two control widgets (i.e. at least 3 widgets)
|
||||
if (m_itemMap.size() > 2) { wid->show(); }
|
||||
else { wid->hide(); }
|
||||
}
|
||||
else { wid->show(); }
|
||||
|
||||
int spaceX = horizontalSpacing();
|
||||
if (spaceX == -1)
|
||||
{
|
||||
spaceX = wid->style()->layoutSpacing(
|
||||
QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
|
||||
}
|
||||
int spaceY = verticalSpacing();
|
||||
if (spaceY == -1)
|
||||
{
|
||||
spaceY = wid->style()->layoutSpacing(
|
||||
QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
|
||||
}
|
||||
int nextX = x + item->sizeHint().width() + spaceX;
|
||||
if (nextX - spaceX > effectiveRect.right() && lineHeight > 0)
|
||||
{
|
||||
x = effectiveRect.x();
|
||||
y = y + lineHeight + spaceY;
|
||||
nextX = x + item->sizeHint().width() + spaceX;
|
||||
lineHeight = 0;
|
||||
}
|
||||
|
||||
if (!testOnly)
|
||||
{
|
||||
item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
|
||||
}
|
||||
|
||||
x = nextX;
|
||||
lineHeight = qMax(lineHeight, item->sizeHint().height());
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
wid->hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
return y + lineHeight - rect.y() + bottom;
|
||||
}
|
||||
|
||||
int ControlLayout::smartSpacing(QStyle::PixelMetric pm) const
|
||||
{
|
||||
QObject *parent = this->parent();
|
||||
if (!parent) { return -1; }
|
||||
else if (parent->isWidgetType())
|
||||
{
|
||||
QWidget *pw = static_cast<QWidget *>(parent);
|
||||
return pw->style()->pixelMetric(pm, nullptr, pw);
|
||||
}
|
||||
else { return static_cast<QLayout *>(parent)->spacing(); }
|
||||
}
|
||||
|
||||
|
||||
140
src/gui/widgets/Controls.cpp
Normal file
140
src/gui/widgets/Controls.cpp
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Controls.cpp - labeled control widgets
|
||||
*
|
||||
* Copyright (c) 2019-2019 Johannes Lorenz <j.git$$$lorenz-ho.me, $$$=@>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This program 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 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Controls.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QString>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "ComboBox.h"
|
||||
#include "LcdSpinBox.h"
|
||||
#include "LedCheckbox.h"
|
||||
#include "Knob.h"
|
||||
|
||||
|
||||
|
||||
|
||||
Control::~Control() {}
|
||||
|
||||
|
||||
|
||||
|
||||
void KnobControl::setText(const QString &text) { m_knob->setLabel(text); }
|
||||
|
||||
QWidget *KnobControl::topWidget() { return m_knob; }
|
||||
|
||||
void KnobControl::setModel(AutomatableModel *model)
|
||||
{
|
||||
m_knob->setModel(model->dynamicCast<FloatModel>(true));
|
||||
}
|
||||
|
||||
FloatModel *KnobControl::model() { return m_knob->model(); }
|
||||
|
||||
AutomatableModelView* KnobControl::modelView() { return m_knob; }
|
||||
|
||||
KnobControl::KnobControl(QWidget *parent) :
|
||||
m_knob(new Knob(parent)) {}
|
||||
|
||||
KnobControl::~KnobControl() {}
|
||||
|
||||
|
||||
|
||||
|
||||
void ComboControl::setText(const QString &text) { m_label->setText(text); }
|
||||
|
||||
void ComboControl::setModel(AutomatableModel *model)
|
||||
{
|
||||
m_combo->setModel(model->dynamicCast<ComboBoxModel>(true));
|
||||
}
|
||||
|
||||
ComboBoxModel *ComboControl::model() { return m_combo->model(); }
|
||||
|
||||
AutomatableModelView* ComboControl::modelView() { return m_combo; }
|
||||
|
||||
ComboControl::ComboControl(QWidget *parent) :
|
||||
m_widget(new QWidget(parent)),
|
||||
m_combo(new ComboBox(nullptr)),
|
||||
m_label(new QLabel(m_widget))
|
||||
{
|
||||
m_combo->setFixedSize(64, 22);
|
||||
QVBoxLayout* vbox = new QVBoxLayout(m_widget);
|
||||
vbox->addWidget(m_combo);
|
||||
vbox->addWidget(m_label);
|
||||
m_combo->repaint();
|
||||
}
|
||||
|
||||
ComboControl::~ComboControl() {}
|
||||
|
||||
|
||||
|
||||
|
||||
void CheckControl::setText(const QString &text) { m_label->setText(text); }
|
||||
|
||||
QWidget *CheckControl::topWidget() { return m_widget; }
|
||||
|
||||
void CheckControl::setModel(AutomatableModel *model)
|
||||
{
|
||||
m_checkBox->setModel(model->dynamicCast<BoolModel>(true));
|
||||
}
|
||||
|
||||
BoolModel *CheckControl::model() { return m_checkBox->model(); }
|
||||
|
||||
AutomatableModelView* CheckControl::modelView() { return m_checkBox; }
|
||||
|
||||
CheckControl::CheckControl(QWidget *parent) :
|
||||
m_widget(new QWidget(parent)),
|
||||
m_checkBox(new LedCheckBox(nullptr, QString(), LedCheckBox::Green)),
|
||||
m_label(new QLabel(m_widget))
|
||||
{
|
||||
QVBoxLayout* vbox = new QVBoxLayout(m_widget);
|
||||
vbox->addWidget(m_checkBox);
|
||||
vbox->addWidget(m_label);
|
||||
}
|
||||
|
||||
CheckControl::~CheckControl() {}
|
||||
|
||||
|
||||
|
||||
|
||||
void LcdControl::setText(const QString &text) { m_lcd->setLabel(text); }
|
||||
|
||||
QWidget *LcdControl::topWidget() { return m_lcd; }
|
||||
|
||||
void LcdControl::setModel(AutomatableModel *model)
|
||||
{
|
||||
m_lcd->setModel(model->dynamicCast<IntModel>(true));
|
||||
}
|
||||
|
||||
IntModel *LcdControl::model() { return m_lcd->model(); }
|
||||
|
||||
AutomatableModelView* LcdControl::modelView() { return m_lcd; }
|
||||
|
||||
LcdControl::LcdControl(int numDigits, QWidget *parent) :
|
||||
m_lcd(new LcdSpinBox(numDigits, parent))
|
||||
{
|
||||
}
|
||||
|
||||
LcdControl::~LcdControl() {}
|
||||
|
||||
160
src/gui/widgets/LinkedModelGroupViews.cpp
Normal file
160
src/gui/widgets/LinkedModelGroupViews.cpp
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* LinkedModelGroupViews.h - view for groups of linkable models
|
||||
*
|
||||
* Copyright (c) 2019-2019 Johannes Lorenz <j.git$$$lorenz-ho.me, $$$=@>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
* This program 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 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program 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 this program (see COPYING); if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "LinkedModelGroupViews.h"
|
||||
|
||||
#include <QPushButton>
|
||||
#include "Controls.h"
|
||||
#include "ControlLayout.h"
|
||||
#include "LinkedModelGroups.h"
|
||||
|
||||
|
||||
/*
|
||||
LinkedModelGroupViewBase
|
||||
*/
|
||||
|
||||
|
||||
LinkedModelGroupView::LinkedModelGroupView(QWidget* parent,
|
||||
LinkedModelGroup *model, std::size_t colNum) :
|
||||
QWidget(parent),
|
||||
m_model(model),
|
||||
m_colNum(colNum),
|
||||
m_layout(new ControlLayout(this))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
LinkedModelGroupView::~LinkedModelGroupView() {}
|
||||
|
||||
|
||||
|
||||
|
||||
void LinkedModelGroupView::modelChanged(LinkedModelGroup *group)
|
||||
{
|
||||
// reconnect models
|
||||
group->foreach_model([this](const std::string& str,
|
||||
const LinkedModelGroup::ModelInfo& minf)
|
||||
{
|
||||
auto itr = m_widgets.find(str);
|
||||
// in case there are new or deleted widgets, the subclass has already
|
||||
// modified m_widgets, so this will go into the else case
|
||||
if (itr == m_widgets.end())
|
||||
{
|
||||
// no widget? this can happen when the whole view is being destroyed
|
||||
// (for some strange reasons)
|
||||
}
|
||||
else
|
||||
{
|
||||
itr->second->setModel(minf.m_model);
|
||||
}
|
||||
});
|
||||
|
||||
m_model = group;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void LinkedModelGroupView::addControl(Control* ctrl, const std::string& id,
|
||||
const std::string &display, bool removable)
|
||||
{
|
||||
int wdgNum = static_cast<int>(m_widgets.size());
|
||||
if (ctrl)
|
||||
{
|
||||
QWidget* box = new QWidget(this);
|
||||
QHBoxLayout* boxLayout = new QHBoxLayout(box);
|
||||
boxLayout->addWidget(ctrl->topWidget());
|
||||
|
||||
if (removable)
|
||||
{
|
||||
QPushButton* removeBtn = new QPushButton;
|
||||
removeBtn->setIcon( embed::getIconPixmap( "discard" ) );
|
||||
QObject::connect(removeBtn, &QPushButton::clicked,
|
||||
this, [this,ctrl](bool){
|
||||
AutomatableModel* controlModel = ctrl->model();
|
||||
// remove control out of model group
|
||||
// (will also remove it from the UI)
|
||||
m_model->removeControl(controlModel);
|
||||
// delete model (includes disconnecting all connections)
|
||||
delete controlModel;
|
||||
},
|
||||
Qt::DirectConnection);
|
||||
boxLayout->addWidget(removeBtn);
|
||||
}
|
||||
|
||||
// required, so the Layout knows how to sort/filter widgets by string
|
||||
box->setObjectName(QString::fromStdString(display));
|
||||
m_layout->addWidget(box);
|
||||
|
||||
// take ownership of control and add it
|
||||
m_widgets.emplace(id, std::unique_ptr<Control>(ctrl));
|
||||
++wdgNum;
|
||||
}
|
||||
|
||||
if (isHidden()) { setHidden(false); }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void LinkedModelGroupView::removeControl(const QString& key)
|
||||
{
|
||||
auto itr = m_widgets.find(key.toStdString());
|
||||
if (itr != m_widgets.end())
|
||||
{
|
||||
QLayoutItem* item = m_layout->itemByString(key);
|
||||
Q_ASSERT(!!item);
|
||||
QWidget* wdg = item->widget();
|
||||
Q_ASSERT(!!wdg);
|
||||
|
||||
// remove item from layout
|
||||
m_layout->removeItem(item);
|
||||
// the widget still exists and is visible - remove it now
|
||||
delete wdg;
|
||||
// erase widget pointer from dictionary
|
||||
m_widgets.erase(itr);
|
||||
// repaint immediately, so we don't have dangling model views
|
||||
m_layout->update();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
LinkedModelGroupsViewBase
|
||||
*/
|
||||
|
||||
|
||||
void LinkedModelGroupsView::modelChanged(LinkedModelGroups *groups)
|
||||
{
|
||||
LinkedModelGroupView* groupView = getGroupView();
|
||||
LinkedModelGroup* group0 = groups->getGroup(0);
|
||||
if (group0 && groupView)
|
||||
{
|
||||
groupView->modelChanged(group0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user