Merge branch 'master' into features/menubutton

This commit is contained in:
Uwe Rathmann 2023-11-03 18:25:29 +01:00
commit 130bd3f9d2
39 changed files with 1016 additions and 460 deletions

View File

@ -63,7 +63,8 @@ namespace
Drawer( QQuickItem* parent = nullptr )
: QskDrawer( parent )
{
auto box = new QskLinearBox( Qt::Vertical );
auto box = new QskLinearBox( Qt::Vertical, this );
box->setSection( QskAspect::Header );
box->setPanel( true );
box->setPaddingHint( QskBox::Panel, 20 );
@ -76,8 +77,6 @@ namespace
auto btn = new QskPushButton( "Close", box );
connect( btn, &QskPushButton::clicked, this, &QskDrawer::close );
setContent( box );
}
};
@ -211,19 +210,17 @@ namespace
}
{
auto drawer = new Drawer( parentItem() );
drawer->setEdge( Qt::RightEdge );
auto burger = new QskPushButton( "", this );
burger->setEmphasis( QskPushButton::LowEmphasis );
connect( burger, &QskPushButton::clicked,
drawer, &QskPopup::open );
this, &Header::drawerRequested );
}
}
Q_SIGNALS:
void enabledToggled( bool );
void drawerRequested();
};
class MainView : public QskMainView
@ -246,6 +243,12 @@ namespace
connect( header, &Header::enabledToggled,
tabView, &TabView::setPagesEnabled );
auto drawer = new Drawer( tabView );
drawer->setEdge( Qt::RightEdge );
connect( header, &Header::drawerRequested,
drawer, &QskPopup::toggle );
setHeader( header );
setBody( tabView );
}

View File

@ -4,7 +4,7 @@
*****************************************************************************/
/*
Definitions ( where possible ) taken from
Definitions ( where possible ) taken from
https://www.figma.com/file/NAWMapFlXnoOb86Q2H5GKr/Windows-UI-(Community)
*/
@ -35,35 +35,18 @@
*/
/*
The palette is made of a specific configurable colors and
predefined semitransparent shades of gray. Both need to
be resolved to opaque colors with the base colors of the sections.
The palette is made of a couple of configurable base/accent colors and a
predefined set of semitransparent shades of gray. These grays are used
to create darkened/lightend colors from the configurable colors that can
be used for borders etc.
Resolving the colors can be done in 2 ways:
However borders need to be darkened for light base colors and lightened for
dark ones. So we actually have 2 different sets of grays for light/dark
colors schemes.
- render time
This actually means, that we do not create opaque colors and
create the scene graph nodes with semitransparent colors.
- definition time
We create opaque colors for the base colors of the sections
and set them as skin hints.
Resolving at render time sounds like the right solution as we
background colors set in application code will just work.
Unfortunately we have 2 different sets of grays for light/dark
base colors and when applications are setting a light color, where a
dark color ( or v.v ) is expected we might end up with unacceptable
results: ( white on light or black on dark ).
So there are pros and cons and we do not have a final opinion
about waht to do. For the moment we implement resolving at definition
time as an option to be able to play with both solutions.
The main advantage of this concept is, that a working color schemes
can be defined by setting the accent/base colors only.
*/
#include "QskFluent2Skin.h"
#include "QskFluent2Theme.h"
@ -139,9 +122,30 @@ namespace
return qRgba( value, value, value, qRound( opacity * 255 ) );
}
/*
When application code is manipulating base colors manually the colors of
the borders will be lightened/darkened from it like expected - as long
as the application color matches the color scheme. Otherwise we end
up with lightened borders on light backgrounds or v.v.
To avoid this problem we could resolve the grays with the background
colors of the sections at definition time. This solves the problem with
applications using backgrounds from the "wrong" color scheme, but requires
more work for customizing controls hen using the "correct" scheme
( -> border colors need to be set as well ).
When enabling QSK_RESOLVE_COLORS a code path is enabled that is intended
to play with resolving the grays at definition time. But I'm not decided
if it is worth to make a feature from it. TODO ...
*/
#define QSK_RESOLVE_COLORS 0
#if QSK_RESOLVE_COLORS
inline constexpr QRgb rgbFlattened( QRgb foreground, QRgb background )
{
//Q_ASSERT( qAlpha( background ) == 255 );
Q_ASSERT( qAlpha( background ) == 255 );
const auto r2 = qAlpha( foreground ) / 255.0;
const auto r1 = 1.0 - r2;
@ -160,15 +164,25 @@ namespace
or without resolving the foreground alpha value
*/
#if 0
return rgbFlattened( foreground, background );
#else
//Q_ASSERT( qAlpha( background ) == 255 );
}
#else // !QSK_RESOLVE_COLORS
inline QRgb rgbFlattened( QRgb foreground, QRgb background )
{
const auto alpha = qAlpha( foreground ) / 255.0;
return QskRgb::interpolated( background, foreground, alpha );
}
inline constexpr QRgb rgbSolid( QRgb foreground, QRgb background )
{
Q_UNUSED( background );
return foreground;
#endif
}
#endif
class Editor : private QskSkinHintTableEditor
{
public:
@ -521,7 +535,8 @@ void Editor::setupComboBoxColors(
const auto& pal = theme.palette;
for ( const auto state : { QskAspect::NoState, Q::Hovered, Q::Focused, Q::Pressed, Q::Disabled } )
for ( const auto state :
{ QskAspect::NoState, Q::Hovered, Q::Focused, Q::Pressed, Q::Disabled } )
{
QRgb panelColor, borderColor1, borderColor2, textColor;
@ -606,21 +621,16 @@ void Editor::setupDialogButtonBoxColors(
void Editor::setupDrawerMetrics()
{
using Q = QskDrawer;
using A = QskAspect;
setPadding( Q::Panel, 5 );
setHint( Q::Overlay | QskAspect::Style, false );
#if 1
setAnimation( Q::Panel | QskAspect::Position, 200 );
#endif
setAnimation( Q::Panel | A::Position, 300, QEasingCurve::OutCubic );
}
void Editor::setupDrawerColors(
QskAspect::Section section, const QskFluent2Theme& theme )
{
using Q = QskDrawer;
setGradient( Q::Panel | section, theme.palette.background.solid.base );
setGradient( QskDrawer::Panel | section,
theme.palette.background.solid.base );
}
void Editor::setupFocusIndicatorMetrics()
@ -699,7 +709,7 @@ void Editor::setupListViewColors(
{
if ( state1 == Q::Hovered )
cellColor = pal.fillColor.subtle.tertiary;
else
else
cellColor = pal.fillColor.subtle.secondary;
}
@ -735,6 +745,7 @@ void Editor::setupListViewColors(
void Editor::setupMenuMetrics()
{
using Q = QskMenu;
using A = QskAspect;
setPadding( Q::Panel, { 4, 6, 4, 6 } );
setBoxBorderMetrics( Q::Panel, 1 );
@ -748,6 +759,8 @@ void Editor::setupMenuMetrics()
setStrutSize( Q::Icon, 12, 12 );
setPadding( Q::Icon, { 8, 8, 0, 8 } );
setAnimation( Q::Panel | A::Position, 100 );
}
void Editor::setupMenuColors(
@ -790,7 +803,8 @@ void Editor::setupMenuColors(
setColor( Q::Text | Q::Selected | Q::Pressed, pal.fillColor.text.secondary );
setGraphicRole( Q::Icon, QskFluent2Skin::GraphicRoleFillColorTextPrimary );
setGraphicRole( Q::Icon | Q::Selected | Q::Pressed, QskFluent2Skin::GraphicRoleFillColorTextSecondary );
setGraphicRole( Q::Icon | Q::Selected | Q::Pressed,
QskFluent2Skin::GraphicRoleFillColorTextSecondary );
}
void Editor::setupPageIndicatorMetrics()
@ -808,7 +822,7 @@ void Editor::setupPageIndicatorMetrics()
/*
Pressed/Hovered are not yet implemented.
Sizes would be:
Sizes would be:
- Q::Pressed : 3
- Q::Pressed | Q::Selected : 5
@ -857,9 +871,11 @@ void Editor::setupPageIndicatorColors(
void Editor::setupPopup( const QskFluent2Theme& theme )
{
using Q = QskPopup;
using A = QskAspect;
const auto& pal = theme.palette;
setHint( Q::Overlay | A::Style, true );
setGradient( Q::Overlay, pal.background.overlay.defaultColor );
}
@ -1045,7 +1061,7 @@ void Editor::setupRadioBoxMetrics()
However the colors of the inner side of the border are not solid for
the selected states and we use a dummy indicator to get this done.
How to solve this in a better way, TODO ...
How to solve this in a better way, TODO ...
*/
setBoxShape( Q::CheckIndicator, 100, Qt::RelativeSize );
@ -1095,7 +1111,7 @@ void Editor::setupRadioBoxColors(
if ( states & Q::Disabled )
textColor = pal.fillColor.text.disabled;
QRgb panelBorderColor;
QRgb panelBorderColor;
if ( states & ( Q::Disabled | Q::Pressed ) )
panelBorderColor = pal.strokeColor.controlStrong.disabled;
else
@ -1408,7 +1424,7 @@ void Editor::setupSliderColors(
setBoxBorderGradient( Q::Handle, pal.elevation.circle.border, handleColor );
}
for ( auto state : { A::NoState , Q::Pressed , Q::Disabled } )
for ( auto state : { A::NoState, Q::Pressed, Q::Disabled } )
{
QRgb grooveColor, fillColor, rippleColor;
@ -1568,7 +1584,7 @@ void Editor::setupTabButtonColors(
using Q = QskTabButton;
const auto& pal = theme.palette;
for ( const auto state : { QskAspect::NoState,
for ( const auto state : { QskAspect::NoState,
Q::Checked, Q::Hovered, Q::Pressed, Q::Disabled } )
{
QRgb panelColor, textColor;
@ -1866,6 +1882,8 @@ void Editor::setupSwitchButtonColors(
void Editor::setupSubWindow( const QskFluent2Theme& theme )
{
using Q = QskSubWindow;
using A = QskAspect;
const auto& pal = theme.palette;
setPadding( Q::Panel, { 0, 31, 0, 0 } );
@ -1883,6 +1901,8 @@ void Editor::setupSubWindow( const QskFluent2Theme& theme )
setColor( Q::TitleBarText, pal.fillColor.text.primary );
setAlignment( Q::TitleBarText, Qt::AlignLeft );
setTextOptions( Q::TitleBarText, Qt::ElideRight, QskTextOptions::NoWrap );
setAnimation( Q::Panel | A::Position, 300, QEasingCurve::OutCubic );
}
void Editor::setupVirtualKeyboardMetrics()

View File

@ -378,11 +378,9 @@ void Editor::setupMenu()
setColor( Q::Text, m_pal.onSurface );
setFontRole( Q::Text, QskMaterial3Skin::M3BodyMedium );
setPosition( Q::Panel, 0 );
setPosition( Q::Panel | QskPopup::Closed, 1_dp );
setAnimation( Q::Panel | A::Metric, 150 );
setAnimation( Q::Cursor | A::Position | A::Metric, 75, QEasingCurve::OutCubic );
setAnimation( Q::Panel | A::Position, 75 );
}
void Editor::setupTextLabel()
@ -809,12 +807,10 @@ void Editor::setupDialogButtonBox()
void Editor::setupDrawer()
{
using Q = QskDrawer;
using A = QskAspect;
setPadding( Q::Panel, 5_dp );
setGradient( Q::Panel, m_pal.background );
setHint( Q::Overlay | QskAspect::Style, false );
setAnimation( Q::Panel | QskAspect::Position, qskDuration );
setAnimation( Q::Panel | A::Position, 300, QEasingCurve::OutCubic );
}
void Editor::setupSlider()
@ -1264,6 +1260,7 @@ void Editor::setupSubWindow()
for ( auto subControl : { Q::Panel, Q::TitleBarPanel, Q::TitleBarText } )
setAnimation( subControl | A::Color, qskDuration );
setAnimation( Q::Panel | A::Position, qskDuration, QEasingCurve::OutCubic );
}
QskMaterial3Theme::QskMaterial3Theme( QskSkin::ColorScheme colorScheme )

View File

@ -409,7 +409,7 @@ void Editor::setupPopup()
using Q = QskPopup;
setHint( Q::Overlay | A::Style, true );
setGradient( Q::Overlay, QColor( 220, 220, 220, 150 ) );
setGradient( Q::Overlay, qRgba( 220, 220, 220, 150 ) );
}
void Editor::setupMenu()
@ -446,11 +446,8 @@ void Editor::setupMenu()
setGraphicRole( Q::Icon | Q::Disabled, DisabledSymbol );
setGraphicRole( Q::Icon | Q::Selected, CursorSymbol );
setPosition( Q::Panel, 0 );
setPosition( Q::Panel | QskPopup::Closed, 1 );
setAnimation( Q::Panel | A::Metric, 150 );
setAnimation( Q::Cursor | A::Position | A::Metric, 75, QEasingCurve::OutCubic );
setAnimation( Q::Panel | A::Position, 100 );
}
void Editor::setupTextLabel()
@ -759,13 +756,13 @@ void Editor::setupDialogButtonBox()
setBoxShape( Q::Panel, 2 );
}
void Editor::setupDrawer() {
void Editor::setupDrawer()
{
using Q = QskDrawer;
using A = QskAspect;
setPadding( Q::Panel, 5 );
setGradient( Q::Panel, m_pal.darker125 );
setAnimation( Q::Panel | QskAspect::Position, qskDuration );
setHint( Q::Overlay | QskAspect::Style, false );
setPanel( Q::Panel, Plain );
setAnimation( Q::Panel | A::Position, 300, QEasingCurve::OutCubic );
}
void Editor::setupTabButton()
@ -1127,8 +1124,12 @@ void Editor::setupSubWindow()
setAlignment( Q::TitleBarText, Qt::AlignLeft | Qt::AlignVCenter );
#if 1
for ( auto subControl : { Q::Panel, Q::TitleBarPanel, Q::TitleBarText } )
setAnimation( subControl | A::Color, qskDuration );
#endif
setAnimation( Q::Panel | A::Position, qskDuration, QEasingCurve::OutCubic );
}
void Editor::setupSpinBox()

View File

@ -176,6 +176,7 @@ list(APPEND HEADERS
controls/QskComboBoxSkinlet.h
controls/QskControl.h
controls/QskDrawer.h
controls/QskDrawerSkinlet.h
controls/QskEvent.h
controls/QskFlickAnimator.h
controls/QskFocusIndicator.h
@ -280,6 +281,7 @@ list(APPEND SOURCES
controls/QskControlPrivate.cpp
controls/QskDirtyItemFilter.cpp
controls/QskDrawer.cpp
controls/QskDrawerSkinlet.cpp
controls/QskEvent.cpp
controls/QskFlickAnimator.cpp
controls/QskFocusIndicator.cpp

View File

@ -915,10 +915,16 @@ void QskControl::itemChange( QQuickItem::ItemChange change,
break;
}
case QQuickItem::ItemChildAddedChange:
case QQuickItem::ItemChildRemovedChange:
{
if ( autoLayoutChildren() && qskIsAdjustableByLayout( value.item ) )
polish();
if ( autoLayoutChildren() )
{
if ( qskIsVisibleToLayout( value.item ) )
resetImplicitSize();
if ( qskIsAdjustableByLayout( value.item ) )
polish();
}
break;
}
case QQuickItem::ItemActiveFocusHasChanged:

View File

@ -1,48 +1,268 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#include "QskDrawer.h"
#include "QskAspect.h"
#include "QskControl.h"
#include "QskQuick.h"
#include "QskEvent.h"
#include <QskPopup.h>
#include <QskBox.h>
#include <QskAnimationHint.h>
#include <QskQuick.h>
#include "QskPanGestureRecognizer.h"
#include "QskGesture.h"
#include <qguiapplication.h>
#include <qstylehints.h>
QSK_QT_PRIVATE_BEGIN
#include <private/qquickitem_p.h>
#include <private/qquickitemchangelistener_p.h>
QSK_QT_PRIVATE_END
QSK_SUBCONTROL( QskDrawer, Panel )
QSK_SUBCONTROL( QskDrawer, Overlay )
static inline qreal qskDefaultDragMargin()
{
// a skin hint ???
return QGuiApplication::styleHints()->startDragDistance();
}
static inline 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 inline QRectF qskAlignedToEdge(
const QRectF& r, const QSizeF& sz, Qt::Edge edge )
{
switch( edge )
{
case Qt::LeftEdge:
return QRectF( r.left(), r.top(), sz.width(), r.height() );
case Qt::RightEdge:
return QRectF( r.right() - sz.width(), r.top(), sz.width(), r.height() );
case Qt::TopEdge:
return QRectF( r.left(), r.top(), r.width(), sz.height() );
case Qt::BottomEdge:
return QRectF( r.left(), r.bottom() - sz.height(), r.width(), sz.height() );
}
return QRectF();
}
static QPointF qskDrawerTranslation( const QskDrawer* drawer, const QSizeF& size )
{
const auto ratio = 1.0 - drawer->fadingFactor();
auto dx = 0.0;
auto dy = 0.0;
switch( drawer->edge() )
{
case Qt::LeftEdge:
dx = -ratio * size.width();
break;
case Qt::RightEdge:
dx = ratio * size.width();
break;
case Qt::TopEdge:
dy = -ratio * size.height();
break;
case Qt::BottomEdge:
dy = ratio * size.height();
break;
}
return QPointF( dx, dy );
}
namespace
{
// Using an eventFilter for QskEvent::GeometryChange instead ???
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()
{
m_adjustedItem->polish();
}
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;
};
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() );
if ( drawer->isFading() )
return false;
auto rect = qskItemRect( watchedItem() );
if ( !drawer->isOpen() )
{
const auto dragMargin = drawer->dragMargin();
if ( dragMargin <= 0.0 )
return false;
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 );
}
};
}
class QskDrawer::PrivateData
{
public:
QskControl* content = nullptr;
QskBox* contentBox = nullptr;
Qt::Edge edge = Qt::LeftEdge;
PrivateData( Qt::Edge edge )
: edge( edge )
{
}
inline void resetListener( QskDrawer* drawer )
{
delete listener;
listener = nullptr;
if ( drawer->parentItem() && drawer->isVisible() )
listener = new GeometryListener( drawer->parentItem(), drawer );
}
GeometryListener* listener = nullptr;
GestureRecognizer* gestureRecognizer = nullptr;
qreal dragMargin = qskDefaultDragMargin();
Qt::Edge edge;
};
QskDrawer::QskDrawer( QQuickItem* parentItem )
: Inherited ( parentItem )
, m_data( new PrivateData )
: QskDrawer( Qt::LeftEdge, parentItem )
{
}
QskDrawer::QskDrawer( Qt::Edge edge, QQuickItem* parentItem )
: Inherited ( parentItem )
, m_data( new PrivateData( edge ) )
{
#if 1
setZ( 1 );
#endif
setPopupFlag( PopupFlag::CloseOnPressOutside, true );
m_data->contentBox = new QskBox(this);
m_data->contentBox->setSubcontrolProxy( QskBox::Panel, Panel );
m_data->contentBox->setPanel( true );
/*
A drawer wants to be on top of its parent - not being
layouted into its layoutRect(). So we opt out and do
the layout updates manually.
*/
setPlacementPolicy( QskPlacementPolicy::Ignore );
setSubcontrolProxy( Inherited::Overlay, Overlay );
setAutoLayoutChildren( true );
setInteractive( true );
setFaderAspect( Panel | QskAspect::Metric );
connect(this, &QskDrawer::closed, this, [this]() {
startTransition( Panel | QskAspect::Metric,
animationHint( Panel | QskAspect::Position ),
0.0, 1.0 );
});
connect( this, &QskPopup::fadingChanged, this, &QQuickItem::setClip );
}
QskDrawer::~QskDrawer()
{
delete m_data->listener;
}
Qt::Edge QskDrawer::edge() const
@ -60,79 +280,188 @@ void QskDrawer::setEdge( Qt::Edge edge )
edgeChanged( edge );
}
void QskDrawer::setContent( QskControl* content )
void QskDrawer::setInteractive( bool on )
{
content->setParentItem( m_data->contentBox );
if ( content->parent() == nullptr )
content->setParent( m_data->contentBox );
m_data->content = content;
}
void QskDrawer::updateLayout()
{
if ( !isOpen() && !isFading() )
if ( on == isInteractive() )
return;
const auto padding = paddingHint( Panel );
auto contentSize = m_data->content->preferredSize();
contentSize = contentSize.grownBy( padding );
const auto parentSize = parentItem()->size();
switch( m_data->edge )
if ( on )
{
case Qt::Edge::LeftEdge:
{
qreal x = metric( faderAspect() ) * contentSize.width() * -1.0;
m_data->gestureRecognizer = new GestureRecognizer( this );
if ( parentItem() )
qskCatchMouseEvents( parentItem() );
}
else
{
// how to revert qskCatchMouseEvents properly ???
delete m_data->gestureRecognizer;
m_data->gestureRecognizer = nullptr;
}
qskSetItemGeometry( m_data->contentBox,
x, 0, contentSize.width(), parentSize.height() );
break;
}
case Qt::Edge::RightEdge:
{
qreal x = ( metric( faderAspect() ) * contentSize.width() )
+ parentSize.width() - contentSize.width();
Q_EMIT interactiveChanged( on );
}
qskSetItemGeometry( m_data->contentBox,
x, 0, contentSize.width(), parentSize.height() );
break;
bool QskDrawer::isInteractive() const
{
return m_data->gestureRecognizer != nullptr;
}
void QskDrawer::setDragMargin( qreal margin )
{
margin = std::max( margin, 0.0 );
if ( margin != m_data->dragMargin )
{
m_data->dragMargin = margin;
Q_EMIT dragMarginChanged( margin );
}
}
void QskDrawer::resetDragMargin()
{
setDragMargin( qskDefaultDragMargin() );
}
qreal QskDrawer::dragMargin() const
{
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 )
{
const auto forwards = qskCheckDirection( m_data->edge, gesture );
if ( forwards != isOpen() )
setOpen( forwards );
}
case Qt::Edge::TopEdge:
{
qreal y = metric( faderAspect() ) * contentSize.height();
return;
}
qskSetItemGeometry( m_data->contentBox,
0, -y, parentSize.width(), contentSize.height() );
Inherited::gestureEvent( event );
}
void QskDrawer::itemChange( QQuickItem::ItemChange change,
const QQuickItem::ItemChangeData& value )
{
Inherited::itemChange( change, value );
switch( static_cast< int >( change ) )
{
case QQuickItem::ItemParentHasChanged:
{
if ( parentItem() && isInteractive() )
qskCatchMouseEvents( parentItem() );
m_data->resetListener( this );
break;
}
case Qt::Edge::BottomEdge:
case QQuickItem::ItemVisibleHasChanged:
{
qreal y = metric( faderAspect() ) * contentSize.height() + parentSize.height() -
contentSize.height();
qskSetItemGeometry( m_data->contentBox,
0, y, parentSize.width(), contentSize.height() );
m_data->resetListener( this );
break;
}
}
m_data->content->setGeometry( QPointF( padding.left(), padding.top() ),
m_data->contentBox->size().shrunkBy( padding ) );
Inherited::updateLayout();
}
void QskDrawer::aboutToShow()
void QskDrawer::updateResources()
{
startTransition( Panel | QskAspect::Metric,
animationHint( Panel | QskAspect::Position ), 1.0, 0.0 );
Inherited::updateResources();
Inherited::aboutToShow();
/*
Adjusting the geometry to the parent needs to be done before
the layouting of the children ( -> autoLayoutChildren ) is done.
So we are using this hook even if it is not about resources: TODO ...
*/
if ( const auto item = parentItem() )
{
auto r = qskItemRect( item );
r = qskAlignedToEdge( r, sizeConstraint( Qt::PreferredSize ), edge() );
r.translate( qskDrawerTranslation( this, r.size() ) );
setGeometry( r );
}
}
void QskDrawer::updateNode( QSGNode* node )
{
if ( isFading() && clip() )
{
if ( auto clipNode = QQuickItemPrivate::get( this )->clipNode() )
{
/*
The clipRect is changing while fading. Couldn't
find a way how to trigger updates - maybe be enabling/disabling
the clip. So we do the updates manually. TODO ...
*/
const auto r = clipRect();
if ( r != clipNode->rect() )
{
clipNode->setRect( r );
clipNode->update();
}
}
}
Inherited::updateNode( node );
}
QRectF QskDrawer::clipRect() const
{
if ( isFading() && parentItem() )
{
/*
We might not fit into our parent and our children not
into our rect. So we want to have a clip against the
edge, where the drawer slides in/out only.
Otherwise we would have unwanted effects, when clipping gets
disabled once the transition is over.
*/
constexpr qreal d = 1e6;
QRectF r( -d, -d, 2.0 * d, 2.0 * d );
switch( edge() )
{
case Qt::LeftEdge:
r.setLeft( -x() );
break;
case Qt::RightEdge:
r.setRight( parentItem()->width() - x() );
break;
case Qt::TopEdge:
r.setTop( -y() );
break;
case Qt::BottomEdge:
r.setBottom( parentItem()->height() - y() );
break;
}
return r;
}
return Inherited::clipRect();
}
QskAspect QskDrawer::fadingAspect() const
{
return QskDrawer::Panel | QskAspect::Position;
}
QRectF QskDrawer::layoutRectForSize( const QSizeF& size ) const
{
return subControlContentsRect( size, Panel );
}
#include "moc_QskDrawer.cpp"

View File

@ -1,8 +1,12 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#ifndef QSK_DRAWER_H
#define QSK_DRAWER_H
#include "QskPopup.h"
#include <qnamespace.h>
class QSK_EXPORT QskDrawer : public QskPopup
{
@ -12,24 +16,46 @@ class QSK_EXPORT QskDrawer : public QskPopup
Q_PROPERTY( Qt::Edge edge READ edge WRITE setEdge NOTIFY edgeChanged )
Q_PROPERTY( qreal dragMargin READ dragMargin
WRITE setDragMargin RESET resetDragMargin NOTIFY dragMarginChanged )
Q_PROPERTY( bool interactive READ isInteractive
WRITE setInteractive NOTIFY interactiveChanged )
public:
QSK_SUBCONTROLS( Panel, Overlay )
QSK_SUBCONTROLS( Panel )
QskDrawer( QQuickItem* = nullptr );
QskDrawer( Qt::Edge, QQuickItem* = nullptr );
~QskDrawer() override;
void setEdge( Qt::Edge );
Qt::Edge edge() const;
void updateLayout() override;
void setInteractive( bool );
bool isInteractive() const;
void setContent( QskControl* );
void setDragMargin( qreal );
void resetDragMargin();
qreal dragMargin() const;
QRectF clipRect() const override;
QskAspect fadingAspect() const override;
QRectF layoutRectForSize( const QSizeF& ) const override;
Q_SIGNALS:
void edgeChanged( Qt::Edge );
void dragMarginChanged( qreal );
void interactiveChanged( bool );
protected:
void aboutToShow() override;
void gestureEvent( QskGestureEvent* ) override;
void itemChange( ItemChange, const ItemChangeData& ) override;
void updateResources() override;
void updateNode( QSGNode* ) override;
private:
class PrivateData;

View File

@ -0,0 +1,45 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#include "QskDrawerSkinlet.h"
#include "QskDrawer.h"
QskDrawerSkinlet::QskDrawerSkinlet( QskSkin* skin )
: Inherited( skin )
{
appendNodeRoles( { PanelRole } );
}
QskDrawerSkinlet::~QskDrawerSkinlet() = default;
QRectF QskDrawerSkinlet::subControlRect(
const QskSkinnable* skinnable, const QRectF& contentsRect,
QskAspect::Subcontrol subControl ) const
{
if ( subControl == QskDrawer::Panel )
return contentsRect;
return Inherited::subControlRect( skinnable, contentsRect, subControl );
}
QSGNode* QskDrawerSkinlet::updateSubNode(
const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const
{
if ( nodeRole == PanelRole )
return updateBoxNode( skinnable, node, QskDrawer::Panel );
return Inherited::updateSubNode( skinnable, nodeRole, node );
}
QSizeF QskDrawerSkinlet::sizeHint( const QskSkinnable* skinnable,
Qt::SizeHint which, const QSizeF& constraint ) const
{
if ( which == Qt::PreferredSize )
return skinnable->strutSizeHint( QskDrawer::Panel );
return Inherited::sizeHint( skinnable, which, constraint );
}
#include "moc_QskDrawerSkinlet.cpp"

View File

@ -0,0 +1,40 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#ifndef QSK_DRAWER_SKINLET_H
#define QSK_DRAWER_SKINLET_H
#include "QskPopupSkinlet.h"
class QSK_EXPORT QskDrawerSkinlet : public QskPopupSkinlet
{
Q_GADGET
using Inherited = QskPopupSkinlet;
public:
enum NodeRole
{
ContentsRole = Inherited::RoleCount,
PanelRole,
RoleCount
};
Q_INVOKABLE QskDrawerSkinlet( QskSkin* = nullptr );
~QskDrawerSkinlet() override;
QRectF subControlRect( const QskSkinnable*,
const QRectF&, QskAspect::Subcontrol ) const override;
QSizeF sizeHint( const QskSkinnable*,
Qt::SizeHint, const QSizeF& ) const override;
protected:
QSGNode* updateSubNode( const QskSkinnable*,
quint8 nodeRole, QSGNode* ) const override;
};
#endif

View File

@ -68,7 +68,8 @@ class QskGestureRecognizer::PrivateData
return watchedItem->acceptedMouseButtons();
}
QQuickItem* watchedItem = nullptr;
QPointer< QQuickItem > watchedItem = nullptr;
QPointer< QQuickItem > targetItem = nullptr;
QVector< QMouseEvent* > pendingEvents;
@ -121,6 +122,16 @@ QQuickItem* QskGestureRecognizer::watchedItem() const
return m_data->watchedItem;
}
void QskGestureRecognizer::setTargetItem( QQuickItem* item )
{
m_data->targetItem = item;
}
QQuickItem* QskGestureRecognizer::targetItem() const
{
return m_data->targetItem;
}
void QskGestureRecognizer::setAcceptedMouseButtons( Qt::MouseButtons buttons )
{
m_data->buttons = buttons;
@ -131,12 +142,9 @@ Qt::MouseButtons QskGestureRecognizer::acceptedMouseButtons() const
return m_data->buttons;
}
QRectF QskGestureRecognizer::gestureRect() const
bool QskGestureRecognizer::isAcceptedPos( const QPointF& pos ) const
{
if ( m_data->watchedItem )
return qskItemRect( m_data->watchedItem );
return QRectF( 0.0, 0.0, -1.0, -1.0 );
return m_data->watchedItem && m_data->watchedItem->contains( pos );
}
void QskGestureRecognizer::setRejectOnTimeout( bool on )
@ -302,7 +310,7 @@ bool QskGestureRecognizer::processMouseEvent(
if ( event->type() == QEvent::MouseButtonPress )
{
if ( !gestureRect().contains( pos ) )
if ( !isAcceptedPos( pos ) )
return false;
if ( m_data->state != Idle )

View File

@ -22,6 +22,7 @@ class QSK_EXPORT QskGestureRecognizer : public QObject
Q_PROPERTY( State state READ state NOTIFY stateChanged )
Q_PROPERTY( QQuickItem* watchedItem READ watchedItem WRITE setWatchedItem )
Q_PROPERTY( QQuickItem* targetItem READ targetItem WRITE setTargetItem )
Q_PROPERTY( Qt::MouseButtons acceptedMouseButtons
READ acceptedMouseButtons WRITE setAcceptedMouseButtons )
@ -43,11 +44,16 @@ class QSK_EXPORT QskGestureRecognizer : public QObject
QskGestureRecognizer( QObject* parent = nullptr );
~QskGestureRecognizer() override;
bool eventFilter( QObject* object, QEvent* event) override;
bool eventFilter( QObject*, QEvent* ) override;
// the item where the gesture happens
void setWatchedItem( QQuickItem* );
QQuickItem* watchedItem() const;
// the item processing the gesture events
void setTargetItem( QQuickItem* );
QQuickItem* targetItem() const;
// Qt::NoButton means: all buttons accepted
void setAcceptedMouseButtons( Qt::MouseButtons );
Qt::MouseButtons acceptedMouseButtons() const;
@ -67,7 +73,7 @@ class QSK_EXPORT QskGestureRecognizer : public QObject
State state() const;
virtual QRectF gestureRect() const;
virtual bool isAcceptedPos( const QPointF& ) const;
Q_SIGNALS:
void stateChanged( State from, State to );

View File

@ -76,20 +76,28 @@ static inline QVariant qskAligned05( const QVariant& value )
#endif
static inline bool qskCheckReceiverThread( const QObject* receiver )
static inline void qskSendAnimatorEvent(
const QskAspect aspect, int index, bool on, QObject* receiver )
{
/*
QskInputPanelSkinlet changes the skin state, what leads to
sending events from the wrong thread. Until we have fixed it
let's block sending the event to avoid running into assertions
in QCoreApplication::sendEvent
*/
const auto state = on ? QskAnimatorEvent::Started : QskAnimatorEvent::Terminated;
const QThread* thread = receiver->thread();
if ( thread == nullptr )
return true;
return ( thread == QThread::currentThread() );
const auto thread = receiver->thread();
if ( thread && ( thread != QThread::currentThread() ) )
{
/*
QskInputPanelSkinlet changes the skin state, what leads to
sending events from the wrong thread. We can't use
QCoreApplication::sendEvent then, TODO ...
*/
auto event = new QskAnimatorEvent( aspect, index, state );
QCoreApplication::postEvent( receiver, event );
}
else
{
QskAnimatorEvent event( aspect, index, state );
QCoreApplication::sendEvent( receiver, &event );
}
}
QskHintAnimator::QskHintAnimator() noexcept
@ -141,7 +149,7 @@ void QskHintAnimator::advance( qreal progress )
{
if ( m_updateFlags == QskAnimationHint::UpdateAuto )
{
if ( m_aspect.isMetric() )
if ( !m_aspect.isColor() )
{
m_control->resetImplicitSize();
@ -338,11 +346,7 @@ void QskHintAnimatorTable::start( QskControl* control,
animator->start();
if ( qskCheckReceiverThread( control ) )
{
QskAnimatorEvent event( aspect, index, QskAnimatorEvent::Started );
QCoreApplication::sendEvent( control, &event );
}
qskSendAnimatorEvent( aspect, index, true, control );
}
const QskHintAnimator* QskHintAnimatorTable::animator( QskAspect aspect, int index ) const
@ -390,15 +394,7 @@ bool QskHintAnimatorTable::cleanup()
it = animators.erase( it );
if ( control )
{
if ( qskCheckReceiverThread( control ) )
{
auto event = new QskAnimatorEvent(
aspect, index, QskAnimatorEvent::Terminated );
QCoreApplication::postEvent( control, event );
}
}
qskSendAnimatorEvent( aspect, index, false, control );
}
else
{

View File

@ -24,7 +24,7 @@ class QSK_EXPORT QskListViewSkinlet : public QskScrollViewSkinlet
public:
enum NodeRole
{
TextRole,
TextRole = Inherited::RoleCount,
GraphicRole,
RoleCount

View File

@ -103,7 +103,7 @@ void QskMainView::focusInEvent( QFocusEvent* event )
{
if ( auto focusItem = nextItemInFocusChain( true ) )
{
if ( qskIsItemComplete( focusItem )
if ( !qskIsItemInDestructor( focusItem )
&& qskIsAncestorOf( this, focusItem ) )
{
focusItem->setFocus( true );

View File

@ -18,7 +18,10 @@
#include <qvariant.h>
#include <qeventloop.h>
QSK_SUBCONTROL( QskMenu, Overlay )
QSK_QT_PRIVATE_BEGIN
#include <private/qquickitem_p.h>
QSK_QT_PRIVATE_END
QSK_SUBCONTROL( QskMenu, Panel )
QSK_SUBCONTROL( QskMenu, Segment )
QSK_SUBCONTROL( QskMenu, Cursor )
@ -58,20 +61,23 @@ QskMenu::QskMenu( QQuickItem* parent )
, m_data( new PrivateData )
{
setModal( true );
setFaderAspect( QskMenu::Panel | QskAspect::Position | QskAspect::Metric );
setPopupFlag( QskPopup::CloseOnPressOutside, true );
setPopupFlag( QskPopup::DeleteOnClose, true );
setPlacementPolicy( QskPlacementPolicy::Ignore );
setSubcontrolProxy( Inherited::Overlay, Overlay );
initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed );
// we hide the focus indicator while fading
connect( this, &QskMenu::fadingChanged, this,
&QskControl::focusIndicatorRectChanged );
// we hide the focus indicator while sliding
connect( this, &QskPopup::fadingChanged,
this, &QskControl::focusIndicatorRectChanged );
connect( this, &QskMenu::opened, this,
connect( this, &QskPopup::fadingChanged,
this, &QQuickItem::setClip );
connect( this, &QskPopup::opened, this,
[this]() { m_data->triggeredIndex = -1; } );
setAcceptHoverEvents( true );
@ -81,6 +87,17 @@ QskMenu::~QskMenu()
{
}
QRectF QskMenu::clipRect() const
{
if ( isFading() )
{
constexpr qreal d = 1e6;
return QRectF( -d, m_data->origin.y() - y(), 2.0 * d, d );
}
return Inherited::clipRect();
}
#if 1
// has no effect as we do not offer submenus yet. TODO ...
@ -263,25 +280,57 @@ QString QskMenu::triggeredText() const
return optionAt( m_data->triggeredIndex ).text();
}
void QskMenu::updateResources()
{
qreal dy = 0.0;
if ( isFading() )
dy = ( 1.0 - fadingFactor() ) * height();
setPosition( m_data->origin.x(), m_data->origin.y() - dy );
Inherited::updateResources();
}
void QskMenu::updateNode( QSGNode* node )
{
if ( isFading() && clip() )
{
if ( auto clipNode = QQuickItemPrivate::get( this )->clipNode() )
{
/*
The clipRect is changing while fading. Couldn't
find a way how to trigger updates - maybe be enabling/disabling
the clip. So we do the updates manually. TODO ...
*/
const auto r = clipRect();
if ( r != clipNode->rect() )
{
clipNode->setRect( r );
clipNode->update();
}
}
}
Inherited::updateNode( node );
}
void QskMenu::keyPressEvent( QKeyEvent* event )
{
if( m_data->currentIndex < 0 )
return;
int key = event->key();
switch( key )
switch( event->key() )
{
case Qt::Key_Up:
{
traverse( -1 );
break;
return;
}
case Qt::Key_Down:
{
traverse( 1 );
break;
return;
}
case Qt::Key_Select:
@ -293,19 +342,17 @@ void QskMenu::keyPressEvent( QKeyEvent* event )
return;
}
case Qt::Key_Escape:
case Qt::Key_Cancel:
{
close();
return;
}
default:
{
if ( const int steps = qskFocusChainIncrement( event ) )
{
traverse( steps );
return;
}
}
}
return Inherited::keyPressEvent( event );
}
void QskMenu::keyReleaseEvent( QKeyEvent* )
@ -439,7 +486,7 @@ void QskMenu::mouseReleaseEvent( QMouseEvent* event )
void QskMenu::aboutToShow()
{
setGeometry( QRectF( m_data->origin, sizeConstraint() ) );
setSize( sizeConstraint() );
if ( m_data->currentIndex < 0 )
{
@ -496,6 +543,11 @@ void QskMenu::trigger( int index )
}
}
QskAspect QskMenu::fadingAspect() const
{
return QskMenu::Panel | QskAspect::Position;
}
int QskMenu::exec()
{
(void) execPopup();

View File

@ -37,7 +37,7 @@ class QSK_EXPORT QskMenu : public QskPopup
using Inherited = QskPopup;
public:
QSK_SUBCONTROLS( Overlay, Panel, Segment, Cursor, Text, Icon, Separator )
QSK_SUBCONTROLS( Panel, Segment, Cursor, Text, Icon, Separator )
QSK_STATES( Selected, Pressed )
QskMenu( QQuickItem* parentItem = nullptr );
@ -81,6 +81,9 @@ class QSK_EXPORT QskMenu : public QskPopup
bool isPressed() const;
QRectF clipRect() const override;
QskAspect fadingAspect() const override;
Q_INVOKABLE int exec();
Q_SIGNALS:
@ -115,6 +118,9 @@ class QSK_EXPORT QskMenu : public QskPopup
void aboutToShow() override;
void trigger( int );
void updateResources() override;
void updateNode( QSGNode* ) override;
private:
void traverse( int steps );

View File

@ -9,12 +9,13 @@
#include "QskGraphic.h"
#include "QskColorFilter.h"
#include "QskTextOptions.h"
#include "QskSGNode.h"
#include "QskFunctions.h"
#include "QskMargins.h"
#include "QskFunctions.h"
#include "QskLabelData.h"
#include "QskSGNode.h"
#include <qfontmetrics.h>
#include <qmath.h>
@ -209,11 +210,31 @@ QskMenuSkinlet::QskMenuSkinlet( QskSkin* skin )
: Inherited( skin )
, m_data( new PrivateData() )
{
appendNodeRoles( { PanelRole } );
appendNodeRoles( { ContentsRole, PanelRole } );
}
QskMenuSkinlet::~QskMenuSkinlet() = default;
QSGNode* QskMenuSkinlet::updateSubNode(
const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const
{
switch ( nodeRole )
{
case ContentsRole:
{
const auto popup = static_cast< const QskPopup* >( skinnable );
auto rect = popup->contentsRect();
if ( rect.isEmpty() )
return nullptr;
return updateContentsNode( popup, node );
}
}
return Inherited::updateSubNode( skinnable, nodeRole, node );
}
QRectF QskMenuSkinlet::cursorRect(
const QskSkinnable* skinnable, const QRectF& contentsRect, int index ) const
{
@ -407,7 +428,8 @@ QskAspect::States QskMenuSkinlet::sampleStates(
}
}
const auto cursorPos = menu->effectiveSkinHint( Q::Segment | Q::Hovered | A::Metric | A::Position ).toPointF();
const auto cursorPos = menu->effectiveSkinHint(
Q::Segment | Q::Hovered | A::Metric | A::Position ).toPointF();
if( !cursorPos.isNull() && menu->indexAtPosition( cursorPos ) == index )
{

View File

@ -20,7 +20,9 @@ class QSK_EXPORT QskMenuSkinlet : public QskPopupSkinlet
public:
enum NodeRole
{
PanelRole = QskPopupSkinlet::RoleCount,
ContentsRole = Inherited::RoleCount,
PanelRole,
RoleCount
};
@ -48,7 +50,10 @@ class QSK_EXPORT QskMenuSkinlet : public QskPopupSkinlet
Qt::SizeHint, const QSizeF& ) const override;
protected:
QSGNode* updateContentsNode( const QskPopup*, QSGNode* ) const override;
QSGNode* updateSubNode( const QskSkinnable*,
quint8 nodeRole, QSGNode* ) const override;
QSGNode* updateContentsNode( const QskPopup*, QSGNode* ) const;
QSGNode* updateMenuNode( const QskSkinnable*, QSGNode* ) const;
QSGNode* updateSampleNode( const QskSkinnable*,

View File

@ -7,10 +7,11 @@
#include "QskEvent.h"
#include "QskGesture.h"
#include <qcoreapplication.h>
#include <qline.h>
#include <qmath.h>
#include <qquickitem.h>
#include <qguiapplication.h>
#include <qstylehints.h>
static inline bool qskIsInOrientation(
const QPointF& from, const QPointF& to, Qt::Orientations orientations )
@ -60,9 +61,14 @@ static inline qreal qskAngle(
}
static void qskSendPanGestureEvent(
QQuickItem* item, QskGesture::State state, qreal velocity, qreal angle,
const QPointF& origin, const QPointF& lastPosition, const QPointF& position )
QskGestureRecognizer* recognizer, QskGesture::State state,
qreal velocity, qreal angle, const QPointF& origin,
const QPointF& lastPosition, const QPointF& position )
{
auto item = recognizer->targetItem();
if ( item == nullptr )
item = recognizer->watchedItem();
auto gesture = std::make_shared< QskPanGesture >();
gesture->setState( state );
@ -146,7 +152,7 @@ class QskPanGestureRecognizer::PrivateData
public:
Qt::Orientations orientations = Qt::Horizontal | Qt::Vertical;
int minDistance = 15;
int minDistance = QGuiApplication::styleHints()->startDragDistance() + 5;
quint64 timestampVelocity = 0.0; // timestamp of the last mouse event
qreal angle = 0.0;
@ -243,12 +249,12 @@ void QskPanGestureRecognizer::processMove( const QPointF& pos, quint64 timestamp
if ( started )
{
qskSendPanGestureEvent( watchedItem(), QskGesture::Started,
qskSendPanGestureEvent( this, QskGesture::Started,
velocity, m_data->angle, m_data->origin, m_data->origin, m_data->pos );
}
else
{
qskSendPanGestureEvent( watchedItem(), QskGesture::Updated,
qskSendPanGestureEvent( this, QskGesture::Updated,
velocity, m_data->angle, m_data->origin, oldPos, m_data->pos );
}
}
@ -261,7 +267,7 @@ void QskPanGestureRecognizer::processRelease( const QPointF&, quint64 timestamp
const ulong elapsedTotal = timestamp - timestampStarted();
const qreal velocity = m_data->velocityTracker.velocity( elapsedTotal );
qskSendPanGestureEvent( watchedItem(), QskGesture::Finished,
qskSendPanGestureEvent( this, QskGesture::Finished,
velocity, m_data->angle, m_data->origin, m_data->pos, m_data->pos );
}
}

View File

@ -23,11 +23,12 @@ class QSK_EXPORT QskPanGestureRecognizer : public QskGestureRecognizer
void setOrientations( Qt::Orientations );
Qt::Orientations orientations() const;
private:
protected:
void processPress( const QPointF&, quint64 timestamp, bool isFinal ) override;
void processMove( const QPointF&, quint64 timestamp ) override;
void processRelease( const QPointF&, quint64 timestamp ) override;
private:
class PrivateData;
std::unique_ptr< PrivateData > m_data;
};

View File

@ -10,6 +10,7 @@
#include "QskWindow.h"
#include "QskEvent.h"
#include "QskPlatform.h"
#include "QskHintAnimator.h"
#include <qpa/qplatformintegration.h>
@ -75,6 +76,23 @@ static bool qskReplayMousePress()
return false;
}
static void qskStartFading( QskPopup* popup, bool on )
{
const auto aspect = popup->fadingAspect();
auto hint = popup->animationHint( aspect );
if ( hint.isValid() )
{
hint.updateFlags = QskAnimationHint::UpdatePolish | QskAnimationHint::UpdateNode;
const qreal from = on ? 0.0 : 1.0;
const qreal to = on ? 1.0 : 0.0;
popup->startTransition( aspect, hint, from, to );
}
}
namespace
{
class InputGrabber final : public QskInputGrabber
@ -130,7 +148,6 @@ class QskPopup::PrivateData
PrivateData()
: flags( 0 )
, isModal( false )
, hasFaderEffect( true )
, autoGrabFocus( true )
, handoverFocus( true )
{
@ -139,11 +156,9 @@ class QskPopup::PrivateData
InputGrabber* inputGrabber = nullptr;
uint priority = 0;
QskAspect faderAspect;
int flags : 4;
bool isModal : 1;
bool hasFaderEffect : 1;
const bool autoGrabFocus : 1;
const bool handoverFocus : 1;
@ -196,6 +211,11 @@ void QskPopup::close()
setOpen( false );
}
void QskPopup::toggle()
{
setOpen( !isOpen() );
}
void QskPopup::setOpen( bool on )
{
if ( on == isOpen() )
@ -213,6 +233,8 @@ void QskPopup::setOpen( bool on )
else
Q_EMIT closed();
qskStartFading( this, on );
if ( isFading() )
{
Q_EMIT fadingChanged( true );
@ -234,15 +256,22 @@ bool QskPopup::isOpen() const
return !hasSkinState( QskPopup::Closed );
}
QskAspect QskPopup::fadingAspect() const
{
return QskAspect();
}
bool QskPopup::isFading() const
{
if ( m_data->faderAspect.value() == 0 )
return false;
return runningHintAnimator( fadingAspect() ) != nullptr;
}
QskSkinHintStatus status;
(void) effectiveSkinHint( m_data->faderAspect, &status );
qreal QskPopup::fadingFactor() const
{
if ( auto animator = runningHintAnimator( fadingAspect() ) )
return animator->currentValue().value< qreal >();
return status.source == QskSkinHintStatus::Animator;
return isOpen() ? 1.0 : 0.0;
}
QRectF QskPopup::overlayRect() const
@ -293,44 +322,23 @@ void QskPopup::updateInputGrabber()
}
}
QskAspect QskPopup::faderAspect() const
{
return m_data->faderAspect;
}
void QskPopup::setFaderAspect( QskAspect aspect )
{
auto faderAspect = aspect;
faderAspect.clearStates(); // animated values are always stateless
if ( faderAspect == m_data->faderAspect )
return;
if ( isFading() )
{
// stop the running animation TODO ...
}
m_data->faderAspect = faderAspect;
}
bool QskPopup::isTransitionAccepted( QskAspect aspect ) const
{
if ( isVisible() && m_data->hasFaderEffect )
if ( isVisible() && !isInitiallyPainted() )
{
/*
Usually we suppress transitions, when a control has never been
painted before as there is no valid starting point. Popups are
different as we want to have smooth fade/slide appearances.
*/
if ( ( aspect.value() == 0 ) )
{
return true;
}
if ( aspect == m_data->faderAspect )
return true;
if ( aspect.isColor() )
{
if ( aspect.subControl() == effectiveSubcontrol( QskPopup::Overlay ) )
return true;
}
if ( aspect.subControl() == effectiveSubcontrol( fadingAspect().subControl() ) )
return true;
if ( aspect.subControl() == effectiveSubcontrol( QskPopup::Overlay ) )
return true;
}
return Inherited::isTransitionAccepted( aspect );
@ -366,20 +374,6 @@ bool QskPopup::isModal() const
return m_data->isModal;
}
void QskPopup::setFaderEffect( bool on )
{
if ( on != m_data->hasFaderEffect )
{
m_data->hasFaderEffect = on;
Q_EMIT faderEffectChanged( on );
}
}
bool QskPopup::hasFaderEffect() const
{
return m_data->hasFaderEffect;
}
void QskPopup::setPopupFlags( PopupFlags flags )
{
const auto newFlags = static_cast< int >( flags );
@ -488,10 +482,10 @@ bool QskPopup::event( QEvent* event )
}
case QskEvent::Animator:
{
const auto animtorEvent = static_cast< QskAnimatorEvent* >( event );
const auto animatorEvent = static_cast< QskAnimatorEvent* >( event );
if ( ( animtorEvent->state() == QskAnimatorEvent::Terminated )
&& ( animtorEvent->aspect() == m_data->faderAspect ) )
if ( ( animatorEvent->state() == QskAnimatorEvent::Terminated )
&& ( animatorEvent->aspect() == fadingAspect() ) )
{
if ( !isOpen() )
{
@ -523,6 +517,17 @@ bool QskPopup::event( QEvent* event )
return ok;
}
void QskPopup::keyPressEvent( QKeyEvent* event )
{
if ( qskIsStandardKeyInput( event, QKeySequence::Cancel ) )
{
close();
return;
}
return Inherited::keyPressEvent( event );
}
void QskPopup::focusInEvent( QFocusEvent* event )
{
Inherited::focusInEvent( event );
@ -543,7 +548,7 @@ void QskPopup::focusInEvent( QFocusEvent* event )
if ( auto focusItem = nextItemInFocusChain( true ) )
{
if ( qskIsItemComplete( focusItem )
if ( !qskIsItemInDestructor( focusItem )
&& qskIsAncestorOf( this, focusItem ) )
{
focusItem->setFocus( true );

View File

@ -14,13 +14,11 @@ class QSK_EXPORT QskPopup : public QskControl
Q_PROPERTY( bool open READ isOpen WRITE setOpen NOTIFY openChanged )
Q_PROPERTY( bool modal READ isModal WRITE setModal NOTIFY modalChanged )
Q_PROPERTY( bool fading READ isFading NOTIFY fadingChanged )
Q_PROPERTY( bool overlay READ hasOverlay
WRITE setOverlay RESET resetOverlay NOTIFY overlayChanged )
Q_PROPERTY( bool faderEffect READ hasFaderEffect
WRITE setFaderEffect NOTIFY faderEffectChanged )
Q_PROPERTY( uint priority READ priority WRITE setPriority NOTIFY priorityChanged )
using Inherited = QskControl;
@ -58,20 +56,20 @@ class QSK_EXPORT QskPopup : public QskControl
void setPriority( uint );
uint priority() const;
void setFaderEffect( bool );
bool hasFaderEffect() const;
bool isOpen() const;
bool isClosed() const;
QskAspect faderAspect() const;
void setFaderAspect( QskAspect );
bool isFading() const;
qreal fadingFactor() const;
virtual QskAspect fadingAspect() const;
virtual QRectF overlayRect() const;
bool isOpen() const;
bool isFading() const;
public Q_SLOTS:
void open();
void close();
void toggle();
void setOpen( bool );
Q_SIGNALS:
@ -83,7 +81,6 @@ class QSK_EXPORT QskPopup : public QskControl
void modalChanged( bool );
void overlayChanged( bool );
void priorityChanged( uint );
void faderEffectChanged( bool );
protected:
void aboutToShow() override;
@ -92,6 +89,7 @@ class QSK_EXPORT QskPopup : public QskControl
bool event( QEvent* ) override;
void focusInEvent( QFocusEvent* ) override;
void focusOutEvent( QFocusEvent* ) override;
void keyPressEvent( QKeyEvent* ) override;
void windowChangeEvent( QskWindowChangeEvent* ) override;
void itemChange( QQuickItem::ItemChange,
@ -113,6 +111,11 @@ class QSK_EXPORT QskPopup : public QskControl
std::unique_ptr< PrivateData > m_data;
};
inline bool QskPopup::isClosed() const
{
return !isOpen();
}
Q_DECLARE_OPERATORS_FOR_FLAGS( QskPopup::PopupFlags )
#endif

View File

@ -5,108 +5,17 @@
#include "QskPopupSkinlet.h"
#include "QskPopup.h"
#include "QskSGNode.h"
#include "QskRgbValue.h"
#include <qtransform.h>
#include <qsgnode.h>
#include <qquickwindow.h>
namespace
static inline QRgb qskInterpolatedRgb( QRgb rgb, qreal factor )
{
class RootNode : public QSGNode
{
public:
~RootNode() override
{
delete m_clipNode;
delete m_transformNode;
delete m_contentsNode;
}
void setClipRect( const QRectF& rect )
{
if ( m_clipNode == nullptr )
{
m_clipNode = new QSGClipNode();
m_clipNode->setFlag( QSGNode::OwnedByParent, false );
m_clipNode->setIsRectangular( true );
}
m_clipNode->setClipRect( rect );
}
void resetClip()
{
delete m_clipNode;
m_clipNode = nullptr;
}
void setTranslation( qreal dx, qreal dy )
{
if ( dx != 0.0 || dy != 0.0 )
{
if ( m_transformNode == nullptr )
{
m_transformNode = new QSGTransformNode();
m_transformNode->setFlag( QSGNode::OwnedByParent, false );
}
QTransform transform;
transform.translate( dx, dy );
m_transformNode->setMatrix( transform );
}
else
{
delete m_transformNode;
m_transformNode = nullptr;
}
}
void setContentsNode( QSGNode* contentsNode )
{
if ( m_contentsNode != contentsNode )
{
if ( contentsNode )
contentsNode->setFlag( QSGNode::OwnedByParent, false );
delete m_contentsNode;
m_contentsNode = contentsNode;
}
}
void rearrangeNodes()
{
const std::initializer_list< QSGNode* > nodes =
{ m_clipNode, m_transformNode, m_contentsNode };
QSGNode* parentNode = this;
for ( auto node : nodes )
{
if ( node )
{
QskSGNode::setParentNode( node, parentNode );
parentNode = node;
}
}
}
inline QSGNode* contentsNode()
{
return m_contentsNode;
}
private:
QSGClipNode* m_clipNode = nullptr;
QSGTransformNode* m_transformNode = nullptr;
QSGNode* m_contentsNode = nullptr;
};
return QskRgb::toTransparent( rgb, qRound( factor * qAlpha( rgb ) ) );
}
QskPopupSkinlet::QskPopupSkinlet( QskSkin* skin )
: Inherited( skin )
{
appendNodeRoles( { OverlayRole, ContentsRole } );
appendNodeRoles( { OverlayRole } );
}
QskPopupSkinlet::~QskPopupSkinlet() = default;
@ -130,49 +39,38 @@ QSGNode* QskPopupSkinlet::updateSubNode(
switch ( nodeRole )
{
case OverlayRole:
return updateBoxNode( skinnable, node, QskPopup::Overlay );
case ContentsRole:
return updateExtraNode( popup, node );
return updateOverlayNode( popup, node );
}
return Inherited::updateSubNode( skinnable, nodeRole, node );
}
QSGNode* QskPopupSkinlet::updateExtraNode( const QskPopup* popup, QSGNode* node ) const
QSGNode* QskPopupSkinlet::updateOverlayNode(
const QskPopup* popup, QSGNode* node ) const
{
auto cr = popup->contentsRect();
if ( cr.isEmpty() )
using Q = QskPopup;
const auto factor = popup->fadingFactor();
if ( factor <= 0.0 )
return nullptr;
auto rootNode = QskSGNode::ensureNode< RootNode >( node );
const auto rect = popup->subControlRect( Q::Overlay );
if ( rect.isEmpty() )
return nullptr;
const auto faderProgress = popup->metric( popup->faderAspect() );
if ( faderProgress > 0.0 && faderProgress <= 1.0 )
{
auto clipRect = QRectF( popup->mapFromScene( QPointF() ), popup->window()->size() );
clipRect.setTop( cr.top() );
auto gradient = popup->gradientHint( Q::Overlay );
rootNode->setClipRect( clipRect );
}
else
if ( gradient.isVisible() && factor != 1.0 )
{
rootNode->resetClip();
auto stops = gradient.stops();
for ( auto& stop : stops )
stop.setRgb( qskInterpolatedRgb( stop.rgb(), factor ) );
gradient.setStops( stops );
}
rootNode->setTranslation( 0.0, -faderProgress * cr.height() );
auto contentsNode = updateContentsNode( popup, rootNode->contentsNode() );
rootNode->setContentsNode( contentsNode );
rootNode->rearrangeNodes();
return rootNode;
}
QSGNode* QskPopupSkinlet::updateContentsNode( const QskPopup*, QSGNode* ) const
{
return nullptr;
return updateBoxNode( popup, node, rect, gradient, QskPopup::Overlay );
}
#include "moc_QskPopupSkinlet.cpp"

View File

@ -20,8 +20,6 @@ class QSK_EXPORT QskPopupSkinlet : public QskSkinlet
enum NodeRole
{
OverlayRole,
ContentsRole,
RoleCount
};
@ -35,10 +33,7 @@ class QSK_EXPORT QskPopupSkinlet : public QskSkinlet
QSGNode* updateSubNode( const QskSkinnable*,
quint8 nodeRole, QSGNode* ) const override;
virtual QSGNode* updateContentsNode( const QskPopup*, QSGNode* ) const;
private:
QSGNode* updateExtraNode( const QskPopup*, QSGNode* ) const;
QSGNode* updateOverlayNode( const QskPopup*, QSGNode* ) const;
};

View File

@ -47,6 +47,21 @@ bool qskIsItemComplete( const QQuickItem* item )
return QQuickItemPrivate::get( item )->componentComplete;
}
bool qskIsItemInDestructor( const QQuickItem* item )
{
auto d = QQuickItemPrivate::get( item );
#if QT_VERSION >= QT_VERSION_CHECK( 6, 5, 0 )
return d->inDestructor;
#else
/*
QskQuickItem sets componentComplete to false in its destructor,
but for other items we will will return the wrong information
*/
return !d->componentComplete;
#endif
}
bool qskIsAncestorOf( const QQuickItem* item, const QQuickItem* child )
{
if ( item == nullptr || child == nullptr )
@ -356,7 +371,7 @@ QList< QQuickItem* > qskPaintOrderChildItems( const QQuickItem* item )
return QList< QQuickItem* >();
}
const QSGNode* qskItemNode( const QQuickItem* item )
const QSGTransformNode* qskItemNode( const QQuickItem* item )
{
if ( item == nullptr )
return nullptr;

View File

@ -16,6 +16,7 @@ class QskSizePolicy;
class QQuickItem;
class QSGNode;
class QSGTransformNode;
class QRectF;
template< typename T > class QList;
@ -24,6 +25,7 @@ template< typename T > class QList;
of QQuickItem.
*/
QSK_EXPORT bool qskIsItemInDestructor( const QQuickItem* );
QSK_EXPORT bool qskIsItemComplete( const QQuickItem* );
QSK_EXPORT bool qskIsAncestorOf( const QQuickItem* item, const QQuickItem* child );
QSK_EXPORT bool qskIsTabFence( const QQuickItem* );
@ -66,7 +68,7 @@ QSK_EXPORT QList< QQuickItem* > qskPaintOrderChildItems( const QQuickItem* );
QSK_EXPORT void qskUpdateInputMethod( const QQuickItem*, Qt::InputMethodQueries );
QSK_EXPORT void qskInputMethodSetVisible( const QQuickItem*, bool );
QSK_EXPORT const QSGNode* qskItemNode( const QQuickItem* );
QSK_EXPORT const QSGTransformNode* qskItemNode( const QQuickItem* );
QSK_EXPORT const QSGNode* qskPaintNode( const QQuickItem* );
QSK_EXPORT void qskItemUpdateRecursive( QQuickItem* );

View File

@ -180,6 +180,11 @@ QskQuickItem::~QskQuickItem()
We set componentComplete to false, so that operations
that are triggered by detaching the item from its parent
can be aware of the about-to-delete state.
Note, that since Qt >= 6.5 this information is stored
in QQuickItemPrivate::inDestructor.
s.a: qskIsItemInDestructor
*/
d_func()->componentComplete = false;

View File

@ -136,15 +136,15 @@ namespace
setOrientations( Qt::Horizontal | Qt::Vertical );
}
QRectF gestureRect() const override
bool isAcceptedPos( const QPointF& pos ) const override
{
if ( auto scrollBox = qobject_cast< const QskScrollBox* >( watchedItem() ) )
{
if ( qskIsScrollable( scrollBox, orientations() ) )
return scrollBox->viewContentsRect();
return scrollBox->viewContentsRect().contains( pos );
}
return QRectF( 0.0, 0.0, -1.0, -1.0 ); // empty
return false;
}
};
}

View File

@ -32,6 +32,9 @@
#include "QskComboBox.h"
#include "QskComboBoxSkinlet.h"
#include "QskDrawer.h"
#include "QskDrawerSkinlet.h"
#include "QskFocusIndicator.h"
#include "QskFocusIndicatorSkinlet.h"
@ -160,6 +163,7 @@ QskSkin::QskSkin( QObject* parent )
declareSkinlet< QskBox, QskBoxSkinlet >();
declareSkinlet< QskCheckBox, QskCheckBoxSkinlet >();
declareSkinlet< QskComboBox, QskComboBoxSkinlet >();
declareSkinlet< QskDrawer, QskDrawerSkinlet >();
declareSkinlet< QskFocusIndicator, QskFocusIndicatorSkinlet >();
declareSkinlet< QskGraphicLabel, QskGraphicLabelSkinlet >();
declareSkinlet< QskListView, QskListViewSkinlet >();

View File

@ -121,7 +121,7 @@ namespace
scheme = Qt::ColorScheme::Unknown;
}
const auto systemScheme = qGuiApp->styleHints()->colorScheme();
const auto systemScheme = QGuiApplication::styleHints()->colorScheme();
if( scheme == systemScheme )
{

View File

@ -988,6 +988,23 @@ bool QskSkinnable::moveSkinHint( QskAspect aspect, const QVariant& value )
return moveSkinHint( aspect, effectiveSkinHint( aspect ), value );
}
const QskHintAnimator* QskSkinnable::runningHintAnimator(
QskAspect aspect, int index ) const
{
const auto& animators = m_data->animators;
if ( animators.isEmpty() )
return nullptr;
aspect = qskAnimatorAspect( aspect );
auto animator = animators.animator( aspect, index );
if ( animator == nullptr && index >= 0 )
animator = animators.animator( aspect, -1 );
return animator;
}
QVariant QskSkinnable::animatedHint(
QskAspect aspect, QskSkinHintStatus* status ) const
{
@ -1259,13 +1276,19 @@ bool QskSkinnable::isTransitionAccepted( QskAspect aspect ) const
{
Q_UNUSED( aspect )
/*
Usually we only need smooth transitions, when state changes
happen while the skinnable is visible. There are few exceptions
like QskPopup::Closed, that is used to slide/fade in.
*/
if ( auto control = qskControlCast( owningItem() ) )
return control->isInitiallyPainted();
{
/*
Usually we only need smooth transitions, when state changes
happen while the skinnable is visible. There are few exceptions
like QskPopup::Closed, that is used to slide/fade in.
*/
if ( control->flags() & QQuickItem::ItemHasContents )
return control->isInitiallyPainted();
return true;
}
return false;
}

View File

@ -25,6 +25,7 @@ class QQuickItem;
class QskArcMetrics;
class QskControl;
class QskAnimationHint;
class QskHintAnimator;
class QskColorFilter;
class QskBoxShapeMetrics;
class QskBoxBorderMetrics;
@ -262,6 +263,8 @@ class QSK_EXPORT QskSkinnable
bool startHintTransitions( const QVector< QskAspect::Subcontrol >&,
QskAspect::States, QskAspect::States, int index = -1 );
const QskHintAnimator* runningHintAnimator( QskAspect, int index = -1 ) const;
protected:
virtual void updateNode( QSGNode* );
virtual bool isTransitionAccepted( QskAspect ) const;

View File

@ -253,4 +253,15 @@ void QskSubWindow::itemChange( QQuickItem::ItemChange change,
}
}
void QskSubWindow::updateResources()
{
setOpacity( fadingFactor() );
Inherited::updateResources();
}
QskAspect QskSubWindow::fadingAspect() const
{
return QskSubWindow::Panel | QskAspect::Position;
}
#include "moc_QskSubWindow.cpp"

View File

@ -83,6 +83,7 @@ class QSK_EXPORT QskSubWindow : public QskPopup
QRectF titleBarRect() const;
QRectF layoutRectForSize( const QSizeF& ) const override;
QskAspect fadingAspect() const override;
Q_SIGNALS:
void decorationsChanged( Decorations );
@ -95,6 +96,8 @@ class QSK_EXPORT QskSubWindow : public QskPopup
bool event( QEvent* ) override;
void updateLayout() override;
void updateResources() override;
QSizeF layoutSizeHint( Qt::SizeHint, const QSizeF& ) const override;
void itemChange( QQuickItem::ItemChange,

View File

@ -94,6 +94,18 @@ QSGNode* QskSubWindowSkinlet::updateSubNode(
return nullptr;
}
case OverlayRole:
{
/*
Overloading QskPopupSkinlet: as the opacity of the subwindow already
depends on the fadingFactor we do not want the additional opacity
adjustments for the overlay node.
Maybe we should have a flag that indicates if the popup does
opacity or geometry transitions, when fading TODO ...
*/
updateBoxNode( subWindow, node, Q::Overlay );
break;
}
}
return Inherited::updateSubNode( skinnable, nodeRole, node );

View File

@ -424,8 +424,18 @@ void QskWindow::exposeEvent( QExposeEvent* event )
void QskWindow::resizeEvent( QResizeEvent* event )
{
auto rootItem = contentItem();
const auto oldRect = qskItemGeometry( rootItem );
Inherited::resizeEvent( event );
const auto newRect = qskItemGeometry( rootItem );
if ( newRect != oldRect )
{
QskGeometryChangeEvent event( newRect, oldRect );
QCoreApplication::sendEvent( rootItem, &event );
}
if ( isExposed() )
layoutItems();
}

View File

@ -77,6 +77,7 @@ static void qskSetupSubWindow(
const QString& title, QskDialog::Actions actions,
QskDialog::Action defaultAction, QskDialogSubWindow* subWindow )
{
subWindow->setPopupFlag( QskPopup::DeleteOnClose );
subWindow->setModal( true );
subWindow->setWindowTitle( title );
subWindow->setDialogActions( actions );
@ -128,14 +129,14 @@ static QskDialog::Action qskMessageSubWindow(
const QString& text, int symbolType, QskDialog::Actions actions,
QskDialog::Action defaultAction )
{
QskMessageSubWindow subWindow( window->contentItem() );
subWindow.setSymbolType( symbolType );
subWindow.setText( text );
auto subWindow = new QskMessageSubWindow( window->contentItem() );
subWindow->setSymbolType( symbolType );
subWindow->setText( text );
qskSetupSubWindow( title, actions, defaultAction, &subWindow );
( void ) subWindow.exec();
qskSetupSubWindow( title, actions, defaultAction, subWindow );
( void ) subWindow->exec();
auto clickedAction = subWindow.clickedAction();
auto clickedAction = subWindow->clickedAction();
if ( clickedAction == QskDialog::NoAction )
{
// dialog might have been closed by the window menu
@ -172,16 +173,16 @@ static QString qskSelectSubWindow(
QskDialog::Actions actions, QskDialog::Action defaultAction,
const QStringList& entries, int selectedRow )
{
QskSelectionSubWindow subWindow( window->contentItem() );
subWindow.setInfoText( text );
subWindow.setEntries( entries );
subWindow.setSelectedRow( selectedRow );
auto subWindow = new QskSelectionSubWindow( window->contentItem() );
subWindow->setInfoText( text );
subWindow->setEntries( entries );
subWindow->setSelectedRow( selectedRow );
QString selectedEntry;
qskSetupSubWindow( title, actions, defaultAction, &subWindow );
if ( subWindow.exec() == QskDialog::Accepted )
selectedEntry = subWindow.selectedEntry();
qskSetupSubWindow( title, actions, defaultAction, subWindow );
if ( subWindow->exec() == QskDialog::Accepted )
selectedEntry = subWindow->selectedEntry();
return selectedEntry;
}

View File

@ -278,15 +278,10 @@ void QskDialogSubWindow::keyPressEvent( QKeyEvent* event )
{
auto button = m_data->buttonBox->defaultButton();
if ( button && button->isEnabled() )
{
button->click();
}
if ( qskIsStandardKeyInput( event, QKeySequence::Cancel ) )
{
// using shortcuts instead ???
reject();
return;
return;
}
}
Inherited::keyPressEvent( event );