input panel improved

This commit is contained in:
Uwe Rathmann 2018-04-30 10:03:51 +02:00
parent 7fe675d74d
commit 3ffd9b3a71
6 changed files with 170 additions and 96 deletions

View File

@ -18,6 +18,7 @@
#include <QskTabBar.h> #include <QskTabBar.h>
#include <QskTabView.h> #include <QskTabView.h>
#include <QskInputPanel.h> #include <QskInputPanel.h>
#include <QskInputPredictionBar.h>
#include <QskVirtualKeyboard.h> #include <QskVirtualKeyboard.h>
#include <QskScrollView.h> #include <QskScrollView.h>
#include <QskListView.h> #include <QskListView.h>
@ -243,6 +244,7 @@ void QskSquiekSkin::initHints()
initDialogButtonHints(); initDialogButtonHints();
initFocusIndicatorHints(); initFocusIndicatorHints();
initInputPanelHints(); initInputPanelHints();
initInputPredictionBar();
initVirtualKeyboardHints(); initVirtualKeyboardHints();
initListViewHints(); initListViewHints();
initPageIndicatorHints(); initPageIndicatorHints();
@ -622,6 +624,24 @@ void QskSquiekSkin::initInputPanelHints()
setPanel( Q::Panel, Raised ); setPanel( Q::Panel, Raised );
} }
void QskSquiekSkin::initInputPredictionBar()
{
using namespace QskAspect;
using Q = QskInputPredictionBar;
const ColorPalette& pal = m_data->palette;
setMargins( Q::Panel | Padding, 5 );
setPanel( Q::Panel, Flat );
setButton( Q::ButtonPanel, Flat );
setButton( Q::ButtonPanel | QskPushButton::Pressed, Sunken );
setMetric( Q::ButtonPanel | MinimumWidth, qskDpiScaled( 30.0 ) );
setColor( Q::ButtonText, pal.themeForeground );
setColor( Q::ButtonText | QskPushButton::Disabled, pal.darker200 );
}
void QskSquiekSkin::initVirtualKeyboardHints() void QskSquiekSkin::initVirtualKeyboardHints()
{ {
using namespace QskAspect; using namespace QskAspect;

View File

@ -30,6 +30,7 @@ private:
void initDialogButtonBoxHints(); void initDialogButtonBoxHints();
void initFocusIndicatorHints(); void initFocusIndicatorHints();
void initInputPanelHints(); void initInputPanelHints();
void initInputPredictionBar();
void initVirtualKeyboardHints(); void initVirtualKeyboardHints();
void initListViewHints(); void initListViewHints();
void initPageIndicatorHints(); void initPageIndicatorHints();

View File

@ -193,6 +193,50 @@ bool QskInputContext::isAnimating() const
return false; return false;
} }
QskPopup* QskInputContext::createEmbeddingPopup( QskInputPanel* panel )
{
auto popup = new QskPopup();
popup->setAutoLayoutChildren( true );
popup->setTransparentForPositioner( false );
popup->setModal( true );
auto box = new QskLinearBox( popup );
box->addItem( panel );
/*
When the panel has an input proxy ( usually a local text input )
we don't need to see the input item and display the overlay
and align in the center of the window.
*/
const bool hasInputProxy = panel->hasInputProxy();
popup->setOverlay( hasInputProxy );
if ( hasInputProxy )
box->setMargins( QMarginsF( 5, 5, 5, 5 ) );
else
box->setExtraSpacingAt( Qt::TopEdge | Qt::LeftEdge | Qt::RightEdge );
return popup;
}
QskWindow* QskInputContext::createEmbeddingWindow( QskInputPanel* panel )
{
auto window = new QskWindow();
window->setFlags( window->flags() & Qt::Dialog );
//window->setModality( Qt::ApplicationModal );
window->setAutoLayoutChildren( true );
#if 0
window->setFlags( Qt::Tool | Qt::WindowDoesNotAcceptFocus );
#endif
panel->setParentItem( window->contentItem() );
return window;
}
void QskInputContext::showInputPanel() void QskInputContext::showInputPanel()
{ {
auto focusItem = qobject_cast< QQuickItem* >( qGuiApp->focusObject() ); auto focusItem = qobject_cast< QQuickItem* >( qGuiApp->focusObject() );
@ -209,9 +253,6 @@ void QskInputContext::showInputPanel()
m_data->inputItem = focusItem; m_data->inputItem = focusItem;
auto& inputPopup = m_data->inputPopup;
auto& inputWindow = m_data->inputWindow;
if ( qskInputPanel == nullptr ) if ( qskInputPanel == nullptr )
qskSetInputPanel( new QskInputPanel() ); qskSetInputPanel( new QskInputPanel() );
@ -227,66 +268,53 @@ void QskInputContext::showInputPanel()
{ {
// The input panel is embedded in a top level window // The input panel is embedded in a top level window
delete inputPopup; delete m_data->inputPopup;
if ( inputWindow == nullptr ) if ( m_data->inputWindow == nullptr )
{ {
inputWindow = new QskWindow(); auto window = createEmbeddingWindow( qskInputPanel );
inputWindow->setDeleteOnClose( true );
#if 0
inputWindow->setFlags( Qt::Tool | Qt::WindowDoesNotAcceptFocus );
#endif
qskInputPanel->setParentItem( inputWindow->contentItem() ); if ( window )
{
QSize size = window->effectivePreferredSize();
if ( size.isEmpty() )
{
// no idea, may be something based on the screen size
size = QSize( 800, 240 );
}
window->resize( size );
window->show();
window->setDeleteOnClose( true );
window->installEventFilter( this );
}
m_data->inputWindow = window;
} }
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 // The input panel is embedded in a popup
delete inputWindow; delete m_data->inputWindow;
if ( inputPopup == nullptr ) if ( m_data->inputPopup == nullptr )
{ {
inputPopup = new QskPopup( m_data->inputItem->window()->contentItem() ); auto popup = createEmbeddingPopup( qskInputPanel );
inputPopup->setAutoLayoutChildren( true ); if ( popup )
inputPopup->setTransparentForPositioner( false ); {
inputPopup->setModal( true ); popup->setParentItem( m_data->inputItem->window()->contentItem() );
if ( popup->parent() == nullptr )
popup->setParent( this );
auto box = new QskLinearBox( inputPopup ); popup->setVisible( true );
box->addItem( qskInputPanel ); popup->installEventFilter( this );
}
/* m_data->inputPopup = popup;
When the panel has an input proxy ( usually a local text input )
we don't need to see the input item and display the overlay
and align in the center of the window.
*/
const bool hasInputProxy = qskInputPanel->hasInputProxy();
inputPopup->setOverlay( hasInputProxy );
if ( hasInputProxy )
box->setMargins( QMarginsF( 5, 5, 5, 5 ) );
else
box->setExtraSpacingAt( Qt::TopEdge | Qt::LeftEdge | Qt::RightEdge );
} }
inputPopup->setParentItem( m_data->inputItem->window()->contentItem() );
inputPopup->setVisible( true );
inputPopup->installEventFilter( this );
} }
m_data->engine->setPredictor( m_data->engine->setPredictor(
@ -314,7 +342,17 @@ void QskInputContext::hideInputPanel()
focusItem->setFocus( false ); focusItem->setFocus( false );
} }
#endif #endif
}
if ( qskInputPanel )
{
qskInputPanel->setParentItem( nullptr );
qskInputPanel->attachInputItem( nullptr );
qskInputPanel->setEngine( nullptr );
}
if ( m_data->inputPopup )
{
m_data->inputPopup->deleteLater(); m_data->inputPopup->deleteLater();
} }
@ -327,14 +365,6 @@ void QskInputContext::hideInputPanel()
window->close(); // deleteOnClose is set window->close(); // deleteOnClose is set
} }
if ( qskInputPanel )
{
//qskInputPanel->setVisible( false );
qskInputPanel->setParentItem( nullptr );
qskInputPanel->attachInputItem( nullptr );
qskInputPanel->setEngine( nullptr );
}
m_data->inputItem = nullptr; m_data->inputItem = nullptr;
} }
@ -370,47 +400,46 @@ void QskInputContext::setFocusObject( QObject* focusObject )
return; return;
} }
bool doTerminate = true; const auto w = m_data->inputItem->window();
if ( w == nullptr )
return;
if ( focusObject == nullptr && m_data->inputPopup ) if ( m_data->inputWindow )
{ {
if ( const auto window = m_data->inputItem->window() ) if ( focusObject == nullptr )
{ {
auto focusItem = window->contentItem()->scopedFocusItem(); if ( m_data->inputItem->hasFocus() )
if ( focusItem == m_data->inputPopup )
doTerminate = false;
}
}
if ( doTerminate )
{
if ( m_data->inputWindow )
{
auto focusWindow = QGuiApplication::focusWindow();
if ( focusWindow == nullptr ||
QGuiApplication::focusWindow() == m_data->inputWindow )
{ {
doTerminate = false; /*
As long as the focus is noewhere and
the local focus stay on the input item
we don't care
*/
return;
} }
} }
else if ( m_data->inputPopup ) else
{ {
auto focusItem = qobject_cast< QQuickItem* >( focusObject ); const auto focusItem = qobject_cast< QQuickItem* >( focusObject );
if ( focusItem && focusItem->window() == m_data->inputWindow )
if ( ( focusItem == m_data->inputPopup ) return;
|| qskIsAncestorOf( m_data->inputPopup, focusItem ) ) }
{ }
doTerminate = false; else if ( m_data->inputPopup )
} {
if ( w->contentItem()->scopedFocusItem() == m_data->inputPopup )
{
/*
As long as the focus stays inside the inputPopup
we don't care
*/
return;
} }
} }
if ( doTerminate ) hideInputPanel();
{ m_data->inputItem = nullptr;
hideInputPanel();
m_data->inputItem = nullptr;
}
} }
void QskInputContext::registerPredictor( void QskInputContext::registerPredictor(

View File

@ -12,6 +12,8 @@
class QskTextPredictor; class QskTextPredictor;
class QskInputPanel; class QskInputPanel;
class QskPopup;
class QskWindow;
class QQuickItem; class QQuickItem;
class QSK_EXPORT QskInputContext : public QPlatformInputContext class QSK_EXPORT QskInputContext : public QPlatformInputContext
@ -58,6 +60,9 @@ public:
protected: protected:
virtual bool eventFilter( QObject*, QEvent* ) override; virtual bool eventFilter( QObject*, QEvent* ) override;
virtual QskPopup* createEmbeddingPopup( QskInputPanel* );
virtual QskWindow* createEmbeddingWindow( QskInputPanel* );
private: private:
class PrivateData; class PrivateData;
std::unique_ptr< PrivateData > m_data; std::unique_ptr< PrivateData > m_data;

View File

@ -240,8 +240,11 @@ QskInputEngine::Result QskInputEngine::processKey( int key,
QString QskInputEngine::predictiveText( int index ) const QString QskInputEngine::predictiveText( int index ) const
{ {
if ( m_data->predictor ) if ( const QskTextPredictor* predictor = m_data->predictor )
return m_data->predictor->candidate( index ); {
if ( index >= 0 && index < predictor->candidateCount() )
return predictor->candidate( index );
}
return QString(); return QString();
} }

View File

@ -110,14 +110,16 @@ static inline void qskSyncInputProxy(
namespace namespace
{ {
class TextInput final : public QskTextInput class TextInputProxy final : public QskTextInput
{ {
public: public:
TextInput( QQuickItem* parentItem = nullptr ): TextInputProxy( QQuickItem* parentItem = nullptr ):
QskTextInput( parentItem ) QskTextInput( parentItem )
{ {
setObjectName( "InputPanelInputProxy" ); setObjectName( "InputPanelInputProxy" );
#if 1
setFocusPolicy( Qt::NoFocus ); setFocusPolicy( Qt::NoFocus );
#endif
} }
protected: protected:
@ -154,7 +156,7 @@ public:
QskLinearBox* layout; QskLinearBox* layout;
QskTextLabel* prompt; QskTextLabel* prompt;
TextInput* inputProxy; TextInputProxy* inputProxy;
QskInputPredictionBar* predictionBar; QskInputPredictionBar* predictionBar;
QskVirtualKeyboard* keyboard; QskVirtualKeyboard* keyboard;
@ -175,7 +177,7 @@ QskInputPanel::QskInputPanel( QQuickItem* parent ):
m_data->prompt = new QskTextLabel(); m_data->prompt = new QskTextLabel();
m_data->prompt->setVisible( false ); m_data->prompt->setVisible( false );
m_data->inputProxy = new TextInput(); m_data->inputProxy = new TextInputProxy();
m_data->inputProxy->setVisible( m_data->hasInputProxy ); m_data->inputProxy->setVisible( m_data->hasInputProxy );
m_data->predictionBar = new QskInputPredictionBar(); m_data->predictionBar = new QskInputPredictionBar();
@ -331,9 +333,11 @@ void QskInputPanel::commitPredictiveText( int index )
if ( m_data->engine ) if ( m_data->engine )
{ {
const QString text = m_data->engine->predictiveText( index ); const QString text = m_data->engine->predictiveText( index );
m_data->engine->reset();
qskSendText( m_data->inputItem, text, true ); m_data->engine->reset();
m_data->predictionBar->setPrediction( QVector< QString >() );
qskSendText( m_data->receiverItem(), text, true );
} }
} }
@ -599,6 +603,18 @@ void QskInputPanel::keyPressEvent( QKeyEvent* event )
break; break;
} }
case Qt::Key_Shift:
case Qt::Key_Control:
case Qt::Key_Meta:
case Qt::Key_Alt:
case Qt::Key_AltGr:
case Qt::Key_CapsLock:
case Qt::Key_NumLock:
case Qt::Key_ScrollLock:
{
break;
}
default: default:
{ {
const auto text = event->text(); const auto text = event->text();