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/qdeclarativetextedit_p.h" |
43 | #include "private/qdeclarativetextedit_p_p.h" |
44 | |
45 | #include "private/qdeclarativeevents_p_p.h" |
46 | #include <private/qdeclarativeglobal_p.h> |
47 | #include <qdeclarativeinfo.h> |
48 | |
49 | #include <QtCore/qmath.h> |
50 | |
51 | #include <private/qtextengine_p.h> |
52 | #include <QTextLayout> |
53 | #include <QTextLine> |
54 | #include <QTextDocument> |
55 | #include <QTextObject> |
56 | #include <QGraphicsSceneMouseEvent> |
57 | #include <QDebug> |
58 | #include <QPainter> |
59 | |
60 | #include <private/qtextcontrol_p.h> |
61 | |
62 | QT_BEGIN_NAMESPACE |
63 | |
64 | /*! |
65 | \qmlclass TextEdit QDeclarativeTextEdit |
66 | \ingroup qml-basic-visual-elements |
67 | \since 4.7 |
68 | \brief The TextEdit item displays multiple lines of editable formatted text. |
69 | \inherits Item |
70 | |
71 | The TextEdit item displays a block of editable, formatted text. |
72 | |
73 | It can display both plain and rich text. For example: |
74 | |
75 | \qml |
76 | TextEdit { |
77 | width: 240 |
78 | text: "<b>Hello</b> <i>World!</i>" |
79 | font.family: "Helvetica" |
80 | font.pointSize: 20 |
81 | color: "blue" |
82 | focus: true |
83 | } |
84 | \endqml |
85 | |
86 | \image declarative-textedit.gif |
87 | |
88 | Setting \l {Item::focus}{focus} to \c true enables the TextEdit item to receive keyboard focus. |
89 | |
90 | Note that the TextEdit does not implement scrolling, following the cursor, or other behaviors specific |
91 | to a look-and-feel. For example, to add flickable scrolling that follows the cursor: |
92 | |
93 | \snippet snippets/declarative/texteditor.qml 0 |
94 | |
95 | A particular look-and-feel might use smooth scrolling (eg. using SmoothedFollow), might have a visible |
96 | scrollbar, or a scrollbar that fades in to show location, etc. |
97 | |
98 | Clipboard support is provided by the cut(), copy(), and paste() functions, and the selection can |
99 | be handled in a traditional "mouse" mechanism by setting selectByMouse, or handled completely |
100 | from QML by manipulating selectionStart and selectionEnd, or using selectAll() or selectWord(). |
101 | |
102 | You can translate between cursor positions (characters from the start of the document) and pixel |
103 | points using positionAt() and positionToRectangle(). |
104 | |
105 | \sa Text, TextInput, {declarative/text/textselection}{Text Selection example} |
106 | */ |
107 | |
108 | /*! |
109 | \qmlsignal TextEdit::onLinkActivated(string link) |
110 | \since QtQuick 1.1 |
111 | |
112 | This handler is called when the user clicks on a link embedded in the text. |
113 | The link must be in rich text or HTML format and the |
114 | \a link string provides access to the particular link. |
115 | */ |
116 | QDeclarativeTextEdit::QDeclarativeTextEdit(QDeclarativeItem *parent) |
117 | : QDeclarativeImplicitSizePaintedItem(*(new QDeclarativeTextEditPrivate), parent) |
118 | { |
119 | Q_D(QDeclarativeTextEdit); |
120 | d->init(); |
121 | } |
122 | |
123 | QString QDeclarativeTextEdit::text() const |
124 | { |
125 | Q_D(const QDeclarativeTextEdit); |
126 | |
127 | #ifndef QT_NO_TEXTHTMLPARSER |
128 | if (d->richText) |
129 | return d->document->toHtml(); |
130 | else |
131 | #endif |
132 | return d->document->toPlainText(); |
133 | } |
134 | |
135 | /*! |
136 | \qmlproperty string TextEdit::font.family |
137 | |
138 | Sets the family name of the font. |
139 | |
140 | The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]". |
141 | If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen. |
142 | If the family isn't available a family will be set using the font matching algorithm. |
143 | */ |
144 | |
145 | /*! |
146 | \qmlproperty bool TextEdit::font.bold |
147 | |
148 | Sets whether the font weight is bold. |
149 | */ |
150 | |
151 | /*! |
152 | \qmlproperty enumeration TextEdit::font.weight |
153 | |
154 | Sets the font's weight. |
155 | |
156 | The weight can be one of: |
157 | \list |
158 | \o Font.Light |
159 | \o Font.Normal - the default |
160 | \o Font.DemiBold |
161 | \o Font.Bold |
162 | \o Font.Black |
163 | \endlist |
164 | |
165 | \qml |
166 | TextEdit { text: "Hello"; font.weight: Font.DemiBold } |
167 | \endqml |
168 | */ |
169 | |
170 | /*! |
171 | \qmlproperty bool TextEdit::font.italic |
172 | |
173 | Sets whether the font has an italic style. |
174 | */ |
175 | |
176 | /*! |
177 | \qmlproperty bool TextEdit::font.underline |
178 | |
179 | Sets whether the text is underlined. |
180 | */ |
181 | |
182 | /*! |
183 | \qmlproperty bool TextEdit::font.strikeout |
184 | |
185 | Sets whether the font has a strikeout style. |
186 | */ |
187 | |
188 | /*! |
189 | \qmlproperty real TextEdit::font.pointSize |
190 | |
191 | Sets the font size in points. The point size must be greater than zero. |
192 | */ |
193 | |
194 | /*! |
195 | \qmlproperty int TextEdit::font.pixelSize |
196 | |
197 | Sets the font size in pixels. |
198 | |
199 | Using this function makes the font device dependent. Use |
200 | \l{TextEdit::font.pointSize} to set the size of the font in a |
201 | device independent manner. |
202 | */ |
203 | |
204 | /*! |
205 | \qmlproperty real TextEdit::font.letterSpacing |
206 | |
207 | Sets the letter spacing for the font. |
208 | |
209 | Letter spacing changes the default spacing between individual letters in the font. |
210 | A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing. |
211 | */ |
212 | |
213 | /*! |
214 | \qmlproperty real TextEdit::font.wordSpacing |
215 | |
216 | Sets the word spacing for the font. |
217 | |
218 | Word spacing changes the default spacing between individual words. |
219 | A positive value increases the word spacing by a corresponding amount of pixels, |
220 | while a negative value decreases the inter-word spacing accordingly. |
221 | */ |
222 | |
223 | /*! |
224 | \qmlproperty enumeration TextEdit::font.capitalization |
225 | |
226 | Sets the capitalization for the text. |
227 | |
228 | \list |
229 | \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied. |
230 | \o Font.AllUppercase - This alters the text to be rendered in all uppercase type. |
231 | \o Font.AllLowercase - This alters the text to be rendered in all lowercase type. |
232 | \o Font.SmallCaps - This alters the text to be rendered in small-caps type. |
233 | \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character. |
234 | \endlist |
235 | |
236 | \qml |
237 | TextEdit { text: "Hello"; font.capitalization: Font.AllLowercase } |
238 | \endqml |
239 | */ |
240 | |
241 | /*! |
242 | \qmlproperty string TextEdit::text |
243 | |
244 | The text to display. If the text format is AutoText the text edit will |
245 | automatically determine whether the text should be treated as |
246 | rich text. This determination is made using Qt::mightBeRichText(). |
247 | */ |
248 | void QDeclarativeTextEdit::setText(const QString &text) |
249 | { |
250 | Q_D(QDeclarativeTextEdit); |
251 | if (QDeclarativeTextEdit::text() == text) |
252 | return; |
253 | |
254 | d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(text)); |
255 | if (d->richText) { |
256 | #ifndef QT_NO_TEXTHTMLPARSER |
257 | d->control->setHtml(text); |
258 | #else |
259 | d->control->setPlainText(text); |
260 | #endif |
261 | } else { |
262 | d->control->setPlainText(text); |
263 | } |
264 | q_textChanged(); |
265 | } |
266 | |
267 | /*! |
268 | \qmlproperty enumeration TextEdit::textFormat |
269 | |
270 | The way the text property should be displayed. |
271 | |
272 | \list |
273 | \o TextEdit.AutoText |
274 | \o TextEdit.PlainText |
275 | \o TextEdit.RichText |
276 | \endlist |
277 | |
278 | The default is TextEdit.AutoText. If the text format is TextEdit.AutoText the text edit |
279 | will automatically determine whether the text should be treated as |
280 | rich text. This determination is made using Qt::mightBeRichText(). |
281 | |
282 | \table |
283 | \row |
284 | \o |
285 | \qml |
286 | Column { |
287 | TextEdit { |
288 | font.pointSize: 24 |
289 | text: "<b>Hello</b> <i>World!</i>" |
290 | } |
291 | TextEdit { |
292 | font.pointSize: 24 |
293 | textFormat: TextEdit.RichText |
294 | text: "<b>Hello</b> <i>World!</i>" |
295 | } |
296 | TextEdit { |
297 | font.pointSize: 24 |
298 | textFormat: TextEdit.PlainText |
299 | text: "<b>Hello</b> <i>World!</i>" |
300 | } |
301 | } |
302 | \endqml |
303 | \o \image declarative-textformat.png |
304 | \endtable |
305 | */ |
306 | QDeclarativeTextEdit::TextFormat QDeclarativeTextEdit::textFormat() const |
307 | { |
308 | Q_D(const QDeclarativeTextEdit); |
309 | return d->format; |
310 | } |
311 | |
312 | void QDeclarativeTextEdit::setTextFormat(TextFormat format) |
313 | { |
314 | Q_D(QDeclarativeTextEdit); |
315 | if (format == d->format) |
316 | return; |
317 | bool wasRich = d->richText; |
318 | d->richText = format == RichText || (format == AutoText && Qt::mightBeRichText(d->text)); |
319 | |
320 | if (wasRich && !d->richText) { |
321 | d->control->setPlainText(d->text); |
322 | updateSize(); |
323 | } else if (!wasRich && d->richText) { |
324 | #ifndef QT_NO_TEXTHTMLPARSER |
325 | d->control->setHtml(d->text); |
326 | #else |
327 | d->control->setPlainText(d->text); |
328 | #endif |
329 | updateSize(); |
330 | } |
331 | d->format = format; |
332 | d->control->setAcceptRichText(d->format != PlainText); |
333 | emit textFormatChanged(d->format); |
334 | } |
335 | |
336 | QFont QDeclarativeTextEdit::font() const |
337 | { |
338 | Q_D(const QDeclarativeTextEdit); |
339 | return d->sourceFont; |
340 | } |
341 | |
342 | void QDeclarativeTextEdit::setFont(const QFont &font) |
343 | { |
344 | Q_D(QDeclarativeTextEdit); |
345 | if (d->sourceFont == font) |
346 | return; |
347 | |
348 | d->sourceFont = font; |
349 | QFont oldFont = d->font; |
350 | d->font = font; |
351 | if (d->font.pointSizeF() != -1) { |
352 | // 0.5pt resolution |
353 | qreal size = qRound(d->font.pointSizeF()*2.0); |
354 | d->font.setPointSizeF(size/2.0); |
355 | } |
356 | |
357 | if (oldFont != d->font) { |
358 | clearCache(); |
359 | d->document->setDefaultFont(d->font); |
360 | if(d->cursor){ |
361 | d->cursor->setHeight(QFontMetrics(d->font).height()); |
362 | moveCursorDelegate(); |
363 | } |
364 | updateSize(); |
365 | update(); |
366 | } |
367 | emit fontChanged(d->sourceFont); |
368 | } |
369 | |
370 | /*! |
371 | \qmlproperty color TextEdit::color |
372 | |
373 | The text color. |
374 | |
375 | \qml |
376 | // green text using hexadecimal notation |
377 | TextEdit { color: "#00FF00" } |
378 | \endqml |
379 | |
380 | \qml |
381 | // steelblue text using SVG color name |
382 | TextEdit { color: "steelblue" } |
383 | \endqml |
384 | */ |
385 | QColor QDeclarativeTextEdit::color() const |
386 | { |
387 | Q_D(const QDeclarativeTextEdit); |
388 | return d->color; |
389 | } |
390 | |
391 | void QDeclarativeTextEdit::setColor(const QColor &color) |
392 | { |
393 | Q_D(QDeclarativeTextEdit); |
394 | if (d->color == color) |
395 | return; |
396 | |
397 | clearCache(); |
398 | d->color = color; |
399 | QPalette pal = d->control->palette(); |
400 | pal.setColor(QPalette::Text, color); |
401 | d->control->setPalette(pal); |
402 | update(); |
403 | emit colorChanged(d->color); |
404 | } |
405 | |
406 | /*! |
407 | \qmlproperty color TextEdit::selectionColor |
408 | |
409 | The text highlight color, used behind selections. |
410 | */ |
411 | QColor QDeclarativeTextEdit::selectionColor() const |
412 | { |
413 | Q_D(const QDeclarativeTextEdit); |
414 | return d->selectionColor; |
415 | } |
416 | |
417 | void QDeclarativeTextEdit::setSelectionColor(const QColor &color) |
418 | { |
419 | Q_D(QDeclarativeTextEdit); |
420 | if (d->selectionColor == color) |
421 | return; |
422 | |
423 | clearCache(); |
424 | d->selectionColor = color; |
425 | QPalette pal = d->control->palette(); |
426 | pal.setColor(QPalette::Highlight, color); |
427 | d->control->setPalette(pal); |
428 | update(); |
429 | emit selectionColorChanged(d->selectionColor); |
430 | } |
431 | |
432 | /*! |
433 | \qmlproperty color TextEdit::selectedTextColor |
434 | |
435 | The selected text color, used in selections. |
436 | */ |
437 | QColor QDeclarativeTextEdit::selectedTextColor() const |
438 | { |
439 | Q_D(const QDeclarativeTextEdit); |
440 | return d->selectedTextColor; |
441 | } |
442 | |
443 | void QDeclarativeTextEdit::setSelectedTextColor(const QColor &color) |
444 | { |
445 | Q_D(QDeclarativeTextEdit); |
446 | if (d->selectedTextColor == color) |
447 | return; |
448 | |
449 | clearCache(); |
450 | d->selectedTextColor = color; |
451 | QPalette pal = d->control->palette(); |
452 | pal.setColor(QPalette::HighlightedText, color); |
453 | d->control->setPalette(pal); |
454 | update(); |
455 | emit selectedTextColorChanged(d->selectedTextColor); |
456 | } |
457 | |
458 | /*! |
459 | \qmlproperty enumeration TextEdit::horizontalAlignment |
460 | \qmlproperty enumeration TextEdit::verticalAlignment |
461 | |
462 | Sets the horizontal and vertical alignment of the text within the TextEdit item's |
463 | width and height. By default, the text alignment follows the natural alignment |
464 | of the text, for example text that is read from left to right will be aligned to |
465 | the left. |
466 | |
467 | Valid values for \c horizontalAlignment are: |
468 | \list |
469 | \o TextEdit.AlignLeft (default) |
470 | \o TextEdit.AlignRight |
471 | \o TextEdit.AlignHCenter |
472 | \o TextEdit.AlignJustify |
473 | \endlist |
474 | |
475 | Valid values for \c verticalAlignment are: |
476 | \list |
477 | \o TextEdit.AlignTop (default) |
478 | \o TextEdit.AlignBottom |
479 | \o TextEdit.AlignVCenter |
480 | \endlist |
481 | |
482 | When using the attached property \l {LayoutMirroring::enabled} to mirror application |
483 | layouts, the horizontal alignment of text will also be mirrored. However, the property |
484 | \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment |
485 | of TextEdit, use the property \l {LayoutMirroring::enabled}. |
486 | */ |
487 | QDeclarativeTextEdit::HAlignment QDeclarativeTextEdit::hAlign() const |
488 | { |
489 | Q_D(const QDeclarativeTextEdit); |
490 | return d->hAlign; |
491 | } |
492 | |
493 | void QDeclarativeTextEdit::setHAlign(HAlignment align) |
494 | { |
495 | Q_D(QDeclarativeTextEdit); |
496 | bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror; |
497 | d->hAlignImplicit = false; |
498 | if (d->setHAlign(align, forceAlign) && isComponentComplete()) { |
499 | d->updateDefaultTextOption(); |
500 | updateSize(); |
501 | } |
502 | } |
503 | |
504 | void QDeclarativeTextEdit::resetHAlign() |
505 | { |
506 | Q_D(QDeclarativeTextEdit); |
507 | d->hAlignImplicit = true; |
508 | if (d->determineHorizontalAlignment() && isComponentComplete()) { |
509 | d->updateDefaultTextOption(); |
510 | updateSize(); |
511 | } |
512 | } |
513 | |
514 | QDeclarativeTextEdit::HAlignment QDeclarativeTextEdit::effectiveHAlign() const |
515 | { |
516 | Q_D(const QDeclarativeTextEdit); |
517 | QDeclarativeTextEdit::HAlignment effectiveAlignment = d->hAlign; |
518 | if (!d->hAlignImplicit && d->effectiveLayoutMirror) { |
519 | switch (d->hAlign) { |
520 | case QDeclarativeTextEdit::AlignLeft: |
521 | effectiveAlignment = QDeclarativeTextEdit::AlignRight; |
522 | break; |
523 | case QDeclarativeTextEdit::AlignRight: |
524 | effectiveAlignment = QDeclarativeTextEdit::AlignLeft; |
525 | break; |
526 | default: |
527 | break; |
528 | } |
529 | } |
530 | return effectiveAlignment; |
531 | } |
532 | |
533 | bool QDeclarativeTextEditPrivate::setHAlign(QDeclarativeTextEdit::HAlignment alignment, bool forceAlign) |
534 | { |
535 | Q_Q(QDeclarativeTextEdit); |
536 | if (hAlign != alignment || forceAlign) { |
537 | hAlign = alignment; |
538 | emit q->horizontalAlignmentChanged(alignment); |
539 | return true; |
540 | } |
541 | return false; |
542 | } |
543 | |
544 | bool QDeclarativeTextEditPrivate::determineHorizontalAlignment() |
545 | { |
546 | Q_Q(QDeclarativeTextEdit); |
547 | if (hAlignImplicit && q->isComponentComplete()) { |
548 | bool alignToRight; |
549 | if (text.isEmpty() && !control->textCursor().isNull()) { |
550 | const QString preeditText = control->textCursor().block().layout()->preeditAreaText(); |
551 | alignToRight = preeditText.isEmpty() |
552 | ? QApplication::keyboardInputDirection() == Qt::RightToLeft |
553 | : preeditText.isRightToLeft(); |
554 | } else { |
555 | alignToRight = rightToLeftText; |
556 | } |
557 | return setHAlign(alignToRight ? QDeclarativeTextEdit::AlignRight : QDeclarativeTextEdit::AlignLeft); |
558 | } |
559 | return false; |
560 | } |
561 | |
562 | void QDeclarativeTextEditPrivate::mirrorChange() |
563 | { |
564 | Q_Q(QDeclarativeTextEdit); |
565 | if (q->isComponentComplete()) { |
566 | if (!hAlignImplicit && (hAlign == QDeclarativeTextEdit::AlignRight || hAlign == QDeclarativeTextEdit::AlignLeft)) { |
567 | updateDefaultTextOption(); |
568 | q->updateSize(); |
569 | } |
570 | } |
571 | } |
572 | |
573 | QDeclarativeTextEdit::VAlignment QDeclarativeTextEdit::vAlign() const |
574 | { |
575 | Q_D(const QDeclarativeTextEdit); |
576 | return d->vAlign; |
577 | } |
578 | |
579 | void QDeclarativeTextEdit::setVAlign(QDeclarativeTextEdit::VAlignment alignment) |
580 | { |
581 | Q_D(QDeclarativeTextEdit); |
582 | if (alignment == d->vAlign) |
583 | return; |
584 | d->vAlign = alignment; |
585 | d->updateDefaultTextOption(); |
586 | updateSize(); |
587 | moveCursorDelegate(); |
588 | emit verticalAlignmentChanged(d->vAlign); |
589 | } |
590 | |
591 | /*! |
592 | \qmlproperty enumeration TextEdit::wrapMode |
593 | |
594 | Set this property to wrap the text to the TextEdit item's width. |
595 | The text will only wrap if an explicit width has been set. |
596 | |
597 | \list |
598 | \o TextEdit.NoWrap - no wrapping will be performed. If the text contains insufficient newlines, then implicitWidth will exceed a set width. |
599 | \o TextEdit.WordWrap - wrapping is done on word boundaries only. If a word is too long, implicitWidth will exceed a set width. |
600 | \o TextEdit.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word. |
601 | \o TextEdit.Wrap - if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word. |
602 | \endlist |
603 | |
604 | The default is TextEdit.NoWrap. If you set a width, consider using TextEdit.Wrap. |
605 | */ |
606 | QDeclarativeTextEdit::WrapMode QDeclarativeTextEdit::wrapMode() const |
607 | { |
608 | Q_D(const QDeclarativeTextEdit); |
609 | return d->wrapMode; |
610 | } |
611 | |
612 | void QDeclarativeTextEdit::setWrapMode(WrapMode mode) |
613 | { |
614 | Q_D(QDeclarativeTextEdit); |
615 | if (mode == d->wrapMode) |
616 | return; |
617 | d->wrapMode = mode; |
618 | d->updateDefaultTextOption(); |
619 | updateSize(); |
620 | emit wrapModeChanged(); |
621 | } |
622 | |
623 | /*! |
624 | \qmlproperty int TextEdit::lineCount |
625 | \since QtQuick 1.1 |
626 | |
627 | Returns the total number of lines in the textEdit item. |
628 | */ |
629 | int QDeclarativeTextEdit::lineCount() const |
630 | { |
631 | Q_D(const QDeclarativeTextEdit); |
632 | return d->lineCount; |
633 | } |
634 | |
635 | /*! |
636 | \qmlproperty real TextEdit::paintedWidth |
637 | |
638 | Returns the width of the text, including the width past the width |
639 | which is covered due to insufficient wrapping if \l wrapMode is set. |
640 | */ |
641 | qreal QDeclarativeTextEdit::paintedWidth() const |
642 | { |
643 | Q_D(const QDeclarativeTextEdit); |
644 | return d->paintedSize.width(); |
645 | } |
646 | |
647 | /*! |
648 | \qmlproperty real TextEdit::paintedHeight |
649 | |
650 | Returns the height of the text, including the height past the height |
651 | that is covered if the text does not fit within the set height. |
652 | */ |
653 | qreal QDeclarativeTextEdit::paintedHeight() const |
654 | { |
655 | Q_D(const QDeclarativeTextEdit); |
656 | return d->paintedSize.height(); |
657 | } |
658 | |
659 | /*! |
660 | \qmlmethod rectangle TextEdit::positionToRectangle(position) |
661 | |
662 | Returns the rectangle at the given \a position in the text. The x, y, |
663 | and height properties correspond to the cursor that would describe |
664 | that position. |
665 | */ |
666 | QRectF QDeclarativeTextEdit::positionToRectangle(int pos) const |
667 | { |
668 | Q_D(const QDeclarativeTextEdit); |
669 | QTextCursor c(d->document); |
670 | c.setPosition(pos); |
671 | return d->control->cursorRect(c); |
672 | |
673 | } |
674 | |
675 | /*! |
676 | \qmlmethod int TextEdit::positionAt(int x, int y) |
677 | |
678 | Returns the text position closest to pixel position (\a x, \a y). |
679 | |
680 | Position 0 is before the first character, position 1 is after the first character |
681 | but before the second, and so on until position \l {text}.length, which is after all characters. |
682 | */ |
683 | int QDeclarativeTextEdit::positionAt(int x, int y) const |
684 | { |
685 | Q_D(const QDeclarativeTextEdit); |
686 | int r = d->document->documentLayout()->hitTest(QPoint(x,y-d->yoff), Qt::FuzzyHit); |
687 | QTextCursor cursor = d->control->textCursor(); |
688 | if (r > cursor.position()) { |
689 | // The cursor position includes positions within the preedit text, but only positions in the |
690 | // same text block are offset so it is possible to get a position that is either part of the |
691 | // preedit or the next text block. |
692 | QTextLayout *layout = cursor.block().layout(); |
693 | const int preeditLength = layout |
694 | ? layout->preeditAreaText().length() |
695 | : 0; |
696 | if (preeditLength > 0 |
697 | && d->document->documentLayout()->blockBoundingRect(cursor.block()).contains(x,y-d->yoff)) { |
698 | r = r > cursor.position() + preeditLength |
699 | ? r - preeditLength |
700 | : cursor.position(); |
701 | } |
702 | } |
703 | return r; |
704 | } |
705 | |
706 | void QDeclarativeTextEdit::moveCursorSelection(int pos) |
707 | { |
708 | //Note that this is the same as setCursorPosition but with the KeepAnchor flag set |
709 | Q_D(QDeclarativeTextEdit); |
710 | QTextCursor cursor = d->control->textCursor(); |
711 | if (cursor.position() == pos) |
712 | return; |
713 | cursor.setPosition(pos, QTextCursor::KeepAnchor); |
714 | d->control->setTextCursor(cursor); |
715 | } |
716 | |
717 | /*! |
718 | \qmlmethod void TextEdit::moveCursorSelection(int position, SelectionMode mode = TextEdit.SelectCharacters) |
719 | \since QtQuick 1.1 |
720 | |
721 | Moves the cursor to \a position and updates the selection according to the optional \a mode |
722 | parameter. (To only move the cursor, set the \l cursorPosition property.) |
723 | |
724 | When this method is called it additionally sets either the |
725 | selectionStart or the selectionEnd (whichever was at the previous cursor position) |
726 | to the specified position. This allows you to easily extend and contract the selected |
727 | text range. |
728 | |
729 | The selection mode specifies whether the selection is updated on a per character or a per word |
730 | basis. If not specified the selection mode will default to TextEdit.SelectCharacters. |
731 | |
732 | \list |
733 | \o TextEdit.SelectCharacters - Sets either the selectionStart or selectionEnd (whichever was at |
734 | the previous cursor position) to the specified position. |
735 | \o TextEdit.SelectWords - Sets the selectionStart and selectionEnd to include all |
736 | words between the specified postion and the previous cursor position. Words partially in the |
737 | range are included. |
738 | \endlist |
739 | |
740 | For example, take this sequence of calls: |
741 | |
742 | \code |
743 | cursorPosition = 5 |
744 | moveCursorSelection(9, TextEdit.SelectCharacters) |
745 | moveCursorSelection(7, TextEdit.SelectCharacters) |
746 | \endcode |
747 | |
748 | This moves the cursor to position 5, extend the selection end from 5 to 9 |
749 | and then retract the selection end from 9 to 7, leaving the text from position 5 to 7 |
750 | selected (the 6th and 7th characters). |
751 | |
752 | The same sequence with TextEdit.SelectWords will extend the selection start to a word boundary |
753 | before or on position 5 and extend the selection end to a word boundary on or past position 9. |
754 | */ |
755 | void QDeclarativeTextEdit::moveCursorSelection(int pos, SelectionMode mode) |
756 | { |
757 | Q_D(QDeclarativeTextEdit); |
758 | QTextCursor cursor = d->control->textCursor(); |
759 | if (cursor.position() == pos) |
760 | return; |
761 | if (mode == SelectCharacters) { |
762 | cursor.setPosition(pos, QTextCursor::KeepAnchor); |
763 | } else if (cursor.anchor() < pos || (cursor.anchor() == pos && cursor.position() < pos)) { |
764 | if (cursor.anchor() > cursor.position()) { |
765 | cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor); |
766 | cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor); |
767 | if (cursor.position() == cursor.anchor()) |
768 | cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::MoveAnchor); |
769 | else |
770 | cursor.setPosition(cursor.position(), QTextCursor::MoveAnchor); |
771 | } else { |
772 | cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor); |
773 | cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor); |
774 | } |
775 | |
776 | cursor.setPosition(pos, QTextCursor::KeepAnchor); |
777 | cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor); |
778 | if (cursor.position() != pos) |
779 | cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); |
780 | } else if (cursor.anchor() > pos || (cursor.anchor() == pos && cursor.position() > pos)) { |
781 | if (cursor.anchor() < cursor.position()) { |
782 | cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor); |
783 | cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::MoveAnchor); |
784 | } else { |
785 | cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor); |
786 | cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor); |
787 | cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); |
788 | if (cursor.position() != cursor.anchor()) { |
789 | cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor); |
790 | cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::MoveAnchor); |
791 | } |
792 | } |
793 | |
794 | cursor.setPosition(pos, QTextCursor::KeepAnchor); |
795 | cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); |
796 | if (cursor.position() != pos) { |
797 | cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor); |
798 | cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor); |
799 | } |
800 | } |
801 | d->control->setTextCursor(cursor); |
802 | } |
803 | |
804 | /*! |
805 | \qmlproperty bool TextEdit::cursorVisible |
806 | If true the text edit shows a cursor. |
807 | |
808 | This property is set and unset when the text edit gets active focus, but it can also |
809 | be set directly (useful, for example, if a KeyProxy might forward keys to it). |
810 | */ |
811 | bool QDeclarativeTextEdit::isCursorVisible() const |
812 | { |
813 | Q_D(const QDeclarativeTextEdit); |
814 | return d->cursorVisible; |
815 | } |
816 | |
817 | void QDeclarativeTextEdit::setCursorVisible(bool on) |
818 | { |
819 | Q_D(QDeclarativeTextEdit); |
820 | if (d->cursorVisible == on) |
821 | return; |
822 | d->cursorVisible = on; |
823 | QFocusEvent focusEvent(on ? QEvent::FocusIn : QEvent::FocusOut); |
824 | if (!on && !d->persistentSelection) |
825 | d->control->setCursorIsFocusIndicator(true); |
826 | d->control->processEvent(&focusEvent, QPointF(0, -d->yoff)); |
827 | emit cursorVisibleChanged(d->cursorVisible); |
828 | } |
829 | |
830 | /*! |
831 | \qmlproperty int TextEdit::cursorPosition |
832 | The position of the cursor in the TextEdit. |
833 | */ |
834 | int QDeclarativeTextEdit::cursorPosition() const |
835 | { |
836 | Q_D(const QDeclarativeTextEdit); |
837 | return d->control->textCursor().position(); |
838 | } |
839 | |
840 | void QDeclarativeTextEdit::setCursorPosition(int pos) |
841 | { |
842 | Q_D(QDeclarativeTextEdit); |
843 | if (pos < 0 || pos > d->text.length()) |
844 | return; |
845 | QTextCursor cursor = d->control->textCursor(); |
846 | if (cursor.position() == pos && cursor.anchor() == pos) |
847 | return; |
848 | cursor.setPosition(pos); |
849 | d->control->setTextCursor(cursor); |
850 | } |
851 | |
852 | /*! |
853 | \qmlproperty Component TextEdit::cursorDelegate |
854 | The delegate for the cursor in the TextEdit. |
855 | |
856 | If you set a cursorDelegate for a TextEdit, this delegate will be used for |
857 | drawing the cursor instead of the standard cursor. An instance of the |
858 | delegate will be created and managed by the text edit when a cursor is |
859 | needed, and the x and y properties of delegate instance will be set so as |
860 | to be one pixel before the top left of the current character. |
861 | |
862 | Note that the root item of the delegate component must be a QDeclarativeItem or |
863 | QDeclarativeItem derived item. |
864 | */ |
865 | QDeclarativeComponent* QDeclarativeTextEdit::cursorDelegate() const |
866 | { |
867 | Q_D(const QDeclarativeTextEdit); |
868 | return d->cursorComponent; |
869 | } |
870 | |
871 | void QDeclarativeTextEdit::setCursorDelegate(QDeclarativeComponent* c) |
872 | { |
873 | Q_D(QDeclarativeTextEdit); |
874 | if(d->cursorComponent){ |
875 | if(d->cursor){ |
876 | d->control->setCursorWidth(-1); |
877 | dirtyCache(cursorRectangle()); |
878 | delete d->cursor; |
879 | d->cursor = 0; |
880 | } |
881 | } |
882 | d->cursorComponent = c; |
883 | if(c && c->isReady()){ |
884 | loadCursorDelegate(); |
885 | }else{ |
886 | if(c) |
887 | connect(c, SIGNAL(statusChanged()), |
888 | this, SLOT(loadCursorDelegate())); |
889 | } |
890 | |
891 | emit cursorDelegateChanged(); |
892 | } |
893 | |
894 | void QDeclarativeTextEdit::loadCursorDelegate() |
895 | { |
896 | Q_D(QDeclarativeTextEdit); |
897 | if(d->cursorComponent->isLoading()) |
898 | return; |
899 | d->cursor = qobject_cast<QDeclarativeItem*>(d->cursorComponent->create(qmlContext(this))); |
900 | if(d->cursor){ |
901 | d->control->setCursorWidth(0); |
902 | dirtyCache(cursorRectangle()); |
903 | QDeclarative_setParent_noEvent(d->cursor, this); |
904 | d->cursor->setParentItem(this); |
905 | d->cursor->setHeight(QFontMetrics(d->font).height()); |
906 | moveCursorDelegate(); |
907 | }else{ |
908 | qmlInfo(this) << "Error loading cursor delegate." ; |
909 | } |
910 | } |
911 | |
912 | /*! |
913 | \qmlproperty int TextEdit::selectionStart |
914 | |
915 | The cursor position before the first character in the current selection. |
916 | |
917 | This property is read-only. To change the selection, use select(start,end), |
918 | selectAll(), or selectWord(). |
919 | |
920 | \sa selectionEnd, cursorPosition, selectedText |
921 | */ |
922 | int QDeclarativeTextEdit::selectionStart() const |
923 | { |
924 | Q_D(const QDeclarativeTextEdit); |
925 | return d->control->textCursor().selectionStart(); |
926 | } |
927 | |
928 | /*! |
929 | \qmlproperty int TextEdit::selectionEnd |
930 | |
931 | The cursor position after the last character in the current selection. |
932 | |
933 | This property is read-only. To change the selection, use select(start,end), |
934 | selectAll(), or selectWord(). |
935 | |
936 | \sa selectionStart, cursorPosition, selectedText |
937 | */ |
938 | int QDeclarativeTextEdit::selectionEnd() const |
939 | { |
940 | Q_D(const QDeclarativeTextEdit); |
941 | return d->control->textCursor().selectionEnd(); |
942 | } |
943 | |
944 | /*! |
945 | \qmlproperty string TextEdit::selectedText |
946 | |
947 | This read-only property provides the text currently selected in the |
948 | text edit. |
949 | |
950 | It is equivalent to the following snippet, but is faster and easier |
951 | to use. |
952 | \code |
953 | //myTextEdit is the id of the TextEdit |
954 | myTextEdit.text.toString().substring(myTextEdit.selectionStart, |
955 | myTextEdit.selectionEnd); |
956 | \endcode |
957 | */ |
958 | QString QDeclarativeTextEdit::selectedText() const |
959 | { |
960 | Q_D(const QDeclarativeTextEdit); |
961 | return d->control->textCursor().selectedText(); |
962 | } |
963 | |
964 | /*! |
965 | \qmlproperty bool TextEdit::activeFocusOnPress |
966 | |
967 | Whether the TextEdit should gain active focus on a mouse press. By default this is |
968 | set to true. |
969 | */ |
970 | bool QDeclarativeTextEdit::focusOnPress() const |
971 | { |
972 | Q_D(const QDeclarativeTextEdit); |
973 | return d->focusOnPress; |
974 | } |
975 | |
976 | void QDeclarativeTextEdit::setFocusOnPress(bool on) |
977 | { |
978 | Q_D(QDeclarativeTextEdit); |
979 | if (d->focusOnPress == on) |
980 | return; |
981 | d->focusOnPress = on; |
982 | emit activeFocusOnPressChanged(d->focusOnPress); |
983 | } |
984 | |
985 | /*! |
986 | \qmlproperty bool TextEdit::persistentSelection |
987 | |
988 | Whether the TextEdit should keep the selection visible when it loses active focus to another |
989 | item in the scene. By default this is set to true; |
990 | */ |
991 | bool QDeclarativeTextEdit::persistentSelection() const |
992 | { |
993 | Q_D(const QDeclarativeTextEdit); |
994 | return d->persistentSelection; |
995 | } |
996 | |
997 | void QDeclarativeTextEdit::setPersistentSelection(bool on) |
998 | { |
999 | Q_D(QDeclarativeTextEdit); |
1000 | if (d->persistentSelection == on) |
1001 | return; |
1002 | d->persistentSelection = on; |
1003 | emit persistentSelectionChanged(d->persistentSelection); |
1004 | } |
1005 | |
1006 | /* |
1007 | \qmlproperty real TextEdit::textMargin |
1008 | |
1009 | The margin, in pixels, around the text in the TextEdit. |
1010 | */ |
1011 | qreal QDeclarativeTextEdit::textMargin() const |
1012 | { |
1013 | Q_D(const QDeclarativeTextEdit); |
1014 | return d->textMargin; |
1015 | } |
1016 | |
1017 | void QDeclarativeTextEdit::setTextMargin(qreal margin) |
1018 | { |
1019 | Q_D(QDeclarativeTextEdit); |
1020 | if (d->textMargin == margin) |
1021 | return; |
1022 | d->textMargin = margin; |
1023 | d->document->setDocumentMargin(d->textMargin); |
1024 | emit textMarginChanged(d->textMargin); |
1025 | } |
1026 | |
1027 | void QDeclarativeTextEdit::geometryChanged(const QRectF &newGeometry, |
1028 | const QRectF &oldGeometry) |
1029 | { |
1030 | if (newGeometry.width() != oldGeometry.width()) |
1031 | updateSize(); |
1032 | QDeclarativePaintedItem::geometryChanged(newGeometry, oldGeometry); |
1033 | } |
1034 | |
1035 | /*! |
1036 | Ensures any delayed caching or data loading the class |
1037 | needs to performed is complete. |
1038 | */ |
1039 | void QDeclarativeTextEdit::componentComplete() |
1040 | { |
1041 | Q_D(QDeclarativeTextEdit); |
1042 | QDeclarativePaintedItem::componentComplete(); |
1043 | if (d->dirty) { |
1044 | d->determineHorizontalAlignment(); |
1045 | d->updateDefaultTextOption(); |
1046 | updateSize(); |
1047 | d->dirty = false; |
1048 | } |
1049 | } |
1050 | |
1051 | /*! |
1052 | \qmlproperty bool TextEdit::selectByMouse |
1053 | |
1054 | Defaults to false. |
1055 | |
1056 | If true, the user can use the mouse to select text in some |
1057 | platform-specific way. Note that for some platforms this may |
1058 | not be an appropriate interaction (eg. may conflict with how |
1059 | the text needs to behave inside a Flickable. |
1060 | */ |
1061 | bool QDeclarativeTextEdit::selectByMouse() const |
1062 | { |
1063 | Q_D(const QDeclarativeTextEdit); |
1064 | return d->selectByMouse; |
1065 | } |
1066 | |
1067 | void QDeclarativeTextEdit::setSelectByMouse(bool on) |
1068 | { |
1069 | Q_D(QDeclarativeTextEdit); |
1070 | if (d->selectByMouse != on) { |
1071 | d->selectByMouse = on; |
1072 | setKeepMouseGrab(on); |
1073 | if (on) |
1074 | setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByMouse); |
1075 | else |
1076 | setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByMouse); |
1077 | emit selectByMouseChanged(on); |
1078 | } |
1079 | } |
1080 | |
1081 | |
1082 | /*! |
1083 | \qmlproperty enum TextEdit::mouseSelectionMode |
1084 | \since QtQuick 1.1 |
1085 | |
1086 | Specifies how text should be selected using a mouse. |
1087 | |
1088 | \list |
1089 | \o TextEdit.SelectCharacters - The selection is updated with individual characters. (Default) |
1090 | \o TextEdit.SelectWords - The selection is updated with whole words. |
1091 | \endlist |
1092 | |
1093 | This property only applies when \l selectByMouse is true. |
1094 | */ |
1095 | |
1096 | QDeclarativeTextEdit::SelectionMode QDeclarativeTextEdit::mouseSelectionMode() const |
1097 | { |
1098 | Q_D(const QDeclarativeTextEdit); |
1099 | return d->mouseSelectionMode; |
1100 | } |
1101 | |
1102 | void QDeclarativeTextEdit::setMouseSelectionMode(SelectionMode mode) |
1103 | { |
1104 | Q_D(QDeclarativeTextEdit); |
1105 | if (d->mouseSelectionMode != mode) { |
1106 | d->mouseSelectionMode = mode; |
1107 | d->control->setWordSelectionEnabled(mode == SelectWords); |
1108 | emit mouseSelectionModeChanged(mode); |
1109 | } |
1110 | } |
1111 | |
1112 | /*! |
1113 | \qmlproperty bool TextEdit::readOnly |
1114 | |
1115 | Whether the user an interact with the TextEdit item. If this |
1116 | property is set to true the text cannot be edited by user interaction. |
1117 | |
1118 | By default this property is false. |
1119 | */ |
1120 | void QDeclarativeTextEdit::setReadOnly(bool r) |
1121 | { |
1122 | Q_D(QDeclarativeTextEdit); |
1123 | if (r == isReadOnly()) |
1124 | return; |
1125 | |
1126 | setFlag(QGraphicsItem::ItemAcceptsInputMethod, !r); |
1127 | |
1128 | Qt::TextInteractionFlags flags = Qt::LinksAccessibleByMouse; |
1129 | if (d->selectByMouse) |
1130 | flags = flags | Qt::TextSelectableByMouse; |
1131 | if (!r) |
1132 | flags = flags | Qt::TextSelectableByKeyboard | Qt::TextEditable; |
1133 | d->control->setTextInteractionFlags(flags); |
1134 | if (!r) |
1135 | d->control->moveCursor(QTextCursor::End); |
1136 | |
1137 | emit readOnlyChanged(r); |
1138 | } |
1139 | |
1140 | bool QDeclarativeTextEdit::isReadOnly() const |
1141 | { |
1142 | Q_D(const QDeclarativeTextEdit); |
1143 | return !(d->control->textInteractionFlags() & Qt::TextEditable); |
1144 | } |
1145 | |
1146 | /*! |
1147 | Sets how the text edit should interact with user input to the given |
1148 | \a flags. |
1149 | */ |
1150 | void QDeclarativeTextEdit::setTextInteractionFlags(Qt::TextInteractionFlags flags) |
1151 | { |
1152 | Q_D(QDeclarativeTextEdit); |
1153 | d->control->setTextInteractionFlags(flags); |
1154 | } |
1155 | |
1156 | /*! |
1157 | Returns the flags specifying how the text edit should interact |
1158 | with user input. |
1159 | */ |
1160 | Qt::TextInteractionFlags QDeclarativeTextEdit::textInteractionFlags() const |
1161 | { |
1162 | Q_D(const QDeclarativeTextEdit); |
1163 | return d->control->textInteractionFlags(); |
1164 | } |
1165 | |
1166 | /*! |
1167 | \qmlproperty rectangle TextEdit::cursorRectangle |
1168 | |
1169 | The rectangle where the text cursor is rendered |
1170 | within the text edit. Read-only. |
1171 | */ |
1172 | QRect QDeclarativeTextEdit::cursorRectangle() const |
1173 | { |
1174 | Q_D(const QDeclarativeTextEdit); |
1175 | return d->control->cursorRect().toRect().translated(0,d->yoff); |
1176 | } |
1177 | |
1178 | |
1179 | /*! |
1180 | \overload |
1181 | Handles the given \a event. |
1182 | */ |
1183 | bool QDeclarativeTextEdit::event(QEvent *event) |
1184 | { |
1185 | Q_D(QDeclarativeTextEdit); |
1186 | if (event->type() == QEvent::ShortcutOverride) { |
1187 | d->control->processEvent(event, QPointF(0, -d->yoff)); |
1188 | return event->isAccepted(); |
1189 | } |
1190 | return QDeclarativePaintedItem::event(event); |
1191 | } |
1192 | |
1193 | /*! |
1194 | \overload |
1195 | Handles the given key \a event. |
1196 | */ |
1197 | void QDeclarativeTextEdit::keyPressEvent(QKeyEvent *event) |
1198 | { |
1199 | Q_D(QDeclarativeTextEdit); |
1200 | keyPressPreHandler(event); |
1201 | if (!event->isAccepted()) |
1202 | d->control->processEvent(event, QPointF(0, -d->yoff)); |
1203 | if (!event->isAccepted()) |
1204 | QDeclarativePaintedItem::keyPressEvent(event); |
1205 | } |
1206 | |
1207 | /*! |
1208 | \overload |
1209 | Handles the given key \a event. |
1210 | */ |
1211 | void QDeclarativeTextEdit::keyReleaseEvent(QKeyEvent *event) |
1212 | { |
1213 | Q_D(QDeclarativeTextEdit); |
1214 | keyReleasePreHandler(event); |
1215 | if (!event->isAccepted()) |
1216 | d->control->processEvent(event, QPointF(0, -d->yoff)); |
1217 | if (!event->isAccepted()) |
1218 | QDeclarativePaintedItem::keyReleaseEvent(event); |
1219 | } |
1220 | |
1221 | void QDeclarativeTextEditPrivate::focusChanged(bool hasFocus) |
1222 | { |
1223 | Q_Q(QDeclarativeTextEdit); |
1224 | q->setCursorVisible(hasFocus && scene && scene->hasFocus()); |
1225 | QDeclarativeItemPrivate::focusChanged(hasFocus); |
1226 | } |
1227 | |
1228 | /*! |
1229 | \qmlmethod void TextEdit::deselect() |
1230 | \since QtQuick 1.1 |
1231 | |
1232 | Removes active text selection. |
1233 | */ |
1234 | void QDeclarativeTextEdit::deselect() |
1235 | { |
1236 | Q_D(QDeclarativeTextEdit); |
1237 | QTextCursor c = d->control->textCursor(); |
1238 | c.clearSelection(); |
1239 | d->control->setTextCursor(c); |
1240 | } |
1241 | |
1242 | /*! |
1243 | \qmlmethod void TextEdit::selectAll() |
1244 | |
1245 | Causes all text to be selected. |
1246 | */ |
1247 | void QDeclarativeTextEdit::selectAll() |
1248 | { |
1249 | Q_D(QDeclarativeTextEdit); |
1250 | d->control->selectAll(); |
1251 | } |
1252 | |
1253 | /*! |
1254 | \qmlmethod void TextEdit::selectWord() |
1255 | |
1256 | Causes the word closest to the current cursor position to be selected. |
1257 | */ |
1258 | void QDeclarativeTextEdit::selectWord() |
1259 | { |
1260 | Q_D(QDeclarativeTextEdit); |
1261 | QTextCursor c = d->control->textCursor(); |
1262 | c.select(QTextCursor::WordUnderCursor); |
1263 | d->control->setTextCursor(c); |
1264 | } |
1265 | |
1266 | /*! |
1267 | \qmlmethod void TextEdit::select(int start, int end) |
1268 | |
1269 | Causes the text from \a start to \a end to be selected. |
1270 | |
1271 | If either start or end is out of range, the selection is not changed. |
1272 | |
1273 | After calling this, selectionStart will become the lesser |
1274 | and selectionEnd will become the greater (regardless of the order passed |
1275 | to this method). |
1276 | |
1277 | \sa selectionStart, selectionEnd |
1278 | */ |
1279 | void QDeclarativeTextEdit::select(int start, int end) |
1280 | { |
1281 | Q_D(QDeclarativeTextEdit); |
1282 | if (start < 0 || end < 0 || start > d->text.length() || end > d->text.length()) |
1283 | return; |
1284 | QTextCursor cursor = d->control->textCursor(); |
1285 | cursor.beginEditBlock(); |
1286 | cursor.setPosition(start, QTextCursor::MoveAnchor); |
1287 | cursor.setPosition(end, QTextCursor::KeepAnchor); |
1288 | cursor.endEditBlock(); |
1289 | d->control->setTextCursor(cursor); |
1290 | |
1291 | // QTBUG-11100 |
1292 | updateSelectionMarkers(); |
1293 | } |
1294 | |
1295 | /*! |
1296 | \qmlmethod void TextEdit::isRightToLeft(int start, int end) |
1297 | |
1298 | Returns true if the natural reading direction of the editor text |
1299 | found between positions \a start and \a end is right to left. |
1300 | */ |
1301 | bool QDeclarativeTextEdit::isRightToLeft(int start, int end) |
1302 | { |
1303 | Q_D(QDeclarativeTextEdit); |
1304 | if (start > end) { |
1305 | qmlInfo(this) << "isRightToLeft(start, end) called with the end property being smaller than the start." ; |
1306 | return false; |
1307 | } else { |
1308 | return d->text.mid(start, end - start).isRightToLeft(); |
1309 | } |
1310 | } |
1311 | |
1312 | #ifndef QT_NO_CLIPBOARD |
1313 | /*! |
1314 | \qmlmethod TextEdit::cut() |
1315 | |
1316 | Moves the currently selected text to the system clipboard. |
1317 | */ |
1318 | void QDeclarativeTextEdit::cut() |
1319 | { |
1320 | Q_D(QDeclarativeTextEdit); |
1321 | d->control->cut(); |
1322 | } |
1323 | |
1324 | /*! |
1325 | \qmlmethod TextEdit::copy() |
1326 | |
1327 | Copies the currently selected text to the system clipboard. |
1328 | */ |
1329 | void QDeclarativeTextEdit::copy() |
1330 | { |
1331 | Q_D(QDeclarativeTextEdit); |
1332 | d->control->copy(); |
1333 | } |
1334 | |
1335 | /*! |
1336 | \qmlmethod TextEdit::paste() |
1337 | |
1338 | Replaces the currently selected text by the contents of the system clipboard. |
1339 | */ |
1340 | void QDeclarativeTextEdit::paste() |
1341 | { |
1342 | Q_D(QDeclarativeTextEdit); |
1343 | d->control->paste(); |
1344 | } |
1345 | #endif // QT_NO_CLIPBOARD |
1346 | |
1347 | /*! |
1348 | \overload |
1349 | Handles the given mouse \a event. |
1350 | */ |
1351 | void QDeclarativeTextEdit::mousePressEvent(QGraphicsSceneMouseEvent *event) |
1352 | { |
1353 | Q_D(QDeclarativeTextEdit); |
1354 | if (d->focusOnPress){ |
1355 | bool hadActiveFocus = hasActiveFocus(); |
1356 | forceActiveFocus(); |
1357 | if (d->showInputPanelOnFocus) { |
1358 | if (hasActiveFocus() && hadActiveFocus && !isReadOnly()) { |
1359 | // re-open input panel on press if already focused |
1360 | openSoftwareInputPanel(); |
1361 | } |
1362 | } else { // show input panel on click |
1363 | if (hasActiveFocus() && !hadActiveFocus) { |
1364 | d->clickCausedFocus = true; |
1365 | } |
1366 | } |
1367 | } |
1368 | |
1369 | d->control->processEvent(event, QPointF(0, -d->yoff)); |
1370 | if (!event->isAccepted()) |
1371 | QDeclarativePaintedItem::mousePressEvent(event); |
1372 | } |
1373 | |
1374 | /*! |
1375 | \overload |
1376 | Handles the given mouse \a event. |
1377 | */ |
1378 | void QDeclarativeTextEdit::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) |
1379 | { |
1380 | Q_D(QDeclarativeTextEdit); |
1381 | d->control->processEvent(event, QPointF(0, -d->yoff)); |
1382 | if (!d->showInputPanelOnFocus) { // input panel on click |
1383 | if (d->focusOnPress && !isReadOnly() && boundingRect().contains(event->pos())) { |
1384 | if (QGraphicsView * view = qobject_cast<QGraphicsView*>(qApp->focusWidget())) { |
1385 | if (view->scene() && view->scene() == scene()) { |
1386 | qt_widget_private(view)->handleSoftwareInputPanel(event->button(), d->clickCausedFocus); |
1387 | } |
1388 | } |
1389 | } |
1390 | } |
1391 | d->clickCausedFocus = false; |
1392 | |
1393 | if (!event->isAccepted()) |
1394 | QDeclarativePaintedItem::mouseReleaseEvent(event); |
1395 | } |
1396 | |
1397 | /*! |
1398 | \overload |
1399 | Handles the given mouse \a event. |
1400 | */ |
1401 | void QDeclarativeTextEdit::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) |
1402 | { |
1403 | Q_D(QDeclarativeTextEdit); |
1404 | |
1405 | d->control->processEvent(event, QPointF(0, -d->yoff)); |
1406 | if (!event->isAccepted()) |
1407 | QDeclarativePaintedItem::mouseDoubleClickEvent(event); |
1408 | |
1409 | } |
1410 | |
1411 | /*! |
1412 | \overload |
1413 | Handles the given mouse \a event. |
1414 | */ |
1415 | void QDeclarativeTextEdit::mouseMoveEvent(QGraphicsSceneMouseEvent *event) |
1416 | { |
1417 | Q_D(QDeclarativeTextEdit); |
1418 | d->control->processEvent(event, QPointF(0, -d->yoff)); |
1419 | if (!event->isAccepted()) |
1420 | QDeclarativePaintedItem::mouseMoveEvent(event); |
1421 | } |
1422 | |
1423 | /*! |
1424 | \overload |
1425 | Handles the given input method \a event. |
1426 | */ |
1427 | void QDeclarativeTextEdit::inputMethodEvent(QInputMethodEvent *event) |
1428 | { |
1429 | Q_D(QDeclarativeTextEdit); |
1430 | const bool wasComposing = isInputMethodComposing(); |
1431 | d->control->processEvent(event, QPointF(0, -d->yoff)); |
1432 | if (wasComposing != isInputMethodComposing()) |
1433 | emit inputMethodComposingChanged(); |
1434 | } |
1435 | |
1436 | /*! |
1437 | \overload |
1438 | Returns the value of the given \a property. |
1439 | */ |
1440 | QVariant QDeclarativeTextEdit::inputMethodQuery(Qt::InputMethodQuery property) const |
1441 | { |
1442 | Q_D(const QDeclarativeTextEdit); |
1443 | return d->control->inputMethodQuery(property); |
1444 | } |
1445 | |
1446 | /*! |
1447 | Draws the contents of the text edit using the given \a painter within |
1448 | the given \a bounds. |
1449 | */ |
1450 | void QDeclarativeTextEdit::drawContents(QPainter *painter, const QRect &bounds) |
1451 | { |
1452 | Q_D(QDeclarativeTextEdit); |
1453 | |
1454 | painter->setRenderHint(QPainter::TextAntialiasing, true); |
1455 | painter->translate(0,d->yoff); |
1456 | |
1457 | d->control->drawContents(painter, bounds.translated(0,-d->yoff)); |
1458 | |
1459 | painter->translate(0,-d->yoff); |
1460 | } |
1461 | |
1462 | void QDeclarativeTextEdit::updateImgCache(const QRectF &rf) |
1463 | { |
1464 | Q_D(const QDeclarativeTextEdit); |
1465 | QRect r; |
1466 | if (!rf.isValid()) { |
1467 | r = QRect(0,0,INT_MAX,INT_MAX); |
1468 | } else { |
1469 | r = rf.toRect(); |
1470 | if (r.height() > INT_MAX/2) { |
1471 | // Take care of overflow when translating "everything" |
1472 | r.setTop(r.y() + d->yoff); |
1473 | r.setBottom(INT_MAX/2); |
1474 | } else { |
1475 | r = r.translated(0,d->yoff); |
1476 | } |
1477 | } |
1478 | dirtyCache(r); |
1479 | emit update(); |
1480 | } |
1481 | |
1482 | /*! |
1483 | \qmlproperty bool TextEdit::smooth |
1484 | |
1485 | This property holds whether the text is smoothly scaled or transformed. |
1486 | |
1487 | Smooth filtering gives better visual quality, but is slower. If |
1488 | the item is displayed at its natural size, this property has no visual or |
1489 | performance effect. |
1490 | |
1491 | \note Generally scaling artifacts are only visible if the item is stationary on |
1492 | the screen. A common pattern when animating an item is to disable smooth |
1493 | filtering at the beginning of the animation and reenable it at the conclusion. |
1494 | */ |
1495 | |
1496 | /*! |
1497 | \qmlproperty bool TextEdit::canPaste |
1498 | \since QtQuick 1.1 |
1499 | |
1500 | Returns true if the TextEdit is writable and the content of the clipboard is |
1501 | suitable for pasting into the TextEdit. |
1502 | */ |
1503 | bool QDeclarativeTextEdit::canPaste() const |
1504 | { |
1505 | Q_D(const QDeclarativeTextEdit); |
1506 | return d->canPaste; |
1507 | } |
1508 | |
1509 | /*! |
1510 | \qmlproperty bool TextEdit::inputMethodComposing |
1511 | |
1512 | \since QtQuick 1.1 |
1513 | |
1514 | This property holds whether the TextEdit has partial text input from an |
1515 | input method. |
1516 | |
1517 | While it is composing an input method may rely on mouse or key events from |
1518 | the TextEdit to edit or commit the partial text. This property can be used |
1519 | to determine when to disable events handlers that may interfere with the |
1520 | correct operation of an input method. |
1521 | */ |
1522 | bool QDeclarativeTextEdit::isInputMethodComposing() const |
1523 | { |
1524 | Q_D(const QDeclarativeTextEdit); |
1525 | if (QTextLayout *layout = d->control->textCursor().block().layout()) |
1526 | return layout->preeditAreaText().length() > 0; |
1527 | return false; |
1528 | } |
1529 | |
1530 | void QDeclarativeTextEditPrivate::init() |
1531 | { |
1532 | Q_Q(QDeclarativeTextEdit); |
1533 | |
1534 | q->setSmooth(smooth); |
1535 | q->setAcceptedMouseButtons(Qt::LeftButton); |
1536 | q->setFlag(QGraphicsItem::ItemHasNoContents, false); |
1537 | q->setFlag(QGraphicsItem::ItemAcceptsInputMethod); |
1538 | |
1539 | control = new QTextControl(q); |
1540 | control->setIgnoreUnusedNavigationEvents(true); |
1541 | control->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::TextSelectableByKeyboard | Qt::TextEditable); |
1542 | control->setDragEnabled(false); |
1543 | |
1544 | // QTextControl follows the default text color |
1545 | // defined by the platform, declarative text |
1546 | // should be black by default |
1547 | QPalette pal = control->palette(); |
1548 | if (pal.color(QPalette::Text) != color) { |
1549 | pal.setColor(QPalette::Text, color); |
1550 | control->setPalette(pal); |
1551 | } |
1552 | |
1553 | QObject::connect(control, SIGNAL(updateRequest(QRectF)), q, SLOT(updateImgCache(QRectF))); |
1554 | |
1555 | QObject::connect(control, SIGNAL(textChanged()), q, SLOT(q_textChanged())); |
1556 | QObject::connect(control, SIGNAL(selectionChanged()), q, SIGNAL(selectionChanged())); |
1557 | QObject::connect(control, SIGNAL(selectionChanged()), q, SLOT(updateSelectionMarkers())); |
1558 | QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SLOT(updateSelectionMarkers())); |
1559 | QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SIGNAL(cursorPositionChanged())); |
1560 | QObject::connect(control, SIGNAL(microFocusChanged()), q, SLOT(moveCursorDelegate())); |
1561 | QObject::connect(control, SIGNAL(linkActivated(QString)), q, SIGNAL(linkActivated(QString))); |
1562 | #ifndef QT_NO_CLIPBOARD |
1563 | QObject::connect(q, SIGNAL(readOnlyChanged(bool)), q, SLOT(q_canPasteChanged())); |
1564 | QObject::connect(QApplication::clipboard(), SIGNAL(dataChanged()), q, SLOT(q_canPasteChanged())); |
1565 | canPaste = control->canPaste(); |
1566 | #endif |
1567 | |
1568 | document = control->document(); |
1569 | document->setDefaultFont(font); |
1570 | document->setDocumentMargin(textMargin); |
1571 | document->setUndoRedoEnabled(false); // flush undo buffer. |
1572 | document->setUndoRedoEnabled(true); |
1573 | updateDefaultTextOption(); |
1574 | } |
1575 | |
1576 | void QDeclarativeTextEdit::q_textChanged() |
1577 | { |
1578 | Q_D(QDeclarativeTextEdit); |
1579 | d->text = text(); |
1580 | d->rightToLeftText = d->document->begin().layout()->engine()->isRightToLeft(); |
1581 | d->determineHorizontalAlignment(); |
1582 | d->updateDefaultTextOption(); |
1583 | updateSize(); |
1584 | updateTotalLines(); |
1585 | emit textChanged(d->text); |
1586 | } |
1587 | |
1588 | void QDeclarativeTextEdit::moveCursorDelegate() |
1589 | { |
1590 | Q_D(QDeclarativeTextEdit); |
1591 | d->determineHorizontalAlignment(); |
1592 | updateMicroFocus(); |
1593 | emit cursorRectangleChanged(); |
1594 | if(!d->cursor) |
1595 | return; |
1596 | QRectF cursorRect = cursorRectangle(); |
1597 | d->cursor->setX(cursorRect.x()); |
1598 | d->cursor->setY(cursorRect.y()); |
1599 | } |
1600 | |
1601 | void QDeclarativeTextEditPrivate::updateSelection() |
1602 | { |
1603 | Q_Q(QDeclarativeTextEdit); |
1604 | QTextCursor cursor = control->textCursor(); |
1605 | bool startChange = (lastSelectionStart != cursor.selectionStart()); |
1606 | bool endChange = (lastSelectionEnd != cursor.selectionEnd()); |
1607 | cursor.beginEditBlock(); |
1608 | cursor.setPosition(lastSelectionStart, QTextCursor::MoveAnchor); |
1609 | cursor.setPosition(lastSelectionEnd, QTextCursor::KeepAnchor); |
1610 | cursor.endEditBlock(); |
1611 | control->setTextCursor(cursor); |
1612 | if(startChange) |
1613 | q->selectionStartChanged(); |
1614 | if(endChange) |
1615 | q->selectionEndChanged(); |
1616 | } |
1617 | |
1618 | void QDeclarativeTextEdit::updateSelectionMarkers() |
1619 | { |
1620 | Q_D(QDeclarativeTextEdit); |
1621 | if(d->lastSelectionStart != d->control->textCursor().selectionStart()){ |
1622 | d->lastSelectionStart = d->control->textCursor().selectionStart(); |
1623 | emit selectionStartChanged(); |
1624 | } |
1625 | if(d->lastSelectionEnd != d->control->textCursor().selectionEnd()){ |
1626 | d->lastSelectionEnd = d->control->textCursor().selectionEnd(); |
1627 | emit selectionEndChanged(); |
1628 | } |
1629 | } |
1630 | |
1631 | QRectF QDeclarativeTextEdit::boundingRect() const |
1632 | { |
1633 | Q_D(const QDeclarativeTextEdit); |
1634 | QRectF r = QDeclarativePaintedItem::boundingRect(); |
1635 | int cursorWidth = 1; |
1636 | if(d->cursor) |
1637 | cursorWidth = d->cursor->width(); |
1638 | if(!d->document->isEmpty()) |
1639 | cursorWidth += 3;// ### Need a better way of accounting for space between char and cursor |
1640 | |
1641 | // Could include font max left/right bearings to either side of rectangle. |
1642 | |
1643 | r.setRight(r.right() + cursorWidth); |
1644 | return r.translated(0,d->yoff); |
1645 | } |
1646 | |
1647 | qreal QDeclarativeTextEditPrivate::implicitWidth() const |
1648 | { |
1649 | Q_Q(const QDeclarativeTextEdit); |
1650 | if (!requireImplicitWidth) { |
1651 | // We don't calculate implicitWidth unless it is required. |
1652 | // We need to force a size update now to ensure implicitWidth is calculated |
1653 | const_cast<QDeclarativeTextEditPrivate*>(this)->requireImplicitWidth = true; |
1654 | const_cast<QDeclarativeTextEdit*>(q)->updateSize(); |
1655 | } |
1656 | return mImplicitWidth; |
1657 | } |
1658 | |
1659 | //### we should perhaps be a bit smarter here -- depending on what has changed, we shouldn't |
1660 | // need to do all the calculations each time |
1661 | void QDeclarativeTextEdit::updateSize() |
1662 | { |
1663 | Q_D(QDeclarativeTextEdit); |
1664 | if (isComponentComplete()) { |
1665 | qreal naturalWidth = d->mImplicitWidth; |
1666 | // ### assumes that if the width is set, the text will fill to edges |
1667 | // ### (unless wrap is false, then clipping will occur) |
1668 | if (widthValid()) { |
1669 | if (!d->requireImplicitWidth) { |
1670 | emit implicitWidthChanged(); |
1671 | // if the implicitWidth is used, then updateSize() has already been called (recursively) |
1672 | if (d->requireImplicitWidth) |
1673 | return; |
1674 | } |
1675 | if (d->requireImplicitWidth) { |
1676 | d->document->setTextWidth(-1); |
1677 | naturalWidth = d->document->idealWidth(); |
1678 | } |
1679 | if (d->document->textWidth() != width()) |
1680 | d->document->setTextWidth(width()); |
1681 | } else { |
1682 | d->document->setTextWidth(-1); |
1683 | } |
1684 | QFontMetrics fm = QFontMetrics(d->font); |
1685 | int dy = height(); |
1686 | dy -= (int)d->document->size().height(); |
1687 | |
1688 | int nyoff; |
1689 | if (heightValid()) { |
1690 | if (d->vAlign == AlignBottom) |
1691 | nyoff = dy; |
1692 | else if (d->vAlign == AlignVCenter) |
1693 | nyoff = dy/2; |
1694 | else |
1695 | nyoff = 0; |
1696 | } else { |
1697 | nyoff = 0; |
1698 | } |
1699 | if (nyoff != d->yoff) { |
1700 | prepareGeometryChange(); |
1701 | d->yoff = nyoff; |
1702 | } |
1703 | setBaselineOffset(fm.ascent() + d->yoff + d->textMargin); |
1704 | |
1705 | //### need to comfirm cost of always setting these |
1706 | int newWidth = qCeil(d->document->idealWidth()); |
1707 | if (!widthValid() && d->document->textWidth() != newWidth) |
1708 | d->document->setTextWidth(newWidth); // ### Text does not align if width is not set (QTextDoc bug) |
1709 | // ### Setting the implicitWidth triggers another updateSize(), and unless there are bindings nothing has changed. |
1710 | if (!widthValid()) |
1711 | setImplicitWidth(newWidth); |
1712 | else if (d->requireImplicitWidth) |
1713 | setImplicitWidth(naturalWidth); |
1714 | qreal newHeight = d->document->size().height(); |
1715 | if (newHeight == 0) |
1716 | newHeight = fm.height(); |
1717 | setImplicitHeight(newHeight); |
1718 | |
1719 | d->paintedSize = QSize(newWidth, newHeight); |
1720 | setContentsSize(d->paintedSize); |
1721 | emit paintedSizeChanged(); |
1722 | } else { |
1723 | d->dirty = true; |
1724 | } |
1725 | emit update(); |
1726 | } |
1727 | |
1728 | void QDeclarativeTextEdit::updateTotalLines() |
1729 | { |
1730 | Q_D(QDeclarativeTextEdit); |
1731 | |
1732 | int subLines = 0; |
1733 | |
1734 | for (QTextBlock it = d->document->begin(); it != d->document->end(); it = it.next()) { |
1735 | QTextLayout *layout = it.layout(); |
1736 | if (!layout) |
1737 | continue; |
1738 | subLines += layout->lineCount()-1; |
1739 | } |
1740 | |
1741 | int newTotalLines = d->document->lineCount() + subLines; |
1742 | if (d->lineCount != newTotalLines) { |
1743 | d->lineCount = newTotalLines; |
1744 | emit lineCountChanged(); |
1745 | } |
1746 | } |
1747 | |
1748 | void QDeclarativeTextEditPrivate::updateDefaultTextOption() |
1749 | { |
1750 | Q_Q(QDeclarativeTextEdit); |
1751 | QTextOption opt = document->defaultTextOption(); |
1752 | int oldAlignment = opt.alignment(); |
1753 | |
1754 | QDeclarativeTextEdit::HAlignment horizontalAlignment = q->effectiveHAlign(); |
1755 | if (rightToLeftText) { |
1756 | if (horizontalAlignment == QDeclarativeTextEdit::AlignLeft) |
1757 | horizontalAlignment = QDeclarativeTextEdit::AlignRight; |
1758 | else if (horizontalAlignment == QDeclarativeTextEdit::AlignRight) |
1759 | horizontalAlignment = QDeclarativeTextEdit::AlignLeft; |
1760 | } |
1761 | opt.setAlignment((Qt::Alignment)(int)(horizontalAlignment | vAlign)); |
1762 | |
1763 | QTextOption::WrapMode oldWrapMode = opt.wrapMode(); |
1764 | opt.setWrapMode(QTextOption::WrapMode(wrapMode)); |
1765 | |
1766 | if (oldWrapMode == opt.wrapMode() && oldAlignment == opt.alignment()) |
1767 | return; |
1768 | document->setDefaultTextOption(opt); |
1769 | } |
1770 | |
1771 | |
1772 | /*! |
1773 | \qmlmethod void TextEdit::openSoftwareInputPanel() |
1774 | |
1775 | Opens software input panels like virtual keyboards for typing, useful for |
1776 | customizing when you want the input keyboard to be shown and hidden in |
1777 | your application. |
1778 | |
1779 | By default the opening of input panels follows the platform style. On Symbian^1 and |
1780 | Symbian^3 -based devices the panels are opened by clicking TextEdit. On other platforms |
1781 | the panels are automatically opened when TextEdit element gains active focus. Input panels are |
1782 | always closed if no editor has active focus. |
1783 | |
1784 | You can disable the automatic behavior by setting the property \c activeFocusOnPress to false |
1785 | and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement |
1786 | the behavior you want. |
1787 | |
1788 | Only relevant on platforms, which provide virtual keyboards. |
1789 | |
1790 | \code |
1791 | import QtQuick 1.0 |
1792 | TextEdit { |
1793 | id: textEdit |
1794 | text: "Hello world!" |
1795 | activeFocusOnPress: false |
1796 | MouseArea { |
1797 | anchors.fill: parent |
1798 | onClicked: { |
1799 | if (!textEdit.activeFocus) { |
1800 | textEdit.forceActiveFocus(); |
1801 | textEdit.openSoftwareInputPanel(); |
1802 | } else { |
1803 | textEdit.focus = false; |
1804 | } |
1805 | } |
1806 | onPressAndHold: textEdit.closeSoftwareInputPanel(); |
1807 | } |
1808 | } |
1809 | \endcode |
1810 | */ |
1811 | void QDeclarativeTextEdit::openSoftwareInputPanel() |
1812 | { |
1813 | QEvent event(QEvent::RequestSoftwareInputPanel); |
1814 | if (qApp) { |
1815 | if (QGraphicsView * view = qobject_cast<QGraphicsView*>(qApp->focusWidget())) { |
1816 | if (view->scene() && view->scene() == scene()) { |
1817 | QApplication::sendEvent(view, &event); |
1818 | } |
1819 | } |
1820 | } |
1821 | } |
1822 | |
1823 | /*! |
1824 | \qmlmethod void TextEdit::closeSoftwareInputPanel() |
1825 | |
1826 | Closes a software input panel like a virtual keyboard shown on the screen, useful |
1827 | for customizing when you want the input keyboard to be shown and hidden in |
1828 | your application. |
1829 | |
1830 | By default the opening of input panels follows the platform style. On Symbian^1 and |
1831 | Symbian^3 -based devices the panels are opened by clicking TextEdit. On other platforms |
1832 | the panels are automatically opened when TextEdit element gains active focus. Input panels are |
1833 | always closed if no editor has active focus. |
1834 | |
1835 | You can disable the automatic behavior by setting the property \c activeFocusOnPress to false |
1836 | and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement |
1837 | the behavior you want. |
1838 | |
1839 | Only relevant on platforms, which provide virtual keyboards. |
1840 | |
1841 | \code |
1842 | import QtQuick 1.0 |
1843 | TextEdit { |
1844 | id: textEdit |
1845 | text: "Hello world!" |
1846 | activeFocusOnPress: false |
1847 | MouseArea { |
1848 | anchors.fill: parent |
1849 | onClicked: { |
1850 | if (!textEdit.activeFocus) { |
1851 | textEdit.forceActiveFocus(); |
1852 | textEdit.openSoftwareInputPanel(); |
1853 | } else { |
1854 | textEdit.focus = false; |
1855 | } |
1856 | } |
1857 | onPressAndHold: textEdit.closeSoftwareInputPanel(); |
1858 | } |
1859 | } |
1860 | \endcode |
1861 | */ |
1862 | void QDeclarativeTextEdit::closeSoftwareInputPanel() |
1863 | { |
1864 | QEvent event(QEvent::CloseSoftwareInputPanel); |
1865 | if (qApp) { |
1866 | if (QGraphicsView * view = qobject_cast<QGraphicsView*>(qApp->focusWidget())) { |
1867 | if (view->scene() && view->scene() == scene()) { |
1868 | QApplication::sendEvent(view, &event); |
1869 | } |
1870 | } |
1871 | } |
1872 | } |
1873 | |
1874 | void QDeclarativeTextEdit::focusInEvent(QFocusEvent *event) |
1875 | { |
1876 | Q_D(const QDeclarativeTextEdit); |
1877 | if (d->showInputPanelOnFocus) { |
1878 | if (d->focusOnPress && !isReadOnly()) { |
1879 | openSoftwareInputPanel(); |
1880 | } |
1881 | } |
1882 | QDeclarativePaintedItem::focusInEvent(event); |
1883 | } |
1884 | |
1885 | void QDeclarativeTextEdit::q_canPasteChanged() |
1886 | { |
1887 | Q_D(QDeclarativeTextEdit); |
1888 | bool old = d->canPaste; |
1889 | d->canPaste = d->control->canPaste(); |
1890 | if(old!=d->canPaste) |
1891 | emit canPasteChanged(); |
1892 | } |
1893 | |
1894 | QT_END_NAMESPACE |
1895 | |