QskVirtualKeyboard much simpler now

This commit is contained in:
Uwe Rathmann 2018-04-06 17:30:24 +02:00
parent 80c41c53e0
commit 07d28529be
11 changed files with 552 additions and 666 deletions

View File

@ -12,6 +12,7 @@
#include <QskLinearBox.h>
#include <QskListView.h>
#include <QskTextInput.h>
#include <QskInputPanel.h>
#include <QskWindow.h>
#include <QskSetup.h>
@ -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;

View File

@ -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 );

View File

@ -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()

View File

@ -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 <QString>
#include <QLocale>
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() );
}
}

View File

@ -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

View File

@ -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 <QVector>
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"

View File

@ -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

View File

@ -4,8 +4,8 @@
*****************************************************************************/
#include "QskVirtualKeyboard.h"
#include "QskPushButton.h"
#include "QskTextOptions.h"
#include "QskLinearBox.h"
#include <QGuiApplication>
#include <QStyleHints>
@ -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 )

View File

@ -7,79 +7,15 @@
#define QSK_VIRTUAL_KEYBOARD_H
#include "QskBox.h"
#include "QskPushButton.h"
#include <QRectF>
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

View File

@ -0,0 +1,2 @@
TEMPLATE = subdirs
SUBDIRS = ../

View File

@ -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