2017-07-21 18:21:34 +02:00
|
|
|
/******************************************************************************
|
|
|
|
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
|
|
|
* This file may be used under the terms of the QSkinny License, Version 1.0
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
#include "QskAbstractButton.h"
|
|
|
|
#include "QskAspect.h"
|
2018-05-09 08:20:32 +02:00
|
|
|
#include "QskEvent.h"
|
|
|
|
#include "QskQuick.h"
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2018-07-19 14:10:48 +02:00
|
|
|
#include <qbasictimer.h>
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
QSK_STATE( QskAbstractButton, Flat, QskAspect::FirstSystemState << 1 )
|
|
|
|
QSK_STATE( QskAbstractButton, Checked, QskAspect::LastSystemState >> 4 )
|
|
|
|
QSK_STATE( QskAbstractButton, Pressed, QskAspect::LastSystemState >> 3 )
|
|
|
|
QSK_STATE( QskAbstractButton, Checkable, QskAspect::LastSystemState >> 2 )
|
|
|
|
|
|
|
|
static QskAbstractButton* qskCheckedSibling( const QskAbstractButton* button )
|
|
|
|
{
|
|
|
|
QQuickItem* parentItem = button->parentItem();
|
|
|
|
if ( parentItem == nullptr )
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
const auto siblings = parentItem->childItems();
|
|
|
|
for ( QQuickItem* sibling : siblings )
|
|
|
|
{
|
|
|
|
if ( QskAbstractButton* btn = qobject_cast< QskAbstractButton* >( sibling ) )
|
|
|
|
{
|
|
|
|
if ( btn != button && btn->exclusive() && btn->isChecked() )
|
|
|
|
return btn;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
class QskAbstractButton::PrivateData
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
PrivateData():
|
|
|
|
autoRepeatDelay( 300 ),
|
|
|
|
autoRepeatInterval( 100 ),
|
|
|
|
exclusive( false ),
|
|
|
|
autoRepeat( false )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
QBasicTimer repeatTimer;
|
|
|
|
|
|
|
|
int autoRepeatDelay; // milliseconds
|
|
|
|
int autoRepeatInterval; // milliseconds
|
|
|
|
|
|
|
|
bool exclusive : 1;
|
|
|
|
bool autoRepeat : 1;
|
|
|
|
};
|
|
|
|
|
|
|
|
QskAbstractButton::QskAbstractButton( QQuickItem* parent ):
|
|
|
|
Inherited( parent ),
|
|
|
|
m_data( new PrivateData() )
|
|
|
|
{
|
2017-10-24 19:32:54 +02:00
|
|
|
setFocusPolicy( Qt::StrongFocus );
|
2017-07-21 18:21:34 +02:00
|
|
|
setAcceptedMouseButtons( Qt::LeftButton );
|
|
|
|
setAcceptHoverEvents( true );
|
|
|
|
}
|
|
|
|
|
|
|
|
QskAbstractButton::~QskAbstractButton()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskAbstractButton::click()
|
|
|
|
{
|
|
|
|
setPressed( true );
|
|
|
|
releaseButton();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskAbstractButton::releaseButton()
|
|
|
|
{
|
|
|
|
if ( !isPressed() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ( skinState() & Checkable )
|
|
|
|
{
|
|
|
|
// we will have toggled before released,
|
|
|
|
// maybe there is more work to have the signals coming
|
|
|
|
// in a logical order. TODO ...
|
|
|
|
|
2018-07-26 12:24:27 +02:00
|
|
|
setCheckedState( !( skinState() & Checked ) );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
setPressed( false );
|
|
|
|
Q_EMIT clicked();
|
|
|
|
}
|
|
|
|
|
2018-07-26 12:24:27 +02:00
|
|
|
void QskAbstractButton::setCheckedState( bool on )
|
|
|
|
{
|
|
|
|
setChecked( on );
|
|
|
|
}
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
void QskAbstractButton::toggle()
|
|
|
|
{
|
|
|
|
setChecked( !( skinState() & Checked ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QskAbstractButton::isPressed() const
|
|
|
|
{
|
|
|
|
return skinState() & Pressed;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskAbstractButton::setPressed( bool on )
|
|
|
|
{
|
|
|
|
if ( on == isPressed() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
setSkinStateFlag( Pressed, on );
|
2018-07-26 12:24:27 +02:00
|
|
|
Q_EMIT pressedChanged( on );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
if ( on )
|
|
|
|
Q_EMIT pressed();
|
|
|
|
else
|
|
|
|
Q_EMIT released();
|
|
|
|
|
|
|
|
if ( m_data->autoRepeat )
|
|
|
|
{
|
|
|
|
if ( on )
|
|
|
|
{
|
|
|
|
m_data->repeatTimer.start( m_data->autoRepeatDelay, this );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_data->repeatTimer.stop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskAbstractButton::setCheckable( bool on )
|
|
|
|
{
|
|
|
|
if ( on == isCheckable() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
setSkinStateFlag( Checkable, on );
|
2018-07-26 12:24:27 +02:00
|
|
|
Q_EMIT checkableChanged( on );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool QskAbstractButton::isCheckable() const
|
|
|
|
{
|
|
|
|
return skinState() & Checkable;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskAbstractButton::setChecked( bool on )
|
|
|
|
{
|
|
|
|
if ( on == isChecked() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ( m_data->exclusive && !on )
|
|
|
|
{
|
|
|
|
// an exclusive button can't be unchecked
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto checkedButton = qskCheckedSibling( this );
|
|
|
|
if ( checkedButton )
|
|
|
|
{
|
|
|
|
// we enter temporary state, where no buttons are selected,
|
|
|
|
// better delay the notifications
|
|
|
|
|
|
|
|
checkedButton->setSkinStateFlag( Checked, false );
|
|
|
|
}
|
|
|
|
|
|
|
|
setSkinStateFlag( Checked, on );
|
2018-07-26 12:24:27 +02:00
|
|
|
Q_EMIT checkedChanged( on );
|
2017-07-21 18:21:34 +02:00
|
|
|
Q_EMIT toggled( on );
|
|
|
|
|
|
|
|
if ( checkedButton )
|
|
|
|
{
|
2018-07-26 12:24:27 +02:00
|
|
|
Q_EMIT checkedButton->checkedChanged( false );
|
2017-07-21 18:21:34 +02:00
|
|
|
Q_EMIT checkedButton->toggled( false );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QskAbstractButton::isChecked() const
|
|
|
|
{
|
|
|
|
return skinState() & Checked;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskAbstractButton::setAutoRepeat( bool on )
|
|
|
|
{
|
|
|
|
if ( on != m_data->autoRepeat )
|
|
|
|
{
|
|
|
|
m_data->autoRepeat = on;
|
|
|
|
|
|
|
|
if ( m_data->autoRepeat && isPressed() )
|
|
|
|
m_data->repeatTimer.start( m_data->autoRepeatDelay, this );
|
|
|
|
else
|
|
|
|
m_data->repeatTimer.stop();
|
|
|
|
|
2018-07-26 12:24:27 +02:00
|
|
|
Q_EMIT autoRepeatChanged( on );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QskAbstractButton::autoRepeat() const
|
|
|
|
{
|
|
|
|
return m_data->autoRepeat;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskAbstractButton::setAutoRepeatDelay( int ms )
|
|
|
|
{
|
|
|
|
if ( ms != m_data->autoRepeatDelay )
|
|
|
|
{
|
|
|
|
m_data->autoRepeatDelay = ms;
|
|
|
|
Q_EMIT autoRepeatDelayChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int QskAbstractButton::autoRepeatDelay() const
|
|
|
|
{
|
|
|
|
return m_data->autoRepeatDelay;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskAbstractButton::setAutoRepeatInterval( int ms )
|
|
|
|
{
|
|
|
|
if ( ms != m_data->autoRepeatInterval )
|
|
|
|
{
|
|
|
|
m_data->autoRepeatInterval = ms;
|
|
|
|
Q_EMIT autoRepeatIntervalChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int QskAbstractButton::autoRepeatInterval() const
|
|
|
|
{
|
|
|
|
return m_data->autoRepeatInterval;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskAbstractButton::setExclusive( bool on )
|
|
|
|
{
|
|
|
|
if ( on != m_data->exclusive )
|
|
|
|
{
|
|
|
|
m_data->exclusive = on;
|
2018-07-26 12:24:27 +02:00
|
|
|
Q_EMIT exclusiveChanged( on );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QskAbstractButton::exclusive() const
|
|
|
|
{
|
|
|
|
return m_data->exclusive;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QskAbstractButton::event( QEvent* event )
|
|
|
|
{
|
2018-05-09 08:20:32 +02:00
|
|
|
const auto eventType = static_cast< int >( event->type() );
|
|
|
|
switch( eventType )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-05-09 08:20:32 +02:00
|
|
|
case QEvent::Shortcut:
|
|
|
|
{
|
|
|
|
// TODO
|
|
|
|
// setFocus( true, Qt::ShortcutFocusReason );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case QskEvent::WindowChange:
|
|
|
|
{
|
|
|
|
if ( qskIsItemComplete( this ) && isPressed() )
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
When the window change happens on pressed() we won't get
|
|
|
|
the corrsponding release.
|
|
|
|
*/
|
|
|
|
setPressed( false );
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return Inherited::event( event );
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskAbstractButton::keyPressEvent( QKeyEvent* event )
|
|
|
|
{
|
2018-04-19 15:27:45 +02:00
|
|
|
switch ( event->key() )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-04-19 15:27:45 +02:00
|
|
|
case Qt::Key_Select:
|
|
|
|
case Qt::Key_Space:
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-04-19 15:27:45 +02:00
|
|
|
if ( !event->isAutoRepeat() )
|
2017-07-21 18:21:34 +02:00
|
|
|
setPressed( true );
|
2018-04-19 15:27:45 +02:00
|
|
|
|
|
|
|
// always accepting
|
|
|
|
return;
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Inherited::keyPressEvent( event );
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskAbstractButton::keyReleaseEvent( QKeyEvent* event )
|
|
|
|
{
|
|
|
|
if ( !event->isAutoRepeat() )
|
|
|
|
{
|
|
|
|
switch ( event->key() )
|
|
|
|
{
|
|
|
|
case Qt::Key_Select:
|
|
|
|
case Qt::Key_Space:
|
|
|
|
{
|
|
|
|
releaseButton();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Inherited::keyReleaseEvent( event );
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskAbstractButton::mouseMoveEvent( QMouseEvent* event )
|
|
|
|
{
|
|
|
|
if ( isPressed() )
|
|
|
|
{
|
|
|
|
if ( !contains( event->pos() ) )
|
|
|
|
{
|
|
|
|
setPressed( false );
|
|
|
|
Q_EMIT canceled();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
event->accept();
|
|
|
|
}
|
|
|
|
|
2017-10-24 19:32:54 +02:00
|
|
|
void QskAbstractButton::mousePressEvent( QMouseEvent* )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2017-10-24 19:32:54 +02:00
|
|
|
// QGuiApplication::styleHints()->mousePressAndHoldInterval() ???
|
2017-07-21 18:21:34 +02:00
|
|
|
setPressed( true );
|
|
|
|
}
|
|
|
|
|
2017-10-24 19:32:54 +02:00
|
|
|
void QskAbstractButton::mouseReleaseEvent( QMouseEvent* )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
releaseButton();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskAbstractButton::mouseUngrabEvent()
|
|
|
|
{
|
|
|
|
if ( isPressed() )
|
|
|
|
{
|
|
|
|
setPressed( false );
|
|
|
|
Q_EMIT canceled();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskAbstractButton::focusInEvent( QFocusEvent* event )
|
|
|
|
{
|
|
|
|
Inherited::focusInEvent( event );
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskAbstractButton::focusOutEvent( QFocusEvent* event )
|
|
|
|
{
|
|
|
|
if ( isPressed() )
|
|
|
|
{
|
|
|
|
setPressed( false );
|
|
|
|
Q_EMIT canceled();
|
|
|
|
}
|
|
|
|
|
|
|
|
Inherited::focusOutEvent( event );
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskAbstractButton::timerEvent( QTimerEvent* event )
|
|
|
|
{
|
|
|
|
if ( event->timerId() == m_data->repeatTimer.timerId() )
|
|
|
|
{
|
|
|
|
if ( isPressed() )
|
|
|
|
{
|
|
|
|
Q_EMIT released();
|
|
|
|
Q_EMIT clicked();
|
|
|
|
Q_EMIT pressed();
|
|
|
|
|
|
|
|
m_data->repeatTimer.start( m_data->autoRepeatInterval, this );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_data->repeatTimer.stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Inherited::timerEvent( event );
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "moc_QskAbstractButton.cpp"
|