Merge branch 'peter-ha-qsktextinput'

This commit is contained in:
Uwe 2018-03-28 15:52:26 +02:00
commit 63795d21b0
16 changed files with 513 additions and 813 deletions

View File

@ -37,26 +37,26 @@ static QString qskKeyString( int code )
class QskInputCompositionModel::PrivateData
{
public:
PrivateData() :
inputItem( nullptr ),
groupIndex( 0 )
{
}
// QInputMethod
QString preedit;
QTextCharFormat preeditFormat;
QList< QInputMethodEvent::Attribute > preeditAttributes;
QObject* inputItem;
int groupIndex;
};
static inline void sendCompositionEvent( QInputMethodEvent* e )
{
if ( auto focusObject = QGuiApplication::focusObject() )
QCoreApplication::sendEvent( focusObject, e );
}
QskInputCompositionModel::QskInputCompositionModel():
m_data( new PrivateData )
{
m_data->groupIndex = 0;
m_data->preeditFormat.setFontUnderline(true);
m_data->preeditFormat.setFontUnderline( true );
m_data->preeditAttributes.append( QInputMethodEvent::Attribute(
QInputMethodEvent::TextFormat, 0, 0, m_data->preeditFormat ) );
}
@ -71,13 +71,12 @@ void QskInputCompositionModel::composeKey( Qt::Key key )
if ( !inputMethod )
return;
auto focusObject = QGuiApplication::focusObject();
if ( !focusObject )
if ( !m_data->inputItem )
return;
QInputMethodQueryEvent queryEvent(
Qt::ImSurroundingText | Qt::ImMaximumTextLength | Qt::ImHints );
QCoreApplication::sendEvent( focusObject, &queryEvent );
QCoreApplication::sendEvent( m_data->inputItem, &queryEvent );
const auto hints = static_cast< Qt::InputMethodHints >(
queryEvent.value( Qt::ImHints ).toInt() );
const int maxLength = queryEvent.value( Qt::ImMaximumTextLength ).toInt();
@ -224,8 +223,7 @@ void QskInputCompositionModel::commitCandidate( int index )
void QskInputCompositionModel::backspace()
{
auto focusWindow = QGuiApplication::focusWindow();
if ( !focusWindow )
if ( !m_data->inputItem )
return;
if ( !m_data->preedit.isEmpty() )
@ -237,8 +235,8 @@ void QskInputCompositionModel::backspace()
// Backspace one character only if preedit was inactive
QKeyEvent keyPress( QEvent::KeyPress, Qt::Key_Backspace, Qt::NoModifier );
QKeyEvent keyRelease( QEvent::KeyRelease, Qt::Key_Backspace, Qt::NoModifier );
QCoreApplication::sendEvent( focusWindow, &keyPress );
QCoreApplication::sendEvent( focusWindow, &keyRelease );
QCoreApplication::sendEvent( m_data->inputItem, &keyPress );
QCoreApplication::sendEvent( m_data->inputItem, &keyRelease );
return;
}
@ -253,18 +251,28 @@ void QskInputCompositionModel::moveCursor( Qt::Key key )
if ( key != Qt::Key_Left && key != Qt::Key_Right )
return;
auto focusWindow = QGuiApplication::focusWindow();
if ( !focusWindow )
if ( !m_data->inputItem )
return;
// Moving cursor is disabled when preedit is active.
if ( !m_data->preedit.isEmpty() )
return;
// ### this should be in the panel:
QKeyEvent moveCursorPress( QEvent::KeyPress, key, Qt::NoModifier );
QKeyEvent moveCursorRelease( QEvent::KeyRelease, key, Qt::NoModifier );
QCoreApplication::sendEvent( focusWindow, &moveCursorPress );
QCoreApplication::sendEvent( focusWindow, &moveCursorRelease );
#if 1
QFocusEvent focusIn( QEvent::FocusIn ); // hack to display the cursor
#endif
QCoreApplication::sendEvent( m_data->inputItem, &focusIn );
QCoreApplication::sendEvent( m_data->inputItem, &moveCursorPress );
QCoreApplication::sendEvent( m_data->inputItem, &moveCursorRelease );
}
void QskInputCompositionModel::sendCompositionEvent( QInputMethodEvent* e )
{
if ( m_data->inputItem )
QCoreApplication::sendEvent( m_data->inputItem, e );
}
bool QskInputCompositionModel::hasIntermediate() const
@ -300,6 +308,11 @@ QVector< Qt::Key > QskInputCompositionModel::groups() const
return QVector< Qt::Key >();
}
void QskInputCompositionModel::setInputItem( QObject *inputItem )
{
m_data->inputItem = inputItem;
}
bool QskInputCompositionModel::nextGroupIndex(int& index, bool forward) const
{
Q_UNUSED(index);

View File

@ -10,6 +10,7 @@
#include <memory>
class QInputMethodEvent;
class QStringList;
class QskInputCompositionModel : public QObject
@ -37,6 +38,8 @@ public:
virtual QVector< Qt::Key > groups() const;
void setInputItem( QObject* inputItem );
protected:
// Used for text composition
virtual bool hasIntermediate() const;
@ -50,6 +53,7 @@ Q_SIGNALS:
private:
void backspace();
void moveCursor( Qt::Key key );
void sendCompositionEvent( QInputMethodEvent* e );
class PrivateData;
std::unique_ptr< PrivateData > m_data;

View File

@ -21,11 +21,15 @@
#include <QRectF>
QskInputContext::QskInputContext():
Inherited()
Inherited(),
m_inputCompositionModel( new QskInputCompositionModel )
{
connect( qskSetup, &QskSetup::inputPanelChanged,
this, &QskInputContext::setInputPanel );
setInputPanel( qskSetup->inputPanel() );
// We could connect candidatesChanged() here, but we don't emit
// the signal in the normal composition model anyhow
}
QskInputContext::~QskInputContext()
@ -41,11 +45,11 @@ bool QskInputContext::isValid() const
void QskInputContext::update( Qt::InputMethodQueries queries )
{
if ( !m_focusObject )
if ( !m_inputItem )
return;
QInputMethodQueryEvent queryEvent( queries );
if ( !QCoreApplication::sendEvent( m_focusObject, &queryEvent ) )
if ( !QCoreApplication::sendEvent( m_inputItem, &queryEvent ) )
return;
// Qt::ImCursorRectangle
@ -204,75 +208,44 @@ QLocale QskInputContext::locale() const
void QskInputContext::setFocusObject( QObject* focusObject )
{
if ( m_focusObject )
m_focusObject->removeEventFilter( this );
m_focusObject = focusObject;
if ( !m_focusObject )
return;
QInputMethodQueryEvent queryEvent( Qt::ImEnabled );
if ( !QCoreApplication::sendEvent( m_focusObject, &queryEvent ) )
return;
if ( !queryEvent.value( Qt::ImEnabled ).toBool() )
{
hideInputPanel();
m_inputItem = nullptr;
m_inputCompositionModel->setInputItem( nullptr );
return;
}
m_focusObject->installEventFilter( this );
bool inputItemChanged = false;
auto focusQuickItem = qobject_cast< QQuickItem* >( focusObject );
if( focusQuickItem )
{
// Do not change the input item when panel buttons get the focus:
if( qskNearestFocusScope( focusQuickItem ) != m_inputPanel )
{
m_inputItem = focusQuickItem;
m_inputCompositionModel->setInputItem( m_inputItem ); // ### use a signal/slot connection
inputItemChanged = true;
}
}
if( inputItemChanged )
{
QInputMethodQueryEvent queryEvent( Qt::ImEnabled );
if ( !QCoreApplication::sendEvent( m_inputItem, &queryEvent ) )
return;
if ( !queryEvent.value( Qt::ImEnabled ).toBool() )
{
hideInputPanel();
return;
}
}
update( Qt::InputMethodQuery( Qt::ImQueryAll & ~Qt::ImEnabled ) );
}
bool QskInputContext::eventFilter( QObject* object, QEvent* event )
{
if ( m_inputPanel && event->type() == QEvent::KeyPress )
{
auto keyEvent = static_cast< QKeyEvent* >( event );
const auto key = keyEvent->key();
switch ( key )
{
case Qt::Key_Tab:
case Qt::Key_Backtab:
if ( m_inputPanel->advanceFocus( key == Qt::Key_Tab ) )
{
keyEvent->accept();
return true;
}
break;
case Qt::Key_Select:
case Qt::Key_Space:
// if there is a focused key, treat the key like a push button
if ( m_inputPanel->activateFocusKey() )
{
keyEvent->accept();
return true;
}
break;
}
}
if ( m_inputPanel && event->type() == QEvent::KeyRelease )
{
auto keyEvent = static_cast< QKeyEvent* >( event );
const auto key = keyEvent->key();
switch ( key )
{
case Qt::Key_Select:
case Qt::Key_Space:
if ( m_inputPanel->deactivateFocusKey() )
{
keyEvent->accept();
return true;
}
break;
}
}
return Inherited::eventFilter( object, event );
}
void QskInputContext::invokeAction( QInputMethod::Action action, int cursorPosition )
{
Q_UNUSED(cursorPosition);

View File

@ -8,6 +8,7 @@
#include <qpa/qplatforminputcontext.h>
#include <QPointer>
#include <QQuickItem>
#include <memory>
@ -22,7 +23,7 @@ class QskInputContext : public QPlatformInputContext
public:
QskInputContext();
~QskInputContext();
~QskInputContext() override;
bool isValid() const override;
void update( Qt::InputMethodQueries ) override;
@ -35,9 +36,6 @@ public:
QLocale locale() const override;
void setFocusObject( QObject* ) override;
protected:
bool eventFilter( QObject*, QEvent* ) override;
private Q_SLOTS:
void emitAnimatingChanged();
void handleCandidatesChanged();
@ -45,6 +43,7 @@ private Q_SLOTS:
private:
QPointer< QObject > m_focusObject;
QPointer< QQuickItem > m_inputItem;
QPointer< QskInputPanel > m_inputPanel;
std::unique_ptr< QskInputCompositionModel > m_inputCompositionModel;
};

View File

@ -0,0 +1,17 @@
#include "TextInput.h"
TextInput::TextInput( QQuickItem* parent )
: QQuickTextInput( parent )
{
}
TextInput::~TextInput()
{
}
void TextInput::inputMethodEvent(QInputMethodEvent *event)
{
QQuickTextInput::inputMethodEvent(event);
}

View File

@ -0,0 +1,16 @@
#ifndef TEXTINPUT_H
#define TEXTINPUT_H
#include <private/qquicktextinput_p.h>
class TextInput : public QQuickTextInput
{
public:
TextInput( QQuickItem* parent );
virtual ~TextInput() override;
protected:
void inputMethodEvent(QInputMethodEvent *) Q_DECL_OVERRIDE;
};
#endif // TEXTINPUT_H

View File

@ -5,4 +5,8 @@ TARGET = inputpanel
DEFINES += PLUGIN_PATH=$$clean_path( $$QSK_OUT_ROOT/plugins )
SOURCES += \
main.cpp
main.cpp \
TextInput.cpp
HEADERS += \
TextInput.h

View File

@ -3,6 +3,8 @@
* This file may be used under the terms of the 3-clause BSD License
*****************************************************************************/
#include "TextInput.h"
#include <SkinnyFont.h>
#include <SkinnyShortcut.h>
@ -21,8 +23,6 @@
#include <QGuiApplication>
#include <QFontMetricsF>
#include <private/qquicktextinput_p.h>
#define STRINGIFY(x) #x
#define STRING(x) STRINGIFY(x)

View File

@ -521,31 +521,24 @@ void QskMaterialSkin::initTabViewHints()
void QskMaterialSkin::initInputPanelHints()
{
using namespace QskAspect;
using Q = QskInputPanel;
using Q = QskKeyButton;
const ColorPalette& pal = m_data->palette;
// key panel
setMargins( Q::KeyPanel | Margin, 2 );
setMargins( QskInputPanel::Panel | Margin, 2 );
setBoxShape( Q::KeyPanel, 20.0, Qt::RelativeSize );
setBoxBorderMetrics( Q::KeyPanel, 2 );
setBoxShape( Q::Panel, 20.0, Qt::RelativeSize );
setBoxBorderMetrics( Q::Panel, 2 );
setGradient( Q::KeyPanel, pal.darker125 );
setBoxBorderColors( Q::KeyPanel, pal.baseColor );
setGradient( Q::Panel, pal.darker125 );
setBoxBorderColors( Q::Panel, pal.baseColor );
for ( auto state : { NoState, Q::Focused } )
setBoxBorderColors( Q::KeyPanel | Q::Pressed | state, pal.accentColor );
setBoxBorderColors( Q::Panel | Q::Pressed | state, pal.accentColor );
setAnimation( Q::KeyPanel | Color, qskDuration );
setAnimation( Q::KeyPanel | Metric, qskDuration );
// glyph
setSkinHint( Q::KeyGlyph | Alignment, Qt::AlignCenter );
setFontRole( Q::KeyGlyph, QskSkin::TinyFont );
setColor( Q::KeyGlyph, pal.textColor );
setColor( Q::KeyGlyph | Q::Disabled, pal.darker200 );
setAnimation( Q::Panel | Color, qskDuration );
setAnimation( Q::Panel | Metric, qskDuration );
// panel
setBoxShape( Q::Panel, 0 );

View File

@ -554,36 +554,21 @@ void QskSquiekSkin::initTabViewHints()
void QskSquiekSkin::initInputPanelHints()
{
using namespace QskAspect;
using Q = QskInputPanel;
using Q = QskKeyButton;
const ColorPalette& pal = m_data->palette;
// key panel
setMargins( Q::KeyPanel | Margin, 2 ); // should be Panel | Spacing
setMargins( QskInputPanel::Panel | Padding, 5 );
setPanel( QskInputPanel::Panel, Raised );
setButton( Q::KeyPanel, Raised );
setButton( Q::KeyPanel | Q::Pressed, Sunken );
setButton( Q::Panel, Raised );
setButton( Q::Panel | Q::Pressed, Sunken );
setAnimation( Q::KeyPanel | Color, qskDuration );
#if 0
// crashes because animations are started from updateNode
// TODO ...
setAnimation( Q::Panel | Color, qskDuration );
setAnimation( Q::KeyPanel | Metric, qskDuration );
#endif
// glyph
setSkinHint( Q::KeyGlyph | Alignment, Qt::AlignCenter );
setFontRole( Q::KeyGlyph, QskSkin::TinyFont );
setColor( Q::KeyGlyph, pal.themeForeground );
setColor( Q::KeyGlyph | Q::Disabled, pal.darker200 );
// panel
setMargins( Q::Panel | Padding, 5 );
setMargins( Q::Panel | Spacing, 5 );
setPanel( Q::Panel, Raised );
setColor( Q::Text, pal.themeForeground );
setColor( Q::Text | Q::Disabled, pal.darker200 );
}
void QskSquiekSkin::initScrollViewHints()

View File

@ -7,21 +7,19 @@
#include "QskAspect.h"
#include <QskPushButton.h>
#include <QskTextLabel.h>
#include <QskDialog.h>
#include <QFocusEvent>
#include <QGuiApplication>
#include <QStyleHints>
#include <QskLinearBox.h>
#include <QskTextOptions.h>
#include <QTimer>
#include <cmath>
#include <unordered_map>
QSK_SUBCONTROL( QskInputPanel, Panel )
QSK_SUBCONTROL( QskInputPanel, KeyPanel )
QSK_SUBCONTROL( QskInputPanel, KeyGlyph )
QSK_STATE( QskInputPanel, Checked, QskAspect::LastSystemState >> 3 )
QSK_STATE( QskInputPanel, Pressed, QskAspect::LastSystemState >> 2 )
namespace
{
struct KeyTable
@ -34,60 +32,6 @@ namespace
return int( intptr_t( value - data[0] ) );
}
};
QskInputPanel::KeyData* qskKeyDataAt( KeyTable& table, qreal x, qreal y )
{
if ( x < 0 || x > 1 || y < 0 || y > 1 )
return nullptr;
auto rowIndex = size_t( std::floor( y * QskInputPanel::RowCount ) );
auto columnIndex = size_t( std::floor( x * QskInputPanel::KeyCount ) );
Q_FOREVER
{
const auto rect = table.data[ rowIndex ][ columnIndex ].rect;
if ( rect.contains( x, y ) )
return &table.data[ rowIndex ][ columnIndex ];
// Look up/down
if ( y < rect.top() )
{
if ( rowIndex == 0 )
break;
rowIndex -= 1;
continue;
}
else if ( y > rect.bottom() )
{
if ( rowIndex == QskInputPanel::RowCount - 1 )
break;
rowIndex += 1;
continue;
}
// Look left/right
if ( x < rect.left() )
{
if ( columnIndex == 0 )
break;
columnIndex -= 1;
continue;
}
else if ( x > rect.right() )
{
if ( columnIndex == QskInputPanel::KeyCount - 1 )
break;
columnIndex += 1;
continue;
}
}
return nullptr;
}
}
struct QskInputPanelLayouts
@ -130,20 +74,18 @@ struct QskInputPanelLayouts
#define LOWER(x) Qt::Key(x + 32) // Convert an uppercase key to lowercase
static constexpr const QskInputPanelLayouts qskInputPanelLayouts =
{
#include "QskInputPanelLayouts.cpp"
#include "QskInputPanelLayouts.cpp"
};
#undef LOWER
QSK_DECLARE_OPERATORS_FOR_FLAGS( Qt::Key ) // Must appear after the LOWER macro
static const Qt::Key KeyPressed = static_cast< Qt::Key >( Qt::ShiftModifier );
static const Qt::Key KeyLocked = static_cast< Qt::Key >( Qt::ControlModifier );
static const Qt::Key KeyFocused = static_cast< Qt::Key >( Qt::MetaModifier );
static const Qt::Key KeyStates = static_cast< Qt::Key >( Qt::KeyboardModifierMask );
static const int KeyLocked = static_cast< int >( Qt::ControlModifier );
static const int KeyStates = static_cast< int >( Qt::KeyboardModifierMask );
static qreal qskKeyStretch( Qt::Key key )
{
switch ( key )
switch( key )
{
case Qt::Key_Backspace:
case Qt::Key_Shift:
@ -168,20 +110,32 @@ static qreal qskRowStretch( const QskInputPanel::KeyRow& keyRow )
{
qreal stretch = 0;
for ( const auto& key : keyRow )
for( const auto& key : keyRow )
{
if ( !key )
if( !key )
{
continue;
}
stretch += qskKeyStretch( key );
}
if ( stretch == 0.0 )
if( stretch == 0.0 )
{
stretch = QskInputPanel::KeyCount;
}
return stretch;
}
static bool qskIsAutorepeat( int key )
{
return ( key != Qt::Key_Return && key != Qt::Key_Enter
&& key != Qt::Key_Shift && key!= Qt::Key_CapsLock
&& key != Qt::Key_Mode_switch );
}
namespace
{
struct KeyCounter
@ -191,57 +145,194 @@ namespace
};
}
class QskInputPanel::PrivateData
QSK_SUBCONTROL( QskInputPanel, Panel )
QSK_SUBCONTROL( QskKeyButton, Panel )
QSK_SUBCONTROL( QskKeyButton, Text )
QSK_SUBCONTROL( QskKeyButton, TextCancelButton )
QskKeyButton::QskKeyButton( int keyIndex, QskInputPanel* inputPanel, QQuickItem* parent ) :
Inherited( parent ),
m_keyIndex( keyIndex ),
m_inputPanel( inputPanel )
{
public:
PrivateData():
currentLayout( nullptr ),
mode( QskInputPanel::LowercaseMode ),
focusKeyIndex( -1 ),
selectedGroup( -1 ),
candidateOffset( 0 ),
repeatKeyTimerId( -1 )
QskTextOptions options;
options.setFontSizeMode( QskTextOptions::VerticalFit );
setTextOptions( options );
setFocusPolicy( Qt::TabFocus );
auto keyData = m_inputPanel->keyDataAt( m_keyIndex );
const auto key = keyData.key & ~KeyStates;
if ( qskIsAutorepeat( key ) )
{
setAutoRepeat( true );
setAutoRepeatDelay( 500 );
setAutoRepeatInterval( 1000 / QGuiApplication::styleHints()->keyboardAutoRepeatRate() );
}
public:
const QskInputPanelLayouts::Layout* currentLayout;
QskInputPanel::Mode mode;
updateText();
qint16 focusKeyIndex;
qint16 selectedGroup;
qint32 candidateOffset;
connect( this, &QskKeyButton::pressed, this, [ this ]()
{
m_inputPanel->handleKey( m_keyIndex );
} );
int repeatKeyTimerId;
connect( m_inputPanel, &QskInputPanel::modeChanged, this, &QskKeyButton::updateText );
}
QLocale locale;
QskAspect::Subcontrol QskKeyButton::effectiveSubcontrol( QskAspect::Subcontrol subControl ) const
{
if( subControl == QskPushButton::Panel )
{
return QskKeyButton::Panel;
}
QVector< Qt::Key > groups;
QVector< Qt::Key > candidates;
if( subControl == QskPushButton::Text )
{
// ### we could also introduce a state to not always query the button
return isCancelButton() ? QskKeyButton::TextCancelButton : QskKeyButton::Text;
}
std::unordered_map< int, KeyCounter > activeKeys;
KeyTable keyTable[ ModeCount ];
return subControl;
}
int QskKeyButton::keyIndex() const
{
return m_keyIndex;
}
void QskKeyButton::updateText()
{
QString text = m_inputPanel->currentTextForKeyIndex( m_keyIndex );
if( text.count() == 1 && text.at( 0 ) == QChar( 0 ) )
{
setVisible( false );
}
else
{
setVisible( true );
setText( text );
}
}
bool QskKeyButton::isCancelButton() const
{
auto keyData = m_inputPanel->keyDataAt( m_keyIndex );
bool isCancel = ( keyData.key == 0x2716 );
return isCancel;
}
class QskInputPanel::PrivateData
{
public:
PrivateData():
currentLayout( nullptr ),
mode( QskInputPanel::LowercaseMode ),
selectedGroup( -1 ),
candidateOffset( 0 ),
buttonsBox( nullptr ),
isUIInitialized( false )
{
}
public:
const QskInputPanelLayouts::Layout* currentLayout;
QskInputPanel::Mode mode;
qint16 selectedGroup;
qint32 candidateOffset;
QLocale locale;
QVector< Qt::Key > groups;
QVector< Qt::Key > candidates;
std::unordered_map< int, KeyCounter > activeKeys;
KeyTable keyTable[ ModeCount ];
QskLinearBox* buttonsBox;
QList< QskKeyButton* > keyButtons;
bool isUIInitialized;
};
QskInputPanel::QskInputPanel( QQuickItem* parent ):
QskControl( parent ),
Inherited( parent ),
m_data( new PrivateData )
{
qRegisterMetaType< Qt::Key >();
setFlag( ItemHasContents );
setAcceptedMouseButtons( Qt::LeftButton );
initSizePolicy( QskSizePolicy::Expanding, QskSizePolicy::Expanding );
setAutoFillBackground( true );
auto margins = marginsHint( Panel | QskAspect::Margin );
setMargins( margins );
updateLocale( locale() );
QObject::connect( this, &QskControl::localeChanged,
this, &QskInputPanel::updateLocale );
this, &QskInputPanel::updateLocale );
setFlag( ItemIsFocusScope, true );
#if 0
setTabFence( true );
#endif
setAutoLayoutChildren( true );
auto& panelKeyData = keyData();
m_data->buttonsBox = new QskLinearBox( Qt::Vertical, this );
m_data->buttonsBox->setAutoAddChildren( true );
for( const auto& keyRow : panelKeyData )
{
QskLinearBox* rowBox = new QskLinearBox( Qt::Horizontal, m_data->buttonsBox );
rowBox->setAutoAddChildren( true );
for( const auto& keyData : keyRow )
{
if( !keyData.key )
{
continue;
}
int keyIndex = m_data->keyTable[ m_data->mode ].indexOf( &keyData );
QskKeyButton* button = new QskKeyButton( keyIndex, this, rowBox );
rowBox->setRetainSizeWhenHidden( button, true );
m_data->keyButtons.append( button );
}
}
connect( this, &QskInputPanel::modeChanged, this, [ this ]() {
updateLayout();
});
}
QskInputPanel::~QskInputPanel()
{
}
QskAspect::Subcontrol QskInputPanel::effectiveSubcontrol( QskAspect::Subcontrol subControl ) const
{
if( subControl == QskBox::Panel )
{
return QskInputPanel::Panel;
}
return subControl;
}
QskInputPanel::Mode QskInputPanel::mode() const
{
return m_data->mode;
@ -254,12 +345,12 @@ const QskInputPanel::KeyDataSet& QskInputPanel::keyData( Mode mode ) const
return m_data->keyTable[ mode ].data;
}
QString QskInputPanel::textForKey( Qt::Key key ) const
QString QskInputPanel::textForKey( int key ) const
{
key &= ~KeyStates;
// Special cases
switch ( key )
switch( key )
{
case Qt::Key_Backspace:
case Qt::Key_Muhenkan:
@ -306,7 +397,7 @@ QString QskInputPanel::displayLanguageName() const
{
const auto locale = this->locale();
switch ( locale.language() )
switch( locale.language() )
{
case QLocale::Bulgarian:
return QStringLiteral( "български език" );
@ -324,19 +415,21 @@ QString QskInputPanel::displayLanguageName() const
return QStringLiteral( "Eλληνικά" );
case QLocale::English:
{
switch ( locale.country() )
{
case QLocale::Canada:
case QLocale::UnitedStates:
case QLocale::UnitedStatesMinorOutlyingIslands:
case QLocale::UnitedStatesVirginIslands:
return QStringLiteral( "English (US)" );
switch( locale.country() )
{
case QLocale::Canada:
case QLocale::UnitedStates:
case QLocale::UnitedStatesMinorOutlyingIslands:
case QLocale::UnitedStatesVirginIslands:
return QStringLiteral( "English (US)" );
default:
return QStringLiteral( "English (UK)" );
default:
return QStringLiteral( "English (UK)" );
}
break;
}
}
case QLocale::Spanish:
return QStringLiteral( "Español" );
@ -397,7 +490,7 @@ void QskInputPanel::setPreeditGroups( const QVector< Qt::Key >& groups )
{
auto& topRow = m_data->keyTable[ LowercaseMode ].data[ 0 ];
for ( const auto& group : groups )
for( const auto& group : groups )
{
auto& keyData = topRow[ &group - groups.data() ];
keyData.key = group;
@ -406,96 +499,23 @@ void QskInputPanel::setPreeditGroups( const QVector< Qt::Key >& groups )
m_data->groups = groups;
selectGroup( -1 );
if ( m_data->mode == LowercaseMode )
if( m_data->mode == LowercaseMode )
{
update();
}
}
void QskInputPanel::setPreeditCandidates( const QVector< Qt::Key >& candidates )
{
if ( m_data->candidates == candidates )
if( m_data->candidates == candidates )
{
return;
}
m_data->candidates = candidates;
setCandidateOffset( 0 );
}
bool QskInputPanel::advanceFocus( bool forward )
{
deactivateFocusKey();
auto offset = forward ? 1 : -1;
auto focusKeyIndex = m_data->focusKeyIndex;
Q_FOREVER
{
focusKeyIndex += offset;
if ( focusKeyIndex < 0 || focusKeyIndex >= RowCount * KeyCount )
{
clearFocusKey();
return false;
}
const auto key = keyDataAt( focusKeyIndex ).key;
if ( key && key != Qt::Key_unknown )
break;
}
if ( m_data->focusKeyIndex >= 0 )
keyDataAt( m_data->focusKeyIndex ).key &= ~KeyFocused;
m_data->focusKeyIndex = focusKeyIndex;
keyDataAt( m_data->focusKeyIndex ).key |= KeyFocused;
update();
return true;
}
bool QskInputPanel::activateFocusKey()
{
if ( m_data->focusKeyIndex > 0 && m_data->focusKeyIndex < RowCount * KeyCount )
{
auto& keyData = keyDataAt( m_data->focusKeyIndex );
if ( keyData.key & KeyPressed )
handleKey( m_data->focusKeyIndex );
else
keyData.key |= KeyPressed;
update();
return true;
}
return false;
}
bool QskInputPanel::deactivateFocusKey()
{
if ( m_data->focusKeyIndex > 0 && m_data->focusKeyIndex < RowCount * KeyCount )
{
auto& keyData = keyDataAt( m_data->focusKeyIndex );
if ( keyData.key & KeyPressed )
{
keyData.key &= ~KeyPressed;
handleKey( m_data->focusKeyIndex );
}
update();
return true;
}
return true;
}
void QskInputPanel::clearFocusKey()
{
if ( m_data->focusKeyIndex > 0 && m_data->focusKeyIndex < RowCount * KeyCount )
{
keyDataAt( m_data->focusKeyIndex ).key &= ~KeyFocused;
update();
}
m_data->focusKeyIndex = -1;
}
void QskInputPanel::setCandidateOffset( int candidateOffset )
{
m_data->candidateOffset = candidateOffset;
@ -508,202 +528,83 @@ void QskInputPanel::setCandidateOffset( int candidateOffset )
const bool continueLeft = m_data->candidateOffset > 0;
const bool continueRight = ( candidateCount - m_data->candidateOffset ) > count;
for ( int i = 0; i < count; ++i )
for( int i = 0; i < count; ++i )
{
auto& keyData = topRow[ i + groupCount ];
if ( continueLeft && i == 0 )
if( continueLeft && i == 0 )
{
keyData.key = Qt::Key_ApplicationLeft;
else if ( continueRight && ( i == KeyCount - groupCount - 1 ) )
}
else if( continueRight && ( i == KeyCount - groupCount - 1 ) )
{
keyData.key = Qt::Key_ApplicationRight;
}
else
{
keyData.isSuggestionKey = true; // ### reset when switching layouts etc.!
keyData.key = m_data->candidates.at( i + m_data->candidateOffset );
}
}
for ( int i = count; i < KeyCount - groupCount; ++i )
for( int i = count; i < KeyCount - groupCount; ++i )
{
auto& keyData = topRow[ i + groupCount ];
keyData.key = Qt::Key_unknown;
}
if ( m_data->mode == LowercaseMode )
update();
if( m_data->mode == LowercaseMode )
{
updateUI();
}
}
QRectF QskInputPanel::keyboardRect() const
void QskInputPanel::registerCompositionModelForLocale( const QLocale& locale,
QskInputCompositionModel* model )
{
auto keyboardRect = rect(); // ### margins? would eliminate this thing below
if ( QskDialog::instance()->policy() != QskDialog::TopLevelWindow )
keyboardRect.adjust( 0, keyboardRect.height() * 0.5, 0, 0 );
return keyboardRect;
Q_EMIT inputMethodRegistered( locale, model );
}
void QskInputPanel::geometryChanged(
const QRectF& newGeometry, const QRectF& oldGeometry )
{
Inherited::geometryChanged( newGeometry, oldGeometry );
Q_EMIT keyboardRectChanged();
}
void QskInputPanel::mousePressEvent( QMouseEvent* e )
void QskInputPanel::updateLayout()
{
if ( !keyboardRect().contains( e->pos() ) )
if( geometry().isNull() )
return; // no need to calculate anything, will be called again
QRectF rect = layoutRect();
qreal verticalSpacing = m_data->buttonsBox->spacing();
const auto& children = m_data->buttonsBox->childItems();
for( auto rowItem : children )
{
e->ignore();
return;
auto rowBox = qobject_cast< QskLinearBox* >( rowItem );
const qreal horizontalSpacing = rowBox->spacing();
const auto& rowChildren = rowBox->childItems();
for( auto keyItem : rowChildren )
{
auto button = qobject_cast< QskKeyButton* >( keyItem );
QRectF keyRect = keyDataAt( button->keyIndex() ).rect;
qreal width = keyRect.width() * rect.width() - horizontalSpacing;
qreal height = keyRect.height() * rect.height() - verticalSpacing;
button->setFixedSize( width, height );
}
}
QTouchEvent::TouchPoint touchPoint( 0 );
touchPoint.setPos( e->pos() );
touchPoint.setState( Qt::TouchPointPressed );
QTouchEvent touchEvent( QTouchEvent::TouchBegin, nullptr,
e->modifiers(), Qt::TouchPointPressed, { touchPoint } );
QCoreApplication::sendEvent( this, &touchEvent );
e->setAccepted( touchEvent.isAccepted() );
}
void QskInputPanel::mouseMoveEvent( QMouseEvent* e )
void QskInputPanel::updateUI()
{
QTouchEvent::TouchPoint touchPoint( 0 );
touchPoint.setPos( e->pos() );
touchPoint.setState( Qt::TouchPointMoved );
QTouchEvent touchEvent( QTouchEvent::TouchUpdate, nullptr,
e->modifiers(), Qt::TouchPointMoved, { touchPoint } );
QCoreApplication::sendEvent( this, &touchEvent );
e->setAccepted( touchEvent.isAccepted() );
}
void QskInputPanel::mouseReleaseEvent( QMouseEvent* e )
{
QTouchEvent::TouchPoint touchPoint( 0 );
touchPoint.setPos( e->pos() );
touchPoint.setState( Qt::TouchPointReleased );
QTouchEvent touchEvent( QTouchEvent::TouchEnd, nullptr,
e->modifiers(), Qt::TouchPointReleased, { touchPoint } );
QCoreApplication::sendEvent( this, &touchEvent );
e->setAccepted( touchEvent.isAccepted() );
}
// Try to handle touch-specific details here; once touch is resolved, send to handleKey()
void QskInputPanel::touchEvent( QTouchEvent* e )
{
if ( e->type() == QEvent::TouchCancel )
for( QskKeyButton* button : qskAsConst( m_data->keyButtons ) )
{
for ( auto& it : m_data->activeKeys )
keyDataAt( it.second.keyIndex ).key &= ~KeyPressed;
m_data->activeKeys.clear();
return;
}
const auto rect = keyboardRect();
for ( const auto& tp : e->touchPoints() )
{
const auto pos = tp.pos();
const auto x = ( pos.x() - rect.x() ) / rect.width();
const auto y = ( pos.y() - rect.y() ) / rect.height();
auto keyData = qskKeyDataAt( m_data->keyTable[ m_data->mode ], x, y );
if ( !keyData || ( !keyData->key || keyData->key == Qt::Key_unknown ) )
{
auto it = m_data->activeKeys.find( tp.id() );
if ( it == m_data->activeKeys.cend() )
continue;
keyDataAt( it->second.keyIndex ).key &= ~KeyPressed;
m_data->activeKeys.erase( it );
continue;
}
const auto keyIndex = m_data->keyTable[ m_data->mode ].indexOf( keyData );
auto it = m_data->activeKeys.find( tp.id() );
if ( tp.state() == Qt::TouchPointReleased )
{
const int repeatCount = it->second.count;
auto it = m_data->activeKeys.find( tp.id() );
keyDataAt( it->second.keyIndex ).key &= ~KeyPressed;
m_data->activeKeys.erase( it );
if ( repeatCount < 0 )
continue; // Don't compose an accepted held key
handleKey( keyIndex );
continue;
}
if ( it == m_data->activeKeys.end() )
{
m_data->activeKeys.emplace( tp.id(), KeyCounter { keyIndex, 0 } );
}
else
{
if ( it->second.keyIndex != keyIndex )
{
keyDataAt( it->second.keyIndex ).key &= ~KeyPressed;
it->second.count = 0;
}
it->second.keyIndex = keyIndex;
}
keyDataAt( keyIndex ).key |= KeyPressed;
}
// Now start an update timer based on active keys
if ( m_data->activeKeys.empty() && m_data->repeatKeyTimerId >= 0 )
{
killTimer( m_data->repeatKeyTimerId );
m_data->repeatKeyTimerId = -1;
}
else if ( m_data->repeatKeyTimerId < 0 )
{
m_data->repeatKeyTimerId = startTimer( 1000
/ QGuiApplication::styleHints()->keyboardAutoRepeatRate() );
} /* else timer is already running as it should be */
update();
}
void QskInputPanel::timerEvent( QTimerEvent* e )
{
if ( e->timerId() == m_data->repeatKeyTimerId )
{
for ( auto it = m_data->activeKeys.begin(); it != m_data->activeKeys.end(); )
{
if ( it->second.count >= 0 && it->second.count++ > 20 ) // ### use platform long-press hint
{
const auto key = keyDataAt( it->second.keyIndex ).key & ~KeyStates;
if ( !key || key == Qt::Key_unknown )
{
it = m_data->activeKeys.erase( it );
continue;
}
if ( key == Qt::Key_ApplicationLeft || key == Qt::Key_ApplicationRight )
{
setCandidateOffset( m_data->candidateOffset
+ ( key == Qt::Key_ApplicationLeft ? -1 : 1 ) );
}
else if ( !( key & KeyLocked ) ) // do not repeat locked keys
{
// long press events could be emitted from here
compose( key & ~KeyStates );
}
}
++it;
}
button->updateText();
}
}
@ -716,35 +617,43 @@ QskInputPanel::KeyData& QskInputPanel::keyDataAt( int keyIndex ) const
void QskInputPanel::handleKey( int keyIndex )
{
const auto key = keyDataAt( keyIndex ).key & ~KeyStates;
KeyData keyData = keyDataAt( keyIndex );
const auto key = keyData.key & ~KeyStates;
// Preedit keys
const auto row = keyIndex / KeyCount;
const auto column = keyIndex % KeyCount;
if ( m_data->mode == LowercaseMode && !m_data->groups.isEmpty() && row == 0 )
if( m_data->mode == LowercaseMode && !m_data->groups.isEmpty() && row == 0 )
{
if ( key == Qt::Key_ApplicationLeft
|| key == Qt::Key_ApplicationRight )
if( key == Qt::Key_ApplicationLeft
|| key == Qt::Key_ApplicationRight )
{
setCandidateOffset( m_data->candidateOffset
+ ( key == Qt::Key_ApplicationLeft ? -1 : 1 ) );
+ ( key == Qt::Key_ApplicationLeft ? -1 : 1 ) );
return;
}
const auto groupCount = m_data->groups.length();
if ( column < groupCount )
if( column < groupCount )
{
selectGroup( column );
else if ( column < KeyCount )
}
else if( column < KeyCount )
{
selectCandidate( column - groupCount + m_data->candidateOffset );
}
else
Q_UNREACHABLE(); // Handle the final key...
{
Q_UNREACHABLE(); // Handle the final key...
}
return;
}
// Mode-switching keys
switch ( key )
switch( key )
{
case Qt::Key_CapsLock:
case Qt::Key_Kana_Lock:
@ -758,19 +667,37 @@ void QskInputPanel::handleKey( int keyIndex )
case Qt::Key_Mode_switch: // Cycle through modes, but skip caps
setMode( static_cast< QskInputPanel::Mode >(
m_data->mode ? ( ( m_data->mode + 1 ) % QskInputPanel::ModeCount )
: SpecialCharacterMode ) );
m_data->mode ? ( ( m_data->mode + 1 ) % QskInputPanel::ModeCount )
: SpecialCharacterMode ) );
return;
// This is (one of) the cancel symbol, not Qt::Key_Cancel:
case Qt::Key( 10006 ):
Q_EMIT cancelPressed();
return;
default:
break;
}
// Normal keys
compose( key );
if( keyData.isSuggestionKey )
{
selectCandidate( keyIndex );
}
else
{
compose( key );
}
}
void QskInputPanel::compose( Qt::Key key )
QString QskInputPanel::currentTextForKeyIndex( int keyIndex ) const
{
auto keyData = keyDataAt( keyIndex );
QString text = textForKey( keyData.key );
return text;
}
void QskInputPanel::compose( int key )
{
QGuiApplication::inputMethod()->invokeAction(
static_cast< QInputMethod::Action >( Compose ), key );
@ -779,18 +706,26 @@ void QskInputPanel::compose( Qt::Key key )
void QskInputPanel::selectGroup( int index )
{
auto& topRow = m_data->keyTable[ m_data->mode ].data[ 0 ];
if ( m_data->selectedGroup >= 0 )
topRow[ m_data->selectedGroup ].key &= ~KeyLocked;
if ( m_data->selectedGroup == index )
index = -1; // clear selection
if( m_data->selectedGroup >= 0 )
{
auto group = static_cast< int >( m_data->selectedGroup );
topRow[ group ].key &= ~KeyLocked;
}
if( m_data->selectedGroup == index )
{
index = -1; // clear selection
}
m_data->selectedGroup = index;
QGuiApplication::inputMethod()->invokeAction(
static_cast< QInputMethod::Action >( SelectGroup ), m_data->selectedGroup + 1 );
if ( m_data->selectedGroup < 0 )
if( m_data->selectedGroup < 0 )
{
return;
}
topRow[ m_data->selectedGroup ].key |= KeyLocked;
}
@ -803,7 +738,7 @@ void QskInputPanel::selectCandidate( int index )
void QskInputPanel::updateLocale( const QLocale& locale )
{
switch ( locale.language() )
switch( locale.language() )
{
case QLocale::Bulgarian:
m_data->currentLayout = &qskInputPanelLayouts.bg;
@ -826,21 +761,24 @@ void QskInputPanel::updateLocale( const QLocale& locale )
break;
case QLocale::English:
{
switch ( locale.country() )
{
case QLocale::Canada:
case QLocale::UnitedStates:
case QLocale::UnitedStatesMinorOutlyingIslands:
case QLocale::UnitedStatesVirginIslands:
m_data->currentLayout = &qskInputPanelLayouts.en_US;
break;
default:
m_data->currentLayout = &qskInputPanelLayouts.en_GB;
break;
switch( locale.country() )
{
case QLocale::Canada:
case QLocale::UnitedStates:
case QLocale::UnitedStatesMinorOutlyingIslands:
case QLocale::UnitedStatesVirginIslands:
m_data->currentLayout = &qskInputPanelLayouts.en_US;
break;
default:
m_data->currentLayout = &qskInputPanelLayouts.en_GB;
break;
}
break;
}
break;
}
case QLocale::Spanish:
m_data->currentLayout = &qskInputPanelLayouts.es;
break;
@ -905,10 +843,6 @@ void QskInputPanel::updateLocale( const QLocale& locale )
m_data->currentLayout = &qskInputPanelLayouts.zh;
break;
case QLocale::C:
m_data->currentLayout = &qskInputPanelLayouts.en_US;
break;
default:
qWarning() << "QskInputPanel: unsupported locale:" << locale;
m_data->currentLayout = &qskInputPanelLayouts.en_US;
@ -925,12 +859,12 @@ void QskInputPanel::updateKeyData()
// Key data is in normalized coordinates
const auto keyHeight = 1.0f / RowCount;
for ( const auto& keyLayout : *m_data->currentLayout )
for( const auto& keyLayout : *m_data->currentLayout )
{
auto& keyDataLayout = m_data->keyTable[ &keyLayout - *m_data->currentLayout ];
qreal yPos = 0;
for ( int i = 0; i < RowCount; i++ )
for( int i = 0; i < RowCount; i++ )
{
auto& row = keyLayout.data[i];
auto& keyDataRow = keyDataLayout.data[ i ];
@ -940,7 +874,7 @@ void QskInputPanel::updateKeyData()
qreal xPos = 0;
qreal keyWidth = baseKeyWidth;
for ( const auto& key : row )
for( const auto& key : row )
{
auto& keyData = keyDataRow[ &key - row ];
keyData.key = key;
@ -958,7 +892,7 @@ void QskInputPanel::updateKeyData()
void QskInputPanel::setMode( QskInputPanel::Mode mode )
{
m_data->mode = mode;
update();
Q_EMIT modeChanged( m_data->mode );
}
#include "QskInputPanel.moc"

View File

@ -6,28 +6,55 @@
#ifndef QSK_INPUT_PANEL_H
#define QSK_INPUT_PANEL_H
#include "QskControl.h"
#include "QskBox.h"
#include "QskPushButton.h"
#include <QRectF>
class QSK_EXPORT QskInputPanel : public QskControl
class QskInputCompositionModel;
class QskInputPanel;
class QskKeyButton : public QskPushButton // ### rename to QskInputButton or so?
{
Q_OBJECT
using Inherited = QskPushButton;
public:
QSK_SUBCONTROLS( Panel, Text, TextCancelButton )
QskKeyButton( int keyIndex, QskInputPanel* inputPanel, QQuickItem* parent = nullptr );
virtual QskAspect::Subcontrol effectiveSubcontrol( QskAspect::Subcontrol subControl ) const override;
int keyIndex() const;
public Q_SLOTS:
void updateText();
private:
bool isCancelButton() const;
const int m_keyIndex;
QskInputPanel* m_inputPanel;
};
class QSK_EXPORT QskInputPanel : public QskBox
{
Q_OBJECT
Q_PROPERTY( QRectF keyboardRect READ keyboardRect NOTIFY keyboardRectChanged )
Q_PROPERTY( QString displayLanguageName READ displayLanguageName
NOTIFY displayLanguageNameChanged )
NOTIFY displayLanguageNameChanged )
using Inherited = QskControl;
using Inherited = QskBox;
public:
QSK_SUBCONTROLS( Panel, KeyPanel, KeyGlyph )
QSK_STATES( Checked, Pressed )
QSK_SUBCONTROLS( Panel )
struct KeyData
{
Qt::Key key = Qt::Key( 0 );
int key = 0;
bool isSuggestionKey = false;
QRectF rect;
};
@ -60,7 +87,9 @@ public:
using KeyDataSet = KeyDataRow[RowCount];
QskInputPanel( QQuickItem* parent = nullptr );
virtual ~QskInputPanel();
virtual ~QskInputPanel() override;
virtual QskAspect::Subcontrol effectiveSubcontrol( QskAspect::Subcontrol subControl ) const override;
void updateLocale( const QLocale& locale );
@ -69,33 +98,32 @@ public:
const KeyDataSet& keyData( QskInputPanel::Mode = CurrentMode ) const;
QString textForKey( Qt::Key ) const;
QString textForKey( int ) const;
QString displayLanguageName() const;
QRectF keyboardRect() const;
// takes ownership:
void registerCompositionModelForLocale( const QLocale& locale,
QskInputCompositionModel* model );
public Q_SLOTS:
void setPreeditGroups( const QVector< Qt::Key >& );
void setPreeditCandidates( const QVector< Qt::Key >& );
bool advanceFocus( bool = true );
bool activateFocusKey();
bool deactivateFocusKey();
void clearFocusKey();
void handleKey( int keyIndex );
KeyData& keyDataAt( int ) const;
QString currentTextForKeyIndex( int keyIndex ) const;
protected:
virtual void geometryChanged( const QRectF&, const QRectF& ) override;
virtual void mousePressEvent( QMouseEvent* ) override;
virtual void mouseMoveEvent( QMouseEvent* ) override;
virtual void mouseReleaseEvent( QMouseEvent* ) override;
virtual void touchEvent( QTouchEvent* ) override;
virtual void timerEvent( QTimerEvent* ) override;
virtual void updateLayout() override;
private:
KeyData& keyDataAt( int ) const;
void createUI();
void updateUI(); // e.g. called when updating Pinyin suggestions
void handleKey( int );
void compose( Qt::Key );
void compose( int );
void selectGroup( int );
void selectCandidate( int );
void setCandidateOffset( int );
@ -105,6 +133,11 @@ private:
Q_SIGNALS:
void keyboardRectChanged();
void displayLanguageNameChanged();
void inputMethodRegistered( const QLocale& locale, QskInputCompositionModel* model );
void inputMethodEventReceived( QInputMethodEvent* inputMethodEvent );
void keyEventReceived( QKeyEvent* keyEvent );
void modeChanged( QskInputPanel::Mode mode );
void cancelPressed();
public:
class PrivateData;

View File

@ -1,222 +0,0 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#include "QskInputPanelSkinlet.h"
#include "QskInputPanel.h"
#include "QskAspect.h"
#include "QskSkin.h"
#include "QskTextOptions.h"
#include "QskTextNode.h"
#include "QskBoxNode.h"
static constexpr const QSGNode::Flag IsSubtreeBlocked =
static_cast< QSGNode::Flag >( 0x100000 );
static constexpr const QSGNode::Flag KeyFrameNode =
static_cast< QSGNode::Flag >( 0x200000 );
static constexpr const QSGNode::Flag KeyGlyphNode =
static_cast< QSGNode::Flag >( 0x400000 );
namespace
{
class KeySkinnable: public QskSkinnable
{
public:
KeySkinnable( QskInputPanel* panel ):
m_panel( panel )
{
}
void setKey( Qt::Key key )
{
setSkinStateFlag( QskInputPanel::Pressed, key & Qt::ShiftModifier );
setSkinStateFlag( QskInputPanel::Checked, key & Qt::ControlModifier );
setSkinStateFlag( QskInputPanel::Focused, key & Qt::MetaModifier );
}
const QMetaObject* metaObject() const override final
{
// Use the parent skinlet
return &QskInputPanelSkinlet::staticMetaObject;
}
protected:
virtual const QskSkinlet* effectiveSkinlet() const override final
{
return m_panel->effectiveSkinlet();
}
private:
virtual QskControl* owningControl() const override final
{
return const_cast< QskInputPanel* >( m_panel );
}
private:
QskInputPanel* m_panel;
};
class FrameNode final : public QskBoxNode, public KeySkinnable
{
public:
FrameNode( QskInputPanel* panel ):
KeySkinnable( panel )
{
setFlag( KeyFrameNode );
}
};
class TextNode final : public QskTextNode, public KeySkinnable
{
public:
TextNode( QskInputPanel* panel ):
KeySkinnable( panel )
{
setFlag( KeyGlyphNode );
}
};
class InputPanelNode final : public QskBoxNode
{
public:
InputPanelNode()
{
}
virtual bool isSubtreeBlocked() const override final
{
return flags() & IsSubtreeBlocked;
}
using Row = QSGNode*[ QskInputPanel::KeyCount ];
Row frames[ QskInputPanel::RowCount ] = {};
Row glyphs[ QskInputPanel::RowCount ] = {};
};
}
static_assert( QskInputPanel::RowCount * QskInputPanel::KeyCount < 255,
"The number of keys must fit into an unsigned byte." );
QskInputPanelSkinlet::QskInputPanelSkinlet( QskSkin* skin ):
Inherited( skin )
{
setNodeRoles( { Panel0, Panel1, Panel2 } );
}
QskInputPanelSkinlet::~QskInputPanelSkinlet()
{
}
QSGNode* QskInputPanelSkinlet::updateSubNode(
const QskSkinnable* control, quint8 nodeRole, QSGNode* node ) const
{
auto inputPanel = static_cast< const QskInputPanel* >( control );
const bool blockSubtree = inputPanel->mode() != nodeRole;
auto panelNode = static_cast< InputPanelNode* >( node );
if ( panelNode && panelNode->isSubtreeBlocked() != blockSubtree )
{
panelNode->setFlag( IsSubtreeBlocked, blockSubtree );
panelNode->markDirty( QSGNode::DirtySubtreeBlocked );
}
if ( !blockSubtree )
node = updatePanelNode( inputPanel, panelNode );
return node;
}
QSGNode* QskInputPanelSkinlet::updatePanelNode(
const QskInputPanel* panel, QSGNode* node ) const
{
auto panelNode = static_cast< InputPanelNode* >( node );
if ( panelNode == nullptr )
panelNode = new InputPanelNode;
const auto contentsRect = panel->keyboardRect();
auto& panelKeyData = panel->keyData();
for ( const auto& keyRow : panelKeyData )
{
const auto rowIndex = &keyRow - panelKeyData;
auto& frames = panelNode->frames[ rowIndex ];
auto& glyphs = panelNode->glyphs[ rowIndex ];
for ( const auto& keyData : keyRow )
{
const auto colIndex = &keyData - keyRow;
auto& frame = frames[ colIndex ];
auto& glyph = glyphs[ colIndex ];
if ( !keyData.key || keyData.key == Qt::Key_unknown )
{
delete frame;
frame = nullptr;
delete glyph;
glyph = nullptr;
continue;
}
const qreal sx = contentsRect.size().width();
const qreal sy = contentsRect.size().height();
const QRectF keyRect(
contentsRect.x() + keyData.rect.x() * sx,
contentsRect.y() + keyData.rect.y() * sy,
keyData.rect.width() * sx,
keyData.rect.height() * sy );
frame = updateKeyFrameNode( panel, frame, keyRect, keyData.key );
if ( frame->parent() != panelNode )
panelNode->appendChildNode( frame );
glyph = updateKeyGlyphNode( panel, glyph, keyRect, keyData.key );
if ( glyph->parent() != panelNode )
panelNode->appendChildNode( glyph );
}
}
updateBoxNode( panel, panelNode, contentsRect, QskInputPanel::Panel );
return panelNode;
}
QSGNode* QskInputPanelSkinlet::updateKeyFrameNode(
const QskInputPanel* panel, QSGNode* node,
const QRectF& rect, Qt::Key key ) const
{
auto frameNode = static_cast< FrameNode* >( node );
if ( frameNode == nullptr )
frameNode = new FrameNode( const_cast< QskInputPanel* >( panel ) );
frameNode->setKey( key );
updateBoxNode( frameNode, frameNode, rect, QskInputPanel::KeyPanel );
return frameNode;
}
QSGNode* QskInputPanelSkinlet::updateKeyGlyphNode(
const QskInputPanel* panel, QSGNode* node,
const QRectF& rect, Qt::Key key ) const
{
auto textNode = static_cast< TextNode* >( node );
if ( textNode == nullptr )
textNode = new TextNode( const_cast< QskInputPanel* >( panel ) );
textNode->setKey( key );
QskTextOptions options;
options.setFontSizeMode( QskTextOptions::VerticalFit );
const auto alignment = textNode->flagHint< Qt::Alignment >(
QskInputPanel::KeyGlyph | QskAspect::Alignment, Qt::AlignCenter );
return updateTextNode( panel, textNode, rect, alignment,
panel->textForKey( key ), options, QskInputPanel::KeyGlyph );
}
#include "moc_QskInputPanelSkinlet.cpp"

View File

@ -1,43 +0,0 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the QSkinny License, Version 1.0
*****************************************************************************/
#ifndef QSK_INPUT_PANEL_SKINLET_H
#define QSK_INPUT_PANEL_SKINLET_H
#include "QskSkinlet.h"
class QskInputPanel;
class QSK_EXPORT QskInputPanelSkinlet : public QskSkinlet
{
Q_GADGET
using Inherited = QskSkinlet;
enum NodeRole
{
Panel0,
Panel1,
Panel2
};
public:
Q_INVOKABLE QskInputPanelSkinlet( QskSkin* = nullptr );
virtual ~QskInputPanelSkinlet();
protected:
virtual QSGNode* updateSubNode( const QskSkinnable*,
quint8, QSGNode* ) const override;
virtual QSGNode* updatePanelNode( const QskInputPanel*, QSGNode* ) const;
virtual QSGNode* updateKeyFrameNode( const QskInputPanel*,
QSGNode*, const QRectF&, Qt::Key ) const;
virtual QSGNode* updateKeyGlyphNode( const QskInputPanel*,
QSGNode*, const QRectF&, Qt::Key ) const;
};
#endif

View File

@ -39,9 +39,6 @@ QSK_QT_PRIVATE_END
#include "QskGraphicLabel.h"
#include "QskGraphicLabelSkinlet.h"
#include "QskInputPanel.h"
#include "QskInputPanelSkinlet.h"
#include "QskListView.h"
#include "QskListViewSkinlet.h"
@ -130,7 +127,6 @@ QskSkin::QskSkin( QObject* parent ):
declareSkinlet< QskBox, QskBoxSkinlet >();
declareSkinlet< QskFocusIndicator, QskFocusIndicatorSkinlet >();
declareSkinlet< QskGraphicLabel, QskGraphicLabelSkinlet >();
declareSkinlet< QskInputPanel, QskInputPanelSkinlet >();
declareSkinlet< QskListView, QskListViewSkinlet >();
declareSkinlet< QskPageIndicator, QskPageIndicatorSkinlet >();
declareSkinlet< QskPopup, QskPopupSkinlet >();

View File

@ -136,7 +136,6 @@ HEADERS += \
controls/QskGraphicLabelSkinlet.h \
controls/QskHintAnimator.h \
controls/QskInputPanel.h \
controls/QskInputPanelSkinlet.h \
controls/QskListView.h \
controls/QskListViewSkinlet.h \
controls/QskObjectTree.h \
@ -201,7 +200,6 @@ SOURCES += \
controls/QskGraphicLabelSkinlet.cpp \
controls/QskHintAnimator.cpp \
controls/QskInputPanel.cpp \
controls/QskInputPanelSkinlet.cpp \
controls/QskListView.cpp \
controls/QskListViewSkinlet.cpp \
controls/QskObjectTree.cpp \