QskInputContext improvements

This commit is contained in:
Uwe Rathmann 2018-04-11 17:33:43 +02:00
parent 07d5d933c1
commit 0ee83c1e32
4 changed files with 204 additions and 108 deletions

View File

@ -11,9 +11,11 @@
#include "QskInputPanel.h"
#include <QskDialog.h>
#include <QskPopup.h>
#include <QskWindow.h>
#include <QskControl.h>
#include <QskSetup.h>
#include <QskEvent.h>
#include <QGuiApplication>
#include <QHash>
@ -82,14 +84,16 @@ static void qskSetCandidates( QQuickItem* inputPanel,
class QskInputContext::PrivateData
{
public:
PrivateData():
ownsInputPanelWindow( false )
{
}
// item receiving the input
QPointer< QQuickItem > inputItem;
// item, wher the user enters texts/keys
QPointer< QQuickItem > inputPanel;
// popup or window embedding the inputPanel
QPointer< QskPopup > inputPopup;
QPointer< QskWindow > inputWindow;
QskInputCompositionModel* compositionModel;
QHash< QLocale, QskInputCompositionModel* > compositionModels;
@ -123,10 +127,6 @@ QskInputContext::QskInputContext():
QskInputContext::~QskInputContext()
{
#if 1
if ( m_data->inputPanel )
delete m_data->inputPanel;
#endif
}
bool QskInputContext::isValid() const
@ -152,13 +152,10 @@ void QskInputContext::setInputItem( QQuickItem* item )
m_data->inputItem = item;
if ( m_data->inputItem == nullptr )
{
hideInputPanel();
return;
}
if ( item )
update( Qt::ImQueryAll );
else
hideInputPanel();
}
void QskInputContext::update( Qt::InputMethodQueries queries )
@ -271,57 +268,123 @@ bool QskInputContext::isAnimating() const
void QskInputContext::showInputPanel()
{
if ( !m_data->inputPanel )
auto& inputPanel = m_data->inputPanel;
auto& inputPopup = m_data->inputPopup;
auto& inputWindow = m_data->inputWindow;
if ( inputPanel == nullptr )
{
setInputPanel( new QskInputPanel() );
auto panel = new QskInputPanel();
panel->setParent( this );
if ( QskDialog::instance()->policy() == QskDialog::TopLevelWindow )
setInputPanel( panel );
}
const bool isPopupPanel = qobject_cast< QskPopup* >( inputPanel );
bool useWindow = false;
if ( !isPopupPanel )
{
m_data->ownsInputPanelWindow = true;
useWindow = ( QskDialog::instance()->policy() == QskDialog::TopLevelWindow );
}
auto window = new QskWindow;
window->setFlags( Qt::Tool | Qt::WindowDoesNotAcceptFocus );
window->resize( 800, 240 ); // ### what size?
if ( useWindow )
{
delete inputPopup;
m_data->inputPanel->setParentItem( window->contentItem() );
connect( window, &QskWindow::visibleChanged,
this, &QskInputContext::emitInputPanelVisibleChanged );
if ( inputWindow == nullptr )
{
inputWindow = new QskWindow();
inputWindow->setDeleteOnClose( true );
inputWindow->setFlags( Qt::Tool | Qt::WindowDoesNotAcceptFocus );
inputPanel->setParentItem( inputWindow->contentItem() );
inputWindow->resize( 800, 240 ); // ### what size?
inputWindow->show();
inputWindow->installEventFilter( this );
}
}
else
{
auto window = qobject_cast< QQuickWindow* >( QGuiApplication::focusWindow() );
delete inputWindow;
if ( inputPopup == nullptr )
{
if ( isPopupPanel )
{
inputPopup = qobject_cast< QskPopup* >( inputPanel );
}
else
{
auto popup = new QskPopup( m_data->inputItem->window()->contentItem() );
popup->setAutoLayoutChildren( true );
popup->setModal( true );
inputPanel->setParentItem( popup );
inputPopup = popup;
}
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 )
{
m_data->inputPanel->setParentItem( window->contentItem() );
m_data->inputPanel->setSize( window->size() );
}
inputPopup->setParentItem( window->contentItem() );
}
}
if ( m_data->ownsInputPanelWindow )
{
if ( m_data->inputPanel->window() )
m_data->inputPanel->window()->show();
}
else
{
m_data->inputPanel->setVisible( true );
inputPopup->setGeometry( qskItemGeometry( inputPopup->parentItem() ) );
inputPopup->setVisible( true );
}
inputPanel->setVisible( true );
connect( inputPanel->window(), &QskWindow::visibleChanged,
this, &QskInputContext::emitInputPanelVisibleChanged );
}
void QskInputContext::hideInputPanel()
{
if ( m_data->inputPanel == nullptr )
return;
if ( m_data->ownsInputPanelWindow )
if ( m_data->inputPanel )
{
if ( auto window = m_data->inputPanel->window() )
window->hide();
// to get rid of the scene graph nodes
m_data->inputPanel->setVisible( false );
}
if ( m_data->inputPopup == m_data->inputPanel )
{
m_data->inputPopup->removeEventFilter( this );
m_data->inputPopup = nullptr;
}
else
{
m_data->inputPanel->setVisible( false );
if ( m_data->inputPopup )
{
auto popup = m_data->inputPopup.data();
m_data->inputPopup = nullptr;
popup->deleteLater();
}
}
QskWindow* window = m_data->inputWindow;
m_data->inputWindow = nullptr;
if ( window )
{
window->removeEventFilter( this );
window->close(); // deleteOnClose is set
}
qGuiApp->removeEventFilter( this );
@ -361,10 +424,29 @@ void QskInputContext::setFocusObject( QObject* focusObject )
{
/*
Do not change the input item when
navigating on or into the panel
navigating to or inside the input popup/window
*/
if( qskNearestFocusScope( focusItem ) != m_data->inputPanel )
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;
}
}
}
if ( isAccepted )
setInputItem( focusItem );
}
@ -469,6 +551,15 @@ void QskInputContext::setInputPanel( QQuickItem* inputPanel )
{
m_data->inputPanel->disconnect( this );
if ( m_data->inputPanel->parent() == this )
{
delete m_data->inputPanel;
}
else
{
m_data->inputPanel->setParentItem( nullptr );
}
if ( model )
model->disconnect( m_data->inputPanel );
}
@ -478,25 +569,12 @@ void QskInputContext::setInputPanel( QQuickItem* inputPanel )
if ( inputPanel )
{
if ( inputPanel->parent() == nullptr )
inputPanel->setParent( this );
connect( inputPanel, &QQuickItem::visibleChanged,
this, &QPlatformInputContext::emitInputPanelVisibleChanged );
// maybe using a QQuickItemChangeListener instead
#if 1
connect( inputPanel, &QQuickItem::xChanged,
this, &QPlatformInputContext::emitKeyboardRectChanged );
connect( inputPanel, &QQuickItem::yChanged,
this, &QPlatformInputContext::emitKeyboardRectChanged );
connect( inputPanel, &QQuickItem::widthChanged,
this, &QPlatformInputContext::emitKeyboardRectChanged );
connect( inputPanel, &QQuickItem::heightChanged,
this, &QPlatformInputContext::emitKeyboardRectChanged );
#endif
if ( auto control = qobject_cast< QskControl* >( inputPanel ) )
{
connect( control, &QskControl::localeChanged,
@ -521,7 +599,24 @@ void QskInputContext::commit()
bool QskInputContext::eventFilter( QObject* object, QEvent* event )
{
if ( event->type() == QEvent::InputMethodQuery )
switch( static_cast< int >( event->type() ) )
{
case QEvent::Move:
case QEvent::Resize:
{
if ( m_data->inputPanel && object == m_data->inputPanel->window() )
emitKeyboardRectChanged();
break;
}
case QskEvent::GeometryChange:
{
if ( object == m_data->inputPopup )
emitKeyboardRectChanged();
break;
}
case QEvent::InputMethodQuery:
{
/*
Qt/Quick expects that the item associated with the input context
@ -536,6 +631,9 @@ bool QskInputContext::eventFilter( QObject* object, QEvent* event )
sendEventToInputItem( event );
return true;
}
break;
}
}
return Inherited::eventFilter( object, event );

View File

@ -26,35 +26,26 @@
#define STRINGIFY(x) #x
#define STRING(x) STRINGIFY(x)
#define LOCAL_PANEL 1
class InputBox : public QskLinearBox
{
public:
InputBox( QQuickItem* parentItem = nullptr ):
QskLinearBox( Qt::Vertical, parentItem )
{
setDefaultAlignment( Qt::AlignHCenter | Qt::AlignTop );
setExtraSpacingAt( Qt::BottomEdge | Qt::RightEdge );
setMargins( 10 );
setSpacing( 10 );
auto* textInput = new QskTextInput( this );
textInput->setText( "I am a line edit. Press and edit Me." );
textInput->setSelectByMouse( true );
textInput->setSizePolicy( Qt::Horizontal, QskSizePolicy::Preferred );
auto* textInput1 = new QskTextInput( this );
textInput1->setText( "I am a line edit. Press and edit Me." );
textInput1->setSelectByMouse( true );
textInput1->setSizePolicy( Qt::Horizontal, QskSizePolicy::Preferred );
#if LOCAL_PANEL
auto* inputPanel = new QskInputPanel( this );
inputPanel->setVisible( false );
/*
QskInputContext is connected to QskSetup::inputPanelChanged,
making it the system input. If no input panel has been assigned
QskInputContext would create a window or subwindow on the fly.
*/
qskSetup->setInputPanel( inputPanel );
#endif
auto* textInput2 = new QskTextInput( this );
textInput2->setText( "Another text" );
textInput2->setSelectByMouse( true );
textInput2->setSizePolicy( Qt::Horizontal, QskSizePolicy::Preferred );
}
};
@ -169,11 +160,20 @@ int main( int argc, char* argv[] )
SkinnyFont::init( &app );
SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts );
#if !LOCAL_PANEL
#if 0
// We don't want to have a top level window.
qskDialog->setPolicy( QskDialog::EmbeddedBox );
#endif
#if 0
/*
QskInputContext is connected to QskSetup::inputPanelChanged,
making it the system input. If no input panel has been assigned
QskInputContext would create a window or subwindow on the fly.
*/
qskSetup->setInputPanel( new QskInputPanel() );
#endif
auto box = new QskLinearBox( Qt::Horizontal );
box->setSpacing( 10 );
box->setMargins( 20 );

View File

@ -1538,10 +1538,13 @@ void QskControl::cleanupNodes()
d->dirtyAttributes |= QQuickItemPrivate::EffectReference;
}
if ( d->window )
{
// putting the nodes on the cleanup list of the window to be deleteted
// in the next cycle of the scene graph
QQuickWindowPrivate::get( window() )->cleanup( d->itemNodeInstance );
QQuickWindowPrivate::get( d->window )->cleanup( d->itemNodeInstance );
}
// now we can forget about the nodes

View File

@ -111,11 +111,6 @@ QskInputPanel::QskInputPanel( QQuickItem* parent ):
m_data( new PrivateData() )
{
setAutoLayoutChildren( true );
setFlag( ItemIsFocusScope, true );
#if 0
// TODO ...
setTabFence( true );
#endif
auto layout = new QskLinearBox( Qt::Vertical, this );