hydrogen 1.2.6
Rotary.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, see https://www.gnu.org/licenses
20 *
21 */
22#include "Rotary.h"
23#include "../Skin.h"
24#include "../HydrogenApp.h"
25#include "../MainForm.h"
26
27#include <cmath>
28
29#include <QFile>
30#include <QSvgRenderer>
31
32#include <core/Globals.h>
33
34Rotary::Rotary( QWidget* parent, Type type, QString sBaseTooltip, bool bUseIntSteps, float fMin, float fMax, bool bModifyOnChange )
35 : WidgetWithInput( parent,
36 bUseIntSteps,
37 sBaseTooltip,
38 1, //nScrollSpeed,
39 5, // nScrollSpeedFast,
40 fMin,
41 fMax,
42 bModifyOnChange )
43 , m_type( type ) {
44
47
48 installEventFilter( HydrogenApp::get_instance()->getMainForm() );
49
50 if ( type == Type::Small ) {
51 m_nWidgetWidth = 18;
52 m_nWidgetHeight = 18;
53 } else {
54 m_nWidgetWidth = 44;
55 m_nWidgetHeight = 26;
56 }
57
58 if ( bUseIntSteps ) {
59 m_fDefaultValue = static_cast<int>( type == Type::Center ? ( m_fMin + ( m_fMax - m_fMin ) / 2.0 ) : m_fMin );
60 }
61 else {
62 m_fDefaultValue = ( type == Type::Center ? ( m_fMin + ( m_fMax - m_fMin ) / 2.0 ) : m_fMin );
63 }
64
67
68 // Since the load function does not report success, we will check
69 // for the existence of the knob image separately.
70 QString sKnobPath( Skin::getSvgImagePath() + "/rotary.svg" );
71 QFile knobFile( sKnobPath );
72 if ( knobFile.exists() ) {
73 m_knob = new QSvgRenderer( sKnobPath, this );
74 } else {
75 m_knob = nullptr;
76 ERRORLOG( QString( "Unable to load knob image [%1]" ).arg( sKnobPath ) );
77 }
78
79 QString sBackgroundPath( Skin::getSvgImagePath() + "/rotary_background.svg" );
80 QFile backgroundFile( sBackgroundPath );
81 if ( backgroundFile.exists() ) {
82 m_background = new QSvgRenderer( sBackgroundPath, this );
83 } else {
84 m_background = nullptr;
85 ERRORLOG( QString( "Unable to load background image [%1]" ).arg( sBackgroundPath ) );
86 }
87
89}
90
91Rotary::~ Rotary() {
92}
93
94void Rotary::paintEvent( QPaintEvent* ev )
95{
96
98
99 ev->accept();
100 QPainter painter( this );
101
102 painter.setRenderHint( QPainter::Antialiasing, true );
103
104 QColor colorHighlightActive;
105 QColor colorArc;
106 QColor colorArcCenterSet;
107 QColor colorArcCenterUnset;
108 if ( m_bIsActive ) {
109 colorHighlightActive = pPref->getColorTheme()->m_highlightColor;
110 colorArc = Qt::red;
111 colorArcCenterSet = Qt::green;
112 colorArcCenterUnset = Qt::gray;
113 } else {
114 colorHighlightActive = pPref->getColorTheme()->m_lightColor;
115 colorArc = Qt::darkGray;
116 colorArcCenterSet = Qt::darkGray;
117 colorArcCenterUnset = Qt::lightGray;
118 }
119 QColor colorHandle = Qt::black;
120 QColor colorFont = Qt::white;
121
122 // If the mouse is placed on the widget but the user hasn't
123 // clicked it yet, the highlight will be done more transparent to
124 // indicate that keyboard inputs are not accepted yet.
125 if ( ! hasFocus() ) {
126 colorHighlightActive.setAlpha( 150 );
127 }
128
129 QRect rectBackground( 0, 0, m_nWidgetWidth, m_nWidgetHeight );
130 // Contains both the painted arc and the actual SVG image of the
131 // knob.
132 QRect rectRotary, rectArc;
133 float fArcLineWidth, fBlackMargin;
134 if ( m_type != Type::Small ) {
135 // Center the rotary while using the full height of the widget.
136 fArcLineWidth = 2.0;
137 rectArc = QRect( ( m_nWidgetWidth - m_nWidgetHeight + fArcLineWidth ) * 0.5,
138 fArcLineWidth / 2,
139 m_nWidgetHeight - fArcLineWidth,
140 m_nWidgetHeight - fArcLineWidth );
141 rectRotary = QRect( rectArc.x() + fArcLineWidth / 2,
142 rectArc.y() + fArcLineWidth / 2,
143 rectArc.width() - fArcLineWidth,
144 rectArc.height() - fArcLineWidth );
145 }
146
147 if ( m_background != nullptr && m_type != Type::Small ) {
148 m_background->render( &painter, rectBackground );
149 }
150
151 if ( m_bEntered || hasFocus() ) {
152 painter.fillRect( 0, m_nWidgetHeight - 2, m_nWidgetWidth, 2, colorHighlightActive );
153 }
154
155 if ( m_knob != nullptr ) {
156 if ( m_type == Type::Small ) {
157 m_knob->render( &painter, rectBackground );
158 } else {
159 m_knob->render( &painter, "layer2", rectRotary );
160 }
161 }
162
163 if ( m_bIsActive ) {
164 if ( m_type != Type::Small ) {
165 if ( m_type == Type::Normal ) {
166 int nStartAngle = 210 * 16; // given in 1/16 of a degree
167 int nSpanAngle = static_cast<int>( -239 * 16 * ( m_fValue - m_fMin ) / ( m_fMax - m_fMin ) );
168
169 painter.setPen( QPen( colorArc, fArcLineWidth ) );
170 painter.drawArc( rectArc, nStartAngle, nSpanAngle );
171 } else {
172 // Type::Center
173
174 // There will be a special indication of the
175 // center. Either as a gray dot or a bigger green one if
176 // the value is smaller than +/-1% of the range around 0.
177 if ( ( m_fValue - 0.5 * ( m_fMax + m_fMin ) ) == 0 ) {
178
179 painter.setPen( QPen( colorArcCenterSet, fArcLineWidth * 1.25 ) );
180 painter.drawArc( rectArc, 91 * 16, -3 * 16 );
181
182 } else {
183
184 painter.setPen( QPen( colorArcCenterUnset, fArcLineWidth * 1.25 ) );
185 painter.drawArc( rectArc, 91 * 16, -3 * 16 );
186
187 int nStartAngle = -18 * 16;
188 int nSpanAngle = static_cast<int>( -200* 16 * ( m_fValue - 0.5 * ( m_fMax + m_fMin ) ) / ( m_fMax - m_fMin ) );
189 if ( m_fValue - 0.5 * ( m_fMax + m_fMin ) < 0 ) {
190 nStartAngle *= -1;
191 nStartAngle -= 2 * 16;
192 }
193 nStartAngle += 90 * 16;
194
195 painter.setPen( QPen( colorArc, fArcLineWidth ) );
196 painter.drawArc( rectArc, nStartAngle, nSpanAngle );
197 }
198 }
199 }
200 }
201
202 float fCurrentAngle;
203 float fStartAngle;
204 if ( m_type == Type::Center ) {
205 fStartAngle = -90 * M_PI / 180;
206 fCurrentAngle = fStartAngle + 255 * M_PI / 180 * ( m_fValue - 0.5 * ( m_fMax + m_fMin ) ) / ( m_fMax - m_fMin );
207 } else {
208 fStartAngle = -90 * M_PI / 180;
209 fCurrentAngle = fStartAngle + 255 * M_PI / 180 * ( m_fValue - m_fMin - 0.5 * ( m_fMax - m_fMin ) ) / ( m_fMax - m_fMin );
210 }
211
213 // Indicating the current position using a rotated line instead of
214 // a dot
216 //
217 // float fLength, fWidth, fBaseX, fBaseY;
218 // if ( m_type == Type::Small ) {
219 // fBaseX = 9.0;
220 // fBaseY = 9.0;
221 // fLength = 4;
222 // fWidth = 2;
223 // } else {
224 // fBaseX = 22.0;
225 // fBaseY = 14.0;
226 // fLength = 6;
227 // fWidth = 3;
228 // }
229 //
230 // QPointF p1( fBaseX + std::cos( fCurrentAngle + M_PI / 2 ) * fWidth / 2,
231 // fBaseY + std::sin( fCurrentAngle + M_PI / 2 ) * fWidth / 2 );
232 // QPointF p2( p1.x() + std::cos( fCurrentAngle ) * fLength,
233 // p1.y() + std::sin( fCurrentAngle ) * fLength );
234 // QPointF p3( p2.x() - std::cos( fCurrentAngle + M_PI / 2 ) * fWidth / 2,
235 // p2.y() - std::sin( fCurrentAngle + M_PI / 2 ) * fWidth / 2 );
236 // QPointF p4( p3.x() - std::cos( fCurrentAngle ) * fLength,
237 // p3.y() - std::sin( fCurrentAngle ) * fLength );
238 // QPainterPath path;
239 // path.moveTo( p1 );
240 // path.lineTo( p2 );
241 // path.lineTo( p3 );
242 // path.lineTo( p4 );
243 //
244 // path.setFillRule( Qt::WindingFill );
245 // QPen pen( colorHandle );
246 // pen.setJoinStyle( Qt::RoundJoin );
247 // pen.setWidth( 1.7 );
248 // painter.setPen( pen );
249 // painter.setBrush( QBrush( colorHandle ) );
250 // painter.drawPath( path );
252
253 if ( m_bIsActive ) {
254 float fDistance, fRadius, fBaseX, fBaseY;
255 if ( m_type == Type::Small ) {
256 fBaseX = static_cast<float>( m_nWidgetWidth ) / 2.0;
257 fBaseY = static_cast<float>( m_nWidgetHeight ) / 2.0;
258 fDistance = 3;
259 fRadius = 1;
260 } else {
261 fBaseX = rectRotary.x() + rectRotary.width()/2;
262 fBaseY = rectRotary.y() + rectRotary.height()/2;
263 fDistance = 4;
264 fRadius = 1.5;
265 }
266
267 QPointF p1( fBaseX + std::cos( fCurrentAngle ) * fDistance,
268 fBaseY + std::sin( fCurrentAngle ) * fDistance );
269 painter.setPen( QPen( colorHandle, 1 ) );
270 painter.setBrush( QBrush( colorHandle ) );
271 painter.drawEllipse( p1, fRadius, fRadius );
272 }
273
274 if ( m_type != Type::Small ) {
275 QRectF leftTextRec( 2, 16, 7, 7 );
276 QRectF rightTextRec( 34, 16, 9, 7 );
277
278 QFont font( H2Core::Preferences::get_instance()->getApplicationFontFamily() );
279 painter.setPen( QPen( colorFont, 3 ) );
280 if ( std::fmod( m_fMin, 1 ) == 0 && std::fabs( m_fMin ) < 10 ) {
281 font.setPixelSize( 7 );
282 painter.setFont( font );
283 painter.drawText( leftTextRec, Qt::AlignCenter, QString::number( m_fMin ) );
284 } else {
285 font.setPixelSize( 9 );
286 painter.setFont( font );
287 painter.drawText( leftTextRec, Qt::AlignCenter, "-" );
288 }
289 if ( std::fmod( m_fMax, 1 ) == 0 && std::fabs( m_fMax ) < 10 ) {
290 font.setPixelSize( 7 );
291 painter.setFont( font );
292 painter.drawText( rightTextRec, Qt::AlignCenter, QString::number( m_fMax ) );
293 } else {
294 font.setPixelSize( 9 );
295 painter.setFont( font );
296 painter.drawText( rightTextRec, Qt::AlignCenter, "+" );
297 }
298 }
299}
300
302 if ( changes & H2Core::Preferences::Changes::Colors ) {
303 update();
304 }
305}
#define ERRORLOG(x)
Definition Object.h:242
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.
Type m_type
Definition Rotary.h:70
QSvgRenderer * m_knob
Definition Rotary.h:72
@ Small
No arc will be drawn.
Definition Rotary.h:57
@ Center
The arc features a point at its upmost position.
Definition Rotary.h:55
@ Normal
The arc is of solid red color.
Definition Rotary.h:51
void onPreferencesChanged(H2Core::Preferences::Changes changes)
Definition Rotary.cpp:301
QSvgRenderer * m_background
Definition Rotary.h:71
virtual void paintEvent(QPaintEvent *ev) override
Definition Rotary.cpp:94
Rotary(const Rotary &)=delete
static QString getSvgImagePath()
Definition Skin.h:40
void updateTooltip() override
Indicates child class to recalculate its tool tip in case m_registeredMidiEvents changed.
WidgetWithInput(QWidget *parent, bool bUseIntSteps, QString sBaseTooltip, int nScrollSpeed, int nScrollSpeedFast, float fMin, float fMax, bool bModifyOnChange)