Skip to content
127 changes: 107 additions & 20 deletions include/FloatModelEditorBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
#ifndef LMMS_GUI_FLOAT_MODEL_EDITOR_BASE_H
#define LMMS_GUI_FLOAT_MODEL_EDITOR_BASE_H

#include <QWidget>
#include <QPoint>
#include <QWidget>
#include <optional>

#include "AutomatableModelView.h"
Expand Down Expand Up @@ -61,6 +61,34 @@ class LMMS_EXPORT FloatModelEditorBase : public QWidget, public FloatModelView
setUnit(txt_after);
}

/**
* Sets the tooltip displayed when the mouse hovers over the control.
*
* Unlike the dynamic floating text from getDynamicFloatingText() which represents the
* current value of the model, this is static text intended to provide a helpful description
* of the control. That is, it's just a traditional tooltip, though it uses SimpleTextFloat
* rather than QWidget's own tooltip for consistency with the dynamic floating text.
*
* If no static tooltip is set (when this method is not called), dynamic floating text
* is used in its place. See @ref InteractionType for more information.
*
* @param tip the static tooltip. If empty, neither a static nor dynamic tooltip will be
* displayed when the mouse hovers over the control.
*/
void setToolTip(const QString& tip)
{
m_staticToolTip.emplace(tip);
}

QString toolTip() const { return m_staticToolTip.value_or(QString{}); }

/**
* Removes the static tooltip set by a previous call to setToolTip().
* The dynamic floating text will be used in its place.
* NOTE: This is currently unused.
*/
void unsetToolTip() { m_staticToolTip.reset(); }

signals:
void sliderPressed();
void sliderReleased();
Expand Down Expand Up @@ -88,42 +116,51 @@ class LMMS_EXPORT FloatModelEditorBase : public QWidget, public FloatModelView
virtual float getValue(const QPoint & p);

/**
* This method is called just prior to displaying the floating text
* in order to set its value. If the getCustomFloatingTextUpdate() method
* This method is called just prior to displaying dynamic floating text
* in order to set its value. If the getDynamicFloatingTextUpdate() method
* is not overridden, this method is also called to periodically update
* the floating text.
*
* Floating text is displayed in the following format:
* "[description] [custom text][unit]"
* Dynamic floating text is displayed in the following format:
* "[description] [dynamic text][unit]"
*
* This method controls only the "custom text" portion.
* This method controls only the "dynamic text" portion.
* To modify the other portions, call setDescription() or setUnit().
*/
virtual QString getCustomFloatingText();
virtual QString getDynamicFloatingText();

/**
* This method is called periodically while the floating text is visible
* This method is called periodically while dynamic floating text is visible
* and the value of the float model is changing, allowing dynamic updates
* of the floating text.
*
* Floating text is displayed in the following format:
* "[description] [custom text][unit]"
* Dynamic floating text is displayed in the following format:
* "[description] [dynamic text][unit]"
*
* This method controls only the "custom text" portion.
* This method controls only the "dynamic text" portion.
* To modify the other portions, call setDescription() or setUnit().
*
* @returns the up-to-date value for the floating text, or std::nullopt to indicate
* nothing changed and the previous floating text value should continue being used
* @returns the up-to-date value for the dynamic floating text, or std::nullopt to
* indicate the previous floating text value should continue being used
*/
virtual std::optional<QString> getCustomFloatingTextUpdate()
virtual std::optional<QString> getDynamicFloatingTextUpdate()
{
return getCustomFloatingText();
return getDynamicFloatingText();
}

/**
* @returns formatted floating text given dynamic text
* from @a getDynamicFloatingText() or @a getDynamicFloatingTextUpdate()
*
* The default format is:
* "[description] [dynamic text][unit]"
*/
virtual QString formatFloatingText(const QString& dynamicText) const;

void doConnections() override;

void showTextFloat(int msecBeforeDisplay, int msecDisplayTime);
void showTextFloat();
void showTextFloat(int msecBeforeDisplay, int msecDisplayTime, bool forceTextUpdate = false);
void showTextFloat(bool forceTextUpdate = false);

const SimpleTextFloat& textFloat() const { return *s_textFloat; }

Expand All @@ -134,18 +171,68 @@ class LMMS_EXPORT FloatModelEditorBase : public QWidget, public FloatModelView
return (model()->maxValue() - model()->minValue()) / 100.0f;
}

DirectionOfManipulation directionOfManipulation() const { return m_directionOfManipulation; }

//! Types of user interaction with the control
enum class InteractionType : std::uint8_t
{
//! The user is not interacting with the control.
//! No floating text is shown.
None,

//! The mouse is hovering over the control without any other interaction.
//! If a static tooltip is set (@see setToolTip), it will be displayed,
//! otherwise dynamic floating text will be displayed.
MouseHover,

//! The user is dragging the control to adjust the model's value.
//! This always results in dynamic floating text, not a static tooltip.
MouseDrag,

//! The user is using the mouse wheel on the control to adjust the model's value.
//! This always results in dynamic floating text, not a static tooltip.
MouseWheel
};

//! @returns how the user is interacting with the control
InteractionType currentInteraction() const { return m_interaction; }

//! Updates m_interaction based on the event and current state
void updateInteractionState(QEvent* event);

enum class FloatingTextType : std::uint8_t
{
//! No floating text
None,

//! Traditional static tooltip
Static,

//! Dynamic floating text
Dynamic
};

/**
* @returns which type of floating text is currently being displayed based on how the user
* is interacting with the control and whether a static tooltip has been set for the control.
*/
FloatingTextType floatingTextType() const;

QPoint m_lastMousePos; //!< mouse position in last mouseMoveEvent
float m_leftOver;
bool m_buttonPressed;

DirectionOfManipulation m_directionOfManipulation;

private slots:
virtual void enterValue();
void friendlyUpdate();
void toggleScale();

private:
InteractionType m_interaction = InteractionType::None;

DirectionOfManipulation m_directionOfManipulation;

std::optional<QString> m_staticToolTip;

static SimpleTextFloat* s_textFloat;
};

Expand Down
30 changes: 26 additions & 4 deletions include/Knob.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,21 +236,43 @@ class LMMS_EXPORT Knob : public FloatModelEditorBase
};


/**
* Volume knob specialization
*
* Notes:
* - The units displayed in the tooltip and the @a enterValue() dialog box are hardcoded to dBFS,
* but units shown in the context menu must be set via @a setUnit(). Usually this should be "%".
* - The model is expected to be linearly scaled. (?)
* - The model's value of 0 must mean -inf dBFS and the
* value @a zeroDbfsPoint() (whose default is 100) must mean 0 dBFS.
* - Models with both positive and negative values are allowed. A negative value is assumed to have
* the same effect as its corresponding positive value, but with inverted phase.
* See Flanger's feedback knob for an example.
*/
class LMMS_EXPORT VolumeKnob : public Knob
{
Q_OBJECT

mapPropertyFromModel(float, volumeRatio, setVolumeRatio, m_volumeRatio);

public:
using Knob::Knob;

void setModel(Model* model, bool isOldModelValid = true) override;

//! The value where the volume model is at 0 dBFS (default is 100)
auto zeroDbfsPoint() const -> float { return m_zeroDbfsPoint; }
void setZeroDbfsPoint(float zeroDbfsPoint)
{
assert(zeroDbfsPoint > 0);
m_zeroDbfsPoint = zeroDbfsPoint;
}

protected:
QString getCustomFloatingText() override;
QString getDynamicFloatingText() override;
QString formatFloatingText(const QString& dynamicText) const;
void enterValue() override;

private:
FloatModel m_volumeRatio{100.f, 0.f, 1000000.f};
float m_zeroDbfsPoint = 100.f;
};


Expand Down
11 changes: 6 additions & 5 deletions plugins/Delay/DelayControlsDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,29 +48,30 @@ DelayControlsDialog::DelayControlsDialog( DelayControls *controls ) :
auto sampleDelayKnob = new TempoSyncKnob(KnobType::Bright26, tr("DELAY"), this, Knob::LabelRendering::LegacyFixedFontSize);
sampleDelayKnob->move( 10,14 );
sampleDelayKnob->setModel( &controls->m_delayTimeModel );
sampleDelayKnob->setHintText( tr( "Delay time" ) + " ", " s" );
sampleDelayKnob->setHintText(tr("Delay time:"), " s");
Comment thread
messmerd marked this conversation as resolved.

auto feedbackKnob = new VolumeKnob(KnobType::Bright26, tr("FDBK"), this, Knob::LabelRendering::LegacyFixedFontSize);
feedbackKnob->move( 11, 58 );
feedbackKnob->setZeroDbfsPoint(1.f);
feedbackKnob->setModel( &controls->m_feedbackModel);
feedbackKnob->setHintText( tr ( "Feedback amount" ) + " " , "" );
feedbackKnob->setHintText(tr("Feedback amount:"), "");

auto lfoFreqKnob = new TempoSyncKnob(KnobType::Bright26, tr("RATE"), this, Knob::LabelRendering::LegacyFixedFontSize);
lfoFreqKnob->move( 11, 119 );
lfoFreqKnob->setModel( &controls->m_lfoTimeModel );
lfoFreqKnob->setHintText( tr ( "LFO frequency") + " ", " s" );
lfoFreqKnob->setHintText(tr("LFO frequency:"), " s");

auto lfoAmtKnob = new TempoSyncKnob(KnobType::Bright26, tr("AMNT"), this, Knob::LabelRendering::LegacyFixedFontSize);
lfoAmtKnob->move( 11, 159 );
lfoAmtKnob->setModel( &controls->m_lfoAmountModel );
lfoAmtKnob->setHintText( tr ( "LFO amount" ) + " " , " s" );
lfoAmtKnob->setHintText(tr("LFO amount:"), " s");

auto outFader
= new EqFader(&controls->m_outGainModel, tr("Out gain"), this, &controls->m_outPeakL, &controls->m_outPeakR);
outFader->setMaximumHeight( 196 );
outFader->move( 263, 45 );
outFader->setDisplayConversion( false );
outFader->setHintText( tr( "Gain" ), "dBFS" );
outFader->setHintText(tr("Gain:"), " dBFS");

auto pad = new XyPad(this, &controls->m_feedbackModel, &controls->m_delayTimeModel);
pad->resize( 200, 200 );
Expand Down
4 changes: 2 additions & 2 deletions plugins/DynamicsProcessor/DynamicsProcessorControlDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,13 @@ DynProcControlDialog::DynProcControlDialog(
waveGraph -> setMaximumSize( 204, 205 );

auto inputKnob = new VolumeKnob(KnobType::Bright26, tr("INPUT"), SMALL_FONT_SIZE, this);
inputKnob->setVolumeRatio(1.0);
inputKnob->setZeroDbfsPoint(1.f);
inputKnob -> move( 26, 223 );
inputKnob->setModel( &_controls->m_inputModel );
inputKnob->setHintText( tr( "Input gain:" ) , "" );

auto outputKnob = new VolumeKnob(KnobType::Bright26, tr("OUTPUT"), SMALL_FONT_SIZE, this);
outputKnob->setVolumeRatio(1.0);
outputKnob->setZeroDbfsPoint(1.f);
outputKnob -> move( 76, 223 );
outputKnob->setModel( &_controls->m_outputModel );
outputKnob->setHintText( tr( "Output gain:" ) , "" );
Expand Down
2 changes: 2 additions & 0 deletions plugins/Flanger/FlangerControlsDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,12 @@ FlangerControlsDialog::FlangerControlsDialog( FlangerControls *controls ) :
lfoPhaseKnob->setHintText( tr( "Phase:" ) , " degrees" );

auto feedbackKnob = new VolumeKnob(KnobType::Bright26, tr("FDBK"), this);
feedbackKnob->setZeroDbfsPoint(1.f);
feedbackKnob->setModel( &controls->m_feedbackModel );
feedbackKnob->setHintText( tr( "Feedback amount:" ) , "" );

auto whiteNoiseKnob = new VolumeKnob(KnobType::Bright26, tr("NOISE"), this);
whiteNoiseKnob->setZeroDbfsPoint(1.f);
whiteNoiseKnob->setModel( &controls->m_whiteNoiseAmountModel );
whiteNoiseKnob->setHintText( tr( "White noise amount:" ) , "" );

Expand Down
2 changes: 1 addition & 1 deletion plugins/Vibed/Vibed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ VibedView::VibedView(Instrument* instrument, QWidget* parent) :
setPalette(pal);

m_volumeKnob.move(103, 142);
m_volumeKnob.setHintText(tr("String volume:"), "");
m_volumeKnob.setHintText(tr("String volume:"), "%");

m_stiffnessKnob.move(129, 142);
m_stiffnessKnob.setHintText(tr("String stiffness:"), "");
Expand Down
6 changes: 3 additions & 3 deletions plugins/VstBase/VstPlugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -892,7 +892,7 @@ void VstPluginKnob::timerEvent(QTimerEvent* event)
m_updateNow = true;
}

auto VstPluginKnob::getCustomFloatingText() -> QString
auto VstPluginKnob::getDynamicFloatingText() -> QString
{
constexpr auto updatesPerSecond = 15;

Expand All @@ -907,7 +907,7 @@ auto VstPluginKnob::getCustomFloatingText() -> QString
return getParameterText();
}

auto VstPluginKnob::getCustomFloatingTextUpdate() -> std::optional<QString>
auto VstPluginKnob::getDynamicFloatingTextUpdate() -> std::optional<QString>
{
if (!m_updateNow) { return std::nullopt; }
m_updateNow = false;
Expand All @@ -924,7 +924,7 @@ auto VstPluginKnob::getParameterText() const -> QString
const auto& paramDisplays = m_plugin->allParameterDisplays();
assert(paramLabels.size() == paramDisplays.size());

assert(m_paramIndex < paramLabels.size());
assert(static_cast<std::size_t>(m_paramIndex) < paramLabels.size());
return paramDisplays[m_paramIndex] + ' ' + paramLabels[m_paramIndex];
}

Expand Down
4 changes: 2 additions & 2 deletions plugins/VstBase/VstPlugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,8 @@ class VSTBASE_EXPORT VstPluginKnob : public Knob
private:
void timerEvent(QTimerEvent* event) override;

auto getCustomFloatingText() -> QString override;
auto getCustomFloatingTextUpdate() -> std::optional<QString> override;
auto getDynamicFloatingText() -> QString override;
auto getDynamicFloatingTextUpdate() -> std::optional<QString> override;

auto getParameterText() const -> QString;

Expand Down
4 changes: 2 additions & 2 deletions plugins/WaveShaper/WaveShaperControlDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,13 @@ WaveShaperControlDialog::WaveShaperControlDialog(
waveGraph -> setMaximumSize( 204, 205 );

auto inputKnob = new VolumeKnob(KnobType::Bright26, tr("INPUT"), SMALL_FONT_SIZE, this);
inputKnob->setVolumeRatio(1.0);
inputKnob->setZeroDbfsPoint(1.f);
inputKnob -> move( 26, 225 );
inputKnob->setModel( &_controls->m_inputModel );
inputKnob->setHintText( tr( "Input gain:" ) , "" );

auto outputKnob = new VolumeKnob(KnobType::Bright26, tr("OUTPUT"), SMALL_FONT_SIZE, this);
outputKnob->setVolumeRatio(1.0);
outputKnob->setZeroDbfsPoint(1.f);
outputKnob -> move( 76, 225 );
outputKnob->setModel( &_controls->m_outputModel );
outputKnob->setHintText( tr( "Output gain:" ), "" );
Expand Down
2 changes: 1 addition & 1 deletion src/gui/tracks/SampleTrackView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) :

m_volumeKnob = new VolumeKnob(KnobType::Small17, tr("VOL"), getTrackSettingsWidget(), Knob::LabelRendering::LegacyFixedFontSize, tr("Track volume"));
m_volumeKnob->setModel( &_t->m_volumeModel );
m_volumeKnob->setHintText( tr( "Channel volume:" ), "%" );
m_volumeKnob->setHintText(tr("Volume:"), "%");
m_volumeKnob->show();

m_panningKnob = new Knob(KnobType::Small17, tr("PAN"), getTrackSettingsWidget(), Knob::LabelRendering::LegacyFixedFontSize, tr("Panning"));
Expand Down
11 changes: 6 additions & 5 deletions src/gui/widgets/Draggable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,12 @@ void Draggable::paintEvent(QPaintEvent* event)

void Draggable::mouseMoveEvent(QMouseEvent* me)
{
QPoint pPos = mapToParent(me->pos());
updateInteractionState(me);

if (m_buttonPressed && pPos != m_lastMousePos)
QPoint pPos = mapToParent(me->pos());
if (currentInteraction() == InteractionType::MouseDrag && pPos != m_lastMousePos)
{
float point = (m_directionOfManipulation == DirectionOfManipulation::Vertical) ? pPos.y() : pPos.x();
float point = (directionOfManipulation() == DirectionOfManipulation::Vertical) ? pPos.y() : pPos.x();
float progress = (point - m_pointA) / (m_pointB - m_pointA);

if (progress >= 0 && progress <= 1)
Expand All @@ -115,11 +116,11 @@ void Draggable::mouseMoveEvent(QMouseEvent* me)
void Draggable::handleMovement()
{
float newCoord = std::lerp(m_pointA, m_pointB, (model()->value() - model()->minValue()) / (model()->maxValue() - model()->minValue()));
if (m_directionOfManipulation == DirectionOfManipulation::Vertical)
if (directionOfManipulation() == DirectionOfManipulation::Vertical)
{
move(x(), newCoord - m_pixmap.height() / 2.f);
}
else if (m_directionOfManipulation == DirectionOfManipulation::Horizontal)
else if (directionOfManipulation() == DirectionOfManipulation::Horizontal)
{
move(newCoord - m_pixmap.width() / 2.f, y());
}
Expand Down
Loading
Loading