improving QskInputContext

This commit is contained in:
Uwe Rathmann 2018-04-12 12:03:51 +02:00
parent 0ee83c1e32
commit d1ecec2ad8
6 changed files with 169 additions and 56 deletions

View File

@ -10,6 +10,7 @@
#include "QskHunspellCompositionModel.h"
#include "QskInputPanel.h"
#include "QskLinearBox.h"
#include <QskDialog.h>
#include <QskPopup.h>
#include <QskWindow.h>
@ -91,8 +92,8 @@ public:
QPointer< QQuickItem > inputPanel;
// popup or window embedding the inputPanel
QPointer< QskPopup > inputPopup;
QPointer< QskWindow > inputWindow;
QskPopup* inputPopup = nullptr;
QskWindow* inputWindow = nullptr;
QskInputCompositionModel* compositionModel;
QHash< QLocale, QskInputCompositionModel* > compositionModels;
@ -320,9 +321,14 @@ void QskInputContext::showInputPanel()
{
auto popup = new QskPopup( m_data->inputItem->window()->contentItem() );
popup->setAutoLayoutChildren( true );
popup->setTransparentForPositioner( false );
popup->setOverlay( false );
popup->setModal( true );
inputPanel->setParentItem( popup );
auto box = new QskLinearBox( popup );
box->setExtraSpacingAt( Qt::TopEdge | Qt::LeftEdge | Qt::RightEdge );
box->addItem( inputPanel );
inputPopup = popup;
}
@ -343,7 +349,6 @@ void QskInputContext::showInputPanel()
}
}
inputPopup->setGeometry( qskItemGeometry( inputPopup->parentItem() ) );
inputPopup->setVisible( true );
}
@ -363,19 +368,13 @@ void QskInputContext::hideInputPanel()
if ( m_data->inputPopup == m_data->inputPanel )
{
m_data->inputPopup->removeEventFilter( this );
m_data->inputPopup = nullptr;
}
else
{
if ( m_data->inputPopup )
{
auto popup = m_data->inputPopup.data();
m_data->inputPopup = nullptr;
popup->deleteLater();
}
m_data->inputPopup->deleteLater();
}
QskWindow* window = m_data->inputWindow;
@ -599,40 +598,78 @@ void QskInputContext::commit()
bool QskInputContext::eventFilter( QObject* object, QEvent* event )
{
switch( static_cast< int >( event->type() ) )
if ( object == m_data->inputWindow )
{
case QEvent::Move:
case QEvent::Resize:
switch( event->type() )
{
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
holds the focus. But this does not work, when a virtual
keyboard is used, where you can navigate and select inside.
So we have to fix the receiver.
*/
if ( ( object != m_data->inputItem )
&& qskIsAncestorOf( m_data->inputPanel, m_data->inputItem ) )
case QEvent::Move:
{
sendEventToInputItem( event );
return true;
}
if ( m_data->inputPanel )
emitKeyboardRectChanged();
break;
break;
}
case QEvent::Resize:
{
QQuickItem* panel = m_data->inputPanel;
if ( m_data->inputPanel )
m_data->inputPanel->setSize( m_data->inputWindow->size() );
break;
}
case QEvent::DeferredDelete:
{
object->removeEventFilter( this );
qGuiApp->removeEventFilter( this );
m_data->inputWindow = nullptr;
break;
}
default:
break;
}
}
else
{
switch( static_cast<int>( event->type() ) )
{
case QskEvent::GeometryChange:
{
if ( object == m_data->inputPanel )
{
if ( event->type() == QskEvent::GeometryChange )
emitKeyboardRectChanged();
}
break;
}
case QEvent::InputMethodQuery:
{
/*
Qt/Quick expects that the item associated with the input context
holds the focus. But this does not work, when a virtual
keyboard is used, where you can navigate and select inside.
So we have to fix the receiver.
*/
if ( ( object != m_data->inputItem )
&& qskIsAncestorOf( m_data->inputPanel, m_data->inputItem ) )
{
sendEventToInputItem( event );
return true;
}
break;
}
case QEvent::DeferredDelete:
{
if ( object == m_data->inputPopup )
{
object->removeEventFilter( this );
qGuiApp->removeEventFilter( this );
m_data->inputPopup = nullptr;
}
break;
}
}
}

View File

@ -160,7 +160,7 @@ int main( int argc, char* argv[] )
SkinnyFont::init( &app );
SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts );
#if 0
#if 1
// We don't want to have a top level window.
qskDialog->setPolicy( QskDialog::EmbeddedBox );
#endif
@ -195,7 +195,7 @@ int main( int argc, char* argv[] )
window.addItem( box );
window.addItem( new QskFocusIndicator() );
window.resize( 800, 300 );
window.resize( 600, 600 );
window.show();
return app.exec();

View File

@ -102,6 +102,7 @@ QString qskNativeLocaleString( const QLocale& locale )
class QskInputPanel::PrivateData
{
public:
QskLinearBox* layout;
QskInputSuggestionBar* suggestionBar;
QskVirtualKeyboard* keyboard;
};
@ -111,16 +112,17 @@ QskInputPanel::QskInputPanel( QQuickItem* parent ):
m_data( new PrivateData() )
{
setAutoLayoutChildren( true );
initSizePolicy( QskSizePolicy::Expanding, QskSizePolicy::Constrained );
auto layout = new QskLinearBox( Qt::Vertical, this );
m_data->layout = new QskLinearBox( Qt::Vertical, this );
m_data->suggestionBar = new QskInputSuggestionBar( layout );
m_data->suggestionBar = new QskInputSuggestionBar( m_data->layout );
m_data->suggestionBar->setVisible( false );
connect( m_data->suggestionBar, &QskInputSuggestionBar::suggested,
this, &QskInputPanel::commitCandidate );
m_data->keyboard = new QskVirtualKeyboard( layout );
m_data->keyboard = new QskVirtualKeyboard( m_data->layout );
connect( m_data->keyboard, &QskVirtualKeyboard::keySelected,
this, &QskInputPanel::commitKey );
@ -130,6 +132,48 @@ QskInputPanel::~QskInputPanel()
{
}
qreal QskInputPanel::heightForWidth( qreal width ) const
{
/*
This code looks like as it could be generalized
and moved to QskLinearBox. TODO ...
*/
const auto margins = this->margins();
width -= margins.left() + margins.right();
qreal height = m_data->keyboard->heightForWidth( width );
if ( m_data->suggestionBar->isVisible() )
{
height += m_data->layout->spacing();
height += m_data->suggestionBar->sizeHint().height();
}
height += margins.top() + margins.bottom();
return height;
}
qreal QskInputPanel::widthForHeight( qreal height ) const
{
const auto margins = this->margins();
height -= margins.top() + margins.bottom();
if ( m_data->suggestionBar->isVisible() )
{
height -= m_data->layout->spacing();
height -= m_data->suggestionBar->sizeHint().height();
}
qreal width = m_data->keyboard->widthForHeight( height );
width += margins.left() + margins.right();
return width;
}
bool QskInputPanel::isCandidatesEnabled() const
{
return m_data->suggestionBar->isVisible();

View File

@ -34,6 +34,9 @@ public:
bool isCandidatesEnabled() const;
QVector< QString > candidates() const;
virtual qreal heightForWidth( qreal width ) const override;
virtual qreal widthForHeight( qreal height ) const override;
public Q_SLOTS:
void setCandidatesEnabled( bool );
void setCandidates( const QVector< QString >& );

View File

@ -15,10 +15,10 @@ namespace
enum
{
RowCount = 5,
KeyCount = 12
ColumnCount = 12
};
using KeyRow = Qt::Key[KeyCount];
using KeyRow = Qt::Key[ ColumnCount ];
class Button final : public QskPushButton
{
@ -60,7 +60,7 @@ struct QskVirtualKeyboardLayouts
{
struct KeyCodes
{
using Row = Qt::Key[ KeyCount ];
using Row = Qt::Key[ ColumnCount ];
Row data[ RowCount ];
};
@ -137,7 +137,7 @@ static qreal qskRowStretch( const KeyRow& keyRow )
if( stretch == 0.0 )
{
stretch = KeyCount;
stretch = ColumnCount;
}
return stretch;
@ -216,16 +216,16 @@ QskVirtualKeyboard::QskVirtualKeyboard( QQuickItem* parent ):
m_data( new PrivateData )
{
setPolishOnResize( true );
initSizePolicy( QskSizePolicy::Expanding, QskSizePolicy::Expanding );
initSizePolicy( QskSizePolicy::Expanding, QskSizePolicy::Constrained );
m_data->keyButtons.reserve( RowCount * KeyCount );
m_data->keyButtons.reserve( RowCount * ColumnCount );
const auto autoRepeatInterval =
1000 / QGuiApplication::styleHints()->keyboardAutoRepeatRate();
for ( int row = 0; row < RowCount; row++ )
{
for ( int col = 0; col < KeyCount; col++ )
for ( int col = 0; col < ColumnCount; col++ )
{
auto button = new Button( row, col, this );
button->installEventFilter( this );
@ -266,6 +266,32 @@ QskVirtualKeyboard::Mode QskVirtualKeyboard::mode() const
return m_data->mode;
}
qreal QskVirtualKeyboard::heightForWidth( qreal width ) const
{
constexpr qreal ratio = qreal( RowCount ) / ColumnCount;
const auto margins = this->margins();
width -= margins.left() + margins.right();
qreal height = width * ratio;
height += margins.top() + margins.bottom();
return height;
}
qreal QskVirtualKeyboard::widthForHeight( qreal height ) const
{
constexpr qreal ratio = qreal( RowCount ) / ColumnCount;
const auto margins = this->margins();
height -= margins.top() + margins.bottom();
qreal width = height / ratio;
width += margins.left() + margins.right();
return height;
}
void QskVirtualKeyboard::updateLayout()
{
const auto r = layoutRect();
@ -290,7 +316,7 @@ void QskVirtualKeyboard::updateLayout()
auto totalHSpacing = -spacing;
if ( spacing )
{
for ( int col = 0; col < KeyCount; col++ )
for ( int col = 0; col < ColumnCount; col++ )
{
if ( keys[ col ] != Qt::Key( 0 ) )
totalHSpacing += spacing;
@ -300,10 +326,10 @@ void QskVirtualKeyboard::updateLayout()
const auto baseKeyWidth = ( r.width() - totalHSpacing ) / qskRowStretch( keys );
qreal xPos = r.left();
for ( int col = 0; col < KeyCount; col++ )
for ( int col = 0; col < ColumnCount; col++ )
{
const Qt::Key key = keys[ col ];
auto button = m_data->keyButtons[ row * KeyCount + col ];
auto button = m_data->keyButtons[ row * ColumnCount + col ];
button->setVisible( key != Qt::Key( 0 ) );

View File

@ -38,6 +38,9 @@ public:
void setMode( Mode );
Mode mode() const;
virtual qreal heightForWidth( qreal width ) const override;
virtual qreal widthForHeight( qreal height ) const override;
Q_SIGNALS:
void modeChanged( Mode );
void keySelected( Qt::Key );