QSkinny 0.8.0
C++/Qt UI toolkit
All Classes Namespaces Functions Variables Enumerations Enumerator Properties Pages
QskInputPanelBox.cpp
1/******************************************************************************
2 * QSkinny - Copyright (C) The authors
3 * SPDX-License-Identifier: BSD-3-Clause
4 *****************************************************************************/
5
6#include "QskInputPanelBox.h"
7#include "QskInputPredictionBar.h"
8#include "QskLinearBox.h"
9#include "QskTextField.h"
10#include "QskTextLabel.h"
11#include "QskVirtualKeyboard.h"
12
13#include <qpointer.h>
14#include <qstring.h>
15
16static void qskSetupInput( const QQuickItem* item, QskTextInput* textInput )
17{
18 if ( item == nullptr )
19 return;
20
21 // finding attributes from the input hints of item
22
23 int maxCharacters = 32767;
24 QskTextInput::EchoMode echoMode = QskTextInput::Normal;
25
26 Qt::InputMethodQueries queries = Qt::ImQueryAll;
27 queries &= ~Qt::ImEnabled;
28
29 QInputMethodQueryEvent event( queries );
30 QCoreApplication::sendEvent( const_cast< QQuickItem* >( item ), &event );
31
32 if ( event.queries() & Qt::ImHints )
33 {
34 const auto hints = static_cast< Qt::InputMethodHints >(
35 event.value( Qt::ImHints ).toInt() );
36
37 if ( hints & Qt::ImhHiddenText )
38 echoMode = QskTextInput::NoEcho;
39 }
40
41 if ( event.queries() & Qt::ImMaximumTextLength )
42 {
43 // needs to be handled before Qt::ImCursorPosition !
44
45 const auto value = event.value( Qt::ImMaximumTextLength );
46 if ( value.isValid() )
47 maxCharacters = qBound( 0, value.toInt(), maxCharacters );
48 }
49
50 textInput->setMaxLength( maxCharacters );
51
52 if ( event.queries() & Qt::ImSurroundingText )
53 {
54 const auto text = event.value( Qt::ImSurroundingText ).toString();
55 textInput->setText( text );
56 }
57
58 if ( event.queries() & Qt::ImCursorPosition )
59 {
60 const auto pos = event.value( Qt::ImCursorPosition ).toInt();
61 textInput->setCursorPosition( pos );
62 }
63
64 if ( event.queries() & Qt::ImCurrentSelection )
65 {
66#if 0
67 const auto text = event.value( Qt::ImCurrentSelection ).toString();
68 if ( !text.isEmpty() )
69 {
70 }
71#endif
72 }
73
74 int passwordMaskDelay = -1;
75 QString passwordCharacter;
76
77 if ( echoMode == QskTextInput::NoEcho )
78 {
79 /*
80 Qt::ImhHiddenText does not provide information
81 to decide between NoEcho/Password, or provides
82 more details about how to deal with hidden inputs.
83 So we try to find out more from trying some properties.
84 */
85
86 QVariant value;
87
88 value = item->property( "passwordMaskDelay" );
89 if ( value.canConvert< int >() )
90 passwordMaskDelay = value.toInt();
91
92 value = item->property( "passwordCharacter" );
93 if ( value.canConvert< QString >() )
94 passwordCharacter = value.toString();
95
96 value = item->property( "echoMode" );
97 if ( value.canConvert< int >() )
98 {
99 const auto mode = value.toInt();
100 if ( mode == QskTextInput::Password )
101 echoMode = QskTextInput::Password;
102 }
103 }
104
105 if ( passwordMaskDelay >= 0 )
106 textInput->setPasswordMaskDelay( passwordMaskDelay );
107 else
108 textInput->resetPasswordMaskDelay();
109
110 if ( !passwordCharacter.isEmpty() )
111 textInput->setPasswordCharacter( passwordCharacter );
112 else
113 textInput->resetPasswordCharacter();
114
115 textInput->setEchoMode( echoMode );
116}
117
118namespace
119{
120 class TextFieldProxy final : public QskTextField
121 {
122 public:
123 TextFieldProxy( QskInputPanelBox* panelBox, QQuickItem* parentItem = nullptr )
124 : QskTextField( parentItem )
125 , m_panelBox( panelBox )
126 {
127 setObjectName( QStringLiteral( "InputBoxProxy" ) );
128
129 setFocusPolicy( Qt::NoFocus );
130 initSizePolicy( QskSizePolicy::Ignored, QskSizePolicy::Fixed );
131 }
132
133 protected:
134 QskAspect::Subcontrol substitutedSubcontrol(
135 QskAspect::Subcontrol subControl ) const override
136 {
137 if ( subControl == QskTextField::TextPanel )
138 return m_panelBox->effectiveSubcontrol( QskInputPanelBox::ProxyPanel );
139
140 if ( subControl == QskTextField::Text )
141 return m_panelBox->effectiveSubcontrol( QskInputPanelBox::ProxyText );
142
143 return subControl;
144 }
145
146 void focusInEvent( QFocusEvent* ) override
147 {
148 }
149
150 void focusOutEvent( QFocusEvent* ) override
151 {
152 }
153
154 private:
155 QskInputPanelBox* m_panelBox;
156 };
157}
158
159QSK_SUBCONTROL( QskInputPanelBox, Panel )
160QSK_SUBCONTROL( QskInputPanelBox, ProxyPanel )
161QSK_SUBCONTROL( QskInputPanelBox, ProxyText )
162
163class QskInputPanelBox::PrivateData
164{
165 public:
166 QPointer< QQuickItem > inputItem;
167
168 QskLinearBox* layout;
169 QskTextLabel* prompt;
170 TextFieldProxy* inputProxy;
171 QskInputPredictionBar* predictionBar;
172 QskVirtualKeyboard* keyboard;
173
174 QskInputPanelBox::PanelHints panelHints = QskInputPanelBox::InputProxy;
175};
176
177QskInputPanelBox::QskInputPanelBox( QQuickItem* parent )
178 : Inherited( parent )
179 , m_data( new PrivateData() )
180{
181 setAutoLayoutChildren( true );
182
183 m_data->prompt = new QskTextLabel();
184 m_data->prompt->setVisible( false );
185
186 m_data->inputProxy = new TextFieldProxy( this, nullptr );
187 m_data->inputProxy->setVisible(
188 m_data->panelHints & QskInputPanelBox::InputProxy );
189
190 m_data->predictionBar = new QskInputPredictionBar();
191 m_data->predictionBar->setVisible(
192 m_data->panelHints & QskInputPanelBox::Prediction );
193
194 m_data->keyboard = new QskVirtualKeyboard();
195
196 auto layout = new QskLinearBox( Qt::Vertical, this );
197 layout->setDefaultAlignment( Qt::AlignLeft | Qt::AlignHCenter );
198 layout->setSpacing( 0 );
199
200 layout->addItem( m_data->prompt );
201 layout->addItem( m_data->inputProxy );
202 layout->addStretch( 10 );
203 layout->addItem( m_data->predictionBar );
204 layout->addItem( m_data->keyboard );
205
206 m_data->layout = layout;
207
208 connect( m_data->predictionBar, &QskInputPredictionBar::predictiveTextSelected,
209 this, &QskInputPanelBox::predictiveTextSelected );
210
211 connect( m_data->keyboard, &QskVirtualKeyboard::keySelected,
212 this, &QskInputPanelBox::keySelected );
213}
214
215QskInputPanelBox::~QskInputPanelBox()
216{
217}
218
219void QskInputPanelBox::setPanelHint( PanelHint hint, bool on )
220{
221 if ( on )
222 setPanelHints( m_data->panelHints | hint );
223 else
224 setPanelHints( m_data->panelHints & ~hint );
225}
226
227void QskInputPanelBox::setPanelHints( PanelHints hints )
228{
229 if ( hints == m_data->panelHints )
230 return;
231
232 m_data->panelHints = hints;
233
234 m_data->inputProxy->setVisible( hints & QskInputPanelBox::InputProxy );
235 m_data->predictionBar->setVisible( hints & QskInputPanelBox::Prediction );
236
237 const bool showPrompt = ( hints & QskInputPanelBox::InputProxy ) &&
238 !m_data->prompt->text().isEmpty();
239
240 m_data->prompt->setVisible( showPrompt );
241
242 Q_EMIT panelHintsChanged();
243}
244
245QskInputPanelBox::PanelHints QskInputPanelBox::panelHints() const
246{
247 return m_data->panelHints;
248}
249
250void QskInputPanelBox::attachInputItem( QQuickItem* item )
251{
252 if ( item == m_data->inputItem )
253 return;
254
255 m_data->inputItem = item;
256
257 if ( item )
258 {
259 if ( m_data->panelHints & QskInputPanelBox::InputProxy )
260 {
261 qskSetupInput( item, m_data->inputProxy );
262 m_data->inputProxy->setEditing( true );
263 }
264 }
265}
266
267QQuickItem* QskInputPanelBox::attachedInputItem() const
268{
269 return m_data->inputItem;
270}
271
272QQuickItem* QskInputPanelBox::inputProxy() const
273{
274 if ( m_data->panelHints & QskInputPanelBox::InputProxy )
275 return m_data->inputProxy;
276
277 return nullptr;
278}
279
280QskAspect::Subcontrol QskInputPanelBox::substitutedSubcontrol(
281 QskAspect::Subcontrol subControl ) const
282{
283 if ( subControl == QskBox::Panel )
284 return QskInputPanelBox::Panel;
285
286#if 1
287 // TODO ...
288 if ( subControl == QskInputPanelBox::ProxyPanel )
289 return QskTextField::TextPanel;
290
291 if ( subControl == QskInputPanelBox::ProxyText )
292 return QskTextField::Text;
293#endif
294
295 return subControl;
296}
297
298QString QskInputPanelBox::inputPrompt() const
299{
300 return m_data->prompt->text();
301}
302
303void QskInputPanelBox::setInputPrompt( const QString& text )
304{
305 auto prompt = m_data->prompt;
306
307 if ( text != prompt->text() )
308 {
309 prompt->setText( text );
310
311 if ( m_data->panelHints & QskInputPanelBox::InputProxy )
312 prompt->setVisible( !text.isEmpty() );
313
314 Q_EMIT inputPromptChanged( text );
315 }
316}
317
318void QskInputPanelBox::setPrediction( const QStringList& prediction )
319{
320 m_data->predictionBar->setPrediction( prediction );
321}
322
323void QskInputPanelBox::setKeyboard( QskVirtualKeyboard* keyboard )
324{
325 if( m_data->keyboard )
326 {
327 m_data->keyboard->deleteLater();
328 }
329
330 m_data->keyboard = keyboard;
331 m_data->layout->addItem( m_data->keyboard );
332
333 connect( m_data->keyboard, &QskVirtualKeyboard::keySelected,
334 this, &QskInputPanelBox::keySelected );
335}
336
337void QskInputPanelBox::keyPressEvent( QKeyEvent* event )
338{
339 int keyCode = -1;
340
341 switch ( event->key() )
342 {
343 case Qt::Key_Return:
344 case Qt::Key_Escape:
345 {
346 Q_EMIT keySelected( event->key() );
347 break;
348 }
349
350 default:
351 {
352 const auto text = event->text();
353
354 if ( !text.isEmpty() )
355 keyCode = text[ 0 ].unicode();
356 else
357 keyCode = event->key();
358
359 if ( m_data->keyboard->hasKey( keyCode ) )
360 {
361 // animating the corresponding key button ???
362 Q_EMIT keySelected( keyCode );
363 }
364 }
365 }
366}
367
368#include "moc_QskInputPanelBox.cpp"
Subcontrol
For use within the rendering or lay-outing of a specific QskSkinnable.
Definition QskAspect.h:104
void initSizePolicy(QskSizePolicy::Policy, QskSizePolicy::Policy)
Layout stringing items in rows and columns.