diff --git a/playground/inputpanel/main.cpp b/playground/inputpanel/main.cpp index 026b4558..24fc6e32 100644 --- a/playground/inputpanel/main.cpp +++ b/playground/inputpanel/main.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -62,30 +63,28 @@ public: QskListView( parentItem ), m_maxWidth( 0.0 ) { - append( QLocale::Bulgarian, QStringLiteral( "български език" ) ); - append( QLocale::Czech, QStringLiteral( "Čeština" ) ); - append( QLocale::German, QStringLiteral( "Deutsch" ) ); - append( QLocale::Danish, QStringLiteral( "Dansk" ) ); + for ( auto language : + { + QLocale::Bulgarian, QLocale::Czech, QLocale::German, + QLocale::Danish, QLocale::English, QLocale::Spanish, + QLocale::Finnish, QLocale::French, QLocale::Hungarian, + QLocale::Italian, QLocale::Japanese, QLocale::Latvian, + QLocale::Lithuanian, QLocale::Dutch, QLocale::Portuguese, + QLocale::Romanian, QLocale::Russian, QLocale::Slovenian, + QLocale::Slovak, QLocale::Turkish, QLocale::Chinese + } ) + { - append( QLocale( QLocale::English, QLocale::UnitedStates ), QStringLiteral( "English (US)" ) ); - append( QLocale( QLocale::English, QLocale::UnitedKingdom ), QStringLiteral( "English (UK)" ) ); - - append( QLocale::Spanish, QStringLiteral( "Español" ) ); - append( QLocale::Finnish, QStringLiteral( "Suomi" ) ); - append( QLocale::French, QStringLiteral( "Français" ) ); - append( QLocale::Hungarian, QStringLiteral( "Magyar" ) ); - append( QLocale::Italian, QStringLiteral( "Italiano" ) ); - append( QLocale::Japanese, QStringLiteral( "日本語" ) ); - append( QLocale::Latvian, QStringLiteral( "Latviešu" ) ); - append( QLocale::Lithuanian, QStringLiteral( "Lietuvių") ); - append( QLocale::Dutch, QStringLiteral( "Nederlands" ) ); - append( QLocale::Portuguese, QStringLiteral( "Português" ) ); - append( QLocale::Romanian, QStringLiteral( "Română" ) ); - append( QLocale::Russian, QStringLiteral( "Русский" ) ); - append( QLocale::Slovenian, QStringLiteral( "Slovenščina" ) ); - append( QLocale::Slovak, QStringLiteral( "Slovenčina" ) ); - append( QLocale::Turkish, QStringLiteral( "Türkçe" ) ); - append( QLocale::Chinese, QStringLiteral( "中文" ) ); + if ( language == QLocale::English ) + { + append( QLocale( QLocale::English, QLocale::UnitedStates ) ); + append( QLocale( QLocale::English, QLocale::UnitedKingdom ) ); + } + else + { + append( QLocale( language ) ); + } + } setSizePolicy( Qt::Horizontal, QskSizePolicy::Fixed ); setPreferredWidth( columnWidth( 0 ) + 20 ); @@ -145,9 +144,9 @@ public: } private: - inline void append( QLocale locale, const QString& name ) + inline void append( const QLocale& locale ) { - m_values += qMakePair( QString( name ), locale ); + m_values += qMakePair( qskNativeLocaleString( locale ), locale ); } QVector< QPair< QString, QLocale > > m_values; diff --git a/skins/material/QskMaterialSkin.cpp b/skins/material/QskMaterialSkin.cpp index 8d4cd16d..70c110ce 100644 --- a/skins/material/QskMaterialSkin.cpp +++ b/skins/material/QskMaterialSkin.cpp @@ -543,24 +543,24 @@ void QskMaterialSkin::initTabViewHints() void QskMaterialSkin::initInputPanelHints() { using namespace QskAspect; - using Q = QskVirtualKeyboardButton; + using Q = QskVirtualKeyboard; const ColorPalette& pal = m_data->palette; // key panel - setMargins( QskVirtualKeyboard::Panel | Margin, 2 ); + setMargins( Q::ButtonPanel | Margin, 2 ); - setBoxShape( Q::Panel, 20.0, Qt::RelativeSize ); - setBoxBorderMetrics( Q::Panel, 2 ); + setBoxShape( Q::ButtonPanel, 20.0, Qt::RelativeSize ); + setBoxBorderMetrics( Q::ButtonPanel, 2 ); - setGradient( Q::Panel, pal.darker125 ); - setBoxBorderColors( Q::Panel, pal.baseColor ); + setGradient( Q::ButtonPanel, pal.darker125 ); + setBoxBorderColors( Q::ButtonPanel, pal.baseColor ); for ( auto state : { NoState, Q::Focused } ) - setBoxBorderColors( Q::Panel | Q::Pressed | state, pal.accentColor ); + setBoxBorderColors( Q::ButtonPanel | QskPushButton::Pressed | state, pal.accentColor ); - setAnimation( Q::Panel | Color, qskDuration ); - setAnimation( Q::Panel | Metric, qskDuration ); + setAnimation( Q::ButtonPanel | Color, qskDuration ); + setAnimation( Q::ButtonPanel | Metric, qskDuration ); // panel setBoxShape( Q::Panel, 0 ); diff --git a/skins/squiek/QskSquiekSkin.cpp b/skins/squiek/QskSquiekSkin.cpp index 31e25762..300326d8 100644 --- a/skins/squiek/QskSquiekSkin.cpp +++ b/skins/squiek/QskSquiekSkin.cpp @@ -583,21 +583,21 @@ void QskSquiekSkin::initTabViewHints() void QskSquiekSkin::initInputPanelHints() { using namespace QskAspect; - using Q = QskVirtualKeyboardButton; + using Q = QskVirtualKeyboard; const ColorPalette& pal = m_data->palette; // key panel - setMargins( QskVirtualKeyboard::Panel | Padding, 5 ); - setPanel( QskVirtualKeyboard::Panel, Raised ); + setMargins( Q::Panel | Padding, 5 ); + setPanel( Q::Panel, Raised ); - setButton( Q::Panel, Raised ); - setButton( Q::Panel | Q::Pressed, Sunken ); + setButton( Q::ButtonPanel, Raised ); + setButton( Q::ButtonPanel | QskPushButton::Pressed, Sunken ); - setAnimation( Q::Panel | Color, qskDuration ); + setAnimation( Q::ButtonPanel | Color, qskDuration ); - setColor( Q::Text, pal.themeForeground ); - setColor( Q::Text | Q::Disabled, pal.darker200 ); + setColor( Q::ButtonText, pal.themeForeground ); + setColor( Q::ButtonText | QskPushButton::Disabled, pal.darker200 ); } void QskSquiekSkin::initScrollViewHints() diff --git a/src/inputpanel/QskInputPanel.cpp b/src/inputpanel/QskInputPanel.cpp new file mode 100644 index 00000000..78e8d500 --- /dev/null +++ b/src/inputpanel/QskInputPanel.cpp @@ -0,0 +1,101 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#include "QskInputPanel.h" + +#include +#include + +QString qskNativeLocaleString( const QLocale& locale ) +{ + switch( locale.language() ) + { + case QLocale::Bulgarian: + return QStringLiteral( "български език" ); + + case QLocale::Czech: + return QStringLiteral( "Čeština" ); + + case QLocale::German: + return QStringLiteral( "Deutsch" ); + + case QLocale::Danish: + return QStringLiteral( "Dansk" ); + + case QLocale::Greek: + 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)" ); + + default: + return QStringLiteral( "English (UK)" ); + } + + break; + } + + case QLocale::Spanish: + return QStringLiteral( "Español" ); + + case QLocale::Finnish: + return QStringLiteral( "Suomi" ); + + case QLocale::French: + return QStringLiteral( "Français" ); + + case QLocale::Hungarian: + return QStringLiteral( "Magyar" ); + + case QLocale::Italian: + return QStringLiteral( "Italiano" ); + + case QLocale::Japanese: + return QStringLiteral( "日本語" ); + + case QLocale::Latvian: + return QStringLiteral( "Latviešu" ); + + case QLocale::Lithuanian: + return QStringLiteral( "Lietuvių" ); + + case QLocale::Dutch: + return QStringLiteral( "Nederlands" ); + + case QLocale::Portuguese: + return QStringLiteral( "Português" ); + + case QLocale::Romanian: + return QStringLiteral( "Română" ); + + case QLocale::Russia: + return QStringLiteral( "Русский" ); + + case QLocale::Slovenian: + return QStringLiteral( "Slovenščina" ); + + case QLocale::Slovak: + return QStringLiteral( "Slovenčina" ); + + case QLocale::Turkish: + return QStringLiteral( "Türkçe" ); + + case QLocale::Chinese: + return QStringLiteral( "中文" ); + + default: + return QLocale::languageToString( locale.language() ); + } +} + + + diff --git a/src/inputpanel/QskInputPanel.h b/src/inputpanel/QskInputPanel.h new file mode 100644 index 00000000..ac787932 --- /dev/null +++ b/src/inputpanel/QskInputPanel.h @@ -0,0 +1,16 @@ +/****************************************************************************** + * 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_H +#define QSK_INPUT_PANEL_H + +#include "QskGlobal.h" + +class QLocale; +class QString; + +QSK_EXPORT QString qskNativeLocaleString( const QLocale& ); + +#endif diff --git a/src/inputpanel/QskInputSuggestionBar.cpp b/src/inputpanel/QskInputSuggestionBar.cpp new file mode 100644 index 00000000..4bae663b --- /dev/null +++ b/src/inputpanel/QskInputSuggestionBar.cpp @@ -0,0 +1,162 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#include "QskInputSuggestionBar.h" +#include "QskPushButton.h" +#include "QskLinearBox.h" +#include "QskTextOptions.h" + +#include + +QSK_SUBCONTROL( QskInputSuggestionBar, Panel ) +QSK_SUBCONTROL( QskInputSuggestionBar, ButtonPanel ) +QSK_SUBCONTROL( QskInputSuggestionBar, ButtonText ) + +namespace +{ + class Button final : public QskPushButton + { + public: + Button( QQuickItem* parent ): + QskPushButton( parent ) + { + QskTextOptions options; + options.setElideMode( Qt::ElideRight ); + + setTextOptions( options ); + } + + virtual QskAspect::Subcontrol effectiveSubcontrol( + QskAspect::Subcontrol subControl ) const override final + { + if( subControl == QskPushButton::Panel ) + return QskInputSuggestionBar::ButtonPanel; + + if( subControl == QskPushButton::Text ) + return QskInputSuggestionBar::ButtonText; + + return subControl; + } + }; +} + +class QskInputSuggestionBar::PrivateData +{ +public: + QskLinearBox* layoutBox; + QVector< QString > candidates; + + int candidateOffset = 0; + const int buttonCount = 12; +}; + +QskInputSuggestionBar::QskInputSuggestionBar( QQuickItem* parent ): + Inherited( parent ), + m_data( new PrivateData ) +{ + setAutoLayoutChildren( true ); + initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Expanding ); + + m_data->layoutBox = new QskLinearBox( Qt::Horizontal, this ); + + for( int i = 0; i < m_data->buttonCount; i++ ) + { + auto button = new Button( m_data->layoutBox ); + connect( button, &QskPushButton::clicked, + this, &QskInputSuggestionBar::candidateClicked ); + } +} + +QskInputSuggestionBar::~QskInputSuggestionBar() +{ +} + +QskAspect::Subcontrol QskInputSuggestionBar::effectiveSubcontrol( + QskAspect::Subcontrol subControl ) const +{ + if( subControl == QskBox::Panel ) + return QskInputSuggestionBar::Panel; + + return subControl; +} + +void QskInputSuggestionBar::setCandidates( const QVector< QString >& candidates ) +{ + if( m_data->candidates != candidates ) + { + m_data->candidates = candidates; + setCandidateOffset( 0 ); + } +} + +void QskInputSuggestionBar::setCandidateOffset( int offset ) +{ + m_data->candidateOffset = offset; + + const auto candidateCount = m_data->candidates.length(); + const auto count = std::min( candidateCount, m_data->buttonCount ); + const bool continueLeft = m_data->candidateOffset > 0; + const bool continueRight = ( candidateCount - m_data->candidateOffset ) > count; + + for( int i = 0; i < count; i++ ) + { + auto button = qobject_cast< QskPushButton* >( + m_data->layoutBox->itemAtIndex( i ) ); + + if( continueLeft && i == 0 ) + { + button->setText( QChar( 0x2B05 ) ); + } + else if( continueRight && ( i == m_data->buttonCount - 1 ) ) + { + button->setText( QChar( 0x27A1 ) ); + } + else + { + const int index = i + m_data->candidateOffset; + button->setText( m_data->candidates[index] ); + } + + button->setVisible( true ); + } + + for( int i = count; i < m_data->buttonCount; ++i ) + m_data->layoutBox->itemAtIndex( i )->setVisible( false ); +} + +void QskInputSuggestionBar::candidateClicked() +{ + const int index = m_data->layoutBox->indexOf( + qobject_cast< QQuickItem*> ( sender() ) ); + + const int offset = m_data->candidateOffset; + + if ( index == 0 ) + { + if ( offset > 0 ) + { + setCandidateOffset( offset - 1 ); + return; + } + } + else if ( index == m_data->buttonCount - 1 ) + { + if ( m_data->candidates.count() - offset >= m_data->buttonCount ) + { + setCandidateOffset( offset + 1 ); + return; + } + } + +#if 0 + QGuiApplication::inputMethod()->invokeAction( + static_cast< QInputMethod::Action >( SelectCandidate ), index ); + + setPreeditCandidates( QVector< QString >() ); +#endif + Q_EMIT suggested( m_data->candidates[ index - offset ] ); +} + +#include "moc_QskInputSuggestionBar.cpp" diff --git a/src/inputpanel/QskInputSuggestionBar.h b/src/inputpanel/QskInputSuggestionBar.h new file mode 100644 index 00000000..8b232edc --- /dev/null +++ b/src/inputpanel/QskInputSuggestionBar.h @@ -0,0 +1,40 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#ifndef QSK_INPUT_SUGGESTION_BAR_H +#define QSK_INPUT_SUGGESTION_BAR_H + +#include "QskBox.h" + +class QSK_EXPORT QskInputSuggestionBar : public QskBox +{ + Q_OBJECT + + using Inherited = QskBox; + +public: + QSK_SUBCONTROLS( Panel, ButtonPanel, ButtonText ) + + QskInputSuggestionBar( QQuickItem* parent = nullptr ); + virtual ~QskInputSuggestionBar(); + + virtual QskAspect::Subcontrol effectiveSubcontrol( + QskAspect::Subcontrol subControl ) const override; + +Q_SIGNALS: + void suggested( const QString& ); + +public Q_SLOTS: + void setCandidates( const QVector< QString >& ); + +private: + void candidateClicked(); + void setCandidateOffset( int ); + + class PrivateData; + std::unique_ptr< PrivateData > m_data; +}; + +#endif diff --git a/src/inputpanel/QskVirtualKeyboard.cpp b/src/inputpanel/QskVirtualKeyboard.cpp index 0c1f9947..3500eae1 100644 --- a/src/inputpanel/QskVirtualKeyboard.cpp +++ b/src/inputpanel/QskVirtualKeyboard.cpp @@ -4,8 +4,8 @@ *****************************************************************************/ #include "QskVirtualKeyboard.h" +#include "QskPushButton.h" #include "QskTextOptions.h" -#include "QskLinearBox.h" #include #include @@ -19,15 +19,47 @@ QSK_QT_PRIVATE_END namespace { - struct KeyTable + enum { - using Row = QskVirtualKeyboard::KeyData[ QskVirtualKeyboard::KeyCount ]; - Row data[ QskVirtualKeyboard::RowCount ]; + RowCount = 5, + KeyCount = 12 + }; - int indexOf( const QskVirtualKeyboard::KeyData* value ) const + using KeyRow = Qt::Key[KeyCount]; + + class Button final : public QskPushButton + { + public: + Button( int row, int column, QQuickItem* parent ): + QskPushButton( parent ), + m_row( row ), + m_column( column ) { - return int( intptr_t( value - data[0] ) ); + QskTextOptions options; + options.setFontSizeMode( QskTextOptions::VerticalFit ); + setTextOptions( options ); + + setFocusPolicy( Qt::TabFocus ); } + + virtual QskAspect::Subcontrol effectiveSubcontrol( + QskAspect::Subcontrol subControl ) const override + { + if( subControl == QskPushButton::Panel ) + return QskVirtualKeyboard::ButtonPanel; + + if( subControl == QskPushButton::Text ) + return QskVirtualKeyboard::ButtonText; + + return subControl; + } + + int row() const { return m_row; } + int column() const { return m_column; } + + private: + const int m_row; + const int m_column; }; } @@ -35,8 +67,8 @@ struct QskVirtualKeyboardLayouts { struct KeyCodes { - using Row = Qt::Key[ QskVirtualKeyboard::KeyCount ]; - Row data[ QskVirtualKeyboard::RowCount ]; + using Row = Qt::Key[ KeyCount ]; + Row data[ RowCount ]; }; using Layout = KeyCodes[ QskVirtualKeyboard::ModeCount ]; @@ -64,8 +96,6 @@ struct QskVirtualKeyboardLayouts Layout sk; // Slovak Layout tr; // Turkish Layout zh; // Chinese - - Q_GADGET }; #define LOWER(x) Qt::Key(x + 32) // Convert an uppercase key to lowercase @@ -75,11 +105,6 @@ static constexpr const QskVirtualKeyboardLayouts qskKeyboardLayouts = }; #undef LOWER -QSK_DECLARE_OPERATORS_FOR_FLAGS( Qt::Key ) // Must appear after the LOWER macro - -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 ) @@ -103,7 +128,7 @@ static qreal qskKeyStretch( Qt::Key key ) return 1.0; } -static qreal qskRowStretch( const QskVirtualKeyboard::KeyRow& keyRow ) +static qreal qskRowStretch( const KeyRow& keyRow ) { qreal stretch = 0; @@ -119,249 +144,14 @@ static qreal qskRowStretch( const QskVirtualKeyboard::KeyRow& keyRow ) if( stretch == 0.0 ) { - stretch = QskVirtualKeyboard::KeyCount; + stretch = KeyCount; } return stretch; } -static bool qskIsAutorepeat( int key ) +static QString qskTextForKey( Qt::Key key ) { - return ( key != Qt::Key_Return && key != Qt::Key_Enter - && key != Qt::Key_Shift && key != Qt::Key_CapsLock - && key != Qt::Key_Mode_switch ); -} - -static inline QPlatformInputContext* qskInputContext() -{ - auto inputMethod = QGuiApplication::inputMethod(); - return QInputMethodPrivate::get( inputMethod )->platformInputContext(); -} - -QSK_SUBCONTROL( QskVirtualKeyboardCandidateButton, Panel ) -QSK_SUBCONTROL( QskVirtualKeyboardCandidateButton, Text ) - -QskVirtualKeyboardCandidateButton::QskVirtualKeyboardCandidateButton( - QskVirtualKeyboard* inputPanel, QQuickItem* parent ) : - Inherited( parent ), - m_inputPanel( inputPanel ), - m_index( -1 ) -{ - setFlag( QQuickItem::ItemAcceptsInputMethod ); - setText( QStringLiteral( " " ) ); // ### - - connect( this, &QskVirtualKeyboardButton::pressed, - this, [ this ]() { m_inputPanel->handleCandidateKey( m_index, m_text ); } ); -} - -void QskVirtualKeyboardCandidateButton::setIndexAndText(int index, const QString& text ) -{ - m_index = index; - m_text = text; - setText( m_text ); -} - -QskAspect::Subcontrol QskVirtualKeyboardCandidateButton::effectiveSubcontrol( - QskAspect::Subcontrol subControl ) const -{ - if( subControl == QskPushButton::Panel ) - { - return QskVirtualKeyboardCandidateButton::Panel; - } - - if( subControl == QskPushButton::Text ) - { - return QskVirtualKeyboardCandidateButton::Text; - } - - return subControl; -} - -int QskVirtualKeyboardCandidateButton::maxCandidates() -{ - return 12; -} - -QSK_SUBCONTROL( QskVirtualKeyboard, Panel ) - -QSK_SUBCONTROL( QskVirtualKeyboardButton, Panel ) -QSK_SUBCONTROL( QskVirtualKeyboardButton, Text ) - -QskVirtualKeyboardButton::QskVirtualKeyboardButton( - int keyIndex, QskVirtualKeyboard* inputPanel, QQuickItem* parent ) : - Inherited( parent ), - m_keyIndex( keyIndex ), - m_inputPanel( inputPanel ) -{ - 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() ); - } - - updateText(); - - connect( this, &QskVirtualKeyboardButton::pressed, this, - [ this ]() { m_inputPanel->handleKey( m_keyIndex ); } ); - - connect( m_inputPanel, &QskVirtualKeyboard::modeChanged, - this, &QskVirtualKeyboardButton::updateText ); -} - -QskAspect::Subcontrol QskVirtualKeyboardButton::effectiveSubcontrol( - QskAspect::Subcontrol subControl ) const -{ - if( subControl == QskPushButton::Panel ) - return QskVirtualKeyboardButton::Panel; - - if( subControl == QskPushButton::Text ) - return QskVirtualKeyboardButton::Text; - - return subControl; -} - -int QskVirtualKeyboardButton::keyIndex() const -{ - return m_keyIndex; -} - -void QskVirtualKeyboardButton::updateText() -{ - QString text = m_inputPanel->currentTextForKeyIndex( m_keyIndex ); - - if( text.count() == 1 && text.at( 0 ) == QChar( 0 ) ) - { - setVisible( false ); - } - else - { - setVisible( true ); - setText( text ); - } -} - -class QskVirtualKeyboard::PrivateData -{ -public: - PrivateData(): - currentLayout( nullptr ), - mode( QskVirtualKeyboard::LowercaseMode ), - selectedGroup( -1 ), - candidateOffset( 0 ), - candidateBox( nullptr ), - buttonsBox( nullptr ), - isUIInitialized( false ), - candidateBoxVisible( false ) - { - } - -public: - const QskVirtualKeyboardLayouts::Layout* currentLayout; - QskVirtualKeyboard::Mode mode; - - qint16 selectedGroup; - qint32 candidateOffset; - - QVector< QString > candidates; - - KeyTable keyTable[ ModeCount ]; - - QList< QskVirtualKeyboardCandidateButton* > candidateButtons; - QskLinearBox* candidateBox; - QskLinearBox* buttonsBox; - QList< QskVirtualKeyboardButton* > keyButtons; - bool isUIInitialized; - bool candidateBoxVisible; -}; - -QskVirtualKeyboard::QskVirtualKeyboard( QQuickItem* parent ): - Inherited( parent ), - m_data( new PrivateData ) -{ - qRegisterMetaType< Qt::Key >(); - - setFlag( ItemHasContents ); - setFlag( ItemIsFocusScope, true ); -#if 0 - // TODO ... - setTabFence( true ); -#endif - - initSizePolicy( QskSizePolicy::Expanding, QskSizePolicy::Expanding ); - - updateLocale( locale() ); - - connect( this, &QskControl::localeChanged, - this, &QskVirtualKeyboard::updateLocale ); - - setAutoLayoutChildren( true ); - - m_data->buttonsBox = new QskLinearBox( Qt::Vertical, this ); - m_data->buttonsBox->setAutoAddChildren( true ); - - const auto& panelKeyData = keyData(); - - for( const auto& keyRow : panelKeyData ) - { - auto rowBox = new QskLinearBox( Qt::Horizontal, m_data->buttonsBox ); - rowBox->setAutoAddChildren( true ); - - for( const auto& keyData : keyRow ) - { - if( !keyData.key ) - continue; - - const int keyIndex = m_data->keyTable[ m_data->mode ].indexOf( &keyData ); - - auto button = new QskVirtualKeyboardButton( keyIndex, this, rowBox ); - button->installEventFilter( this ); - rowBox->setRetainSizeWhenHidden( button, true ); - - m_data->keyButtons.append( button ); - } - } -} - -QskVirtualKeyboard::~QskVirtualKeyboard() -{ -} - -QskAspect::Subcontrol QskVirtualKeyboard::effectiveSubcontrol( - QskAspect::Subcontrol subControl ) const -{ - if( subControl == QskBox::Panel ) - return QskVirtualKeyboard::Panel; - - return subControl; -} - - -QskVirtualKeyboard::Mode QskVirtualKeyboard::mode() const -{ - return m_data->mode; -} - -const QskVirtualKeyboard::KeyDataSet& QskVirtualKeyboard::keyData( Mode mode ) const -{ - mode = mode == CurrentMode ? m_data->mode : mode; - Q_ASSERT( mode >= 0 && mode < ModeCount ); - return m_data->keyTable[ mode ].data; -} - -QString QskVirtualKeyboard::textForKey( int key ) const -{ - key &= ~KeyStates; - // Special cases switch( key ) { @@ -397,236 +187,169 @@ QString QskVirtualKeyboard::textForKey( int key ) const return QChar( 0x27A1 ); default: - break; - } - - // TODO: possibly route through locale for custom strings - - // Default to direct key mapping - return QChar( key ); -} - -QString QskVirtualKeyboard::displayLanguageName() const -{ - const auto locale = this->locale(); - - switch( locale.language() ) - { - case QLocale::Bulgarian: - return QStringLiteral( "български език" ); - - case QLocale::Czech: - return QStringLiteral( "Čeština" ); - - case QLocale::German: - return QStringLiteral( "Deutsch" ); - - case QLocale::Danish: - return QStringLiteral( "Dansk" ); - - case QLocale::Greek: - 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)" ); - - default: - return QStringLiteral( "English (UK)" ); - } - - break; - } - - case QLocale::Spanish: - return QStringLiteral( "Español" ); - - case QLocale::Finnish: - return QStringLiteral( "Suomi" ); - - case QLocale::French: - return QStringLiteral( "Français" ); - - case QLocale::Hungarian: - return QStringLiteral( "Magyar" ); - - case QLocale::Italian: - return QStringLiteral( "Italiano" ); - - case QLocale::Japanese: - return QStringLiteral( "日本語" ); - - case QLocale::Latvian: - return QStringLiteral( "Latviešu" ); - - case QLocale::Lithuanian: - return QStringLiteral( "Lietuvių" ); - - case QLocale::Dutch: - return QStringLiteral( "Nederlands" ); - - case QLocale::Portuguese: - return QStringLiteral( "Português" ); - - case QLocale::Romanian: - return QStringLiteral( "Română" ); - - case QLocale::Russia: - return QStringLiteral( "Русский" ); - - case QLocale::Slovenian: - return QStringLiteral( "Slovenščina" ); - - case QLocale::Slovak: - return QStringLiteral( "Slovenčina" ); - - case QLocale::Turkish: - return QStringLiteral( "Türkçe" ); - - case QLocale::Chinese: - return QStringLiteral( "中文" ); - - default: - break; - } - - return QLocale::languageToString( locale.language() ); -} - -void QskVirtualKeyboard::setPreeditCandidates( const QVector< QString >& candidates ) -{ - if( m_data->candidates != candidates ) - { - m_data->candidates = candidates; - setCandidateOffset( 0 ); + return QChar( key ); } } -void QskVirtualKeyboard::setCandidateOffset( int candidateOffset ) +static bool qskIsAutorepeat( int key ) { - m_data->candidateOffset = candidateOffset; + return ( key != Qt::Key_Return && key != Qt::Key_Enter + && key != Qt::Key_Shift && key != Qt::Key_CapsLock + && key != Qt::Key_Mode_switch ); +} - const auto candidateCount = m_data->candidates.length(); - const auto count = std::min( candidateCount, QskVirtualKeyboardCandidateButton::maxCandidates() ); - const bool continueLeft = m_data->candidateOffset > 0; - const bool continueRight = ( candidateCount - m_data->candidateOffset ) > count; +static inline QPlatformInputContext* qskInputContext() +{ + auto inputMethod = QGuiApplication::inputMethod(); + return QInputMethodPrivate::get( inputMethod )->platformInputContext(); +} - for( int i = 0; i < count; ++i ) +QSK_SUBCONTROL( QskVirtualKeyboard, Panel ) +QSK_SUBCONTROL( QskVirtualKeyboard, ButtonPanel ) +QSK_SUBCONTROL( QskVirtualKeyboard, ButtonText ) + +class QskVirtualKeyboard::PrivateData +{ +public: + PrivateData(): + currentLayout( nullptr ), + mode( QskVirtualKeyboard::LowercaseMode ) { - auto button = m_data->candidateButtons[i]; + } - if( continueLeft && i == 0 ) +public: + const QskVirtualKeyboardLayouts::Layout* currentLayout; + QskVirtualKeyboard::Mode mode; + + QVector< Button* > keyButtons; +}; + +QskVirtualKeyboard::QskVirtualKeyboard( QQuickItem* parent ): + Inherited( parent ), + m_data( new PrivateData ) +{ + setFlag( ItemHasContents ); + setFlag( ItemIsFocusScope, true ); +#if 0 + // TODO ... + setTabFence( true ); +#endif + + setPolishOnResize( true ); + initSizePolicy( QskSizePolicy::Expanding, QskSizePolicy::Expanding ); + + m_data->keyButtons.reserve( RowCount * KeyCount ); + + const auto autoRepeatInterval = + 1000 / QGuiApplication::styleHints()->keyboardAutoRepeatRate(); + + for ( int row = 0; row < RowCount; row++ ) + { + for ( int col = 0; col < KeyCount; col++ ) { - button->setIndexAndText( i, textForKey( Qt::Key_ApplicationLeft ) ); - } - else if( continueRight && ( i == KeyCount - 1 ) ) - { - button->setIndexAndText( i, textForKey( Qt::Key_ApplicationRight ) ); - } - else - { - const int index = i + m_data->candidateOffset; - button->setIndexAndText( index, m_data->candidates[index] ); + auto button = new Button( row, col, this ); + button->installEventFilter( this ); + + button->setAutoRepeat( false ); + button->setAutoRepeatDelay( 500 ); + button->setAutoRepeatInterval( autoRepeatInterval ); + + connect( button, &QskPushButton::pressed, + this, &QskVirtualKeyboard::buttonPressed ); + + m_data->keyButtons += button; } } - for( int i = count; i < QskVirtualKeyboardCandidateButton::maxCandidates(); ++i ) - { - m_data->candidateButtons[i]->setIndexAndText( -1, QString() ); - } + connect( this, &QskControl::localeChanged, + this, &QskVirtualKeyboard::updateLocale ); + + updateLocale( locale() ); +} + +QskVirtualKeyboard::~QskVirtualKeyboard() +{ +} + +QskAspect::Subcontrol QskVirtualKeyboard::effectiveSubcontrol( + QskAspect::Subcontrol subControl ) const +{ + if( subControl == QskBox::Panel ) + return QskVirtualKeyboard::Panel; + + return subControl; +} + + +QskVirtualKeyboard::Mode QskVirtualKeyboard::mode() const +{ + return m_data->mode; +} + +void QskVirtualKeyboard::setPreeditCandidates( const QVector< QString >& ) +{ +#if 0 + m_suggestionBar->setCandidates( candidates ); +#endif } void QskVirtualKeyboard::updateLayout() { - if( geometry().isNull() ) - return; // no need to calculate anything, will be called again + const auto r = layoutRect(); + if( r.isEmpty() ) + return; - QRectF rect = layoutRect(); - qreal verticalSpacing = m_data->buttonsBox->spacing(); + QTransform transform; + transform.translate( r.top(), r.left() ); + transform.scale( r.width(), r.height() ); - const auto& children = m_data->buttonsBox->childItems(); - for( auto rowItem : children ) + const auto keyHeight = 1.0f / RowCount; + + const auto& keyCodes = ( *m_data->currentLayout )[ m_data->mode ]; + + qreal yPos = 0; + + for( int row = 0; row < RowCount; row++ ) { - auto rowBox = qobject_cast< QskLinearBox* >( rowItem ); - const qreal horizontalSpacing = rowBox->spacing(); + const auto& keys = keyCodes.data[ row ]; - const auto& rowChildren = rowBox->childItems(); - for( auto keyItem : rowChildren ) + const auto baseKeyWidth = 1.0 / qskRowStretch( keys ); + + qreal xPos = 0; + + for ( int col = 0; col < KeyCount; col++ ) { - auto button = qobject_cast< QskVirtualKeyboardButton* >( keyItem ); - QRectF keyRect = keyDataAt( button->keyIndex() ).rect; - qreal width = keyRect.width() * rect.width() - horizontalSpacing; - qreal height = keyRect.height() * rect.height() - verticalSpacing; + const Qt::Key key = keys[ col ]; + auto button = m_data->keyButtons[ row * KeyCount + col ]; - button->setFixedSize( width, height ); + button->setVisible( key != Qt::Key( 0 ) ); + + if ( button->isVisible() ) + { + const qreal keyWidth = baseKeyWidth * qskKeyStretch( key ); + + const QRectF rect( xPos, yPos, keyWidth, keyHeight ); + + button->setGeometry( transform.mapRect( rect ) ); + button->setAutoRepeat( qskIsAutorepeat( key ) ); + button->setText( qskTextForKey( key ) ); + + xPos += keyWidth; + } } + + yPos += keyHeight; } } -void QskVirtualKeyboard::createUI() +void QskVirtualKeyboard::buttonPressed() { - setAutoLayoutChildren( true ); + const auto button = static_cast< const Button* >( sender() ); + if ( button == nullptr ) + return; - auto outerBox = new QskLinearBox( Qt::Vertical, this ); - - m_data->candidateBox = new QskLinearBox( Qt::Horizontal, outerBox ); -#if 1 - // should be skin hints TODO ... - QMarginsF margins( 0, 10, 0, 20 ); // ### - m_data->candidateBox->setMargins( margins ); -#endif - - // to determine suggestions buttons width - // (otherwise empty buttons would be too small when there are only a few suggestions): - // ### Can this be done with the layout engine or so? - QRectF rect = layoutRect(); - auto candidateButtonWidth = rect.width() / QskVirtualKeyboardCandidateButton::maxCandidates() - - m_data->candidateBox->spacing() * QskVirtualKeyboardCandidateButton::maxCandidates(); - - for( int a = 0; a < QskVirtualKeyboardCandidateButton::maxCandidates(); ++a ) - { - auto button = new QskVirtualKeyboardCandidateButton( this, m_data->candidateBox ); - - qreal height = button->sizeHint().height(); -#if 1 - // should be done by margins/paddings - button->setPreferredHeight( height + 10 ); -#endif - button->setPreferredWidth( candidateButtonWidth ); - button->installEventFilter( this ); - - m_data->candidateBox->setRetainSizeWhenHidden( button, true ); - m_data->candidateButtons.append( button ); - } - - m_data->candidateBox->setVisible( m_data->candidateBoxVisible ); - outerBox->setRetainSizeWhenHidden( m_data->candidateBox, true ); -} - -void QskVirtualKeyboard::updateUI() -{ - for( auto button : qskAsConst( m_data->keyButtons ) ) - button->updateText(); -} - -QskVirtualKeyboard::KeyData& QskVirtualKeyboard::keyDataAt( int keyIndex ) const -{ - const auto row = keyIndex / KeyCount; - const auto col = keyIndex % KeyCount; - - return m_data->keyTable[ m_data->mode ].data[ row ][ col ]; -} - -void QskVirtualKeyboard::handleKey( int keyIndex ) -{ - KeyData keyData = keyDataAt( keyIndex ); - const auto key = keyData.key & ~KeyStates; + const auto& keyCodes = ( *m_data->currentLayout )[ m_data->mode ]; + const Qt::Key key = keyCodes.data[ button->row() ][ button->column() ]; // Mode-switching keys switch( key ) @@ -653,7 +376,6 @@ void QskVirtualKeyboard::handleKey( int keyIndex ) break; } - default: { QGuiApplication::inputMethod()->invokeAction( @@ -662,38 +384,8 @@ void QskVirtualKeyboard::handleKey( int keyIndex ) } } -void QskVirtualKeyboard::handleCandidateKey( int index, const QString& text ) +void QskVirtualKeyboard::setCandidateBarVisible( bool ) { - if( text == textForKey( Qt::Key_ApplicationLeft ) ) - { - setCandidateOffset( m_data->candidateOffset - 1 ); - } - else if( text == textForKey( Qt::Key_ApplicationRight ) ) - { - setCandidateOffset( m_data->candidateOffset + 1 ); - } - else - { - QGuiApplication::inputMethod()->invokeAction( - static_cast< QInputMethod::Action >( SelectCandidate ), index ); - - setPreeditCandidates( QVector< QString >() ); - } -} - -void QskVirtualKeyboard::setCandidateBarVisible( bool visible ) -{ - // need to cache it until we have created the UI - m_data->candidateBoxVisible = visible; - if( m_data->isUIInitialized ) - m_data->candidateBox->setVisible( m_data->candidateBoxVisible ); -} - -QString QskVirtualKeyboard::currentTextForKeyIndex( int keyIndex ) const -{ - auto keyData = keyDataAt( keyIndex ); - QString text = textForKey( keyData.key ); - return text; } void QskVirtualKeyboard::updateLocale( const QLocale& locale ) @@ -813,45 +505,8 @@ void QskVirtualKeyboard::updateLocale( const QLocale& locale ) } - Q_EMIT displayLanguageNameChanged(); - - updateKeyData(); setMode( LowercaseMode ); -} - -void QskVirtualKeyboard::updateKeyData() -{ - // Key data is in normalized coordinates - const auto keyHeight = 1.0f / RowCount; - - 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++ ) - { - auto& row = keyLayout.data[i]; - auto& keyDataRow = keyDataLayout.data[ i ]; - - const auto baseKeyWidth = 1.0 / qskRowStretch( row ); - - qreal xPos = 0; - qreal keyWidth = baseKeyWidth; - - for( const auto& key : row ) - { - auto& keyData = keyDataRow[ &key - row ]; - keyData.key = key; - - keyWidth = baseKeyWidth * qskKeyStretch( key ); - keyData.rect = { xPos, yPos, keyWidth, keyHeight }; - xPos += keyWidth; - } - - yPos += keyHeight; - } - } + polish(); } void QskVirtualKeyboard::setMode( QskVirtualKeyboard::Mode mode ) diff --git a/src/inputpanel/QskVirtualKeyboard.h b/src/inputpanel/QskVirtualKeyboard.h index b8c7fdd5..6d2efbc5 100644 --- a/src/inputpanel/QskVirtualKeyboard.h +++ b/src/inputpanel/QskVirtualKeyboard.h @@ -7,79 +7,15 @@ #define QSK_VIRTUAL_KEYBOARD_H #include "QskBox.h" -#include "QskPushButton.h" - -#include - -class QskInputCompositionModel; -class QskVirtualKeyboard; - -class QSK_EXPORT QskVirtualKeyboardCandidateButton : public QskPushButton -{ - Q_OBJECT - - using Inherited = QskPushButton; - -public: - QSK_SUBCONTROLS( Panel, Text ) - - QskVirtualKeyboardCandidateButton( QskVirtualKeyboard* inputPanel, QQuickItem* parent = nullptr ); - void setIndexAndText( int index, const QString& text ); - - virtual QskAspect::Subcontrol effectiveSubcontrol( QskAspect::Subcontrol subControl ) const override; - - static int maxCandidates(); - // add a setter here as well if needed - -private: - QskVirtualKeyboard* m_inputPanel; - int m_index; - QString m_text; -}; - -class QSK_EXPORT QskVirtualKeyboardButton : public QskPushButton -{ - Q_OBJECT - - using Inherited = QskPushButton; - -public: - QSK_SUBCONTROLS( Panel, Text ) - - QskVirtualKeyboardButton( int keyIndex, - QskVirtualKeyboard*, QQuickItem* parent = nullptr ); - - virtual QskAspect::Subcontrol effectiveSubcontrol( - QskAspect::Subcontrol ) const override; - - int keyIndex() const; - -public Q_SLOTS: - void updateText(); - -private: - const int m_keyIndex; - QskVirtualKeyboard* m_inputPanel; -}; class QSK_EXPORT QskVirtualKeyboard : public QskBox { Q_OBJECT - Q_PROPERTY( QString displayLanguageName READ displayLanguageName - NOTIFY displayLanguageNameChanged ) - using Inherited = QskBox; public: - QSK_SUBCONTROLS( Panel ) - - struct KeyData - { - int key = 0; - bool isSuggestionKey = false; - QRectF rect; - }; + QSK_SUBCONTROLS( Panel, ButtonPanel, ButtonText ) enum Action { @@ -98,16 +34,6 @@ public: }; Q_ENUM( Mode ) - enum - { - RowCount = 5, - KeyCount = 12 - }; - - using KeyRow = Qt::Key[KeyCount]; - using KeyDataRow = KeyData[KeyCount]; - using KeyDataSet = KeyDataRow[RowCount]; - QskVirtualKeyboard( QQuickItem* parent = nullptr ); virtual ~QskVirtualKeyboard() override; @@ -116,19 +42,11 @@ public: void updateLocale( const QLocale& ); - void setMode( QskVirtualKeyboard::Mode ); + void setMode( Mode ); Mode mode() const; - const KeyDataSet& keyData( QskVirtualKeyboard::Mode = CurrentMode ) const; - - QString textForKey( int ) const; - QString displayLanguageName() const; - - void handleKey( int keyIndex ); - KeyData& keyDataAt( int ) const; - QString currentTextForKeyIndex( int keyIndex ) const; - - void handleCandidateKey( int index, const QString& text ); +Q_SIGNALS: + void modeChanged( Mode ); public Q_SLOTS: void setPreeditCandidates( const QVector< QString >& ); @@ -138,23 +56,12 @@ protected: virtual bool eventFilter( QObject*, QEvent* ) override; virtual void updateLayout() override; -private: - void createUI(); - void updateUI(); // e.g. called when updating Pinyin suggestions - - void setCandidateOffset( int ); - - void updateKeyData(); - -Q_SIGNALS: - void displayLanguageNameChanged(); - void modeChanged( QskVirtualKeyboard::Mode ); +private Q_SLOTS: + void buttonPressed(); private: class PrivateData; std::unique_ptr< PrivateData > m_data; }; -Q_DECLARE_TYPEINFO( QskVirtualKeyboard::KeyData, Q_PRIMITIVE_TYPE ); - #endif diff --git a/src/inputpanel/inputpanel.pro b/src/inputpanel/inputpanel.pro new file mode 100644 index 00000000..cb7cab62 --- /dev/null +++ b/src/inputpanel/inputpanel.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS = ../ diff --git a/src/src.pro b/src/src.pro index eca3b48b..1a663360 100644 --- a/src/src.pro +++ b/src/src.pro @@ -298,7 +298,11 @@ SOURCES += \ dialogs/QskSelectionWindow.cpp SOURCES += \ + inputpanel/QskInputPanel.cpp \ + inputpanel/QskInputSuggestionBar.cpp \ inputpanel/QskVirtualKeyboard.cpp HEADERS += \ + inputpanel/QskInputPanel.h \ + inputpanel/QskInputSuggestionBar.h \ inputpanel/QskVirtualKeyboard.h