First version of a new dynamic LADSPA control dialog (#2068)
Introduce a new version of the LADSPA control dialog which uses "bar controllers" and arranges them in a grid layout. Long parameter names are elided long if needed. The new dialog is implemented in the class `LadspaMatrixControlDialog`.
Note: it is still possible to reactivate the old dialog as it has not been removed yet. You can do so in `plugins/LadspaEffect/LadspaControls.h` by replacing the implementation of `createView()` with the following:
```
gui::EffectControlDialog* createView() override
{
return new gui::LadspaControlDialog( this );
}
```
Introduce the new base class `FloatModelEditorBase`. It serves as the base widget for widgets that manipulate a float model and provides some base functionalities like context menus, edit dialogs, mouse handling, etc. Currently `BarModelEditor` and `Knob` inherit from `FloatModelEditorBase`. Therefore lots of code was moved from `Knob` to `FloatModelEditorBase`.
`BarModelEditor` supports style sheets. See the changes in style.css for the available options and their usage.
Extend `LedCheckBox` so that it adds support to render the text with the default font in the default size. The class now supports two modes:
* Legacy mode
* Non-legacy mode
Legacy mode is the default and renders the LedCheckBox as before. The font is set to 7 pixels and all the text is rendered with a shadow.
Non-legacy mode uses the current font to render the text. The text is rendered without a shadow and the pixmap is centered vertically to the left side of the text. Non-legacy mode is currently only used in the context of the matrix LADSPA dialog. Toggle options are rendered using it as well as the "Link Channels" button.
Add `TempoSyncBarModelEditor` which adds a tempo sync option to a `BarModelEditor`. Please note that the code was mostly copied and adjusted from the `TempoSyncKnob` class. It was not attempted yet to unify some of the code because with the current design it seems to be a road to "inheritance hell". A better solution might be to refactor the code so that it becomes more composable.
What follows are the individual commit messages of the 64 commits that have been squashed into a single merge commit.
* First version of a new dynamic LADSPA control dialog
The new dialog shows the LADSPA controls in a matrix layout. Each row
corresponds to a LADSPA parameter. Each parameter can have several
channels which can be linked. Each channel has its own row of controls.
The class LadspaMatrixControlView was added by copying and modifying
LadspaControlView to get rid of the link buttons which are associated
with some controls and some labels.
* Remove trailing whitespaces
* Merge fix
* Use the new connect syntax
* Remove empty destructors
* Use override keyword
* Reformat a bit
* Use nullptr
* Use range-based loops
* Add BarModelEditor and improve layouts
Add the new class `BarModelEditor` which is intended to become a new way
to adjust values of float models.
Simplify the layout in `LadspaMatrixControlDialog` by removing some
nested layouts. Remove the "Parameters" column.
Adjust `LadspaMatrixControlView` to implement the following changes:
* Show the name of the control next to toggle buttons (`LedCheckBox`).
* Use the new `BarModelEditor` for integer and float types.
* SHow the name of the control next to time based parameters that use
`TempoSyncKnob`.
The names are shown so that the "Parameters" column can be removed.
Technical details
------------------
The class `LadspaMatrixControlDialog` now creates a widget that contains
the matrix layout with the controls. This widget is then added to a
scroll area. The layout is populated in the new method
`arrangeControls`.
Add some helper methods to `LadspaMatrixControlDialog` which retrieve
the `LadspaControls` instance and the number of channels.
Add the implementation of `BarModelEditor` to `src/gui/CMakeLists.txt`.
TODOs
------
Extract common code out of the `Knob` class so that it can be reused by
`BarModelEditor`.
* Use factory to create LADSPA control widgets
Replace the class `LadspaMatrixControlView` with the factory class
`LadspaWidgetFactory`. The former was a widget that wrapped the widget
representation of a LADSPA control in yet another widget with a layout.
The factory simply returns the configured widget so that it can be
incorporated directly in layouts or other widgets.
Adjust `LadspaMatrixControlDialog` so that it uses the
`LadspaWidgetFactory` instead of the `LadspaMatrixControlView`.
* Initial version of FloatModelEditorBase
Create an initial version of `FloatModelEditorBase`. It is intended to
become the base widget for all widgets that manipulate a float model and
provides some base functionalities like context menus, edit dialogs,
mouse handling, etc.
The initial version is a copy of `Knob`. The enum and its values have
been suffixed with "Temp" as they will be removed later anyway. Same
applies for the function `convertPixmapToGrayScaleTemp`.
* Remove Knob related code from FloatModelEditorBase
First removal of Knob related code from FloatModelEditorBase.
* Remove setHtmlLabel
* Remove enum for knob types
* Remove label related code
Remove setLabel and the members m_label and m_isHtmlLabel.
* Remove several Qt properties
* Remove angle related members and methods
* Remove unused members and includes
* Clean up includes
* Make BarModelEditor inherit from FloatModelEditorBase
Make `BarModelEditor` inherit from `FloatModelEditorBase` so that it
inherits all shared functionality. Currently the class mostly implements
size related methods and overrides the paint method.
* Move LadspaWidgetFactory to LadspaEffect
Move the class `LadspaWidgetFactory` to the project LadspaEffect to make
it hopefully compile with mingw32 and mingw64.
Interestingly the code compiled and worked under Linux and MacOS.
* Code adjustments after scripted checks
Add an include guard and an additional `#pragme once`. Add comments to
closing namespace scopes.
* Export BarModelEditor
Export BarModelEditor so that for example the LadspaEffect project/
plugin can use it.
* Improve rendering of bar model editor
Increase the margin from 2 to 3 so that we have a more prominent border
around the filled area.
Fix a problem in the rendering code which led to situations where the
bar was drawn to the left of the start position for small values.
Return a more exact minimum size hint.
* Elide long parameter names
Elide long parameter names if needed.
* Enable horizontal direction of manipulation
Extend `FloatModelEditorBase` so that it also allows manipulation of the
values when the mouse is moved in horizontal directions. The default is
to use the vertical direction.
Make use of this new feature in `BarModelEditor` which now reacts to
horizontal movements when changing values.
* Represent enum parameters using the bar widget
The implementation of the current LADSPA dialog in master uses knobs to
represent enum parameters. Therefore it should also work with the new
bar widget.
This gets rid of many labels with the parameter name followed by
"(unsupported)".
* Remove TODO in LadspaWidgetFactory
* Render text of LedCheckBox with default font
Extend LedCheckBox so that it adds support to render the text with the
default font in the default size. The class now supports two modes:
* Legacy mode
* Non-legacy mode
Legacy mode is the default and renders the LedCheckBox as before. The
font is set to 7 pixels and all the text is rendered with a shadow.
Non-legacy mode uses the current font to render the text. The text is
rendered without a shadow and the pixmap is centered vertically to the
left side of the text.
The non-legacy mode is currently only used in the context of the matrix
LADSPA dialog. Toggle options are rendered using it as well as the "Link
Channels" button.
* Use "yellow" LEDs for bool parameters
Use "yellow" LEDs (which are actually blue) for dynamically added bool
parameters so that the dialog is consistent with regards to the LED
colors. The "Link Channels" button also uses a "yellow" LED.
* Fix Qt5 builds
Fix the Qt5 builds which do not know horizontalAdvance as a member of
FontMetrics.
Also refactor LedCheckBox::onTextUpdated to make it more compact.
* Style sheets for BarModelEditor
Add style sheets options for BarModelEditor. See the changes in
style.css for the available options and their usage.
The members that can be manipulated by the style sheet options are
initialized accoriding to the palette so that the BarModelEditor should
also render something useful if no style is set.
Adjust the paintEvent method to use the style sheet brushes and colors.
* Layout optimizations
Set the vertical scroll bar to always show so that the controls do not
move around if the scroll bar is hidden or shown.
Set the margin of the grid layout to 0 so that the whole dialog becomes
tighter.
* Get rid of initial scroll bars
Make sure that the widget is shown without a scrollbar whenever possible
using the following rules.
If the widget fits on the workspace we use the height of the widget as
the minimum size of the scroll area. This will ensure that the scrollbar
is not shown initially (and never will be).
If the widget is larger than the workspace then we want it to mostly
cover the workspace. In that case the minimum height is set to 90 % of
the workspace.
* Switch to a green theme
Switch to a green theme to better match the default theme.
* Adjust classic theme
Adjust the classic theme so that it renders the bars in a legible way.
* Fixes for CodeFactor
Remove virtual keyword from methods that are marked as override.
Remove whitespaces that make CodeFactor unhappy. One of these fixes
includes moving the implementation of
LadspaMatrixControlDialog::isResizable into the cpp file.
* CodeFactor is still unhappy
Remove a blank line after the constructor.
* Center align time based controls
Align time based controls, i.e. knobs, in the center.
The implementation defeats the purpose of the widget factory a bit but
it makes the design look nicer.
* Fix build
Fix the build by adjusting the enums. I added the changes while writing
the commit message for the merge commit but they did not make it.
* Attempt at CodeFactor warning
Try to make the last CodeFactor warning disappear.
* Add bar controller with tempo sync option
Add `TempoSyncBarModelEditor` which adds a tempo sync option to a `BarModelEditor`. Please note that the code was mostly copied and adjusted from the `TempoSyncKnob` class. It was not attempted yet to unify some of the code because with the current design it seems to be a road to "inheritance hell". A better solution might be to refactor the code so that it is more composable.
Another option might be to move the tempo sync functionality into a class like `FloatModelEditorBase`. Although this would not be a 100% right place either because `TempoSyncKnobModel` inherits from `FloatModel`. In that case we'd have specialized code in a generic base class.
Adjust `LadspaWidgetFactory` so that it now returns an instance of a `TempoSyncBarModelEditor` instead of a `TempoSyncKnob`.
Remove the extra layout code from `LadspaMatrixControlDialog.cpp` as most things are bar editors now.
Adjust `TempoSyncKnobModel` so we do not have to make `TempoSyncBarModelEditor` a friend of it.
* Another attempt to please CodeFactor
Have another attempt to fix CodeFactor's complaints about `LadspaMatrixControlDialog.h`.
* Coding conventions, blanks and blank lines
Remove lots of unnecessary white space. Remove blank lines. Remove leading underscores from parameters.
Remove line breaks in `TempoSyncBarModelEditor.cpp` to make the code more readable. Remove repeated method calls by introducing local variables.
* Break down complex method
Break down the complex method `TempoSyncBarModelEditor::updateDescAndIcon` by delegating to the new methods `updateTextDescription` and `updateIcon`.
* Another attempt to please CodeFactor
Another attempt to fix `LadspaMatrixControlDialog.h` to please CodeFactor.
* Work on TODOs
The TODO "Assumes that all processors are equal..." has been turned into a comment when we start iterating over the channels. If the channels are not of the same structure this would likely also have been a problem in the old implementation.
The TODO "Use a factory..." has been removed as this is already done.
The include of `FloatModelEditorBase.h` in LadspaWidgetFactory has been removed.
* Modifier keys for mouse wheel adjustments
Integrate the changes of commit 7000afb2ea into `FloatModelEditorBase`.
This commit added modifier keys for mouse wheel adjustments to the `Knob` class. The port to `FloatModelEditorBase` results in the bar control now also supporting different scales of adjustments that can be switched with the Shift, Ctrl and Alt keys.
* Show current value on mouse over
Integrate the changes of commit fcdf4c0568 into `FloatModelEditorBase`. This commit lets users show the current value of a float model when the mouse is moved over the control.
* Make Knob inherit from FloatModelEditorBase
Make `Knob` inherit from `FloatModelEditorBase`. This is mostly a continuation for the changes introduced with commit c63d86f.
The idea is that `FloatModelEditorBase` contains the underlying functionality and logic to deal with float models. `Knob` and other classes then only override the presentation aspects. This way `Knob` and `BarModelEditor` can share the same functionality but can differ in how they present the data.
Technical details
------------------
Remove all methods that are already defined in `FloatModelEditorBase` from `Knob`. These are all methods that are defined in the same way in `FloatModelEditorBase`. The method `paintEvent` is not removed because it is overridden by `Knob` as it has its own presentation.
Remove forward declaration of `QPixmap` from `FloatModelEditorBase` as it was not used. Remove unused function `convertPixmapToGrayScaleTemp` from `FloatModelEditorBase`.
* Cleanup includes in Knob class
* Code style changes in FloatModelEditorBase
`FloatModelEditorBase` is a new class/file. Therefore we can do some extensive code cleanup on the code that was initially copied from `Knob`.
Adjust whitespace for methods and some if-statements. Remove underscores from parameter names. Use only two blank lines between method definitions.
* Cleanup include for FloatModelEditorBase
* Show effect name as title of dialog
Add the effect name as the title in the content of the window. This improves the structure of the dialog as it is now clearer from the content itself to which effect the controls belong to.
This duplicates the information from the window title. However, the window title is rather small and the larger font in the content makes it easier to find an effect in a set of opened dialogs.
* Revert "Show effect name as title of dialog"
This reverts commit 8ce0d366d0.
* Fix problem with LedCheckBox in grid layout
The LedCheckBox does not play nice when being used in a grid layout as it disables the resizing behavior of every column it appears in.
The solution is to wrap it into the layout of a parent widget that knows how to behave.
* Reduce minimum width of BarModelEditor
Reduce the minimum width of `BarModelEditor` from 200 to 50.
---------
Co-authored-by: Hyunjin Song <tteu.ingog@gmail.com>
This commit is contained in:
committed by
GitHub
parent
d5e6ac6dc5
commit
c6ed4a274a
@@ -975,6 +975,11 @@ lmms--gui--CompressorControlDialog lmms--gui--Knob {
|
||||
qproperty-lineWidth: 2;
|
||||
}
|
||||
|
||||
lmms--gui--BarModelEditor {
|
||||
qproperty-backgroundBrush: rgba(28, 73, 51, 255);
|
||||
qproperty-barBrush: rgba(17, 136, 71, 255);
|
||||
}
|
||||
|
||||
/* palette information */
|
||||
|
||||
lmms--gui--LmmsPalette {
|
||||
|
||||
@@ -1019,6 +1019,11 @@ lmms--gui--CompressorControlDialog lmms--gui--Knob {
|
||||
qproperty-lineWidth: 2;
|
||||
}
|
||||
|
||||
lmms--gui--BarModelEditor {
|
||||
qproperty-backgroundBrush: rgba(28, 73, 51, 255);
|
||||
qproperty-barBrush: rgba(17, 136, 71, 255);
|
||||
}
|
||||
|
||||
/* palette information */
|
||||
|
||||
lmms--gui--LmmsPalette {
|
||||
|
||||
76
include/BarModelEditor.h
Normal file
76
include/BarModelEditor.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* BarModelEditor.h - edit model values using a bar display
|
||||
*
|
||||
* Copyright (c) 2023-now Michael Gregorius
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef LMMS_GUI_BAR_MODEL_EDITOR_H
|
||||
#define LMMS_GUI_BAR_MODEL_EDITOR_H
|
||||
|
||||
#include "FloatModelEditorBase.h"
|
||||
|
||||
|
||||
namespace lmms::gui
|
||||
{
|
||||
|
||||
class LMMS_EXPORT BarModelEditor : public FloatModelEditorBase
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Q_PROPERTY(QBrush backgroundBrush READ getBackgroundBrush WRITE setBackgroundBrush)
|
||||
Q_PROPERTY(QBrush barBrush READ getBarBrush WRITE setBarBrush)
|
||||
Q_PROPERTY(QColor textColor READ getTextColor WRITE setTextColor)
|
||||
|
||||
BarModelEditor(QString text, FloatModel * floatModel, QWidget * parent = nullptr);
|
||||
|
||||
// Define how the widget will behave in a layout
|
||||
QSizePolicy sizePolicy() const;
|
||||
|
||||
QSize minimumSizeHint() const override;
|
||||
|
||||
QSize sizeHint() const override;
|
||||
|
||||
QBrush const & getBackgroundBrush() const;
|
||||
void setBackgroundBrush(QBrush const & backgroundBrush);
|
||||
|
||||
QBrush const & getBarBrush() const;
|
||||
void setBarBrush(QBrush const & barBrush);
|
||||
|
||||
QColor const & getTextColor() const;
|
||||
void setTextColor(QColor const & textColor);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
private:
|
||||
QString const m_text;
|
||||
|
||||
QBrush m_backgroundBrush;
|
||||
QBrush m_barBrush;
|
||||
QColor m_textColor;
|
||||
};
|
||||
|
||||
} // namespace lmms::gui
|
||||
|
||||
#endif // LMMS_GUI_BAR_MODEL_EDITOR_H
|
||||
121
include/FloatModelEditorBase.h
Normal file
121
include/FloatModelEditorBase.h
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* FloatModelEditorBase.h - Base editor for float models
|
||||
*
|
||||
* Copyright (c) 2004-2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2023 Michael Gregorius
|
||||
*
|
||||
* 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 LMMS_GUI_FLOAT_MODEL_EDITOR_BASE_H
|
||||
#define LMMS_GUI_FLOAT_MODEL_EDITOR_BASE_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QPoint>
|
||||
|
||||
#include "AutomatableModelView.h"
|
||||
|
||||
|
||||
namespace lmms::gui
|
||||
{
|
||||
|
||||
class SimpleTextFloat;
|
||||
|
||||
class LMMS_EXPORT FloatModelEditorBase : public QWidget, public FloatModelView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
mapPropertyFromModel(bool, isVolumeKnob, setVolumeKnob, m_volumeKnob);
|
||||
mapPropertyFromModel(float, volumeRatio, setVolumeRatio, m_volumeRatio);
|
||||
|
||||
void initUi(const QString & name); //!< to be called by ctors
|
||||
|
||||
public:
|
||||
enum class DirectionOfManipulation
|
||||
{
|
||||
Vertical,
|
||||
Horizontal
|
||||
};
|
||||
|
||||
FloatModelEditorBase(DirectionOfManipulation directionOfManipulation = DirectionOfManipulation::Vertical, QWidget * _parent = nullptr, const QString & _name = QString()); //!< default ctor
|
||||
FloatModelEditorBase(const FloatModelEditorBase& other) = delete;
|
||||
|
||||
// TODO: remove
|
||||
inline void setHintText(const QString & txt_before, const QString & txt_after)
|
||||
{
|
||||
setDescription(txt_before);
|
||||
setUnit(txt_after);
|
||||
}
|
||||
|
||||
signals:
|
||||
void sliderPressed();
|
||||
void sliderReleased();
|
||||
void sliderMoved(float value);
|
||||
|
||||
|
||||
protected:
|
||||
void contextMenuEvent(QContextMenuEvent * me) override;
|
||||
void dragEnterEvent(QDragEnterEvent * dee) override;
|
||||
void dropEvent(QDropEvent * de) override;
|
||||
void focusOutEvent(QFocusEvent * fe) override;
|
||||
void mousePressEvent(QMouseEvent * me) override;
|
||||
void mouseReleaseEvent(QMouseEvent * me) override;
|
||||
void mouseMoveEvent(QMouseEvent * me) override;
|
||||
void mouseDoubleClickEvent(QMouseEvent * me) override;
|
||||
void paintEvent(QPaintEvent * me) override;
|
||||
void wheelEvent(QWheelEvent * me) override;
|
||||
|
||||
void enterEvent(QEvent *event) override;
|
||||
void leaveEvent(QEvent *event) override;
|
||||
|
||||
virtual float getValue(const QPoint & p);
|
||||
|
||||
private slots:
|
||||
virtual void enterValue();
|
||||
void friendlyUpdate();
|
||||
void toggleScale();
|
||||
|
||||
private:
|
||||
virtual QString displayValue() const;
|
||||
|
||||
void doConnections() override;
|
||||
|
||||
void showTextFloat(int msecBeforeDisplay, int msecDisplayTime);
|
||||
void setPosition(const QPoint & p);
|
||||
|
||||
inline float pageSize() const
|
||||
{
|
||||
return (model()->maxValue() - model()->minValue()) / 100.0f;
|
||||
}
|
||||
|
||||
static SimpleTextFloat * s_textFloat;
|
||||
|
||||
BoolModel m_volumeKnob;
|
||||
FloatModel m_volumeRatio;
|
||||
|
||||
QPoint m_lastMousePos; //!< mouse position in last mouseMoveEvent
|
||||
float m_leftOver;
|
||||
bool m_buttonPressed;
|
||||
|
||||
DirectionOfManipulation m_directionOfManipulation;
|
||||
};
|
||||
|
||||
} // namespace lmms::gui
|
||||
|
||||
#endif // LMMS_GUI_FLOAT_MODEL_EDITOR_BASE_H
|
||||
@@ -26,12 +26,9 @@
|
||||
#define LMMS_GUI_KNOB_H
|
||||
|
||||
#include <memory>
|
||||
#include <QPixmap>
|
||||
#include <QWidget>
|
||||
#include <QPoint>
|
||||
#include <QTextDocument>
|
||||
|
||||
#include "AutomatableModelView.h"
|
||||
#include "FloatModelEditorBase.h"
|
||||
|
||||
|
||||
class QPixmap;
|
||||
@@ -50,7 +47,7 @@ enum class KnobType
|
||||
|
||||
void convertPixmapToGrayScale(QPixmap &pixMap);
|
||||
|
||||
class LMMS_EXPORT Knob : public QWidget, public FloatModelView
|
||||
class LMMS_EXPORT Knob : public FloatModelEditorBase
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_ENUMS( KnobType )
|
||||
@@ -72,9 +69,6 @@ class LMMS_EXPORT Knob : public QWidget, public FloatModelView
|
||||
Q_PROPERTY(QColor arcActiveColor MEMBER m_arcActiveColor)
|
||||
Q_PROPERTY(QColor arcInactiveColor MEMBER m_arcInactiveColor)
|
||||
|
||||
mapPropertyFromModel(bool,isVolumeKnob,setVolumeKnob,m_volumeKnob);
|
||||
mapPropertyFromModel(float,volumeRatio,setVolumeRatio,m_volumeRatio);
|
||||
|
||||
Q_PROPERTY(KnobType knobNum READ knobNum WRITE setknobNum)
|
||||
|
||||
Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor)
|
||||
@@ -87,13 +81,6 @@ public:
|
||||
Knob( QWidget * _parent = nullptr, const QString & _name = QString() ); //!< default ctor
|
||||
Knob( const Knob& other ) = delete;
|
||||
|
||||
// TODO: remove
|
||||
inline void setHintText( const QString & _txt_before,
|
||||
const QString & _txt_after )
|
||||
{
|
||||
setDescription( _txt_before );
|
||||
setUnit( _txt_after );
|
||||
}
|
||||
void setLabel( const QString & txt );
|
||||
void setHtmlLabel( const QString &htmltxt );
|
||||
|
||||
@@ -125,46 +112,16 @@ public:
|
||||
void setTextColor( const QColor & c );
|
||||
|
||||
|
||||
signals:
|
||||
void sliderPressed();
|
||||
void sliderReleased();
|
||||
void sliderMoved( float value );
|
||||
|
||||
|
||||
protected:
|
||||
void contextMenuEvent( QContextMenuEvent * _me ) override;
|
||||
void dragEnterEvent( QDragEnterEvent * _dee ) override;
|
||||
void dropEvent( QDropEvent * _de ) override;
|
||||
void focusOutEvent( QFocusEvent * _fe ) override;
|
||||
void mousePressEvent( QMouseEvent * _me ) override;
|
||||
void mouseReleaseEvent( QMouseEvent * _me ) override;
|
||||
void mouseMoveEvent( QMouseEvent * _me ) override;
|
||||
void mouseDoubleClickEvent( QMouseEvent * _me ) override;
|
||||
void paintEvent( QPaintEvent * _me ) override;
|
||||
void wheelEvent( QWheelEvent * _me ) override;
|
||||
|
||||
void changeEvent(QEvent * ev) override;
|
||||
|
||||
void enterEvent(QEvent *event) override;
|
||||
void leaveEvent(QEvent *event) override;
|
||||
|
||||
virtual float getValue( const QPoint & _p );
|
||||
|
||||
private slots:
|
||||
virtual void enterValue();
|
||||
void friendlyUpdate();
|
||||
void toggleScale();
|
||||
|
||||
private:
|
||||
virtual QString displayValue() const;
|
||||
|
||||
void doConnections() override;
|
||||
|
||||
QLineF calculateLine( const QPointF & _mid, float _radius,
|
||||
float _innerRadius = 1) const;
|
||||
|
||||
void drawKnob( QPainter * _p );
|
||||
void showTextFloat(int msecBeforeDisplay, int msecDisplayTime);
|
||||
void setPosition( const QPoint & _p );
|
||||
bool updateAngle();
|
||||
|
||||
int angleFromValue( float value, float minValue, float maxValue, float totalAngle ) const
|
||||
@@ -172,25 +129,11 @@ private:
|
||||
return static_cast<int>( ( value - 0.5 * ( minValue + maxValue ) ) / ( maxValue - minValue ) * m_totalAngle ) % 360;
|
||||
}
|
||||
|
||||
inline float pageSize() const
|
||||
{
|
||||
return ( model()->maxValue() - model()->minValue() ) / 100.0f;
|
||||
}
|
||||
|
||||
|
||||
static SimpleTextFloat * s_textFloat;
|
||||
|
||||
QString m_label;
|
||||
bool m_isHtmlLabel;
|
||||
QTextDocument* m_tdRenderer;
|
||||
|
||||
std::unique_ptr<QPixmap> m_knobPixmap;
|
||||
BoolModel m_volumeKnob;
|
||||
FloatModel m_volumeRatio;
|
||||
|
||||
QPoint m_lastMousePos; //!< mouse position in last mouseMoveEvent
|
||||
float m_leftOver;
|
||||
bool m_buttonPressed;
|
||||
|
||||
float m_totalAngle;
|
||||
int m_angle;
|
||||
@@ -211,9 +154,7 @@ private:
|
||||
QColor m_textColor;
|
||||
|
||||
KnobType m_knobNum;
|
||||
|
||||
} ;
|
||||
|
||||
};
|
||||
|
||||
} // namespace lmms::gui
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ namespace gui
|
||||
{
|
||||
|
||||
class LadspaControlView;
|
||||
class LadspaMatrixControlDialog;
|
||||
|
||||
} // namespace gui
|
||||
|
||||
@@ -125,6 +126,7 @@ private:
|
||||
|
||||
|
||||
friend class gui::LadspaControlView;
|
||||
friend class gui::LadspaMatrixControlDialog;
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
@@ -47,10 +47,12 @@ public:
|
||||
|
||||
LedCheckBox( const QString & _txt, QWidget * _parent,
|
||||
const QString & _name = QString(),
|
||||
LedColor _color = LedColor::Yellow );
|
||||
LedColor _color = LedColor::Yellow,
|
||||
bool legacyMode = true);
|
||||
LedCheckBox( QWidget * _parent,
|
||||
const QString & _name = QString(),
|
||||
LedColor _color = LedColor::Yellow );
|
||||
LedColor _color = LedColor::Yellow,
|
||||
bool legacyMode = true);
|
||||
|
||||
~LedCheckBox() override;
|
||||
|
||||
@@ -74,8 +76,13 @@ private:
|
||||
|
||||
QString m_text;
|
||||
|
||||
bool m_legacyMode;
|
||||
|
||||
void initUi( LedColor _color ); //!< to be called by ctors
|
||||
|
||||
void onTextUpdated(); //!< to be called when you updated @a m_text
|
||||
void paintLegacy(QPaintEvent * p);
|
||||
void paintNonLegacy(QPaintEvent * p);
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
90
include/TempoSyncBarModelEditor.h
Normal file
90
include/TempoSyncBarModelEditor.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* TempoSyncBarModelEditor.h - adds bpm to ms conversion for the bar editor class
|
||||
*
|
||||
* Copyright (c) 2005-2008 Danny McRae <khjklujn/at/yahoo.com>
|
||||
* Copyright (c) 2009-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2023 Michael Gregorius
|
||||
*
|
||||
* 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 LMMS_GUI_TEMPO_SYNC_BAR_MODEL_EDITOR_H
|
||||
#define LMMS_GUI_TEMPO_SYNC_BAR_MODEL_EDITOR_H
|
||||
|
||||
#include <QPixmap>
|
||||
#include <QPointer>
|
||||
|
||||
#include "BarModelEditor.h"
|
||||
#include "TempoSyncKnobModel.h"
|
||||
|
||||
namespace lmms::gui
|
||||
{
|
||||
|
||||
class MeterDialog;
|
||||
|
||||
class LMMS_EXPORT TempoSyncBarModelEditor : public BarModelEditor
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TempoSyncBarModelEditor(QString text, FloatModel * floatModel, QWidget * parent = nullptr);
|
||||
~TempoSyncBarModelEditor() override;
|
||||
|
||||
const QString & syncDescription();
|
||||
void setSyncDescription(const QString & new_description);
|
||||
|
||||
const QPixmap & syncIcon();
|
||||
void setSyncIcon(const QPixmap & new_pix);
|
||||
|
||||
TempoSyncKnobModel * model()
|
||||
{
|
||||
return castModel<TempoSyncKnobModel>();
|
||||
}
|
||||
|
||||
void modelChanged() override;
|
||||
|
||||
|
||||
signals:
|
||||
void syncDescriptionChanged(const QString & new_description);
|
||||
void syncIconChanged();
|
||||
|
||||
|
||||
protected:
|
||||
void contextMenuEvent(QContextMenuEvent * me) override;
|
||||
|
||||
|
||||
protected slots:
|
||||
void updateDescAndIcon();
|
||||
void showCustom();
|
||||
|
||||
|
||||
private:
|
||||
void updateTextDescription();
|
||||
void updateIcon();
|
||||
|
||||
|
||||
private:
|
||||
QPixmap m_tempoSyncIcon;
|
||||
QString m_tempoSyncDescription;
|
||||
|
||||
QPointer<MeterDialog> m_custom;
|
||||
};
|
||||
|
||||
} // namespace lmms::gui
|
||||
|
||||
#endif // LMMS_GUI_TEMPO_SYNC_BAR_MODEL_EDITOR_H
|
||||
@@ -82,6 +82,9 @@ public:
|
||||
|
||||
void setScale( float _new_scale );
|
||||
|
||||
MeterModel & getCustomMeterModel() { return m_custom; }
|
||||
MeterModel const & getCustomMeterModel() const { return m_custom; }
|
||||
|
||||
signals:
|
||||
void syncModeChanged( lmms::TempoSyncKnobModel::SyncMode _new_mode );
|
||||
void scaleChanged( float _new_scale );
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
INCLUDE(BuildPlugin)
|
||||
|
||||
BUILD_PLUGIN(ladspaeffect LadspaEffect.cpp LadspaControls.cpp LadspaControlDialog.cpp LadspaSubPluginFeatures.cpp LadspaEffect.h LadspaControls.h LadspaControlDialog.h LadspaSubPluginFeatures.h MOCFILES LadspaEffect.h LadspaControls.h LadspaControlDialog.h EMBEDDED_RESOURCES logo.png)
|
||||
BUILD_PLUGIN(ladspaeffect LadspaEffect.cpp LadspaControls.cpp LadspaControlDialog.cpp LadspaMatrixControlDialog.cpp LadspaSubPluginFeatures.cpp LadspaWidgetFactory.cpp LadspaEffect.h LadspaControls.h LadspaControlDialog.h LadspaMatrixControlDialog.h LadspaSubPluginFeatures.h LadspaWidgetFactory.h MOCFILES LadspaEffect.h LadspaControls.h LadspaControlDialog.h LadspaMatrixControlDialog.h EMBEDDED_RESOURCES logo.png)
|
||||
|
||||
SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/ladspa")
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "EffectControls.h"
|
||||
#include "LadspaControlDialog.h"
|
||||
#include "LadspaMatrixControlDialog.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
@@ -59,7 +60,7 @@ public:
|
||||
|
||||
gui::EffectControlDialog* createView() override
|
||||
{
|
||||
return new gui::LadspaControlDialog( this );
|
||||
return new gui::LadspaMatrixControlDialog( this );
|
||||
}
|
||||
|
||||
|
||||
@@ -79,6 +80,7 @@ private:
|
||||
|
||||
|
||||
friend class gui::LadspaControlDialog;
|
||||
friend class gui::LadspaMatrixControlDialog;
|
||||
friend class LadspaEffect;
|
||||
|
||||
|
||||
|
||||
243
plugins/LadspaEffect/LadspaMatrixControlDialog.cpp
Normal file
243
plugins/LadspaEffect/LadspaMatrixControlDialog.cpp
Normal file
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* LadspaMatrixControlDialog.h - Dialog for displaying and editing control port
|
||||
* values for LADSPA plugins in a matrix display
|
||||
*
|
||||
* Copyright (c) 2015 Michael Gregorius <michaelgregorius/at/web[dot]de>
|
||||
*
|
||||
* This file is part of LMMS - http://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 <QGroupBox>
|
||||
#include <QLayout>
|
||||
#include <QLabel>
|
||||
#include <QScrollArea>
|
||||
#include <QScrollBar>
|
||||
#include <QSpacerItem>
|
||||
#include <QMdiArea>
|
||||
|
||||
|
||||
#include "LadspaBase.h"
|
||||
#include "LadspaControl.h"
|
||||
#include "LadspaEffect.h"
|
||||
#include "LadspaMatrixControlDialog.h"
|
||||
#include "LadspaWidgetFactory.h"
|
||||
#include "LadspaControlView.h"
|
||||
#include "LedCheckBox.h"
|
||||
|
||||
#include "GuiApplication.h"
|
||||
#include "MainWindow.h"
|
||||
|
||||
|
||||
namespace lmms::gui
|
||||
{
|
||||
|
||||
LadspaMatrixControlDialog::LadspaMatrixControlDialog(LadspaControls * ladspaControls) :
|
||||
EffectControlDialog(ladspaControls),
|
||||
m_scrollArea(nullptr),
|
||||
m_stereoLink(nullptr)
|
||||
{
|
||||
QVBoxLayout * mainLayout = new QVBoxLayout(this);
|
||||
|
||||
m_scrollArea = new QScrollArea(this);
|
||||
m_scrollArea->setWidgetResizable(true);
|
||||
m_scrollArea->setFrameShape(QFrame::NoFrame);
|
||||
// Set to always on so that the elements do not move around when the
|
||||
// scroll bar is hidden or shown.
|
||||
m_scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
|
||||
|
||||
// Add a scroll area that grows
|
||||
mainLayout->addWidget(m_scrollArea, 1);
|
||||
|
||||
// Populate the parameter matrix and put it into the scroll area
|
||||
updateEffectView(ladspaControls);
|
||||
|
||||
// Add button to link all channels if there's more than one channel
|
||||
if (getChannelCount() > 1)
|
||||
{
|
||||
mainLayout->addSpacing(3);
|
||||
|
||||
m_stereoLink = new LedCheckBox(tr("Link Channels"), this, QString(), LedCheckBox::LedColor::Green, false);
|
||||
m_stereoLink->setModel(&ladspaControls->m_stereoLinkModel);
|
||||
mainLayout->addWidget(m_stereoLink, 0, Qt::AlignCenter);
|
||||
}
|
||||
}
|
||||
|
||||
bool LadspaMatrixControlDialog::isResizable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LadspaMatrixControlDialog::needsLinkColumn() const
|
||||
{
|
||||
LadspaControls * ladspaControls = getLadspaControls();
|
||||
|
||||
ch_cnt_t const channelCount = getChannelCount();
|
||||
for (ch_cnt_t i = 0; i < channelCount; ++i)
|
||||
{
|
||||
// Create a const reference so that the C++11 based for loop does not detach the Qt container
|
||||
auto const & currentControls = ladspaControls->m_controls[i];
|
||||
for (auto ladspaControl : currentControls)
|
||||
{
|
||||
if (ladspaControl->m_link)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void LadspaMatrixControlDialog::arrangeControls(QWidget * parent, QGridLayout* gridLayout)
|
||||
{
|
||||
LadspaControls * ladspaControls = getLadspaControls();
|
||||
|
||||
int const headerRow = 0;
|
||||
int const linkColumn = 0;
|
||||
|
||||
bool const linkColumnNeeded = needsLinkColumn();
|
||||
if (linkColumnNeeded)
|
||||
{
|
||||
gridLayout->addWidget(new QLabel("<b>" + tr("Link") + "</b>", parent), headerRow, linkColumn, Qt::AlignHCenter);
|
||||
|
||||
// If there's a link column then it should not stretch
|
||||
gridLayout->setColumnStretch(linkColumn, 0);
|
||||
}
|
||||
|
||||
int const channelStartColumn = linkColumnNeeded ? 1 : 0;
|
||||
|
||||
// The header row should not grow vertically
|
||||
gridLayout->setRowStretch(0, 0);
|
||||
|
||||
// Records the maximum row with parameters so that we can add a vertical spacer after that row
|
||||
int maxRow = 0;
|
||||
|
||||
// Iterate the channels and add widgets for each control
|
||||
// Note: the code assumes that all channels have the same structure, i.e. that all channels
|
||||
// have the same number of parameters which are in the same order.
|
||||
ch_cnt_t const numberOfChannels = getChannelCount();
|
||||
for (ch_cnt_t i = 0; i < numberOfChannels; ++i)
|
||||
{
|
||||
int currentChannelColumn = channelStartColumn + i;
|
||||
gridLayout->setColumnStretch(currentChannelColumn, 1);
|
||||
|
||||
// First add the channel header with the channel number
|
||||
gridLayout->addWidget(new QLabel("<b>" + tr("Channel %1").arg(QString::number(i + 1)) + "</b>", parent), headerRow, currentChannelColumn, Qt::AlignHCenter);
|
||||
|
||||
int currentRow = 1;
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
// Configure the current parameter row to not stretch.
|
||||
// Only do this once, i.e. when working with the first channel.
|
||||
gridLayout->setRowStretch(currentRow, 0);
|
||||
}
|
||||
|
||||
// Create a const reference so that the C++11 based for loop does not detach the Qt container
|
||||
auto const & currentControls = ladspaControls->m_controls[i];
|
||||
for (auto ladspaControl : currentControls)
|
||||
{
|
||||
// Only use the first channel to determine if we need to add link controls
|
||||
if (i == 0 && ladspaControl->m_link)
|
||||
{
|
||||
LedCheckBox * linkCheckBox = new LedCheckBox("", parent, "", LedCheckBox::LedColor::Green);
|
||||
linkCheckBox->setModel(&ladspaControl->m_linkEnabledModel);
|
||||
linkCheckBox->setToolTip(tr("Link channels"));
|
||||
gridLayout->addWidget(linkCheckBox, currentRow, linkColumn, Qt::AlignHCenter);
|
||||
}
|
||||
|
||||
QWidget * controlWidget = LadspaWidgetFactory::createWidget(ladspaControl, this);
|
||||
if (controlWidget)
|
||||
{
|
||||
gridLayout->addWidget(controlWidget, currentRow, currentChannelColumn);
|
||||
}
|
||||
|
||||
// Record the maximum row so that we add a vertical spacer after that row
|
||||
maxRow = std::max(maxRow, currentRow);
|
||||
|
||||
++currentRow;
|
||||
}
|
||||
}
|
||||
|
||||
// Add a spacer item after the maximum row
|
||||
QSpacerItem * spacer = new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
|
||||
gridLayout->addItem(spacer, maxRow + 1, 0);
|
||||
}
|
||||
|
||||
QWidget * LadspaMatrixControlDialog::createMatrixWidget()
|
||||
{
|
||||
QWidget *widget = new QWidget(this);
|
||||
QGridLayout *gridLayout = new QGridLayout(widget);
|
||||
gridLayout->setMargin(0);
|
||||
widget->setLayout(gridLayout);
|
||||
|
||||
arrangeControls(widget, gridLayout);
|
||||
|
||||
return widget;
|
||||
}
|
||||
|
||||
void LadspaMatrixControlDialog::updateEffectView(LadspaControls * ladspaControls)
|
||||
{
|
||||
m_effectControls = ladspaControls;
|
||||
|
||||
// No need to delete the existing widget as it's deleted
|
||||
// by the scroll view when we replace it.
|
||||
QWidget * matrixWidget = createMatrixWidget();
|
||||
m_scrollArea->setWidget(matrixWidget);
|
||||
|
||||
// Make sure that the horizontal scroll bar does not show
|
||||
// From: https://forum.qt.io/topic/13374/solved-qscrollarea-vertical-scroll-only/4
|
||||
m_scrollArea->setMinimumWidth(matrixWidget->minimumSizeHint().width() + m_scrollArea->verticalScrollBar()->width());
|
||||
|
||||
|
||||
// Make sure that the widget is shown without a scrollbar whenever possible
|
||||
// If the widget fits on the workspace we use the height of the widget as the minimum size of the scroll area.
|
||||
// This will ensure that the scrollbar is not shown initially (and never will be).
|
||||
// If the widget is larger than the workspace then we want it to mostly cover the workspace.
|
||||
//
|
||||
// This is somewhat ugly but I have no idea how to control the initial size of the scroll area otherwise
|
||||
auto const workspaceSize = getGUI()->mainWindow()->workspace()->viewport()->size();
|
||||
// Make sure that we always account a minumum height for the workspace, i.e. that we never compute
|
||||
// something close to 0 if the LMMS window is very small
|
||||
int workspaceHeight = qMax(200, static_cast<int>(workspaceSize.height() * 0.9));
|
||||
int minOfWidgetAndWorkspace = qMin(matrixWidget->minimumSizeHint().height(), workspaceHeight);
|
||||
m_scrollArea->setMinimumHeight(minOfWidgetAndWorkspace);
|
||||
|
||||
if (getChannelCount() > 1 && m_stereoLink != nullptr)
|
||||
{
|
||||
m_stereoLink->setModel(&ladspaControls->m_stereoLinkModel);
|
||||
}
|
||||
|
||||
connect(ladspaControls, &LadspaControls::effectModelChanged,
|
||||
this, &LadspaMatrixControlDialog::updateEffectView,
|
||||
Qt::DirectConnection);
|
||||
}
|
||||
|
||||
LadspaControls * LadspaMatrixControlDialog::getLadspaControls() const
|
||||
{
|
||||
return dynamic_cast<LadspaControls *>(m_effectControls);
|
||||
}
|
||||
|
||||
ch_cnt_t LadspaMatrixControlDialog::getChannelCount() const
|
||||
{
|
||||
return getLadspaControls()->m_processors;
|
||||
}
|
||||
|
||||
} // namespace lmms::gui
|
||||
91
plugins/LadspaEffect/LadspaMatrixControlDialog.h
Normal file
91
plugins/LadspaEffect/LadspaMatrixControlDialog.h
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* LadspaMatrixControlDialog.h - Dialog for displaying and editing control port
|
||||
* values for LADSPA plugins in a matrix display
|
||||
*
|
||||
* Copyright (c) 2015 Michael Gregorius <michaelgregorius/at/web[dot]de>
|
||||
*
|
||||
* This file is part of LMMS - http://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 LADSPA_MATRIX_CONTROL_DIALOG_H
|
||||
#define LADSPA_MATRIX_CONTROL_DIALOG_H
|
||||
|
||||
#include "EffectControlDialog.h"
|
||||
|
||||
#include "lmms_basics.h"
|
||||
|
||||
|
||||
class QGridLayout;
|
||||
class QScrollArea;
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
class LadspaControls;
|
||||
|
||||
namespace gui
|
||||
{
|
||||
|
||||
class LedCheckBox;
|
||||
|
||||
|
||||
class LadspaMatrixControlDialog : public EffectControlDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
LadspaMatrixControlDialog(LadspaControls* ctl);
|
||||
bool isResizable() const override;
|
||||
|
||||
private slots:
|
||||
void updateEffectView(LadspaControls* ctl);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Checks if a link column is needed for the current effect controls.
|
||||
* @return true if a link column is needed.
|
||||
*/
|
||||
bool needsLinkColumn() const;
|
||||
|
||||
/**
|
||||
* @brief Arranges widgets for the current controls in a grid/matrix layout.
|
||||
* @param parent The parent of all created widgets
|
||||
* @param gridLayout The layout into which the controls are organized
|
||||
*/
|
||||
void arrangeControls(QWidget * parent, QGridLayout* gridLayout);
|
||||
|
||||
/**
|
||||
* @brief Creates a widget that holds the widgets of the current controls in a matrix arrangement.
|
||||
* @param ladspaControls
|
||||
* @return
|
||||
*/
|
||||
QWidget * createMatrixWidget();
|
||||
|
||||
LadspaControls * getLadspaControls() const;
|
||||
ch_cnt_t getChannelCount() const;
|
||||
|
||||
private:
|
||||
QScrollArea* m_scrollArea;
|
||||
LedCheckBox* m_stereoLink;
|
||||
};
|
||||
|
||||
} // namespace gui
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif
|
||||
83
plugins/LadspaEffect/LadspaWidgetFactory.cpp
Normal file
83
plugins/LadspaEffect/LadspaWidgetFactory.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* LadspaWidgetFactory.cpp - Factory that creates widgets for LADSPA ports
|
||||
*
|
||||
* Copyright (c) 2006-2008 Danny McRae <khjklujn/at/users.sourceforge.net>
|
||||
* Copyright (c) 2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2015-2023 Michael Gregorius
|
||||
*
|
||||
* 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 "LadspaWidgetFactory.h"
|
||||
|
||||
#include "LadspaControl.h"
|
||||
#include "LadspaBase.h"
|
||||
|
||||
#include "BarModelEditor.h"
|
||||
#include "LedCheckBox.h"
|
||||
#include "TempoSyncBarModelEditor.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
|
||||
|
||||
namespace lmms::gui
|
||||
{
|
||||
|
||||
QWidget * LadspaWidgetFactory::createWidget(LadspaControl * ladspaControl, QWidget * parent)
|
||||
{
|
||||
auto const * port = ladspaControl->port();
|
||||
|
||||
QString const name = port->name;
|
||||
|
||||
switch (port->data_type)
|
||||
{
|
||||
case BufferDataType::Toggled:
|
||||
{
|
||||
// The actual check box is put into a widget because LedCheckBox does not play nice with layouts.
|
||||
// Putting it directly into a grid layout disables the resizing behavior of all columns where it
|
||||
// appears. Hence we put it into the layout of a widget that knows how to play nice with layouts.
|
||||
QWidget * widgetWithLayout = new QWidget(parent);
|
||||
QHBoxLayout * layout = new QHBoxLayout(widgetWithLayout);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
LedCheckBox * toggle = new LedCheckBox(
|
||||
name, parent, QString(), LedCheckBox::LedColor::Green, false);
|
||||
toggle->setModel(ladspaControl->toggledModel());
|
||||
layout->addWidget(toggle, 0, Qt::AlignLeft);
|
||||
|
||||
return widgetWithLayout;
|
||||
}
|
||||
|
||||
case BufferDataType::Integer:
|
||||
case BufferDataType::Enum:
|
||||
case BufferDataType::Floating:
|
||||
return new BarModelEditor(name, ladspaControl->knobModel(), parent);
|
||||
|
||||
case BufferDataType::Time:
|
||||
return new TempoSyncBarModelEditor(name, ladspaControl->tempoSyncKnobModel(), parent);
|
||||
|
||||
default:
|
||||
return new QLabel(QObject::tr("%1 (unsupported)").arg(name), parent);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace lmms::gui
|
||||
49
plugins/LadspaEffect/LadspaWidgetFactory.h
Normal file
49
plugins/LadspaEffect/LadspaWidgetFactory.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* LadspaWidgetFactory.h - Factory that creates widgets for LADSPA ports
|
||||
*
|
||||
* Copyright (c) 2023 Michael Gregorius
|
||||
*
|
||||
* 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 LMMS_GUI_LADSPA_WIDGET_FACTORY_H
|
||||
#define LMMS_GUI_LADSPA_WIDGET_FACTORY_H
|
||||
|
||||
|
||||
class QWidget;
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
class LadspaControl;
|
||||
|
||||
namespace gui
|
||||
{
|
||||
|
||||
class LadspaWidgetFactory
|
||||
{
|
||||
public:
|
||||
static QWidget * createWidget(LadspaControl * ladspaControl, QWidget * parent);
|
||||
};
|
||||
|
||||
} // namespace gui
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif // LMMS_GUI_LADSPA_WIDGET_FACTORY_H
|
||||
@@ -94,11 +94,13 @@ SET(LMMS_SRCS
|
||||
|
||||
gui/widgets/AutomatableButton.cpp
|
||||
gui/widgets/AutomatableSlider.cpp
|
||||
gui/widgets/BarModelEditor.cpp
|
||||
gui/widgets/CPULoadWidget.cpp
|
||||
gui/widgets/CaptionMenu.cpp
|
||||
gui/widgets/ComboBox.cpp
|
||||
gui/widgets/CustomTextKnob.cpp
|
||||
gui/widgets/Fader.cpp
|
||||
gui/widgets/FloatModelEditorBase.cpp
|
||||
gui/widgets/Graph.cpp
|
||||
gui/widgets/GroupBox.cpp
|
||||
gui/widgets/Knob.cpp
|
||||
@@ -115,6 +117,7 @@ SET(LMMS_SRCS
|
||||
gui/widgets/SimpleTextFloat.cpp
|
||||
gui/widgets/TabBar.cpp
|
||||
gui/widgets/TabWidget.cpp
|
||||
gui/widgets/TempoSyncBarModelEditor.cpp
|
||||
gui/widgets/TempoSyncKnob.cpp
|
||||
gui/widgets/TextFloat.cpp
|
||||
gui/widgets/TimeDisplayWidget.cpp
|
||||
|
||||
117
src/gui/widgets/BarModelEditor.cpp
Normal file
117
src/gui/widgets/BarModelEditor.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
#include <BarModelEditor.h>
|
||||
|
||||
#include <QPainter>
|
||||
#include <QStyle>
|
||||
|
||||
|
||||
namespace lmms::gui
|
||||
{
|
||||
|
||||
BarModelEditor::BarModelEditor(QString text, FloatModel * floatModel, QWidget * parent) :
|
||||
FloatModelEditorBase(DirectionOfManipulation::Horizontal, parent),
|
||||
m_text(text),
|
||||
m_backgroundBrush(palette().base()),
|
||||
m_barBrush(palette().button()),
|
||||
m_textColor(palette().text().color())
|
||||
{
|
||||
setModel(floatModel);
|
||||
}
|
||||
|
||||
QSizePolicy BarModelEditor::sizePolicy() const
|
||||
{
|
||||
return QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
||||
}
|
||||
|
||||
QSize BarModelEditor::minimumSizeHint() const
|
||||
{
|
||||
auto const fm = fontMetrics();
|
||||
return QSize(50, fm.height() + 6);
|
||||
}
|
||||
|
||||
QSize BarModelEditor::sizeHint() const
|
||||
{
|
||||
return minimumSizeHint();
|
||||
}
|
||||
|
||||
QBrush const & BarModelEditor::getBackgroundBrush() const
|
||||
{
|
||||
return m_backgroundBrush;
|
||||
}
|
||||
|
||||
void BarModelEditor::setBackgroundBrush(QBrush const & backgroundBrush)
|
||||
{
|
||||
m_backgroundBrush = backgroundBrush;
|
||||
}
|
||||
|
||||
QBrush const & BarModelEditor::getBarBrush() const
|
||||
{
|
||||
return m_barBrush;
|
||||
}
|
||||
|
||||
void BarModelEditor::setBarBrush(QBrush const & barBrush)
|
||||
{
|
||||
m_barBrush = barBrush;
|
||||
}
|
||||
|
||||
QColor const & BarModelEditor::getTextColor() const
|
||||
{
|
||||
return m_textColor;
|
||||
}
|
||||
|
||||
void BarModelEditor::setTextColor(QColor const & textColor)
|
||||
{
|
||||
m_textColor = textColor;
|
||||
}
|
||||
|
||||
void BarModelEditor::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
QWidget::paintEvent(event);
|
||||
|
||||
auto const * mod = model();
|
||||
auto const minValue = mod->minValue();
|
||||
auto const maxValue = mod->maxValue();
|
||||
auto const range = maxValue - minValue;
|
||||
|
||||
QRect const r = rect();
|
||||
|
||||
QPainter painter(this);
|
||||
|
||||
// Paint the base rectangle into which the bar and the text go
|
||||
QBrush const & backgroundBrush = getBackgroundBrush();
|
||||
painter.setPen(backgroundBrush.color());
|
||||
painter.setBrush(backgroundBrush);
|
||||
painter.drawRect(r);
|
||||
|
||||
|
||||
// Paint the bar
|
||||
// Compute the percentage as:
|
||||
// min + x * (max - min) = v <=> x = (v - min) / (max - min)
|
||||
auto const percentage = range == 0 ? 1. : (mod->value() - minValue) / range;
|
||||
|
||||
int const margin = 3;
|
||||
QMargins const margins(margin, margin, margin, margin);
|
||||
QRect const valueRect = r.marginsRemoved(margins);
|
||||
|
||||
QBrush const & barBrush = getBarBrush();
|
||||
painter.setPen(barBrush.color());
|
||||
painter.setBrush(barBrush);
|
||||
QPoint const startPoint = valueRect.topLeft();
|
||||
QPoint endPoint = valueRect.bottomRight();
|
||||
endPoint.setX(startPoint.x() + percentage * (endPoint.x() - startPoint.x()));
|
||||
|
||||
painter.drawRect(QRect(startPoint, endPoint));
|
||||
|
||||
|
||||
// Draw the text into the value rectangle but move it slightly to the right
|
||||
QRect const textRect = valueRect.marginsRemoved(QMargins(3, 0, 0, 0));
|
||||
|
||||
// Elide the text if needed
|
||||
auto const fm = fontMetrics();
|
||||
QString const elidedText = fm.elidedText(m_text, Qt::ElideRight, textRect.width());
|
||||
|
||||
// Now draw the text
|
||||
painter.setPen(getTextColor());
|
||||
painter.drawText(textRect, elidedText);
|
||||
}
|
||||
|
||||
} // namespace lmms::gui
|
||||
464
src/gui/widgets/FloatModelEditorBase.cpp
Normal file
464
src/gui/widgets/FloatModelEditorBase.cpp
Normal file
@@ -0,0 +1,464 @@
|
||||
/*
|
||||
* FloatModelEditorBase.cpp - Base editor for float models
|
||||
*
|
||||
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2023 Michael Gregorius
|
||||
*
|
||||
* 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 "FloatModelEditorBase.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QInputDialog>
|
||||
#include <QPainter>
|
||||
|
||||
#ifndef __USE_XOPEN
|
||||
#define __USE_XOPEN
|
||||
#endif
|
||||
|
||||
#include "lmms_math.h"
|
||||
#include "CaptionMenu.h"
|
||||
#include "ControllerConnection.h"
|
||||
#include "GuiApplication.h"
|
||||
#include "LocaleHelper.h"
|
||||
#include "MainWindow.h"
|
||||
#include "ProjectJournal.h"
|
||||
#include "SimpleTextFloat.h"
|
||||
#include "StringPairDrag.h"
|
||||
|
||||
|
||||
namespace lmms::gui
|
||||
{
|
||||
|
||||
SimpleTextFloat * FloatModelEditorBase::s_textFloat = nullptr;
|
||||
|
||||
FloatModelEditorBase::FloatModelEditorBase(DirectionOfManipulation directionOfManipulation, QWidget * parent, const QString & name) :
|
||||
QWidget(parent),
|
||||
FloatModelView(new FloatModel(0, 0, 0, 1, nullptr, name, true), this),
|
||||
m_volumeKnob(false),
|
||||
m_volumeRatio(100.0, 0.0, 1000000.0),
|
||||
m_buttonPressed(false),
|
||||
m_directionOfManipulation(directionOfManipulation)
|
||||
{
|
||||
initUi(name);
|
||||
}
|
||||
|
||||
|
||||
void FloatModelEditorBase::initUi(const QString & name)
|
||||
{
|
||||
if (s_textFloat == nullptr)
|
||||
{
|
||||
s_textFloat = new SimpleTextFloat;
|
||||
}
|
||||
|
||||
setWindowTitle(name);
|
||||
|
||||
setFocusPolicy(Qt::ClickFocus);
|
||||
|
||||
doConnections();
|
||||
}
|
||||
|
||||
|
||||
void FloatModelEditorBase::showTextFloat(int msecBeforeDisplay, int msecDisplayTime)
|
||||
{
|
||||
s_textFloat->setText(displayValue());
|
||||
s_textFloat->moveGlobal(this, QPoint(width() + 2, 0));
|
||||
s_textFloat->showWithDelay(msecBeforeDisplay, msecDisplayTime);
|
||||
}
|
||||
|
||||
|
||||
float FloatModelEditorBase::getValue(const QPoint & p)
|
||||
{
|
||||
// Find out which direction/coordinate is relevant for this control
|
||||
int const coordinate = m_directionOfManipulation == DirectionOfManipulation::Vertical ? p.y() : -p.x();
|
||||
|
||||
// knob value increase is linear to mouse movement
|
||||
float value = .4f * coordinate;
|
||||
|
||||
// if shift pressed we want slower movement
|
||||
if (getGUI()->mainWindow()->isShiftPressed())
|
||||
{
|
||||
value /= 4.0f;
|
||||
value = qBound(-4.0f, value, 4.0f);
|
||||
}
|
||||
|
||||
return value * pageSize();
|
||||
}
|
||||
|
||||
|
||||
void FloatModelEditorBase::contextMenuEvent(QContextMenuEvent *)
|
||||
{
|
||||
// for the case, the user clicked right while pressing left mouse-
|
||||
// button, the context-menu appears while mouse-cursor is still hidden
|
||||
// and it isn't shown again until user does something which causes
|
||||
// an QApplication::restoreOverrideCursor()-call...
|
||||
mouseReleaseEvent(nullptr);
|
||||
|
||||
CaptionMenu contextMenu(model()->displayName(), this);
|
||||
addDefaultActions(&contextMenu);
|
||||
contextMenu.addAction(QPixmap(),
|
||||
model()->isScaleLogarithmic() ? tr("Set linear") : tr("Set logarithmic"),
|
||||
this, SLOT(toggleScale()));
|
||||
contextMenu.addSeparator();
|
||||
contextMenu.exec(QCursor::pos());
|
||||
}
|
||||
|
||||
|
||||
void FloatModelEditorBase::toggleScale()
|
||||
{
|
||||
model()->setScaleLogarithmic(! model()->isScaleLogarithmic());
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
void FloatModelEditorBase::dragEnterEvent(QDragEnterEvent * dee)
|
||||
{
|
||||
StringPairDrag::processDragEnterEvent(dee, "float_value,"
|
||||
"automatable_model");
|
||||
}
|
||||
|
||||
|
||||
void FloatModelEditorBase::dropEvent(QDropEvent * de)
|
||||
{
|
||||
QString type = StringPairDrag::decodeKey(de);
|
||||
QString val = StringPairDrag::decodeValue(de);
|
||||
if (type == "float_value")
|
||||
{
|
||||
model()->setValue(LocaleHelper::toFloat(val));
|
||||
de->accept();
|
||||
}
|
||||
else if (type == "automatable_model")
|
||||
{
|
||||
auto mod = dynamic_cast<AutomatableModel*>(Engine::projectJournal()->journallingObject(val.toInt()));
|
||||
if (mod != nullptr)
|
||||
{
|
||||
AutomatableModel::linkModels(model(), mod);
|
||||
mod->setValue(model()->value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FloatModelEditorBase::mousePressEvent(QMouseEvent * me)
|
||||
{
|
||||
if (me->button() == Qt::LeftButton &&
|
||||
! (me->modifiers() & Qt::ControlModifier) &&
|
||||
! (me->modifiers() & Qt::ShiftModifier))
|
||||
{
|
||||
AutomatableModel *thisModel = model();
|
||||
if (thisModel)
|
||||
{
|
||||
thisModel->addJournalCheckPoint();
|
||||
thisModel->saveJournallingState(false);
|
||||
}
|
||||
|
||||
const QPoint & p = me->pos();
|
||||
m_lastMousePos = p;
|
||||
m_leftOver = 0.0f;
|
||||
|
||||
emit sliderPressed();
|
||||
|
||||
showTextFloat(0, 0);
|
||||
|
||||
s_textFloat->setText(displayValue());
|
||||
s_textFloat->moveGlobal(this,
|
||||
QPoint(width() + 2, 0));
|
||||
s_textFloat->show();
|
||||
m_buttonPressed = true;
|
||||
}
|
||||
else if (me->button() == Qt::LeftButton &&
|
||||
(me->modifiers() & Qt::ShiftModifier))
|
||||
{
|
||||
new StringPairDrag("float_value",
|
||||
QString::number(model()->value()),
|
||||
QPixmap(), this);
|
||||
}
|
||||
else
|
||||
{
|
||||
FloatModelView::mousePressEvent(me);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FloatModelEditorBase::mouseMoveEvent(QMouseEvent * me)
|
||||
{
|
||||
if (m_buttonPressed && me->pos() != m_lastMousePos)
|
||||
{
|
||||
// knob position is changed depending on last mouse position
|
||||
setPosition(me->pos() - m_lastMousePos);
|
||||
emit sliderMoved(model()->value());
|
||||
// original position for next time is current position
|
||||
m_lastMousePos = me->pos();
|
||||
}
|
||||
s_textFloat->setText(displayValue());
|
||||
s_textFloat->show();
|
||||
}
|
||||
|
||||
|
||||
void FloatModelEditorBase::mouseReleaseEvent(QMouseEvent* event)
|
||||
{
|
||||
if (event && event->button() == Qt::LeftButton)
|
||||
{
|
||||
AutomatableModel *thisModel = model();
|
||||
if (thisModel)
|
||||
{
|
||||
thisModel->restoreJournallingState();
|
||||
}
|
||||
}
|
||||
|
||||
m_buttonPressed = false;
|
||||
|
||||
emit sliderReleased();
|
||||
|
||||
QApplication::restoreOverrideCursor();
|
||||
|
||||
s_textFloat->hide();
|
||||
}
|
||||
|
||||
|
||||
void FloatModelEditorBase::enterEvent(QEvent *event)
|
||||
{
|
||||
showTextFloat(700, 2000);
|
||||
}
|
||||
|
||||
|
||||
void FloatModelEditorBase::leaveEvent(QEvent *event)
|
||||
{
|
||||
s_textFloat->hide();
|
||||
}
|
||||
|
||||
|
||||
void FloatModelEditorBase::focusOutEvent(QFocusEvent * fe)
|
||||
{
|
||||
// make sure we don't loose mouse release event
|
||||
mouseReleaseEvent(nullptr);
|
||||
QWidget::focusOutEvent(fe);
|
||||
}
|
||||
|
||||
|
||||
void FloatModelEditorBase::mouseDoubleClickEvent(QMouseEvent *)
|
||||
{
|
||||
enterValue();
|
||||
}
|
||||
|
||||
|
||||
void FloatModelEditorBase::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter p(this);
|
||||
|
||||
QColor const foreground(3, 94, 97);
|
||||
|
||||
auto const * mod = model();
|
||||
auto const minValue = mod->minValue();
|
||||
auto const maxValue = mod->maxValue();
|
||||
auto const range = maxValue - minValue;
|
||||
|
||||
// Compute the percentage
|
||||
// min + x * (max - min) = v <=> x = (v - min) / (max - min)
|
||||
auto const percentage = range == 0 ? 1. : (mod->value() - minValue) / range;
|
||||
|
||||
QRect r = rect();
|
||||
p.setPen(foreground);
|
||||
p.setBrush(foreground);
|
||||
p.drawRect(QRect(r.topLeft(), QPoint(r.width() * percentage, r.height())));
|
||||
}
|
||||
|
||||
|
||||
void FloatModelEditorBase::wheelEvent(QWheelEvent * we)
|
||||
{
|
||||
we->accept();
|
||||
const int deltaY = we->angleDelta().y();
|
||||
float direction = deltaY > 0 ? 1 : -1;
|
||||
|
||||
auto * m = model();
|
||||
float const step = m->step<float>();
|
||||
float const range = m->range();
|
||||
|
||||
// This is the default number of steps or mouse wheel events that it takes to sweep
|
||||
// from the lowest value to the highest value.
|
||||
// It might be modified if the user presses modifier keys. See below.
|
||||
float numberOfStepsForFullSweep = 100.;
|
||||
|
||||
auto const modKeys = we->modifiers();
|
||||
if (modKeys == Qt::ShiftModifier)
|
||||
{
|
||||
// The shift is intended to go through the values in very coarse steps as in:
|
||||
// "Shift into overdrive"
|
||||
numberOfStepsForFullSweep = 10;
|
||||
}
|
||||
else if (modKeys == Qt::ControlModifier)
|
||||
{
|
||||
// The control key gives more control, i.e. it enables more fine-grained adjustments
|
||||
numberOfStepsForFullSweep = 1000;
|
||||
}
|
||||
else if (modKeys == Qt::AltModifier)
|
||||
{
|
||||
// The alt key enables even finer adjustments
|
||||
numberOfStepsForFullSweep = 2000;
|
||||
|
||||
// It seems that on some systems pressing Alt with mess with the directions,
|
||||
// i.e. scrolling the mouse wheel is interpreted as pressing the mouse wheel
|
||||
// left and right. Account for this quirk.
|
||||
if (deltaY == 0)
|
||||
{
|
||||
int const deltaX = we->angleDelta().x();
|
||||
if (deltaX != 0)
|
||||
{
|
||||
direction = deltaX > 0 ? 1 : -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the number of steps but make sure that we always do at least one step
|
||||
const float stepMult = std::max(range / numberOfStepsForFullSweep / step, 1.f);
|
||||
const int inc = direction * stepMult;
|
||||
model()->incValue(inc);
|
||||
|
||||
s_textFloat->setText(displayValue());
|
||||
s_textFloat->moveGlobal(this, QPoint(width() + 2, 0));
|
||||
s_textFloat->setVisibilityTimeOut(1000);
|
||||
|
||||
emit sliderMoved(model()->value());
|
||||
}
|
||||
|
||||
|
||||
void FloatModelEditorBase::setPosition(const QPoint & p)
|
||||
{
|
||||
const float value = getValue(p) + m_leftOver;
|
||||
const auto step = model()->step<float>();
|
||||
const float oldValue = model()->value();
|
||||
|
||||
if (model()->isScaleLogarithmic()) // logarithmic code
|
||||
{
|
||||
const float pos = model()->minValue() < 0
|
||||
? oldValue / qMax(qAbs(model()->maxValue()), qAbs(model()->minValue()))
|
||||
: (oldValue - model()->minValue()) / model()->range();
|
||||
const float ratio = 0.1f + qAbs(pos) * 15.f;
|
||||
float newValue = value * ratio;
|
||||
if (qAbs(newValue) >= step)
|
||||
{
|
||||
float roundedValue = qRound((oldValue - value) / step) * step;
|
||||
model()->setValue(roundedValue);
|
||||
m_leftOver = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_leftOver = value;
|
||||
}
|
||||
}
|
||||
|
||||
else // linear code
|
||||
{
|
||||
if (qAbs(value) >= step)
|
||||
{
|
||||
float roundedValue = qRound((oldValue - value) / step) * step;
|
||||
model()->setValue(roundedValue);
|
||||
m_leftOver = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_leftOver = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FloatModelEditorBase::enterValue()
|
||||
{
|
||||
bool ok;
|
||||
float new_val;
|
||||
|
||||
if (isVolumeKnob() &&
|
||||
ConfigManager::inst()->value("app", "displaydbfs").toInt())
|
||||
{
|
||||
new_val = QInputDialog::getDouble(
|
||||
this, tr("Set value"),
|
||||
tr("Please enter a new value between "
|
||||
"-96.0 dBFS and 6.0 dBFS:"),
|
||||
ampToDbfs(model()->getRoundedValue() / 100.0),
|
||||
-96.0, 6.0, model()->getDigitCount(), &ok);
|
||||
if (new_val <= -96.0)
|
||||
{
|
||||
new_val = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
new_val = dbfsToAmp(new_val) * 100.0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
new_val = QInputDialog::getDouble(
|
||||
this, tr("Set value"),
|
||||
tr("Please enter a new value between "
|
||||
"%1 and %2:").
|
||||
arg(model()->minValue()).
|
||||
arg(model()->maxValue()),
|
||||
model()->getRoundedValue(),
|
||||
model()->minValue(),
|
||||
model()->maxValue(), model()->getDigitCount(), &ok);
|
||||
}
|
||||
|
||||
if (ok)
|
||||
{
|
||||
model()->setValue(new_val);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FloatModelEditorBase::friendlyUpdate()
|
||||
{
|
||||
if (model() && (model()->controllerConnection() == nullptr ||
|
||||
model()->controllerConnection()->getController()->frequentUpdates() == false ||
|
||||
Controller::runningFrames() % (256*4) == 0))
|
||||
{
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QString FloatModelEditorBase::displayValue() const
|
||||
{
|
||||
if (isVolumeKnob() &&
|
||||
ConfigManager::inst()->value("app", "displaydbfs").toInt())
|
||||
{
|
||||
return m_description.trimmed() + QString(" %1 dBFS").
|
||||
arg(ampToDbfs(model()->getRoundedValue() / volumeRatio()),
|
||||
3, 'f', 2);
|
||||
}
|
||||
|
||||
return m_description.trimmed() + QString(" %1").
|
||||
arg(model()->getRoundedValue()) + m_unit;
|
||||
}
|
||||
|
||||
|
||||
void FloatModelEditorBase::doConnections()
|
||||
{
|
||||
if (model() != nullptr)
|
||||
{
|
||||
QObject::connect(model(), SIGNAL(dataChanged()),
|
||||
this, SLOT(friendlyUpdate()));
|
||||
|
||||
QObject::connect(model(), SIGNAL(propertiesChanged()),
|
||||
this, SLOT(update()));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lmms::gui
|
||||
@@ -24,11 +24,6 @@
|
||||
|
||||
#include "Knob.h"
|
||||
|
||||
#include <memory>
|
||||
#include <QApplication>
|
||||
#include <QFontMetrics>
|
||||
#include <QInputDialog>
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
|
||||
#ifndef __USE_XOPEN
|
||||
@@ -36,36 +31,19 @@
|
||||
#endif
|
||||
|
||||
#include "lmms_math.h"
|
||||
#include "CaptionMenu.h"
|
||||
#include "ConfigManager.h"
|
||||
#include "ControllerConnection.h"
|
||||
#include "DeprecationHelper.h"
|
||||
#include "embed.h"
|
||||
#include "gui_templates.h"
|
||||
#include "GuiApplication.h"
|
||||
#include "LocaleHelper.h"
|
||||
#include "MainWindow.h"
|
||||
#include "ProjectJournal.h"
|
||||
#include "SimpleTextFloat.h"
|
||||
#include "StringPairDrag.h"
|
||||
|
||||
|
||||
namespace lmms::gui
|
||||
{
|
||||
|
||||
SimpleTextFloat * Knob::s_textFloat = nullptr;
|
||||
|
||||
|
||||
|
||||
|
||||
Knob::Knob( KnobType _knob_num, QWidget * _parent, const QString & _name ) :
|
||||
QWidget( _parent ),
|
||||
FloatModelView( new FloatModel( 0, 0, 0, 1, nullptr, _name, true ), this ),
|
||||
FloatModelEditorBase(DirectionOfManipulation::Vertical, _parent, _name),
|
||||
m_label( "" ),
|
||||
m_isHtmlLabel(false),
|
||||
m_tdRenderer(nullptr),
|
||||
m_volumeKnob( false ),
|
||||
m_volumeRatio( 100.0, 0.0, 1000000.0 ),
|
||||
m_buttonPressed( false ),
|
||||
m_angle( -10 ),
|
||||
m_lineWidth( 0 ),
|
||||
m_textColor( 255, 255, 255 ),
|
||||
@@ -84,18 +62,10 @@ Knob::Knob( QWidget * _parent, const QString & _name ) :
|
||||
|
||||
void Knob::initUi( const QString & _name )
|
||||
{
|
||||
if( s_textFloat == nullptr )
|
||||
{
|
||||
s_textFloat = new SimpleTextFloat;
|
||||
}
|
||||
|
||||
setWindowTitle( _name );
|
||||
|
||||
onKnobNumUpdated();
|
||||
setTotalAngle( 270.0f );
|
||||
setInnerRadius( 1.0f );
|
||||
setOuterRadius( 10.0f );
|
||||
setFocusPolicy( Qt::ClickFocus );
|
||||
|
||||
// This is a workaround to enable style sheets for knobs which are not styled knobs.
|
||||
//
|
||||
@@ -123,13 +93,9 @@ void Knob::initUi( const QString & _name )
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
doConnections();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Knob::onKnobNumUpdated()
|
||||
{
|
||||
if( m_knobNum != KnobType::Styled )
|
||||
@@ -484,195 +450,6 @@ void Knob::drawKnob( QPainter * _p )
|
||||
_p->drawImage( 0, 0, m_cache );
|
||||
}
|
||||
|
||||
void Knob::showTextFloat(int msecBeforeDisplay, int msecDisplayTime)
|
||||
{
|
||||
s_textFloat->setText(displayValue());
|
||||
s_textFloat->moveGlobal(this, QPoint(width() + 2, 0));
|
||||
s_textFloat->showWithDelay(msecBeforeDisplay, msecDisplayTime);
|
||||
}
|
||||
|
||||
float Knob::getValue( const QPoint & _p )
|
||||
{
|
||||
float value;
|
||||
|
||||
// knob value increase is linear to mouse movement
|
||||
value = .4f * _p.y();
|
||||
|
||||
// if shift pressed we want slower movement
|
||||
if( getGUI()->mainWindow()->isShiftPressed() )
|
||||
{
|
||||
value /= 4.0f;
|
||||
value = qBound( -4.0f, value, 4.0f );
|
||||
}
|
||||
return value * pageSize();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Knob::contextMenuEvent( QContextMenuEvent * )
|
||||
{
|
||||
// for the case, the user clicked right while pressing left mouse-
|
||||
// button, the context-menu appears while mouse-cursor is still hidden
|
||||
// and it isn't shown again until user does something which causes
|
||||
// an QApplication::restoreOverrideCursor()-call...
|
||||
mouseReleaseEvent( nullptr );
|
||||
|
||||
CaptionMenu contextMenu( model()->displayName(), this );
|
||||
addDefaultActions( &contextMenu );
|
||||
contextMenu.addAction( QPixmap(),
|
||||
model()->isScaleLogarithmic() ? tr( "Set linear" ) : tr( "Set logarithmic" ),
|
||||
this, SLOT(toggleScale()));
|
||||
contextMenu.addSeparator();
|
||||
contextMenu.exec( QCursor::pos() );
|
||||
}
|
||||
|
||||
|
||||
void Knob::toggleScale()
|
||||
{
|
||||
model()->setScaleLogarithmic( ! model()->isScaleLogarithmic() );
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Knob::dragEnterEvent( QDragEnterEvent * _dee )
|
||||
{
|
||||
StringPairDrag::processDragEnterEvent( _dee, "float_value,"
|
||||
"automatable_model" );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Knob::dropEvent( QDropEvent * _de )
|
||||
{
|
||||
QString type = StringPairDrag::decodeKey( _de );
|
||||
QString val = StringPairDrag::decodeValue( _de );
|
||||
if( type == "float_value" )
|
||||
{
|
||||
model()->setValue( LocaleHelper::toFloat(val) );
|
||||
_de->accept();
|
||||
}
|
||||
else if( type == "automatable_model" )
|
||||
{
|
||||
auto mod = dynamic_cast<AutomatableModel*>(Engine::projectJournal()->journallingObject(val.toInt()));
|
||||
if( mod != nullptr )
|
||||
{
|
||||
AutomatableModel::linkModels( model(), mod );
|
||||
mod->setValue( model()->value() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Knob::mousePressEvent( QMouseEvent * _me )
|
||||
{
|
||||
if( _me->button() == Qt::LeftButton &&
|
||||
! ( _me->modifiers() & Qt::ControlModifier ) &&
|
||||
! ( _me->modifiers() & Qt::ShiftModifier ) )
|
||||
{
|
||||
AutomatableModel *thisModel = model();
|
||||
if( thisModel )
|
||||
{
|
||||
thisModel->addJournalCheckPoint();
|
||||
thisModel->saveJournallingState( false );
|
||||
}
|
||||
|
||||
const QPoint & p = _me->pos();
|
||||
m_lastMousePos = p;
|
||||
m_leftOver = 0.0f;
|
||||
|
||||
emit sliderPressed();
|
||||
|
||||
showTextFloat(0, 0);
|
||||
|
||||
m_buttonPressed = true;
|
||||
}
|
||||
else if( _me->button() == Qt::LeftButton &&
|
||||
(_me->modifiers() & Qt::ShiftModifier) )
|
||||
{
|
||||
new StringPairDrag( "float_value",
|
||||
QString::number( model()->value() ),
|
||||
QPixmap(), this );
|
||||
}
|
||||
else
|
||||
{
|
||||
FloatModelView::mousePressEvent( _me );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Knob::mouseMoveEvent( QMouseEvent * _me )
|
||||
{
|
||||
if( m_buttonPressed && _me->pos() != m_lastMousePos )
|
||||
{
|
||||
// knob position is changed depending on last mouse position
|
||||
setPosition( _me->pos() - m_lastMousePos );
|
||||
emit sliderMoved( model()->value() );
|
||||
// original position for next time is current position
|
||||
m_lastMousePos = _me->pos();
|
||||
}
|
||||
s_textFloat->setText( displayValue() );
|
||||
s_textFloat->show();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Knob::mouseReleaseEvent( QMouseEvent* event )
|
||||
{
|
||||
if( event && event->button() == Qt::LeftButton )
|
||||
{
|
||||
AutomatableModel *thisModel = model();
|
||||
if( thisModel )
|
||||
{
|
||||
thisModel->restoreJournallingState();
|
||||
}
|
||||
}
|
||||
|
||||
m_buttonPressed = false;
|
||||
|
||||
emit sliderReleased();
|
||||
|
||||
QApplication::restoreOverrideCursor();
|
||||
|
||||
s_textFloat->hide();
|
||||
}
|
||||
|
||||
void Knob::enterEvent(QEvent *event)
|
||||
{
|
||||
showTextFloat(700, 2000);
|
||||
}
|
||||
|
||||
void Knob::leaveEvent(QEvent *event)
|
||||
{
|
||||
s_textFloat->hide();
|
||||
}
|
||||
|
||||
|
||||
void Knob::focusOutEvent( QFocusEvent * _fe )
|
||||
{
|
||||
// make sure we don't loose mouse release event
|
||||
mouseReleaseEvent( nullptr );
|
||||
QWidget::focusOutEvent( _fe );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Knob::mouseDoubleClickEvent( QMouseEvent * )
|
||||
{
|
||||
enterValue();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Knob::paintEvent( QPaintEvent * _me )
|
||||
{
|
||||
QPainter p( this );
|
||||
@@ -697,201 +474,6 @@ void Knob::paintEvent( QPaintEvent * _me )
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Knob::wheelEvent(QWheelEvent * we)
|
||||
{
|
||||
we->accept();
|
||||
const int deltaY = we->angleDelta().y();
|
||||
float direction = deltaY > 0 ? 1 : -1;
|
||||
|
||||
auto * m = model();
|
||||
float const step = m->step<float>();
|
||||
float const range = m->range();
|
||||
|
||||
// This is the default number of steps or mouse wheel events that it takes to sweep
|
||||
// from the lowest value to the highest value.
|
||||
// It might be modified if the user presses modifier keys. See below.
|
||||
float numberOfStepsForFullSweep = 100.;
|
||||
|
||||
auto const modKeys = we->modifiers();
|
||||
if (modKeys == Qt::ShiftModifier)
|
||||
{
|
||||
// The shift is intended to go through the values in very coarse steps as in:
|
||||
// "Shift into overdrive"
|
||||
numberOfStepsForFullSweep = 10;
|
||||
}
|
||||
else if (modKeys == Qt::ControlModifier)
|
||||
{
|
||||
// The control key gives more control, i.e. it enables more fine-grained adjustments
|
||||
numberOfStepsForFullSweep = 1000;
|
||||
}
|
||||
else if (modKeys == Qt::AltModifier)
|
||||
{
|
||||
// The alt key enables even finer adjustments
|
||||
numberOfStepsForFullSweep = 2000;
|
||||
|
||||
// It seems that on some systems pressing Alt with mess with the directions,
|
||||
// i.e. scrolling the mouse wheel is interpreted as pressing the mouse wheel
|
||||
// left and right. Account for this quirk.
|
||||
if (deltaY == 0)
|
||||
{
|
||||
int const deltaX = we->angleDelta().x();
|
||||
if (deltaX != 0)
|
||||
{
|
||||
direction = deltaX > 0 ? 1 : -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the number of steps but make sure that we always do at least one step
|
||||
const float stepMult = std::max(range / numberOfStepsForFullSweep / step, 1.f);
|
||||
const int inc = direction * stepMult;
|
||||
model()->incValue(inc);
|
||||
|
||||
s_textFloat->setText( displayValue() );
|
||||
s_textFloat->moveGlobal( this, QPoint( width() + 2, 0 ) );
|
||||
s_textFloat->setVisibilityTimeOut( 1000 );
|
||||
|
||||
emit sliderMoved( model()->value() );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Knob::setPosition( const QPoint & _p )
|
||||
{
|
||||
const float value = getValue( _p ) + m_leftOver;
|
||||
const auto step = model()->step<float>();
|
||||
const float oldValue = model()->value();
|
||||
|
||||
|
||||
|
||||
if( model()->isScaleLogarithmic() ) // logarithmic code
|
||||
{
|
||||
const float pos = model()->minValue() < 0
|
||||
? oldValue / qMax( qAbs( model()->maxValue() ), qAbs( model()->minValue() ) )
|
||||
: ( oldValue - model()->minValue() ) / model()->range();
|
||||
const float ratio = 0.1f + qAbs( pos ) * 15.f;
|
||||
float newValue = value * ratio;
|
||||
if( qAbs( newValue ) >= step )
|
||||
{
|
||||
float roundedValue = qRound( ( oldValue - value ) / step ) * step;
|
||||
model()->setValue( roundedValue );
|
||||
m_leftOver = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_leftOver = value;
|
||||
}
|
||||
}
|
||||
|
||||
else // linear code
|
||||
{
|
||||
if( qAbs( value ) >= step )
|
||||
{
|
||||
float roundedValue = qRound( ( oldValue - value ) / step ) * step;
|
||||
model()->setValue( roundedValue );
|
||||
m_leftOver = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_leftOver = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Knob::enterValue()
|
||||
{
|
||||
bool ok;
|
||||
float new_val;
|
||||
|
||||
if( isVolumeKnob() &&
|
||||
ConfigManager::inst()->value( "app", "displaydbfs" ).toInt() )
|
||||
{
|
||||
new_val = QInputDialog::getDouble(
|
||||
this, tr( "Set value" ),
|
||||
tr( "Please enter a new value between "
|
||||
"-96.0 dBFS and 6.0 dBFS:" ),
|
||||
ampToDbfs( model()->getRoundedValue() / 100.0 ),
|
||||
-96.0, 6.0, model()->getDigitCount(), &ok );
|
||||
if( new_val <= -96.0 )
|
||||
{
|
||||
new_val = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
new_val = dbfsToAmp( new_val ) * 100.0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
new_val = QInputDialog::getDouble(
|
||||
this, tr( "Set value" ),
|
||||
tr( "Please enter a new value between "
|
||||
"%1 and %2:" ).
|
||||
arg( model()->minValue() ).
|
||||
arg( model()->maxValue() ),
|
||||
model()->getRoundedValue(),
|
||||
model()->minValue(),
|
||||
model()->maxValue(), model()->getDigitCount(), &ok );
|
||||
}
|
||||
|
||||
if( ok )
|
||||
{
|
||||
model()->setValue( new_val );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Knob::friendlyUpdate()
|
||||
{
|
||||
if (model() && (model()->controllerConnection() == nullptr ||
|
||||
model()->controllerConnection()->getController()->frequentUpdates() == false ||
|
||||
Controller::runningFrames() % (256*4) == 0))
|
||||
{
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
QString Knob::displayValue() const
|
||||
{
|
||||
if( isVolumeKnob() &&
|
||||
ConfigManager::inst()->value( "app", "displaydbfs" ).toInt() )
|
||||
{
|
||||
return m_description.trimmed() + QString( " %1 dBFS" ).
|
||||
arg( ampToDbfs( model()->getRoundedValue() / volumeRatio() ),
|
||||
3, 'f', 2 );
|
||||
}
|
||||
return m_description.trimmed() + QString( " %1" ).
|
||||
arg( model()->getRoundedValue() ) + m_unit;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Knob::doConnections()
|
||||
{
|
||||
if( model() != nullptr )
|
||||
{
|
||||
QObject::connect( model(), SIGNAL(dataChanged()),
|
||||
this, SLOT(friendlyUpdate()));
|
||||
|
||||
QObject::connect( model(), SIGNAL(propertiesChanged()),
|
||||
this, SLOT(update()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Knob::changeEvent(QEvent * ev)
|
||||
{
|
||||
if (ev->type() == QEvent::EnabledChange)
|
||||
|
||||
@@ -44,9 +44,10 @@ static const auto names = std::array<QString, 3>
|
||||
|
||||
|
||||
LedCheckBox::LedCheckBox( const QString & _text, QWidget * _parent,
|
||||
const QString & _name, LedColor _color ) :
|
||||
const QString & _name, LedColor _color, bool legacyMode ) :
|
||||
AutomatableButton( _parent, _name ),
|
||||
m_text( _text )
|
||||
m_text( _text ),
|
||||
m_legacyMode(legacyMode)
|
||||
{
|
||||
initUi( _color );
|
||||
}
|
||||
@@ -55,8 +56,8 @@ LedCheckBox::LedCheckBox( const QString & _text, QWidget * _parent,
|
||||
|
||||
|
||||
LedCheckBox::LedCheckBox( QWidget * _parent,
|
||||
const QString & _name, LedColor _color ) :
|
||||
LedCheckBox( QString(), _parent, _name, _color )
|
||||
const QString & _name, LedColor _color, bool legacyMode ) :
|
||||
LedCheckBox( QString(), _parent, _name, _color, legacyMode )
|
||||
{
|
||||
}
|
||||
|
||||
@@ -80,24 +81,16 @@ void LedCheckBox::setText( const QString &s )
|
||||
|
||||
|
||||
|
||||
void LedCheckBox::paintEvent( QPaintEvent * )
|
||||
void LedCheckBox::paintEvent( QPaintEvent * pe )
|
||||
{
|
||||
QPainter p( this );
|
||||
p.setFont( pointSize<7>( font() ) );
|
||||
|
||||
if( model()->value() == true )
|
||||
{
|
||||
p.drawPixmap( 0, 0, *m_ledOnPixmap );
|
||||
if (!m_legacyMode)
|
||||
{
|
||||
paintNonLegacy(pe);
|
||||
}
|
||||
else
|
||||
{
|
||||
p.drawPixmap( 0, 0, *m_ledOffPixmap );
|
||||
paintLegacy(pe);
|
||||
}
|
||||
|
||||
p.setPen( QColor( 64, 64, 64 ) );
|
||||
p.drawText( m_ledOffPixmap->width() + 4, 11, text() );
|
||||
p.setPen( QColor( 255, 255, 255 ) );
|
||||
p.drawText( m_ledOffPixmap->width() + 3, 10, text() );
|
||||
}
|
||||
|
||||
|
||||
@@ -111,7 +104,11 @@ void LedCheckBox::initUi( LedColor _color )
|
||||
names[static_cast<std::size_t>(_color)].toUtf8().constData() ) );
|
||||
m_ledOffPixmap = new QPixmap( embed::getIconPixmap( "led_off" ) );
|
||||
|
||||
setFont( pointSize<7>( font() ) );
|
||||
if (m_legacyMode)
|
||||
{
|
||||
setFont( pointSize<7>( font() ) );
|
||||
}
|
||||
|
||||
setText( m_text );
|
||||
}
|
||||
|
||||
@@ -120,9 +117,45 @@ void LedCheckBox::initUi( LedColor _color )
|
||||
|
||||
void LedCheckBox::onTextUpdated()
|
||||
{
|
||||
setFixedSize(m_ledOffPixmap->width() + 5 + horizontalAdvance(QFontMetrics(font()),
|
||||
text()),
|
||||
m_ledOffPixmap->height());
|
||||
QFontMetrics const fm = fontMetrics();
|
||||
|
||||
int const width = m_ledOffPixmap->width() + 5 + horizontalAdvance(fm, text());
|
||||
int const height = m_legacyMode ? m_ledOffPixmap->height() : qMax(m_ledOffPixmap->height(), fm.height());
|
||||
|
||||
setFixedSize(width, height);
|
||||
}
|
||||
|
||||
void LedCheckBox::paintLegacy(QPaintEvent * pe)
|
||||
{
|
||||
QPainter p( this );
|
||||
p.setFont( pointSize<7>( font() ) );
|
||||
|
||||
if( model()->value() == true )
|
||||
{
|
||||
p.drawPixmap( 0, 0, *m_ledOnPixmap );
|
||||
}
|
||||
else
|
||||
{
|
||||
p.drawPixmap( 0, 0, *m_ledOffPixmap );
|
||||
}
|
||||
|
||||
p.setPen( QColor( 64, 64, 64 ) );
|
||||
p.drawText( m_ledOffPixmap->width() + 4, 11, text() );
|
||||
p.setPen( QColor( 255, 255, 255 ) );
|
||||
p.drawText( m_ledOffPixmap->width() + 3, 10, text() );
|
||||
}
|
||||
|
||||
void LedCheckBox::paintNonLegacy(QPaintEvent * pe)
|
||||
{
|
||||
QPainter p(this);
|
||||
|
||||
QPixmap * drawnPixmap = model()->value() ? m_ledOnPixmap : m_ledOffPixmap;
|
||||
|
||||
p.drawPixmap( 0, rect().height() / 2 - drawnPixmap->height() / 2, *drawnPixmap);
|
||||
|
||||
QRect r = rect();
|
||||
r -= QMargins(m_ledOffPixmap->width() + 5, 0, 0, 0);
|
||||
p.drawText(r, text());
|
||||
}
|
||||
|
||||
|
||||
|
||||
302
src/gui/widgets/TempoSyncBarModelEditor.cpp
Normal file
302
src/gui/widgets/TempoSyncBarModelEditor.cpp
Normal file
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
* TempoSyncBarModelEditor.cpp - adds bpm to ms conversion for the bar editor class
|
||||
*
|
||||
* Copyright (c) 2005-2007 Danny McRae <khjklujn/at/yahoo.com>
|
||||
* Copyright (c) 2005-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2023 Michael Gregorius
|
||||
*
|
||||
* 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 <QMdiArea>
|
||||
|
||||
#include "TempoSyncBarModelEditor.h"
|
||||
#include "Engine.h"
|
||||
#include "CaptionMenu.h"
|
||||
#include "embed.h"
|
||||
#include "GuiApplication.h"
|
||||
#include "MainWindow.h"
|
||||
#include "MeterDialog.h"
|
||||
#include "Song.h"
|
||||
#include "SubWindow.h"
|
||||
|
||||
|
||||
namespace lmms::gui
|
||||
{
|
||||
|
||||
TempoSyncBarModelEditor::TempoSyncBarModelEditor(QString text, FloatModel * floatModel, QWidget * parent) :
|
||||
BarModelEditor(text, floatModel, parent),
|
||||
m_tempoSyncIcon(embed::getIconPixmap("tempo_sync")),
|
||||
m_tempoSyncDescription(tr("Tempo Sync")),
|
||||
m_custom(nullptr)
|
||||
{
|
||||
modelChanged();
|
||||
}
|
||||
|
||||
|
||||
TempoSyncBarModelEditor::~TempoSyncBarModelEditor()
|
||||
{
|
||||
if(m_custom)
|
||||
{
|
||||
delete m_custom->parentWidget();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TempoSyncBarModelEditor::modelChanged()
|
||||
{
|
||||
TempoSyncKnobModel * tempoSyncModel = model();
|
||||
|
||||
if(tempoSyncModel == nullptr)
|
||||
{
|
||||
qWarning("no TempoSyncKnobModel has been set!");
|
||||
}
|
||||
|
||||
if(m_custom != nullptr)
|
||||
{
|
||||
m_custom->setModel(&tempoSyncModel->getCustomMeterModel());
|
||||
}
|
||||
|
||||
connect(tempoSyncModel, &TempoSyncKnobModel::syncModeChanged, this, &TempoSyncBarModelEditor::updateDescAndIcon);
|
||||
connect(this, SIGNAL(sliderMoved(float)), tempoSyncModel, SLOT(disableSync()));
|
||||
|
||||
updateDescAndIcon();
|
||||
}
|
||||
|
||||
|
||||
void TempoSyncBarModelEditor::contextMenuEvent(QContextMenuEvent *)
|
||||
{
|
||||
mouseReleaseEvent(nullptr);
|
||||
|
||||
TempoSyncKnobModel * tempoSyncModel = model();
|
||||
|
||||
CaptionMenu contextMenu(tempoSyncModel->displayName(), this);
|
||||
addDefaultActions(&contextMenu);
|
||||
|
||||
contextMenu.addSeparator();
|
||||
|
||||
float limit = 60000.0f / (Engine::getSong()->getTempo() * tempoSyncModel->scale());
|
||||
|
||||
QMenu * syncMenu = contextMenu.addMenu(m_tempoSyncIcon, m_tempoSyncDescription);
|
||||
|
||||
float const maxValue = tempoSyncModel->maxValue();
|
||||
|
||||
if(limit / 8.0f <= maxValue)
|
||||
{
|
||||
connect(syncMenu, SIGNAL(triggered(QAction*)), tempoSyncModel, SLOT(setTempoSync(QAction*)));
|
||||
|
||||
syncMenu->addAction(embed::getIconPixmap("note_none"),
|
||||
tr("No Sync"))->setData((int) TempoSyncKnobModel::SyncMode::None);
|
||||
|
||||
if(limit / 0.125f <= maxValue)
|
||||
{
|
||||
syncMenu->addAction(embed::getIconPixmap("note_double_whole"),
|
||||
tr("Eight beats"))->setData((int) TempoSyncKnobModel::SyncMode::DoubleWholeNote);
|
||||
}
|
||||
|
||||
if(limit / 0.25f <= maxValue)
|
||||
{
|
||||
syncMenu->addAction(embed::getIconPixmap("note_whole"),
|
||||
tr("Whole note"))->setData((int) TempoSyncKnobModel::SyncMode::WholeNote);
|
||||
}
|
||||
|
||||
if(limit / 0.5f <= maxValue)
|
||||
{
|
||||
syncMenu->addAction(embed::getIconPixmap("note_half"),
|
||||
tr("Half note"))->setData((int) TempoSyncKnobModel::SyncMode::HalfNote);
|
||||
}
|
||||
|
||||
if(limit <= maxValue)
|
||||
{
|
||||
syncMenu->addAction(embed::getIconPixmap("note_quarter"),
|
||||
tr("Quarter note"))->setData((int) TempoSyncKnobModel::SyncMode::QuarterNote);
|
||||
}
|
||||
|
||||
if(limit / 2.0f <= maxValue)
|
||||
{
|
||||
syncMenu->addAction(embed::getIconPixmap("note_eighth"),
|
||||
tr("8th note"))->setData((int) TempoSyncKnobModel::SyncMode::EighthNote);
|
||||
}
|
||||
|
||||
if(limit / 4.0f <= maxValue)
|
||||
{
|
||||
syncMenu->addAction(embed::getIconPixmap("note_sixteenth"),
|
||||
tr("16th note"))->setData((int) TempoSyncKnobModel::SyncMode::SixteenthNote);
|
||||
}
|
||||
|
||||
syncMenu->addAction(embed::getIconPixmap("note_thirtysecond"),
|
||||
tr("32nd note"))->setData((int) TempoSyncKnobModel::SyncMode::ThirtysecondNote);
|
||||
|
||||
syncMenu->addAction(embed::getIconPixmap("dont_know"),
|
||||
tr("Custom..."), this, SLOT(showCustom()))->setData((int) TempoSyncKnobModel::SyncMode::Custom);
|
||||
|
||||
contextMenu.addSeparator();
|
||||
}
|
||||
|
||||
contextMenu.exec(QCursor::pos());
|
||||
|
||||
delete syncMenu;
|
||||
}
|
||||
|
||||
void TempoSyncBarModelEditor::updateDescAndIcon()
|
||||
{
|
||||
updateTextDescription();
|
||||
|
||||
if(m_custom != nullptr && model()->syncMode() != TempoSyncKnobModel::SyncMode::Custom)
|
||||
{
|
||||
m_custom->parentWidget()->hide();
|
||||
}
|
||||
|
||||
updateIcon();
|
||||
|
||||
emit syncDescriptionChanged(m_tempoSyncDescription);
|
||||
emit syncIconChanged();
|
||||
}
|
||||
|
||||
|
||||
const QString & TempoSyncBarModelEditor::syncDescription()
|
||||
{
|
||||
return m_tempoSyncDescription;
|
||||
}
|
||||
|
||||
|
||||
void TempoSyncBarModelEditor::setSyncDescription(const QString & new_description)
|
||||
{
|
||||
m_tempoSyncDescription = new_description;
|
||||
emit syncDescriptionChanged(new_description);
|
||||
}
|
||||
|
||||
|
||||
const QPixmap & TempoSyncBarModelEditor::syncIcon()
|
||||
{
|
||||
return m_tempoSyncIcon;
|
||||
}
|
||||
|
||||
|
||||
void TempoSyncBarModelEditor::setSyncIcon(const QPixmap & new_icon)
|
||||
{
|
||||
m_tempoSyncIcon = new_icon;
|
||||
emit syncIconChanged();
|
||||
}
|
||||
|
||||
|
||||
void TempoSyncBarModelEditor::showCustom()
|
||||
{
|
||||
if(m_custom == nullptr)
|
||||
{
|
||||
m_custom = new MeterDialog(getGUI()->mainWindow()->workspace());
|
||||
QMdiSubWindow * subWindow = getGUI()->mainWindow()->addWindowedWidget(m_custom);
|
||||
Qt::WindowFlags flags = subWindow->windowFlags();
|
||||
flags &= ~Qt::WindowMaximizeButtonHint;
|
||||
subWindow->setWindowFlags(flags);
|
||||
subWindow->setFixedSize(subWindow->size());
|
||||
m_custom->setWindowTitle("Meter");
|
||||
m_custom->setModel(&model()->getCustomMeterModel());
|
||||
}
|
||||
|
||||
m_custom->parentWidget()->show();
|
||||
model()->setTempoSync(TempoSyncKnobModel::SyncMode::Custom);
|
||||
}
|
||||
|
||||
|
||||
void TempoSyncBarModelEditor::updateTextDescription()
|
||||
{
|
||||
TempoSyncKnobModel * tempoSyncModel = model();
|
||||
|
||||
auto const syncMode = tempoSyncModel->syncMode();
|
||||
|
||||
switch(syncMode)
|
||||
{
|
||||
case TempoSyncKnobModel::SyncMode::None:
|
||||
m_tempoSyncDescription = tr("Tempo Sync");
|
||||
break;
|
||||
case TempoSyncKnobModel::SyncMode::Custom:
|
||||
m_tempoSyncDescription = tr("Custom ") +
|
||||
"(" +
|
||||
QString::number(tempoSyncModel->getCustomMeterModel().numeratorModel().value()) +
|
||||
"/" +
|
||||
QString::number(tempoSyncModel->getCustomMeterModel().denominatorModel().value()) +
|
||||
")";
|
||||
break;
|
||||
case TempoSyncKnobModel::SyncMode::DoubleWholeNote:
|
||||
m_tempoSyncDescription = tr("Synced to Eight Beats");
|
||||
break;
|
||||
case TempoSyncKnobModel::SyncMode::WholeNote:
|
||||
m_tempoSyncDescription = tr("Synced to Whole Note");
|
||||
break;
|
||||
case TempoSyncKnobModel::SyncMode::HalfNote:
|
||||
m_tempoSyncDescription = tr("Synced to Half Note");
|
||||
break;
|
||||
case TempoSyncKnobModel::SyncMode::QuarterNote:
|
||||
m_tempoSyncDescription = tr("Synced to Quarter Note");
|
||||
break;
|
||||
case TempoSyncKnobModel::SyncMode::EighthNote:
|
||||
m_tempoSyncDescription = tr("Synced to 8th Note");
|
||||
break;
|
||||
case TempoSyncKnobModel::SyncMode::SixteenthNote:
|
||||
m_tempoSyncDescription = tr("Synced to 16th Note");
|
||||
break;
|
||||
case TempoSyncKnobModel::SyncMode::ThirtysecondNote:
|
||||
m_tempoSyncDescription = tr("Synced to 32nd Note");
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
}
|
||||
|
||||
void TempoSyncBarModelEditor::updateIcon()
|
||||
{
|
||||
switch(model()->syncMode())
|
||||
{
|
||||
case TempoSyncKnobModel::SyncMode::None:
|
||||
m_tempoSyncIcon = embed::getIconPixmap("tempo_sync");
|
||||
break;
|
||||
case TempoSyncKnobModel::SyncMode::Custom:
|
||||
m_tempoSyncIcon = embed::getIconPixmap("dont_know");
|
||||
break;
|
||||
case TempoSyncKnobModel::SyncMode::DoubleWholeNote:
|
||||
m_tempoSyncIcon = embed::getIconPixmap("note_double_whole");
|
||||
break;
|
||||
case TempoSyncKnobModel::SyncMode::WholeNote:
|
||||
m_tempoSyncIcon = embed::getIconPixmap("note_whole");
|
||||
break;
|
||||
case TempoSyncKnobModel::SyncMode::HalfNote:
|
||||
m_tempoSyncIcon = embed::getIconPixmap("note_half");
|
||||
break;
|
||||
case TempoSyncKnobModel::SyncMode::QuarterNote:
|
||||
m_tempoSyncIcon = embed::getIconPixmap("note_quarter");
|
||||
break;
|
||||
case TempoSyncKnobModel::SyncMode::EighthNote:
|
||||
m_tempoSyncIcon = embed::getIconPixmap("note_eighth");
|
||||
break;
|
||||
case TempoSyncKnobModel::SyncMode::SixteenthNote:
|
||||
m_tempoSyncIcon = embed::getIconPixmap("note_sixteenth");
|
||||
break;
|
||||
case TempoSyncKnobModel::SyncMode::ThirtysecondNote:
|
||||
m_tempoSyncIcon = embed::getIconPixmap("note_thirtysecond");
|
||||
break;
|
||||
default:
|
||||
qWarning("TempoSyncKnob::calculateTempoSyncTime:"
|
||||
"invalid TempoSyncMode");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace lmms::gui
|
||||
Reference in New Issue
Block a user