input comntext improvements
This commit is contained in:
parent
16efc695b9
commit
602e3748df
|
@ -6,16 +6,15 @@
|
||||||
#include <SkinnyFont.h>
|
#include <SkinnyFont.h>
|
||||||
#include <SkinnyShortcut.h>
|
#include <SkinnyShortcut.h>
|
||||||
|
|
||||||
#include <QskInputPanel.h>
|
|
||||||
#include <QskDialog.h>
|
#include <QskDialog.h>
|
||||||
#include <QskFocusIndicator.h>
|
#include <QskFocusIndicator.h>
|
||||||
#include <QskLinearBox.h>
|
#include <QskLinearBox.h>
|
||||||
#include <QskListView.h>
|
#include <QskListView.h>
|
||||||
#include <QskTextInput.h>
|
#include <QskTextInput.h>
|
||||||
#include <QskInputPanel.h>
|
#include <QskInputPanel.h>
|
||||||
|
#include <QskInputContext.h>
|
||||||
|
|
||||||
#include <QskWindow.h>
|
#include <QskWindow.h>
|
||||||
#include <QskSetup.h>
|
|
||||||
#include <QskAspect.h>
|
#include <QskAspect.h>
|
||||||
|
|
||||||
#include <QskObjectCounter.h>
|
#include <QskObjectCounter.h>
|
||||||
|
@ -258,17 +257,16 @@ int main( int argc, char* argv[] )
|
||||||
SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts );
|
SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts );
|
||||||
|
|
||||||
#if 1
|
#if 1
|
||||||
// We don't want to have a top level window.
|
// We don't want to have the input panel in a top level window.
|
||||||
qskDialog->setPolicy( QskDialog::EmbeddedBox );
|
qskDialog->setPolicy( QskDialog::EmbeddedBox );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
/*
|
/*
|
||||||
QskInputContext is connected to QskSetup::inputPanelChanged,
|
If no input panel has been assigned QskInputContext creates
|
||||||
making it the system input. If no input panel has been assigned
|
default panel if none has been assigned
|
||||||
QskInputContext would create a window or subwindow on the fly.
|
|
||||||
*/
|
*/
|
||||||
qskSetup->setInputPanel( new QskInputPanel() );
|
QskInputContext::setInputPanel( new QskInputPanel() );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
auto box = new QskLinearBox( Qt::Horizontal );
|
auto box = new QskLinearBox( Qt::Horizontal );
|
||||||
|
|
|
@ -159,9 +159,6 @@ public:
|
||||||
|
|
||||||
Q_PROPERTY( QStringList skinList READ skinList NOTIFY skinListChanged )
|
Q_PROPERTY( QStringList skinList READ skinList NOTIFY skinListChanged )
|
||||||
|
|
||||||
Q_PRIVATE_PROPERTY( setup(), QQuickItem* inputPanel READ inputPanel
|
|
||||||
WRITE setInputPanel NOTIFY inputPanelChanged )
|
|
||||||
|
|
||||||
Q_PRIVATE_PROPERTY( setup(), QskSetupFlagsProvider controlFlags
|
Q_PRIVATE_PROPERTY( setup(), QskSetupFlagsProvider controlFlags
|
||||||
READ controlFlags WRITE setControlFlags NOTIFY controlFlagsChanged )
|
READ controlFlags WRITE setControlFlags NOTIFY controlFlagsChanged )
|
||||||
|
|
||||||
|
@ -177,8 +174,7 @@ public:
|
||||||
|
|
||||||
connect( setup(), &QskSetup::skinChanged,
|
connect( setup(), &QskSetup::skinChanged,
|
||||||
this, &QskMain::skinChanged, Qt::QueuedConnection );
|
this, &QskMain::skinChanged, Qt::QueuedConnection );
|
||||||
connect( setup(), &QskSetup::inputPanelChanged,
|
|
||||||
this, &QskMain::inputPanelChanged );
|
|
||||||
connect( setup(), &QskSetup::controlFlagsChanged,
|
connect( setup(), &QskSetup::controlFlagsChanged,
|
||||||
this, &QskMain::controlFlagsChanged, Qt::QueuedConnection );
|
this, &QskMain::controlFlagsChanged, Qt::QueuedConnection );
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,34 +172,37 @@ void qskForceActiveFocus( QQuickItem* item, Qt::FocusReason reason )
|
||||||
|
|
||||||
void qskUpdateInputMethod( const QQuickItem* item, Qt::InputMethodQueries queries )
|
void qskUpdateInputMethod( const QQuickItem* item, Qt::InputMethodQueries queries )
|
||||||
{
|
{
|
||||||
|
if ( ( item == nullptr ) ||
|
||||||
|
!( item->flags() & QQuickItem::ItemAcceptsInputMethod ) )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto inputMethod = QGuiApplication::inputMethod();
|
auto inputMethod = QGuiApplication::inputMethod();
|
||||||
|
|
||||||
bool doUpdate = item->hasActiveFocus();
|
bool doUpdate = item->hasActiveFocus();
|
||||||
|
|
||||||
if ( !doUpdate )
|
/*
|
||||||
|
We could also get the inputContext from QInputMethodPrivate
|
||||||
|
but for some reason the gcc sanitizer reports errors
|
||||||
|
when using it. So let's go with QGuiApplicationPrivate.
|
||||||
|
*/
|
||||||
|
const auto inputContext =
|
||||||
|
QGuiApplicationPrivate::platformIntegration()->inputContext();
|
||||||
|
|
||||||
|
if ( inputContext && inputContext->isInputPanelVisible() )
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
We could also get the inputContext from QInputMethodPrivate
|
QskInputContext allows to navigate inside the input panel
|
||||||
but for some reason the gcc sanitizer reports errors
|
without losing the connected input item
|
||||||
when using it. So let's go with QGuiApplicationPrivate.
|
|
||||||
*/
|
*/
|
||||||
const auto inputContext =
|
|
||||||
QGuiApplicationPrivate::platformIntegration()->inputContext();
|
|
||||||
|
|
||||||
if ( inputContext && inputContext->isInputPanelVisible() )
|
QQuickItem* inputItem = nullptr;
|
||||||
|
|
||||||
|
if ( QMetaObject::invokeMethod( inputContext, "inputItem",
|
||||||
|
Qt::DirectConnection, Q_RETURN_ARG( QQuickItem*, inputItem ) ) )
|
||||||
{
|
{
|
||||||
/*
|
doUpdate = ( item == inputItem );
|
||||||
QskInputContext allows to navigate inside the input panel
|
|
||||||
without losing the connected input item
|
|
||||||
*/
|
|
||||||
|
|
||||||
QQuickItem* inputItem = nullptr;
|
|
||||||
|
|
||||||
if ( QMetaObject::invokeMethod( inputContext, "inputItem",
|
|
||||||
Qt::DirectConnection, Q_RETURN_ARG( QQuickItem*, inputItem ) ) )
|
|
||||||
{
|
|
||||||
doUpdate = ( item == inputItem );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -126,7 +126,6 @@ public:
|
||||||
|
|
||||||
QskGraphicProviderMap graphicProviders;
|
QskGraphicProviderMap graphicProviders;
|
||||||
|
|
||||||
QPointer< QQuickItem > inputPanel;
|
|
||||||
QskSetup::Flags controlFlags;
|
QskSetup::Flags controlFlags;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -261,20 +260,6 @@ QskGraphicProvider* QskSetup::graphicProvider( const QString& providerId ) const
|
||||||
return m_data->graphicProviders.provider( providerId );
|
return m_data->graphicProviders.provider( providerId );
|
||||||
}
|
}
|
||||||
|
|
||||||
void QskSetup::setInputPanel( QQuickItem* inputPanel )
|
|
||||||
{
|
|
||||||
if ( m_data->inputPanel == inputPanel )
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_data->inputPanel = inputPanel;
|
|
||||||
Q_EMIT inputPanelChanged( m_data->inputPanel );
|
|
||||||
}
|
|
||||||
|
|
||||||
QQuickItem* QskSetup::inputPanel()
|
|
||||||
{
|
|
||||||
return m_data->inputPanel;
|
|
||||||
}
|
|
||||||
|
|
||||||
QLocale QskSetup::inheritedLocale( const QObject* object )
|
QLocale QskSetup::inheritedLocale( const QObject* object )
|
||||||
{
|
{
|
||||||
VisitorLocale visitor;
|
VisitorLocale visitor;
|
||||||
|
|
|
@ -58,9 +58,6 @@ public:
|
||||||
|
|
||||||
QskSkin* skin();
|
QskSkin* skin();
|
||||||
|
|
||||||
void setInputPanel( QQuickItem* );
|
|
||||||
QQuickItem* inputPanel();
|
|
||||||
|
|
||||||
void addGraphicProvider( const QString& providerId, QskGraphicProvider* );
|
void addGraphicProvider( const QString& providerId, QskGraphicProvider* );
|
||||||
QskGraphicProvider* graphicProvider( const QString& providerId ) const;
|
QskGraphicProvider* graphicProvider( const QString& providerId ) const;
|
||||||
|
|
||||||
|
@ -74,7 +71,6 @@ public:
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void skinChanged( QskSkin* );
|
void skinChanged( QskSkin* );
|
||||||
void inputPanelChanged( QQuickItem* );
|
|
||||||
void controlFlagsChanged();
|
void controlFlagsChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -540,6 +540,7 @@ void QskTextInput::setReadOnly( bool on )
|
||||||
|
|
||||||
m_data->textInput->setReadOnly( on );
|
m_data->textInput->setReadOnly( on );
|
||||||
|
|
||||||
|
// we are killing user settings here ?
|
||||||
m_data->textInput->setFlag( QQuickItem::ItemAcceptsInputMethod, !on );
|
m_data->textInput->setFlag( QQuickItem::ItemAcceptsInputMethod, !on );
|
||||||
qskUpdateInputMethod( this, Qt::ImEnabled );
|
qskUpdateInputMethod( this, Qt::ImEnabled );
|
||||||
|
|
||||||
|
@ -645,10 +646,13 @@ QskTextInput::EchoMode QskTextInput::echoMode() const
|
||||||
|
|
||||||
void QskTextInput::setEchoMode( EchoMode mode )
|
void QskTextInput::setEchoMode( EchoMode mode )
|
||||||
{
|
{
|
||||||
m_data->textInput->setEchoMode(
|
if ( mode != echoMode() )
|
||||||
static_cast< QQuickTextInput::EchoMode >( mode ) );
|
{
|
||||||
|
m_data->textInput->setEchoMode(
|
||||||
|
static_cast< QQuickTextInput::EchoMode >( mode ) );
|
||||||
|
|
||||||
qskUpdateInputMethod( this, Qt::ImHints );
|
qskUpdateInputMethod( this, Qt::ImHints );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString QskTextInput::displayText() const
|
QString QskTextInput::displayText() const
|
||||||
|
|
|
@ -4,21 +4,68 @@
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
#include "QskInputContext.h"
|
#include "QskInputContext.h"
|
||||||
|
#include "QskInputPanel.h"
|
||||||
#include "QskTextPredictor.h"
|
#include "QskTextPredictor.h"
|
||||||
#include "QskInputPanel.h"
|
#include "QskInputPanel.h"
|
||||||
#include "QskInputEngine.h"
|
#include "QskInputEngine.h"
|
||||||
|
|
||||||
#include "QskLinearBox.h"
|
#include <QskLinearBox.h>
|
||||||
#include <QskDialog.h>
|
#include <QskDialog.h>
|
||||||
#include <QskPopup.h>
|
#include <QskPopup.h>
|
||||||
#include <QskWindow.h>
|
#include <QskWindow.h>
|
||||||
#include <QskSetup.h>
|
|
||||||
#include <QskEvent.h>
|
#include <QskEvent.h>
|
||||||
|
|
||||||
#include <QHash>
|
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
|
|
||||||
|
QSK_QT_PRIVATE_BEGIN
|
||||||
|
#include <private/qguiapplication_p.h>
|
||||||
|
QSK_QT_PRIVATE_END
|
||||||
|
|
||||||
|
#include <qpa/qplatformintegration.h>
|
||||||
|
|
||||||
|
static QPointer< QskInputPanel > qskInputPanel = nullptr;
|
||||||
|
|
||||||
|
static void qskDeletePanel()
|
||||||
|
{
|
||||||
|
delete qskInputPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qskInputPanelHook()
|
||||||
|
{
|
||||||
|
qAddPostRoutine( qskDeletePanel );
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_COREAPP_STARTUP_FUNCTION( qskInputPanelHook )
|
||||||
|
|
||||||
|
static void qskSetInputPanel( QskInputPanel* inputPanel )
|
||||||
|
{
|
||||||
|
if ( inputPanel == qskInputPanel )
|
||||||
|
return;
|
||||||
|
|
||||||
|
delete qskInputPanel;
|
||||||
|
qskInputPanel = inputPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QskInputContext::setInputPanel( QskInputPanel* inputPanel )
|
||||||
|
{
|
||||||
|
if ( inputPanel == qskInputPanel )
|
||||||
|
return;
|
||||||
|
|
||||||
|
qskSetInputPanel( inputPanel );
|
||||||
|
|
||||||
|
const auto inputContext =
|
||||||
|
QGuiApplicationPrivate::platformIntegration()->inputContext();
|
||||||
|
|
||||||
|
if ( auto context = qobject_cast< QskInputContext* >( inputContext ) )
|
||||||
|
context->hideInputPanel();
|
||||||
|
}
|
||||||
|
|
||||||
|
QskInputPanel* QskInputContext::inputPanel()
|
||||||
|
{
|
||||||
|
return qskInputPanel;
|
||||||
|
}
|
||||||
|
|
||||||
static inline uint qskHashLocale( const QLocale& locale )
|
static inline uint qskHashLocale( const QLocale& locale )
|
||||||
{
|
{
|
||||||
return uint( locale.language() + ( uint( locale.country() ) << 16 ) );
|
return uint( locale.language() + ( uint( locale.country() ) << 16 ) );
|
||||||
|
@ -77,32 +124,20 @@ public:
|
||||||
// item receiving the input
|
// item receiving the input
|
||||||
QPointer< QQuickItem > inputItem;
|
QPointer< QQuickItem > inputItem;
|
||||||
|
|
||||||
// item, wher the user enters texts/keys
|
// popup or window embedding qskInputPanel
|
||||||
QPointer< QQuickItem > inputPanel;
|
|
||||||
|
|
||||||
// popup or window embedding the inputPanel
|
|
||||||
QskPopup* inputPopup = nullptr;
|
QskPopup* inputPopup = nullptr;
|
||||||
QskWindow* inputWindow = nullptr;
|
QskWindow* inputWindow = nullptr;
|
||||||
|
|
||||||
PredictorTable predictorTable;
|
PredictorTable predictorTable;
|
||||||
|
|
||||||
QskInputEngine* engine = nullptr;
|
QskInputEngine* engine = nullptr;
|
||||||
|
|
||||||
// the input panel is embedded in a window
|
|
||||||
bool ownsInputPanelWindow : 1;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
QskInputContext::QskInputContext():
|
QskInputContext::QskInputContext():
|
||||||
m_data( new PrivateData() )
|
m_data( new PrivateData() )
|
||||||
{
|
{
|
||||||
setObjectName( "InputContext" );
|
setObjectName( "InputContext" );
|
||||||
|
|
||||||
m_data->engine = new QskInputEngine( this );
|
m_data->engine = new QskInputEngine( this );
|
||||||
|
|
||||||
connect( qskSetup, &QskSetup::inputPanelChanged,
|
|
||||||
this, &QskInputContext::setInputPanel );
|
|
||||||
|
|
||||||
setInputPanel( qskSetup->inputPanel() );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QskInputContext::~QskInputContext()
|
QskInputContext::~QskInputContext()
|
||||||
|
@ -125,37 +160,6 @@ QQuickItem* QskInputContext::inputItem()
|
||||||
return m_data->inputItem;
|
return m_data->inputItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QskInputContext::setInputItem( QQuickItem* item )
|
|
||||||
{
|
|
||||||
if ( m_data->inputItem == item )
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto panel = qobject_cast< QskInputPanel* >( m_data->inputPanel );
|
|
||||||
|
|
||||||
if ( isInputPanelVisible() )
|
|
||||||
{
|
|
||||||
if ( item == nullptr )
|
|
||||||
{
|
|
||||||
hideInputPanel();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ( panel )
|
|
||||||
panel->attachInputItem( item );
|
|
||||||
|
|
||||||
update( Qt::ImQueryAll );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// no need for updates
|
|
||||||
if ( panel )
|
|
||||||
panel->attachInputItem( nullptr );
|
|
||||||
}
|
|
||||||
|
|
||||||
m_data->inputItem = item;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QskInputContext::update( Qt::InputMethodQueries queries )
|
void QskInputContext::update( Qt::InputMethodQueries queries )
|
||||||
{
|
{
|
||||||
if ( queries & Qt::ImEnabled )
|
if ( queries & Qt::ImEnabled )
|
||||||
|
@ -170,225 +174,172 @@ void QskInputContext::update( Qt::InputMethodQueries queries )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( auto panel = qobject_cast< QskInputPanel* >( m_data->inputPanel ) )
|
if ( qskInputPanel )
|
||||||
panel->processInputMethodQueries( queries );
|
qskInputPanel->processInputMethodQueries( queries );
|
||||||
}
|
}
|
||||||
|
|
||||||
QRectF QskInputContext::keyboardRect() const
|
QRectF QskInputContext::keyboardRect() const
|
||||||
{
|
{
|
||||||
if ( m_data->inputPanel
|
// is this correct and what is this good for ?
|
||||||
&& QskDialog::instance()->policy() != QskDialog::TopLevelWindow )
|
if ( m_data->inputPopup )
|
||||||
{
|
return m_data->inputPopup->geometry();
|
||||||
return qskItemGeometry( m_data->inputPanel );
|
|
||||||
}
|
|
||||||
|
|
||||||
return Inherited::keyboardRect();
|
return Inherited::keyboardRect();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QskInputContext::isAnimating() const
|
bool QskInputContext::isAnimating() const
|
||||||
{
|
{
|
||||||
|
// can be implemented once we have some sliding/fading effects
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QskInputContext::showInputPanel()
|
void QskInputContext::showInputPanel()
|
||||||
{
|
{
|
||||||
auto& inputPanel = m_data->inputPanel;
|
auto focusItem = qobject_cast< QQuickItem* >( qGuiApp->focusObject() );
|
||||||
|
|
||||||
|
if ( focusItem == nullptr )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( ( focusItem == qskInputPanel )
|
||||||
|
|| qskIsAncestorOf( qskInputPanel, focusItem ) )
|
||||||
|
{
|
||||||
|
// ignore: usually the input proxy of the panel
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_data->inputItem = focusItem;
|
||||||
|
|
||||||
auto& inputPopup = m_data->inputPopup;
|
auto& inputPopup = m_data->inputPopup;
|
||||||
auto& inputWindow = m_data->inputWindow;
|
auto& inputWindow = m_data->inputWindow;
|
||||||
|
|
||||||
if ( inputPanel == nullptr )
|
if ( qskInputPanel == nullptr )
|
||||||
|
qskSetInputPanel( new QskInputPanel() );
|
||||||
|
|
||||||
|
connect( qskInputPanel, &QQuickItem::visibleChanged,
|
||||||
|
this, &QPlatformInputContext::emitInputPanelVisibleChanged,
|
||||||
|
Qt::UniqueConnection );
|
||||||
|
|
||||||
|
connect( qskInputPanel, &QskControl::localeChanged,
|
||||||
|
this, &QPlatformInputContext::emitLocaleChanged,
|
||||||
|
Qt::UniqueConnection );
|
||||||
|
|
||||||
|
if ( QskDialog::instance()->policy() == QskDialog::TopLevelWindow )
|
||||||
{
|
{
|
||||||
auto panel = new QskInputPanel();
|
// The input panel is embedded in a top level window
|
||||||
|
|
||||||
panel->setParent( this );
|
|
||||||
panel->setInputProxy( true );
|
|
||||||
|
|
||||||
setInputPanel( panel );
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool isPopupPanel = qobject_cast< QskPopup* >( inputPanel );
|
|
||||||
|
|
||||||
bool useWindow = false;
|
|
||||||
if ( !isPopupPanel )
|
|
||||||
{
|
|
||||||
useWindow = ( QskDialog::instance()->policy() == QskDialog::TopLevelWindow );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( useWindow )
|
|
||||||
{
|
|
||||||
delete inputPopup;
|
delete inputPopup;
|
||||||
|
|
||||||
if ( inputWindow == nullptr )
|
if ( inputWindow == nullptr )
|
||||||
{
|
{
|
||||||
inputWindow = new QskWindow();
|
inputWindow = new QskWindow();
|
||||||
inputWindow->setDeleteOnClose( true );
|
inputWindow->setDeleteOnClose( true );
|
||||||
|
#if 0
|
||||||
inputWindow->setFlags( Qt::Tool | Qt::WindowDoesNotAcceptFocus );
|
inputWindow->setFlags( Qt::Tool | Qt::WindowDoesNotAcceptFocus );
|
||||||
|
#endif
|
||||||
|
|
||||||
inputPanel->setParentItem( inputWindow->contentItem() );
|
qskInputPanel->setParentItem( inputWindow->contentItem() );
|
||||||
|
|
||||||
QSizeF size;
|
|
||||||
if ( auto control = qobject_cast< const QskControl* >( inputPanel ) )
|
|
||||||
size = control->sizeHint();
|
|
||||||
|
|
||||||
if ( size.isEmpty() )
|
|
||||||
size = QSizeF( 800, 240 ); // ### what size?
|
|
||||||
|
|
||||||
inputWindow->resize( size.toSize() );
|
|
||||||
inputWindow->show();
|
|
||||||
|
|
||||||
inputWindow->installEventFilter( this );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QSize size = qskInputPanel->sizeHint().toSize();
|
||||||
|
if ( size.isEmpty() )
|
||||||
|
{
|
||||||
|
// no idea, may be something based on the screen size
|
||||||
|
size = QSize( 800, 240 );
|
||||||
|
}
|
||||||
|
|
||||||
|
inputWindow->resize( size );
|
||||||
|
inputWindow->show();
|
||||||
|
|
||||||
|
inputWindow->installEventFilter( this );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// The input panel is embedded in a popup
|
||||||
|
|
||||||
delete inputWindow;
|
delete inputWindow;
|
||||||
|
|
||||||
if ( inputPopup == nullptr )
|
if ( inputPopup == nullptr )
|
||||||
{
|
{
|
||||||
if ( isPopupPanel )
|
inputPopup = new QskPopup( m_data->inputItem->window()->contentItem() );
|
||||||
{
|
|
||||||
inputPopup = qobject_cast< QskPopup* >( inputPanel );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto popup = new QskPopup( m_data->inputItem->window()->contentItem() );
|
|
||||||
|
|
||||||
popup->setAutoLayoutChildren( true );
|
inputPopup->setAutoLayoutChildren( true );
|
||||||
popup->setTransparentForPositioner( false );
|
inputPopup->setTransparentForPositioner( false );
|
||||||
popup->setOverlay( false );
|
inputPopup->setModal( true );
|
||||||
popup->setModal( true );
|
|
||||||
|
|
||||||
auto box = new QskLinearBox( popup );
|
auto box = new QskLinearBox( inputPopup );
|
||||||
box->addItem( inputPanel );
|
box->addItem( qskInputPanel );
|
||||||
|
|
||||||
if ( auto panel = qobject_cast< QskInputPanel* >( inputPanel ) )
|
/*
|
||||||
{
|
When the panel has an input proxy ( usually a local text input )
|
||||||
if ( panel->hasInputProxy() )
|
we don't need to see the input item and display the overlay
|
||||||
{
|
and align in the center of the window.
|
||||||
popup->setOverlay( true );
|
*/
|
||||||
}
|
const bool hasInputProxy = qskInputPanel->hasInputProxy();
|
||||||
}
|
|
||||||
|
|
||||||
if ( !popup->hasOverlay() )
|
inputPopup->setOverlay( hasInputProxy );
|
||||||
{
|
|
||||||
box->setExtraSpacingAt( Qt::TopEdge | Qt::LeftEdge | Qt::RightEdge );
|
|
||||||
}
|
|
||||||
|
|
||||||
inputPopup = popup;
|
if ( !hasInputProxy )
|
||||||
}
|
box->setExtraSpacingAt( Qt::TopEdge | Qt::LeftEdge | Qt::RightEdge );
|
||||||
|
|
||||||
inputPopup->installEventFilter( this );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( inputPopup->window() == nullptr )
|
|
||||||
{
|
|
||||||
QQuickWindow* window = nullptr;
|
|
||||||
if ( m_data->inputItem )
|
|
||||||
window = m_data->inputItem->window();
|
|
||||||
else
|
|
||||||
window = qobject_cast< QQuickWindow* >( QGuiApplication::focusWindow() );
|
|
||||||
|
|
||||||
if ( window )
|
|
||||||
{
|
|
||||||
inputPopup->setParentItem( window->contentItem() );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inputPopup->setParentItem( m_data->inputItem->window()->contentItem() );
|
||||||
inputPopup->setVisible( true );
|
inputPopup->setVisible( true );
|
||||||
|
inputPopup->installEventFilter( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
update( Qt::ImQueryAll );
|
|
||||||
|
|
||||||
#if 1
|
|
||||||
if ( auto panel = qobject_cast< QskInputPanel* >( m_data->inputPanel ) )
|
|
||||||
panel->updateInputProxy( m_data->inputItem );
|
|
||||||
#endif
|
|
||||||
|
|
||||||
inputPanel->setVisible( true );
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
if ( auto focusItem = inputPanel->nextItemInFocusChain( true ) )
|
|
||||||
qskForceActiveFocus( focusItem, Qt::OtherFocusReason );
|
|
||||||
#endif
|
|
||||||
|
|
||||||
connect( inputPanel->window(), &QskWindow::visibleChanged,
|
|
||||||
this, &QskInputContext::emitInputPanelVisibleChanged );
|
|
||||||
|
|
||||||
updateInputPanel( m_data->inputItem );
|
|
||||||
|
|
||||||
m_data->engine->setPredictor(
|
m_data->engine->setPredictor(
|
||||||
m_data->predictorTable.find( locale() ) );
|
m_data->predictorTable.find( locale() ) );
|
||||||
|
|
||||||
|
qskInputPanel->setLocale( locale() );
|
||||||
|
qskInputPanel->attachInputItem( m_data->inputItem );
|
||||||
|
qskInputPanel->setEngine( m_data->engine );
|
||||||
}
|
}
|
||||||
|
|
||||||
void QskInputContext::hideInputPanel()
|
void QskInputContext::hideInputPanel()
|
||||||
{
|
{
|
||||||
if ( m_data->inputPanel )
|
if ( m_data->inputPopup )
|
||||||
{
|
{
|
||||||
// to get rid of the scene graph nodes
|
|
||||||
m_data->inputPanel->setVisible( false );
|
|
||||||
if ( auto panel = qobject_cast< QskInputPanel* >( m_data->inputPanel ) )
|
|
||||||
panel->setEngine( nullptr );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( m_data->inputPopup == m_data->inputPanel )
|
|
||||||
{
|
|
||||||
m_data->inputPopup->removeEventFilter( this );
|
|
||||||
m_data->inputPopup = nullptr;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ( m_data->inputPopup )
|
|
||||||
{
|
|
||||||
#if 1
|
#if 1
|
||||||
if ( auto focusItem = m_data->inputPopup->scopedFocusItem() )
|
if ( auto focusItem = m_data->inputPopup->scopedFocusItem() )
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
Qt bug: QQuickItem::hasFocus() is not cleared
|
Qt bug: QQuickItem::hasFocus() is not cleared
|
||||||
when the corresponding focusScope gets deleted.
|
when the corresponding focusScope gets deleted.
|
||||||
Usually no problem, but here the focusItem is no
|
Usually no problem, but here the focusItem is no
|
||||||
child and will be reused with a different parent
|
child and will be reused with a different parent
|
||||||
later.
|
later.
|
||||||
*/
|
*/
|
||||||
focusItem->setFocus( false );
|
focusItem->setFocus( false );
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
m_data->inputPopup->deleteLater();
|
m_data->inputPopup->deleteLater();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QskWindow* window = m_data->inputWindow;
|
if ( m_data->inputWindow )
|
||||||
m_data->inputWindow = nullptr;
|
|
||||||
|
|
||||||
if ( window )
|
|
||||||
{
|
{
|
||||||
|
QskWindow* window = m_data->inputWindow;
|
||||||
|
m_data->inputWindow = nullptr;
|
||||||
|
|
||||||
window->removeEventFilter( this );
|
window->removeEventFilter( this );
|
||||||
window->close(); // deleteOnClose is set
|
window->close(); // deleteOnClose is set
|
||||||
}
|
}
|
||||||
|
|
||||||
qGuiApp->removeEventFilter( this );
|
if ( qskInputPanel )
|
||||||
|
{
|
||||||
|
//qskInputPanel->setVisible( false );
|
||||||
|
qskInputPanel->setParentItem( nullptr );
|
||||||
|
qskInputPanel->attachInputItem( nullptr );
|
||||||
|
qskInputPanel->setEngine( nullptr );
|
||||||
|
}
|
||||||
|
|
||||||
updateInputPanel( nullptr );
|
m_data->inputItem = nullptr;
|
||||||
}
|
|
||||||
|
|
||||||
void QskInputContext::updateInputPanel( QQuickItem* inputItem )
|
|
||||||
{
|
|
||||||
auto panel = qobject_cast< QskInputPanel* >( m_data->inputPanel );
|
|
||||||
if ( panel == nullptr )
|
|
||||||
return;
|
|
||||||
|
|
||||||
panel->setLocale( locale() );
|
|
||||||
panel->attachInputItem( inputItem );
|
|
||||||
|
|
||||||
panel->setEngine( inputItem ? m_data->engine : nullptr );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QskInputContext::isInputPanelVisible() const
|
bool QskInputContext::isInputPanelVisible() const
|
||||||
{
|
{
|
||||||
auto panel = m_data->inputPanel;
|
return qskInputPanel && qskInputPanel->isVisible()
|
||||||
|
&& qskInputPanel->window() && qskInputPanel->window()->isVisible();
|
||||||
return panel && panel->isVisible()
|
|
||||||
&& panel->window() && panel->window()->isVisible();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QLocale QskInputContext::locale() const
|
QLocale QskInputContext::locale() const
|
||||||
|
@ -411,48 +362,52 @@ Qt::LayoutDirection QskInputContext::inputDirection() const
|
||||||
|
|
||||||
void QskInputContext::setFocusObject( QObject* focusObject )
|
void QskInputContext::setFocusObject( QObject* focusObject )
|
||||||
{
|
{
|
||||||
auto focusItem = qobject_cast< QQuickItem* >( focusObject );
|
if ( m_data->inputItem == nullptr || m_data->inputItem == focusObject )
|
||||||
|
|
||||||
if ( focusItem == nullptr )
|
|
||||||
{
|
{
|
||||||
if ( m_data->inputItem )
|
// we don't care
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool doTerminate = true;
|
||||||
|
|
||||||
|
if ( focusObject == nullptr && m_data->inputPopup )
|
||||||
|
{
|
||||||
|
if ( const auto window = m_data->inputItem->window() )
|
||||||
{
|
{
|
||||||
if ( m_data->inputItem->window() == QGuiApplication::focusWindow() )
|
auto focusItem = window->contentItem()->scopedFocusItem();
|
||||||
setInputItem( nullptr );
|
if ( focusItem == m_data->inputPopup )
|
||||||
|
doTerminate = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if ( doTerminate )
|
||||||
{
|
{
|
||||||
/*
|
if ( m_data->inputWindow )
|
||||||
Do not change the input item when
|
|
||||||
navigating to or inside the input popup/window
|
|
||||||
*/
|
|
||||||
|
|
||||||
bool isAccepted = ( m_data->inputItem == nullptr );
|
|
||||||
|
|
||||||
if ( !isAccepted )
|
|
||||||
{
|
{
|
||||||
if ( m_data->inputWindow )
|
auto focusWindow = QGuiApplication::focusWindow();
|
||||||
|
|
||||||
|
if ( focusWindow == nullptr ||
|
||||||
|
QGuiApplication::focusWindow() == m_data->inputWindow )
|
||||||
{
|
{
|
||||||
if ( focusItem->window() != m_data->inputWindow )
|
doTerminate = false;
|
||||||
isAccepted = true;
|
|
||||||
}
|
|
||||||
else if ( m_data->inputPopup )
|
|
||||||
{
|
|
||||||
if ( ( focusItem != m_data->inputPopup )
|
|
||||||
&& !qskIsAncestorOf( m_data->inputPopup, focusItem ) )
|
|
||||||
{
|
|
||||||
isAccepted = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
isAccepted = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if ( m_data->inputPopup )
|
||||||
|
{
|
||||||
|
auto focusItem = qobject_cast< QQuickItem* >( focusObject );
|
||||||
|
|
||||||
if ( isAccepted )
|
if ( ( focusItem == m_data->inputPopup )
|
||||||
setInputItem( focusItem );
|
|| qskIsAncestorOf( m_data->inputPopup, focusItem ) )
|
||||||
|
{
|
||||||
|
doTerminate = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( doTerminate )
|
||||||
|
{
|
||||||
|
hideInputPanel();
|
||||||
|
m_data->inputItem = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -484,51 +439,17 @@ void QskInputContext::invokeAction( QInputMethod::Action, int )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void QskInputContext::setInputPanel( QQuickItem* inputPanel )
|
|
||||||
{
|
|
||||||
if ( m_data->inputPanel == inputPanel )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ( m_data->inputPanel )
|
|
||||||
{
|
|
||||||
m_data->inputPanel->disconnect( this );
|
|
||||||
|
|
||||||
if ( m_data->inputPanel->parent() == this )
|
|
||||||
{
|
|
||||||
delete m_data->inputPanel;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_data->inputPanel->setParentItem( nullptr );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_data->inputPanel = inputPanel;
|
|
||||||
m_data->ownsInputPanelWindow = false;
|
|
||||||
|
|
||||||
if ( inputPanel )
|
|
||||||
{
|
|
||||||
if ( inputPanel->parent() == nullptr )
|
|
||||||
inputPanel->setParent( this );
|
|
||||||
|
|
||||||
connect( inputPanel, &QQuickItem::visibleChanged,
|
|
||||||
this, &QPlatformInputContext::emitInputPanelVisibleChanged );
|
|
||||||
|
|
||||||
if ( auto control = qobject_cast< QskControl* >( inputPanel ) )
|
|
||||||
{
|
|
||||||
connect( control, &QskControl::localeChanged,
|
|
||||||
this, &QPlatformInputContext::emitLocaleChanged );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QskInputContext::reset()
|
void QskInputContext::reset()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void QskInputContext::commit()
|
void QskInputContext::commit()
|
||||||
{
|
{
|
||||||
// called on focus changes
|
/*
|
||||||
|
commit is called, when the input item loses the focus.
|
||||||
|
As it it should be possible to navigate inside of the
|
||||||
|
inputPanel this is no valid reason to hide the panel.
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QskInputContext::eventFilter( QObject* object, QEvent* event )
|
bool QskInputContext::eventFilter( QObject* object, QEvent* event )
|
||||||
|
@ -539,15 +460,15 @@ bool QskInputContext::eventFilter( QObject* object, QEvent* event )
|
||||||
{
|
{
|
||||||
case QEvent::Move:
|
case QEvent::Move:
|
||||||
{
|
{
|
||||||
if ( m_data->inputPanel )
|
if ( qskInputPanel )
|
||||||
emitKeyboardRectChanged();
|
emitKeyboardRectChanged();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QEvent::Resize:
|
case QEvent::Resize:
|
||||||
{
|
{
|
||||||
if ( m_data->inputPanel )
|
if ( qskInputPanel )
|
||||||
m_data->inputPanel->setSize( m_data->inputWindow->size() );
|
qskInputPanel->setSize( m_data->inputWindow->size() );
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
class QskTextPredictor;
|
class QskTextPredictor;
|
||||||
|
class QskInputPanel;
|
||||||
class QQuickItem;
|
class QQuickItem;
|
||||||
|
|
||||||
class QSK_EXPORT QskInputContext : public QPlatformInputContext
|
class QSK_EXPORT QskInputContext : public QPlatformInputContext
|
||||||
|
@ -51,19 +52,16 @@ public:
|
||||||
|
|
||||||
virtual bool filterEvent( const QEvent* ) override;
|
virtual bool filterEvent( const QEvent* ) override;
|
||||||
|
|
||||||
|
static void setInputPanel( QskInputPanel* );
|
||||||
|
static QskInputPanel* inputPanel();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void updateInputPanel( QQuickItem* inputItem );
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void setInputPanel( QQuickItem* );
|
|
||||||
|
|
||||||
virtual bool eventFilter( QObject*, QEvent* ) override;
|
virtual bool eventFilter( QObject*, QEvent* ) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setInputItem( QQuickItem* );
|
|
||||||
|
|
||||||
class PrivateData;
|
class PrivateData;
|
||||||
std::unique_ptr< PrivateData > m_data;
|
std::unique_ptr< PrivateData > m_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -18,10 +18,27 @@
|
||||||
#include <QInputMethodQueryEvent>
|
#include <QInputMethodQueryEvent>
|
||||||
#include <QTextCharFormat>
|
#include <QTextCharFormat>
|
||||||
|
|
||||||
static inline void qskSendText( QQuickItem* inputItem,
|
static inline void qskSendReplaceText( QQuickItem* receiver, const QString& text )
|
||||||
|
{
|
||||||
|
if ( receiver == nullptr )
|
||||||
|
return;
|
||||||
|
|
||||||
|
QInputMethodEvent::Attribute attribute(
|
||||||
|
QInputMethodEvent::Selection, 0, 32767, QVariant() );
|
||||||
|
|
||||||
|
QInputMethodEvent event1( QString(), { attribute } );
|
||||||
|
QCoreApplication::sendEvent( receiver, &event1 );
|
||||||
|
|
||||||
|
QInputMethodEvent event2;
|
||||||
|
event2.setCommitString( text );
|
||||||
|
|
||||||
|
QCoreApplication::sendEvent( receiver, &event2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void qskSendText( QQuickItem* receiver,
|
||||||
const QString& text, bool isFinal )
|
const QString& text, bool isFinal )
|
||||||
{
|
{
|
||||||
if ( inputItem == nullptr )
|
if ( receiver == nullptr )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if ( isFinal )
|
if ( isFinal )
|
||||||
|
@ -29,7 +46,7 @@ static inline void qskSendText( QQuickItem* inputItem,
|
||||||
QInputMethodEvent event;
|
QInputMethodEvent event;
|
||||||
event.setCommitString( text );
|
event.setCommitString( text );
|
||||||
|
|
||||||
QCoreApplication::sendEvent( inputItem, &event );
|
QCoreApplication::sendEvent( receiver, &event );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -41,20 +58,20 @@ static inline void qskSendText( QQuickItem* inputItem,
|
||||||
|
|
||||||
QInputMethodEvent event( text, { attribute } );
|
QInputMethodEvent event( text, { attribute } );
|
||||||
|
|
||||||
QCoreApplication::sendEvent( inputItem, &event );
|
QCoreApplication::sendEvent( receiver, &event );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void qskSendKey( QQuickItem* inputItem, int key )
|
static inline void qskSendKey( QQuickItem* receiver, int key )
|
||||||
{
|
{
|
||||||
if ( inputItem == nullptr )
|
if ( receiver == nullptr )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
QKeyEvent keyPress( QEvent::KeyPress, key, Qt::NoModifier );
|
QKeyEvent keyPress( QEvent::KeyPress, key, Qt::NoModifier );
|
||||||
QCoreApplication::sendEvent( inputItem, &keyPress );
|
QCoreApplication::sendEvent( receiver, &keyPress );
|
||||||
|
|
||||||
QKeyEvent keyRelease( QEvent::KeyRelease, key, Qt::NoModifier );
|
QKeyEvent keyRelease( QEvent::KeyRelease, key, Qt::NoModifier );
|
||||||
QCoreApplication::sendEvent( inputItem, &keyRelease );
|
QCoreApplication::sendEvent( receiver, &keyRelease );
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
|
@ -65,6 +82,7 @@ namespace
|
||||||
TextInput( QQuickItem* parentItem = nullptr ):
|
TextInput( QQuickItem* parentItem = nullptr ):
|
||||||
QskTextInput( parentItem )
|
QskTextInput( parentItem )
|
||||||
{
|
{
|
||||||
|
setObjectName( "InputPanelInputProxy" );
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -75,10 +93,18 @@ class QskInputPanel::PrivateData
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PrivateData():
|
PrivateData():
|
||||||
|
inputHints( 0 ),
|
||||||
|
maxChars( -1 ),
|
||||||
|
hasPrediction( true ),
|
||||||
hasInputProxy( true )
|
hasInputProxy( true )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QQuickItem* receiverItem()
|
||||||
|
{
|
||||||
|
return hasInputProxy ? inputProxy : inputItem;
|
||||||
|
}
|
||||||
|
|
||||||
QPointer< QskInputEngine > engine;
|
QPointer< QskInputEngine > engine;
|
||||||
QPointer< QQuickItem > inputItem;
|
QPointer< QQuickItem > inputItem;
|
||||||
|
|
||||||
|
@ -88,6 +114,10 @@ public:
|
||||||
QskInputPredictionBar* predictionBar;
|
QskInputPredictionBar* predictionBar;
|
||||||
QskVirtualKeyboard* keyboard;
|
QskVirtualKeyboard* keyboard;
|
||||||
|
|
||||||
|
Qt::InputMethodHints inputHints;
|
||||||
|
int maxChars;
|
||||||
|
|
||||||
|
bool hasPrediction : 1;
|
||||||
bool hasInputProxy : 1;
|
bool hasInputProxy : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -146,7 +176,8 @@ void QskInputPanel::setEngine( QskInputEngine* engine )
|
||||||
this, &QskInputPanel::updatePredictionBar );
|
this, &QskInputPanel::updatePredictionBar );
|
||||||
}
|
}
|
||||||
|
|
||||||
m_data->predictionBar->setVisible( engine && engine->predictor() );
|
m_data->predictionBar->setVisible(
|
||||||
|
m_data->hasPrediction && engine && engine->predictor() );
|
||||||
}
|
}
|
||||||
|
|
||||||
void QskInputPanel::attachInputItem( QQuickItem* item )
|
void QskInputPanel::attachInputItem( QQuickItem* item )
|
||||||
|
@ -165,6 +196,9 @@ void QskInputPanel::attachInputItem( QQuickItem* item )
|
||||||
queries &= ~Qt::ImEnabled;
|
queries &= ~Qt::ImEnabled;
|
||||||
|
|
||||||
processInputMethodQueries( queries );
|
processInputMethodQueries( queries );
|
||||||
|
|
||||||
|
if ( m_data->hasInputProxy )
|
||||||
|
m_data->inputProxy->setEditing( true );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,56 +268,6 @@ void QskInputPanel::setInputProxy( bool on )
|
||||||
prompt->setVisible( false );
|
prompt->setVisible( false );
|
||||||
}
|
}
|
||||||
|
|
||||||
void QskInputPanel::updateInputProxy( const QQuickItem* inputItem )
|
|
||||||
{
|
|
||||||
if ( inputItem == nullptr )
|
|
||||||
return;
|
|
||||||
|
|
||||||
QInputMethodQueryEvent event( Qt::ImQueryAll );
|
|
||||||
QCoreApplication::sendEvent( const_cast< QQuickItem* >( inputItem ), &event );
|
|
||||||
|
|
||||||
const auto proxy = m_data->inputProxy;
|
|
||||||
|
|
||||||
if ( event.queries() & Qt::ImHints )
|
|
||||||
{
|
|
||||||
const auto hints = static_cast< Qt::InputMethodHints >(
|
|
||||||
event.value( Qt::ImHints ).toInt() );
|
|
||||||
|
|
||||||
const auto echoMode = ( hints & Qt::ImhHiddenText )
|
|
||||||
? QskTextInput::PasswordEchoOnEdit : QskTextInput::Normal;
|
|
||||||
|
|
||||||
proxy->setEchoMode( echoMode );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( event.queries() & Qt::ImSurroundingText )
|
|
||||||
{
|
|
||||||
const auto text = event.value( Qt::ImSurroundingText ).toString();
|
|
||||||
proxy->setText( text );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( event.queries() & Qt::ImCursorPosition )
|
|
||||||
{
|
|
||||||
const auto pos = event.value( Qt::ImCursorPosition ).toInt();
|
|
||||||
proxy->setCursorPosition( pos );
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
if ( event.queries() & Qt::ImCurrentSelection )
|
|
||||||
{
|
|
||||||
const auto text = event.value( Qt::ImCursorPosition ).toString();
|
|
||||||
if ( !text.isEmpty() )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if ( event.queries() & Qt::ImMaximumTextLength )
|
|
||||||
{
|
|
||||||
const auto length = event.value( Qt::ImMaximumTextLength ).toInt();
|
|
||||||
proxy->setMaxLength( length );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QskInputPanel::commitPredictiveText( int index )
|
void QskInputPanel::commitPredictiveText( int index )
|
||||||
{
|
{
|
||||||
m_data->predictionBar->setPrediction( QVector< QString >() );
|
m_data->predictionBar->setPrediction( QVector< QString >() );
|
||||||
|
@ -302,34 +286,23 @@ void QskInputPanel::commitKey( int key )
|
||||||
if ( m_data->engine == nullptr || m_data->inputItem == nullptr )
|
if ( m_data->engine == nullptr || m_data->inputItem == nullptr )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto engine = m_data->engine;
|
|
||||||
auto inputItem = m_data->inputItem;
|
|
||||||
|
|
||||||
QInputMethodQueryEvent event( Qt::ImHints );
|
|
||||||
QCoreApplication::sendEvent( inputItem, &event );
|
|
||||||
|
|
||||||
const auto inputHints = static_cast< Qt::InputMethodHints >(
|
|
||||||
event.value( Qt::ImHints ).toInt() );
|
|
||||||
|
|
||||||
int spaceLeft = -1;
|
int spaceLeft = -1;
|
||||||
|
|
||||||
if ( !( inputHints & Qt::ImhMultiLine ) )
|
if ( !( m_data->inputHints & Qt::ImhMultiLine ) )
|
||||||
{
|
{
|
||||||
QInputMethodQueryEvent event(
|
auto receiver = m_data->receiverItem();
|
||||||
Qt::ImSurroundingText | Qt::ImMaximumTextLength );
|
|
||||||
|
|
||||||
QCoreApplication::sendEvent( inputItem, &event );
|
if ( m_data->maxChars >= 0 )
|
||||||
|
|
||||||
const int max = event.value( Qt::ImMaximumTextLength ).toInt();
|
|
||||||
|
|
||||||
if ( max > 0 )
|
|
||||||
{
|
{
|
||||||
|
QInputMethodQueryEvent event( Qt::ImSurroundingText );
|
||||||
|
QCoreApplication::sendEvent( receiver, &event );
|
||||||
|
|
||||||
const auto text = event.value( Qt::ImSurroundingText ).toString();
|
const auto text = event.value( Qt::ImSurroundingText ).toString();
|
||||||
spaceLeft = max - text.length();
|
spaceLeft = m_data->maxChars - text.length();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
processKey( key, inputHints, spaceLeft );
|
processKey( key, m_data->inputHints, spaceLeft );
|
||||||
}
|
}
|
||||||
|
|
||||||
void QskInputPanel::processKey( int key,
|
void QskInputPanel::processKey( int key,
|
||||||
|
@ -338,16 +311,35 @@ void QskInputPanel::processKey( int key,
|
||||||
const auto result = m_data->engine->processKey( key, inputHints, spaceLeft );
|
const auto result = m_data->engine->processKey( key, inputHints, spaceLeft );
|
||||||
|
|
||||||
auto inputItem = m_data->inputItem;
|
auto inputItem = m_data->inputItem;
|
||||||
|
auto inputProxy = m_data->inputProxy;
|
||||||
|
|
||||||
if ( result.key )
|
if ( result.key )
|
||||||
{
|
{
|
||||||
// sending a control key
|
switch( result.key )
|
||||||
qskSendKey( inputItem, result.key );
|
{
|
||||||
|
case Qt::Key_Return:
|
||||||
|
{
|
||||||
|
if ( m_data->hasInputProxy )
|
||||||
|
qskSendReplaceText( inputItem, inputProxy->text() );
|
||||||
|
|
||||||
|
qskSendKey( inputItem, result.key );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Qt::Key_Escape:
|
||||||
|
{
|
||||||
|
qskSendKey( inputItem, result.key );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
qskSendKey( m_data->receiverItem(), result.key );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if ( !result.text.isEmpty() )
|
else if ( !result.text.isEmpty() )
|
||||||
{
|
{
|
||||||
// changing the current text
|
// changing the current text
|
||||||
qskSendText( inputItem, result.text, result.isFinal );
|
qskSendText( m_data->receiverItem(), result.text, result.isFinal );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,73 +348,182 @@ void QskInputPanel::processInputMethodQueries( Qt::InputMethodQueries queries )
|
||||||
if ( m_data->inputItem == nullptr )
|
if ( m_data->inputItem == nullptr )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/*
|
QInputMethodQueryEvent event( queries );
|
||||||
adjust the input panel to information provided from the input item
|
QCoreApplication::sendEvent( m_data->inputItem, &event );
|
||||||
*/
|
|
||||||
|
|
||||||
QInputMethodQueryEvent queryEvent( queries );
|
if ( queries & Qt::ImHints )
|
||||||
QCoreApplication::sendEvent( m_data->inputItem, &queryEvent );
|
|
||||||
|
|
||||||
if ( queryEvent.queries() & Qt::ImHints )
|
|
||||||
{
|
{
|
||||||
/*
|
bool hasPrediction = true;
|
||||||
ImhHiddenText = 0x1, // might need to disable certain checks
|
bool hasEchoMode = false;
|
||||||
ImhSensitiveData = 0x2, // shouldn't change anything
|
|
||||||
ImhNoAutoUppercase = 0x4, // if we support auto uppercase, disable it
|
|
||||||
ImhPreferNumbers = 0x8, // default to number keyboard
|
|
||||||
ImhPreferUppercase = 0x10, // start with shift on
|
|
||||||
ImhPreferLowercase = 0x20, // start with shift off
|
|
||||||
ImhNoPredictiveText = 0x40, // not use predictive text
|
|
||||||
|
|
||||||
ImhDate = 0x80, // ignored for now (no date keyboard)
|
|
||||||
ImhTime = 0x100, // ignored for know (no time keyboard)
|
|
||||||
|
|
||||||
ImhPreferLatin = 0x200, // can be used to launch chinese kb in english mode
|
|
||||||
|
|
||||||
ImhMultiLine = 0x400, // not useful?
|
|
||||||
|
|
||||||
ImhDigitsOnly // default to number keyboard, disable other keys
|
|
||||||
ImhFormattedNumbersOnly // hard to say
|
|
||||||
ImhUppercaseOnly // caps-lock, disable shift
|
|
||||||
ImhLowercaseOnly // disable shift
|
|
||||||
ImhDialableCharactersOnly // dial pad (calculator?)
|
|
||||||
ImhEmailCharactersOnly // disable certain symbols (email-only kb?)
|
|
||||||
ImhUrlCharactersOnly // disable certain symbols (url-only kb?)
|
|
||||||
ImhLatinOnly // disable chinese input
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
const auto hints = static_cast< Qt::InputMethodHints >(
|
const auto hints = static_cast< Qt::InputMethodHints >(
|
||||||
queryEvent.value( Qt::ImHints ).toInt() );
|
event.value( Qt::ImHints ).toInt() );
|
||||||
|
|
||||||
#endif
|
if ( hints & Qt::ImhHiddenText )
|
||||||
|
{
|
||||||
|
hasEchoMode = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( hints & Qt::ImhSensitiveData )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( hints & Qt::ImhNoAutoUppercase )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( hints & Qt::ImhPreferNumbers )
|
||||||
|
{
|
||||||
|
// we should start with having the number keys being visible
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( hints & Qt::ImhPreferUppercase )
|
||||||
|
{
|
||||||
|
// we should start with having the upper keys being visible
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( hints & Qt::ImhPreferLowercase )
|
||||||
|
{
|
||||||
|
// we should start with having the upper keys being visible
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( hints & Qt::ImhNoPredictiveText )
|
||||||
|
{
|
||||||
|
hasPrediction = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( hints & Qt::ImhDate )
|
||||||
|
{
|
||||||
|
// we should have a date/time input
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( hints & Qt::ImhTime )
|
||||||
|
{
|
||||||
|
// we should have a date/time input
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( hints & Qt::ImhPreferLatin )
|
||||||
|
{
|
||||||
|
// conflicts with our concept of using the locale
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( hints & Qt::ImhMultiLine )
|
||||||
|
{
|
||||||
|
// we need an implementation of QskTextEdit for this
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( hints & Qt::ImhDigitsOnly )
|
||||||
|
{
|
||||||
|
// using a numpad instead of our virtual keyboard
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( hints & Qt::ImhFormattedNumbersOnly )
|
||||||
|
{
|
||||||
|
// a numpad with decimal point and minus sign
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( hints & Qt::ImhUppercaseOnly )
|
||||||
|
{
|
||||||
|
// locking all other keys
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( hints & Qt::ImhLowercaseOnly )
|
||||||
|
{
|
||||||
|
// locking all other keys
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( hints & Qt::ImhDialableCharactersOnly )
|
||||||
|
{
|
||||||
|
// characters suitable for phone dialing
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( hints & Qt::ImhEmailCharactersOnly )
|
||||||
|
{
|
||||||
|
// characters suitable for email addresses
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( hints & Qt::ImhUrlCharactersOnly )
|
||||||
|
{
|
||||||
|
// characters suitable for URLs
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( hints & Qt::ImhLatinOnly )
|
||||||
|
{
|
||||||
|
// locking all other keys
|
||||||
|
}
|
||||||
|
|
||||||
|
m_data->hasPrediction = hasPrediction;
|
||||||
|
|
||||||
|
m_data->predictionBar->setVisible(
|
||||||
|
hasPrediction && m_data->engine && m_data->engine->predictor() );
|
||||||
|
|
||||||
|
m_data->inputProxy->setEchoMode(
|
||||||
|
hasEchoMode ? QskTextInput::PasswordEchoOnEdit : QskTextInput::Normal );
|
||||||
|
|
||||||
|
m_data->inputHints = hints;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
if ( queries & Qt::ImPreferredLanguage )
|
||||||
if ( queryEvent.queries() & Qt::ImPreferredLanguage )
|
|
||||||
{
|
{
|
||||||
|
// already handled by the input context
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( queries & Qt::ImMaximumTextLength )
|
||||||
|
{
|
||||||
|
// needs to be handled before Qt::ImCursorPosition !
|
||||||
|
|
||||||
|
m_data->maxChars = event.value( Qt::ImMaximumTextLength ).toInt();
|
||||||
|
#if 1
|
||||||
|
if ( m_data->maxChars >= 32767 )
|
||||||
|
m_data->maxChars = -1;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if ( m_data->hasInputProxy )
|
||||||
|
m_data->inputProxy->setMaxLength( m_data->maxChars );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ( queries & Qt::ImSurroundingText )
|
||||||
|
{
|
||||||
|
if ( m_data->hasInputProxy )
|
||||||
|
{
|
||||||
|
const auto text = event.value( Qt::ImSurroundingText ).toString();
|
||||||
|
m_data->inputProxy->setText( text );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( queries & Qt::ImCursorPosition )
|
||||||
|
{
|
||||||
|
if ( m_data->hasInputProxy )
|
||||||
|
{
|
||||||
|
const auto pos = event.value( Qt::ImCursorPosition ).toInt();
|
||||||
|
m_data->inputProxy->setCursorPosition( pos );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( queries & Qt::ImCurrentSelection )
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
const auto text = event.value( Qt::ImCurrentSelection ).toString();
|
||||||
|
if ( !text.isEmpty() )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
Qt::ImMicroFocus
|
Qt::ImMicroFocus
|
||||||
Qt::ImCursorRectangle
|
Qt::ImCursorRectangle
|
||||||
Qt::ImFont
|
Qt::ImFont
|
||||||
Qt::ImCursorPosition
|
|
||||||
Qt::ImSurroundingText // important for chinese input
|
|
||||||
Qt::ImCurrentSelection // important for prediction
|
|
||||||
Qt::ImMaximumTextLength // should be monitored
|
|
||||||
Qt::ImAnchorPosition
|
Qt::ImAnchorPosition
|
||||||
|
|
||||||
Qt::ImAbsolutePosition
|
Qt::ImAbsolutePosition
|
||||||
Qt::ImTextBeforeCursor // important for chinese
|
Qt::ImTextBeforeCursor
|
||||||
Qt::ImTextAfterCursor // important for chinese
|
Qt::ImTextAfterCursor
|
||||||
Qt::ImPlatformData // hard to say...
|
Qt::ImPlatformData // hard to say...
|
||||||
Qt::ImEnterKeyType
|
Qt::ImEnterKeyType
|
||||||
Qt::ImAnchorRectangle
|
Qt::ImAnchorRectangle
|
||||||
Qt::ImInputItemClipRectangle // could be used for the geometry of the panel
|
Qt::ImInputItemClipRectangle
|
||||||
*/
|
*/
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QskInputPanel::keyPressEvent( QKeyEvent* event )
|
void QskInputPanel::keyPressEvent( QKeyEvent* event )
|
||||||
|
|
|
@ -16,7 +16,7 @@ class QLocale;
|
||||||
|
|
||||||
template class QVector< QString >;
|
template class QVector< QString >;
|
||||||
|
|
||||||
class QSK_EXPORT QskInputPanel: public QskBox
|
class QSK_EXPORT QskInputPanel : public QskBox
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
@ -49,7 +49,6 @@ public:
|
||||||
virtual QskAspect::Subcontrol effectiveSubcontrol(
|
virtual QskAspect::Subcontrol effectiveSubcontrol(
|
||||||
QskAspect::Subcontrol ) const override;
|
QskAspect::Subcontrol ) const override;
|
||||||
|
|
||||||
void updateInputProxy( const QQuickItem* );
|
|
||||||
virtual void processInputMethodQueries( Qt::InputMethodQueries );
|
virtual void processInputMethodQueries( Qt::InputMethodQueries );
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
|
|
Loading…
Reference in New Issue