QskSegmentedBar ( inspired by the IOS segmented control ) added
This commit is contained in:
parent
c4efdc1816
commit
50ee82fbaa
@ -19,6 +19,7 @@
|
||||
#include <QskPushButton.h>
|
||||
#include <QskProgressBar.h>
|
||||
#include <QskScrollView.h>
|
||||
#include <QskSegmentedBar.h>
|
||||
#include <QskSeparator.h>
|
||||
#include <QskSlider.h>
|
||||
#include <QskSubWindow.h>
|
||||
@ -127,6 +128,7 @@ namespace
|
||||
void setupProgressBar();
|
||||
void setupPushButton();
|
||||
void setupScrollView();
|
||||
void setupSegmentedBar();
|
||||
void setupSeparator();
|
||||
void setupSubWindow();
|
||||
void setupSlider();
|
||||
@ -159,6 +161,7 @@ void Editor::setup()
|
||||
setupProgressBar();
|
||||
setupPushButton();
|
||||
setupScrollView();
|
||||
setupSegmentedBar();
|
||||
setupSeparator();
|
||||
setupSlider();
|
||||
setupSubWindow();
|
||||
@ -326,6 +329,11 @@ void Editor::setupFocusIndicator()
|
||||
setGradient( Q::Panel, QskGradient() );
|
||||
}
|
||||
|
||||
void Editor::setupSegmentedBar()
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
void Editor::setupSeparator()
|
||||
{
|
||||
using A = QskAspect;
|
||||
|
@ -147,6 +147,7 @@ namespace
|
||||
void setupProgressBar();
|
||||
void setupPushButton();
|
||||
void setupScrollView();
|
||||
void setupSegmentedBar();
|
||||
void setupSeparator();
|
||||
void setupSlider();
|
||||
void setupSubWindow();
|
||||
@ -267,6 +268,7 @@ void Editor::setup()
|
||||
setupProgressBar();
|
||||
setupPushButton();
|
||||
setupScrollView();
|
||||
setupSegmentedBar();
|
||||
setupSeparator();
|
||||
setupSlider();
|
||||
setupSubWindow();
|
||||
@ -465,6 +467,11 @@ void Editor::setupSeparator()
|
||||
setSeparator( Q::Panel | A::Vertical );
|
||||
}
|
||||
|
||||
void Editor::setupSegmentedBar()
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
void Editor::setupPageIndicator()
|
||||
{
|
||||
using Q = QskPageIndicator;
|
||||
|
476
src/controls/QskSegmentedBar.cpp
Normal file
476
src/controls/QskSegmentedBar.cpp
Normal file
@ -0,0 +1,476 @@
|
||||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
||||
* This file may be used under the terms of the QSkinny License, Version 1.0
|
||||
*****************************************************************************/
|
||||
|
||||
#include "QskSegmentedBar.h"
|
||||
|
||||
#include "QskGraphic.h"
|
||||
#include "QskGraphicProvider.h"
|
||||
#include "QskTextOptions.h"
|
||||
#include "QskEvent.h"
|
||||
#include "QskSkinlet.h"
|
||||
#include "QskAspect.h"
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QStyleHints>
|
||||
#include <QFontMetricsF>
|
||||
|
||||
QSK_SUBCONTROL( QskSegmentedBar, Panel )
|
||||
QSK_SUBCONTROL( QskSegmentedBar, Segment )
|
||||
QSK_SUBCONTROL( QskSegmentedBar, Cursor )
|
||||
QSK_SUBCONTROL( QskSegmentedBar, Text )
|
||||
QSK_SUBCONTROL( QskSegmentedBar, Graphic )
|
||||
|
||||
QSK_SYSTEM_STATE( QskSegmentedBar, Selected, QskAspect::FirstSystemState << 2 )
|
||||
|
||||
namespace
|
||||
{
|
||||
class Option
|
||||
{
|
||||
public:
|
||||
Option() = default;
|
||||
|
||||
Option( const QUrl& graphicSource, const QString& text )
|
||||
: graphicSource( graphicSource )
|
||||
, text( text )
|
||||
{
|
||||
#if 1
|
||||
// lazy loading TODO ...
|
||||
if ( !graphicSource.isEmpty() )
|
||||
graphic = Qsk::loadGraphic( graphicSource );
|
||||
#endif
|
||||
}
|
||||
|
||||
QUrl graphicSource;
|
||||
QString text;
|
||||
|
||||
QskGraphic graphic;
|
||||
|
||||
bool isEnabled = true;
|
||||
};
|
||||
}
|
||||
|
||||
class QskSegmentedBar::PrivateData
|
||||
{
|
||||
public:
|
||||
PrivateData( Qt::Orientation orientation )
|
||||
: orientation( orientation )
|
||||
{
|
||||
textOptions.setElideMode( Qt::ElideMiddle );
|
||||
}
|
||||
|
||||
void addOption( QskSegmentedBar* bar, const Option& option )
|
||||
{
|
||||
this->options += option;
|
||||
|
||||
bar->resetImplicitSize();
|
||||
bar->update();
|
||||
|
||||
Q_EMIT bar->countChanged();
|
||||
|
||||
if ( this->options.count() == 1 )
|
||||
bar->setSelectedIndex( 0 );
|
||||
}
|
||||
|
||||
QVector< Option > options;
|
||||
|
||||
QskTextOptions textOptions;
|
||||
|
||||
int selectedIndex = -1;
|
||||
int currentIndex = -1;
|
||||
|
||||
Qt::Orientation orientation;
|
||||
bool isPressed = false;
|
||||
};
|
||||
|
||||
QskSegmentedBar::QskSegmentedBar( QQuickItem* parent )
|
||||
: QskSegmentedBar( Qt::Horizontal, parent )
|
||||
{
|
||||
}
|
||||
|
||||
QskSegmentedBar::QskSegmentedBar( Qt::Orientation orientation, QQuickItem* parent )
|
||||
: Inherited( parent )
|
||||
, m_data( new PrivateData( orientation ) )
|
||||
{
|
||||
if( orientation == Qt::Horizontal )
|
||||
initSizePolicy( QskSizePolicy::Preferred, QskSizePolicy::Fixed );
|
||||
else
|
||||
initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Preferred );
|
||||
|
||||
setAcceptedMouseButtons( Qt::LeftButton );
|
||||
setWheelEnabled( true );
|
||||
setFocusPolicy( Qt::StrongFocus );
|
||||
|
||||
connect( this, &QskSegmentedBar::currentIndexChanged,
|
||||
this, &QskControl::focusIndicatorRectChanged );
|
||||
}
|
||||
|
||||
QskSegmentedBar::~QskSegmentedBar()
|
||||
{
|
||||
}
|
||||
|
||||
void QskSegmentedBar::setOrientation( Qt::Orientation orientation )
|
||||
{
|
||||
if( orientation != m_data->orientation )
|
||||
{
|
||||
setSizePolicy( sizePolicy( Qt::Vertical ), sizePolicy( Qt::Horizontal ) );
|
||||
m_data->orientation = orientation;
|
||||
|
||||
resetImplicitSize();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
Qt::Orientation QskSegmentedBar::orientation() const
|
||||
{
|
||||
return m_data->orientation;
|
||||
}
|
||||
|
||||
void QskSegmentedBar::setTextOptions( const QskTextOptions& textOptions )
|
||||
{
|
||||
if( textOptions != m_data->textOptions )
|
||||
{
|
||||
m_data->textOptions = textOptions;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
QskTextOptions QskSegmentedBar::textOptions() const
|
||||
{
|
||||
return m_data->textOptions;
|
||||
}
|
||||
|
||||
int QskSegmentedBar::addText( const QString& text )
|
||||
{
|
||||
m_data->addOption( this, Option( QUrl(), text ) );
|
||||
return count() - 1;
|
||||
}
|
||||
|
||||
int QskSegmentedBar::addGraphic( const QUrl& graphicSource )
|
||||
{
|
||||
m_data->addOption( this, Option( graphicSource, QString() ) );
|
||||
return count() - 1;
|
||||
}
|
||||
|
||||
QVariant QskSegmentedBar::optionAt( int index ) const
|
||||
{
|
||||
const auto& options = m_data->options;
|
||||
|
||||
if( index < 0 || index >= options.count() )
|
||||
return QVariantList();
|
||||
|
||||
const auto& option = options[ index ];
|
||||
|
||||
QVariant value;
|
||||
|
||||
if ( option.graphicSource.isValid() )
|
||||
value = QVariant::fromValue( option.graphic );
|
||||
else
|
||||
value = QVariant::fromValue( option.text );
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
QskAspect::Placement QskSegmentedBar::effectivePlacement() const
|
||||
{
|
||||
return static_cast< QskAspect::Placement >( m_data->orientation );
|
||||
}
|
||||
|
||||
void QskSegmentedBar::mousePressEvent( QMouseEvent* event )
|
||||
{
|
||||
const int index = indexAtPosition( event->localPos() );
|
||||
|
||||
if( index < 0 || index >= count() || !m_data->options[ index ].isEnabled )
|
||||
return;
|
||||
|
||||
m_data->isPressed = true;
|
||||
|
||||
if( ( focusPolicy() & Qt::ClickFocus ) == Qt::ClickFocus )
|
||||
{
|
||||
if( !QGuiApplication::styleHints()->setFocusOnTouchRelease() )
|
||||
{
|
||||
if( index != m_data->currentIndex )
|
||||
setCurrentIndex( index );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QskSegmentedBar::mouseReleaseEvent( QMouseEvent* event )
|
||||
{
|
||||
int index = -1;
|
||||
|
||||
if( m_data->isPressed )
|
||||
{
|
||||
m_data->isPressed = false;
|
||||
index = indexAtPosition( event->localPos() );
|
||||
}
|
||||
|
||||
if( index < 0 || !m_data->options[ index ].isEnabled )
|
||||
return;
|
||||
|
||||
if( ( focusPolicy() & Qt::ClickFocus ) == Qt::ClickFocus )
|
||||
{
|
||||
if( QGuiApplication::styleHints()->setFocusOnTouchRelease() )
|
||||
{
|
||||
if( index != m_data->currentIndex )
|
||||
setCurrentIndex( index );
|
||||
}
|
||||
}
|
||||
|
||||
setSelectedIndex( index );
|
||||
}
|
||||
|
||||
void QskSegmentedBar::keyPressEvent( QKeyEvent* event )
|
||||
{
|
||||
switch( event->key() )
|
||||
{
|
||||
case Qt::Key_Up:
|
||||
case Qt::Key_Down:
|
||||
case Qt::Key_Left:
|
||||
case Qt::Key_Right:
|
||||
{
|
||||
bool forwards;
|
||||
|
||||
if ( m_data->orientation == Qt::Vertical )
|
||||
forwards = ( event->key() == Qt::Key_Down );
|
||||
else
|
||||
forwards = ( event->key() == Qt::Key_Right );
|
||||
|
||||
const int index = nextIndex( m_data->selectedIndex, forwards );
|
||||
if ( index != m_data->selectedIndex )
|
||||
{
|
||||
if ( index >= 0 && index < count() )
|
||||
setSelectedIndex( index );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
case Qt::Key_Select:
|
||||
case Qt::Key_Space:
|
||||
|
||||
// stop further processing
|
||||
return;
|
||||
|
||||
default:
|
||||
{
|
||||
const int steps = qskFocusChainIncrement( event );
|
||||
|
||||
if( steps != 0 )
|
||||
{
|
||||
const int index = nextIndex( m_data->currentIndex, steps > 0 );
|
||||
|
||||
if( index != m_data->currentIndex )
|
||||
setCurrentIndex( index );
|
||||
|
||||
if( index >= 0 )
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Inherited::keyPressEvent( event );
|
||||
}
|
||||
|
||||
void QskSegmentedBar::keyReleaseEvent( QKeyEvent* event )
|
||||
{
|
||||
if( event->key() == Qt::Key_Select || event->key() == Qt::Key_Space )
|
||||
{
|
||||
if( m_data->currentIndex >= 0 )
|
||||
setSelectedIndex( m_data->currentIndex );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Inherited::keyReleaseEvent( event );
|
||||
}
|
||||
|
||||
void QskSegmentedBar::focusInEvent( QFocusEvent* event )
|
||||
{
|
||||
int index = m_data->currentIndex;
|
||||
|
||||
switch( event->reason() )
|
||||
{
|
||||
case Qt::TabFocusReason:
|
||||
{
|
||||
index = nextIndex( -1, true );
|
||||
break;
|
||||
}
|
||||
|
||||
case Qt::BacktabFocusReason:
|
||||
{
|
||||
index = nextIndex( -1, false );
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
if( index < 0 || index >= count() )
|
||||
index = nextIndex( -1, true );
|
||||
}
|
||||
}
|
||||
|
||||
if( index != m_data->currentIndex )
|
||||
setCurrentIndex( index );
|
||||
|
||||
Inherited::focusInEvent( event );
|
||||
}
|
||||
|
||||
void QskSegmentedBar::clear()
|
||||
{
|
||||
if( count() == 0 )
|
||||
return;
|
||||
|
||||
m_data->options.clear();
|
||||
Q_EMIT countChanged();
|
||||
|
||||
if( m_data->selectedIndex >= 0 )
|
||||
{
|
||||
m_data->selectedIndex = -1;
|
||||
Q_EMIT selectedIndexChanged( m_data->selectedIndex );
|
||||
}
|
||||
|
||||
if( m_data->currentIndex >= 0 )
|
||||
{
|
||||
m_data->currentIndex = -1;
|
||||
Q_EMIT currentIndexChanged( m_data->currentIndex );
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void QskSegmentedBar::setCurrentIndex( int index )
|
||||
{
|
||||
const auto& options = m_data->options;
|
||||
|
||||
if( ( index < 0 ) || ( index >= options.count() )
|
||||
|| !options[ index ].isEnabled )
|
||||
{
|
||||
index = -1;
|
||||
}
|
||||
|
||||
if( index != m_data->currentIndex )
|
||||
{
|
||||
m_data->currentIndex = index;
|
||||
Q_EMIT currentIndexChanged( index );
|
||||
}
|
||||
}
|
||||
|
||||
int QskSegmentedBar::currentIndex() const
|
||||
{
|
||||
return m_data->currentIndex;
|
||||
}
|
||||
|
||||
void QskSegmentedBar::setSelectedIndex( int index )
|
||||
{
|
||||
if( index < 0 || index >= m_data->options.count() )
|
||||
{
|
||||
index = -1;
|
||||
}
|
||||
else if ( !m_data->options[ index ].isEnabled )
|
||||
{
|
||||
index = -1; // ???
|
||||
}
|
||||
|
||||
if( index != m_data->selectedIndex )
|
||||
{
|
||||
m_data->selectedIndex = index;
|
||||
|
||||
movePositionHint( Cursor, index );
|
||||
update();
|
||||
|
||||
Q_EMIT selectedIndexChanged( index );
|
||||
}
|
||||
}
|
||||
|
||||
int QskSegmentedBar::selectedIndex() const
|
||||
{
|
||||
return m_data->selectedIndex;
|
||||
}
|
||||
|
||||
int QskSegmentedBar::nextIndex( int index, bool forwards ) const
|
||||
{
|
||||
const auto& options = m_data->options;
|
||||
const int count = options.count();
|
||||
|
||||
if( forwards )
|
||||
{
|
||||
if( index < 0 || index >= count )
|
||||
index = -1;
|
||||
|
||||
while( ++index < count )
|
||||
{
|
||||
if( options[ index ].isEnabled )
|
||||
return index;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( index < 0 || index >= count )
|
||||
index = count;
|
||||
|
||||
while( --index >= 0 )
|
||||
{
|
||||
if( options[ index ].isEnabled )
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int QskSegmentedBar::count() const
|
||||
{
|
||||
return m_data->options.count();
|
||||
}
|
||||
|
||||
void QskSegmentedBar::setSegmentEnabled( int index, bool enabled )
|
||||
{
|
||||
auto& options = m_data->options;
|
||||
|
||||
if( ( index < 0 ) || ( index >= options.count() )
|
||||
|| ( options[ index ].isEnabled == enabled ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
options[ index ].isEnabled = enabled;
|
||||
|
||||
if( !enabled )
|
||||
{
|
||||
if( m_data->currentIndex == index )
|
||||
setCurrentIndex( -1 );
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
bool QskSegmentedBar::isSegmentEnabled( int index ) const
|
||||
{
|
||||
const auto& options = m_data->options;
|
||||
|
||||
if( index < 0 || index >= options.count() )
|
||||
return false;
|
||||
|
||||
return options[ index ].isEnabled;
|
||||
}
|
||||
|
||||
int QskSegmentedBar::indexAtPosition( const QPointF& pos ) const
|
||||
{
|
||||
return effectiveSkinlet()->sampleIndexAt( this,
|
||||
contentsRect(), QskSegmentedBar::Segment, pos );
|
||||
}
|
||||
|
||||
QRectF QskSegmentedBar::focusIndicatorRect() const
|
||||
{
|
||||
if( m_data->currentIndex >= 0 )
|
||||
{
|
||||
return effectiveSkinlet()->sampleRect( this,
|
||||
contentsRect(), QskSegmentedBar::Segment, m_data->currentIndex );
|
||||
}
|
||||
|
||||
return Inherited::focusIndicatorRect();
|
||||
}
|
||||
|
||||
#include "moc_QskSegmentedBar.cpp"
|
95
src/controls/QskSegmentedBar.h
Normal file
95
src/controls/QskSegmentedBar.h
Normal file
@ -0,0 +1,95 @@
|
||||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
||||
* This file may be used under the terms of the QSkinny License, Version 1.0
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef QSK_SEGMENTED_BAR_H
|
||||
#define QSK_SEGMENTED_BAR_H
|
||||
|
||||
#include "QskControl.h"
|
||||
|
||||
#include <qurl.h>
|
||||
#include <qstring.h>
|
||||
|
||||
class QskTextOptions;
|
||||
class QskGraphic;
|
||||
|
||||
class QSK_EXPORT QskSegmentedBar : public QskControl
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY( Qt::Orientation orientation READ orientation
|
||||
WRITE setOrientation NOTIFY orientationChanged )
|
||||
|
||||
Q_PROPERTY( int selectedIndex READ selectedIndex
|
||||
WRITE setSelectedIndex NOTIFY selectedIndexChanged USER true )
|
||||
|
||||
Q_PROPERTY( int currentIndex READ currentIndex
|
||||
WRITE setCurrentIndex NOTIFY currentIndexChanged )
|
||||
|
||||
Q_PROPERTY( int count READ count NOTIFY countChanged )
|
||||
|
||||
using Inherited = QskControl;
|
||||
|
||||
public:
|
||||
QSK_SUBCONTROLS( Panel, Segment, Cursor, Text, Graphic )
|
||||
QSK_STATES( Selected )
|
||||
|
||||
QskSegmentedBar( QQuickItem* parent = nullptr );
|
||||
QskSegmentedBar( Qt::Orientation, QQuickItem* parent = nullptr );
|
||||
|
||||
~QskSegmentedBar() override;
|
||||
|
||||
void setOrientation( Qt::Orientation );
|
||||
Qt::Orientation orientation() const;
|
||||
|
||||
void setTextOptions( const QskTextOptions& );
|
||||
QskTextOptions textOptions() const;
|
||||
|
||||
int addText( const QString& );
|
||||
int addGraphic( const QUrl& );
|
||||
|
||||
void clear();
|
||||
|
||||
int selectedIndex() const;
|
||||
int currentIndex() const;
|
||||
|
||||
int count() const;
|
||||
|
||||
QVariant optionAt( int ) const;
|
||||
|
||||
void setSegmentEnabled( int, bool );
|
||||
bool isSegmentEnabled( int ) const;
|
||||
|
||||
int indexAtPosition( const QPointF& ) const;
|
||||
|
||||
QRectF focusIndicatorRect() const override final;
|
||||
QskAspect::Placement effectivePlacement() const override;
|
||||
|
||||
public Q_SLOTS:
|
||||
void setSelectedIndex( int index );
|
||||
void setCurrentIndex( int index );
|
||||
|
||||
Q_SIGNALS:
|
||||
void selectedIndexChanged( int );
|
||||
void currentIndexChanged( int );
|
||||
void countChanged();
|
||||
void orientationChanged();
|
||||
|
||||
protected:
|
||||
void mouseReleaseEvent( QMouseEvent* ) override;
|
||||
void mousePressEvent( QMouseEvent* ) override;
|
||||
|
||||
void keyPressEvent( QKeyEvent* ) override;
|
||||
void keyReleaseEvent( QKeyEvent* ) override;
|
||||
|
||||
void focusInEvent( QFocusEvent* ) override;
|
||||
|
||||
private:
|
||||
int nextIndex( int index, bool forward ) const;
|
||||
|
||||
class PrivateData;
|
||||
std::unique_ptr< PrivateData > m_data;
|
||||
};
|
||||
|
||||
#endif
|
322
src/controls/QskSegmentedBarSkinlet.cpp
Normal file
322
src/controls/QskSegmentedBarSkinlet.cpp
Normal file
@ -0,0 +1,322 @@
|
||||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
||||
* This file may be used under the terms of the QSkinny License, Version 1.0
|
||||
*****************************************************************************/
|
||||
|
||||
#include "QskSegmentedBarSkinlet.h"
|
||||
#include "QskSegmentedBar.h"
|
||||
|
||||
#include "QskGraphicNode.h"
|
||||
#include "QskGraphic.h"
|
||||
#include "QskColorFilter.h"
|
||||
#include "QskTextNode.h"
|
||||
#include "QskTextOptions.h"
|
||||
#include "QskSGNode.h"
|
||||
#include "QskFunctions.h"
|
||||
|
||||
#include <qfontmetrics.h>
|
||||
#include <qmath.h>
|
||||
|
||||
QskSegmentedBarSkinlet::QskSegmentedBarSkinlet( QskSkin* skin )
|
||||
: Inherited( skin )
|
||||
{
|
||||
setNodeRoles( { PanelRole, SegmentRole, CursorRole, TextRole, GraphicRole } );
|
||||
}
|
||||
|
||||
QskSegmentedBarSkinlet::~QskSegmentedBarSkinlet() = default;
|
||||
|
||||
QRectF QskSegmentedBarSkinlet::subControlRect(
|
||||
const QskSkinnable* skinnable, const QRectF& contentsRect,
|
||||
QskAspect::Subcontrol subControl ) const
|
||||
{
|
||||
using Q = QskSegmentedBar;
|
||||
|
||||
const auto bar = static_cast< const QskSegmentedBar* >( skinnable );
|
||||
|
||||
if( subControl == Q::Panel )
|
||||
return contentsRect;
|
||||
|
||||
if( subControl == Q::Cursor )
|
||||
return cursorRect( bar, contentsRect );
|
||||
|
||||
return Inherited::subControlRect( skinnable, contentsRect, subControl );
|
||||
}
|
||||
|
||||
QRectF QskSegmentedBarSkinlet::cursorRect(
|
||||
const QskSegmentedBar* bar, const QRectF& contentsRect ) const
|
||||
{
|
||||
using Q = QskSegmentedBar;
|
||||
|
||||
if( bar->selectedIndex() < 0 || bar->count() <= 0 )
|
||||
return QRectF();
|
||||
|
||||
auto rect = subControlRect( bar, contentsRect, Q::Panel );
|
||||
rect = bar->innerBox( Q::Panel, rect );
|
||||
|
||||
if( rect.isEmpty() )
|
||||
return QRectF();
|
||||
|
||||
// position is related to the index: 2.5 means
|
||||
// the cursor is between segment[2] and segment[3]
|
||||
|
||||
const qreal position = bar->positionHint( Q::Cursor );
|
||||
|
||||
const int index1 = qFloor( position );
|
||||
const int index2 = qCeil( position );
|
||||
|
||||
auto cursorRect = segmentRect( bar, contentsRect, index1 );
|
||||
if ( index1 != index2 )
|
||||
{
|
||||
const auto targetRect = segmentRect( bar, contentsRect, index2 );
|
||||
cursorRect = qskInterpolatedRect( cursorRect, targetRect, position - index1 );
|
||||
}
|
||||
|
||||
return cursorRect;
|
||||
}
|
||||
|
||||
QRectF QskSegmentedBarSkinlet::segmentRect(
|
||||
const QskSegmentedBar* bar, const QRectF& contentsRect, int index ) const
|
||||
{
|
||||
using Q = QskSegmentedBar;
|
||||
|
||||
const auto spacing = bar->spacingHint( Q::Panel );
|
||||
const auto count = bar->count();
|
||||
|
||||
auto rect = subControlRect( bar, contentsRect, Q::Panel );
|
||||
rect = bar->innerBox( Q::Panel, rect );
|
||||
|
||||
if( bar->orientation() == Qt::Horizontal )
|
||||
{
|
||||
const qreal w = ( rect.width() - ( count - 1 ) * spacing ) / count;
|
||||
|
||||
rect.setLeft( index * ( w + spacing ) );
|
||||
rect.setWidth( w );
|
||||
}
|
||||
else
|
||||
{
|
||||
const qreal h = ( rect.height() - ( count - 1 ) * spacing ) / count;
|
||||
|
||||
rect.setTop( index * ( h + spacing ) );
|
||||
rect.setHeight( h );
|
||||
}
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
QSGNode* QskSegmentedBarSkinlet::updateSubNode(
|
||||
const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const
|
||||
{
|
||||
using Q = QskSegmentedBar;
|
||||
|
||||
switch( nodeRole )
|
||||
{
|
||||
case PanelRole:
|
||||
return updateBoxNode( skinnable, node, Q::Panel );
|
||||
|
||||
case CursorRole:
|
||||
return updateBoxNode( skinnable, node, Q::Cursor );
|
||||
|
||||
case SegmentRole:
|
||||
return updateSeriesNode( skinnable, Q::Segment, node );
|
||||
|
||||
case TextRole:
|
||||
return updateSeriesNode( skinnable, Q::Text, node );
|
||||
|
||||
case GraphicRole:
|
||||
return updateSeriesNode( skinnable, Q::Graphic, node );
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QSizeF QskSegmentedBarSkinlet::segmentSizeHint( const QskSegmentedBar* bar ) const
|
||||
{
|
||||
qreal widthMax = 0;
|
||||
qreal graphicRatioMax = 0;
|
||||
|
||||
const QFontMetricsF fm( bar->effectiveFont( QskSegmentedBar::Text ) );
|
||||
|
||||
for ( int i = 0; i < bar->count(); i++ )
|
||||
{
|
||||
const auto value = bar->optionAt( i );
|
||||
|
||||
if ( value.canConvert< QskGraphic >() )
|
||||
{
|
||||
const auto graphic = value.value< QskGraphic >();
|
||||
|
||||
if ( !graphic.isNull() )
|
||||
{
|
||||
const auto sz = graphic.defaultSize();
|
||||
|
||||
if( sz.isValid() )
|
||||
{
|
||||
const qreal ratio = sz.width() / sz.height();
|
||||
|
||||
if( graphicRatioMax < ratio )
|
||||
graphicRatioMax = ratio;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( value.canConvert< QString >() )
|
||||
{
|
||||
const auto text = value.value< QString >();
|
||||
if ( !text.isEmpty() )
|
||||
{
|
||||
const auto sz = fm.size( Qt::TextShowMnemonic, text );
|
||||
|
||||
if( sz.width() > widthMax )
|
||||
widthMax = sz.width();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( graphicRatioMax > 0 )
|
||||
{
|
||||
const qreal w = fm.height() * graphicRatioMax;
|
||||
|
||||
if( w > widthMax )
|
||||
widthMax = w;
|
||||
}
|
||||
|
||||
return bar->outerBoxSize( QskSegmentedBar::Segment, QSizeF( widthMax, fm.height() ) );
|
||||
}
|
||||
|
||||
QSizeF QskSegmentedBarSkinlet::sizeHint( const QskSkinnable* skinnable,
|
||||
Qt::SizeHint which, const QSizeF& ) const
|
||||
{
|
||||
using Q = QskSegmentedBar;
|
||||
|
||||
if ( which != Qt::PreferredSize )
|
||||
return QSizeF();
|
||||
|
||||
const auto count = sampleCount( skinnable, Q::Segment );
|
||||
if( count == 0 )
|
||||
return QSizeF( 0, 0 );
|
||||
|
||||
qreal w = 0;
|
||||
qreal h = 0;
|
||||
|
||||
if ( count > 0 )
|
||||
{
|
||||
const qreal spacing = skinnable->spacingHint( Q::Panel );
|
||||
|
||||
const auto bar = static_cast< const QskSegmentedBar* >( skinnable );
|
||||
const auto segmentSize = segmentSizeHint( bar );
|
||||
|
||||
if( bar->orientation() == Qt::Horizontal )
|
||||
{
|
||||
w = count * segmentSize.width() + ( count - 1 ) * spacing;
|
||||
h = segmentSize.height();
|
||||
}
|
||||
else
|
||||
{
|
||||
w = segmentSize.width();
|
||||
h = count * segmentSize.height() + ( count - 1 ) * spacing;
|
||||
}
|
||||
}
|
||||
|
||||
const auto hint = skinnable->outerBoxSize( Q::Panel, QSizeF( w, h ) );
|
||||
return hint.expandedTo( skinnable->strutSizeHint( Q::Panel ) );
|
||||
}
|
||||
|
||||
int QskSegmentedBarSkinlet::sampleCount(
|
||||
const QskSkinnable* skinnable, QskAspect::Subcontrol ) const
|
||||
{
|
||||
const auto bar = static_cast< const QskSegmentedBar* >( skinnable );
|
||||
return bar->count();
|
||||
}
|
||||
|
||||
QRectF QskSegmentedBarSkinlet::sampleRect( const QskSkinnable* skinnable,
|
||||
const QRectF& contentsRect, QskAspect::Subcontrol subControl, int index ) const
|
||||
{
|
||||
using Q = QskSegmentedBar;
|
||||
|
||||
if ( subControl == Q::Segment )
|
||||
{
|
||||
const auto bar = static_cast< const QskSegmentedBar* >( skinnable );
|
||||
return segmentRect( bar, contentsRect, index );
|
||||
}
|
||||
|
||||
if ( subControl == Q::Text || subControl == Q::Graphic )
|
||||
{
|
||||
const auto rect = sampleRect( skinnable, contentsRect, Q::Segment, index );
|
||||
return skinnable->innerBox( Q::Segment, rect );
|
||||
}
|
||||
|
||||
return Inherited::sampleRect( skinnable, contentsRect, subControl, index );
|
||||
}
|
||||
|
||||
QskAspect::States QskSegmentedBarSkinlet::sampleStates(
|
||||
const QskSkinnable* skinnable, QskAspect::Subcontrol subControl, int index ) const
|
||||
{
|
||||
using Q = QskSegmentedBar;
|
||||
|
||||
auto states = Inherited::sampleStates( skinnable, subControl, index );
|
||||
|
||||
if ( subControl == Q::Segment || subControl == Q::Graphic || subControl == Q::Text )
|
||||
{
|
||||
const auto bar = static_cast< const QskSegmentedBar* >( skinnable );
|
||||
|
||||
if ( bar->isSegmentEnabled( index ) )
|
||||
{
|
||||
if ( bar->selectedIndex() == index )
|
||||
states |= Q::Selected;
|
||||
}
|
||||
else
|
||||
{
|
||||
states |= Q::Disabled;
|
||||
}
|
||||
}
|
||||
|
||||
return states;
|
||||
}
|
||||
|
||||
QSGNode* QskSegmentedBarSkinlet::updateSampleNode( const QskSkinnable* skinnable,
|
||||
QskAspect::Subcontrol subControl, int index, QSGNode* node ) const
|
||||
{
|
||||
using Q = QskSegmentedBar;
|
||||
|
||||
auto bar = static_cast< const QskSegmentedBar* >( skinnable );
|
||||
|
||||
const auto rect = sampleRect( bar, bar->contentsRect(), subControl, index );
|
||||
|
||||
if ( subControl == Q::Segment )
|
||||
{
|
||||
return updateBoxNode( skinnable, node, rect, subControl );
|
||||
}
|
||||
|
||||
const auto alignment = bar->alignmentHint( subControl, Qt::AlignCenter );
|
||||
|
||||
if ( subControl == Q::Text )
|
||||
{
|
||||
const auto value = bar->optionAt( index );
|
||||
if ( value.canConvert< QString >() )
|
||||
{
|
||||
const auto text = value.value< QString >();
|
||||
|
||||
return QskSkinlet::updateTextNode( bar, node,
|
||||
rect, alignment, text, bar->textOptions(), Q::Text );
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if ( subControl == Q::Graphic )
|
||||
{
|
||||
const auto value = bar->optionAt( index );
|
||||
if ( value.canConvert< QskGraphic >() )
|
||||
{
|
||||
const auto graphic = value.value< QskGraphic >();
|
||||
const auto filter = bar->effectiveGraphicFilter( subControl );
|
||||
|
||||
return QskSkinlet::updateGraphicNode(
|
||||
bar, node, graphic, filter, rect, alignment );
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return Inherited::updateSampleNode( skinnable, subControl, index, node );
|
||||
}
|
||||
|
||||
#include "moc_QskSegmentedBarSkinlet.cpp"
|
63
src/controls/QskSegmentedBarSkinlet.h
Normal file
63
src/controls/QskSegmentedBarSkinlet.h
Normal file
@ -0,0 +1,63 @@
|
||||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
||||
* This file may be used under the terms of the QSkinny License, Version 1.0
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef QSK_SEGMENTED_BAR_SKINLET_H
|
||||
#define QSK_SEGMENTED_BAR_SKINLET_H
|
||||
|
||||
#include "QskSkinlet.h"
|
||||
|
||||
class QskSegmentedBar;
|
||||
|
||||
class QSK_EXPORT QskSegmentedBarSkinlet : public QskSkinlet
|
||||
{
|
||||
Q_GADGET
|
||||
|
||||
using Inherited = QskSkinlet;
|
||||
|
||||
public:
|
||||
enum NodeRole
|
||||
{
|
||||
PanelRole,
|
||||
SegmentRole,
|
||||
CursorRole,
|
||||
|
||||
TextRole,
|
||||
GraphicRole,
|
||||
|
||||
RoleCount
|
||||
};
|
||||
|
||||
Q_INVOKABLE QskSegmentedBarSkinlet( QskSkin* = nullptr );
|
||||
~QskSegmentedBarSkinlet() override;
|
||||
|
||||
QRectF subControlRect( const QskSkinnable*, const QRectF&,
|
||||
QskAspect::Subcontrol ) const override;
|
||||
|
||||
QSizeF sizeHint( const QskSkinnable*,
|
||||
Qt::SizeHint, const QSizeF& ) const override;
|
||||
|
||||
int sampleCount( const QskSkinnable*, QskAspect::Subcontrol ) const override;
|
||||
|
||||
QRectF sampleRect( const QskSkinnable*,
|
||||
const QRectF&, QskAspect::Subcontrol, int index ) const override;
|
||||
|
||||
QskAspect::States sampleStates( const QskSkinnable*,
|
||||
QskAspect::Subcontrol, int index ) const override;
|
||||
|
||||
protected:
|
||||
QSGNode* updateSubNode( const QskSkinnable*,
|
||||
quint8 nodeRole, QSGNode* ) const override;
|
||||
|
||||
QSGNode* updateSampleNode( const QskSkinnable*,
|
||||
QskAspect::Subcontrol, int index, QSGNode* ) const override;
|
||||
|
||||
private:
|
||||
QSizeF segmentSizeHint( const QskSegmentedBar* ) const;
|
||||
|
||||
QRectF segmentRect( const QskSegmentedBar*, const QRectF&, int index ) const;
|
||||
QRectF cursorRect( const QskSegmentedBar*, const QRectF& ) const;
|
||||
};
|
||||
|
||||
#endif
|
@ -44,18 +44,39 @@ QSK_QT_PRIVATE_END
|
||||
#include "QskMenu.h"
|
||||
#include "QskMenuSkinlet.h"
|
||||
|
||||
#include "QskPageIndicator.h"
|
||||
#include "QskPageIndicatorSkinlet.h"
|
||||
|
||||
#include "QskPopup.h"
|
||||
#include "QskPopupSkinlet.h"
|
||||
|
||||
#include "QskProgressBar.h"
|
||||
#include "QskProgressBarSkinlet.h"
|
||||
|
||||
#include "QskPushButton.h"
|
||||
#include "QskPushButtonSkinlet.h"
|
||||
|
||||
#include "QskScrollView.h"
|
||||
#include "QskScrollViewSkinlet.h"
|
||||
|
||||
#include "QskSegmentedBar.h"
|
||||
#include "QskSegmentedBarSkinlet.h"
|
||||
|
||||
#include "QskSeparator.h"
|
||||
#include "QskSeparatorSkinlet.h"
|
||||
|
||||
#include "QskSlider.h"
|
||||
#include "QskSliderSkinlet.h"
|
||||
|
||||
#include "QskSubWindow.h"
|
||||
#include "QskSubWindowSkinlet.h"
|
||||
|
||||
#include "QskSubWindowArea.h"
|
||||
#include "QskSubWindowAreaSkinlet.h"
|
||||
|
||||
#include "QskSwitchButton.h"
|
||||
#include "QskSwitchButtonSkinlet.h"
|
||||
|
||||
#include "QskTabButton.h"
|
||||
#include "QskTabButtonSkinlet.h"
|
||||
|
||||
@ -68,24 +89,6 @@ QSK_QT_PRIVATE_END
|
||||
#include "QskTextInput.h"
|
||||
#include "QskTextInputSkinlet.h"
|
||||
|
||||
#include "QskSeparator.h"
|
||||
#include "QskSeparatorSkinlet.h"
|
||||
|
||||
#include "QskSubWindow.h"
|
||||
#include "QskSubWindowSkinlet.h"
|
||||
|
||||
#include "QskSubWindowArea.h"
|
||||
#include "QskSubWindowAreaSkinlet.h"
|
||||
|
||||
#include "QskSwitchButton.h"
|
||||
#include "QskSwitchButtonSkinlet.h"
|
||||
|
||||
#include "QskPageIndicator.h"
|
||||
#include "QskPageIndicatorSkinlet.h"
|
||||
|
||||
#include "QskProgressBar.h"
|
||||
#include "QskProgressBarSkinlet.h"
|
||||
|
||||
#include "QskStatusIndicator.h"
|
||||
#include "QskStatusIndicatorSkinlet.h"
|
||||
|
||||
@ -155,6 +158,7 @@ QskSkin::QskSkin( QObject* parent )
|
||||
declareSkinlet< QskMenu, QskMenuSkinlet >();
|
||||
declareSkinlet< QskPushButton, QskPushButtonSkinlet >();
|
||||
declareSkinlet< QskScrollView, QskScrollViewSkinlet >();
|
||||
declareSkinlet< QskSegmentedBar, QskSegmentedBarSkinlet >();
|
||||
declareSkinlet< QskSeparator, QskSeparatorSkinlet >();
|
||||
declareSkinlet< QskSlider, QskSliderSkinlet >();
|
||||
declareSkinlet< QskStatusIndicator, QskStatusIndicatorSkinlet >();
|
||||
|
@ -179,6 +179,8 @@ HEADERS += \
|
||||
controls/QskScrollBox.h \
|
||||
controls/QskScrollView.h \
|
||||
controls/QskScrollViewSkinlet.h \
|
||||
controls/QskSegmentedBar.h \
|
||||
controls/QskSegmentedBarSkinlet.h \
|
||||
controls/QskSeparator.h \
|
||||
controls/QskSeparatorSkinlet.h \
|
||||
controls/QskSetup.h \
|
||||
@ -261,6 +263,8 @@ SOURCES += \
|
||||
controls/QskScrollBox.cpp \
|
||||
controls/QskScrollView.cpp \
|
||||
controls/QskScrollViewSkinlet.cpp \
|
||||
controls/QskSegmentedBar.cpp \
|
||||
controls/QskSegmentedBarSkinlet.cpp \
|
||||
controls/QskSeparator.cpp \
|
||||
controls/QskSeparatorSkinlet.cpp \
|
||||
controls/QskSetup.cpp \
|
||||
|
Loading…
x
Reference in New Issue
Block a user