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

View File

@ -26,35 +26,26 @@
#define STRINGIFY(x) #x #define STRINGIFY(x) #x
#define STRING(x) STRINGIFY(x) #define STRING(x) STRINGIFY(x)
#define LOCAL_PANEL 1
class InputBox : public QskLinearBox class InputBox : public QskLinearBox
{ {
public: public:
InputBox( QQuickItem* parentItem = nullptr ): InputBox( QQuickItem* parentItem = nullptr ):
QskLinearBox( Qt::Vertical, parentItem ) QskLinearBox( Qt::Vertical, parentItem )
{ {
setDefaultAlignment( Qt::AlignHCenter | Qt::AlignTop ); setExtraSpacingAt( Qt::BottomEdge | Qt::RightEdge );
setMargins( 10 ); setMargins( 10 );
setSpacing( 10 ); setSpacing( 10 );
auto* textInput = new QskTextInput( this ); auto* textInput1 = new QskTextInput( this );
textInput->setText( "I am a line edit. Press and edit Me." ); textInput1->setText( "I am a line edit. Press and edit Me." );
textInput->setSelectByMouse( true ); textInput1->setSelectByMouse( true );
textInput->setSizePolicy( Qt::Horizontal, QskSizePolicy::Preferred ); textInput1->setSizePolicy( Qt::Horizontal, QskSizePolicy::Preferred );
#if LOCAL_PANEL auto* textInput2 = new QskTextInput( this );
auto* inputPanel = new QskInputPanel( this ); textInput2->setText( "Another text" );
inputPanel->setVisible( false ); textInput2->setSelectByMouse( true );
textInput2->setSizePolicy( Qt::Horizontal, QskSizePolicy::Preferred );
/*
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
} }
}; };
@ -169,11 +160,20 @@ int main( int argc, char* argv[] )
SkinnyFont::init( &app ); SkinnyFont::init( &app );
SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts ); SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts );
#if !LOCAL_PANEL #if 0
// We don't want to have a top level window. // We don't want to have a top level window.
qskDialog->setPolicy( QskDialog::EmbeddedBox ); qskDialog->setPolicy( QskDialog::EmbeddedBox );
#endif #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 ); auto box = new QskLinearBox( Qt::Horizontal );
box->setSpacing( 10 ); box->setSpacing( 10 );
box->setMargins( 20 ); box->setMargins( 20 );

View File

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

View File

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