mycontrols example added

This commit is contained in:
Uwe Rathmann 2019-02-26 21:52:02 +01:00
parent 9989ae85d3
commit 2b789b157b
9 changed files with 896 additions and 0 deletions

View File

@ -8,6 +8,7 @@ SUBDIRS += \
layouts \ layouts \
listbox \ listbox \
messagebox \ messagebox \
mycontrols \
sliders \ sliders \
thumbnails \ thumbnails \
tabview tabview

View File

@ -0,0 +1,223 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the 3-clause BSD License
*****************************************************************************/
#include "MySkin.h"
#include "MyToggleButton.h"
#include "MyToggleButtonSkinlet.h"
#include <QskSkin.h>
#include <QskAnimationHint.h>
#include <QskSetup.h>
#include <QskBoxBorderMetrics.h>
#include <QskBoxShapeMetrics.h>
#include <QskBoxBorderColors.h>
#include <QskGradient.h>
#include <QskRgbValue.h>
#include <QskColorFilter.h>
#include <QskBox.h>
#include <QskBoxSkinlet.h>
#include <QskFocusIndicator.h>
#include <QskFocusIndicatorSkinlet.h>
class MySkin : public QskSkin
{
public:
enum GraphicRole
{
GraphicRoleNormal,
GraphicRoleInverted
};
MySkin()
{
declareSkinlet< QskBox, QskBoxSkinlet >();
declareSkinlet< QskFocusIndicator, QskFocusIndicatorSkinlet >();
declareSkinlet< MyToggleButton, MyToggleButtonSkinlet >();
#if 1
setGradient( QskAspect::Control, Qt::gray );
#endif
}
void initFocusIndicatorHints(
qreal border, qreal radius, qreal padding, QRgb rgb )
{
const auto subControl = QskFocusIndicator::Panel;
setBoxBorderMetrics( subControl, border );
setBoxShape( subControl, radius );
setMargins( subControl | QskAspect::Padding, padding );
setGradient( subControl, Qt::transparent );
setBoxBorderColors( subControl, rgb );
}
void initBoxHints(
qreal border, qreal radius,
const QskBoxBorderColors& borderColors,
const QskGradient& fillColor )
{
const auto subControl = QskBox::Panel;
setBoxBorderMetrics( subControl, border );
setBoxShape( subControl, radius );
setBoxBorderColors( subControl, borderColors );
setGradient( subControl, fillColor );
setMargins( subControl | QskAspect::Padding, 0.5 * radius );
}
void initToggleButtonHints(
qreal width, qreal height, qreal radius,
QRgb baseColor, QRgb baseTextColor,
QRgb foregroundColor, QRgb foregroundTextColor )
{
using namespace QskAspect;
using Q = MyToggleButton;
for( auto subControl : { Q::UncheckedLabel, Q::CheckedLabel } )
{
QColor color1 = foregroundTextColor;
QColor color2 = baseTextColor;
if( subControl == Q::UncheckedLabel )
{
std::swap( color1, color2 );
}
setColor( subControl | Q::Checked, color1 );
setColor( subControl, color2 );
setSkinHint( subControl | Alignment, Qt::AlignCenter );
setAnimation( subControl | Color, animator() );
}
for( auto subControl : { Q::UncheckedIcon, Q::CheckedIcon } )
{
int role1 = MySkin::GraphicRoleNormal;
int role2 = MySkin::GraphicRoleInverted;
if( subControl == Q::UncheckedIcon )
{
std::swap( role1, role2 );
}
setGraphicRole( subControl, role1 );
setGraphicRole( subControl | Q::Checked, role2 );
setAnimation( subControl | Flag, animator() );
}
setGradient( Q::Panel, baseColor );
setBoxBorderColors( Q::Panel, QColor( baseColor ).dark( 120 ) );
setGradient( Q::Cursor, foregroundColor );
setBoxBorderColors( Q::Cursor, QColor( foregroundColor ).dark( 120 ) );
for( auto subControl : { Q::Panel, Q::Cursor } )
{
setMetric( subControl | MinimumWidth, width );
setMetric( subControl | MinimumHeight, height );
setMargins( subControl | Padding, -4 );
setBoxShape( subControl, radius );
setBoxBorderMetrics( subControl, 1 );
}
setMargins( Q::CheckedPanel | Padding, 10 );
setMargins( Q::UncheckedPanel | Padding, 10 );
for( auto state : { NoState, Q::Disabled } )
{
const auto aspect = Q::Cursor | state | Position;
setMetric( aspect | Q::Checked, 0 );
setMetric( aspect, 1 );
}
setAnimation( Q::Cursor | Metric, animator() );
}
void setGraphicFilter( int role, QRgb rgb )
{
QskColorFilter filter;
filter.addColorSubstitution( QskRgbValue::Khaki, rgb );
QskSkin::setGraphicFilter( role, filter );
}
private:
virtual QskAnimationHint animator() const
{
return QskAnimationHint( 200 );
}
};
class MySkin1 : public MySkin
{
public:
MySkin1()
{
using namespace QskRgbValue;
setGraphicFilter( GraphicRoleNormal, QskRgbValue::Crimson );
setGraphicFilter( GraphicRoleInverted, QskRgbValue::Gold );
initFocusIndicatorHints( 2, 3, 6, Teal );
initBoxHints( 2, 8, DarkCyan, LightCyan );
initToggleButtonHints( 150, 120, 6,
AliceBlue, Black, CornflowerBlue, White );
}
QskAnimationHint animator() const override
{
return QskAnimationHint( 200 );
}
};
class MySkin2 : public MySkin
{
public:
MySkin2()
{
using namespace QskRgbValue;
setGraphicFilter( GraphicRoleNormal, QskRgbValue::HotPink );
setGraphicFilter( GraphicRoleInverted, QskRgbValue::White );
initFocusIndicatorHints( 2, 6, 6, Crimson );
initBoxHints( 4, 30, LightPink, MistyRose );
initToggleButtonHints( 130, 100, 40,
Linen, Black, HotPink, White );
}
QskAnimationHint animator() const override
{
return QskAnimationHint( 100, QEasingCurve::InQuad );
}
};
QStringList MySkinFactory::skinNames() const
{
return { QStringLiteral( "MySkin1" ), QStringLiteral( "MySkin2" ) };
}
QskSkin* MySkinFactory::createSkin( const QString& skinName )
{
if ( skinName == QStringLiteral( "MySkin1" ) )
return new MySkin1();
if ( skinName == QStringLiteral( "MySkin2" ) )
return new MySkin2();
return nullptr;
}
#include "moc_MySkin.cpp"

View File

@ -0,0 +1,21 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the 3-clause BSD License
*****************************************************************************/
#ifndef MY_SKIN_H
#define MY_SKIN_H
#include <QskSkinFactory.h>
class MySkinFactory : public QskSkinFactory
{
Q_OBJECT
using Inherited = QskSkinFactory;
public:
QStringList skinNames() const override;
QskSkin* createSkin( const QString& skinName ) override;
};
#endif

View File

@ -0,0 +1,236 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the 3-clause BSD License
*****************************************************************************/
#include "MyToggleButton.h"
#include <QskAspect.h>
#include <QskGraphic.h>
#include <QskGraphicProvider.h>
#include <QskTextOptions.h>
QSK_SUBCONTROL( MyToggleButton, Panel )
QSK_SUBCONTROL( MyToggleButton, Cursor )
QSK_SUBCONTROL( MyToggleButton, CheckedPanel )
QSK_SUBCONTROL( MyToggleButton, CheckedLabel )
QSK_SUBCONTROL( MyToggleButton, UncheckedPanel )
QSK_SUBCONTROL( MyToggleButton, UncheckedLabel )
QSK_SUBCONTROL( MyToggleButton, CheckedIcon )
QSK_SUBCONTROL( MyToggleButton, UncheckedIcon )
class MyToggleButton::PrivateData
{
public:
struct
{
QString text;
QString iconSource;
QskGraphic icon;
bool iconDirty = false;
} content[ 2 ];
QskTextOptions textOptions;
/*
Normal: Checked: left, Unchecked : right
Inverted: Checked: right, Unchecked : left
*/
bool inverted = false;
};
MyToggleButton::MyToggleButton( QQuickItem* parent )
: Inherited( parent )
, m_data( new PrivateData() )
{
setCheckable( true );
setAcceptHoverEvents( false );
initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed );
}
MyToggleButton::MyToggleButton(
const QString& checkedIcon, const QString& uncheckedIcon, QQuickItem* parent )
: MyToggleButton( parent )
{
if ( !uncheckedIcon.isEmpty() )
{
auto& data = m_data->content[ UncheckedSection ];
data.iconSource = uncheckedIcon;
data.iconDirty = true;
}
if ( !checkedIcon.isEmpty() )
{
auto& data = m_data->content[ CheckedSection ];
data.iconSource = checkedIcon;
data.iconDirty = true;
}
}
MyToggleButton::~MyToggleButton()
{
}
void MyToggleButton::setInverted( bool on )
{
if ( m_data->inverted != on )
{
m_data->inverted = on;
update();
Q_EMIT invertedChanged( on );
}
}
bool MyToggleButton::isInverted() const
{
return m_data->inverted;
}
void MyToggleButton::setTextOptions( const QskTextOptions& options )
{
if( options != m_data->textOptions )
{
m_data->textOptions = options;
if( !( m_data->content[ 0 ].text.isEmpty() && m_data->content[ 1 ].text.isEmpty() ) )
{
update();
}
}
}
QskTextOptions MyToggleButton::textOptions() const
{
return m_data->textOptions;
}
void MyToggleButton::setTextAt( int index, const QString& text )
{
if( index < 0 || index > 1 )
{
return;
}
auto& data = m_data->content[ index ];
if( !data.icon.isNull() )
{
// we don't support text + graphic
data.icon.reset();
update();
}
data.iconDirty = false;
if( text != data.text )
{
data.text = text;
update();
}
}
QString MyToggleButton::textAt( int index ) const
{
if( index < 0 || index > 1 )
{
return QString();
}
return m_data->content[ index ].text;
}
void MyToggleButton::setIconAt( int index, const QString& icon )
{
if( index < 0 || index > 1 )
{
return;
}
auto& data = m_data->content[ index ];
if( !data.text.isEmpty() )
{
// we don't support text + graphic
data.text.clear();
update();
}
if( icon != data.iconSource )
{
data.icon.reset();
data.iconSource = icon;
data.iconDirty = true;
polish();
update();
}
}
QString MyToggleButton::iconAt( int index ) const
{
if( index < 0 || index > 1 )
{
return QString();
}
return m_data->content[ index ].iconSource;
}
QskGraphic MyToggleButton::graphicAt( int index ) const
{
if( index < 0 || index > 1 )
{
return QskGraphic();
}
auto& data = const_cast< MyToggleButton* >( this )->m_data->content[ index ];
if( data.iconDirty )
{
data.icon = Qsk::loadGraphic( data.iconSource );
data.iconDirty = false;
}
return data.icon;
}
QSizeF MyToggleButton::contentsSizeHint() const
{
const qreal width = metric( Panel | QskAspect::MinimumWidth );
const qreal height = metric( Panel | QskAspect::MinimumHeight );
return QSizeF( width, height );
}
// better use Minimum Width/Height hints TODO ...
static constexpr qreal aspectRatio = 4.0 / 3.0;
qreal MyToggleButton::heightForWidth( qreal width ) const
{
return width / aspectRatio;
}
qreal MyToggleButton::widthForHeight( qreal height ) const
{
return height * aspectRatio;
}
void MyToggleButton::updateLayout()
{
for( int i = 0; i < 2; i++ )
{
if( m_data->content[ i ].iconDirty )
{
( void )graphicAt( Checked );
}
}
}
#include "moc_MyToggleButton.cpp"

View File

@ -0,0 +1,65 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the 3-clause BSD License
*****************************************************************************/
#ifndef MY_TOGGLE_BUTTON_H
#define MY_TOGGLE_BUTTON_H
#include <QskAbstractButton.h>
class QskGraphic;
class QskTextOptions;
class MyToggleButton : public QskAbstractButton
{
Q_OBJECT
using Inherited = QskAbstractButton;
public:
QSK_SUBCONTROLS( Panel, Cursor, CheckedPanel, CheckedLabel, CheckedIcon,
UncheckedPanel, UncheckedLabel, UncheckedIcon )
enum
{
UncheckedSection = 0,
CheckedSection = 1
};
MyToggleButton( QQuickItem* parent = nullptr );
MyToggleButton( const QString& ckeckedIcon, const QString& uncheckedIcon,
QQuickItem* parent = nullptr );
~MyToggleButton() override;
void setTextAt( int index, const QString& );
QString textAt( int index ) const;
void setIconAt( int index, const QString& icon );
QString iconAt( int index ) const;
QSizeF contentsSizeHint() const override;
qreal heightForWidth( qreal width ) const override;
qreal widthForHeight( qreal height ) const override;
void setTextOptions( const QskTextOptions& );
QskTextOptions textOptions() const;
QskGraphic graphicAt( int index ) const;
void setInverted( bool );
bool isInverted() const;
Q_SIGNALS:
void invertedChanged( bool );
protected:
void updateLayout() override;
private:
class PrivateData;
std::unique_ptr< PrivateData > m_data;
};
#endif

View File

@ -0,0 +1,145 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the 3-clause BSD License
*****************************************************************************/
#include "MyToggleButtonSkinlet.h"
#include "MyToggleButton.h"
#include <QskTextOptions.h>
#include <QskGraphic.h>
static inline QRectF sectionRect( const QRectF& rect, int section )
{
auto r = rect;
if ( section == 0 )
r.setWidth( 0.5 * rect.width() );
else
r.setLeft( rect.right() - 0.5 * rect.width() );
return r;
}
MyToggleButtonSkinlet::MyToggleButtonSkinlet( QskSkin* skin )
: Inherited( skin )
{
// sorted in stacking order
setNodeRoles( { PanelRole, CursorRole, UncheckedPanelRole, UncheckedLabelRole,
UncheckedIconRole, CheckedPanelRole, CheckedLabelRole, CheckedIconRole
} );
}
QRectF MyToggleButtonSkinlet::subControlRect(
const QskSkinnable* skinnable, QskAspect::Subcontrol subControl ) const
{
using Q = MyToggleButton;
const auto button = static_cast< const MyToggleButton* >( skinnable );
if( subControl == Q::Panel )
{
return button->contentsRect();
}
else if( subControl == Q::UncheckedPanel )
{
auto rect = innerRect( skinnable, Q::Panel );
return sectionRect( rect, button->isInverted() ? 0 : 1 );
}
else if( subControl == Q::CheckedPanel )
{
auto rect = innerRect( skinnable, Q::Panel );
return sectionRect( rect, button->isInverted() ? 1 : 0 );
}
else if( subControl == Q::CheckedLabel || subControl == Q::CheckedIcon )
{
return innerRect( skinnable, Q::CheckedPanel );
}
else if( subControl == Q::UncheckedLabel || subControl == Q::UncheckedIcon )
{
return innerRect( skinnable, Q::UncheckedPanel );
}
else if( subControl == Q::Cursor )
{
auto position = skinnable->metric( Q::Cursor | QskAspect::Position );
if ( button->isInverted() )
position = 1.0 - position;
auto rect = innerRect( skinnable, Q::Panel );
rect.setWidth( 0.5 * rect.width() );
rect.moveLeft( rect.left() + position * rect.width() );
return rect;
}
return Inherited::subControlRect( skinnable, subControl );
}
QRectF MyToggleButtonSkinlet::innerRect(
const QskSkinnable* skinnable, QskAspect::Subcontrol subControl ) const
{
return skinnable->innerBox( subControl, subControlRect( skinnable, subControl ) );
}
QSGNode* MyToggleButtonSkinlet::updateSubNode(
const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const
{
using Q = MyToggleButton;
const auto button = static_cast< const Q* >( skinnable );
switch( nodeRole )
{
case PanelRole:
{
return updateBoxNode( button, node, Q::Panel );
}
case CheckedLabelRole:
{
return updateTextNode(
button, node, button->textAt( Q::CheckedSection ),
button->textOptions(), Q::CheckedLabel );
}
case UncheckedLabelRole:
{
return updateTextNode(
button, node, button->textAt( Q::UncheckedSection ),
button->textOptions(), Q::UncheckedLabel );
}
case CheckedIconRole:
{
return updateGraphicNode(
button, node, button->graphicAt( Q::CheckedSection ), Q::CheckedIcon );
}
case UncheckedIconRole:
{
return updateGraphicNode(
button, node, button->graphicAt( Q::UncheckedSection ), Q::UncheckedIcon );
}
case CheckedPanelRole:
case UncheckedPanelRole:
{
// not implemented
return nullptr;
}
case CursorRole:
{
return updateBoxNode( button, node, Q::Cursor );
}
default:
{
return nullptr;
}
}
}
#include "moc_MyToggleButtonSkinlet.cpp"

View File

@ -0,0 +1,46 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the 3-clause BSD License
*****************************************************************************/
#ifndef MY_TOGGLE_BUTTON_SKINLET_H
#define MY_TOGGLE_BUTTON_SKINLET_H
#include <QskSkinlet.h>
class MyToggleButton;
class MyToggleButtonSkinlet : public QskSkinlet
{
Q_GADGET
using Inherited = QskSkinlet;
public:
enum NodeRole
{
PanelRole,
CursorRole,
CheckedPanelRole,
CheckedLabelRole,
CheckedIconRole,
UncheckedPanelRole,
UncheckedLabelRole,
UncheckedIconRole
};
Q_INVOKABLE MyToggleButtonSkinlet( QskSkin* = nullptr );
~MyToggleButtonSkinlet() override = default;
QRectF subControlRect( const QskSkinnable*, QskAspect::Subcontrol ) const override;
protected:
QSGNode* updateSubNode( const QskSkinnable*, quint8 nodeRole, QSGNode* ) const override;
private:
QRectF innerRect( const QskSkinnable*, QskAspect::Subcontrol ) const;
};
#endif

View File

@ -0,0 +1,145 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the 3-clause BSD License
*****************************************************************************/
#include "MySkin.h"
#include "MyToggleButton.h"
#include <SkinnyFont.h>
#include <SkinnyShapeProvider.h>
#include <SkinnyShortcut.h>
#include <QskWindow.h>
#include <QskObjectCounter.h>
#include <QskFocusIndicator.h>
#include <QskBox.h>
#include <QskLinearBox.h>
#include <QskSkinManager.h>
#include <QskSkinTransition.h>
#include <QskSetup.h>
#include <QskSkin.h>
#include <QGuiApplication>
/*
This example is intended to show how to:
- implement custom controls
- set up skins, customizing these controls
For demonstration purposes we limit the set of controls
being used:
- QskPushButton
- QskBox
- QskFocusIndicator
- MyToggleButton
*/
class ContentBox : public QskBox
{
public:
ContentBox( QQuickItem* parent = nullptr )
: QskBox( parent )
{
setAutoLayoutChildren( true );
auto layout = new QskLinearBox( Qt::Horizontal, 2, this );
layout->setMargins( 10 );
layout->setSpacing( 10 );
layout->setDefaultAlignment( Qt::AlignCenter );
layout->setExtraSpacingAt( Qt::BottomEdge );
{
auto button = new MyToggleButton( layout );
button->setTextAt( 0, "On" );
button->setTextAt( 1, "Off" );
}
{
auto button = new MyToggleButton( layout );
button->setIconAt( 0, "image://shapes/Ring/Khaki" );
button->setIconAt( 1, "image://shapes/Diamond/Khaki" );
}
}
};
class Window : public QskWindow
{
Q_OBJECT
public:
Window()
{
auto button = new MyToggleButton();
button->setTextAt( 0, "Skin 1" );
button->setTextAt( 1, "Skin 2" );
auto box = new QskLinearBox( Qt::Vertical );
box->setMargins( 20 );
box->addItem( button, Qt::AlignRight );
box->addItem( new ContentBox() );
connect( button, &MyToggleButton::toggled,
this, &Window::setAlternativeSkin );
setAlternativeSkin( button->isChecked() );
addItem( box );
addItem( new QskFocusIndicator() );
}
private:
void setAlternativeSkin( bool on )
{
const auto skinNames = qskSkinManager->skinNames();
auto oldSkin = qskSetup->skin();
if ( oldSkin->parent() == qskSetup )
oldSkin->setParent( nullptr ); // otherwise setSkin deletes it
auto newSkin = qskSetup->setSkin( skinNames[ on ? 1 : 0 ] );
QskSkinTransition transition;
transition.setSourceSkin( oldSkin );
transition.setTargetSkin( newSkin );
transition.setAnimation( 600 );
transition.process();
if ( oldSkin->parent() == nullptr )
delete oldSkin;
}
};
int main( int argc, char* argv[] )
{
#ifdef ITEM_STATISTICS
QskObjectCounter counter( true );
#endif
QGuiApplication app( argc, argv );
SkinnyFont::init( &app );
SkinnyShortcut::enable( SkinnyShortcut::DebugBackground |
SkinnyShortcut::DebugStatistics | SkinnyShortcut::Quit );
Qsk::addGraphicProvider( "shapes", new SkinnyShapeProvider() );
qskSkinManager->setPluginPaths( QStringList() ); // no plugins
qskSkinManager->registerFactory(
QStringLiteral( "MySkinFactory" ), new MySkinFactory() );
qskSetup->setControlFlag( QskSetup::PreferRasterForTextures, true );
Window window;
window.resize( 480, 360 );
window.show();
return app.exec();
}
#include "main.moc"

View File

@ -0,0 +1,14 @@
include( $${PWD}/../example.pri )
TARGET = mycontrols
SOURCES += \
MySkin.cpp \
MyToggleButton.cpp \
MyToggleButtonSkinlet.cpp \
main.cpp
HEADERS += \
MyToggleButton.h \
MyToggleButtonSkinlet.h \
MySkin.h