QskInputPanel reintroduced being a composite of QskVirtualKeyboard +

QskInputSuggestionBar
This commit is contained in:
Uwe Rathmann 2018-04-10 16:51:35 +02:00
parent 1513d3716f
commit 10a3435e91
9 changed files with 294 additions and 115 deletions

View File

@ -4,12 +4,12 @@
*****************************************************************************/
#include "QskInputContext.h"
#include "QskVirtualKeyboard.h"
#include "QskInputCompositionModel.h"
#include "QskPinyinCompositionModel.h"
#include "QskHunspellCompositionModel.h"
#include "QskInputPanel.h"
#include <QskDialog.h>
#include <QskWindow.h>
#include <QskControl.h>
@ -19,7 +19,7 @@
#include <QHash>
#include <QPointer>
void qskSetLocale( QQuickItem* inputPanel, const QLocale& locale )
static void qskSetLocale( QQuickItem* inputPanel, const QLocale& locale )
{
if ( auto control = qobject_cast< QskControl* >( inputPanel ) )
{
@ -35,7 +35,7 @@ void qskSetLocale( QQuickItem* inputPanel, const QLocale& locale )
}
}
QLocale qskLocale( const QQuickItem* inputPanel )
static QLocale qskLocale( const QQuickItem* inputPanel )
{
if ( inputPanel == nullptr )
return QLocale();
@ -46,14 +46,37 @@ QLocale qskLocale( const QQuickItem* inputPanel )
return inputPanel->property( "locale" ).toLocale();
}
QskVirtualKeyboard* qskVirtualKeyboard( QQuickItem* inputPanel )
static void qskSetCandidatesEnabled( QQuickItem* inputPanel, bool on )
{
// we should not depend on QskVirtualKeyboard TODO ...
if ( inputPanel == nullptr )
return;
if ( inputPanel )
return inputPanel->findChild< QskVirtualKeyboard* >();
if ( auto panel = qobject_cast< QskInputPanel* >( inputPanel ) )
{
panel->setCandidatesEnabled( on );
}
else
{
QMetaObject::invokeMethod( inputPanel, "setCandidatesEnabled",
Qt::DirectConnection, Q_ARG( bool, on ) );
}
}
return nullptr;
static void qskSetCandidates( QQuickItem* inputPanel,
const QVector< QString >& candidates )
{
if ( inputPanel == nullptr )
return;
if ( auto panel = qobject_cast< QskInputPanel* >( inputPanel ) )
{
panel->setCandidates( candidates );
}
else
{
QMetaObject::invokeMethod( inputPanel, "setCandidates",
Qt::DirectConnection, Q_ARG( QVector< QString >, candidates ) );
}
}
class QskInputContext::PrivateData
@ -205,8 +228,8 @@ void QskInputContext::update( Qt::InputMethodQueries queries )
connect( newModel, &QskInputCompositionModel::candidatesChanged,
this, &QskInputContext::handleCandidatesChanged );
if ( auto keyboard = qskVirtualKeyboard( m_data->inputPanel ) )
keyboard->setCandidateBarVisible( newModel->supportsSuggestions() );
qskSetCandidatesEnabled( m_data->inputPanel,
newModel->supportsSuggestions() );
}
}
@ -250,7 +273,7 @@ void QskInputContext::showInputPanel()
{
if ( !m_data->inputPanel )
{
setInputPanel( new QskVirtualKeyboard() );
setInputPanel( new QskInputPanel() );
if ( QskDialog::instance()->policy() == QskDialog::TopLevelWindow )
{
@ -390,12 +413,12 @@ void QskInputContext::invokeAction( QInputMethod::Action action, int value )
switch ( static_cast< int >( action ) )
{
case QskVirtualKeyboard::Compose:
case QskInputPanel::Compose:
{
model->composeKey( static_cast< Qt::Key >( value ) );
break;
}
case QskVirtualKeyboard::SelectCandidate:
case QskInputPanel::SelectCandidate:
{
model->commitCandidate( value );
break;
@ -422,8 +445,7 @@ void QskInputContext::handleCandidatesChanged()
for( int i = 0; i < count; i++ )
candidates += model->candidate( i );
if ( auto keyboard = qskVirtualKeyboard( m_data->inputPanel ) )
keyboard->setPreeditCandidates( candidates );
qskSetCandidates( m_data->inputPanel, candidates );
}
void QskInputContext::setInputPanel( QQuickItem* inputPanel )
@ -472,8 +494,8 @@ void QskInputContext::setInputPanel( QQuickItem* inputPanel )
if ( model )
{
if ( auto keyboard = qskVirtualKeyboard( inputPanel ) )
keyboard->setCandidateBarVisible( model->supportsSuggestions() );
qskSetCandidatesEnabled( m_data->inputPanel,
model->supportsSuggestions() );
}
}
}

View File

@ -6,7 +6,7 @@
#include <SkinnyFont.h>
#include <SkinnyShortcut.h>
#include <QskVirtualKeyboard.h>
#include <QskInputPanel.h>
#include <QskDialog.h>
#include <QskFocusIndicator.h>
#include <QskLinearBox.h>
@ -45,7 +45,8 @@ public:
textInput->setSizePolicy( Qt::Horizontal, QskSizePolicy::Preferred );
#if LOCAL_PANEL
auto* inputPanel = new QskVirtualKeyboard( this );
auto* inputPanel = new QskInputPanel( this );
inputPanel->setVisible( false );
/*
QskInputContext is connected to QskSetup::inputPanelChanged,

View File

@ -86,10 +86,10 @@ namespace
setFlag( ItemAcceptsInputMethod, false );
setFocusOnPress( false );
componentComplete();
connect( this, &TextInput::contentSizeChanged,
this, &TextInput::updateClip );
componentComplete();
}
void setAlignment( Qt::Alignment alignment )
@ -100,7 +100,7 @@ namespace
inline bool handleEvent( QEvent* event )
{
return QQuickTextInput::event( event );
return this->event( event );
}
virtual void focusInEvent( QFocusEvent* ) override
@ -115,6 +115,9 @@ namespace
#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)
d->updateCursorBlinking();
d->setBlinkingCursorEnabled( true );
#else
d->setCursorBlinkPeriod(
QGuiApplication::styleHints()->cursorFlashTime() );
#endif
if ( d->determineHorizontalAlignment() )
@ -133,6 +136,9 @@ namespace
this, SLOT(q_updateAlignment()) );
qGuiApp->inputMethod()->show();
polish();
update();
}
virtual void focusOutEvent( QFocusEvent* event ) override
@ -147,6 +153,8 @@ namespace
#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)
d->updateCursorBlinking();
d->setBlinkingCursorEnabled( false );
#else
d->setCursorBlinkPeriod( 0 );
#endif
if ( d->m_passwordEchoEditing || d->m_passwordEchoTimer.isActive() )
@ -171,6 +179,9 @@ namespace
disconnect( QGuiApplication::inputMethod(),
SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
this, SLOT(q_updateAlignment()) );
polish();
update();
}
virtual void geometryChanged(

View File

@ -4,10 +4,20 @@
*****************************************************************************/
#include "QskInputPanel.h"
#include "QskVirtualKeyboard.h"
#include "QskInputSuggestionBar.h"
#include "QskLinearBox.h"
#include <QString>
#include <QLocale>
QSK_QT_PRIVATE_BEGIN
#include <private/qinputmethod_p.h>
QSK_QT_PRIVATE_END
#include <qpa/qplatformintegration.h>
#include <qpa/qplatforminputcontext.h>
QString qskNativeLocaleString( const QLocale& locale )
{
switch( locale.language() )
@ -97,5 +107,153 @@ QString qskNativeLocaleString( const QLocale& locale )
}
}
static inline QQuickItem* qskInputItem()
{
QPlatformInputContext* inputContext;
#if 1
inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext();
#else
// for some reason the gcc sanitizer does not like this one
inputContext = QInputMethodPrivate::get( inputMethod )->platformInputContext();
#endif
QQuickItem* item = nullptr;
QMetaObject::invokeMethod( inputContext, "inputItem",
Qt::DirectConnection, Q_RETURN_ARG( QQuickItem*, item ) );
return item;
}
static inline void qskInstallEventFilter( QskInputPanel* panel, bool on )
{
if ( on )
qGuiApp->installEventFilter( panel );
else
qGuiApp->removeEventFilter( panel );
}
class QskInputPanel::PrivateData
{
public:
QskInputSuggestionBar* suggestionBar;
QskVirtualKeyboard* keyboard;
};
QskInputPanel::QskInputPanel( QQuickItem* parent ):
QskControl( parent ),
m_data( new PrivateData() )
{
setAutoLayoutChildren( true );
setFlag( ItemIsFocusScope, true );
#if 0
// TODO ...
setTabFence( true );
#endif
auto layout = new QskLinearBox( Qt::Vertical, this );
m_data->suggestionBar = new QskInputSuggestionBar( layout );
m_data->suggestionBar->setVisible( false );
connect( m_data->suggestionBar, &QskInputSuggestionBar::suggested,
this, &QskInputPanel::commitCandidate );
m_data->keyboard = new QskVirtualKeyboard( layout );
connect( m_data->keyboard, &QskVirtualKeyboard::keySelected,
this, &QskInputPanel::commitKey );
}
QskInputPanel::~QskInputPanel()
{
}
bool QskInputPanel::isCandidatesEnabled() const
{
return m_data->suggestionBar->isVisible();
}
QVector< QString > QskInputPanel::candidates() const
{
return m_data->suggestionBar->candidates();
}
void QskInputPanel::setCandidatesEnabled( bool on )
{
m_data->suggestionBar->setVisible( on );
}
void QskInputPanel::setCandidates( const QVector< QString >& candidates )
{
m_data->suggestionBar->setCandidates( candidates );
}
void QskInputPanel::commitCandidate( int index )
{
m_data->suggestionBar->setCandidates( QVector< QString >() );
QGuiApplication::inputMethod()->invokeAction(
static_cast< QInputMethod::Action >( SelectCandidate ), index );
}
void QskInputPanel::commitKey( Qt::Key key )
{
QGuiApplication::inputMethod()->invokeAction(
static_cast< QInputMethod::Action >( Compose ), key );
}
void QskInputPanel::updateLayout()
{
if ( !isInitiallyPainted() )
qskInstallEventFilter( this, isVisible() );
Inherited::updateLayout();
}
void QskInputPanel::itemChange( QQuickItem::ItemChange change,
const QQuickItem::ItemChangeData& value )
{
switch( change )
{
case QQuickItem::ItemVisibleHasChanged:
case QQuickItem::ItemSceneChange:
{
if ( isInitiallyPainted() )
qskInstallEventFilter( this, isVisible() );
break;
}
default:
break;
}
Inherited::itemChange( change, value );
}
bool QskInputPanel::eventFilter( QObject* object, QEvent* event )
{
if ( event->type() == QEvent::InputMethodQuery )
{
const auto item = qskInputItem();
/*
Qt/Quick expects that the item associated with the input context
always has the focus. But this does not work, when a virtual
keyboard is used, where you can navigate and select inside.
So we have to fix the receiver.
Maybe QEvent::EnterEditFocus is good for something ??
*/
if ( item && ( object != item ) && qskIsAncestorOf( this, item ) )
{
QGuiApplication::sendEvent( item, event );
return true;
}
}
return Inherited::eventFilter( object, event );
}
#include "moc_QskInputPanel.cpp"

View File

@ -7,9 +7,49 @@
#define QSK_INPUT_PANEL_H
#include "QskGlobal.h"
#include "QskControl.h"
class QLocale;
class QString;
class QLocale;
template class QVector< QString >;
class QSK_EXPORT QskInputPanel: public QskControl
{
Q_OBJECT
using Inherited = QskControl;
public:
enum Action
{
Compose = 0x10,
SelectCandidate = 0x11,
};
Q_ENUM( Action )
QskInputPanel( QQuickItem* parent = nullptr );
virtual ~QskInputPanel() override;
bool isCandidatesEnabled() const;
QVector< QString > candidates() const;
public Q_SLOTS:
void setCandidatesEnabled( bool );
void setCandidates( const QVector< QString >& );
protected:
virtual void updateLayout() override;
virtual void itemChange( ItemChange, const ItemChangeData& ) override;
virtual bool eventFilter( QObject*, QEvent* ) override;
private:
void commitKey( Qt::Key );
void commitCandidate( int );
class PrivateData;
std::unique_ptr< PrivateData > m_data;
};
QSK_EXPORT QString qskNativeLocaleString( const QLocale& );

View File

@ -8,6 +8,7 @@
#include "QskLinearBox.h"
#include "QskTextOptions.h"
#include <QFontMetricsF>
#include <QVector>
QSK_SUBCONTROL( QskInputSuggestionBar, Panel )
@ -28,6 +29,19 @@ namespace
setTextOptions( options );
}
virtual QSizeF contentsSizeHint() const override
{
auto size = QFontMetricsF( font() ).size( Qt::TextSingleLine, text() );
const QSizeF minSize( metric( Panel | QskAspect::MinimumWidth ),
metric( Panel | QskAspect::MinimumHeight ) );
size = size.expandedTo( minSize );
size = outerBoxSize( Panel, size );
return size;
}
virtual QskAspect::Subcontrol effectiveSubcontrol(
QskAspect::Subcontrol subControl ) const override final
{
@ -57,16 +71,26 @@ QskInputSuggestionBar::QskInputSuggestionBar( QQuickItem* parent ):
m_data( new PrivateData )
{
setAutoLayoutChildren( true );
initSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Expanding );
initSizePolicy( QskSizePolicy::Expanding, QskSizePolicy::Fixed );
m_data->layoutBox = new QskLinearBox( Qt::Horizontal, this );
for( int i = 0; i < m_data->buttonCount; i++ )
{
auto button = new Button( m_data->layoutBox );
button->setVisible( false );
button->setSizePolicy( Qt::Horizontal, QskSizePolicy::Maximum );
connect( button, &QskPushButton::clicked,
this, &QskInputSuggestionBar::candidateClicked );
if ( i == 0 )
{
// to keep the height
m_data->layoutBox->setRetainSizeWhenHidden( button, true );
}
}
}
QskInputSuggestionBar::~QskInputSuggestionBar()
@ -91,6 +115,11 @@ void QskInputSuggestionBar::setCandidates( const QVector< QString >& candidates
}
}
QVector< QString > QskInputSuggestionBar::candidates() const
{
return m_data->candidates;
}
void QskInputSuggestionBar::setCandidateOffset( int offset )
{
m_data->candidateOffset = offset;
@ -129,7 +158,7 @@ void QskInputSuggestionBar::setCandidateOffset( int offset )
void QskInputSuggestionBar::candidateClicked()
{
const int index = m_data->layoutBox->indexOf(
qobject_cast< QQuickItem*> ( sender() ) );
qobject_cast< QQuickItem* > ( sender() ) );
const int offset = m_data->candidateOffset;
@ -143,20 +172,14 @@ void QskInputSuggestionBar::candidateClicked()
}
else if ( index == m_data->buttonCount - 1 )
{
if ( m_data->candidates.count() - offset >= m_data->buttonCount )
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 ] );
Q_EMIT suggested( offset + index );
}
#include "moc_QskInputSuggestionBar.cpp"

View File

@ -23,8 +23,10 @@ public:
virtual QskAspect::Subcontrol effectiveSubcontrol(
QskAspect::Subcontrol subControl ) const override;
QVector< QString > candidates() const;
Q_SIGNALS:
void suggested( const QString& );
void suggested( int );
public Q_SLOTS:
void setCandidates( const QVector< QString >& );

View File

@ -10,13 +10,6 @@
#include <QGuiApplication>
#include <QStyleHints>
QSK_QT_PRIVATE_BEGIN
#include <private/qinputmethod_p.h>
QSK_QT_PRIVATE_END
#include <qpa/qplatformintegration.h>
#include <qpa/qplatforminputcontext.h>
namespace
{
enum
@ -198,24 +191,6 @@ static bool qskIsAutorepeat( int key )
&& key != Qt::Key_Mode_switch );
}
static inline QQuickItem* qskInputItem()
{
QPlatformInputContext* inputContext;
#if 1
inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext();
#else
// for some reason the gcc sanitizer does not like this one
inputContext = QInputMethodPrivate::get( inputMethod )->platformInputContext();
#endif
QQuickItem* item = nullptr;
QMetaObject::invokeMethod( inputContext, "inputItem",
Qt::DirectConnection, Q_RETURN_ARG( QQuickItem*, item ) );
return item;
}
QSK_SUBCONTROL( QskVirtualKeyboard, Panel )
QSK_SUBCONTROL( QskVirtualKeyboard, ButtonPanel )
QSK_SUBCONTROL( QskVirtualKeyboard, ButtonText )
@ -240,13 +215,6 @@ 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 );
@ -298,13 +266,6 @@ 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()
{
const auto r = layoutRect();
@ -314,7 +275,7 @@ void QskVirtualKeyboard::updateLayout()
const auto spacing = metric( Panel | QskAspect::Spacing );
const auto totalVSpacing = ( RowCount - 1 ) * spacing;
const auto keyHeight = ( r.height() - totalVSpacing ) / RowCount;
const auto keyHeight = ( r.height() - totalVSpacing ) / RowCount;
const auto& keyCodes = ( *m_data->currentLayout )[ m_data->mode ];
@ -400,16 +361,11 @@ void QskVirtualKeyboard::buttonPressed()
}
default:
{
QGuiApplication::inputMethod()->invokeAction(
static_cast< QInputMethod::Action >( Compose ), key );
Q_EMIT keySelected( key );
}
}
}
void QskVirtualKeyboard::setCandidateBarVisible( bool )
{
}
void QskVirtualKeyboard::updateLocale( const QLocale& locale )
{
switch( locale.language() )
@ -539,28 +495,6 @@ void QskVirtualKeyboard::setMode( QskVirtualKeyboard::Mode mode )
Q_EMIT modeChanged( m_data->mode );
}
bool QskVirtualKeyboard::eventFilter( QObject* object, QEvent* event )
{
if ( event->type() == QEvent::InputMethodQuery )
{
/*
Qt/Quick expects that the item associated with the input context
always has the focus. But this does not work, when a virtual
keyboard is used, where you can navigate and select inside.
So we have to fix the receiver.
Maybe QEvent::EnterEditFocus is good for something ??
*/
if ( auto item = qskInputItem() )
QGuiApplication::sendEvent( item, event );
return true;
}
return Inherited::eventFilter( object, event );
}
bool QskVirtualKeyboard::event( QEvent* event )
{
/*

View File

@ -17,13 +17,6 @@ class QSK_EXPORT QskVirtualKeyboard : public QskBox
public:
QSK_SUBCONTROLS( Panel, ButtonPanel, ButtonText )
enum Action
{
Compose = 0x10,
SelectCandidate = 0x11,
};
Q_ENUM( Action )
enum Mode
{
CurrentMode = -1,
@ -47,15 +40,10 @@ public:
Q_SIGNALS:
void modeChanged( Mode );
public Q_SLOTS:
void setPreeditCandidates( const QVector< QString >& );
void setCandidateBarVisible( bool visible );
void keySelected( Qt::Key );
protected:
virtual bool eventFilter( QObject*, QEvent* ) override;
virtual bool event( QEvent* ) override;
virtual void updateLayout() override;
private Q_SLOTS: