263 lines
7.2 KiB
C++
Raw Normal View History

2021-09-24 14:47:56 +02:00
#include "MainItem.h"
#include "DashboardPage.h"
2023-01-02 09:42:41 +01:00
#include "DevicesPage.h"
2021-09-24 14:47:56 +02:00
#include "MenuBar.h"
2023-01-02 09:42:41 +01:00
#include "MembersPage.h"
2022-07-14 14:43:21 +02:00
#include "RoomsPage.h"
2023-01-02 09:42:41 +01:00
#include "StatisticsPage.h"
#include "StoragePage.h"
2021-09-24 14:47:56 +02:00
#include <QskGesture.h>
#include <QskEvent.h>
#include <QskLinearBox.h>
2022-08-02 09:36:22 +02:00
#include <QskStackBoxAnimator.h>
2021-09-24 14:47:56 +02:00
#include <QQuickFramebufferObject>
#include <QGuiApplication>
#include <QQuickWindow>
#include <QtMath>
2021-09-24 14:47:56 +02:00
#include <QTimer>
Cube::Position Cube::s_neighbors[ Cube::NumPositions ][ 4 ] =
{
// Left:
{ Cube::BackPos, // Left
Cube::FrontPos, // Right
Cube::TopPos, // Top
Cube::BottomPos }, // Bottom
// Right:
{ Cube::FrontPos,
Cube::BackPos,
Cube::TopPos,
Cube::BottomPos },
// Top:
{ Cube::LeftPos,
Cube::RightPos,
Cube::BackPos,
Cube::FrontPos },
// Bottom:
{ Cube::LeftPos,
Cube::RightPos,
Cube::FrontPos,
Cube::BackPos },
// Front:
{ Cube::LeftPos,
Cube::RightPos,
Cube::TopPos,
Cube::BottomPos },
// Back:
{ Cube::RightPos,
Cube::LeftPos,
Cube::TopPos,
Cube::BottomPos },
};
2022-08-02 09:36:22 +02:00
Cube::Cube( QQuickItem* parent )
: QskStackBox( false, parent )
, m_currentPosition( FrontPos )
, m_previousPosition( FrontPos )
2022-08-02 09:36:22 +02:00
{
// The code below covers the case where we need 2 cube movements to get
// to the desired position.
// We use transientIndexChanged here to be sure to start a new transition
// at the end; indexChanged doesn't work here.
connect( this, &QskStackBox::transientIndexChanged, this, [ this ]( qreal position )
{
const bool animationIsFinished = ( position == qFloor( position ) );
if( animationIsFinished && position != m_currentPosition )
{
QTimer::singleShot( 0, this, [this]()
{
switchToPosition( m_currentPosition );
} );
}
} );
2022-08-02 09:36:22 +02:00
}
void Cube::doSwitch( Qsk::Direction direction, Position position )
2022-08-02 09:36:22 +02:00
{
m_previousPosition = m_currentPosition;
m_currentPosition = position;
2022-08-03 09:40:18 +02:00
using Animator = QskStackBoxAnimator4;
auto animator = qobject_cast< Animator* >( this->animator() );
2022-08-02 09:36:22 +02:00
if ( animator == nullptr )
{
2022-08-03 09:40:18 +02:00
animator = new Animator( this );
2022-08-02 09:36:22 +02:00
animator->setEasingCurve( QEasingCurve::InOutQuad );
animator->setDuration( 1000 );
2022-08-03 09:40:18 +02:00
setAnimator( animator );
2022-08-02 09:36:22 +02:00
}
const auto orientation = ( direction == Qsk::LeftToRight || direction == Qsk::RightToLeft )
? Qt::Horizontal : Qt::Vertical;
animator->setOrientation( orientation );
const bool inverted = ( direction == Qsk::LeftToRight || direction == Qsk::TopToBottom );
animator->setInverted( inverted );
setCurrentIndex( position );
Q_EMIT cubeIndexChanged( position ); // ### do we need this?
}
void Cube::switchPosition( const Qsk::Direction direction )
{
// ### needs to go to the other function:
// keep track of from where we went to top and bottom,
// so that going up and down will result in going back
// to the same position:
// (We don't want to model the complete cube logic with
// keeping track of the edges here, because that doesn't
// make sense wrt. being upside down etc.)
Position position;
if( ( m_currentPosition == TopPos && direction == Qsk::BottomToTop )
|| ( m_currentPosition == BottomPos && direction == Qsk::TopToBottom ) )
{
position = m_previousPosition;
}
else
{
position = neighbor( m_currentPosition, direction );
}
doSwitch( direction, position );
}
void Cube::switchToPosition( const Position position )
{
if( currentIndex() == position )
return;
const auto from = static_cast< Position >( currentIndex() );
const auto direction = this->direction( from, m_currentPosition );
doSwitch( direction, position );
}
Cube::Position Cube::neighbor( const Position position, const Qsk::Direction direction ) const
{
const auto n = s_neighbors[ position ][ direction ];
return n;
}
Qsk::Direction Cube::direction( const Position from, const Position to ) const
{
// if direct neighbor: use that direction
// otherwise: we need 2 swipes, direction doesn't matter, so choose right to left
const auto neighbors = s_neighbors[ from ];
2022-08-02 09:36:22 +02:00
for( int i = 0; i < 4; ++i )
2022-08-02 09:36:22 +02:00
{
if( neighbors[ i ] == to )
{
return static_cast< Qsk::Direction >( i );
}
2022-08-02 09:36:22 +02:00
}
return Qsk::RightToLeft;
2022-08-02 09:36:22 +02:00
}
2021-09-24 14:47:56 +02:00
MainItem::MainItem( QQuickItem* parent )
: QskControl( parent )
, m_mainLayout( new QskLinearBox( Qt::Horizontal, this ) )
, m_menuBar( new MenuBar( m_mainLayout ) )
, m_cube( new Cube( m_mainLayout ) )
2021-09-24 14:47:56 +02:00
{
setAutoLayoutChildren( true );
setAcceptedMouseButtons( Qt::LeftButton );
setFiltersChildMouseEvents( true );
2022-08-02 09:36:22 +02:00
m_panRecognizer.setOrientations( Qt::Horizontal | Qt::Vertical );
2021-09-24 14:47:56 +02:00
m_panRecognizer.setMinDistance( 50 );
m_panRecognizer.setWatchedItem( this );
m_mainLayout->setSpacing( 0 );
connect( m_menuBar, &MenuBar::pageChangeRequested, this, [this]( int index )
{
const auto position = static_cast< Cube::Position >( index );
m_cube->switchToPosition( position );
} );
2022-07-14 14:43:21 +02:00
auto* const dashboardPage = new DashboardPage( m_cube );
auto* const roomsPage = new RoomsPage( m_cube );
2023-01-02 09:42:41 +01:00
auto* const devicesPage = new DevicesPage( m_cube );
auto* const statisticsPage = new StatisticsPage( m_cube );
auto* const storagePage = new StoragePage( m_cube );
auto* const membersPage = new MembersPage( m_cube );
2021-09-24 14:47:56 +02:00
m_cube->insertItem( Cube::LeftPos, statisticsPage );
m_cube->insertItem( Cube::RightPos, roomsPage );
m_cube->insertItem( Cube::TopPos, storagePage );
m_cube->insertItem( Cube::BottomPos, membersPage );
m_cube->insertItem( Cube::FrontPos, dashboardPage );
m_cube->insertItem( Cube::BackPos, devicesPage );
2022-07-14 14:43:21 +02:00
// the current item needs to be the one at the Front:
m_cube->setCurrentItem( dashboardPage );
2021-09-24 14:47:56 +02:00
}
void MainItem::gestureEvent( QskGestureEvent* event )
{
2022-08-02 09:36:22 +02:00
if( event->gesture()->state() == QskGesture::Finished
&& event->gesture()->type() == QskGesture::Pan )
2021-09-24 14:47:56 +02:00
{
const auto* panGesture = static_cast< const QskPanGesture* >( event->gesture().get() );
2022-08-02 09:36:22 +02:00
const auto delta = panGesture->origin() - panGesture->position();
Qsk::Direction direction;
if( qAbs( delta.x() ) > qAbs( delta.y() ) )
{
direction = ( delta.x() < 0 ) ? Qsk::LeftToRight : Qsk::RightToLeft;
}
else
{
direction = ( delta.y() < 0 ) ? Qsk::TopToBottom : Qsk::BottomToTop;
}
m_cube->switchPosition( direction );
2021-09-24 14:47:56 +02:00
}
}
bool MainItem::gestureFilter( QQuickItem* item, QEvent* event )
{
auto& recognizer = m_panRecognizer;
if( event->type() == QEvent::MouseButtonPress )
{
const auto mouseEvent = static_cast< QMouseEvent* >( event );
if( ( item != this ) || ( recognizer.timeout() < 0 ) )
{
if( recognizer.hasProcessedBefore( mouseEvent ) )
{
return false;
}
}
recognizer.setTimeout( ( item == this ) ? -1 : 100 );
}
return recognizer.processEvent( item, event, false );
}
2022-09-09 09:15:26 +02:00
#include "moc_MainItem.cpp"