Add LcdFloatSpinBox (extracted from microtonal PR) (#5865)

* Add changes extracted from microtonal PR

* Apply suggestions from code review

Co-authored-by: IanCaio <iancaio_dev@hotmail.com>

* Apply suggestions from code review

(adding oneliner spaces)

Co-authored-by: IanCaio <iancaio_dev@hotmail.com>

* Update src/gui/widgets/LcdFloatSpinBox.cpp

Co-authored-by: Hyunjin Song <tteu.ingog@gmail.com>

* Apply suggestions from code review

Co-authored-by: Dominic Clark <mrdomclark@gmail.com>

* Implement suggestions from review

* Reorder constructor parameters, remove old code for cursor movement and hiding from Lcd*SpinBoxes

* Apply suggestions from code review

Co-authored-by: IanCaio <iancaio_dev@hotmail.com>

* Fix right margin width in seamless mode

* Add explanation to border drawing code

Co-authored-by: IanCaio <iancaio_dev@hotmail.com>
Co-authored-by: Hyunjin Song <tteu.ingog@gmail.com>
Co-authored-by: Dominic Clark <mrdomclark@gmail.com>
This commit is contained in:
Martin Pavelek
2021-03-15 18:06:48 +01:00
committed by GitHub
parent 8acb9222fd
commit 17ea61676f
6 changed files with 413 additions and 66 deletions

View File

@@ -75,6 +75,7 @@ SET(LMMS_SRCS
gui/widgets/LeftRightNav.cpp
gui/widgets/Knob.cpp
gui/widgets/LadspaControlView.cpp
gui/widgets/LcdFloatSpinBox.cpp
gui/widgets/LcdSpinBox.cpp
gui/widgets/LcdWidget.cpp
gui/widgets/LedCheckbox.cpp

View File

@@ -0,0 +1,244 @@
/*
* LcdFloatSpinBox.cpp - class LcdFloatSpinBox (LcdSpinBox for floats)
*
* Copyright (c) 2005-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
* Copyright (c) 2008 Paul Giblock <pgllama/at/gmail.com>
* Copyright (c) 2020 Martin Pavelek <he29.HS/at/gmail.com>
*
* 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 "LcdFloatSpinBox.h"
#include <cmath>
#include <QApplication>
#include <QHBoxLayout>
#include <QInputDialog>
#include <QLabel>
#include <QMouseEvent>
#include <QPainter>
#include <QPixmap>
#include <QStyleOptionFrameV2>
#include <QVBoxLayout>
#include "CaptionMenu.h"
#include "embed.h"
#include "GuiApplication.h"
#include "gui_templates.h"
#include "MainWindow.h"
LcdFloatSpinBox::LcdFloatSpinBox(int numWhole, int numFrac, const QString& name, QWidget* parent) :
FloatModelView(new FloatModel(0, 0, 0, 0, nullptr, name, true), this),
m_wholeDisplay(numWhole, parent, name, false),
m_fractionDisplay(numFrac, parent, name, true),
m_mouseMoving(false),
m_intStep(false),
m_origMousePos(),
m_displayOffset(0)
{
layoutSetup();
}
LcdFloatSpinBox::LcdFloatSpinBox(int numWhole, int numFrac, const QString& style, const QString& name, QWidget* parent) :
FloatModelView(new FloatModel(0, 0, 0, 0, nullptr, name, true), this),
m_wholeDisplay(numWhole, style, parent, name, false),
m_fractionDisplay(numFrac, style, parent, name, true),
m_mouseMoving(false),
m_intStep(false),
m_origMousePos(),
m_displayOffset(0)
{
layoutSetup(style);
}
void LcdFloatSpinBox::layoutSetup(const QString &style)
{
// Assemble the LCD parts
QHBoxLayout *lcdLayout = new QHBoxLayout();
m_wholeDisplay.setSeamless(false, true);
m_fractionDisplay.setSeamless(true, false);
lcdLayout->addWidget(&m_wholeDisplay);
QLabel *dotLabel = new QLabel("", this);
QPixmap dotPixmap(embed::getIconPixmap(QString("lcd_" + style + "_dot").toUtf8().constData()));
dotLabel->setPixmap(dotPixmap.copy(0, 0, dotPixmap.size().width(), dotPixmap.size().height() / 2));
lcdLayout->addWidget(dotLabel);
lcdLayout->addWidget(&m_fractionDisplay);
lcdLayout->setContentsMargins(0, 0, 0, 0);
lcdLayout->setSpacing(0);
// Add space for label
QVBoxLayout *outerLayout = new QVBoxLayout();
outerLayout->addLayout(lcdLayout);
outerLayout->addSpacing(9);
outerLayout->setContentsMargins(0, 0, 0, 0);
outerLayout->setSizeConstraint(QLayout::SetFixedSize);
this->setLayout(outerLayout);
}
void LcdFloatSpinBox::update()
{
const int whole = static_cast<int>(model()->value());
const float fraction = model()->value() - whole;
const int intFraction = fraction * std::pow(10.f, m_fractionDisplay.numDigits());
m_wholeDisplay.setValue(whole);
m_fractionDisplay.setValue(intFraction);
QWidget::update();
}
void LcdFloatSpinBox::contextMenuEvent(QContextMenuEvent* event)
{
CaptionMenu contextMenu(model()->displayName());
addDefaultActions(&contextMenu);
contextMenu.exec(QCursor::pos());
}
void LcdFloatSpinBox::mousePressEvent(QMouseEvent* event)
{
if (event->button() == Qt::LeftButton &&
!(event->modifiers() & Qt::ControlModifier) &&
event->y() < m_wholeDisplay.cellHeight() + 2)
{
m_mouseMoving = true;
m_origMousePos = event->globalPos();
AutomatableModel *thisModel = model();
if (thisModel)
{
thisModel->addJournalCheckPoint();
thisModel->saveJournallingState(false);
}
}
else
{
FloatModelView::mousePressEvent(event);
}
}
void LcdFloatSpinBox::mouseMoveEvent(QMouseEvent* event)
{
// switch between integer and fractional step based on cursor position
if (event->x() < m_wholeDisplay.width()) { m_intStep = true; }
else { m_intStep = false; }
if (m_mouseMoving)
{
int dy = event->globalY() - m_origMousePos.y();
if (gui->mainWindow()->isShiftPressed()) { dy = qBound(-4, dy/4, 4); }
if (dy > 1 || dy < -1)
{
model()->setValue(model()->value() - dy / 2 * getStep());
emit manualChange();
m_origMousePos = event->globalPos();
}
}
}
void LcdFloatSpinBox::mouseReleaseEvent(QMouseEvent*)
{
if (m_mouseMoving)
{
model()->restoreJournallingState();
m_mouseMoving = false;
}
}
void LcdFloatSpinBox::wheelEvent(QWheelEvent *event)
{
// switch between integer and fractional step based on cursor position
if (event->x() < m_wholeDisplay.width()) { m_intStep = true; }
else { m_intStep = false; }
event->accept();
model()->setValue(model()->value() + ((event->delta() > 0) ? 1 : -1) * getStep());
emit manualChange();
}
void LcdFloatSpinBox::mouseDoubleClickEvent(QMouseEvent *)
{
enterValue();
}
void LcdFloatSpinBox::enterValue()
{
bool ok;
float newVal;
newVal = QInputDialog::getDouble(
this, tr("Set value"),
tr("Please enter a new value between %1 and %2:").
arg(model()->minValue()).
arg(model()->maxValue()),
model()->value(),
model()->minValue(),
model()->maxValue(),
m_fractionDisplay.numDigits(), &ok);
if (ok)
{
model()->setValue(newVal);
}
}
float LcdFloatSpinBox::getStep() const
{
if (m_intStep) { return 1; }
else { return model()->step<float>(); }
}
void LcdFloatSpinBox::paintEvent(QPaintEvent*)
{
QPainter p(this);
// Border
QStyleOptionFrame opt;
opt.initFrom(this);
opt.state = QStyle::State_Sunken;
opt.rect = QRect(0, 0, width() - 1, m_wholeDisplay.height());
style()->drawPrimitive(QStyle::PE_Frame, &opt, &p, this);
// Label
if (!m_label.isEmpty())
{
p.setFont(pointSizeF(p.font(), 6.5));
p.setPen(m_wholeDisplay.textShadowColor());
p.drawText(width() / 2 - p.fontMetrics().width(m_label) / 2 + 1, height(), m_label);
p.setPen(m_wholeDisplay.textColor());
p.drawText(width() / 2 - p.fontMetrics().width(m_label) / 2, height() - 1, m_label);
}
}

View File

@@ -70,17 +70,11 @@ void LcdSpinBox::update()
void LcdSpinBox::contextMenuEvent( QContextMenuEvent* event )
void LcdSpinBox::contextMenuEvent(QContextMenuEvent* event)
{
// 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( NULL );
CaptionMenu contextMenu( model()->displayName() );
addDefaultActions( &contextMenu );
contextMenu.exec( QCursor::pos() );
CaptionMenu contextMenu(model()->displayName());
addDefaultActions(&contextMenu);
contextMenu.exec(QCursor::pos());
}
@@ -136,12 +130,11 @@ void LcdSpinBox::mouseMoveEvent( QMouseEvent* event )
void LcdSpinBox::mouseReleaseEvent( QMouseEvent* )
void LcdSpinBox::mouseReleaseEvent(QMouseEvent*)
{
if( m_mouseMoving )
if (m_mouseMoving)
{
model()->restoreJournallingState();
QApplication::restoreOverrideCursor();
m_mouseMoving = false;
}
}

View File

@@ -40,28 +40,31 @@
LcdWidget::LcdWidget( QWidget* parent, const QString& name ) :
LcdWidget( 1, parent, name )
LcdWidget::LcdWidget(QWidget* parent, const QString& name, bool leadingZero) :
LcdWidget(1, parent, name, leadingZero)
{
}
LcdWidget::LcdWidget( int numDigits, QWidget* parent, const QString& name ) :
LcdWidget( numDigits, QString("19green"), parent, name )
LcdWidget::LcdWidget(int numDigits, QWidget* parent, const QString& name, bool leadingZero) :
LcdWidget(numDigits, QString("19green"), parent, name, leadingZero)
{
}
LcdWidget::LcdWidget( int numDigits, const QString& style, QWidget* parent, const QString& name ) :
LcdWidget::LcdWidget(int numDigits, const QString& style, QWidget* parent, const QString& name, bool leadingZero) :
QWidget( parent ),
m_label(),
m_textColor( 255, 255, 255 ),
m_textShadowColor( 64, 64, 64 ),
m_numDigits( numDigits )
m_numDigits(numDigits),
m_seamlessLeft(false),
m_seamlessRight(false),
m_leadingZero(leadingZero)
{
initUi( name, style );
}
@@ -77,19 +80,16 @@ LcdWidget::~LcdWidget()
void LcdWidget::setValue( int value )
void LcdWidget::setValue(int value)
{
QString s = m_textForValue[value];
if( s.isEmpty() )
if (s.isEmpty())
{
s = QString::number( value );
// TODO: if pad == true
/*
while( (int) s.length() < m_numDigits )
s = QString::number(value);
if (m_leadingZero)
{
s = "0" + s;
s = s.rightJustified(m_numDigits, '0');
}
*/
}
m_display = s;
@@ -140,15 +140,23 @@ void LcdWidget::paintEvent( QPaintEvent* )
// p.translate( width() / 2 - lcdWidth / 2, 0 );
p.save();
p.translate( margin, margin );
// Don't skip any space and don't draw margin on the left side in seamless mode
if (m_seamlessLeft)
{
p.translate(0, margin);
}
else
{
p.translate(margin, margin);
// Left Margin
p.drawPixmap(
cellRect,
*m_lcdPixmap,
QRect(QPoint(charsPerPixmap * m_cellWidth, isEnabled() ? 0 : m_cellHeight), cellSize)
);
// Left Margin
p.drawPixmap( cellRect, *m_lcdPixmap,
QRect( QPoint( charsPerPixmap*m_cellWidth,
isEnabled()?0:m_cellHeight ),
cellSize ) );
p.translate( m_marginWidth, 0 );
p.translate(m_marginWidth, 0);
}
// Padding
for( int i=0; i < m_numDigits - m_display.length(); i++ )
@@ -177,21 +185,26 @@ void LcdWidget::paintEvent( QPaintEvent* )
}
// Right Margin
p.drawPixmap( QRect( 0, 0, m_marginWidth-1, m_cellHeight ), *m_lcdPixmap,
QRect( charsPerPixmap*m_cellWidth, isEnabled()?0:m_cellHeight,
m_cellWidth / 2, m_cellHeight ) );
p.drawPixmap(QRect(0, 0, m_seamlessRight ? 0 : m_marginWidth - 1, m_cellHeight),
*m_lcdPixmap,
QRect(charsPerPixmap * m_cellWidth, isEnabled() ? 0 : m_cellHeight, m_cellWidth / 2, m_cellHeight));
p.restore();
// Border
QStyleOptionFrame opt;
opt.initFrom( this );
opt.state = QStyle::State_Sunken;
opt.rect = QRect( 0, 0, m_cellWidth * m_numDigits + (margin+m_marginWidth)*2 - 1,
m_cellHeight + (margin*2) );
// When either the left or right edge is seamless, the border drawing must be done
// by the encapsulating class (usually LcdFloatSpinBox).
if (!m_seamlessLeft && !m_seamlessRight)
{
QStyleOptionFrame opt;
opt.initFrom(this);
opt.state = QStyle::State_Sunken;
opt.rect = QRect(0, 0, m_cellWidth * m_numDigits + (margin + m_marginWidth) * 2 - 1,
m_cellHeight + (margin * 2));
style()->drawPrimitive( QStyle::PE_Frame, &opt, &p, this );
style()->drawPrimitive(QStyle::PE_Frame, &opt, &p, this);
}
p.resetTransform();
@@ -235,16 +248,25 @@ void LcdWidget::setMarginWidth( int width )
void LcdWidget::updateSize()
{
int margin = 1;
if (m_label.isEmpty()) {
setFixedSize( m_cellWidth * m_numDigits + 2*(margin+m_marginWidth),
m_cellHeight + (2*margin) );
const int marginX1 = m_seamlessLeft ? 0 : 1 + m_marginWidth;
const int marginX2 = m_seamlessRight ? 0 : 1 + m_marginWidth;
const int marginY = 1;
if (m_label.isEmpty())
{
setFixedSize(
m_cellWidth * m_numDigits + marginX1 + marginX2,
m_cellHeight + (2 * marginY)
);
}
else {
setFixedSize(qMax<int>(
m_cellWidth * m_numDigits + 2*(margin+m_marginWidth),
horizontalAdvance(QFontMetrics(pointSizeF(font(), 6.5)), m_label)),
m_cellHeight + (2*margin) + 9);
else
{
setFixedSize(
qMax<int>(
m_cellWidth * m_numDigits + marginX1 + marginX2,
horizontalAdvance(QFontMetrics(pointSizeF(font(), 6.5)), m_label)
),
m_cellHeight + (2 * marginY) + 9
);
}
update();
@@ -272,7 +294,3 @@ void LcdWidget::initUi(const QString& name , const QString& style)
updateSize();
}