2021-09-24 14:47:56 +02:00
|
|
|
#include "MainItem.h"
|
|
|
|
|
2022-07-14 14:26:45 +02:00
|
|
|
#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>
|
2023-10-05 08:59:30 +02:00
|
|
|
#include <QskPanGestureRecognizer.h>
|
2021-09-24 14:47:56 +02:00
|
|
|
|
|
|
|
#include <QQuickFramebufferObject>
|
|
|
|
#include <QGuiApplication>
|
|
|
|
#include <QQuickWindow>
|
2023-01-02 11:46:23 +01:00
|
|
|
#include <QtMath>
|
2021-09-24 14:47:56 +02:00
|
|
|
|
2023-01-02 11:46:23 +01:00
|
|
|
#include <QTimer>
|
|
|
|
|
2023-10-05 08:59:30 +02:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
class PanRecognizer final : public QskPanGestureRecognizer
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
PanRecognizer( MainItem* mainItem )
|
|
|
|
: QskPanGestureRecognizer( mainItem )
|
|
|
|
{
|
|
|
|
setOrientations( Qt::Horizontal | Qt::Vertical );
|
|
|
|
setMinDistance( 50 );
|
|
|
|
setTimeout( 100 );
|
|
|
|
|
|
|
|
setWatchedItem( mainItem );
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-01-04 10:07:03 +01:00
|
|
|
QPair< Cube::Position, Cube::Edge > Cube::s_neighbors[ Cube::NumPositions ][ Cube::NumEdges ] =
|
2022-12-22 18:15:04 +01:00
|
|
|
{
|
2023-01-04 10:07:03 +01:00
|
|
|
// neighbors of Left side:
|
|
|
|
{
|
|
|
|
{ Cube::BackPos, Cube::BottomEdge }, // going Left
|
|
|
|
{ Cube::FrontPos, Cube::BottomEdge }, // going Right
|
|
|
|
{ Cube::TopPos, Cube::LeftEdge }, // going Top
|
|
|
|
{ Cube::BottomPos, Cube::RightEdge } // going Bottom
|
|
|
|
},
|
2023-01-02 11:46:23 +01:00
|
|
|
|
|
|
|
// Right:
|
2023-01-04 10:07:03 +01:00
|
|
|
{
|
|
|
|
{ Cube::FrontPos, Cube::BottomEdge },
|
|
|
|
{ Cube::BackPos, Cube::BottomEdge },
|
|
|
|
{ Cube::TopPos, Cube::RightEdge },
|
|
|
|
{ Cube::BottomPos, Cube::LeftEdge }
|
|
|
|
},
|
2023-01-02 11:46:23 +01:00
|
|
|
|
|
|
|
// Top:
|
2023-01-04 10:07:03 +01:00
|
|
|
{
|
|
|
|
{ Cube::LeftPos, Cube::RightEdge },
|
|
|
|
{ Cube::RightPos, Cube::LeftEdge },
|
|
|
|
{ Cube::BackPos, Cube::TopEdge },
|
|
|
|
{ Cube::FrontPos, Cube::BottomEdge }
|
|
|
|
},
|
2023-01-02 11:46:23 +01:00
|
|
|
|
|
|
|
// Bottom:
|
2023-01-04 10:07:03 +01:00
|
|
|
{
|
|
|
|
{ Cube::LeftPos, Cube::LeftEdge },
|
|
|
|
{ Cube::RightPos, Cube::RightEdge },
|
|
|
|
{ Cube::FrontPos, Cube::BottomEdge },
|
|
|
|
{ Cube::BackPos, Cube::TopEdge }
|
|
|
|
},
|
2023-01-02 11:46:23 +01:00
|
|
|
|
|
|
|
// Front:
|
2023-01-04 10:07:03 +01:00
|
|
|
{
|
|
|
|
{ Cube::LeftPos, Cube::BottomEdge },
|
|
|
|
{ Cube::RightPos, Cube::BottomEdge },
|
|
|
|
{ Cube::TopPos, Cube::BottomEdge },
|
|
|
|
{ Cube::BottomPos, Cube::BottomEdge }
|
|
|
|
},
|
2023-01-02 11:46:23 +01:00
|
|
|
|
|
|
|
// Back:
|
2023-01-04 10:07:03 +01:00
|
|
|
{
|
|
|
|
{ Cube::RightPos, Cube::BottomEdge },
|
|
|
|
{ Cube::LeftPos, Cube::BottomEdge },
|
|
|
|
{ Cube::TopPos, Cube::TopEdge },
|
|
|
|
{ Cube::BottomPos, Cube::BottomEdge }
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Cube::Edge Cube::s_edgeTransformations[ Cube::NumEdges ][ Cube::NumEdges ] =
|
|
|
|
{
|
|
|
|
// current edge is LeftEdge:
|
|
|
|
{ Cube::TopEdge, // Left
|
|
|
|
Cube::BottomEdge, // Right
|
|
|
|
Cube::RightEdge, // Top
|
|
|
|
Cube::LeftEdge }, // Bottom
|
|
|
|
|
|
|
|
// Right:
|
|
|
|
{ Cube::BottomEdge,
|
|
|
|
Cube::TopEdge,
|
|
|
|
Cube::LeftEdge,
|
|
|
|
Cube::RightEdge },
|
|
|
|
|
|
|
|
// Top:
|
|
|
|
{ Cube::RightEdge,
|
|
|
|
Cube::LeftEdge,
|
|
|
|
Cube::BottomEdge,
|
|
|
|
Cube::TopEdge },
|
|
|
|
|
|
|
|
// Bottom:
|
|
|
|
{ Cube::LeftEdge,
|
|
|
|
Cube::RightEdge,
|
|
|
|
Cube::TopEdge,
|
|
|
|
Cube::BottomEdge }
|
2023-01-02 11:46:23 +01:00
|
|
|
};
|
2022-12-22 18:15:04 +01:00
|
|
|
|
2022-08-02 09:36:22 +02:00
|
|
|
Cube::Cube( QQuickItem* parent )
|
|
|
|
: QskStackBox( false, parent )
|
2023-01-03 14:48:59 +01:00
|
|
|
, m_destination( FrontPos )
|
2023-01-04 10:07:03 +01:00
|
|
|
, m_currentEdge( BottomEdge )
|
2023-01-03 18:08:39 +01:00
|
|
|
, m_isIntermediateHop( false )
|
2022-08-02 09:36:22 +02:00
|
|
|
{
|
2023-01-02 11:46:23 +01: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 ) );
|
|
|
|
|
2023-01-03 14:48:59 +01:00
|
|
|
if( animationIsFinished && position != m_destination )
|
2023-01-02 11:46:23 +01:00
|
|
|
{
|
|
|
|
QTimer::singleShot( 0, this, [this]()
|
|
|
|
{
|
2023-01-03 18:08:39 +01:00
|
|
|
m_isIntermediateHop = true;
|
2023-01-03 14:48:59 +01:00
|
|
|
switchToPosition( m_destination );
|
2023-01-02 11:46:23 +01:00
|
|
|
} );
|
|
|
|
}
|
|
|
|
} );
|
2023-01-04 16:33:29 +01:00
|
|
|
|
|
|
|
QTimer::singleShot( 0, this, [this]()
|
|
|
|
{
|
|
|
|
Q_EMIT cubeIndexChanged( m_destination );
|
|
|
|
} );
|
2022-08-02 09:36:22 +02:00
|
|
|
}
|
|
|
|
|
2023-01-03 09:50:55 +01:00
|
|
|
void Cube::doSwitch( Qsk::Direction direction, Position position )
|
2022-08-02 09:36:22 +02:00
|
|
|
{
|
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->setDuration( 1000 );
|
2022-08-03 09:40:18 +02:00
|
|
|
setAnimator( animator );
|
2022-08-02 09:36:22 +02:00
|
|
|
}
|
|
|
|
|
2023-01-03 18:08:39 +01:00
|
|
|
if( position == m_destination && !m_isIntermediateHop ) // 1 hop
|
|
|
|
{
|
|
|
|
animator->setEasingCurve( QEasingCurve::InOutQuad );
|
|
|
|
}
|
|
|
|
else if( !m_isIntermediateHop ) // 1st of 2 hops
|
|
|
|
{
|
|
|
|
animator->setEasingCurve( QEasingCurve::InQuad );
|
|
|
|
}
|
|
|
|
else // 2nd of 2 hops
|
|
|
|
{
|
|
|
|
animator->setEasingCurve( QEasingCurve::OutQuad );
|
|
|
|
m_isIntermediateHop = false;
|
|
|
|
}
|
|
|
|
|
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 );
|
|
|
|
|
2023-01-04 10:07:03 +01:00
|
|
|
updateEdge( direction, position );
|
|
|
|
|
2023-01-02 11:46:23 +01:00
|
|
|
setCurrentIndex( position );
|
2023-01-04 16:33:29 +01:00
|
|
|
|
|
|
|
if( position == m_destination )
|
|
|
|
{
|
|
|
|
Q_EMIT cubeIndexChanged( position );
|
|
|
|
}
|
2023-01-02 11:46:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Cube::switchPosition( const Qsk::Direction direction )
|
|
|
|
{
|
2023-01-04 10:07:03 +01:00
|
|
|
m_destination = neighbor( currentPosition(), direction );
|
2023-01-03 09:50:55 +01:00
|
|
|
|
2023-01-03 14:48:59 +01:00
|
|
|
doSwitch( direction, m_destination );
|
2023-01-02 11:46:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Cube::switchToPosition( const Position position )
|
|
|
|
{
|
2023-01-03 18:08:39 +01:00
|
|
|
if( currentPosition() == position )
|
2023-01-02 11:46:23 +01:00
|
|
|
return;
|
|
|
|
|
2023-01-03 14:48:59 +01:00
|
|
|
m_destination = position;
|
|
|
|
|
2023-01-04 10:07:03 +01:00
|
|
|
const auto direction = this->direction( currentPosition(), position );
|
|
|
|
const auto nextPosition = neighbor( currentPosition(), direction );
|
2023-01-02 11:46:23 +01:00
|
|
|
|
2023-01-04 10:07:03 +01:00
|
|
|
doSwitch( direction, nextPosition );
|
2023-01-03 09:50:55 +01:00
|
|
|
}
|
2023-01-02 11:46:23 +01:00
|
|
|
|
2023-01-03 18:08:39 +01:00
|
|
|
Cube::Position Cube::currentPosition() const
|
|
|
|
{
|
|
|
|
return static_cast< Position >( currentIndex() );
|
|
|
|
}
|
|
|
|
|
2023-01-03 09:50:55 +01:00
|
|
|
Cube::Position Cube::neighbor( const Position position, const Qsk::Direction direction ) const
|
|
|
|
{
|
2023-01-04 10:07:03 +01:00
|
|
|
const auto index = s_edgeTransformations[ m_currentEdge ][ direction ];
|
|
|
|
const auto n = s_neighbors[ position ][ index ].first;
|
2023-01-03 09:50:55 +01:00
|
|
|
return n;
|
2023-01-02 11:46:23 +01:00
|
|
|
}
|
|
|
|
|
2023-01-03 09:50:55 +01:00
|
|
|
Qsk::Direction Cube::direction( const Position from, const Position to ) const
|
2023-01-02 11:46:23 +01:00
|
|
|
{
|
|
|
|
// 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
|
|
|
|
2023-01-04 10:07:03 +01:00
|
|
|
for( int i = 0; i < NumEdges; ++i )
|
2022-08-02 09:36:22 +02:00
|
|
|
{
|
2023-01-04 10:07:03 +01:00
|
|
|
if( neighbors[ i ].first == to )
|
2023-01-02 11:46:23 +01:00
|
|
|
{
|
|
|
|
return static_cast< Qsk::Direction >( i );
|
|
|
|
}
|
2022-08-02 09:36:22 +02:00
|
|
|
}
|
|
|
|
|
2023-01-02 11:46:23 +01:00
|
|
|
return Qsk::RightToLeft;
|
2022-08-02 09:36:22 +02:00
|
|
|
}
|
|
|
|
|
2023-01-04 10:07:03 +01:00
|
|
|
void Cube::updateEdge( Qsk::Direction direction, Position position )
|
|
|
|
{
|
|
|
|
m_currentEdge = s_neighbors[ currentPosition() ][ direction ].second;
|
|
|
|
|
|
|
|
// When going back to Front, Left etc., switch back to
|
|
|
|
// the bottom edge, otherwise it gets to confusing:
|
|
|
|
if( position != TopPos && position != BottomPos )
|
|
|
|
{
|
|
|
|
m_currentEdge = BottomEdge;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-24 14:47:56 +02:00
|
|
|
MainItem::MainItem( QQuickItem* parent )
|
|
|
|
: QskControl( parent )
|
2023-01-02 10:22:23 +01:00
|
|
|
, 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 );
|
|
|
|
|
2023-10-05 08:59:30 +02:00
|
|
|
(void) new PanRecognizer( this );
|
2021-09-24 14:47:56 +02:00
|
|
|
|
|
|
|
m_mainLayout->setSpacing( 0 );
|
|
|
|
|
2023-01-02 11:46:23 +01:00
|
|
|
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
|
|
|
|
2023-01-04 16:33:29 +01:00
|
|
|
connect( m_cube, &Cube::cubeIndexChanged, m_menuBar, &MenuBar::setActivePage );
|
2023-01-04 10:07:03 +01:00
|
|
|
|
2023-01-02 10:22:23 +01: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
|
|
|
|
2023-01-03 09:50:55 +01: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
|
|
|
|
2023-01-02 11:46:23 +01:00
|
|
|
// the current item needs to be the one at the Front:
|
2023-01-02 10:22:23 +01:00
|
|
|
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
|
2023-02-28 15:49:42 +01:00
|
|
|
&& event->gesture()->type() == QskGesture::Pan )
|
2021-09-24 14:47:56 +02:00
|
|
|
{
|
2023-01-02 11:46:23 +01: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;
|
|
|
|
}
|
|
|
|
|
2023-01-02 11:46:23 +01:00
|
|
|
m_cube->switchPosition( direction );
|
2021-09-24 14:47:56 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-13 18:01:46 +02:00
|
|
|
void MainItem::keyPressEvent( QKeyEvent* event )
|
2023-01-04 17:41:21 +01:00
|
|
|
{
|
2023-09-13 18:01:46 +02:00
|
|
|
// maybe using shortcuts ?
|
|
|
|
|
|
|
|
Qsk::Direction direction;
|
|
|
|
|
|
|
|
switch( event->key() )
|
2023-01-04 17:41:21 +01:00
|
|
|
{
|
2023-09-13 18:01:46 +02:00
|
|
|
case Qt::Key_Up:
|
|
|
|
direction = Qsk::TopToBottom;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Qt::Key_Down:
|
|
|
|
direction = Qsk::BottomToTop;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Qt::Key_Left:
|
|
|
|
direction = Qsk::LeftToRight;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Qt::Key_Right:
|
|
|
|
direction = Qsk::RightToLeft;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return;
|
2023-01-04 17:41:21 +01:00
|
|
|
}
|
|
|
|
|
2023-09-13 18:01:46 +02:00
|
|
|
m_cube->switchPosition( direction );
|
|
|
|
}
|
2022-09-09 09:15:26 +02:00
|
|
|
|
|
|
|
#include "moc_MainItem.cpp"
|