hydrogen 1.2.6
LCDSpinBox.cpp
Go to the documentation of this file.
1/*
2 * Hydrogen
3 * Copyright(c) 2002-2008 by Alex >Comix< Cominu [comix@users.sourceforge.net]
4 * Copyright(c) 2008-2025 The hydrogen development team [hydrogen-devel@lists.sourceforge.net]
5 *
6 * http://www.hydrogen-music.org
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY, without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24#include "LCDSpinBox.h"
25#include "../HydrogenApp.h"
26#include "../Skin.h"
27#include <core/Globals.h>
29
30// used in PlayerControl
31LCDSpinBox::LCDSpinBox( QWidget *pParent, QSize size, Type type, double fMin, double fMax, bool bModifyOnChange, bool bMinusOneAsOff )
32 : QDoubleSpinBox( pParent )
33 , m_size( size )
34 , m_bEntered( false )
35 , m_kind( Kind::Default )
36 , m_bIsActive( true )
37 , m_bModifyOnChange( bModifyOnChange )
38 , m_bMinusOneAsOff( bMinusOneAsOff )
39{
40 setFocusPolicy( Qt::ClickFocus );
41 setLocale( QLocale( QLocale::C, QLocale::AnyCountry ) );
42
43 if ( size.isNull() || size.isEmpty() ) {
44 m_size = sizeHint();
45 }
46 adjustSize();
47 setFixedSize( m_size );
48
49 setType( type );
50
52
53 connect( this, SIGNAL(valueChanged(double)), this,
54 SLOT(valueChanged(double)));
57
58 setMaximum( fMax );
59 setMinimum( fMin );
60 setValue( fMin );
61}
62
65
67 m_type = type;
68
69 if ( type == Type::Int ) {
70 setDecimals( 0 );
71 }
72 else {
73 setDecimals( std::numeric_limits<double>::max_exponent );
74 }
75}
76
77void LCDSpinBox::setSize( QSize size ) {
78 m_size = size;
79
80 setFixedSize( size );
81 adjustSize();
82}
83
84void LCDSpinBox::setIsActive( bool bIsActive ) {
85 m_bIsActive = bIsActive;
86
87 update();
88
89 setEnabled( bIsActive );
90 setReadOnly( ! bIsActive );
91}
92
93void LCDSpinBox::wheelEvent( QWheelEvent *ev ) {
94 static float fCumulatedDelta;
95
96 double fOldValue = value();
97
99
100 // Cumulate scroll positions to provide a native feeling for
101 // fine-grained mouse wheel and touch pads.
102 fCumulatedDelta += ev->angleDelta().y();
103
104 if ( std::fabs( fCumulatedDelta ) >= 120 ) {
105 fCumulatedDelta = 0;
106
107 double fNextValue = nextValueInPatternSizeDenominator( ev->angleDelta().y() > 0, ev->modifiers() == Qt::ControlModifier );
108
109
110 if ( fNextValue == 0 ) {
111 ERRORLOG( QString( "Couldn't find next value for input: %1" ).arg( value() ) );
112 return;
113 }
114
115 if ( ev->angleDelta().y() > 0 ) {
116 setValue( fNextValue + 1 );
117 } else {
118 setValue( fNextValue - 1 );
119 }
120 }
121 } else if ( m_kind == Kind::PatternSizeNumerator ) {
122 QDoubleSpinBox::wheelEvent( ev );
123 if ( value() < 1 ) {
124 setValue( 1 );
125 }
126 } else {
127 QDoubleSpinBox::wheelEvent( ev );
128
129 }
130
131 if ( fOldValue != value() && m_bModifyOnChange ) {
133 }
134}
135
136void LCDSpinBox::keyPressEvent( QKeyEvent *ev ) {
137 double fOldValue = value();
138
139 // Pass Undo/Redo commands up to the parent. In addition, pause and play
140 // button will be passed to the parent too.
141 if ( ev->matches( QKeySequence::StandardKey::Undo )
142 || ev->matches( QKeySequence::StandardKey::Redo )
143 || ev->key() == Qt::Key_Space ) {
144 ev->ignore();
145 return;
146 }
147
149 ( ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down ||
150 ev->key() == Qt::Key_PageUp || ev->key() == Qt::Key_PageDown ) ) {
151 double fNextValue;
152
153 if ( ev->key() == Qt::Key_Up ) {
154 fNextValue = nextValueInPatternSizeDenominator( true, false );
155 }
156 else if ( ev->key() == Qt::Key_Down ) {
157 fNextValue = nextValueInPatternSizeDenominator( false, false );
158 }
159 else if ( ev->key() == Qt::Key_PageUp ) {
160 fNextValue = nextValueInPatternSizeDenominator( true, true );
161 }
162 else if ( ev->key() == Qt::Key_PageDown ) {
163 fNextValue = nextValueInPatternSizeDenominator( false, true );
164 }
165
166 if ( fNextValue == 0 ) {
167 ERRORLOG( QString( "Couldn't find next value for input: %1" ).arg( value() ) );
168 return;
169 }
170
171 setValue( fNextValue );
172
173 QDoubleSpinBox::keyPressEvent( ev );
174 }
175 else if ( m_kind == Kind::PatternSizeNumerator ) {
176
177 QDoubleSpinBox::keyPressEvent( ev );
178
179 if ( value() < 1 ) {
180 setValue( 1 );
181 }
182 }
183 else {
184 QDoubleSpinBox::keyPressEvent( ev );
185 }
186
187 if ( fOldValue != value() && m_bModifyOnChange ) {
189 }
190}
191
192double LCDSpinBox::nextValueInPatternSizeDenominator( bool bUp, bool bAccelerated ) {
193
194 // Determine the next value.
195 std::vector vChoices{ 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 192 };
196
197 double fNextValue = 1.0;
198 double fOffset = 0.0;
199
200 if ( bAccelerated ) {
201 fOffset = 10;
202 } else {
203 fOffset = 1;
204 }
205 for ( int ii = 0; ii < vChoices.size(); ii++ ) {
206 if ( vChoices[ ii ] == value() ) {
207
208 if ( bUp ) {
209 if ( ii < vChoices.size() - 1 ) {
210 fNextValue = vChoices[ ii + 1 ] - fOffset;
211 } else {
212 fNextValue = vChoices[ ii ] - fOffset;
213 }
214 if ( fNextValue < 0 ) {
215 fNextValue = 2;
216 }
217 } else {
218 if ( ii > 0 ) {
219 fNextValue = vChoices[ ii - 1 ] + fOffset;
220 } else {
221 fNextValue = vChoices[ ii ] + fOffset;
222 }
223 }
224 }
225 }
226
227 return fNextValue;
228}
229
230QString LCDSpinBox::textFromValue( double fValue ) const {
231 QString result;
232 if ( m_type == Type::Int && m_bMinusOneAsOff &&
233 fValue == -1.0 ) {
234 result = "off";
235 } else {
236 if ( m_type == Type::Int ) {
237 result = QString( "%1" ).arg( fValue, 0, 'f', 0 );
238 }
239 else {
240 result = QString( "%1" ).arg( fValue ) ;
241 }
242 }
243
244 return result;
245}
246
247QValidator::State LCDSpinBox::validate( QString &text, int &pos ) const {
249 std::vector vChoices{ "1", "2", "3", "4", "6", "8", "12", "16", "24", "32", "48", "64", "96", "192" };
250 std::vector vCandidates1{ "1", "2", "3", "4", "6", "9" };
251 QString sCandidate2( "19" );
252 bool bContained = false;
253 bool bIsCandidate = false;
254 for ( const auto& ii : vChoices ) {
255 if ( ii == text ) {
256 bContained = true;
257 }
258 }
259 for ( const auto& ii : vCandidates1 ) {
260 if ( ii == text.left( 1 ) ) {
261 bIsCandidate = true;
262 }
263 }
264 if ( sCandidate2 == text.left( 2 ) ) {
265 bIsCandidate = true;
266 }
267
268 if ( bContained ) {
269 return QValidator::Acceptable;
270 } else if ( bIsCandidate ) {
271 return QValidator::Intermediate;
272 } else {
273 return QValidator::Invalid;
274 }
275 }
276 return QDoubleSpinBox::validate( text, pos );
277}
278
279double LCDSpinBox::valueFromText( const QString& sText ) const {
280
281 double fResult;
282
283 if ( sText == "off" ){
284 fResult = -1.0;
285 } else {
286 fResult = QDoubleSpinBox::valueFromText( sText );
287 }
288
289 return fResult;
290}
291
292void LCDSpinBox::setValue( double fValue ) {
293 if ( value() == fValue ) {
294 return;
295 }
296
297 QDoubleSpinBox::setValue( fValue );
298}
299
300bool LCDSpinBox::event( QEvent* ev ) {
301
302 if ( ev->type() == QEvent::KeyPress && dynamic_cast<QKeyEvent*>( ev)->key() == Qt::Key_Slash ) {
303 emit slashKeyPressed();
304 return 0;
305 }
306
307 return QDoubleSpinBox::event( ev );
308}
309
310void LCDSpinBox::mousePressEvent( QMouseEvent* ev ) {
311 double fOldValue = value();
312
313 QDoubleSpinBox::mousePressEvent( ev );
314
315 if ( fOldValue != value() && m_bModifyOnChange ) {
317 }
318}
319
320void LCDSpinBox::mouseMoveEvent( QMouseEvent* ev ) {
321 double fOldValue = value();
322
323 QDoubleSpinBox::mouseMoveEvent( ev );
324
325 if ( fOldValue != value() && m_bModifyOnChange ) {
327 }
328}
329
330void LCDSpinBox::mouseReleaseEvent( QMouseEvent* ev ) {
331 double fOldValue = value();
332
333 QDoubleSpinBox::mouseReleaseEvent( ev );
334
335 if ( fOldValue != value() && m_bModifyOnChange ) {
337 }
338}
339
340void LCDSpinBox::paintEvent( QPaintEvent *ev ) {
341
343
344 QDoubleSpinBox::paintEvent( ev );
345
346 if ( m_bEntered || hasFocus() ) {
347 QPainter painter(this);
348
349
350 QColor colorHighlightActive;
351 if ( m_bIsActive ) {
352 colorHighlightActive = pPref->getColorTheme()->m_highlightColor;
353 } else {
354 colorHighlightActive = pPref->getColorTheme()->m_lightColor;
355 }
356
357 // If the mouse is placed on the widget but the user hasn't
358 // clicked it yet, the highlight will be done more transparent to
359 // indicate that keyboard inputs are not accepted yet.
360 if ( ! hasFocus() ) {
361 colorHighlightActive.setAlpha( 150 );
362 }
363
364 QPen pen;
365 pen.setColor( colorHighlightActive );
366 pen.setWidth( 3 );
367 painter.setPen( pen );
368 painter.drawRoundedRect( QRect( 0, 0, m_size.width(), m_size.height() ), 3, 3 );
369 }
370}
371
372#ifdef H2CORE_HAVE_QT6
373void LCDSpinBox::enterEvent( QEnterEvent *ev ) {
374#else
375void LCDSpinBox::enterEvent( QEvent *ev ) {
376#endif
377 QDoubleSpinBox::enterEvent( ev );
378 m_bEntered = true;
379}
380
381void LCDSpinBox::leaveEvent( QEvent* ev ) {
382 QDoubleSpinBox::leaveEvent( ev );
383 m_bEntered = false;
384}
385
387
389
390 QColor spinBoxColor = pPref->getColorTheme()->m_spinBoxColor;
391 QColor spinBoxTextColor = pPref->getColorTheme()->m_spinBoxTextColor;
392 QColor selectionColor = spinBoxColor.darker( 120 );
393
394 QColor spinBoxInactiveColor =
395 Skin::makeWidgetColorInactive( spinBoxColor );
396 QColor spinBoxTextInactiveColor =
397 Skin::makeTextColorInactive( spinBoxTextColor );
398 QColor selectionInactiveColor =
399 Skin::makeWidgetColorInactive( selectionColor );
400
401
402 setStyleSheet( QString( "\
403QAbstractSpinBox:enabled { \
404 color: %1; \
405 background-color: %2; \
406 selection-color: %1; \
407 selection-background-color: %3; \
408} \
409QAbstractSpinBox:disabled { \
410 color: %4; \
411 background-color: %5; \
412 selection-color: %4; \
413 selection-background-color: %6; \
414}" )
415 .arg( spinBoxTextColor.name() )
416 .arg( spinBoxColor.name() )
417 .arg( selectionColor.name() )
418 .arg( spinBoxTextInactiveColor.name() )
419 .arg( spinBoxInactiveColor.name() )
420 .arg( selectionInactiveColor.name() ) );
421}
422
429
430void LCDSpinBox::valueChanged( double fNewValue ) {
431 if ( m_type == Type::Int ) {
432 emit valueChanged( static_cast<int>(fNewValue) );
433 }
434}
#define ERRORLOG(x)
Definition Object.h:242
static Hydrogen * get_instance()
Returns the current Hydrogen instance __instance.
Definition Hydrogen.h:84
void setIsModified(bool bIsModified)
Wrapper around Song::setIsModified() that checks whether a song is set.
static Preferences * get_instance()
Returns a pointer to the current Preferences singleton stored in __instance.
Changes
Bitwise or-able options showing which part of the Preferences were altered using the PreferencesDialo...
@ Colors
At least one of the colors has changed.
static HydrogenApp * get_instance()
Returns the instance of HydrogenApp class.
void preferencesChanged(H2Core::Preferences::Changes changes)
Propagates a change in the Preferences through the GUI.
void valueChanged(double fNewValue)
virtual void mouseMoveEvent(QMouseEvent *ev) override
double nextValueInPatternSizeDenominator(bool bUp, bool bAccelerated)
virtual void mousePressEvent(QMouseEvent *ev) override
virtual double valueFromText(const QString &sText) const override
QSize m_size
Definition LCDSpinBox.h:103
void setValue(double fValue)
void setType(Type type)
void onPreferencesChanged(H2Core::Preferences::Changes changes)
LCDSpinBox(QWidget *pParent, QSize size=QSize(), Type type=Type::Int, double fMin=0.0, double fMax=1.0, bool bModifyOnChange=false, bool bMinusOneAsOff=false)
bool m_bModifyOnChange
Whether Hydrogen::setIsModified() is invoked with true as soon as the value of the widget does change...
Definition LCDSpinBox.h:116
virtual QValidator::State validate(QString &text, int &pos) const override
bool m_bMinusOneAsOff
In some widgets the QString "off" will be displayed instead of -1.
Definition LCDSpinBox.h:112
virtual void leaveEvent(QEvent *ev) override
virtual void wheelEvent(QWheelEvent *ev) override
virtual void keyPressEvent(QKeyEvent *ev) override
virtual void mouseReleaseEvent(QMouseEvent *ev) override
void updateStyleSheet()
void setSize(QSize size)
virtual bool event(QEvent *ev) override
@ Default
Behaves like QDoubleSpinBox.
Definition LCDSpinBox.h:63
@ PatternSizeNumerator
The minimum value - a fractional one - can only be reached by entering it using the keyboard.
Definition LCDSpinBox.h:68
@ PatternSizeDenominator
Only a limited number of values is allowed.
Definition LCDSpinBox.h:70
bool m_bIsActive
Definition LCDSpinBox.h:108
virtual QString textFromValue(double fValue) const override
virtual void paintEvent(QPaintEvent *ev) override
void setIsActive(bool bIsActive)
void slashKeyPressed()
virtual void enterEvent(QEvent *ev) override
bool m_bEntered
Definition LCDSpinBox.h:107
static QColor makeTextColorInactive(QColor color)
If a widget is marked inactive the value of its text color are reduced by this factor.
Definition Skin.cpp:176
static QColor makeWidgetColorInactive(QColor color)
If a widget is marked inactive the value of its background color are reduced by this factor.
Definition Skin.cpp:167