QSkinny 0.8.0
C++/Qt UI toolkit
Loading...
Searching...
No Matches
QskSkin.cpp
1/******************************************************************************
2 * QSkinny - Copyright (C) The authors
3 * SPDX-License-Identifier: BSD-3-Clause
4 *****************************************************************************/
5
6#include "QskSkin.h"
7
8#include "QskAnimationHint.h"
9#include "QskAspect.h"
10#include "QskColorFilter.h"
11#include "QskGraphic.h"
12#include "QskGraphicProviderMap.h"
13#include "QskStandardSymbol.h"
14#include "QskPlatform.h"
15#include "QskMargins.h"
16#include "QskFontRole.h"
17
18#include "QskSkinHintTable.h"
19#include "QskSkinManager.h"
20#include "QskSkinTransition.h"
21
22#include <qguiapplication.h>
23#include <qpa/qplatformdialoghelper.h>
24#include <qpa/qplatformtheme.h>
25
26#include <cmath>
27
28#include "QskBox.h"
29#include "QskBoxSkinlet.h"
30
31#include "QskCheckBox.h"
32#include "QskCheckBoxSkinlet.h"
33
34#include "QskComboBox.h"
35#include "QskComboBoxSkinlet.h"
36
37#include "QskDrawer.h"
38#include "QskDrawerSkinlet.h"
39
40#include "QskFocusIndicator.h"
41#include "QskFocusIndicatorSkinlet.h"
42
43#include "QskGraphicLabel.h"
44#include "QskGraphicLabelSkinlet.h"
45
46#include "QskListView.h"
47#include "QskListViewSkinlet.h"
48
49#include "QskMenu.h"
50#include "QskMenuSkinlet.h"
51
52#include "QskPageIndicator.h"
53#include "QskPageIndicatorSkinlet.h"
54
55#include "QskPopup.h"
56#include "QskPopupSkinlet.h"
57
58#include "QskProgressBar.h"
59#include "QskProgressBarSkinlet.h"
60
61#include "QskProgressRing.h"
62#include "QskProgressRingSkinlet.h"
63
64#include "QskRadioBox.h"
65#include "QskRadioBoxSkinlet.h"
66
67#include "QskPushButton.h"
68#include "QskPushButtonSkinlet.h"
69
70#include "QskScrollView.h"
71#include "QskScrollViewSkinlet.h"
72
73#include "QskSegmentedBar.h"
74#include "QskSegmentedBarSkinlet.h"
75
76#include "QskSeparator.h"
77#include "QskSeparatorSkinlet.h"
78
79#include "QskSlider.h"
80#include "QskSliderSkinlet.h"
81
82#include "QskSpinBox.h"
83#include "QskSpinBoxSkinlet.h"
84
85#include "QskSubWindow.h"
86#include "QskSubWindowSkinlet.h"
87
88#include "QskSubWindowArea.h"
89#include "QskSubWindowAreaSkinlet.h"
90
91#include "QskSwitchButton.h"
92#include "QskSwitchButtonSkinlet.h"
93
94#include "QskTabButton.h"
95#include "QskTabButtonSkinlet.h"
96
97#include "QskTabView.h"
98#include "QskTabViewSkinlet.h"
99
100#include "QskTextLabel.h"
101#include "QskTextLabelSkinlet.h"
102
103#include "QskTextArea.h"
104#include "QskTextAreaSkinlet.h"
105
106#include "QskTextField.h"
107#include "QskTextFieldSkinlet.h"
108
109#include "QskStatusIndicator.h"
110#include "QskStatusIndicatorSkinlet.h"
111
112#include "QskInternalMacros.h"
113
114#include <qhash.h>
115
116static inline QskSkinlet* qskNewSkinlet( const QMetaObject* metaObject, QskSkin* skin )
117{
118 const QByteArray signature = metaObject->className() + QByteArrayLiteral( "(QskSkin*)" );
119
120 QskSkinlet* skinlet = nullptr;
121
122 const int index = metaObject->indexOfConstructor( signature.constData() );
123 if ( index >= 0 )
124 {
125 void* param[] = { &skinlet, &skin };
126 metaObject->static_metacall( QMetaObject::CreateInstance, index, param );
127 }
128
129 return skinlet;
130}
131
132// also used in QskSkinTransition.cpp TODO ...
133
134QSK_HIDDEN_EXTERNAL_BEGIN
135
136QFont qskResolvedFont( const QHash< QskFontRole, QFont >& fontTable,
137 const QskFontRole& fontRole )
138{
139 auto it = fontTable.constFind( fontRole );
140 if ( it != fontTable.constEnd() )
141 return it.value();
142
143 it = fontTable.constFind( QskFontRole() );
144 if ( it != fontTable.constEnd() )
145 return it.value();
146
147 return QGuiApplication::font();
148}
149
150QSK_HIDDEN_EXTERNAL_END
151
152namespace
153{
154 class SkinletData
155 {
156 public:
157 SkinletData( const QMetaObject* metaObject )
158 : metaObject( metaObject )
159 , skinlet( nullptr )
160 {
161 }
162
163 ~SkinletData()
164 {
165 delete skinlet;
166 }
167
168 const QMetaObject* metaObject;
169 QskSkinlet* skinlet; // mutable ???
170 };
171}
172
173class QskSkin::PrivateData
174{
175 public:
176 QHash< const QMetaObject*, SkinletData > skinletMap;
177
178 QskSkinHintTable hintTable;
179
180 QHash< QskFontRole, QFont > fonts;
181 QHash< int, QskColorFilter > graphicFilters;
182
183 QskGraphicProviderMap graphicProviders;
184
185 int colorScheme = -1; // uninitialized
186};
187
188QskSkin::QskSkin( QObject* parent )
189 : QObject( parent )
190 , m_data( new PrivateData() )
191{
192 declareSkinlet< QskControl, QskSkinlet >();
193
194 declareSkinlet< QskBox, QskBoxSkinlet >();
195 declareSkinlet< QskCheckBox, QskCheckBoxSkinlet >();
196 declareSkinlet< QskComboBox, QskComboBoxSkinlet >();
197 declareSkinlet< QskDrawer, QskDrawerSkinlet >();
198 declareSkinlet< QskFocusIndicator, QskFocusIndicatorSkinlet >();
199 declareSkinlet< QskGraphicLabel, QskGraphicLabelSkinlet >();
200 declareSkinlet< QskListView, QskListViewSkinlet >();
201 declareSkinlet< QskPageIndicator, QskPageIndicatorSkinlet >();
202 declareSkinlet< QskPopup, QskPopupSkinlet >();
203 declareSkinlet< QskMenu, QskMenuSkinlet >();
204 declareSkinlet< QskPushButton, QskPushButtonSkinlet >();
205 declareSkinlet< QskScrollView, QskScrollViewSkinlet >();
206 declareSkinlet< QskSegmentedBar, QskSegmentedBarSkinlet >();
207 declareSkinlet< QskSeparator, QskSeparatorSkinlet >();
208 declareSkinlet< QskSlider, QskSliderSkinlet >();
209 declareSkinlet< QskSpinBox, QskSpinBoxSkinlet >();
210 declareSkinlet< QskStatusIndicator, QskStatusIndicatorSkinlet >();
211 declareSkinlet< QskSubWindow, QskSubWindowSkinlet >();
212 declareSkinlet< QskSubWindowArea, QskSubWindowAreaSkinlet >();
213 declareSkinlet< QskSwitchButton, QskSwitchButtonSkinlet >();
214 declareSkinlet< QskTabButton, QskTabButtonSkinlet >();
215 declareSkinlet< QskTabView, QskTabViewSkinlet >();
216 declareSkinlet< QskTextLabel, QskTextLabelSkinlet >();
217 declareSkinlet< QskTextArea, QskTextAreaSkinlet >();
218 declareSkinlet< QskTextField, QskTextFieldSkinlet >();
219 declareSkinlet< QskProgressBar, QskProgressBarSkinlet >();
220 declareSkinlet< QskProgressRing, QskProgressRingSkinlet >();
221 declareSkinlet< QskRadioBox, QskRadioBoxSkinlet >();
222
223 const QFont font = QGuiApplication::font();
224 setupFontTable( font.family(), font.italic() );
225
226 const auto noMargins = QVariant::fromValue( QskMargins( 0 ) );
227
228 {
229 // some defaults
230 const auto aspect = QskAspect::NoSubcontrol | QskAspect::Metric;
231
232 setSkinHint( aspect | QskAspect::Margin, noMargins );
233 setSkinHint( aspect | QskAspect::Padding, noMargins );
234 setSkinHint( aspect | QskAspect::Spacing, 0 );
235 }
236
237 setSkinHint( QskControl::Background | QskAspect::Metric | QskAspect::Padding, noMargins );
238
239 setSkinHint( QskControl::Background | QskAspect::Color,
240 QVariant::fromValue( QskGradient() ) );
241}
242
243QskSkin::~QskSkin()
244{
245}
246
247QskSkin::ColorScheme QskSkin::colorScheme() const
248{
249 if ( m_data->colorScheme < 0 )
250 return QskSkin::UnknownScheme;
251
252 return static_cast< QskSkin::ColorScheme >( m_data->colorScheme );
253}
254
255void QskSkin::setColorScheme( ColorScheme colorScheme )
256{
257 if ( colorScheme == m_data->colorScheme )
258 return;
259
260 m_data->colorScheme = colorScheme;
261
262 QskAnimationHint transitionHint;
263
264 if ( qskSkinManager->currentSkin() == this )
265 transitionHint = qskSkinManager->transitionHint();
266
267 if ( transitionHint.isValid() )
268 {
269 QskSkinTransition transition;
270 transition.setMask( QskSkinTransition::Color );
271 transition.setSourceSkin( this );
272
273 clearHints();
274 initHints();
275
276 transition.setTargetSkin( this );
277 transition.run( transitionHint );
278 }
279 else
280 {
281 clearHints();
282 initHints();
283 }
284
285 Q_EMIT colorSchemeChanged( colorScheme );
286}
287
288void QskSkin::setSkinHint( QskAspect aspect, const QVariant& skinHint )
289{
290 m_data->hintTable.setHint( aspect, skinHint );
291}
292
293const QVariant& QskSkin::skinHint( QskAspect aspect ) const
294{
295 return m_data->hintTable.hint( aspect );
296}
297
298void QskSkin::declareSkinlet( const QMetaObject* metaObject,
299 const QMetaObject* skinletMetaObject )
300{
301 Q_ASSERT( skinletMetaObject->constructorCount() );
302
303 const auto it = m_data->skinletMap.find( metaObject );
304
305 if ( it != m_data->skinletMap.end() )
306 {
307 auto& entry = it.value();
308 if ( entry.metaObject != skinletMetaObject )
309 {
310 entry.metaObject = skinletMetaObject;
311
312 delete entry.skinlet;
313 entry.skinlet = nullptr;
314 }
315 }
316 else
317 {
318 m_data->skinletMap.insert( metaObject, skinletMetaObject );
319 }
320}
321
322static inline void qskSetFont( QskSkin* skin,
323 QskFontRole::Category category, QFont font, qreal pointSize )
324{
325 font.setPointSize( pointSize );
326 skin->setFont( { category, QskFontRole::Normal }, font );
327}
328
329void QskSkin::setupFontTable( const QString& family, bool italic )
330{
331 QFont font( family );
332 font.setItalic( italic );
333 font.setWeight( QFont::Normal );
334
335 qskSetFont( this, QskFontRole::Caption, font, 8 );
336 qskSetFont( this, QskFontRole::Subtitle, font, 10 );
337 qskSetFont( this, QskFontRole::Body, font, 12 );
338 qskSetFont( this, QskFontRole::Title, font, 20 );
339 qskSetFont( this, QskFontRole::Headline, font, 30 );
340 qskSetFont( this, QskFontRole::Display, font, 48 );
341
342 completeFontTable();
343}
344
345void QskSkin::completeFontTable()
346{
347 // varying the weight of QskFontRole::Normal
348
349 for ( int i = QskFontRole::Caption; i <= QskFontRole::Display; i++ )
350 {
351 auto& table = m_data->fonts;
352
353 const auto category = static_cast< QskFontRole::Category >( i );
354
355 const auto it = table.constFind( { category, QskFontRole::Normal } );
356 if ( it == table.constEnd() )
357 continue;
358
359 const auto normalFont = it.value();
360
361 for ( int j = QskFontRole::VeryLow; j <= QskFontRole::VeryHigh; j++ )
362 {
363 const auto emphasis = static_cast< QskFontRole::Emphasis >( j );
364
365 if ( emphasis == QskFontRole::Normal
366 || table.contains( { category, emphasis } ) )
367 {
368 continue;
369 }
370
371#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
372 const auto step = 10; // weight: [0-99]
373#else
374 const auto step = 100; // weight: [1-1000]
375#endif
376
377 int weight = normalFont.weight() + ( j - 2 ) * step;
378 weight = qBound( static_cast<int>( QFont::Thin ),
379 weight, static_cast<int>( QFont::Black ) );
380
381 auto font = normalFont;
382 font.setWeight( static_cast< QFont::Weight >( weight ) );
383
384 m_data->fonts[ { category, emphasis } ] = font;
385 }
386 }
387}
388
389void QskSkin::setFont( const QskFontRole& fontRole, const QFont& font )
390{
391 m_data->fonts[ fontRole ] = font;
392}
393
394void QskSkin::resetFont( const QskFontRole& fontRole )
395{
396 m_data->fonts.remove( fontRole );
397}
398
399QFont QskSkin::font( const QskFontRole& fontRole ) const
400{
401 return qskResolvedFont( m_data->fonts, fontRole );
402}
403
404void QskSkin::setGraphicFilter( int graphicRole, const QskColorFilter& colorFilter )
405{
406 m_data->graphicFilters[ graphicRole ] = colorFilter;
407}
408
409void QskSkin::resetGraphicFilter( int graphicRole )
410{
411 m_data->graphicFilters.remove( graphicRole );
412}
413
414QskColorFilter QskSkin::graphicFilter( int graphicRole ) const
415{
416 return m_data->graphicFilters.value( graphicRole );
417}
418
419const QskSkinHintTable& QskSkin::hintTable() const
420{
421 return m_data->hintTable;
422}
423
424QskSkinHintTable& QskSkin::hintTable()
425{
426 return m_data->hintTable;
427}
428
429const QHash< QskFontRole, QFont >& QskSkin::fontTable() const
430{
431 return m_data->fonts;
432}
433
434const QHash< int, QskColorFilter >& QskSkin::graphicFilters() const
435{
436 return m_data->graphicFilters;
437}
438
439void QskSkin::addGraphicProvider(
440 const QString& providerId, QskGraphicProvider* provider )
441{
442 m_data->graphicProviders.insert( providerId, provider );
443}
444
445QskGraphicProvider* QskSkin::graphicProvider( const QString& providerId ) const
446{
447 return m_data->graphicProviders.provider( providerId );
448}
449
450bool QskSkin::hasGraphicProvider() const
451{
452 return m_data->graphicProviders.size() > 0;
453}
454
455void QskSkin::clearHints()
456{
457 m_data->hintTable.clear();
458 m_data->fonts.clear();
459 m_data->graphicFilters.clear();
460 m_data->graphicProviders.clear();
461}
462
463QString QskSkin::dialogButtonText( int action ) const
464{
465 const auto theme = qskPlatformTheme();
466
467 auto text = theme->standardButtonText( action );
468 text = QPlatformTheme::removeMnemonics( text );
469
470 return text;
471}
472
473const int* QskSkin::dialogButtonLayout( Qt::Orientation orientation ) const
474{
475 // auto policy = QPlatformDialogHelper::UnknownLayout;
476 auto policy = QPlatformDialogHelper::WinLayout;
477
478 if ( const auto theme = qskPlatformTheme() )
479 {
480 const QVariant v = theme->themeHint( QPlatformTheme::DialogButtonBoxLayout );
481 policy = static_cast< QPlatformDialogHelper::ButtonLayout >( v.toInt() );
482 }
483
484 return QPlatformDialogHelper::buttonLayout( orientation, policy );
485}
486
487const QMetaObject* QskSkin::skinletMetaObject( const QMetaObject* metaObject ) const
488{
489 while ( metaObject )
490 {
491 auto it = m_data->skinletMap.constFind( metaObject );
492 if ( it != m_data->skinletMap.constEnd() )
493 return it.value().metaObject;
494
495 metaObject = metaObject->superClass();
496 }
497
498 return nullptr;
499}
500
501QskSkinlet* QskSkin::skinlet( const QMetaObject* metaObject )
502{
503 while ( metaObject )
504 {
505 auto it = m_data->skinletMap.find( metaObject );
506 if ( it != m_data->skinletMap.end() )
507 {
508 auto& entry = it.value();
509
510 if ( entry.skinlet == nullptr )
511 entry.skinlet = qskNewSkinlet( entry.metaObject, this );
512
513 return entry.skinlet;
514 }
515
516 metaObject = metaObject->superClass();
517 }
518
519 static QskSkinlet defaultSkinlet;
520 return &defaultSkinlet;
521}
522
523#include "moc_QskSkin.cpp"
Lookup key for a QskSkinHintTable.
Definition QskAspect.h:15
Describes the rendering interface of a QskControl. Change the skinlet to change the appearance of the...
Definition QskSkinlet.h:34