QskTextInput/QskInputPanel improvements

This commit is contained in:
Uwe Rathmann 2018-04-18 19:41:46 +02:00
parent 3f8616c084
commit 6e2e80d2de
13 changed files with 308 additions and 54 deletions

View File

@ -276,7 +276,9 @@ void QskInputContext::showInputPanel()
if ( inputPanel == nullptr )
{
auto panel = new QskInputPanel();
panel->setParent( this );
panel->setInputProxy( true );
setInputPanel( panel );
}
@ -301,7 +303,14 @@ void QskInputContext::showInputPanel()
inputPanel->setParentItem( inputWindow->contentItem() );
inputWindow->resize( 800, 240 ); // ### what size?
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() );
inputWindow->show();
inputWindow->installEventFilter( this );
@ -320,15 +329,28 @@ void QskInputContext::showInputPanel()
else
{
auto popup = new QskPopup( m_data->inputItem->window()->contentItem() );
popup->setAutoLayoutChildren( true );
popup->setTransparentForPositioner( false );
popup->setOverlay( false );
popup->setModal( true );
auto box = new QskLinearBox( popup );
box->setExtraSpacingAt( Qt::TopEdge | Qt::LeftEdge | Qt::RightEdge );
box->addItem( inputPanel );
if ( auto panel = qobject_cast< QskInputPanel* >( inputPanel ) )
{
if ( panel->hasInputProxy() )
{
popup->setOverlay( true );
}
}
if ( !popup->hasOverlay() )
{
box->setExtraSpacingAt( Qt::TopEdge | Qt::LeftEdge | Qt::RightEdge );
}
inputPopup = popup;
}

View File

@ -8,6 +8,7 @@
#include <QskDialogButtonBox.h>
#include <QskDialogButton.h>
#include <QskFocusIndicator.h>
#include <QskInputPanel.h>
#include <QskListView.h>
#include <QskPageIndicator.h>
#include <QskPushButton.h>
@ -127,6 +128,7 @@ void QskMaterialSkin::initHints()
initDialogButtonHints();
initFocusIndicatorHints();
initInputPanelHints();
initVirtualKeyboardHints();
initListViewHints();
initPageIndicatorHints();
initPopupHints();
@ -543,6 +545,19 @@ void QskMaterialSkin::initTabViewHints()
}
void QskMaterialSkin::initInputPanelHints()
{
using namespace QskAspect;
using Q = QskInputPanel;
const ColorPalette& pal = m_data->palette;
setBoxShape( Q::Panel, 0 );
setBoxBorderMetrics( Q::Panel, 0 );
setGradient( Q::Panel, pal.darker150 );
setBoxBorderColors( Q::Panel, pal.baseColor );
}
void QskMaterialSkin::initVirtualKeyboardHints()
{
using namespace QskAspect;
using Q = QskVirtualKeyboard;

View File

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

View File

@ -17,6 +17,7 @@
#include <QskTabButton.h>
#include <QskTabBar.h>
#include <QskTabView.h>
#include <QskInputPanel.h>
#include <QskVirtualKeyboard.h>
#include <QskScrollView.h>
#include <QskListView.h>
@ -242,6 +243,7 @@ void QskSquiekSkin::initHints()
initDialogButtonHints();
initFocusIndicatorHints();
initInputPanelHints();
initVirtualKeyboardHints();
initListViewHints();
initPageIndicatorHints();
initPopupHints();
@ -612,6 +614,15 @@ void QskSquiekSkin::initTabViewHints()
}
void QskSquiekSkin::initInputPanelHints()
{
using namespace QskAspect;
using Q = QskInputPanel;
setMargins( Q::Panel | Padding, 5 );
setPanel( Q::Panel, Raised );
}
void QskSquiekSkin::initVirtualKeyboardHints()
{
using namespace QskAspect;
using Q = QskVirtualKeyboard;

View File

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

View File

@ -657,6 +657,12 @@ static inline QMarginsF qskEffectivePadding( const QskSkinnable* skinnable,
);
}
QMarginsF QskSkinnable::innerPadding(
QskAspect::Aspect aspect, const QSizeF& outerBoxSize ) const
{
return qskEffectivePadding( this, aspect, outerBoxSize, true );
}
QSizeF QskSkinnable::innerBoxSize(
QskAspect::Aspect aspect, const QSizeF& outerBoxSize ) const
{

View File

@ -132,6 +132,8 @@ public:
QRectF innerBox( QskAspect::Aspect, const QRectF& outerBox ) const;
QRectF outerBox( QskAspect::Aspect, const QRectF& innerBox ) const;
QMarginsF innerPadding( QskAspect::Aspect, const QSizeF& ) const;
QRectF subControlRect( QskAspect::Subcontrol ) const;
virtual const QskSkinlet* effectiveSkinlet() const;
@ -157,8 +159,8 @@ protected:
void setSkinStateFlag( QskAspect::State, bool = true );
virtual void updateNode( QSGNode* );
QskSkinHintTable &hintTable();
const QskSkinHintTable &hintTable() const;
QskSkinHintTable& hintTable();
const QskSkinHintTable& hintTable() const;
private:
QVariant animatedValue( QskAspect::Aspect, QskSkinHintStatus* ) const;

View File

@ -13,6 +13,7 @@
#endif
QSK_QT_PRIVATE_BEGIN
// we need to access QQuickTextInputPrivate::hasAcceptableInput
#define private public
#include <private/qquicktextinput_p.h>
#include <private/qquicktextinput_p_p.h>
@ -89,6 +90,7 @@ namespace
{
auto d = QQuickTextInputPrivate::get( this );
// QQuickTextInputPrivate::checkIsValid ???
const auto state = static_cast< int >( d->hasAcceptableInput( d->m_text ) );
bool isAcceptable = ( state == QValidator::Acceptable );
@ -121,7 +123,7 @@ namespace
}
virtual QSGNode* updatePaintNode(
QSGNode *oldNode, UpdatePaintNodeData* data ) override
QSGNode* oldNode, UpdatePaintNodeData* data ) override
{
updateColors();
return Inherited::updatePaintNode( oldNode, data );
@ -146,7 +148,7 @@ namespace
void TextInput::setEditing( bool on )
{
auto d = QQuickTextInputPrivate::get( this );
if ( d->cursorVisible == on )
return;
@ -157,13 +159,6 @@ namespace
{
if ( d->m_passwordEchoEditing || d->m_passwordEchoTimer.isActive() )
d->updatePasswordEchoEditing( false );
const auto status = d->hasAcceptableInput( d->m_text );
if ( status == QQuickTextInputPrivate::AcceptableInput )
{
if ( d->fixup() )
Q_EMIT editingFinished();
}
}
polish();
@ -224,6 +219,7 @@ class QskTextInput::PrivateData
{
public:
TextInput* textInput;
QString description; // f.e. used as prompt in QskInputPanel
unsigned int activationModes : 3;
};
@ -281,20 +277,20 @@ bool QskTextInput::event( QEvent* event )
void QskTextInput::keyPressEvent( QKeyEvent* event )
{
if ( isEditing() )
if ( isEditing() )
{
switch( event->key() )
{
case Qt::Key_Enter:
case Qt::Key_Return:
{
if ( m_data->textInput->fixup() )
if ( fixup() )
{
QGuiApplication::inputMethod()->commit();
if ( !( inputMethodHints() & Qt::ImhMultiLine) )
if ( !( inputMethodHints() & Qt::ImhMultiLine ) )
setEditing( false );
}
}
break;
}
#if 1
@ -302,6 +298,7 @@ void QskTextInput::keyPressEvent( QKeyEvent* event )
{
QGuiApplication::inputMethod()->hide();
setEditing( false );
break;
}
#endif
@ -312,7 +309,7 @@ void QskTextInput::keyPressEvent( QKeyEvent* event )
}
return;
}
}
if ( ( m_data->activationModes & ActivationOnKey ) && !event->isAutoRepeat() )
{
@ -385,9 +382,9 @@ void QskTextInput::focusOutEvent( QFocusEvent* event )
#if 1
if ( event->reason() != Qt::ActiveWindowFocusReason
&& event->reason() != Qt::PopupFocusReason )
{
{
m_data->textInput->deselect();
}
}
#endif
if ( m_data->activationModes & ActivationOnFocus )
@ -444,6 +441,20 @@ void QskTextInput::setText( const QString& text )
m_data->textInput->setText( text );
}
void QskTextInput::setDescription( const QString& text )
{
if ( m_data->description != text )
{
m_data->description = text;
Q_EMIT descriptionChanged( text );
}
}
QString QskTextInput::description() const
{
return m_data->description;
}
QskTextInput::ActivationModes QskTextInput::activationModes() const
{
return static_cast< QskTextInput::ActivationModes >( m_data->activationModes );
@ -553,6 +564,15 @@ void QskTextInput::setEditing( bool on )
}
else
{
auto d = QQuickTextInputPrivate::get( m_data->textInput );
const auto status = d->hasAcceptableInput( d->m_text );
if ( status == QQuickTextInputPrivate::AcceptableInput )
{
if ( fixup() )
m_data->textInput->Q_EMIT editingFinished();
}
#if 0
inputMethod->reset();
#endif
@ -636,36 +656,36 @@ QString QskTextInput::displayText() const
QString QskTextInput::preeditText() const
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)
auto d = QQuickTextInputPrivate::get( m_data->textInput );
return d->m_textLayout.preeditAreaText();
return m_data->textInput->preeditText();
#else
return QString();
#endif
}
#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
bool QskTextInput::overwriteMode() const
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
return m_data->textInput->overwriteMode();
#else
return false;
#endif
}
void QskTextInput::setOverwriteMode( bool overwrite )
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
m_data->textInput->setOverwriteMode( overwrite );
#else
Q_UNUSED( overwrite )
#endif
}
#endif
bool QskTextInput::hasAcceptableInput() const
{
return m_data->textInput->hasAcceptableInput();
}
bool QskTextInput::fixup()
{
return m_data->textInput->fixup();
}
QVariant QskTextInput::inputMethodQuery(
Qt::InputMethodQuery property) const
{

View File

@ -16,6 +16,9 @@ class QSK_EXPORT QskTextInput : public QskControl
Q_PROPERTY( QString text READ text WRITE setText NOTIFY textChanged )
Q_PROPERTY( QString description READ description
WRITE setDescription NOTIFY descriptionChanged )
Q_PROPERTY( int fontRole READ fontRole
WRITE setFontRole NOTIFY fontRoleChanged )
@ -38,7 +41,7 @@ public:
{
NoActivation,
ActivationOnFocus = 1 << 0 ,
ActivationOnFocus = 1 << 0,
ActivationOnMouse = 1 << 1,
ActivationOnKey = 1 << 2,
@ -66,6 +69,9 @@ public:
QString text() const;
void setDescription( const QString& );
QString description() const;
void setFontRole( int role );
int fontRole() const;
@ -98,12 +104,16 @@ public:
void setEchoMode( EchoMode );
QString displayText() const;
QString preeditText() const;
#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
bool overwriteMode() const;
void setOverwriteMode( bool );
#endif
bool hasAcceptableInput() const;
virtual bool hasAcceptableInput() const;
virtual bool fixup();
virtual QVariant inputMethodQuery( Qt::InputMethodQuery ) const override;
QVariant inputMethodQuery( Qt::InputMethodQuery, QVariant argument) const;
@ -131,11 +141,16 @@ Q_SIGNALS:
void textChanged( const QString& );
void textEdited( const QString& );
void descriptionChanged( const QString& );
void textOptionsChanged();
void fontRoleChanged();
void alignmentChanged();
#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
void overwriteModeChanged( bool );
#endif
void maximumLengthChanged( int );
void echoModeChanged( EchoMode );

View File

@ -6,6 +6,8 @@
#include "QskInputPanel.h"
#include "QskVirtualKeyboard.h"
#include "QskInputSuggestionBar.h"
#include "QskTextInput.h"
#include "QskTextLabel.h"
#include "QskLinearBox.h"
#include <QString>
@ -99,31 +101,68 @@ QString qskNativeLocaleString( const QLocale& locale )
}
}
namespace
{
class TextInput : public QskTextInput
{
public:
TextInput( QQuickItem* parentItem = nullptr ):
QskTextInput( parentItem )
{
}
};
}
QSK_SUBCONTROL( QskInputPanel, Panel )
class QskInputPanel::PrivateData
{
public:
PrivateData():
hasInputProxy( true )
{
}
QskLinearBox* layout;
QskTextLabel* prompt;
TextInput* textInput;
QskInputSuggestionBar* suggestionBar;
QskVirtualKeyboard* keyboard;
bool hasInputProxy : 1;
};
QskInputPanel::QskInputPanel( QQuickItem* parent ):
QskControl( parent ),
Inherited( parent ),
m_data( new PrivateData() )
{
setAutoLayoutChildren( true );
initSizePolicy( QskSizePolicy::Expanding, QskSizePolicy::Constrained );
m_data->layout = new QskLinearBox( Qt::Vertical, this );
m_data->prompt = new QskTextLabel();
m_data->prompt->setVisible( false );
m_data->suggestionBar = new QskInputSuggestionBar( m_data->layout );
m_data->textInput = new TextInput();
m_data->textInput->setVisible( m_data->hasInputProxy );
m_data->suggestionBar = new QskInputSuggestionBar();
m_data->suggestionBar->setVisible( false );
m_data->keyboard = new QskVirtualKeyboard();
auto layout = new QskLinearBox( Qt::Vertical, this );
layout->addItem( m_data->prompt, Qt::AlignLeft | Qt::AlignHCenter );
layout->addItem( m_data->textInput, Qt::AlignLeft | Qt::AlignHCenter );
layout->addStretch( 10 );
layout->addItem( m_data->suggestionBar );
layout->addItem( m_data->keyboard );
m_data->layout = layout;
connect( m_data->suggestionBar, &QskInputSuggestionBar::suggested,
this, &QskInputPanel::commitCandidate );
m_data->keyboard = new QskVirtualKeyboard( m_data->layout );
connect( m_data->keyboard, &QskVirtualKeyboard::keySelected,
this, &QskInputPanel::commitKey );
}
@ -132,6 +171,15 @@ QskInputPanel::~QskInputPanel()
{
}
QskAspect::Subcontrol QskInputPanel::effectiveSubcontrol(
QskAspect::Subcontrol subControl ) const
{
if( subControl == QskBox::Panel )
return QskInputPanel::Panel;
return subControl;
}
qreal QskInputPanel::heightForWidth( qreal width ) const
{
/*
@ -143,14 +191,26 @@ qreal QskInputPanel::heightForWidth( qreal width ) const
width -= margins.left() + margins.right();
const auto padding = innerPadding(
Panel, QSizeF( width, width ) );
width -= padding.left() + padding.right();
qreal height = m_data->keyboard->heightForWidth( width );
if ( m_data->suggestionBar->isVisible() )
const QskControl* controls[] =
{ m_data->prompt, m_data->textInput, m_data->suggestionBar };
for ( auto control : controls )
{
height += m_data->layout->spacing();
height += m_data->suggestionBar->sizeHint().height();
if ( control->isVisible() )
{
height += m_data->layout->spacing();
height += control->sizeHint().height();
}
}
height += padding.top() + padding.bottom();
height += margins.top() + margins.bottom();
return height;
@ -162,18 +222,72 @@ qreal QskInputPanel::widthForHeight( qreal height ) const
height -= margins.top() + margins.bottom();
if ( m_data->suggestionBar->isVisible() )
const auto padding = innerPadding(
Panel, QSizeF( height, height ) );
height -= padding.top() + padding.bottom();
const QskControl* controls[] =
{ m_data->prompt, m_data->textInput, m_data->suggestionBar };
for ( auto control : controls )
{
height -= m_data->layout->spacing();
height -= m_data->suggestionBar->sizeHint().height();
if ( control->isVisible() )
{
height -= m_data->layout->spacing();
height -= control->sizeHint().height();
}
}
qreal width = m_data->keyboard->widthForHeight( height );
width += padding.left() + padding.right();
width += margins.left() + margins.right();
return width;
}
QString QskInputPanel::inputPrompt() const
{
return m_data->prompt->text();
}
void QskInputPanel::setInputPrompt( const QString& text )
{
auto prompt = m_data->prompt;
if ( text != prompt->text() )
{
prompt->setText( text );
if ( m_data->hasInputProxy )
prompt->setVisible( !text.isEmpty() );
Q_EMIT inputPromptChanged( text );
}
}
bool QskInputPanel::hasInputProxy() const
{
return m_data->hasInputProxy;
}
void QskInputPanel::setInputProxy( bool on )
{
if ( m_data->hasInputProxy == on )
return;
m_data->hasInputProxy = on;
m_data->textInput->setVisible( on );
auto prompt = m_data->prompt;
if ( on )
prompt->setVisible( !prompt->text().isEmpty() );
else
prompt->setVisible( false );
}
bool QskInputPanel::isCandidatesEnabled() const
{
return m_data->suggestionBar->isVisible();

View File

@ -7,37 +7,58 @@
#define QSK_INPUT_PANEL_H
#include "QskGlobal.h"
#include "QskControl.h"
#include "QskBox.h"
class QString;
class QLocale;
template class QVector< QString >;
class QSK_EXPORT QskInputPanel: public QskControl
class QSK_EXPORT QskInputPanel: public QskBox
{
Q_OBJECT
using Inherited = QskControl;
using Inherited = QskBox;
Q_PROPERTY( bool inputProxy READ hasInputProxy
WRITE setInputProxy NOTIFY inputProxyChanged )
Q_PROPERTY( QString inputPrompt READ inputPrompt
WRITE setInputPrompt NOTIFY inputPromptChanged )
public:
QSK_SUBCONTROLS( Panel )
enum Action
{
Compose = 0x10,
SelectCandidate = 0x11,
SelectCandidate = 0x11
};
Q_ENUM( Action )
QskInputPanel( QQuickItem* parent = nullptr );
virtual ~QskInputPanel() override;
bool hasInputProxy() const;
QString inputPrompt() const;
bool isCandidatesEnabled() const;
QVector< QString > candidates() const;
virtual qreal heightForWidth( qreal width ) const override;
virtual qreal widthForHeight( qreal height ) const override;
virtual QskAspect::Subcontrol effectiveSubcontrol(
QskAspect::Subcontrol ) const override;
Q_SIGNALS:
void inputProxyChanged( bool );
void inputPromptChanged( const QString& );
public Q_SLOTS:
void setInputPrompt( const QString& );
void setInputProxy( bool );
void setCandidatesEnabled( bool );
void setCandidates( const QVector< QString >& );

View File

@ -260,20 +260,38 @@ QskAspect::Subcontrol QskVirtualKeyboard::effectiveSubcontrol(
return subControl;
}
QskVirtualKeyboard::Mode QskVirtualKeyboard::mode() const
{
return m_data->mode;
}
QSizeF QskVirtualKeyboard::contentsSizeHint() const
{
constexpr qreal ratio = qreal( RowCount ) / ColumnCount;
const qreal w = 600;
return QSizeF( w, ratio * w );
}
qreal QskVirtualKeyboard::heightForWidth( qreal width ) const
{
/*
Not necessarily correct, when
subControlRect( Panel ) != contentsRect: TODO ...
*/
constexpr qreal ratio = qreal( RowCount ) / ColumnCount;
const auto margins = this->margins();
width -= margins.left() + margins.right();
const auto padding = innerPadding(
Panel, QSizeF( width, width ) );
width -= padding.left() + padding.right();
qreal height = width * ratio;
height += padding.top() + padding.bottom();
height += margins.top() + margins.bottom();
return height;
@ -286,10 +304,17 @@ qreal QskVirtualKeyboard::widthForHeight( qreal height ) const
height -= margins.top() + margins.bottom();
qreal width = height / ratio;
width += margins.left() + margins.right();
const auto padding = innerPadding(
Panel, QSizeF( height, height ) );
return height;
height -= padding.top() + padding.bottom();
qreal width = height / ratio;
width += padding.left() + padding.right();
width += padding.left() + padding.right();
return width;
}
void QskVirtualKeyboard::updateLayout()

View File

@ -40,6 +40,7 @@ public:
virtual qreal heightForWidth( qreal width ) const override;
virtual qreal widthForHeight( qreal height ) const override;
virtual QSizeF contentsSizeHint() const override;
Q_SIGNALS:
void modeChanged( Mode );