2017-07-27 08:26:23 +02:00
|
|
|
/******************************************************************************
|
|
|
|
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
|
|
|
* This file may be used under the terms of the 3-clause BSD License
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
#include <SkinnyShapeFactory.h>
|
2018-08-03 08:15:28 +02:00
|
|
|
#include <SkinnyShortcut.h>
|
2017-07-27 08:26:23 +02:00
|
|
|
|
2017-07-30 14:31:05 +02:00
|
|
|
#include <QskAspect.h>
|
2018-01-16 20:26:18 +01:00
|
|
|
#include <QskBoxBorderColors.h>
|
2018-08-03 08:15:28 +02:00
|
|
|
#include <QskBoxBorderMetrics.h>
|
|
|
|
#include <QskBoxShapeMetrics.h>
|
2018-01-12 15:46:15 +01:00
|
|
|
#include <QskFocusIndicator.h>
|
2018-08-03 08:15:28 +02:00
|
|
|
#include <QskGraphic.h>
|
|
|
|
#include <QskLinearBox.h>
|
|
|
|
#include <QskObjectCounter.h>
|
|
|
|
#include <QskPushButton.h>
|
|
|
|
#include <QskScrollArea.h>
|
2020-01-17 13:58:36 +01:00
|
|
|
#include <QskQuick.h>
|
2018-08-03 08:15:28 +02:00
|
|
|
#include <QskWindow.h>
|
2017-07-27 08:26:23 +02:00
|
|
|
|
|
|
|
#include <QGuiApplication>
|
|
|
|
#include <QPainter>
|
|
|
|
|
2020-03-12 09:53:55 +01:00
|
|
|
#include <cstdlib>
|
|
|
|
|
2020-01-17 13:58:36 +01:00
|
|
|
#define HIDE_NODES 1
|
|
|
|
|
2017-07-27 08:26:23 +02:00
|
|
|
const int gridSize = 20;
|
|
|
|
const int thumbnailSize = 150;
|
|
|
|
|
|
|
|
static QColor randomColor()
|
|
|
|
{
|
|
|
|
static const char* colors[] =
|
|
|
|
{
|
|
|
|
"HotPink",
|
|
|
|
"MediumVioletRed",
|
|
|
|
"FireBrick",
|
|
|
|
"PeachPuff",
|
|
|
|
"Gold",
|
|
|
|
"RosyBrown",
|
|
|
|
"Maroon",
|
|
|
|
"Turquoise",
|
|
|
|
"CadetBlue",
|
|
|
|
"Teal",
|
|
|
|
"LightSteelBlue",
|
|
|
|
"CornflowerBlue",
|
|
|
|
"Thistle",
|
|
|
|
"Plum",
|
|
|
|
"DarkViolet",
|
|
|
|
"DarkSlateBlue",
|
|
|
|
"AntiqueWhite",
|
|
|
|
"MistyRose",
|
|
|
|
"Silver",
|
|
|
|
"DarkSlateGray"
|
|
|
|
};
|
|
|
|
|
2020-03-12 09:53:55 +01:00
|
|
|
const int index = std::rand() % int( ( sizeof( colors ) / sizeof( colors[ 0 ] ) ) );
|
2017-07-27 08:26:23 +02:00
|
|
|
return QColor( colors[ index ] );
|
|
|
|
}
|
|
|
|
|
|
|
|
static int randomShape()
|
|
|
|
{
|
2020-03-12 09:53:55 +01:00
|
|
|
return std::rand() % SkinnyShapeFactory::ShapeCount;
|
2017-07-27 08:26:23 +02:00
|
|
|
}
|
|
|
|
|
2018-01-12 15:46:15 +01:00
|
|
|
class Thumbnail : public QskPushButton
|
2017-07-27 08:26:23 +02:00
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
public:
|
|
|
|
Thumbnail( const QColor& color, int shape, QQuickItem* parentItem )
|
|
|
|
: QskPushButton( parentItem )
|
2017-07-27 08:26:23 +02:00
|
|
|
{
|
|
|
|
const QSizeF size( thumbnailSize, thumbnailSize );
|
|
|
|
|
2020-01-17 13:58:36 +01:00
|
|
|
setGraphic( thumbnailGraphic( color, shape, size ) );
|
|
|
|
setFixedSize( size );
|
2017-07-27 08:26:23 +02:00
|
|
|
|
2020-01-17 13:58:36 +01:00
|
|
|
setFlat( true );
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
QskGraphic thumbnailGraphic( const QColor& color,
|
|
|
|
int shape, const QSizeF& size ) const
|
|
|
|
{
|
|
|
|
const auto path = SkinnyShapeFactory::shapePath(
|
|
|
|
static_cast< SkinnyShapeFactory::Shape >( shape ), size );
|
|
|
|
|
|
|
|
QskGraphic graphic;
|
2020-12-05 15:09:31 +01:00
|
|
|
|
2017-07-27 08:26:23 +02:00
|
|
|
QPen pen( Qt::black, 3 );
|
|
|
|
pen.setJoinStyle( Qt::MiterJoin );
|
|
|
|
pen.setCosmetic( true );
|
|
|
|
|
|
|
|
QPainter painter( &graphic );
|
|
|
|
painter.setRenderHint( QPainter::Antialiasing, true );
|
|
|
|
painter.setPen( pen );
|
|
|
|
painter.setBrush( color );
|
|
|
|
|
2020-01-17 13:58:36 +01:00
|
|
|
painter.drawPath( path );
|
2017-07-27 08:26:23 +02:00
|
|
|
painter.end();
|
|
|
|
|
2020-01-17 13:58:36 +01:00
|
|
|
return graphic;
|
2017-07-27 08:26:23 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class IconGrid : public QskLinearBox
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
public:
|
|
|
|
IconGrid( QQuickItem* parentItem = nullptr )
|
|
|
|
: QskLinearBox( Qt::Horizontal, gridSize, parentItem )
|
2017-07-27 08:26:23 +02:00
|
|
|
{
|
|
|
|
setMargins( 20 );
|
|
|
|
setSpacing( 20 );
|
|
|
|
|
|
|
|
for ( int col = 0; col < gridSize; col++ )
|
|
|
|
{
|
|
|
|
for ( int row = 0; row < gridSize; row++ )
|
2018-08-03 08:15:28 +02:00
|
|
|
( void ) new Thumbnail( randomColor(), randomShape(), this );
|
2017-07-27 08:26:23 +02:00
|
|
|
}
|
2020-01-17 13:58:36 +01:00
|
|
|
|
|
|
|
#if HIDE_NODES
|
|
|
|
/*
|
|
|
|
When having too many nodes, the scene graph becomes horribly slow.
|
|
|
|
So we explicitely hide all items outside the visible area
|
|
|
|
( see updateVisibilities below ) and make use of the DeferredUpdate and
|
|
|
|
CleanupOnVisibility features of QskQuickItem.
|
|
|
|
*/
|
|
|
|
setSize( sizeConstraint() );
|
|
|
|
updateLayout(); // so that every item has its initial geometry
|
|
|
|
|
2020-11-11 10:31:39 +01:00
|
|
|
for ( int i = 0; i < elementCount(); i++ )
|
2020-01-17 13:58:36 +01:00
|
|
|
{
|
2020-12-27 16:08:52 +01:00
|
|
|
if ( auto control = qskControlCast( itemAtIndex( i ) ) )
|
2020-01-17 13:58:36 +01:00
|
|
|
{
|
|
|
|
// to support the optimizations in ScrollArea::updateVisibilities
|
2020-12-27 16:08:52 +01:00
|
|
|
control->setLayoutHint( RetainSizeWhenHidden, true );
|
|
|
|
control->setVisible( false );
|
2020-01-17 13:58:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
#if HIDE_NODES
|
|
|
|
void updateVisibilities( const QRectF& viewPort )
|
|
|
|
{
|
|
|
|
if ( !isEmpty() && viewPort != m_viewPort )
|
|
|
|
{
|
|
|
|
setItemsVisible( m_viewPort, false );
|
|
|
|
setItemsVisible( viewPort, true );
|
|
|
|
|
|
|
|
m_viewPort = viewPort;
|
|
|
|
}
|
2017-07-27 08:26:23 +02:00
|
|
|
}
|
2020-01-17 13:58:36 +01:00
|
|
|
|
|
|
|
private:
|
|
|
|
void setItemsVisible( const QRectF& rect, bool on )
|
|
|
|
{
|
|
|
|
const int dim = dimension();
|
|
|
|
|
|
|
|
// we know, that all items have the same size
|
|
|
|
const auto itemSize = qskItemSize( itemAtIndex( 0 ) );
|
|
|
|
|
|
|
|
const int rowMin = rect.top() / ( itemSize.height() + spacing() );
|
|
|
|
const int rowMax = rect.bottom() / ( itemSize.height() + spacing() );
|
|
|
|
|
|
|
|
const int colMin = rect.left() / ( itemSize.width() + spacing() );
|
|
|
|
const int colMax = rect.right() / ( itemSize.height() + spacing() );
|
|
|
|
|
|
|
|
for ( int row = rowMin; row <= rowMax; row++ )
|
|
|
|
{
|
|
|
|
for ( int col = colMin; col <= colMax; col++ )
|
|
|
|
{
|
|
|
|
if ( auto item = itemAtIndex( row * dim + col ) )
|
|
|
|
item->setVisible( on );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QRectF m_viewPort;
|
|
|
|
#endif
|
2017-07-27 08:26:23 +02:00
|
|
|
};
|
|
|
|
|
2017-08-22 20:15:11 +02:00
|
|
|
class ScrollArea : public QskScrollArea
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
public:
|
|
|
|
ScrollArea( QQuickItem* parentItem = nullptr )
|
|
|
|
: QskScrollArea( parentItem )
|
2017-08-22 20:15:11 +02:00
|
|
|
{
|
|
|
|
// settings usually done in the skins
|
2017-10-18 20:00:06 +02:00
|
|
|
setBoxBorderMetricsHint( Viewport, 2 );
|
2017-10-17 17:34:00 +02:00
|
|
|
setBoxShapeHint( Viewport, 20 );
|
|
|
|
|
|
|
|
for ( auto subControl : { HorizontalScrollBar, VerticalScrollBar } )
|
2020-12-15 07:21:12 +01:00
|
|
|
setMetric( subControl | QskAspect::Size, 20 );
|
2017-10-17 17:34:00 +02:00
|
|
|
|
2017-10-18 20:00:06 +02:00
|
|
|
setBoxBorderMetricsHint( VerticalScrollHandle, 1 );
|
2017-10-17 17:34:00 +02:00
|
|
|
setBoxShapeHint( VerticalScrollHandle, 8 );
|
|
|
|
|
2017-10-18 20:00:06 +02:00
|
|
|
setBoxBorderMetricsHint( HorizontalScrollHandle, 1 );
|
2017-10-17 17:34:00 +02:00
|
|
|
setBoxShapeHint( HorizontalScrollHandle, 8 );
|
2019-04-04 18:38:58 +02:00
|
|
|
|
|
|
|
setFlickRecognizerTimeout( 300 );
|
2020-01-17 13:58:36 +01:00
|
|
|
|
|
|
|
connect( this, &QskScrollView::scrollPosChanged,
|
2020-12-05 15:09:31 +01:00
|
|
|
this, &ScrollArea::updateVisibilities );
|
2020-01-17 13:58:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
2020-10-25 17:34:05 +01:00
|
|
|
void geometryChangeEvent( QskGeometryChangeEvent* event ) override
|
2020-01-17 13:58:36 +01:00
|
|
|
{
|
2020-10-25 17:34:05 +01:00
|
|
|
QskScrollArea::geometryChangeEvent( event );
|
2020-01-17 13:58:36 +01:00
|
|
|
updateVisibilities();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
void updateVisibilities()
|
|
|
|
{
|
|
|
|
#if HIDE_NODES
|
|
|
|
const auto box = static_cast< IconGrid* >( scrolledItem() );
|
|
|
|
if ( box )
|
|
|
|
{
|
|
|
|
const QRectF viewPort( scrollPos(), viewContentsRect().size() );
|
|
|
|
box->updateVisibilities( viewPort );
|
|
|
|
}
|
|
|
|
#endif
|
2017-08-22 20:15:11 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-07-27 08:26:23 +02:00
|
|
|
int main( int argc, char* argv[] )
|
|
|
|
{
|
|
|
|
#ifdef ITEM_STATISTICS
|
|
|
|
QskObjectCounter counter( true );
|
|
|
|
#endif
|
|
|
|
|
|
|
|
QGuiApplication app( argc, argv );
|
|
|
|
|
|
|
|
SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts );
|
|
|
|
|
|
|
|
/*
|
2017-07-30 14:31:05 +02:00
|
|
|
In a real world application a thumbnail viewer would probably be implemented
|
2017-07-27 08:26:23 +02:00
|
|
|
with QskScrollView using scene graph node composition - like done
|
2017-07-30 14:31:05 +02:00
|
|
|
with QskListView.
|
2017-07-27 08:26:23 +02:00
|
|
|
|
2018-01-12 15:46:15 +01:00
|
|
|
The thumbnails are implemented as buttons, so that we can see if the gesture
|
2018-08-03 08:15:28 +02:00
|
|
|
recognition for the flicking works without stopping the buttons from being functional.
|
2018-01-12 15:46:15 +01:00
|
|
|
|
2017-07-27 08:26:23 +02:00
|
|
|
This example also shows, that blocking of the scene graph node creation
|
2018-08-03 08:15:28 +02:00
|
|
|
( QskControl::DeferredUpdate + QskControl::CleanupOnVisibility )
|
2017-07-30 14:31:05 +02:00
|
|
|
could be improved to also respect being inside the window or a clip region.
|
2020-01-17 13:58:36 +01:00
|
|
|
To have a similar effect with what is possible today, the code
|
|
|
|
explicitely hide/show(s) the buttons out/inside the viewport.
|
2017-07-27 08:26:23 +02:00
|
|
|
|
|
|
|
But here we only want to demonstrate how QskScrollArea works.
|
|
|
|
*/
|
|
|
|
|
2018-01-16 20:26:18 +01:00
|
|
|
auto box = new QskLinearBox( Qt::Vertical );
|
|
|
|
box->setMargins( 20 );
|
|
|
|
|
|
|
|
auto buttonBox = new QskLinearBox( Qt::Horizontal, box );
|
|
|
|
buttonBox->setSizePolicy( Qt::Vertical, QskSizePolicy::Fixed );
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
new QskPushButton( "Push Me", buttonBox );
|
|
|
|
new QskPushButton( "Push Me", buttonBox );
|
2018-01-16 20:26:18 +01:00
|
|
|
|
2018-11-17 09:22:11 +01:00
|
|
|
auto iconGrid = new IconGrid();
|
|
|
|
|
|
|
|
// don't allow to shrink the iconGrid
|
|
|
|
iconGrid->setSizePolicy( QskSizePolicy::MinimumExpanding,
|
|
|
|
QskSizePolicy::MinimumExpanding );
|
|
|
|
|
2018-01-16 20:26:18 +01:00
|
|
|
auto scrollArea = new ScrollArea( box );
|
2017-07-30 14:31:05 +02:00
|
|
|
scrollArea->setMargins( QMarginsF( 25, 25, 5, 5 ) );
|
2018-11-17 09:22:11 +01:00
|
|
|
scrollArea->setScrolledItem( iconGrid );
|
2017-07-27 08:26:23 +02:00
|
|
|
|
2018-01-16 20:26:18 +01:00
|
|
|
auto focusIndicator = new QskFocusIndicator();
|
|
|
|
focusIndicator->setBoxBorderColorsHint( QskFocusIndicator::Panel, Qt::darkRed );
|
|
|
|
|
2017-07-27 08:26:23 +02:00
|
|
|
QskWindow window;
|
|
|
|
window.resize( 600, 600 );
|
2017-07-30 14:31:05 +02:00
|
|
|
window.setColor( "SteelBlue" );
|
2018-01-16 20:26:18 +01:00
|
|
|
window.addItem( box );
|
|
|
|
window.addItem( focusIndicator );
|
2017-07-27 08:26:23 +02:00
|
|
|
|
|
|
|
window.show();
|
|
|
|
|
|
|
|
return app.exec();
|
|
|
|
}
|