2018-02-06 14:55:35 +01:00
|
|
|
/******************************************************************************
|
|
|
|
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
|
|
|
* This file may be used under the terms of the QSkinny License, Version 1.0
|
|
|
|
*****************************************************************************/
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
#include "QskInputContext.h"
|
|
|
|
|
|
|
|
#include "QskInputCompositionModel.h"
|
|
|
|
#include "QskPinyinCompositionModel.h"
|
2018-04-09 10:05:59 +02:00
|
|
|
#include "QskHunspellCompositionModel.h"
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2018-04-10 16:51:35 +02:00
|
|
|
#include "QskInputPanel.h"
|
2018-04-12 12:03:51 +02:00
|
|
|
#include "QskLinearBox.h"
|
2017-07-21 18:21:34 +02:00
|
|
|
#include <QskDialog.h>
|
2018-04-11 17:33:43 +02:00
|
|
|
#include <QskPopup.h>
|
2017-07-21 18:21:34 +02:00
|
|
|
#include <QskWindow.h>
|
2018-04-04 20:19:47 +02:00
|
|
|
#include <QskControl.h>
|
2017-07-21 18:21:34 +02:00
|
|
|
#include <QskSetup.h>
|
2018-04-11 17:33:43 +02:00
|
|
|
#include <QskEvent.h>
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2018-04-20 08:52:26 +02:00
|
|
|
#include <QTextCharFormat>
|
2018-04-01 12:47:44 +02:00
|
|
|
#include <QHash>
|
|
|
|
#include <QPointer>
|
2018-04-20 08:52:26 +02:00
|
|
|
#include <QGuiApplication>
|
|
|
|
|
|
|
|
static inline QString qskKeyString( int keyCode )
|
|
|
|
{
|
|
|
|
// Special case entry codes here, else default to the symbol
|
|
|
|
switch ( keyCode )
|
|
|
|
{
|
|
|
|
case Qt::Key_Shift:
|
|
|
|
case Qt::Key_CapsLock:
|
|
|
|
case Qt::Key_Mode_switch:
|
|
|
|
case Qt::Key_Backspace:
|
|
|
|
case Qt::Key_Muhenkan:
|
|
|
|
return QString();
|
|
|
|
|
|
|
|
case Qt::Key_Return:
|
|
|
|
case Qt::Key_Kanji:
|
|
|
|
return QChar( QChar::CarriageReturn );
|
|
|
|
|
|
|
|
case Qt::Key_Space:
|
|
|
|
return QChar( QChar::Space );
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return QChar( keyCode );
|
|
|
|
}
|
|
|
|
|
2018-04-10 16:51:35 +02:00
|
|
|
static void qskSetLocale( QQuickItem* inputPanel, const QLocale& locale )
|
2018-04-04 20:19:47 +02:00
|
|
|
{
|
|
|
|
if ( auto control = qobject_cast< QskControl* >( inputPanel ) )
|
|
|
|
{
|
|
|
|
control->setLocale( locale );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const auto mo = inputPanel->metaObject();
|
|
|
|
|
|
|
|
const auto property = mo->property( mo->indexOfProperty( "locale" ) );
|
|
|
|
if ( property.isWritable() )
|
|
|
|
property.write( inputPanel, locale );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-10 16:51:35 +02:00
|
|
|
static QLocale qskLocale( const QQuickItem* inputPanel )
|
2018-04-04 20:19:47 +02:00
|
|
|
{
|
|
|
|
if ( inputPanel == nullptr )
|
|
|
|
return QLocale();
|
|
|
|
|
|
|
|
if ( auto control = qobject_cast< const QskControl* >( inputPanel ) )
|
|
|
|
return control->locale();
|
|
|
|
|
|
|
|
return inputPanel->property( "locale" ).toLocale();
|
|
|
|
}
|
|
|
|
|
2018-04-10 16:51:35 +02:00
|
|
|
static void qskSetCandidatesEnabled( QQuickItem* inputPanel, bool on )
|
2018-04-04 20:19:47 +02:00
|
|
|
{
|
2018-04-10 16:51:35 +02:00
|
|
|
if ( inputPanel == nullptr )
|
|
|
|
return;
|
2018-04-04 20:19:47 +02:00
|
|
|
|
2018-04-10 16:51:35 +02:00
|
|
|
if ( auto panel = qobject_cast< QskInputPanel* >( inputPanel ) )
|
|
|
|
{
|
|
|
|
panel->setCandidatesEnabled( on );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
QMetaObject::invokeMethod( inputPanel, "setCandidatesEnabled",
|
|
|
|
Qt::DirectConnection, Q_ARG( bool, on ) );
|
|
|
|
}
|
|
|
|
}
|
2018-04-04 20:19:47 +02:00
|
|
|
|
2018-04-10 16:51:35 +02:00
|
|
|
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 ) );
|
|
|
|
}
|
2018-04-04 20:19:47 +02:00
|
|
|
}
|
|
|
|
|
2018-04-20 08:52:26 +02:00
|
|
|
static inline uint qskHashLocale( const QLocale& locale )
|
|
|
|
{
|
|
|
|
return uint( locale.language() + uint( locale.country() ) << 16 );
|
|
|
|
}
|
|
|
|
|
2018-04-01 12:47:44 +02:00
|
|
|
class QskInputContext::PrivateData
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-04-01 12:47:44 +02:00
|
|
|
public:
|
2018-04-11 17:33:43 +02:00
|
|
|
// item receiving the input
|
2018-04-01 12:47:44 +02:00
|
|
|
QPointer< QQuickItem > inputItem;
|
2018-04-11 17:33:43 +02:00
|
|
|
|
|
|
|
// item, wher the user enters texts/keys
|
2018-04-12 13:32:28 +02:00
|
|
|
QPointer< QQuickItem > inputPanel;
|
2018-04-11 17:33:43 +02:00
|
|
|
|
|
|
|
// popup or window embedding the inputPanel
|
2018-04-12 12:03:51 +02:00
|
|
|
QskPopup* inputPopup = nullptr;
|
|
|
|
QskWindow* inputWindow = nullptr;
|
2018-04-04 20:19:47 +02:00
|
|
|
|
2018-04-20 08:52:26 +02:00
|
|
|
QHash< uint, QskInputCompositionModel* > compositionModels;
|
2018-04-05 14:18:15 +02:00
|
|
|
|
2018-04-23 14:06:40 +02:00
|
|
|
QString preedit;
|
|
|
|
|
2018-04-05 14:18:15 +02:00
|
|
|
// the input panel is embedded in a window
|
|
|
|
bool ownsInputPanelWindow : 1;
|
2018-04-01 12:47:44 +02:00
|
|
|
};
|
|
|
|
|
2018-04-04 15:19:51 +02:00
|
|
|
QskInputContext::QskInputContext():
|
2018-04-01 12:47:44 +02:00
|
|
|
m_data( new PrivateData() )
|
|
|
|
{
|
2018-04-03 20:15:20 +02:00
|
|
|
setObjectName( "InputContext" );
|
|
|
|
|
2018-04-09 10:05:59 +02:00
|
|
|
#if 1
|
2018-04-20 08:52:26 +02:00
|
|
|
setCompositionModel( locale(), new QskHunspellCompositionModel( this ) );
|
2018-04-09 10:05:59 +02:00
|
|
|
#endif
|
2018-04-12 13:23:10 +02:00
|
|
|
#if 0
|
2018-04-04 12:05:01 +02:00
|
|
|
setCompositionModel( QLocale::Chinese, new QskPinyinCompositionModel( this ) );
|
2018-04-02 17:01:04 +02:00
|
|
|
#endif
|
2018-03-30 01:15:05 -07:00
|
|
|
|
2018-04-20 08:52:26 +02:00
|
|
|
connect( qskSetup, &QskSetup::inputPanelChanged,
|
|
|
|
this, &QskInputContext::setInputPanel );
|
|
|
|
|
2018-04-02 17:01:04 +02:00
|
|
|
setInputPanel( qskSetup->inputPanel() );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
QskInputContext::~QskInputContext()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QskInputContext::isValid() const
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-04-04 12:05:01 +02:00
|
|
|
bool QskInputContext::hasCapability( Capability ) const
|
|
|
|
{
|
|
|
|
// what is QPlatformInputContext::HiddenTextCapability ???
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-04-05 14:18:15 +02:00
|
|
|
QQuickItem* QskInputContext::inputItem()
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-04-05 14:18:15 +02:00
|
|
|
return m_data->inputItem;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskInputContext::setInputItem( QQuickItem* item )
|
|
|
|
{
|
|
|
|
if ( m_data->inputItem == item )
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_data->inputItem = item;
|
|
|
|
|
2018-04-11 17:33:43 +02:00
|
|
|
if ( item )
|
|
|
|
update( Qt::ImQueryAll );
|
|
|
|
else
|
2018-04-05 14:18:15 +02:00
|
|
|
hideInputPanel();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskInputContext::update( Qt::InputMethodQueries queries )
|
|
|
|
{
|
|
|
|
if ( m_data->inputItem == nullptr || m_data->inputPanel == nullptr )
|
2017-07-21 18:21:34 +02:00
|
|
|
return;
|
|
|
|
|
2018-04-04 15:19:51 +02:00
|
|
|
const auto queryEvent = queryInputMethod( queries );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2018-04-05 14:18:15 +02:00
|
|
|
if ( queryEvent.queries() & Qt::ImEnabled )
|
|
|
|
{
|
|
|
|
if ( !queryEvent.value( Qt::ImEnabled ).toBool() )
|
|
|
|
{
|
|
|
|
hideInputPanel();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2018-04-05 14:18:15 +02:00
|
|
|
if ( queryEvent.queries() & Qt::ImHints )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-04-05 14:18:15 +02:00
|
|
|
/*
|
|
|
|
ImhHiddenText = 0x1, // might need to disable certain checks
|
|
|
|
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
|
2018-04-23 14:06:40 +02:00
|
|
|
ImhNoPredictiveText = 0x40, // not use predictive text
|
2018-04-05 14:18:15 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
|
2018-04-04 15:19:51 +02:00
|
|
|
#if 0
|
|
|
|
const auto hints = static_cast< Qt::InputMethodHints >(
|
|
|
|
queryEvent.value( Qt::ImHints ).toInt() );
|
|
|
|
|
|
|
|
#endif
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
2018-04-05 14:18:15 +02:00
|
|
|
if ( queryEvent.queries() & Qt::ImPreferredLanguage )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
const auto locale = queryEvent.value( Qt::ImPreferredLanguage ).toLocale();
|
|
|
|
|
2018-04-20 08:52:26 +02:00
|
|
|
const auto oldModel = compositionModel();
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2018-04-01 12:47:44 +02:00
|
|
|
if( m_data->inputPanel )
|
2018-04-04 20:19:47 +02:00
|
|
|
qskSetLocale( m_data->inputPanel, locale );
|
2018-03-30 01:15:05 -07:00
|
|
|
|
2018-04-01 12:47:44 +02:00
|
|
|
auto newModel = compositionModel();
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2018-04-20 08:52:26 +02:00
|
|
|
if( newModel && ( oldModel != newModel ) )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-04-04 20:19:47 +02:00
|
|
|
connect( newModel, &QskInputCompositionModel::candidatesChanged,
|
|
|
|
this, &QskInputContext::handleCandidatesChanged );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
2018-04-20 08:52:26 +02:00
|
|
|
|
|
|
|
qskSetCandidatesEnabled( m_data->inputPanel, newModel != nullptr );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
2018-04-20 08:52:26 +02:00
|
|
|
#if 0
|
|
|
|
if ( queryEvent.queries() & Qt::ImTextBeforeCursor
|
|
|
|
&& queryEvent.queries() & Qt::ImTextAfterCursor )
|
|
|
|
{
|
|
|
|
const auto text1 = queryEvent.value( Qt::ImTextBeforeCursor ).toString();
|
|
|
|
const auto text2 = queryEvent.value( Qt::ImTextAfterCursor ).toString();
|
|
|
|
|
|
|
|
qDebug() << text1 << text2;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-04-05 14:18:15 +02:00
|
|
|
/*
|
|
|
|
Qt::ImMicroFocus
|
|
|
|
Qt::ImCursorRectangle
|
|
|
|
Qt::ImFont
|
|
|
|
Qt::ImCursorPosition
|
|
|
|
Qt::ImSurroundingText // important for chinese input
|
|
|
|
Qt::ImCurrentSelection // important for prediction
|
|
|
|
Qt::ImMaximumTextLength // should be monitored
|
|
|
|
Qt::ImAnchorPosition
|
|
|
|
|
|
|
|
Qt::ImAbsolutePosition
|
|
|
|
Qt::ImTextBeforeCursor // important for chinese
|
|
|
|
Qt::ImTextAfterCursor // important for chinese
|
|
|
|
Qt::ImPlatformData // hard to say...
|
|
|
|
Qt::ImEnterKeyType
|
|
|
|
Qt::ImAnchorRectangle
|
|
|
|
Qt::ImInputItemClipRectangle // could be used for the geometry of the panel
|
|
|
|
*/
|
2018-04-04 15:19:51 +02:00
|
|
|
}
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
QRectF QskInputContext::keyboardRect() const
|
|
|
|
{
|
2018-04-01 12:47:44 +02:00
|
|
|
if ( m_data->inputPanel
|
2017-07-21 18:21:34 +02:00
|
|
|
&& QskDialog::instance()->policy() != QskDialog::TopLevelWindow )
|
|
|
|
{
|
2018-04-04 20:19:47 +02:00
|
|
|
return qskItemGeometry( m_data->inputPanel );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return Inherited::keyboardRect();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QskInputContext::isAnimating() const
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskInputContext::showInputPanel()
|
|
|
|
{
|
2018-04-11 17:33:43 +02:00
|
|
|
auto& inputPanel = m_data->inputPanel;
|
|
|
|
auto& inputPopup = m_data->inputPopup;
|
|
|
|
auto& inputWindow = m_data->inputWindow;
|
|
|
|
|
|
|
|
if ( inputPanel == nullptr )
|
|
|
|
{
|
|
|
|
auto panel = new QskInputPanel();
|
2018-04-18 19:41:46 +02:00
|
|
|
|
2018-04-11 17:33:43 +02:00
|
|
|
panel->setParent( this );
|
2018-04-18 19:41:46 +02:00
|
|
|
panel->setInputProxy( true );
|
2018-04-11 17:33:43 +02:00
|
|
|
|
|
|
|
setInputPanel( panel );
|
|
|
|
}
|
|
|
|
|
|
|
|
const bool isPopupPanel = qobject_cast< QskPopup* >( inputPanel );
|
|
|
|
|
|
|
|
bool useWindow = false;
|
|
|
|
if ( !isPopupPanel )
|
|
|
|
{
|
|
|
|
useWindow = ( QskDialog::instance()->policy() == QskDialog::TopLevelWindow );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( useWindow )
|
|
|
|
{
|
|
|
|
delete inputPopup;
|
|
|
|
|
|
|
|
if ( inputWindow == nullptr )
|
|
|
|
{
|
|
|
|
inputWindow = new QskWindow();
|
|
|
|
inputWindow->setDeleteOnClose( true );
|
|
|
|
inputWindow->setFlags( Qt::Tool | Qt::WindowDoesNotAcceptFocus );
|
|
|
|
|
|
|
|
inputPanel->setParentItem( inputWindow->contentItem() );
|
|
|
|
|
2018-04-18 19:41:46 +02:00
|
|
|
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() );
|
2018-04-11 17:33:43 +02:00
|
|
|
inputWindow->show();
|
|
|
|
|
|
|
|
inputWindow->installEventFilter( this );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-04-11 17:33:43 +02:00
|
|
|
delete inputWindow;
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2018-04-11 17:33:43 +02:00
|
|
|
if ( inputPopup == nullptr )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-04-11 17:33:43 +02:00
|
|
|
if ( isPopupPanel )
|
|
|
|
{
|
|
|
|
inputPopup = qobject_cast< QskPopup* >( inputPanel );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
auto popup = new QskPopup( m_data->inputItem->window()->contentItem() );
|
2018-04-18 19:41:46 +02:00
|
|
|
|
2018-04-11 17:33:43 +02:00
|
|
|
popup->setAutoLayoutChildren( true );
|
2018-04-12 12:03:51 +02:00
|
|
|
popup->setTransparentForPositioner( false );
|
|
|
|
popup->setOverlay( false );
|
2018-04-11 17:33:43 +02:00
|
|
|
popup->setModal( true );
|
2018-04-05 14:18:15 +02:00
|
|
|
|
2018-04-12 12:03:51 +02:00
|
|
|
auto box = new QskLinearBox( popup );
|
|
|
|
box->addItem( inputPanel );
|
|
|
|
|
2018-04-18 19:41:46 +02:00
|
|
|
if ( auto panel = qobject_cast< QskInputPanel* >( inputPanel ) )
|
|
|
|
{
|
|
|
|
if ( panel->hasInputProxy() )
|
|
|
|
{
|
|
|
|
popup->setOverlay( true );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !popup->hasOverlay() )
|
|
|
|
{
|
|
|
|
box->setExtraSpacingAt( Qt::TopEdge | Qt::LeftEdge | Qt::RightEdge );
|
|
|
|
}
|
|
|
|
|
2018-04-11 17:33:43 +02:00
|
|
|
inputPopup = popup;
|
|
|
|
}
|
2018-04-05 14:18:15 +02:00
|
|
|
|
2018-04-11 17:33:43 +02:00
|
|
|
inputPopup->installEventFilter( this );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
2018-04-11 17:33:43 +02:00
|
|
|
|
|
|
|
if ( inputPopup->window() == nullptr )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-04-11 17:33:43 +02:00
|
|
|
QQuickWindow* window = nullptr;
|
|
|
|
if ( m_data->inputItem )
|
|
|
|
window = m_data->inputItem->window();
|
|
|
|
else
|
|
|
|
window = qobject_cast< QQuickWindow* >( QGuiApplication::focusWindow() );
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
if ( window )
|
|
|
|
{
|
2018-04-11 17:33:43 +02:00
|
|
|
inputPopup->setParentItem( window->contentItem() );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-11 17:33:43 +02:00
|
|
|
inputPopup->setVisible( true );
|
2018-04-05 14:18:15 +02:00
|
|
|
}
|
2018-04-11 17:33:43 +02:00
|
|
|
|
2018-04-12 13:32:28 +02:00
|
|
|
update( Qt::ImQueryAll );
|
2018-04-23 14:06:40 +02:00
|
|
|
|
|
|
|
#if 1
|
|
|
|
if ( auto panel = qobject_cast< QskInputPanel* >( m_data->inputPanel ) )
|
|
|
|
panel->updateInputProxy( m_data->inputItem );
|
|
|
|
#endif
|
|
|
|
|
2018-04-11 17:33:43 +02:00
|
|
|
inputPanel->setVisible( true );
|
|
|
|
|
2018-04-17 21:28:15 +02:00
|
|
|
#if 0
|
|
|
|
if ( auto focusItem = inputPanel->nextItemInFocusChain( true ) )
|
|
|
|
qskForceActiveFocus( focusItem, Qt::OtherFocusReason );
|
|
|
|
#endif
|
|
|
|
|
2018-04-11 17:33:43 +02:00
|
|
|
connect( inputPanel->window(), &QskWindow::visibleChanged,
|
|
|
|
this, &QskInputContext::emitInputPanelVisibleChanged );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void QskInputContext::hideInputPanel()
|
|
|
|
{
|
2018-04-11 17:33:43 +02:00
|
|
|
if ( m_data->inputPanel )
|
|
|
|
{
|
|
|
|
// to get rid of the scene graph nodes
|
|
|
|
m_data->inputPanel->setVisible( false );
|
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2018-04-11 17:33:43 +02:00
|
|
|
if ( m_data->inputPopup == m_data->inputPanel )
|
2018-04-05 14:18:15 +02:00
|
|
|
{
|
2018-04-11 17:33:43 +02:00
|
|
|
m_data->inputPopup->removeEventFilter( this );
|
|
|
|
m_data->inputPopup = nullptr;
|
2018-04-05 14:18:15 +02:00
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
else
|
2018-04-05 14:18:15 +02:00
|
|
|
{
|
2018-04-11 17:33:43 +02:00
|
|
|
if ( m_data->inputPopup )
|
2018-04-17 21:28:15 +02:00
|
|
|
{
|
|
|
|
#if 1
|
|
|
|
if ( auto focusItem = m_data->inputPopup->scopedFocusItem() )
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
Qt bug: QQuickItem::hasFocus() is not cleared
|
|
|
|
when the corresponding focusScope gets deleted.
|
|
|
|
Usually no problem, but here the focusItem is no
|
|
|
|
child and will be reused with a different parent
|
|
|
|
later.
|
|
|
|
*/
|
|
|
|
focusItem->setFocus( false );
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-04-12 12:03:51 +02:00
|
|
|
m_data->inputPopup->deleteLater();
|
2018-04-17 21:28:15 +02:00
|
|
|
}
|
2018-04-11 17:33:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
QskWindow* window = m_data->inputWindow;
|
|
|
|
m_data->inputWindow = nullptr;
|
|
|
|
|
|
|
|
if ( window )
|
|
|
|
{
|
|
|
|
window->removeEventFilter( this );
|
|
|
|
window->close(); // deleteOnClose is set
|
2018-04-05 14:18:15 +02:00
|
|
|
}
|
2018-04-11 08:58:14 +02:00
|
|
|
|
|
|
|
qGuiApp->removeEventFilter( this );
|
2018-04-23 14:06:40 +02:00
|
|
|
|
|
|
|
m_data->preedit.clear();
|
|
|
|
if ( auto model = compositionModel() )
|
|
|
|
model->resetCandidates();
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool QskInputContext::isInputPanelVisible() const
|
|
|
|
{
|
2018-04-01 12:47:44 +02:00
|
|
|
auto panel = m_data->inputPanel;
|
2018-04-03 20:15:20 +02:00
|
|
|
|
2018-04-01 12:47:44 +02:00
|
|
|
return panel && panel->isVisible()
|
2018-04-03 20:15:20 +02:00
|
|
|
&& panel->window() && panel->window()->isVisible();
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
QLocale QskInputContext::locale() const
|
|
|
|
{
|
2018-04-04 20:19:47 +02:00
|
|
|
return qskLocale( m_data->inputPanel );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
2018-04-04 12:05:01 +02:00
|
|
|
Qt::LayoutDirection QskInputContext::inputDirection() const
|
|
|
|
{
|
|
|
|
return Inherited::inputDirection();
|
|
|
|
}
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
void QskInputContext::setFocusObject( QObject* focusObject )
|
|
|
|
{
|
2018-04-01 12:47:44 +02:00
|
|
|
auto focusItem = qobject_cast< QQuickItem* >( focusObject );
|
2018-03-14 17:30:39 +01:00
|
|
|
|
2018-04-05 14:18:15 +02:00
|
|
|
if ( focusItem == nullptr )
|
2018-03-14 17:30:39 +01:00
|
|
|
{
|
2018-04-05 14:18:15 +02:00
|
|
|
if ( m_data->inputItem )
|
2018-03-14 17:30:39 +01:00
|
|
|
{
|
2018-04-05 14:18:15 +02:00
|
|
|
if ( m_data->inputItem->window() == QGuiApplication::focusWindow() )
|
|
|
|
setInputItem( nullptr );
|
2018-03-14 17:30:39 +01:00
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
2018-04-05 14:18:15 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
Do not change the input item when
|
2018-04-11 17:33:43 +02:00
|
|
|
navigating to or inside the input popup/window
|
2018-04-05 14:18:15 +02:00
|
|
|
*/
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2018-04-11 17:33:43 +02:00
|
|
|
bool isAccepted = ( m_data->inputItem == nullptr );
|
|
|
|
|
|
|
|
if ( !isAccepted )
|
|
|
|
{
|
|
|
|
if ( m_data->inputWindow )
|
|
|
|
{
|
|
|
|
if ( focusItem->window() != m_data->inputWindow )
|
|
|
|
isAccepted = true;
|
|
|
|
}
|
|
|
|
else if ( m_data->inputPopup )
|
|
|
|
{
|
|
|
|
if ( ( focusItem != m_data->inputPopup )
|
|
|
|
&& !qskIsAncestorOf( m_data->inputPopup, focusItem ) )
|
|
|
|
{
|
|
|
|
isAccepted = true;
|
|
|
|
}
|
|
|
|
}
|
2018-04-13 16:32:48 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
isAccepted = true;
|
|
|
|
}
|
2018-04-11 17:33:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if ( isAccepted )
|
2018-04-05 14:18:15 +02:00
|
|
|
setInputItem( focusItem );
|
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
2018-04-02 17:01:04 +02:00
|
|
|
void QskInputContext::setCompositionModel(
|
2018-04-01 12:47:44 +02:00
|
|
|
const QLocale& locale, QskInputCompositionModel* model )
|
2018-03-30 01:15:05 -07:00
|
|
|
{
|
2018-04-02 17:01:04 +02:00
|
|
|
auto& models = m_data->compositionModels;
|
|
|
|
|
2018-04-20 08:52:26 +02:00
|
|
|
const auto key = qskHashLocale( locale );
|
|
|
|
|
2018-04-02 17:01:04 +02:00
|
|
|
if ( model )
|
|
|
|
{
|
2018-04-20 08:52:26 +02:00
|
|
|
const auto it = models.find( key );
|
2018-04-02 17:01:04 +02:00
|
|
|
if ( it != models.end() )
|
|
|
|
{
|
|
|
|
if ( it.value() == model )
|
|
|
|
return;
|
|
|
|
|
|
|
|
delete it.value();
|
|
|
|
*it = model;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-04-20 08:52:26 +02:00
|
|
|
models.insert( key, model );
|
2018-04-02 17:01:04 +02:00
|
|
|
}
|
2018-03-30 01:15:05 -07:00
|
|
|
|
2018-04-02 17:01:04 +02:00
|
|
|
connect( model, &QskInputCompositionModel::candidatesChanged,
|
|
|
|
this, &QskInputContext::handleCandidatesChanged );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-04-20 08:52:26 +02:00
|
|
|
const auto it = models.find( key );
|
2018-04-02 17:01:04 +02:00
|
|
|
if ( it != models.end() )
|
|
|
|
{
|
|
|
|
delete it.value();
|
|
|
|
models.erase( it );
|
|
|
|
}
|
|
|
|
}
|
2018-03-30 01:15:05 -07:00
|
|
|
}
|
|
|
|
|
2018-04-01 12:47:44 +02:00
|
|
|
QskInputCompositionModel* QskInputContext::compositionModel() const
|
2018-03-30 01:15:05 -07:00
|
|
|
{
|
2018-04-20 08:52:26 +02:00
|
|
|
const auto key = qskHashLocale( locale() );
|
|
|
|
return m_data->compositionModels.value( key, nullptr );
|
2018-03-30 01:15:05 -07:00
|
|
|
}
|
|
|
|
|
2018-04-04 20:19:47 +02:00
|
|
|
void QskInputContext::invokeAction( QInputMethod::Action action, int value )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-04-04 12:05:01 +02:00
|
|
|
switch ( static_cast< int >( action ) )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-04-10 16:51:35 +02:00
|
|
|
case QskInputPanel::Compose:
|
2018-04-01 12:47:44 +02:00
|
|
|
{
|
2018-04-20 08:52:26 +02:00
|
|
|
processKey( value );
|
2017-07-21 18:21:34 +02:00
|
|
|
break;
|
2018-04-01 12:47:44 +02:00
|
|
|
}
|
2018-04-10 16:51:35 +02:00
|
|
|
case QskInputPanel::SelectCandidate:
|
2018-04-01 12:47:44 +02:00
|
|
|
{
|
2018-04-20 08:52:26 +02:00
|
|
|
if ( auto model = compositionModel() )
|
|
|
|
{
|
|
|
|
auto text = model->candidate( value );
|
|
|
|
|
|
|
|
if ( model->attributes() & QskInputCompositionModel::Words )
|
|
|
|
text += " ";
|
|
|
|
|
|
|
|
sendText( text, true );
|
|
|
|
|
2018-04-23 14:06:40 +02:00
|
|
|
m_data->preedit.clear();
|
|
|
|
model->resetCandidates();
|
2018-04-20 08:52:26 +02:00
|
|
|
}
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
break;
|
2018-04-01 12:47:44 +02:00
|
|
|
}
|
2018-04-04 12:05:01 +02:00
|
|
|
case QInputMethod::Click:
|
|
|
|
case QInputMethod::ContextMenu:
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-20 08:52:26 +02:00
|
|
|
int QskInputContext::keysLeft() const
|
|
|
|
{
|
|
|
|
const auto event = queryInputMethod(
|
|
|
|
Qt::ImSurroundingText | Qt::ImMaximumTextLength | Qt::ImHints );
|
|
|
|
|
|
|
|
const auto hints = static_cast< Qt::InputMethodHints >(
|
|
|
|
event.value( Qt::ImHints ).toInt() );
|
|
|
|
|
|
|
|
if ( !( hints & Qt::ImhMultiLine ) )
|
|
|
|
{
|
|
|
|
const int max = event.value( Qt::ImMaximumTextLength ).toInt();
|
|
|
|
|
|
|
|
if ( max > 0 )
|
|
|
|
{
|
|
|
|
const auto text = event.value( Qt::ImSurroundingText ).toString();
|
|
|
|
return max - text.length();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1; // unlimited
|
|
|
|
}
|
|
|
|
|
|
|
|
Qt::InputMethodHints QskInputContext::inputHints() const
|
|
|
|
{
|
|
|
|
const auto e = queryInputMethod( Qt::ImHints );
|
|
|
|
return static_cast< Qt::InputMethodHints >( e.value( Qt::ImHints ).toInt() );
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskInputContext::processKey( int key )
|
|
|
|
{
|
|
|
|
const auto hints = inputHints();
|
|
|
|
|
|
|
|
auto spaceLeft = keysLeft();
|
|
|
|
|
|
|
|
QskInputCompositionModel* model = nullptr;
|
|
|
|
|
|
|
|
if ( !( hints & Qt::ImhHiddenText ) )
|
|
|
|
model = compositionModel();
|
|
|
|
|
2018-04-23 14:06:40 +02:00
|
|
|
auto& preedit = m_data->preedit;
|
2018-04-20 08:52:26 +02:00
|
|
|
/*
|
|
|
|
First we have to handle the control keys
|
|
|
|
*/
|
|
|
|
switch ( key )
|
|
|
|
{
|
|
|
|
case Qt::Key_Backspace:
|
|
|
|
case Qt::Key_Muhenkan:
|
|
|
|
{
|
|
|
|
if ( model )
|
|
|
|
{
|
2018-04-23 14:06:40 +02:00
|
|
|
if ( !preedit.isEmpty() )
|
2018-04-20 08:52:26 +02:00
|
|
|
{
|
2018-04-23 14:06:40 +02:00
|
|
|
preedit.chop( 1 );
|
|
|
|
sendText( preedit, false );
|
2018-04-20 08:52:26 +02:00
|
|
|
|
2018-04-23 14:06:40 +02:00
|
|
|
model->requestCandidates( preedit );
|
2018-04-20 08:52:26 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sendKey( Qt::Key_Backspace );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
case Qt::Key_Return:
|
|
|
|
{
|
|
|
|
if ( model )
|
|
|
|
{
|
2018-04-23 14:06:40 +02:00
|
|
|
if ( !preedit.isEmpty() )
|
2018-04-20 08:52:26 +02:00
|
|
|
{
|
|
|
|
if ( spaceLeft )
|
2018-04-23 14:06:40 +02:00
|
|
|
sendText( preedit.left( spaceLeft ), true );
|
|
|
|
|
|
|
|
preedit.clear();
|
|
|
|
model->resetCandidates();
|
2018-04-20 08:52:26 +02:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !( hints & Qt::ImhMultiLine ) )
|
|
|
|
{
|
|
|
|
sendKey( Qt::Key_Return );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Qt::Key_Space:
|
|
|
|
{
|
|
|
|
if ( model )
|
|
|
|
{
|
2018-04-23 14:06:40 +02:00
|
|
|
if ( !preedit.isEmpty() && spaceLeft)
|
2018-04-20 08:52:26 +02:00
|
|
|
{
|
2018-04-23 14:06:40 +02:00
|
|
|
preedit = preedit.left( spaceLeft );
|
|
|
|
sendText( preedit, true );
|
|
|
|
spaceLeft -= preedit.length();
|
2018-04-20 08:52:26 +02:00
|
|
|
|
2018-04-23 14:06:40 +02:00
|
|
|
preedit.clear();
|
|
|
|
model->resetCandidates();
|
2018-04-20 08:52:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Qt::Key_Left:
|
|
|
|
case Qt::Key_Right:
|
|
|
|
case Qt::Key_Escape:
|
|
|
|
{
|
|
|
|
sendKey( key );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const QString text = qskKeyString( key );
|
|
|
|
|
|
|
|
if ( model )
|
|
|
|
{
|
2018-04-23 14:06:40 +02:00
|
|
|
preedit += text;
|
|
|
|
|
|
|
|
model->requestCandidates( preedit );
|
|
|
|
|
|
|
|
if ( model->candidateCount() > 0 )
|
|
|
|
{
|
|
|
|
sendText( preedit, false );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
sendText( preedit.left( spaceLeft ), true );
|
|
|
|
preedit.clear();
|
|
|
|
}
|
2018-04-20 08:52:26 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
sendText( text, true );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
void QskInputContext::handleCandidatesChanged()
|
|
|
|
{
|
2018-04-01 12:47:44 +02:00
|
|
|
const auto model = compositionModel();
|
2018-04-02 17:01:04 +02:00
|
|
|
if ( model == nullptr || m_data->inputPanel == nullptr )
|
|
|
|
return;
|
|
|
|
|
|
|
|
const auto count = model->candidateCount();
|
2018-04-01 12:47:44 +02:00
|
|
|
|
2018-04-02 17:01:04 +02:00
|
|
|
QVector< QString > candidates;
|
|
|
|
candidates.reserve( count );
|
2018-03-30 01:15:05 -07:00
|
|
|
|
2018-04-02 17:01:04 +02:00
|
|
|
for( int i = 0; i < count; i++ )
|
|
|
|
candidates += model->candidate( i );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2018-04-10 16:51:35 +02:00
|
|
|
qskSetCandidates( m_data->inputPanel, candidates );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
2018-04-04 20:19:47 +02:00
|
|
|
void QskInputContext::setInputPanel( QQuickItem* inputPanel )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-04-01 12:47:44 +02:00
|
|
|
if ( m_data->inputPanel == inputPanel )
|
2017-07-21 18:21:34 +02:00
|
|
|
return;
|
|
|
|
|
2018-04-01 12:47:44 +02:00
|
|
|
auto model = compositionModel();
|
2018-03-31 18:34:51 +02:00
|
|
|
|
2018-04-01 12:47:44 +02:00
|
|
|
if ( m_data->inputPanel )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-04-01 12:47:44 +02:00
|
|
|
m_data->inputPanel->disconnect( this );
|
2018-03-31 18:34:51 +02:00
|
|
|
|
2018-04-11 17:33:43 +02:00
|
|
|
if ( m_data->inputPanel->parent() == this )
|
|
|
|
{
|
|
|
|
delete m_data->inputPanel;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_data->inputPanel->setParentItem( nullptr );
|
|
|
|
}
|
|
|
|
|
2018-04-01 12:47:44 +02:00
|
|
|
if ( model )
|
|
|
|
model->disconnect( m_data->inputPanel );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
2018-04-01 12:47:44 +02:00
|
|
|
m_data->inputPanel = inputPanel;
|
2018-04-05 14:18:15 +02:00
|
|
|
m_data->ownsInputPanelWindow = false;
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2018-03-31 18:34:51 +02:00
|
|
|
if ( inputPanel )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-04-11 17:33:43 +02:00
|
|
|
if ( inputPanel->parent() == nullptr )
|
|
|
|
inputPanel->setParent( this );
|
|
|
|
|
2018-04-04 20:19:47 +02:00
|
|
|
connect( inputPanel, &QQuickItem::visibleChanged,
|
2018-03-31 18:34:51 +02:00
|
|
|
this, &QPlatformInputContext::emitInputPanelVisibleChanged );
|
|
|
|
|
2018-04-04 20:19:47 +02:00
|
|
|
if ( auto control = qobject_cast< QskControl* >( inputPanel ) )
|
2018-03-31 18:34:51 +02:00
|
|
|
{
|
2018-04-04 20:19:47 +02:00
|
|
|
connect( control, &QskControl::localeChanged,
|
|
|
|
this, &QPlatformInputContext::emitLocaleChanged );
|
|
|
|
}
|
2018-03-31 18:34:51 +02:00
|
|
|
|
2018-04-20 08:52:26 +02:00
|
|
|
qskSetCandidatesEnabled( inputPanel, model != nullptr );
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-04 12:05:01 +02:00
|
|
|
void QskInputContext::reset()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskInputContext::commit()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2018-04-11 08:58:14 +02:00
|
|
|
bool QskInputContext::eventFilter( QObject* object, QEvent* event )
|
2018-04-11 17:33:43 +02:00
|
|
|
{
|
2018-04-12 12:03:51 +02:00
|
|
|
if ( object == m_data->inputWindow )
|
2018-04-11 08:58:14 +02:00
|
|
|
{
|
2018-04-12 12:03:51 +02:00
|
|
|
switch( event->type() )
|
2018-04-11 17:33:43 +02:00
|
|
|
{
|
2018-04-12 12:03:51 +02:00
|
|
|
case QEvent::Move:
|
|
|
|
{
|
|
|
|
if ( m_data->inputPanel )
|
|
|
|
emitKeyboardRectChanged();
|
2018-04-11 17:33:43 +02:00
|
|
|
|
2018-04-12 12:03:51 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case QEvent::Resize:
|
|
|
|
{
|
|
|
|
QQuickItem* panel = m_data->inputPanel;
|
2018-04-11 08:58:14 +02:00
|
|
|
|
2018-04-12 12:03:51 +02:00
|
|
|
if ( m_data->inputPanel )
|
|
|
|
m_data->inputPanel->setSize( m_data->inputWindow->size() );
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case QEvent::DeferredDelete:
|
|
|
|
{
|
|
|
|
m_data->inputWindow = nullptr;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
2018-04-11 17:33:43 +02:00
|
|
|
}
|
2018-04-12 12:03:51 +02:00
|
|
|
}
|
2018-04-20 08:52:26 +02:00
|
|
|
else if ( object == m_data->inputPopup )
|
2018-04-12 12:03:51 +02:00
|
|
|
{
|
2018-04-12 13:32:28 +02:00
|
|
|
switch( static_cast< int >( event->type() ) )
|
2018-04-11 08:58:14 +02:00
|
|
|
{
|
2018-04-12 12:03:51 +02:00
|
|
|
case QskEvent::GeometryChange:
|
2018-04-11 17:33:43 +02:00
|
|
|
{
|
2018-04-20 08:52:26 +02:00
|
|
|
if ( event->type() == QskEvent::GeometryChange )
|
|
|
|
emitKeyboardRectChanged();
|
2018-04-12 12:03:51 +02:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case QEvent::DeferredDelete:
|
|
|
|
{
|
2018-04-20 08:52:26 +02:00
|
|
|
m_data->inputPopup = nullptr;
|
2018-04-12 12:03:51 +02:00
|
|
|
break;
|
|
|
|
}
|
2018-04-11 08:58:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Inherited::eventFilter( object, event );
|
|
|
|
}
|
|
|
|
|
2018-04-20 08:52:26 +02:00
|
|
|
bool QskInputContext::filterEvent( const QEvent* )
|
2018-04-04 12:05:01 +02:00
|
|
|
{
|
2018-04-04 15:19:51 +02:00
|
|
|
// called from QXcbKeyboard, but what about other platforms
|
2018-04-04 12:05:01 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-04-04 15:19:51 +02:00
|
|
|
QInputMethodQueryEvent QskInputContext::queryInputMethod(
|
|
|
|
Qt::InputMethodQueries queries ) const
|
2018-04-04 12:05:01 +02:00
|
|
|
{
|
2018-04-04 15:19:51 +02:00
|
|
|
QInputMethodQueryEvent event( queries );
|
2018-04-20 08:52:26 +02:00
|
|
|
|
|
|
|
if ( m_data->inputItem )
|
|
|
|
QCoreApplication::sendEvent( m_data->inputItem, &event );
|
2018-04-04 15:19:51 +02:00
|
|
|
|
|
|
|
return event;
|
|
|
|
}
|
|
|
|
|
2018-04-20 08:52:26 +02:00
|
|
|
void QskInputContext::sendText(
|
|
|
|
const QString& text, bool isFinal ) const
|
2018-04-04 15:19:51 +02:00
|
|
|
{
|
2018-04-20 08:52:26 +02:00
|
|
|
if ( m_data->inputItem == nullptr )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ( isFinal )
|
|
|
|
{
|
|
|
|
QInputMethodEvent event;
|
|
|
|
event.setCommitString( text );
|
|
|
|
|
|
|
|
QCoreApplication::sendEvent( m_data->inputItem, &event );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
QTextCharFormat format;
|
|
|
|
format.setFontUnderline( true );
|
|
|
|
|
|
|
|
const QInputMethodEvent::Attribute attribute(
|
|
|
|
QInputMethodEvent::TextFormat, 0, text.length(), format );
|
|
|
|
|
|
|
|
QInputMethodEvent event( text, { attribute } );
|
|
|
|
|
|
|
|
QCoreApplication::sendEvent( m_data->inputItem, &event );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void QskInputContext::sendKey( int key ) const
|
|
|
|
{
|
|
|
|
if ( m_data->inputItem == nullptr )
|
|
|
|
return;
|
|
|
|
|
|
|
|
QKeyEvent keyPress( QEvent::KeyPress, key, Qt::NoModifier );
|
|
|
|
QCoreApplication::sendEvent( m_data->inputItem, &keyPress );
|
|
|
|
|
|
|
|
QKeyEvent keyRelease( QEvent::KeyRelease, key, Qt::NoModifier );
|
|
|
|
QCoreApplication::sendEvent( m_data->inputItem, &keyRelease );
|
2018-04-04 12:05:01 +02:00
|
|
|
}
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
#include "moc_QskInputContext.cpp"
|