1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). |
4 | ** Contact: http://www.qt-project.org/legal |
5 | ** |
6 | ** This file is part of the QtDeclarative module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and Digia. For licensing terms and |
14 | ** conditions see http://qt.digia.com/licensing. For further information |
15 | ** use the contact form at http://qt.digia.com/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 2.1 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 2.1 requirements |
23 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
24 | ** |
25 | ** In addition, as a special exception, Digia gives you certain additional |
26 | ** rights. These rights are described in the Digia Qt LGPL Exception |
27 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
28 | ** |
29 | ** GNU General Public License Usage |
30 | ** Alternatively, this file may be used under the terms of the GNU |
31 | ** General Public License version 3.0 as published by the Free Software |
32 | ** Foundation and appearing in the file LICENSE.GPL included in the |
33 | ** packaging of this file. Please review the following information to |
34 | ** ensure the GNU General Public License version 3.0 requirements will be |
35 | ** met: http://www.gnu.org/copyleft/gpl.html. |
36 | ** |
37 | ** |
38 | ** $QT_END_LICENSE$ |
39 | ** |
40 | ****************************************************************************/ |
41 | |
42 | #include "private/qdeclarativetextinput_p.h" |
43 | #include "private/qdeclarativetextinput_p_p.h" |
44 | |
45 | #include <private/qdeclarativeglobal_p.h> |
46 | #include <qdeclarativeinfo.h> |
47 | |
48 | #include <QValidator> |
49 | #include <QTextCursor> |
50 | #include <QApplication> |
51 | #include <QFontMetrics> |
52 | #include <QPainter> |
53 | #include <QTextBoundaryFinder> |
54 | #include <QInputContext> |
55 | #include <qstyle.h> |
56 | |
57 | #ifndef QT_NO_LINEEDIT |
58 | |
59 | QT_BEGIN_NAMESPACE |
60 | |
61 | /*! |
62 | \qmlclass TextInput QDeclarativeTextInput |
63 | \ingroup qml-basic-visual-elements |
64 | \since 4.7 |
65 | \brief The TextInput item displays an editable line of text. |
66 | \inherits Item |
67 | |
68 | The TextInput element displays a single line of editable plain text. |
69 | |
70 | TextInput is used to accept a line of text input. Input constraints |
71 | can be placed on a TextInput item (for example, through a \l validator or \l inputMask), |
72 | and setting \l echoMode to an appropriate value enables TextInput to be used for |
73 | a password input field. |
74 | |
75 | On Mac OS X, the Up/Down key bindings for Home/End are explicitly disabled. |
76 | If you want such bindings (on any platform), you will need to construct them in QML. |
77 | |
78 | \sa TextEdit, Text, {declarative/text/textselection}{Text Selection example} |
79 | */ |
80 | QDeclarativeTextInput::QDeclarativeTextInput(QDeclarativeItem* parent) |
81 | : QDeclarativeImplicitSizePaintedItem(*(new QDeclarativeTextInputPrivate), parent) |
82 | { |
83 | Q_D(QDeclarativeTextInput); |
84 | d->init(); |
85 | } |
86 | |
87 | QDeclarativeTextInput::~QDeclarativeTextInput() |
88 | { |
89 | } |
90 | |
91 | /*! |
92 | \qmlproperty string TextInput::text |
93 | |
94 | The text in the TextInput. |
95 | */ |
96 | |
97 | QString QDeclarativeTextInput::text() const |
98 | { |
99 | Q_D(const QDeclarativeTextInput); |
100 | return d->control->text(); |
101 | } |
102 | |
103 | void QDeclarativeTextInput::setText(const QString &s) |
104 | { |
105 | Q_D(QDeclarativeTextInput); |
106 | if(s == text()) |
107 | return; |
108 | d->control->setText(s); |
109 | } |
110 | |
111 | /*! |
112 | \qmlproperty string TextInput::font.family |
113 | |
114 | Sets the family name of the font. |
115 | |
116 | The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]". |
117 | If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen. |
118 | If the family isn't available a family will be set using the font matching algorithm. |
119 | */ |
120 | |
121 | /*! |
122 | \qmlproperty bool TextInput::font.bold |
123 | |
124 | Sets whether the font weight is bold. |
125 | */ |
126 | |
127 | /*! |
128 | \qmlproperty enumeration TextInput::font.weight |
129 | |
130 | Sets the font's weight. |
131 | |
132 | The weight can be one of: |
133 | \list |
134 | \o Font.Light |
135 | \o Font.Normal - the default |
136 | \o Font.DemiBold |
137 | \o Font.Bold |
138 | \o Font.Black |
139 | \endlist |
140 | |
141 | \qml |
142 | TextInput { text: "Hello"; font.weight: Font.DemiBold } |
143 | \endqml |
144 | */ |
145 | |
146 | /*! |
147 | \qmlproperty bool TextInput::font.italic |
148 | |
149 | Sets whether the font has an italic style. |
150 | */ |
151 | |
152 | /*! |
153 | \qmlproperty bool TextInput::font.underline |
154 | |
155 | Sets whether the text is underlined. |
156 | */ |
157 | |
158 | /*! |
159 | \qmlproperty bool TextInput::font.strikeout |
160 | |
161 | Sets whether the font has a strikeout style. |
162 | */ |
163 | |
164 | /*! |
165 | \qmlproperty real TextInput::font.pointSize |
166 | |
167 | Sets the font size in points. The point size must be greater than zero. |
168 | */ |
169 | |
170 | /*! |
171 | \qmlproperty int TextInput::font.pixelSize |
172 | |
173 | Sets the font size in pixels. |
174 | |
175 | Using this function makes the font device dependent. |
176 | Use \c pointSize to set the size of the font in a device independent manner. |
177 | */ |
178 | |
179 | /*! |
180 | \qmlproperty real TextInput::font.letterSpacing |
181 | |
182 | Sets the letter spacing for the font. |
183 | |
184 | Letter spacing changes the default spacing between individual letters in the font. |
185 | A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing. |
186 | */ |
187 | |
188 | /*! |
189 | \qmlproperty real TextInput::font.wordSpacing |
190 | |
191 | Sets the word spacing for the font. |
192 | |
193 | Word spacing changes the default spacing between individual words. |
194 | A positive value increases the word spacing by a corresponding amount of pixels, |
195 | while a negative value decreases the inter-word spacing accordingly. |
196 | */ |
197 | |
198 | /*! |
199 | \qmlproperty enumeration TextInput::font.capitalization |
200 | |
201 | Sets the capitalization for the text. |
202 | |
203 | \list |
204 | \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied. |
205 | \o Font.AllUppercase - This alters the text to be rendered in all uppercase type. |
206 | \o Font.AllLowercase - This alters the text to be rendered in all lowercase type. |
207 | \o Font.SmallCaps - This alters the text to be rendered in small-caps type. |
208 | \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character. |
209 | \endlist |
210 | |
211 | \qml |
212 | TextInput { text: "Hello"; font.capitalization: Font.AllLowercase } |
213 | \endqml |
214 | */ |
215 | |
216 | QFont QDeclarativeTextInput::font() const |
217 | { |
218 | Q_D(const QDeclarativeTextInput); |
219 | return d->sourceFont; |
220 | } |
221 | |
222 | void QDeclarativeTextInput::setFont(const QFont &font) |
223 | { |
224 | Q_D(QDeclarativeTextInput); |
225 | if (d->sourceFont == font) |
226 | return; |
227 | |
228 | d->sourceFont = font; |
229 | QFont oldFont = d->font; |
230 | d->font = font; |
231 | if (d->font.pointSizeF() != -1) { |
232 | // 0.5pt resolution |
233 | qreal size = qRound(d->font.pointSizeF()*2.0); |
234 | d->font.setPointSizeF(size/2.0); |
235 | } |
236 | |
237 | if (oldFont != d->font) { |
238 | d->control->setFont(d->font); |
239 | updateSize(); |
240 | updateCursorRectangle(); |
241 | if(d->cursorItem){ |
242 | d->cursorItem->setHeight(QFontMetrics(d->font).height()); |
243 | } |
244 | } |
245 | emit fontChanged(d->sourceFont); |
246 | } |
247 | |
248 | /*! |
249 | \qmlproperty color TextInput::color |
250 | |
251 | The text color. |
252 | */ |
253 | QColor QDeclarativeTextInput::color() const |
254 | { |
255 | Q_D(const QDeclarativeTextInput); |
256 | return d->color; |
257 | } |
258 | |
259 | void QDeclarativeTextInput::setColor(const QColor &c) |
260 | { |
261 | Q_D(QDeclarativeTextInput); |
262 | if (c != d->color) { |
263 | d->color = c; |
264 | clearCache(); |
265 | update(); |
266 | emit colorChanged(c); |
267 | } |
268 | } |
269 | |
270 | |
271 | /*! |
272 | \qmlproperty color TextInput::selectionColor |
273 | |
274 | The text highlight color, used behind selections. |
275 | */ |
276 | QColor QDeclarativeTextInput::selectionColor() const |
277 | { |
278 | Q_D(const QDeclarativeTextInput); |
279 | return d->selectionColor; |
280 | } |
281 | |
282 | void QDeclarativeTextInput::setSelectionColor(const QColor &color) |
283 | { |
284 | Q_D(QDeclarativeTextInput); |
285 | if (d->selectionColor == color) |
286 | return; |
287 | |
288 | d->selectionColor = color; |
289 | QPalette p = d->control->palette(); |
290 | p.setColor(QPalette::Highlight, d->selectionColor); |
291 | d->control->setPalette(p); |
292 | if (d->control->hasSelectedText()) { |
293 | clearCache(); |
294 | update(); |
295 | } |
296 | emit selectionColorChanged(color); |
297 | } |
298 | |
299 | /*! |
300 | \qmlproperty color TextInput::selectedTextColor |
301 | |
302 | The highlighted text color, used in selections. |
303 | */ |
304 | QColor QDeclarativeTextInput::selectedTextColor() const |
305 | { |
306 | Q_D(const QDeclarativeTextInput); |
307 | return d->selectedTextColor; |
308 | } |
309 | |
310 | void QDeclarativeTextInput::setSelectedTextColor(const QColor &color) |
311 | { |
312 | Q_D(QDeclarativeTextInput); |
313 | if (d->selectedTextColor == color) |
314 | return; |
315 | |
316 | d->selectedTextColor = color; |
317 | QPalette p = d->control->palette(); |
318 | p.setColor(QPalette::HighlightedText, d->selectedTextColor); |
319 | d->control->setPalette(p); |
320 | if (d->control->hasSelectedText()) { |
321 | clearCache(); |
322 | update(); |
323 | } |
324 | emit selectedTextColorChanged(color); |
325 | } |
326 | |
327 | /*! |
328 | \qmlproperty enumeration TextInput::horizontalAlignment |
329 | |
330 | Sets the horizontal alignment of the text within the TextInput item's |
331 | width and height. By default, the text alignment follows the natural alignment |
332 | of the text, for example text that is read from left to right will be aligned to |
333 | the left. |
334 | |
335 | TextInput does not have vertical alignment, as the natural height is |
336 | exactly the height of the single line of text. If you set the height |
337 | manually to something larger, TextInput will always be top aligned |
338 | vertically. You can use anchors to align it however you want within |
339 | another item. |
340 | |
341 | The valid values for \c horizontalAlignment are \c TextInput.AlignLeft, \c TextInput.AlignRight and |
342 | \c TextInput.AlignHCenter. |
343 | |
344 | When using the attached property \l {LayoutMirroring::enabled} to mirror application |
345 | layouts, the horizontal alignment of text will also be mirrored. However, the property |
346 | \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment |
347 | of TextInput, use the property \l {LayoutMirroring::enabled}. |
348 | */ |
349 | QDeclarativeTextInput::HAlignment QDeclarativeTextInput::hAlign() const |
350 | { |
351 | Q_D(const QDeclarativeTextInput); |
352 | return d->hAlign; |
353 | } |
354 | |
355 | void QDeclarativeTextInput::setHAlign(HAlignment align) |
356 | { |
357 | Q_D(QDeclarativeTextInput); |
358 | bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror; |
359 | d->hAlignImplicit = false; |
360 | if (d->setHAlign(align, forceAlign) && isComponentComplete()) { |
361 | updateCursorRectangle(); |
362 | } |
363 | } |
364 | |
365 | void QDeclarativeTextInput::resetHAlign() |
366 | { |
367 | Q_D(QDeclarativeTextInput); |
368 | d->hAlignImplicit = true; |
369 | if (d->determineHorizontalAlignment() && isComponentComplete()) { |
370 | updateCursorRectangle(); |
371 | } |
372 | } |
373 | |
374 | QDeclarativeTextInput::HAlignment QDeclarativeTextInput::effectiveHAlign() const |
375 | { |
376 | Q_D(const QDeclarativeTextInput); |
377 | QDeclarativeTextInput::HAlignment effectiveAlignment = d->hAlign; |
378 | if (!d->hAlignImplicit && d->effectiveLayoutMirror) { |
379 | switch (d->hAlign) { |
380 | case QDeclarativeTextInput::AlignLeft: |
381 | effectiveAlignment = QDeclarativeTextInput::AlignRight; |
382 | break; |
383 | case QDeclarativeTextInput::AlignRight: |
384 | effectiveAlignment = QDeclarativeTextInput::AlignLeft; |
385 | break; |
386 | default: |
387 | break; |
388 | } |
389 | } |
390 | return effectiveAlignment; |
391 | } |
392 | |
393 | bool QDeclarativeTextInputPrivate::setHAlign(QDeclarativeTextInput::HAlignment alignment, bool forceAlign) |
394 | { |
395 | Q_Q(QDeclarativeTextInput); |
396 | if ((hAlign != alignment || forceAlign) && alignment <= QDeclarativeTextInput::AlignHCenter) { // justify not supported |
397 | hAlign = alignment; |
398 | emit q->horizontalAlignmentChanged(alignment); |
399 | return true; |
400 | } |
401 | return false; |
402 | } |
403 | |
404 | bool QDeclarativeTextInputPrivate::determineHorizontalAlignment() |
405 | { |
406 | if (hAlignImplicit) { |
407 | // if no explicit alignment has been set, follow the natural layout direction of the text |
408 | QString text = control->text(); |
409 | if (text.isEmpty()) |
410 | text = control->preeditAreaText(); |
411 | bool isRightToLeft = text.isEmpty() |
412 | ? QApplication::keyboardInputDirection() == Qt::RightToLeft |
413 | : text.isRightToLeft(); |
414 | return setHAlign(isRightToLeft ? QDeclarativeTextInput::AlignRight : QDeclarativeTextInput::AlignLeft); |
415 | } |
416 | return false; |
417 | } |
418 | |
419 | void QDeclarativeTextInputPrivate::mirrorChange() |
420 | { |
421 | Q_Q(QDeclarativeTextInput); |
422 | if (q->isComponentComplete()) { |
423 | if (!hAlignImplicit && (hAlign == QDeclarativeTextInput::AlignRight || hAlign == QDeclarativeTextInput::AlignLeft)) { |
424 | q->updateCursorRectangle(); |
425 | updateHorizontalScroll(); |
426 | } |
427 | } |
428 | } |
429 | |
430 | /*! |
431 | \qmlproperty bool TextInput::readOnly |
432 | |
433 | Sets whether user input can modify the contents of the TextInput. |
434 | |
435 | If readOnly is set to true, then user input will not affect the text |
436 | property. Any bindings or attempts to set the text property will still |
437 | work. |
438 | */ |
439 | |
440 | bool QDeclarativeTextInput::isReadOnly() const |
441 | { |
442 | Q_D(const QDeclarativeTextInput); |
443 | return d->control->isReadOnly(); |
444 | } |
445 | |
446 | void QDeclarativeTextInput::setReadOnly(bool ro) |
447 | { |
448 | Q_D(QDeclarativeTextInput); |
449 | if (d->control->isReadOnly() == ro) |
450 | return; |
451 | |
452 | setFlag(QGraphicsItem::ItemAcceptsInputMethod, !ro); |
453 | d->control->setReadOnly(ro); |
454 | |
455 | emit readOnlyChanged(ro); |
456 | } |
457 | |
458 | /*! |
459 | \qmlproperty int TextInput::maximumLength |
460 | The maximum permitted length of the text in the TextInput. |
461 | |
462 | If the text is too long, it is truncated at the limit. |
463 | |
464 | By default, this property contains a value of 32767. |
465 | */ |
466 | int QDeclarativeTextInput::maxLength() const |
467 | { |
468 | Q_D(const QDeclarativeTextInput); |
469 | return d->control->maxLength(); |
470 | } |
471 | |
472 | void QDeclarativeTextInput::setMaxLength(int ml) |
473 | { |
474 | Q_D(QDeclarativeTextInput); |
475 | if (d->control->maxLength() == ml) |
476 | return; |
477 | |
478 | d->control->setMaxLength(ml); |
479 | |
480 | emit maximumLengthChanged(ml); |
481 | } |
482 | |
483 | /*! |
484 | \qmlproperty bool TextInput::cursorVisible |
485 | Set to true when the TextInput shows a cursor. |
486 | |
487 | This property is set and unset when the TextInput gets active focus, so that other |
488 | properties can be bound to whether the cursor is currently showing. As it |
489 | gets set and unset automatically, when you set the value yourself you must |
490 | keep in mind that your value may be overwritten. |
491 | |
492 | It can be set directly in script, for example if a KeyProxy might |
493 | forward keys to it and you desire it to look active when this happens |
494 | (but without actually giving it active focus). |
495 | |
496 | It should not be set directly on the element, like in the below QML, |
497 | as the specified value will be overridden an lost on focus changes. |
498 | |
499 | \code |
500 | TextInput { |
501 | text: "Text" |
502 | cursorVisible: false |
503 | } |
504 | \endcode |
505 | |
506 | In the above snippet the cursor will still become visible when the |
507 | TextInput gains active focus. |
508 | */ |
509 | bool QDeclarativeTextInput::isCursorVisible() const |
510 | { |
511 | Q_D(const QDeclarativeTextInput); |
512 | return d->cursorVisible; |
513 | } |
514 | |
515 | void QDeclarativeTextInput::setCursorVisible(bool on) |
516 | { |
517 | Q_D(QDeclarativeTextInput); |
518 | if (d->cursorVisible == on) |
519 | return; |
520 | d->cursorVisible = on; |
521 | d->control->setCursorBlinkPeriod(on?QApplication::cursorFlashTime():0); |
522 | QRect r = d->control->cursorRect(); |
523 | if (d->control->inputMask().isEmpty()) |
524 | updateRect(r); |
525 | else |
526 | updateRect(); |
527 | emit cursorVisibleChanged(d->cursorVisible); |
528 | } |
529 | |
530 | /*! |
531 | \qmlproperty int TextInput::cursorPosition |
532 | The position of the cursor in the TextInput. |
533 | */ |
534 | int QDeclarativeTextInput::cursorPosition() const |
535 | { |
536 | Q_D(const QDeclarativeTextInput); |
537 | return d->control->cursor(); |
538 | } |
539 | void QDeclarativeTextInput::setCursorPosition(int cp) |
540 | { |
541 | Q_D(QDeclarativeTextInput); |
542 | if (cp < 0 || cp > d->control->text().length()) |
543 | return; |
544 | d->control->moveCursor(cp); |
545 | } |
546 | |
547 | /*! |
548 | Returns a Rect which encompasses the cursor, but which may be larger than is |
549 | required. Ignores custom cursor delegates. |
550 | */ |
551 | QRect QDeclarativeTextInput::cursorRectangle() const |
552 | { |
553 | Q_D(const QDeclarativeTextInput); |
554 | QRect r = d->control->cursorRect(); |
555 | // Scroll and make consistent with TextEdit |
556 | // QLineControl inexplicably adds 1 to the height and horizontal padding |
557 | // for unicode direction markers. |
558 | r.adjust(5 - d->hscroll, 0, -4 - d->hscroll, -1); |
559 | return r; |
560 | } |
561 | |
562 | /*! |
563 | \qmlproperty int TextInput::selectionStart |
564 | |
565 | The cursor position before the first character in the current selection. |
566 | |
567 | This property is read-only. To change the selection, use select(start,end), |
568 | selectAll(), or selectWord(). |
569 | |
570 | \sa selectionEnd, cursorPosition, selectedText |
571 | */ |
572 | int QDeclarativeTextInput::selectionStart() const |
573 | { |
574 | Q_D(const QDeclarativeTextInput); |
575 | return d->lastSelectionStart; |
576 | } |
577 | |
578 | /*! |
579 | \qmlproperty int TextInput::selectionEnd |
580 | |
581 | The cursor position after the last character in the current selection. |
582 | |
583 | This property is read-only. To change the selection, use select(start,end), |
584 | selectAll(), or selectWord(). |
585 | |
586 | \sa selectionStart, cursorPosition, selectedText |
587 | */ |
588 | int QDeclarativeTextInput::selectionEnd() const |
589 | { |
590 | Q_D(const QDeclarativeTextInput); |
591 | return d->lastSelectionEnd; |
592 | } |
593 | |
594 | /*! |
595 | \qmlmethod void TextInput::select(int start, int end) |
596 | |
597 | Causes the text from \a start to \a end to be selected. |
598 | |
599 | If either start or end is out of range, the selection is not changed. |
600 | |
601 | After calling this, selectionStart will become the lesser |
602 | and selectionEnd will become the greater (regardless of the order passed |
603 | to this method). |
604 | |
605 | \sa selectionStart, selectionEnd |
606 | */ |
607 | void QDeclarativeTextInput::select(int start, int end) |
608 | { |
609 | Q_D(QDeclarativeTextInput); |
610 | if (start < 0 || end < 0 || start > d->control->text().length() || end > d->control->text().length()) |
611 | return; |
612 | d->control->setSelection(start, end-start); |
613 | } |
614 | |
615 | /*! |
616 | \qmlproperty string TextInput::selectedText |
617 | |
618 | This read-only property provides the text currently selected in the |
619 | text input. |
620 | |
621 | It is equivalent to the following snippet, but is faster and easier |
622 | to use. |
623 | |
624 | \js |
625 | myTextInput.text.toString().substring(myTextInput.selectionStart, |
626 | myTextInput.selectionEnd); |
627 | \endjs |
628 | */ |
629 | QString QDeclarativeTextInput::selectedText() const |
630 | { |
631 | Q_D(const QDeclarativeTextInput); |
632 | return d->control->selectedText(); |
633 | } |
634 | |
635 | /*! |
636 | \qmlproperty bool TextInput::activeFocusOnPress |
637 | |
638 | Whether the TextInput should gain active focus on a mouse press. By default this is |
639 | set to true. |
640 | */ |
641 | bool QDeclarativeTextInput::focusOnPress() const |
642 | { |
643 | Q_D(const QDeclarativeTextInput); |
644 | return d->focusOnPress; |
645 | } |
646 | |
647 | void QDeclarativeTextInput::setFocusOnPress(bool b) |
648 | { |
649 | Q_D(QDeclarativeTextInput); |
650 | if (d->focusOnPress == b) |
651 | return; |
652 | |
653 | d->focusOnPress = b; |
654 | |
655 | emit activeFocusOnPressChanged(d->focusOnPress); |
656 | } |
657 | |
658 | /*! |
659 | \qmlproperty bool TextInput::autoScroll |
660 | |
661 | Whether the TextInput should scroll when the text is longer than the width. By default this is |
662 | set to true. |
663 | */ |
664 | bool QDeclarativeTextInput::autoScroll() const |
665 | { |
666 | Q_D(const QDeclarativeTextInput); |
667 | return d->autoScroll; |
668 | } |
669 | |
670 | void QDeclarativeTextInput::setAutoScroll(bool b) |
671 | { |
672 | Q_D(QDeclarativeTextInput); |
673 | if (d->autoScroll == b) |
674 | return; |
675 | |
676 | d->autoScroll = b; |
677 | //We need to repaint so that the scrolling is taking into account. |
678 | updateSize(true); |
679 | updateCursorRectangle(); |
680 | emit autoScrollChanged(d->autoScroll); |
681 | } |
682 | |
683 | /*! |
684 | \qmlclass IntValidator QIntValidator |
685 | \ingroup qml-basic-visual-elements |
686 | |
687 | This element provides a validator for integer values. |
688 | |
689 | IntValidator uses the \l {QLocale::setDefault()}{default locale} to interpret the number and |
690 | will accept locale specific digits, group separators, and positive and negative signs. In |
691 | addition, IntValidator is always guaranteed to accept a number formatted according to the "C" |
692 | locale. |
693 | */ |
694 | /*! |
695 | \qmlproperty int IntValidator::top |
696 | |
697 | This property holds the validator's highest acceptable value. |
698 | By default, this property's value is derived from the highest signed integer available (typically 2147483647). |
699 | */ |
700 | /*! |
701 | \qmlproperty int IntValidator::bottom |
702 | |
703 | This property holds the validator's lowest acceptable value. |
704 | By default, this property's value is derived from the lowest signed integer available (typically -2147483647). |
705 | */ |
706 | |
707 | /*! |
708 | \qmlclass DoubleValidator QDoubleValidator |
709 | \ingroup qml-basic-visual-elements |
710 | |
711 | This element provides a validator for non-integer numbers. |
712 | */ |
713 | |
714 | /*! |
715 | \qmlproperty real DoubleValidator::top |
716 | |
717 | This property holds the validator's maximum acceptable value. |
718 | By default, this property contains a value of infinity. |
719 | */ |
720 | /*! |
721 | \qmlproperty real DoubleValidator::bottom |
722 | |
723 | This property holds the validator's minimum acceptable value. |
724 | By default, this property contains a value of -infinity. |
725 | */ |
726 | /*! |
727 | \qmlproperty int DoubleValidator::decimals |
728 | |
729 | This property holds the validator's maximum number of digits after the decimal point. |
730 | By default, this property contains a value of 1000. |
731 | */ |
732 | /*! |
733 | \qmlproperty enumeration DoubleValidator::notation |
734 | This property holds the notation of how a string can describe a number. |
735 | |
736 | The possible values for this property are: |
737 | |
738 | \list |
739 | \o DoubleValidator.StandardNotation |
740 | \o DoubleValidator.ScientificNotation (default) |
741 | \endlist |
742 | |
743 | If this property is set to DoubleValidator.ScientificNotation, the written number may have an exponent part (e.g. 1.5E-2). |
744 | */ |
745 | |
746 | /*! |
747 | \qmlclass RegExpValidator QRegExpValidator |
748 | \ingroup qml-basic-visual-elements |
749 | |
750 | This element provides a validator, which counts as valid any string which |
751 | matches a specified regular expression. |
752 | */ |
753 | /*! |
754 | \qmlproperty regExp RegExpValidator::regExp |
755 | |
756 | This property holds the regular expression used for validation. |
757 | |
758 | Note that this property should be a regular expression in JS syntax, e.g /a/ for the regular expression |
759 | matching "a". |
760 | |
761 | By default, this property contains a regular expression with the pattern .* that matches any string. |
762 | */ |
763 | |
764 | /*! |
765 | \qmlproperty Validator TextInput::validator |
766 | |
767 | Allows you to set a validator on the TextInput. When a validator is set |
768 | the TextInput will only accept input which leaves the text property in |
769 | an acceptable or intermediate state. The accepted signal will only be sent |
770 | if the text is in an acceptable state when enter is pressed. |
771 | |
772 | Currently supported validators are IntValidator, DoubleValidator and |
773 | RegExpValidator. An example of using validators is shown below, which allows |
774 | input of integers between 11 and 31 into the text input: |
775 | |
776 | \code |
777 | import QtQuick 1.0 |
778 | TextInput{ |
779 | validator: IntValidator{bottom: 11; top: 31;} |
780 | focus: true |
781 | } |
782 | \endcode |
783 | |
784 | \sa acceptableInput, inputMask |
785 | */ |
786 | #ifndef QT_NO_VALIDATOR |
787 | QValidator* QDeclarativeTextInput::validator() const |
788 | { |
789 | Q_D(const QDeclarativeTextInput); |
790 | //###const cast isn't good, but needed for property system? |
791 | return const_cast<QValidator*>(d->control->validator()); |
792 | } |
793 | |
794 | void QDeclarativeTextInput::setValidator(QValidator* v) |
795 | { |
796 | Q_D(QDeclarativeTextInput); |
797 | if (d->control->validator() == v) |
798 | return; |
799 | |
800 | d->control->setValidator(v); |
801 | if(!d->control->hasAcceptableInput()){ |
802 | d->oldValidity = false; |
803 | emit acceptableInputChanged(); |
804 | } |
805 | |
806 | emit validatorChanged(); |
807 | } |
808 | #endif // QT_NO_VALIDATOR |
809 | |
810 | /*! |
811 | \qmlproperty string TextInput::inputMask |
812 | |
813 | Allows you to set an input mask on the TextInput, restricting the allowable |
814 | text inputs. See QLineEdit::inputMask for further details, as the exact |
815 | same mask strings are used by TextInput. |
816 | |
817 | \sa acceptableInput, validator |
818 | */ |
819 | QString QDeclarativeTextInput::inputMask() const |
820 | { |
821 | Q_D(const QDeclarativeTextInput); |
822 | return d->control->inputMask(); |
823 | } |
824 | |
825 | void QDeclarativeTextInput::setInputMask(const QString &im) |
826 | { |
827 | Q_D(QDeclarativeTextInput); |
828 | if (d->control->inputMask() == im) |
829 | return; |
830 | |
831 | d->control->setInputMask(im); |
832 | emit inputMaskChanged(d->control->inputMask()); |
833 | } |
834 | |
835 | /*! |
836 | \qmlproperty bool TextInput::acceptableInput |
837 | |
838 | This property is always true unless a validator or input mask has been set. |
839 | If a validator or input mask has been set, this property will only be true |
840 | if the current text is acceptable to the validator or input mask as a final |
841 | string (not as an intermediate string). |
842 | */ |
843 | bool QDeclarativeTextInput::hasAcceptableInput() const |
844 | { |
845 | Q_D(const QDeclarativeTextInput); |
846 | return d->control->hasAcceptableInput(); |
847 | } |
848 | |
849 | /*! |
850 | \qmlsignal TextInput::onAccepted() |
851 | |
852 | This handler is called when the Return or Enter key is pressed. |
853 | Note that if there is a \l validator or \l inputMask set on the text |
854 | input, the handler will only be emitted if the input is in an acceptable |
855 | state. |
856 | */ |
857 | |
858 | void QDeclarativeTextInputPrivate::updateInputMethodHints() |
859 | { |
860 | Q_Q(QDeclarativeTextInput); |
861 | Qt::InputMethodHints hints = inputMethodHints; |
862 | uint echo = control->echoMode(); |
863 | if (echo == QDeclarativeTextInput::Password || echo == QDeclarativeTextInput::NoEcho) |
864 | hints |= Qt::ImhHiddenText; |
865 | else if (echo == QDeclarativeTextInput::PasswordEchoOnEdit) |
866 | hints &= ~Qt::ImhHiddenText; |
867 | if (echo != QDeclarativeTextInput::Normal) |
868 | hints |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText); |
869 | q->setInputMethodHints(hints); |
870 | } |
871 | |
872 | /*! |
873 | \qmlproperty enumeration TextInput::echoMode |
874 | |
875 | Specifies how the text should be displayed in the TextInput. |
876 | \list |
877 | \o TextInput.Normal - Displays the text as it is. (Default) |
878 | \o TextInput.Password - Displays asterixes instead of characters. |
879 | \o TextInput.NoEcho - Displays nothing. |
880 | \o TextInput.PasswordEchoOnEdit - Displays characters as they are entered |
881 | while editing, otherwise displays asterisks. |
882 | \endlist |
883 | */ |
884 | QDeclarativeTextInput::EchoMode QDeclarativeTextInput::echoMode() const |
885 | { |
886 | Q_D(const QDeclarativeTextInput); |
887 | return (QDeclarativeTextInput::EchoMode)d->control->echoMode(); |
888 | } |
889 | |
890 | void QDeclarativeTextInput::setEchoMode(QDeclarativeTextInput::EchoMode echo) |
891 | { |
892 | Q_D(QDeclarativeTextInput); |
893 | if (echoMode() == echo) |
894 | return; |
895 | d->control->setEchoMode((uint)echo); |
896 | d->updateInputMethodHints(); |
897 | q_textChanged(); |
898 | emit echoModeChanged(echoMode()); |
899 | } |
900 | |
901 | Qt::InputMethodHints QDeclarativeTextInput::imHints() const |
902 | { |
903 | Q_D(const QDeclarativeTextInput); |
904 | return d->inputMethodHints; |
905 | } |
906 | |
907 | void QDeclarativeTextInput::setIMHints(Qt::InputMethodHints hints) |
908 | { |
909 | Q_D(QDeclarativeTextInput); |
910 | if (d->inputMethodHints == hints) |
911 | return; |
912 | d->inputMethodHints = hints; |
913 | d->updateInputMethodHints(); |
914 | } |
915 | |
916 | /*! |
917 | \qmlproperty Component TextInput::cursorDelegate |
918 | The delegate for the cursor in the TextInput. |
919 | |
920 | If you set a cursorDelegate for a TextInput, this delegate will be used for |
921 | drawing the cursor instead of the standard cursor. An instance of the |
922 | delegate will be created and managed by the TextInput when a cursor is |
923 | needed, and the x property of delegate instance will be set so as |
924 | to be one pixel before the top left of the current character. |
925 | |
926 | Note that the root item of the delegate component must be a QDeclarativeItem or |
927 | QDeclarativeItem derived item. |
928 | */ |
929 | QDeclarativeComponent* QDeclarativeTextInput::cursorDelegate() const |
930 | { |
931 | Q_D(const QDeclarativeTextInput); |
932 | return d->cursorComponent; |
933 | } |
934 | |
935 | void QDeclarativeTextInput::setCursorDelegate(QDeclarativeComponent* c) |
936 | { |
937 | Q_D(QDeclarativeTextInput); |
938 | if (d->cursorComponent == c) |
939 | return; |
940 | |
941 | d->cursorComponent = c; |
942 | if(!c){ |
943 | //note that the components are owned by something else |
944 | delete d->cursorItem; |
945 | }else{ |
946 | d->startCreatingCursor(); |
947 | } |
948 | |
949 | emit cursorDelegateChanged(); |
950 | } |
951 | |
952 | void QDeclarativeTextInputPrivate::startCreatingCursor() |
953 | { |
954 | Q_Q(QDeclarativeTextInput); |
955 | if(cursorComponent->isReady()){ |
956 | q->createCursor(); |
957 | }else if(cursorComponent->isLoading()){ |
958 | q->connect(cursorComponent, SIGNAL(statusChanged(int)), |
959 | q, SLOT(createCursor())); |
960 | }else {//isError |
961 | qmlInfo(q, cursorComponent->errors()) << QDeclarativeTextInput::tr("Could not load cursor delegate" ); |
962 | } |
963 | } |
964 | |
965 | void QDeclarativeTextInput::createCursor() |
966 | { |
967 | Q_D(QDeclarativeTextInput); |
968 | if(d->cursorComponent->isError()){ |
969 | qmlInfo(this, d->cursorComponent->errors()) << tr("Could not load cursor delegate" ); |
970 | return; |
971 | } |
972 | |
973 | if(!d->cursorComponent->isReady()) |
974 | return; |
975 | |
976 | if(d->cursorItem) |
977 | delete d->cursorItem; |
978 | d->cursorItem = qobject_cast<QDeclarativeItem*>(d->cursorComponent->create()); |
979 | if(!d->cursorItem){ |
980 | qmlInfo(this, d->cursorComponent->errors()) << tr("Could not instantiate cursor delegate" ); |
981 | return; |
982 | } |
983 | |
984 | QDeclarative_setParent_noEvent(d->cursorItem, this); |
985 | d->cursorItem->setParentItem(this); |
986 | d->cursorItem->setX(d->control->cursorToX()); |
987 | d->cursorItem->setHeight(d->control->height()-1); // -1 to counter QLineControl's +1 which is not consistent with Text. |
988 | } |
989 | |
990 | /*! |
991 | \qmlmethod rect TextInput::positionToRectangle(int pos) |
992 | |
993 | This function takes a character position and returns the rectangle that the |
994 | cursor would occupy, if it was placed at that character position. |
995 | |
996 | This is similar to setting the cursorPosition, and then querying the cursor |
997 | rectangle, but the cursorPosition is not changed. |
998 | */ |
999 | QRectF QDeclarativeTextInput::positionToRectangle(int pos) const |
1000 | { |
1001 | Q_D(const QDeclarativeTextInput); |
1002 | if (pos > d->control->cursorPosition()) |
1003 | pos += d->control->preeditAreaText().length(); |
1004 | return QRectF(d->control->cursorToX(pos)-d->hscroll, |
1005 | 0.0, |
1006 | d->control->cursorWidth(), |
1007 | cursorRectangle().height()); |
1008 | } |
1009 | |
1010 | int QDeclarativeTextInput::positionAt(int x) const |
1011 | { |
1012 | return positionAt(x, CursorBetweenCharacters); |
1013 | } |
1014 | |
1015 | /*! |
1016 | \qmlmethod int TextInput::positionAt(int x, CursorPosition position = CursorBetweenCharacters) |
1017 | \since QtQuick 1.1 |
1018 | |
1019 | This function returns the character position at |
1020 | x pixels from the left of the textInput. Position 0 is before the |
1021 | first character, position 1 is after the first character but before the second, |
1022 | and so on until position text.length, which is after all characters. |
1023 | |
1024 | This means that for all x values before the first character this function returns 0, |
1025 | and for all x values after the last character this function returns text.length. |
1026 | |
1027 | The cursor position type specifies how the cursor position should be resolved. |
1028 | |
1029 | \list |
1030 | \o TextInput.CursorBetweenCharacters - Returns the position between characters that is nearest x. |
1031 | \o TextInput.CursorOnCharacter - Returns the position before the character that is nearest x. |
1032 | \endlist |
1033 | */ |
1034 | int QDeclarativeTextInput::positionAt(int x, CursorPosition position) const |
1035 | { |
1036 | Q_D(const QDeclarativeTextInput); |
1037 | int pos = d->control->xToPos(x + d->hscroll, QTextLine::CursorPosition(position)); |
1038 | const int cursor = d->control->cursor(); |
1039 | if (pos > cursor) { |
1040 | const int preeditLength = d->control->preeditAreaText().length(); |
1041 | pos = pos > cursor + preeditLength |
1042 | ? pos - preeditLength |
1043 | : cursor; |
1044 | } |
1045 | return pos; |
1046 | } |
1047 | |
1048 | void QDeclarativeTextInputPrivate::focusChanged(bool hasFocus) |
1049 | { |
1050 | Q_Q(QDeclarativeTextInput); |
1051 | focused = hasFocus; |
1052 | q->setCursorVisible(hasFocus && scene && scene->hasFocus()); |
1053 | if(!hasFocus && control->passwordEchoEditing()) |
1054 | control->updatePasswordEchoEditing(false);//QLineControl sets it on key events, but doesn't deal with focus events |
1055 | if (!hasFocus) |
1056 | control->deselect(); |
1057 | QDeclarativeItemPrivate::focusChanged(hasFocus); |
1058 | } |
1059 | |
1060 | void QDeclarativeTextInput::keyPressEvent(QKeyEvent* ev) |
1061 | { |
1062 | Q_D(QDeclarativeTextInput); |
1063 | keyPressPreHandler(ev); |
1064 | if (ev->isAccepted()) |
1065 | return; |
1066 | |
1067 | // Don't allow MacOSX up/down support, and we don't allow a completer. |
1068 | bool ignore = (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) && ev->modifiers() == Qt::NoModifier; |
1069 | if (!ignore && (d->lastSelectionStart == d->lastSelectionEnd) && (ev->key() == Qt::Key_Right || ev->key() == Qt::Key_Left)) { |
1070 | // Ignore when moving off the end unless there is a selection, |
1071 | // because then moving will do something (deselect). |
1072 | int cursorPosition = d->control->cursor(); |
1073 | if (cursorPosition == 0) |
1074 | ignore = ev->key() == (d->control->layoutDirection() == Qt::LeftToRight ? Qt::Key_Left : Qt::Key_Right); |
1075 | if (!ignore && cursorPosition == d->control->text().length()) |
1076 | ignore = ev->key() == (d->control->layoutDirection() == Qt::LeftToRight ? Qt::Key_Right : Qt::Key_Left); |
1077 | } |
1078 | if (ignore) { |
1079 | ev->ignore(); |
1080 | } else { |
1081 | d->control->processKeyEvent(ev); |
1082 | } |
1083 | if (!ev->isAccepted()) |
1084 | QDeclarativePaintedItem::keyPressEvent(ev); |
1085 | } |
1086 | |
1087 | void QDeclarativeTextInput::inputMethodEvent(QInputMethodEvent *ev) |
1088 | { |
1089 | Q_D(QDeclarativeTextInput); |
1090 | ev->ignore(); |
1091 | const bool wasComposing = d->control->preeditAreaText().length() > 0; |
1092 | inputMethodPreHandler(ev); |
1093 | if (!ev->isAccepted()) { |
1094 | if (d->control->isReadOnly()) { |
1095 | ev->ignore(); |
1096 | } else { |
1097 | d->control->processInputMethodEvent(ev); |
1098 | } |
1099 | } |
1100 | if (!ev->isAccepted()) |
1101 | QDeclarativePaintedItem::inputMethodEvent(ev); |
1102 | |
1103 | if (wasComposing != (d->control->preeditAreaText().length() > 0)) |
1104 | emit inputMethodComposingChanged(); |
1105 | } |
1106 | |
1107 | /*! |
1108 | \overload |
1109 | Handles the given mouse \a event. |
1110 | */ |
1111 | void QDeclarativeTextInput::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) |
1112 | { |
1113 | Q_D(QDeclarativeTextInput); |
1114 | if (d->sendMouseEventToInputContext(event, QEvent::MouseButtonDblClick)) |
1115 | return; |
1116 | if (d->selectByMouse) { |
1117 | int cursor = d->xToPos(event->pos().x()); |
1118 | d->control->selectWordAtPos(cursor); |
1119 | event->setAccepted(true); |
1120 | } else { |
1121 | QDeclarativePaintedItem::mouseDoubleClickEvent(event); |
1122 | } |
1123 | } |
1124 | |
1125 | void QDeclarativeTextInput::mousePressEvent(QGraphicsSceneMouseEvent *event) |
1126 | { |
1127 | Q_D(QDeclarativeTextInput); |
1128 | if (d->sendMouseEventToInputContext(event, QEvent::MouseButtonPress)) |
1129 | return; |
1130 | if(d->focusOnPress){ |
1131 | bool hadActiveFocus = hasActiveFocus(); |
1132 | forceActiveFocus(); |
1133 | if (d->showInputPanelOnFocus) { |
1134 | if (hasActiveFocus() && hadActiveFocus && !isReadOnly()) { |
1135 | // re-open input panel on press if already focused |
1136 | openSoftwareInputPanel(); |
1137 | } |
1138 | } else { // show input panel on click |
1139 | if (hasActiveFocus() && !hadActiveFocus) { |
1140 | d->clickCausedFocus = true; |
1141 | } |
1142 | } |
1143 | } |
1144 | if (d->selectByMouse) { |
1145 | setKeepMouseGrab(false); |
1146 | d->selectPressed = true; |
1147 | d->pressPos = event->pos(); |
1148 | } |
1149 | bool mark = (event->modifiers() & Qt::ShiftModifier) && d->selectByMouse; |
1150 | int cursor = d->xToPos(event->pos().x()); |
1151 | d->control->moveCursor(cursor, mark); |
1152 | event->setAccepted(true); |
1153 | } |
1154 | |
1155 | void QDeclarativeTextInput::mouseMoveEvent(QGraphicsSceneMouseEvent *event) |
1156 | { |
1157 | Q_D(QDeclarativeTextInput); |
1158 | if (d->sendMouseEventToInputContext(event, QEvent::MouseMove)) |
1159 | return; |
1160 | if (d->selectPressed) { |
1161 | if (qAbs(int(event->pos().x() - d->pressPos.x())) > QApplication::startDragDistance()) |
1162 | setKeepMouseGrab(true); |
1163 | moveCursorSelection(d->xToPos(event->pos().x()), d->mouseSelectionMode); |
1164 | event->setAccepted(true); |
1165 | } else { |
1166 | QDeclarativePaintedItem::mouseMoveEvent(event); |
1167 | } |
1168 | } |
1169 | |
1170 | /*! |
1171 | \overload |
1172 | Handles the given mouse \a event. |
1173 | */ |
1174 | void QDeclarativeTextInput::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) |
1175 | { |
1176 | Q_D(QDeclarativeTextInput); |
1177 | if (d->sendMouseEventToInputContext(event, QEvent::MouseButtonRelease)) |
1178 | return; |
1179 | if (d->selectPressed) { |
1180 | d->selectPressed = false; |
1181 | setKeepMouseGrab(false); |
1182 | } |
1183 | if (!d->showInputPanelOnFocus) { // input panel on click |
1184 | if (d->focusOnPress && !isReadOnly() && boundingRect().contains(event->pos())) { |
1185 | if (QGraphicsView * view = qobject_cast<QGraphicsView*>(qApp->focusWidget())) { |
1186 | if (view->scene() && view->scene() == scene()) { |
1187 | qt_widget_private(view)->handleSoftwareInputPanel(event->button(), d->clickCausedFocus); |
1188 | } |
1189 | } |
1190 | } |
1191 | } |
1192 | d->clickCausedFocus = false; |
1193 | d->control->processEvent(event); |
1194 | if (!event->isAccepted()) |
1195 | QDeclarativePaintedItem::mouseReleaseEvent(event); |
1196 | } |
1197 | |
1198 | bool QDeclarativeTextInputPrivate::sendMouseEventToInputContext( |
1199 | QGraphicsSceneMouseEvent *event, QEvent::Type eventType) |
1200 | { |
1201 | #if !defined QT_NO_IM |
1202 | Q_Q(QDeclarativeTextInput); |
1203 | |
1204 | QWidget *widget = event->widget(); |
1205 | // event->widget() is null, if this is delayed event from QDeclarativeFlickable. |
1206 | if (!widget && qApp) { |
1207 | QGraphicsView *view = qobject_cast<QGraphicsView*>(qApp->focusWidget()); |
1208 | if (view && view->scene() && view->scene() == q->scene()) |
1209 | widget = view->viewport(); |
1210 | } |
1211 | |
1212 | if (widget && control->composeMode()) { |
1213 | int tmp_cursor = xToPos(event->pos().x()); |
1214 | int mousePos = tmp_cursor - control->cursor(); |
1215 | if (mousePos < 0 || mousePos > control->preeditAreaText().length()) { |
1216 | mousePos = -1; |
1217 | // don't send move events outside the preedit area |
1218 | if (eventType == QEvent::MouseMove) |
1219 | return true; |
1220 | } |
1221 | |
1222 | QInputContext *qic = widget->inputContext(); |
1223 | if (qic) { |
1224 | QMouseEvent mouseEvent( |
1225 | eventType, |
1226 | widget->mapFromGlobal(event->screenPos()), |
1227 | event->screenPos(), |
1228 | event->button(), |
1229 | event->buttons(), |
1230 | event->modifiers()); |
1231 | // may be causing reset() in some input methods |
1232 | qic->mouseHandler(mousePos, &mouseEvent); |
1233 | event->setAccepted(mouseEvent.isAccepted()); |
1234 | } |
1235 | if (!control->preeditAreaText().isEmpty()) |
1236 | return true; |
1237 | } |
1238 | #else |
1239 | Q_UNUSED(event); |
1240 | Q_UNUSED(eventType) |
1241 | #endif |
1242 | |
1243 | return false; |
1244 | } |
1245 | |
1246 | bool QDeclarativeTextInput::sceneEvent(QEvent *event) |
1247 | { |
1248 | Q_D(QDeclarativeTextInput); |
1249 | bool rv = QDeclarativeItem::sceneEvent(event); |
1250 | if (event->type() == QEvent::UngrabMouse) { |
1251 | d->selectPressed = false; |
1252 | setKeepMouseGrab(false); |
1253 | } |
1254 | return rv; |
1255 | } |
1256 | |
1257 | bool QDeclarativeTextInput::event(QEvent* ev) |
1258 | { |
1259 | Q_D(QDeclarativeTextInput); |
1260 | //Anything we don't deal with ourselves, pass to the control |
1261 | bool handled = false; |
1262 | switch(ev->type()){ |
1263 | case QEvent::KeyPress: |
1264 | case QEvent::KeyRelease://###Should the control be doing anything with release? |
1265 | case QEvent::InputMethod: |
1266 | case QEvent::GraphicsSceneMousePress: |
1267 | case QEvent::GraphicsSceneMouseMove: |
1268 | case QEvent::GraphicsSceneMouseRelease: |
1269 | case QEvent::GraphicsSceneMouseDoubleClick: |
1270 | break; |
1271 | default: |
1272 | handled = d->control->processEvent(ev); |
1273 | } |
1274 | if(!handled) |
1275 | handled = QDeclarativePaintedItem::event(ev); |
1276 | return handled; |
1277 | } |
1278 | |
1279 | void QDeclarativeTextInput::geometryChanged(const QRectF &newGeometry, |
1280 | const QRectF &oldGeometry) |
1281 | { |
1282 | if (newGeometry.width() != oldGeometry.width()) { |
1283 | updateSize(); |
1284 | updateCursorRectangle(); |
1285 | } |
1286 | QDeclarativePaintedItem::geometryChanged(newGeometry, oldGeometry); |
1287 | } |
1288 | |
1289 | int QDeclarativeTextInputPrivate::calculateTextWidth() |
1290 | { |
1291 | return qRound(control->naturalTextWidth()); |
1292 | } |
1293 | |
1294 | void QDeclarativeTextInputPrivate::updateHorizontalScroll() |
1295 | { |
1296 | Q_Q(QDeclarativeTextInput); |
1297 | const int preeditLength = control->preeditAreaText().length(); |
1298 | int cix = qRound(control->cursorToX(control->cursor() + preeditLength)); |
1299 | QRect br(q->boundingRect().toRect()); |
1300 | int widthUsed = calculateTextWidth(); |
1301 | |
1302 | QDeclarativeTextInput::HAlignment effectiveHAlign = q->effectiveHAlign(); |
1303 | if (autoScroll) { |
1304 | if (widthUsed <= br.width()) { |
1305 | // text fits in br; use hscroll for alignment |
1306 | switch (effectiveHAlign & ~(Qt::AlignAbsolute|Qt::AlignVertical_Mask)) { |
1307 | case Qt::AlignRight: |
1308 | hscroll = widthUsed - br.width() - 1; |
1309 | break; |
1310 | case Qt::AlignHCenter: |
1311 | hscroll = (widthUsed - br.width()) / 2; |
1312 | break; |
1313 | default: |
1314 | // Left |
1315 | hscroll = 0; |
1316 | break; |
1317 | } |
1318 | } else if (cix - hscroll >= br.width()) { |
1319 | // text doesn't fit, cursor is to the right of br (scroll right) |
1320 | hscroll = cix - br.width() + 1; |
1321 | } else if (cix - hscroll < 0 && hscroll < widthUsed) { |
1322 | // text doesn't fit, cursor is to the left of br (scroll left) |
1323 | hscroll = cix; |
1324 | } else if (widthUsed - hscroll < br.width()) { |
1325 | // text doesn't fit, text document is to the left of br; align |
1326 | // right |
1327 | hscroll = widthUsed - br.width() + 1; |
1328 | } |
1329 | if (preeditLength > 0) { |
1330 | // check to ensure long pre-edit text doesn't push the cursor |
1331 | // off to the left |
1332 | cix = qRound(control->cursorToX( |
1333 | control->cursor() + qMax(0, control->preeditCursor() - 1))); |
1334 | if (cix < hscroll) |
1335 | hscroll = cix; |
1336 | } |
1337 | } else { |
1338 | switch (effectiveHAlign) { |
1339 | case QDeclarativeTextInput::AlignRight: |
1340 | hscroll = q->width() - widthUsed; |
1341 | break; |
1342 | case QDeclarativeTextInput::AlignHCenter: |
1343 | hscroll = (q->width() - widthUsed) / 2; |
1344 | break; |
1345 | default: |
1346 | // Left |
1347 | hscroll = 0; |
1348 | break; |
1349 | } |
1350 | } |
1351 | } |
1352 | |
1353 | void QDeclarativeTextInput::drawContents(QPainter *p, const QRect &r) |
1354 | { |
1355 | Q_D(QDeclarativeTextInput); |
1356 | p->setRenderHint(QPainter::TextAntialiasing, true); |
1357 | p->save(); |
1358 | p->setPen(QPen(d->color)); |
1359 | int flags = QLineControl::DrawText; |
1360 | if(!isReadOnly() && d->cursorVisible && !d->cursorItem) |
1361 | flags |= QLineControl::DrawCursor; |
1362 | if (d->control->hasSelectedText()) |
1363 | flags |= QLineControl::DrawSelections; |
1364 | QPoint offset = QPoint(0,0); |
1365 | QFontMetrics fm = QFontMetrics(d->font); |
1366 | QRect br(boundingRect().toRect()); |
1367 | if (d->autoScroll) { |
1368 | // the y offset is there to keep the baseline constant in case we have script changes in the text. |
1369 | offset = br.topLeft() - QPoint(d->hscroll, d->control->ascent() - fm.ascent()); |
1370 | } else { |
1371 | offset = QPoint(d->hscroll, 0); |
1372 | } |
1373 | d->control->draw(p, offset, r, flags); |
1374 | p->restore(); |
1375 | } |
1376 | |
1377 | /*! |
1378 | \overload |
1379 | Returns the value of the given \a property. |
1380 | */ |
1381 | QVariant QDeclarativeTextInput::inputMethodQuery(Qt::InputMethodQuery property) const |
1382 | { |
1383 | Q_D(const QDeclarativeTextInput); |
1384 | switch(property) { |
1385 | case Qt::ImMicroFocus: |
1386 | return cursorRectangle(); |
1387 | case Qt::ImFont: |
1388 | return font(); |
1389 | case Qt::ImCursorPosition: |
1390 | return QVariant(d->control->cursor()); |
1391 | case Qt::ImSurroundingText: |
1392 | if (d->control->echoMode() == PasswordEchoOnEdit && !d->control->passwordEchoEditing()) |
1393 | return QVariant(displayText()); |
1394 | else |
1395 | return QVariant(text()); |
1396 | case Qt::ImCurrentSelection: |
1397 | return QVariant(selectedText()); |
1398 | case Qt::ImMaximumTextLength: |
1399 | return QVariant(maxLength()); |
1400 | case Qt::ImAnchorPosition: |
1401 | if (d->control->selectionStart() == d->control->selectionEnd()) |
1402 | return QVariant(d->control->cursor()); |
1403 | else if (d->control->selectionStart() == d->control->cursor()) |
1404 | return QVariant(d->control->selectionEnd()); |
1405 | else |
1406 | return QVariant(d->control->selectionStart()); |
1407 | default: |
1408 | return QVariant(); |
1409 | } |
1410 | } |
1411 | |
1412 | /*! |
1413 | \qmlmethod void TextInput::deselect() |
1414 | \since QtQuick 1.1 |
1415 | |
1416 | Removes active text selection. |
1417 | */ |
1418 | void QDeclarativeTextInput::deselect() |
1419 | { |
1420 | Q_D(QDeclarativeTextInput); |
1421 | d->control->deselect(); |
1422 | } |
1423 | |
1424 | /*! |
1425 | \qmlmethod void TextInput::selectAll() |
1426 | |
1427 | Causes all text to be selected. |
1428 | */ |
1429 | void QDeclarativeTextInput::selectAll() |
1430 | { |
1431 | Q_D(QDeclarativeTextInput); |
1432 | d->control->setSelection(0, d->control->text().length()); |
1433 | } |
1434 | |
1435 | /*! |
1436 | \qmlmethod void TextInput::isRightToLeft(int start, int end) |
1437 | |
1438 | Returns true if the natural reading direction of the editor text |
1439 | found between positions \a start and \a end is right to left. |
1440 | */ |
1441 | bool QDeclarativeTextInput::isRightToLeft(int start, int end) |
1442 | { |
1443 | Q_D(QDeclarativeTextInput); |
1444 | if (start > end) { |
1445 | qmlInfo(this) << "isRightToLeft(start, end) called with the end property being smaller than the start." ; |
1446 | return false; |
1447 | } else { |
1448 | return d->control->text().mid(start, end - start).isRightToLeft(); |
1449 | } |
1450 | } |
1451 | |
1452 | #ifndef QT_NO_CLIPBOARD |
1453 | /*! |
1454 | \qmlmethod TextInput::cut() |
1455 | |
1456 | Moves the currently selected text to the system clipboard. |
1457 | */ |
1458 | void QDeclarativeTextInput::cut() |
1459 | { |
1460 | Q_D(QDeclarativeTextInput); |
1461 | d->control->copy(); |
1462 | d->control->del(); |
1463 | } |
1464 | |
1465 | /*! |
1466 | \qmlmethod TextInput::copy() |
1467 | |
1468 | Copies the currently selected text to the system clipboard. |
1469 | */ |
1470 | void QDeclarativeTextInput::copy() |
1471 | { |
1472 | Q_D(QDeclarativeTextInput); |
1473 | d->control->copy(); |
1474 | } |
1475 | |
1476 | /*! |
1477 | \qmlmethod TextInput::paste() |
1478 | |
1479 | Replaces the currently selected text by the contents of the system clipboard. |
1480 | */ |
1481 | void QDeclarativeTextInput::paste() |
1482 | { |
1483 | Q_D(QDeclarativeTextInput); |
1484 | if(!d->control->isReadOnly()) |
1485 | d->control->paste(); |
1486 | } |
1487 | #endif // QT_NO_CLIPBOARD |
1488 | |
1489 | /*! |
1490 | \qmlmethod void TextInput::selectWord() |
1491 | |
1492 | Causes the word closest to the current cursor position to be selected. |
1493 | */ |
1494 | void QDeclarativeTextInput::selectWord() |
1495 | { |
1496 | Q_D(QDeclarativeTextInput); |
1497 | d->control->selectWordAtPos(d->control->cursor()); |
1498 | } |
1499 | |
1500 | /*! |
1501 | \qmlproperty bool TextInput::smooth |
1502 | |
1503 | This property holds whether the text is smoothly scaled or transformed. |
1504 | |
1505 | Smooth filtering gives better visual quality, but is slower. If |
1506 | the item is displayed at its natural size, this property has no visual or |
1507 | performance effect. |
1508 | |
1509 | \note Generally scaling artifacts are only visible if the item is stationary on |
1510 | the screen. A common pattern when animating an item is to disable smooth |
1511 | filtering at the beginning of the animation and reenable it at the conclusion. |
1512 | */ |
1513 | |
1514 | /*! |
1515 | \qmlproperty string TextInput::passwordCharacter |
1516 | |
1517 | This is the character displayed when echoMode is set to Password or |
1518 | PasswordEchoOnEdit. By default it is an asterisk. |
1519 | |
1520 | If this property is set to a string with more than one character, |
1521 | the first character is used. If the string is empty, the value |
1522 | is ignored and the property is not set. |
1523 | */ |
1524 | QString QDeclarativeTextInput::passwordCharacter() const |
1525 | { |
1526 | Q_D(const QDeclarativeTextInput); |
1527 | return QString(d->control->passwordCharacter()); |
1528 | } |
1529 | |
1530 | void QDeclarativeTextInput::setPasswordCharacter(const QString &str) |
1531 | { |
1532 | Q_D(QDeclarativeTextInput); |
1533 | if(str.length() < 1) |
1534 | return; |
1535 | d->control->setPasswordCharacter(str.constData()[0]); |
1536 | EchoMode echoMode_ = echoMode(); |
1537 | if (echoMode_ == Password || echoMode_ == PasswordEchoOnEdit) { |
1538 | updateSize(); |
1539 | } |
1540 | emit passwordCharacterChanged(); |
1541 | } |
1542 | |
1543 | /*! |
1544 | \qmlproperty string TextInput::displayText |
1545 | |
1546 | This is the text displayed in the TextInput. |
1547 | |
1548 | If \l echoMode is set to TextInput::Normal, this holds the |
1549 | same value as the TextInput::text property. Otherwise, |
1550 | this property holds the text visible to the user, while |
1551 | the \l text property holds the actual entered text. |
1552 | */ |
1553 | QString QDeclarativeTextInput::displayText() const |
1554 | { |
1555 | Q_D(const QDeclarativeTextInput); |
1556 | return d->control->displayText(); |
1557 | } |
1558 | |
1559 | /*! |
1560 | \qmlproperty bool TextInput::selectByMouse |
1561 | |
1562 | Defaults to false. |
1563 | |
1564 | If true, the user can use the mouse to select text in some |
1565 | platform-specific way. Note that for some platforms this may |
1566 | not be an appropriate interaction (eg. may conflict with how |
1567 | the text needs to behave inside a Flickable. |
1568 | */ |
1569 | bool QDeclarativeTextInput::selectByMouse() const |
1570 | { |
1571 | Q_D(const QDeclarativeTextInput); |
1572 | return d->selectByMouse; |
1573 | } |
1574 | |
1575 | void QDeclarativeTextInput::setSelectByMouse(bool on) |
1576 | { |
1577 | Q_D(QDeclarativeTextInput); |
1578 | if (d->selectByMouse != on) { |
1579 | d->selectByMouse = on; |
1580 | emit selectByMouseChanged(on); |
1581 | } |
1582 | } |
1583 | |
1584 | /*! |
1585 | \qmlproperty enum TextInput::mouseSelectionMode |
1586 | \since QtQuick 1.1 |
1587 | |
1588 | Specifies how text should be selected using a mouse. |
1589 | |
1590 | \list |
1591 | \o TextInput.SelectCharacters - The selection is updated with individual characters. (Default) |
1592 | \o TextInput.SelectWords - The selection is updated with whole words. |
1593 | \endlist |
1594 | |
1595 | This property only applies when \l selectByMouse is true. |
1596 | */ |
1597 | |
1598 | QDeclarativeTextInput::SelectionMode QDeclarativeTextInput::mouseSelectionMode() const |
1599 | { |
1600 | Q_D(const QDeclarativeTextInput); |
1601 | return d->mouseSelectionMode; |
1602 | } |
1603 | |
1604 | void QDeclarativeTextInput::setMouseSelectionMode(SelectionMode mode) |
1605 | { |
1606 | Q_D(QDeclarativeTextInput); |
1607 | if (d->mouseSelectionMode != mode) { |
1608 | d->mouseSelectionMode = mode; |
1609 | emit mouseSelectionModeChanged(mode); |
1610 | } |
1611 | } |
1612 | |
1613 | /*! |
1614 | \qmlproperty bool TextInput::canPaste |
1615 | \since QtQuick 1.1 |
1616 | |
1617 | Returns true if the TextInput is writable and the content of the clipboard is |
1618 | suitable for pasting into the TextEdit. |
1619 | */ |
1620 | bool QDeclarativeTextInput::canPaste() const |
1621 | { |
1622 | Q_D(const QDeclarativeTextInput); |
1623 | return d->canPaste; |
1624 | } |
1625 | |
1626 | void QDeclarativeTextInput::moveCursorSelection(int position) |
1627 | { |
1628 | Q_D(QDeclarativeTextInput); |
1629 | d->control->moveCursor(position, true); |
1630 | } |
1631 | |
1632 | /*! |
1633 | \qmlmethod void TextInput::moveCursorSelection(int position, SelectionMode mode = TextInput.SelectCharacters) |
1634 | \since QtQuick 1.1 |
1635 | |
1636 | Moves the cursor to \a position and updates the selection according to the optional \a mode |
1637 | parameter. (To only move the cursor, set the \l cursorPosition property.) |
1638 | |
1639 | When this method is called it additionally sets either the |
1640 | selectionStart or the selectionEnd (whichever was at the previous cursor position) |
1641 | to the specified position. This allows you to easily extend and contract the selected |
1642 | text range. |
1643 | |
1644 | The selection mode specifies whether the selection is updated on a per character or a per word |
1645 | basis. If not specified the selection mode will default to TextInput.SelectCharacters. |
1646 | |
1647 | \list |
1648 | \o TextEdit.SelectCharacters - Sets either the selectionStart or selectionEnd (whichever was at |
1649 | the previous cursor position) to the specified position. |
1650 | \o TextEdit.SelectWords - Sets the selectionStart and selectionEnd to include all |
1651 | words between the specified postion and the previous cursor position. Words partially in the |
1652 | range are included. |
1653 | \endlist |
1654 | |
1655 | For example, take this sequence of calls: |
1656 | |
1657 | \code |
1658 | cursorPosition = 5 |
1659 | moveCursorSelection(9, TextInput.SelectCharacters) |
1660 | moveCursorSelection(7, TextInput.SelectCharacters) |
1661 | \endcode |
1662 | |
1663 | This moves the cursor to position 5, extend the selection end from 5 to 9 |
1664 | and then retract the selection end from 9 to 7, leaving the text from position 5 to 7 |
1665 | selected (the 6th and 7th characters). |
1666 | |
1667 | The same sequence with TextInput.SelectWords will extend the selection start to a word boundary |
1668 | before or on position 5 and extend the selection end to a word boundary on or past position 9. |
1669 | */ |
1670 | void QDeclarativeTextInput::moveCursorSelection(int pos, SelectionMode mode) |
1671 | { |
1672 | Q_D(QDeclarativeTextInput); |
1673 | |
1674 | if (mode == SelectCharacters) { |
1675 | d->control->moveCursor(pos, true); |
1676 | } else if (pos != d->control->cursor()){ |
1677 | const int cursor = d->control->cursor(); |
1678 | int anchor; |
1679 | if (!d->control->hasSelectedText()) |
1680 | anchor = d->control->cursor(); |
1681 | else if (d->control->selectionStart() == d->control->cursor()) |
1682 | anchor = d->control->selectionEnd(); |
1683 | else |
1684 | anchor = d->control->selectionStart(); |
1685 | |
1686 | if (anchor < pos || (anchor == pos && cursor < pos)) { |
1687 | const QString text = d->control->text(); |
1688 | QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text); |
1689 | finder.setPosition(anchor); |
1690 | |
1691 | const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons(); |
1692 | if (anchor < text.length() && (!(reasons & QTextBoundaryFinder::StartWord) |
1693 | || ((reasons & QTextBoundaryFinder::EndWord) && anchor > cursor))) { |
1694 | finder.toPreviousBoundary(); |
1695 | } |
1696 | anchor = finder.position() != -1 ? finder.position() : 0; |
1697 | |
1698 | finder.setPosition(pos); |
1699 | if (pos > 0 && !finder.boundaryReasons()) |
1700 | finder.toNextBoundary(); |
1701 | const int cursor = finder.position() != -1 ? finder.position() : text.length(); |
1702 | |
1703 | d->control->setSelection(anchor, cursor - anchor); |
1704 | } else if (anchor > pos || (anchor == pos && cursor > pos)) { |
1705 | const QString text = d->control->text(); |
1706 | QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text); |
1707 | finder.setPosition(anchor); |
1708 | |
1709 | const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons(); |
1710 | if (anchor > 0 && (!(reasons & QTextBoundaryFinder::EndWord) |
1711 | || ((reasons & QTextBoundaryFinder::StartWord) && anchor < cursor))) { |
1712 | finder.toNextBoundary(); |
1713 | } |
1714 | anchor = finder.position() != -1 ? finder.position() : text.length(); |
1715 | |
1716 | finder.setPosition(pos); |
1717 | if (pos < text.length() && !finder.boundaryReasons()) |
1718 | finder.toPreviousBoundary(); |
1719 | const int cursor = finder.position() != -1 ? finder.position() : 0; |
1720 | |
1721 | d->control->setSelection(anchor, cursor - anchor); |
1722 | } |
1723 | } |
1724 | } |
1725 | |
1726 | /*! |
1727 | \qmlmethod void TextInput::openSoftwareInputPanel() |
1728 | |
1729 | Opens software input panels like virtual keyboards for typing, useful for |
1730 | customizing when you want the input keyboard to be shown and hidden in |
1731 | your application. |
1732 | |
1733 | By default the opening of input panels follows the platform style. On Symbian^1 and |
1734 | Symbian^3 -based devices the panels are opened by clicking TextInput. On other platforms |
1735 | the panels are automatically opened when TextInput element gains active focus. Input panels are |
1736 | always closed if no editor has active focus. |
1737 | |
1738 | . You can disable the automatic behavior by setting the property \c activeFocusOnPress to false |
1739 | and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement |
1740 | the behavior you want. |
1741 | |
1742 | Only relevant on platforms, which provide virtual keyboards. |
1743 | |
1744 | \qml |
1745 | import QtQuick 1.0 |
1746 | TextInput { |
1747 | id: textInput |
1748 | text: "Hello world!" |
1749 | activeFocusOnPress: false |
1750 | MouseArea { |
1751 | anchors.fill: parent |
1752 | onClicked: { |
1753 | if (!textInput.activeFocus) { |
1754 | textInput.forceActiveFocus() |
1755 | textInput.openSoftwareInputPanel(); |
1756 | } else { |
1757 | textInput.focus = false; |
1758 | } |
1759 | } |
1760 | onPressAndHold: textInput.closeSoftwareInputPanel(); |
1761 | } |
1762 | } |
1763 | \endqml |
1764 | */ |
1765 | void QDeclarativeTextInput::openSoftwareInputPanel() |
1766 | { |
1767 | QEvent event(QEvent::RequestSoftwareInputPanel); |
1768 | if (qApp) { |
1769 | if (QGraphicsView * view = qobject_cast<QGraphicsView*>(qApp->focusWidget())) { |
1770 | if (view->scene() && view->scene() == scene()) { |
1771 | QApplication::sendEvent(view, &event); |
1772 | } |
1773 | } |
1774 | } |
1775 | } |
1776 | |
1777 | /*! |
1778 | \qmlmethod void TextInput::closeSoftwareInputPanel() |
1779 | |
1780 | Closes a software input panel like a virtual keyboard shown on the screen, useful |
1781 | for customizing when you want the input keyboard to be shown and hidden in |
1782 | your application. |
1783 | |
1784 | By default the opening of input panels follows the platform style. On Symbian^1 and |
1785 | Symbian^3 -based devices the panels are opened by clicking TextInput. On other platforms |
1786 | the panels are automatically opened when TextInput element gains active focus. Input panels are |
1787 | always closed if no editor has active focus. |
1788 | |
1789 | . You can disable the automatic behavior by setting the property \c activeFocusOnPress to false |
1790 | and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement |
1791 | the behavior you want. |
1792 | |
1793 | Only relevant on platforms, which provide virtual keyboards. |
1794 | |
1795 | \qml |
1796 | import QtQuick 1.0 |
1797 | TextInput { |
1798 | id: textInput |
1799 | text: "Hello world!" |
1800 | activeFocusOnPress: false |
1801 | MouseArea { |
1802 | anchors.fill: parent |
1803 | onClicked: { |
1804 | if (!textInput.activeFocus) { |
1805 | textInput.forceActiveFocus(); |
1806 | textInput.openSoftwareInputPanel(); |
1807 | } else { |
1808 | textInput.focus = false; |
1809 | } |
1810 | } |
1811 | onPressAndHold: textInput.closeSoftwareInputPanel(); |
1812 | } |
1813 | } |
1814 | \endqml |
1815 | */ |
1816 | void QDeclarativeTextInput::closeSoftwareInputPanel() |
1817 | { |
1818 | QEvent event(QEvent::CloseSoftwareInputPanel); |
1819 | if (qApp) { |
1820 | QEvent event(QEvent::CloseSoftwareInputPanel); |
1821 | if (QGraphicsView * view = qobject_cast<QGraphicsView*>(qApp->focusWidget())) { |
1822 | if (view->scene() && view->scene() == scene()) { |
1823 | QApplication::sendEvent(view, &event); |
1824 | } |
1825 | } |
1826 | } |
1827 | } |
1828 | |
1829 | void QDeclarativeTextInput::focusInEvent(QFocusEvent *event) |
1830 | { |
1831 | Q_D(const QDeclarativeTextInput); |
1832 | if (d->showInputPanelOnFocus) { |
1833 | if (d->focusOnPress && !isReadOnly()) { |
1834 | openSoftwareInputPanel(); |
1835 | } |
1836 | } |
1837 | QDeclarativePaintedItem::focusInEvent(event); |
1838 | } |
1839 | |
1840 | /*! |
1841 | \qmlproperty bool TextInput::inputMethodComposing |
1842 | |
1843 | \since QtQuick 1.1 |
1844 | |
1845 | This property holds whether the TextInput has partial text input from an |
1846 | input method. |
1847 | |
1848 | While it is composing an input method may rely on mouse or key events from |
1849 | the TextInput to edit or commit the partial text. This property can be |
1850 | used to determine when to disable events handlers that may interfere with |
1851 | the correct operation of an input method. |
1852 | */ |
1853 | bool QDeclarativeTextInput::isInputMethodComposing() const |
1854 | { |
1855 | Q_D(const QDeclarativeTextInput); |
1856 | return d->control->preeditAreaText().length() > 0; |
1857 | } |
1858 | |
1859 | void QDeclarativeTextInputPrivate::init() |
1860 | { |
1861 | Q_Q(QDeclarativeTextInput); |
1862 | control->setParent(q); |
1863 | control->setCursorWidth(1); |
1864 | control->setPasswordCharacter(QLatin1Char('*')); |
1865 | q->setSmooth(smooth); |
1866 | q->setAcceptedMouseButtons(Qt::LeftButton); |
1867 | q->setFlag(QGraphicsItem::ItemHasNoContents, false); |
1868 | q->setFlag(QGraphicsItem::ItemAcceptsInputMethod); |
1869 | q->connect(control, SIGNAL(cursorPositionChanged(int,int)), |
1870 | q, SLOT(cursorPosChanged())); |
1871 | q->connect(control, SIGNAL(selectionChanged()), |
1872 | q, SLOT(selectionChanged())); |
1873 | q->connect(control, SIGNAL(textChanged(QString)), |
1874 | q, SLOT(q_textChanged())); |
1875 | q->connect(control, SIGNAL(accepted()), |
1876 | q, SIGNAL(accepted())); |
1877 | q->connect(control, SIGNAL(updateNeeded(QRect)), |
1878 | q, SLOT(updateRect(QRect))); |
1879 | #ifndef QT_NO_CLIPBOARD |
1880 | q->connect(q, SIGNAL(readOnlyChanged(bool)), |
1881 | q, SLOT(q_canPasteChanged())); |
1882 | q->connect(QApplication::clipboard(), SIGNAL(dataChanged()), |
1883 | q, SLOT(q_canPasteChanged())); |
1884 | canPaste = !control->isReadOnly() && QApplication::clipboard()->text().length() != 0; |
1885 | #endif // QT_NO_CLIPBOARD |
1886 | q->connect(control, SIGNAL(updateMicroFocus()), |
1887 | q, SLOT(updateCursorRectangle())); |
1888 | q->connect(control, SIGNAL(displayTextChanged(QString)), |
1889 | q, SLOT(updateRect())); |
1890 | q->updateSize(); |
1891 | oldValidity = control->hasAcceptableInput(); |
1892 | lastSelectionStart = 0; |
1893 | lastSelectionEnd = 0; |
1894 | QPalette p = control->palette(); |
1895 | selectedTextColor = p.color(QPalette::HighlightedText); |
1896 | selectionColor = p.color(QPalette::Highlight); |
1897 | determineHorizontalAlignment(); |
1898 | } |
1899 | |
1900 | void QDeclarativeTextInput::cursorPosChanged() |
1901 | { |
1902 | Q_D(QDeclarativeTextInput); |
1903 | updateCursorRectangle(); |
1904 | emit cursorPositionChanged(); |
1905 | d->control->resetCursorBlinkTimer(); |
1906 | |
1907 | if(!d->control->hasSelectedText()){ |
1908 | if(d->lastSelectionStart != d->control->cursor()){ |
1909 | d->lastSelectionStart = d->control->cursor(); |
1910 | emit selectionStartChanged(); |
1911 | } |
1912 | if(d->lastSelectionEnd != d->control->cursor()){ |
1913 | d->lastSelectionEnd = d->control->cursor(); |
1914 | emit selectionEndChanged(); |
1915 | } |
1916 | } |
1917 | } |
1918 | |
1919 | void QDeclarativeTextInput::updateCursorRectangle() |
1920 | { |
1921 | Q_D(QDeclarativeTextInput); |
1922 | d->determineHorizontalAlignment(); |
1923 | d->updateHorizontalScroll(); |
1924 | updateRect();//TODO: Only update rect between pos's |
1925 | updateMicroFocus(); |
1926 | emit cursorRectangleChanged(); |
1927 | if (d->cursorItem) |
1928 | d->cursorItem->setX(d->control->cursorToX() - d->hscroll); |
1929 | } |
1930 | |
1931 | void QDeclarativeTextInput::selectionChanged() |
1932 | { |
1933 | Q_D(QDeclarativeTextInput); |
1934 | updateRect();//TODO: Only update rect in selection |
1935 | emit selectedTextChanged(); |
1936 | |
1937 | if(d->lastSelectionStart != d->control->selectionStart()){ |
1938 | d->lastSelectionStart = d->control->selectionStart(); |
1939 | if(d->lastSelectionStart == -1) |
1940 | d->lastSelectionStart = d->control->cursor(); |
1941 | emit selectionStartChanged(); |
1942 | } |
1943 | if(d->lastSelectionEnd != d->control->selectionEnd()){ |
1944 | d->lastSelectionEnd = d->control->selectionEnd(); |
1945 | if(d->lastSelectionEnd == -1) |
1946 | d->lastSelectionEnd = d->control->cursor(); |
1947 | emit selectionEndChanged(); |
1948 | } |
1949 | } |
1950 | |
1951 | void QDeclarativeTextInput::q_textChanged() |
1952 | { |
1953 | Q_D(QDeclarativeTextInput); |
1954 | emit textChanged(); |
1955 | emit displayTextChanged(); |
1956 | updateSize(); |
1957 | d->determineHorizontalAlignment(); |
1958 | d->updateHorizontalScroll(); |
1959 | updateMicroFocus(); |
1960 | if(hasAcceptableInput() != d->oldValidity){ |
1961 | d->oldValidity = hasAcceptableInput(); |
1962 | emit acceptableInputChanged(); |
1963 | } |
1964 | } |
1965 | |
1966 | void QDeclarativeTextInput::updateRect(const QRect &r) |
1967 | { |
1968 | Q_D(QDeclarativeTextInput); |
1969 | if(r == QRect()) |
1970 | clearCache(); |
1971 | else |
1972 | dirtyCache(QRect(r.x() - d->hscroll, r.y(), r.width(), r.height())); |
1973 | update(); |
1974 | } |
1975 | |
1976 | QRectF QDeclarativeTextInput::boundingRect() const |
1977 | { |
1978 | Q_D(const QDeclarativeTextInput); |
1979 | QRectF r = QDeclarativePaintedItem::boundingRect(); |
1980 | |
1981 | int cursorWidth = d->cursorItem ? d->cursorItem->width() : d->control->cursorWidth(); |
1982 | |
1983 | // Could include font max left/right bearings to either side of rectangle. |
1984 | |
1985 | r.setRight(r.right() + cursorWidth); |
1986 | return r; |
1987 | } |
1988 | |
1989 | void QDeclarativeTextInput::updateSize(bool needsRedraw) |
1990 | { |
1991 | Q_D(QDeclarativeTextInput); |
1992 | int w = width(); |
1993 | int h = height(); |
1994 | setImplicitHeight(d->control->height()-1); // -1 to counter QLineControl's +1 which is not consistent with Text. |
1995 | setImplicitWidth(d->calculateTextWidth()); |
1996 | setContentsSize(QSize(width(), height()));//Repaints if changed |
1997 | if(w==width() && h==height() && needsRedraw){ |
1998 | clearCache(); |
1999 | update(); |
2000 | } |
2001 | } |
2002 | |
2003 | void QDeclarativeTextInput::q_canPasteChanged() |
2004 | { |
2005 | Q_D(QDeclarativeTextInput); |
2006 | bool old = d->canPaste; |
2007 | #ifndef QT_NO_CLIPBOARD |
2008 | d->canPaste = !d->control->isReadOnly() && QApplication::clipboard()->text().length() != 0; |
2009 | #endif |
2010 | if(d->canPaste != old) |
2011 | emit canPasteChanged(); |
2012 | } |
2013 | |
2014 | QT_END_NAMESPACE |
2015 | |
2016 | #endif // QT_NO_LINEEDIT |
2017 | |
2018 | |