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