2023-10-17 12:14:42 +02:00
|
|
|
/******************************************************************************
|
|
|
|
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
|
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
*****************************************************************************/
|
|
|
|
|
2023-05-02 18:51:09 +02:00
|
|
|
#include "QskDrawer.h"
|
|
|
|
#include "QskAspect.h"
|
2023-10-17 12:14:42 +02:00
|
|
|
#include "QskAnimationHint.h"
|
|
|
|
#include "QskQuick.h"
|
|
|
|
#include "QskEvent.h"
|
|
|
|
|
|
|
|
#include "QskPanGestureRecognizer.h"
|
|
|
|
#include "QskGesture.h"
|
|
|
|
|
|
|
|
#include <qguiapplication.h>
|
|
|
|
#include <qstylehints.h>
|
2023-05-02 18:51:09 +02:00
|
|
|
|
2023-10-17 12:14:42 +02:00
|
|
|
QSK_QT_PRIVATE_BEGIN
|
|
|
|
#include <private/qquickitem_p.h>
|
|
|
|
#include <private/qquickitemchangelistener_p.h>
|
|
|
|
QSK_QT_PRIVATE_END
|
2023-05-02 18:51:09 +02:00
|
|
|
|
2023-10-17 12:14:42 +02:00
|
|
|
/*
|
|
|
|
Only used for the sliding in animation. Do we want to
|
|
|
|
introduce a specific panel as background ???
|
|
|
|
*/
|
2023-05-02 18:51:09 +02:00
|
|
|
QSK_SUBCONTROL( QskDrawer, Panel )
|
2023-10-17 12:14:42 +02:00
|
|
|
|
|
|
|
static void qskCatchMouseEvents( QQuickItem* item )
|
|
|
|
{
|
|
|
|
#if 1
|
|
|
|
// manipulating other items - do we really want to do this ?
|
|
|
|
item->setAcceptedMouseButtons( Qt::LeftButton );
|
|
|
|
item->setFiltersChildMouseEvents( true );
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool qskCheckDirection( Qt::Edge edge, const QskPanGesture* gesture )
|
|
|
|
{
|
|
|
|
const auto degrees = gesture->angle();
|
|
|
|
|
|
|
|
switch( edge )
|
|
|
|
{
|
|
|
|
case Qt::LeftEdge:
|
|
|
|
return ( degrees < 90.0 ) || ( degrees ) > 270.0;
|
|
|
|
|
|
|
|
case Qt::RightEdge:
|
|
|
|
return ( degrees > 90.0 ) && ( degrees < 270.0 );
|
|
|
|
|
|
|
|
case Qt::TopEdge:
|
|
|
|
return degrees > 180.0;
|
|
|
|
|
|
|
|
case Qt::BottomEdge:
|
|
|
|
return degrees < 180.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qskLayoutDrawer( const QRectF& rect, QskDrawer* drawer )
|
|
|
|
{
|
|
|
|
const auto size = qskSizeConstraint( drawer, Qt::PreferredSize );
|
|
|
|
|
|
|
|
QRectF r( 0.0, 0.0, size.width(), size.height() );
|
|
|
|
|
|
|
|
switch( drawer->edge() )
|
|
|
|
{
|
|
|
|
case Qt::LeftEdge:
|
|
|
|
{
|
|
|
|
r.setHeight( rect.height() );
|
|
|
|
r.moveRight( rect.left() + size.width() );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Qt::RightEdge:
|
|
|
|
{
|
|
|
|
r.setHeight( rect.height() );
|
|
|
|
r.moveLeft( rect.right() - size.width() );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case Qt::TopEdge:
|
|
|
|
{
|
|
|
|
r.setWidth( rect.width() );
|
|
|
|
r.moveBottom( rect.top() + size.height() );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case Qt::BottomEdge:
|
|
|
|
{
|
|
|
|
r.setWidth( rect.width() );
|
|
|
|
r.moveTop( rect.bottom() - size.height() );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
drawer->setGeometry( r );
|
|
|
|
}
|
|
|
|
|
2023-10-17 15:53:30 +02:00
|
|
|
static inline QRectF qskSlidingRect(
|
|
|
|
const QSizeF& size, Qt::Edge edge, qreal ratio )
|
|
|
|
{
|
|
|
|
auto x = 0.0;
|
|
|
|
auto y = 0.0;
|
|
|
|
|
|
|
|
switch( edge )
|
|
|
|
{
|
|
|
|
case Qt::LeftEdge:
|
|
|
|
x = -ratio * size.width();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Qt::RightEdge:
|
|
|
|
x = ratio * size.width();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Qt::TopEdge:
|
|
|
|
y = -ratio * size.height();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Qt::BottomEdge:
|
|
|
|
y = ratio * size.height();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return QRectF( x, y, size.width(), size.height() );
|
|
|
|
}
|
|
|
|
|
2023-10-17 12:14:42 +02:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
class GeometryListener final : public QQuickItemChangeListener
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
GeometryListener( QQuickItem* item, QQuickItem* adjustedItem )
|
|
|
|
: m_item( item )
|
|
|
|
, m_adjustedItem( adjustedItem )
|
|
|
|
{
|
|
|
|
adjust();
|
|
|
|
setEnabled( true );
|
|
|
|
}
|
|
|
|
|
|
|
|
~GeometryListener()
|
|
|
|
{
|
|
|
|
setEnabled( false );
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
void itemGeometryChanged( QQuickItem*,
|
|
|
|
QQuickGeometryChange, const QRectF& ) override
|
|
|
|
{
|
|
|
|
adjust();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
void adjust()
|
|
|
|
{
|
|
|
|
#if 0
|
|
|
|
const auto pos = m_adjustedItem->mapFromItem( m_item, QPointF() );
|
|
|
|
qskSetItemGeometry( m_adjustedItem,
|
|
|
|
pos.x(), pos.y(), m_item->width(), m_item->height() );
|
|
|
|
#else
|
|
|
|
qskLayoutDrawer( QRectF( QPointF(), m_item->size() ),
|
|
|
|
qobject_cast< QskDrawer* >( m_adjustedItem ) );
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void setEnabled( bool on )
|
|
|
|
{
|
|
|
|
const auto changeTypes = QQuickItemPrivate::Geometry;
|
|
|
|
|
|
|
|
auto d = QQuickItemPrivate::get( m_item );
|
|
|
|
if ( on )
|
|
|
|
d->addItemChangeListener( this, changeTypes );
|
|
|
|
else
|
|
|
|
d->removeItemChangeListener( this, changeTypes );
|
|
|
|
}
|
|
|
|
|
|
|
|
QQuickItem* m_item;
|
|
|
|
QQuickItem* m_adjustedItem;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
class GestureRecognizer : public QskPanGestureRecognizer
|
|
|
|
{
|
|
|
|
using Inherited = QskPanGestureRecognizer;
|
|
|
|
|
|
|
|
public:
|
|
|
|
GestureRecognizer( QskDrawer* drawer )
|
|
|
|
: QskPanGestureRecognizer( drawer )
|
|
|
|
{
|
|
|
|
setWatchedItem( drawer->parentItem() );
|
|
|
|
setTargetItem( drawer );
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
bool isAcceptedPos( const QPointF& pos ) const override
|
|
|
|
{
|
|
|
|
auto drawer = qobject_cast< const QskDrawer* >( targetItem() );
|
|
|
|
|
|
|
|
const auto dragMargin = drawer->dragMargin();
|
|
|
|
if ( dragMargin <= 0.0 )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
auto rect = qskItemRect( watchedItem() );
|
|
|
|
|
|
|
|
switch( drawer->edge() )
|
|
|
|
{
|
|
|
|
case Qt::LeftEdge:
|
|
|
|
rect.setRight( rect.left() + dragMargin );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Qt::RightEdge:
|
|
|
|
rect.setLeft( rect.right() - dragMargin );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Qt::TopEdge:
|
|
|
|
rect.setBottom( rect.top() + dragMargin );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Qt::BottomEdge:
|
|
|
|
rect.setTop( rect.bottom() - dragMargin );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rect.contains( pos );
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
2023-05-02 18:51:09 +02:00
|
|
|
|
|
|
|
class QskDrawer::PrivateData
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Qt::Edge edge = Qt::LeftEdge;
|
2023-10-17 12:14:42 +02:00
|
|
|
GestureRecognizer* gestureRecognizer = nullptr;
|
|
|
|
GeometryListener* listener = nullptr;
|
|
|
|
|
|
|
|
// a skin hint ???
|
|
|
|
qreal dragMargin = QGuiApplication::styleHints()->startDragDistance();
|
2023-05-02 18:51:09 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
QskDrawer::QskDrawer( QQuickItem* parentItem )
|
|
|
|
: Inherited ( parentItem )
|
|
|
|
, m_data( new PrivateData )
|
|
|
|
{
|
2023-10-17 12:14:42 +02:00
|
|
|
#if 1
|
2023-05-02 18:51:09 +02:00
|
|
|
setZ( 1 );
|
2023-10-17 12:14:42 +02:00
|
|
|
#endif
|
|
|
|
|
2023-10-17 15:53:30 +02:00
|
|
|
setAutoLayoutChildren( true );
|
2023-05-02 18:51:09 +02:00
|
|
|
|
|
|
|
setPopupFlag( PopupFlag::CloseOnPressOutside, true );
|
2023-10-17 12:14:42 +02:00
|
|
|
setFaderAspect( Panel | QskAspect::Position | QskAspect::Metric );
|
|
|
|
|
|
|
|
/*
|
|
|
|
The drawer wants to be on top of the parent - not being
|
|
|
|
layouted into its layoutRect(). So we opt out and do
|
|
|
|
the layout updates manually.
|
|
|
|
*/
|
|
|
|
setPlacementPolicy( QskPlacementPolicy::Ignore );
|
|
|
|
if ( parentItem )
|
|
|
|
{
|
|
|
|
m_data->listener = new GeometryListener( parentItem, this );
|
|
|
|
qskCatchMouseEvents( parentItem );
|
|
|
|
}
|
2023-05-02 18:51:09 +02:00
|
|
|
|
2023-10-17 12:14:42 +02:00
|
|
|
m_data->gestureRecognizer = new GestureRecognizer( this );
|
2023-05-02 18:51:09 +02:00
|
|
|
|
|
|
|
|
2023-10-17 12:14:42 +02:00
|
|
|
connect( this, &QskPopup::openChanged, this, &QskDrawer::setFading );
|
2023-05-02 18:51:09 +02:00
|
|
|
|
2023-10-17 12:14:42 +02:00
|
|
|
/*
|
|
|
|
When the content of the parentItem does not fit we will have
|
|
|
|
a difference between fading and normal state. To overcome this problem
|
|
|
|
we need to expand the rectangle of the QQuickDefaultClipNode manually to
|
|
|
|
the window borders: TODO ...
|
|
|
|
*/
|
|
|
|
connect( this, &QskPopup::fadingChanged, parentItem, &QQuickItem::setClip );
|
2023-05-02 18:51:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
QskDrawer::~QskDrawer()
|
|
|
|
{
|
2023-10-17 12:14:42 +02:00
|
|
|
delete m_data->listener;
|
2023-05-02 18:51:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Qt::Edge QskDrawer::edge() const
|
|
|
|
{
|
|
|
|
return m_data->edge;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskDrawer::setEdge( Qt::Edge edge )
|
|
|
|
{
|
|
|
|
if( m_data->edge == edge )
|
|
|
|
return;
|
|
|
|
|
|
|
|
update();
|
|
|
|
m_data->edge = edge;
|
|
|
|
edgeChanged( edge );
|
|
|
|
}
|
|
|
|
|
2023-10-17 12:14:42 +02:00
|
|
|
void QskDrawer::setDragMargin( qreal margin )
|
2023-05-02 18:51:09 +02:00
|
|
|
{
|
2023-10-17 12:14:42 +02:00
|
|
|
margin = std::max( margin, 0.0 );
|
2023-05-11 08:15:48 +02:00
|
|
|
|
2023-10-17 12:14:42 +02:00
|
|
|
if ( margin != m_data->dragMargin )
|
|
|
|
{
|
|
|
|
m_data->dragMargin = margin;
|
|
|
|
Q_EMIT dragMarginChanged( margin );
|
|
|
|
}
|
2023-05-02 18:51:09 +02:00
|
|
|
}
|
|
|
|
|
2023-10-17 12:14:42 +02:00
|
|
|
qreal QskDrawer::dragMargin() const
|
2023-05-02 18:51:09 +02:00
|
|
|
{
|
2023-10-17 12:14:42 +02:00
|
|
|
return m_data->dragMargin;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskDrawer::gestureEvent( QskGestureEvent* event )
|
|
|
|
{
|
|
|
|
if ( event->gesture()->type() == QskGesture::Pan )
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
For the moment we treat the gesture like a swipe gesture
|
|
|
|
without dragging the drawer when moving the mouse. TODO ...
|
|
|
|
*/
|
|
|
|
const auto gesture = static_cast< const QskPanGesture* >( event->gesture().get() );
|
|
|
|
if ( gesture->state() == QskGesture::Finished )
|
|
|
|
{
|
|
|
|
if ( qskCheckDirection( m_data->edge, gesture ) )
|
|
|
|
open();
|
|
|
|
}
|
|
|
|
|
2023-05-02 18:51:09 +02:00
|
|
|
return;
|
2023-10-17 12:14:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Inherited::gestureEvent( event );
|
|
|
|
}
|
2023-05-02 18:51:09 +02:00
|
|
|
|
2023-10-17 15:53:30 +02:00
|
|
|
QRectF QskDrawer::layoutRectForSize( const QSizeF& size ) const
|
2023-10-17 12:14:42 +02:00
|
|
|
{
|
2023-10-17 15:53:30 +02:00
|
|
|
qreal ratio;
|
2023-05-02 18:51:09 +02:00
|
|
|
|
2023-10-17 12:14:42 +02:00
|
|
|
if ( isFading() )
|
2023-10-17 15:53:30 +02:00
|
|
|
ratio = metric( faderAspect() );
|
|
|
|
else
|
|
|
|
ratio = isOpen() ? 0.0 : 1.0;
|
2023-10-17 12:14:42 +02:00
|
|
|
|
2023-10-17 15:53:30 +02:00
|
|
|
return qskSlidingRect( size, m_data->edge, ratio );
|
2023-10-17 12:14:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void QskDrawer::itemChange( QQuickItem::ItemChange change,
|
|
|
|
const QQuickItem::ItemChangeData& value )
|
|
|
|
{
|
|
|
|
Inherited::itemChange( change, value );
|
|
|
|
|
|
|
|
switch( static_cast< int >( change ) )
|
|
|
|
{
|
|
|
|
case QQuickItem::ItemParentHasChanged:
|
|
|
|
{
|
|
|
|
if ( parentItem() )
|
|
|
|
qskCatchMouseEvents( parentItem() );
|
2023-05-02 18:51:09 +02:00
|
|
|
|
2023-10-17 12:14:42 +02:00
|
|
|
Q_FALLTHROUGH();
|
|
|
|
}
|
|
|
|
case QQuickItem::ItemVisibleHasChanged:
|
|
|
|
{
|
|
|
|
delete m_data->listener;
|
|
|
|
m_data->listener = nullptr;
|
2023-05-02 18:51:09 +02:00
|
|
|
|
2023-10-17 12:14:42 +02:00
|
|
|
if ( parentItem() && isVisible() )
|
|
|
|
m_data->listener = new GeometryListener( parentItem(), this );
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2023-05-02 18:51:09 +02:00
|
|
|
}
|
|
|
|
|
2023-10-17 12:14:42 +02:00
|
|
|
void QskDrawer::setFading( bool on )
|
2023-05-02 18:51:09 +02:00
|
|
|
{
|
2023-10-17 12:14:42 +02:00
|
|
|
const qreal from = on ? 1.0 : 0.0;
|
|
|
|
const qreal to = on ? 0.0 : 1.0;
|
|
|
|
|
|
|
|
const auto aspect = faderAspect();
|
|
|
|
|
|
|
|
auto hint = animationHint( aspect );
|
2023-10-17 14:36:44 +02:00
|
|
|
hint.updateFlags = QskAnimationHint::UpdatePolish;
|
2023-05-02 18:51:09 +02:00
|
|
|
|
2023-10-17 12:14:42 +02:00
|
|
|
startTransition( aspect, hint, from, to );
|
2023-05-02 18:51:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#include "moc_QskDrawer.cpp"
|