From 2dad73143d07fcb0ee9b94ade03442d03bf4bdbb Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 18 Aug 2015 22:37:57 +0300 Subject: [PATCH] InputField done by QTextEdit --- Telegram/Resources/style.txt | 6 +- Telegram/SourceFiles/gui/flatinput.cpp | 186 ++++++++++++++++--------- Telegram/SourceFiles/gui/flatinput.h | 165 ++++++---------------- 3 files changed, 164 insertions(+), 193 deletions(-) diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index d8c0f33f0..62e6cccc9 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -66,9 +66,9 @@ defaultInputField: InputField { placeholderFg: #999; placeholderFgActive: #aaa; placeholderMargins: margins(2px, 0px, 2px, 0px); - placeholderAlign: align(left); + placeholderAlign: align(topleft); placeholderShift: 50px; - duration: 200; + duration: 100; borderFg: #e0e0e0; borderFgActive: #62c0f7; @@ -83,7 +83,7 @@ defaultInputField: InputField { height: 32px; } dialogsSearchField: InputField(defaultInputField) { - textMargins: margins(34px, 5px, 34px, 5px); + textMargins: margins(34px, 7px, 34px, 7px); iconSprite: sprite(227px, 21px, 24px, 24px); iconPosition: point(6px, 5px); diff --git a/Telegram/SourceFiles/gui/flatinput.cpp b/Telegram/SourceFiles/gui/flatinput.cpp index 79bb320ef..1d6f6e03f 100644 --- a/Telegram/SourceFiles/gui/flatinput.cpp +++ b/Telegram/SourceFiles/gui/flatinput.cpp @@ -42,7 +42,6 @@ namespace { } }; InputStyle _flatInputStyle; - InputStyle _inputFieldStyle; } FlatInput::FlatInput(QWidget *parent, const style::flatInput &st, const QString &pholder, const QString &v) : QLineEdit(v, parent), @@ -384,7 +383,8 @@ void CountryCodeInput::correctValue(QKeyEvent *e, const QString &was) { } } -InputField::InputField(QWidget *parent, const style::InputField &st, const QString &ph, const QString &val) : QTextEdit(val, parent), +InputField::InputField(QWidget *parent, const style::InputField &st, const QString &ph, const QString &val) : TWidget(parent), +_inner(this, val), _oldtext(val), _keyEvent(0), @@ -406,20 +406,22 @@ a_borderFg(st.borderFg->c), a_borderOpacityActive(0), _borderAnim(animFunc(this, &InputField::borderStep)), +_focused(false), _error(false), + _st(&st), _touchPress(false), _touchRightButton(false), _touchMove(false), _replacingEmojis(false) { - setAcceptRichText(false); + _inner.setAcceptRichText(false); resize(_st->width, _st->height); - setWordWrapMode(QTextOption::NoWrap); - setLineWrapMode(QTextEdit::NoWrap); + _inner.setWordWrapMode(QTextOption::NoWrap); + _inner.setLineWrapMode(QTextEdit::NoWrap); - setFont(_st->font->f); - setAlignment(cRtl() ? Qt::AlignRight : Qt::AlignLeft); + _inner.setFont(_st->font->f); + _inner.setAlignment(cRtl() ? Qt::AlignRight : Qt::AlignLeft); _placeholder = _st->font->m.elidedText(_placeholderFull, Qt::ElideRight, width() - _st->textMargins.left() - _st->textMargins.right() - _st->placeholderMargins.left() - _st->placeholderMargins.right() - 1); @@ -427,42 +429,39 @@ _replacingEmojis(false) { p.setColor(QPalette::Text, _st->textFg->c); setPalette(p); - setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + _inner.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + _inner.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setFrameStyle(QFrame::NoFrame | QFrame::Plain); - viewport()->setAutoFillBackground(false); + _inner.setFrameStyle(QFrame::NoFrame | QFrame::Plain); + _inner.viewport()->setAutoFillBackground(false); - setContentsMargins(0, 0, 0, 0); + _inner.setContentsMargins(0, 0, 0, 0); + _inner.document()->setDocumentMargin(0); - //switch (cScale()) { - //case dbisOneAndQuarter: _fakeMargin = 1; break; - //case dbisOneAndHalf: _fakeMargin = 2; break; - //case dbisTwo: _fakeMargin = 4; break; - //} - //setStyleSheet(qsl("QTextEdit { margin: %1px; }").arg(_fakeMargin)); - setStyleSheet(qsl("QTextEdit { padding: 2px 74px; }")); - - viewport()->setAttribute(Qt::WA_AcceptTouchEvents); + setAttribute(Qt::WA_AcceptTouchEvents); + _inner.viewport()->setAttribute(Qt::WA_AcceptTouchEvents); _touchTimer.setSingleShot(true); connect(&_touchTimer, SIGNAL(timeout()), this, SLOT(onTouchTimer())); - connect(document(), SIGNAL(contentsChange(int, int, int)), this, SLOT(onDocumentContentsChange(int, int, int))); - connect(document(), SIGNAL(contentsChanged()), this, SLOT(onDocumentContentsChanged())); - connect(this, SIGNAL(undoAvailable(bool)), this, SLOT(onUndoAvailable(bool))); - connect(this, SIGNAL(redoAvailable(bool)), this, SLOT(onRedoAvailable(bool))); - if (App::wnd()) connect(this, SIGNAL(selectionChanged()), App::wnd(), SLOT(updateGlobalMenu())); + connect(_inner.document(), SIGNAL(contentsChange(int, int, int)), this, SLOT(onDocumentContentsChange(int, int, int))); + connect(_inner.document(), SIGNAL(contentsChanged()), this, SLOT(onDocumentContentsChanged())); + connect(&_inner, SIGNAL(undoAvailable(bool)), this, SLOT(onUndoAvailable(bool))); + connect(&_inner, SIGNAL(redoAvailable(bool)), this, SLOT(onRedoAvailable(bool))); + if (App::wnd()) connect(&_inner, SIGNAL(selectionChanged()), App::wnd(), SLOT(updateGlobalMenu())); } void InputField::onTouchTimer() { _touchRightButton = true; } -bool InputField::viewportEvent(QEvent *e) { +InputField::InputFieldInner::InputFieldInner(InputField *parent, const QString &val) : QTextEdit(val, parent) { +} + +bool InputField::InputFieldInner::viewportEvent(QEvent *e) { if (e->type() == QEvent::TouchBegin || e->type() == QEvent::TouchUpdate || e->type() == QEvent::TouchEnd || e->type() == QEvent::TouchCancel) { QTouchEvent *ev = static_cast(e); if (ev->device()->type() == QTouchDevice::TouchScreen) { - touchEvent(ev); + qobject_cast(parentWidget())->touchEvent(ev); return QTextEdit::viewportEvent(e); } } @@ -508,16 +507,12 @@ void InputField::touchEvent(QTouchEvent *e) { } } -QRect InputField::getTextRect() const { - return rect().marginsRemoved(_st->textMargins + st::textRectMargins); -} - int32 InputField::fakeMargin() const { return _fakeMargin; } void InputField::paintEvent(QPaintEvent *e) { - Painter p(viewport()); + Painter p(this); QRect r(rect().intersected(e->rect())); p.fillRect(r, st::white->b); @@ -552,37 +547,57 @@ void InputField::paintEvent(QPaintEvent *e) { p.restore(); } - QTextEdit::paintEvent(e); + TWidget::paintEvent(e); } void InputField::focusInEvent(QFocusEvent *e) { + _inner.setFocus(); +} + +void InputField::mousePressEvent(QMouseEvent *e) { + _inner.setFocus(); +} + +void InputField::contextMenuEvent(QContextMenuEvent *e) { + _inner.contextMenuEvent(e); +} + +void InputField::InputFieldInner::focusInEvent(QFocusEvent *e) { + f()->focusInInner(); + QTextEdit::focusInEvent(e); + emit f()->focused(); +} + +void InputField::focusInInner() { if (!_focused) { _focused = true; - + a_placeholderFg.start(_st->placeholderFgActive->c); _placeholderFgAnim.start(); - + a_borderFg.start((_error ? _st->borderFgError : _st->borderFgActive)->c); a_borderOpacityActive.start(1); _borderAnim.start(); } - QTextEdit::focusInEvent(e); - emit focused(); } -void InputField::focusOutEvent(QFocusEvent *e) { +void InputField::InputFieldInner::focusOutEvent(QFocusEvent *e) { + f()->focusOutInner(); + QTextEdit::focusOutEvent(e); + emit f()->blurred(); +} + +void InputField::focusOutInner() { if (_focused) { _focused = false; - + a_placeholderFg.start(_st->placeholderFg->c); _placeholderFgAnim.start(); - + a_borderFg.start((_error ? _st->borderFgError : _st->borderFg)->c); a_borderOpacityActive.start(_error ? 1 : 0); _borderAnim.start(); } - QTextEdit::focusOutEvent(e); - emit blurred(); } QSize InputField::sizeHint() const { @@ -599,7 +614,7 @@ QString InputField::getText(int32 start, int32 end) const { if (start < 0) start = 0; bool full = (start == 0) && (end < 0); - QTextDocument *doc(document()); + QTextDocument *doc(_inner.document()); QTextBlock from = full ? doc->begin() : doc->findBlock(start), till = (end < 0) ? doc->end() : doc->findBlock(end); if (till.isValid()) till = till.next(); @@ -671,7 +686,7 @@ QString InputField::getText(int32 start, int32 end) const { } bool InputField::hasText() const { - QTextDocument *doc(document()); + QTextDocument *doc(_inner.document()); QTextBlock from = doc->begin(), till = doc->end(); if (from == till) return false; @@ -704,11 +719,11 @@ void InputField::insertEmoji(EmojiPtr emoji, QTextCursor c) { c.insertText(objectReplacement, imageFormat); } -QVariant InputField::loadResource(int type, const QUrl &name) { +QVariant InputField::InputFieldInner::loadResource(int type, const QUrl &name) { QString imageName = name.toDisplayString(); if (imageName.startsWith(qstr("emoji://e."))) { if (EmojiPtr emoji = emojiFromUrl(imageName)) { - return QVariant(App::emojiSingle(emoji, _st->font->height)); + return QVariant(App::emojiSingle(emoji, f()->_st->font->height)); } } return QVariant(); @@ -718,8 +733,11 @@ void InputField::processDocumentContentsChange(int position, int charsAdded) { int32 emojiPosition = 0, emojiLen = 0; const EmojiData *emoji = 0; - QTextDocument *doc(document()); + static QString space(' '); + QTextDocument *doc(_inner.document()); + QTextCursor c(_inner.textCursor()); + c.joinPreviousEditBlock(); while (true) { int32 start = position, end = position + charsAdded; QTextBlock from = doc->findBlock(start), till = doc->findBlock(end); @@ -738,6 +756,19 @@ void InputField::processDocumentContentsChange(int position, int charsAdded) { QString t(fragment.text()); const QChar *ch = t.constData(), *e = ch + t.size(); for (; ch != e; ++ch) { + // QTextBeginningOfFrame // QTextEndOfFrame + if (ch->unicode() == 0xfdd0 || ch->unicode() == 0xfdd1 || ch->unicode() == QChar::ParagraphSeparator || ch->unicode() == QChar::LineSeparator || ch->unicode() == '\n' || ch->unicode() == '\r') { + if (!_inner.document()->pageSize().isNull()) { + _inner.document()->setPageSize(QSizeF(0, 0)); + } + int32 nlPosition = fp + (ch - t.constData()); + QTextCursor c(doc->docHandle(), nlPosition); + c.setPosition(nlPosition + 1, QTextCursor::KeepAnchor); + c.insertText(space); + position = nlPosition + 1; + emoji = TwoSymbolEmoji; // just a flag + break; + } emoji = emojiFromText(ch, e, emojiLen); if (emoji) { emojiPosition = fp + (ch - t.constData()); @@ -748,10 +779,22 @@ void InputField::processDocumentContentsChange(int position, int charsAdded) { if (emoji) break; } if (emoji) break; + if (b.next() != doc->end()) { + int32 nlPosition = b.next().position() - 1; + QTextCursor c(doc->docHandle(), nlPosition); + c.setPosition(nlPosition + 1, QTextCursor::KeepAnchor); + c.insertText(space); + position = nlPosition + 1; + emoji = TwoSymbolEmoji; // just a flag + break; + } } - if (emoji) { - if (!document()->pageSize().isNull()) { - document()->setPageSize(QSizeF(0, 0)); + if (emoji == TwoSymbolEmoji) { // just skip + emoji = 0; + emojiPosition = 0; + } else if (emoji) { + if (!_inner.document()->pageSize().isNull()) { + _inner.document()->setPageSize(QSizeF(0, 0)); } QTextCursor c(doc->docHandle(), emojiPosition); @@ -780,12 +823,13 @@ void InputField::processDocumentContentsChange(int position, int charsAdded) { break; } } + c.endEditBlock(); } void InputField::onDocumentContentsChange(int position, int charsRemoved, int charsAdded) { if (_replacingEmojis) return; - if (document()->availableRedoSteps() > 0) return; + if (_inner.document()->availableRedoSteps() > 0) return; const int takeBack = 3; @@ -799,10 +843,10 @@ void InputField::onDocumentContentsChange(int position, int charsRemoved, int ch // _insertions.push_back(Insertion(position, charsAdded)); _replacingEmojis = true; - QSizeF s = document()->pageSize(); + QSizeF s = _inner.document()->pageSize(); processDocumentContentsChange(position, charsAdded); - if (document()->pageSize() != s) { - document()->setPageSize(s); + if (_inner.document()->pageSize() != s) { + _inner.document()->setPageSize(s); } _replacingEmojis = false; } @@ -811,11 +855,11 @@ void InputField::onDocumentContentsChanged() { if (_replacingEmojis) return; if (!_insertions.isEmpty()) { - if (document()->availableRedoSteps() > 0) { + if (_inner.document()->availableRedoSteps() > 0) { _insertions.clear(); } else { _replacingEmojis = true; - QSizeF s = document()->pageSize(); + QSizeF s = _inner.document()->pageSize(); do { Insertion i = _insertions.front(); @@ -825,8 +869,8 @@ void InputField::onDocumentContentsChanged() { } } while (!_insertions.isEmpty()); - if (document()->pageSize() != s) { - document()->setPageSize(s); + if (_inner.document()->pageSize() != s) { + _inner.document()->setPageSize(s); } _replacingEmojis = false; } @@ -911,12 +955,12 @@ void InputField::updatePlaceholder() { void InputField::correctValue(QKeyEvent *e, const QString &was) { } -QMimeData *InputField::createMimeDataFromSelection() const { +QMimeData *InputField::InputFieldInner::createMimeDataFromSelection() const { QMimeData *result = new QMimeData(); QTextCursor c(textCursor()); int32 start = c.selectionStart(), end = c.selectionEnd(); if (end > start) { - result->setText(getText(start, end)); + result->setText(f()->getText(start, end)); } return result; } @@ -925,10 +969,10 @@ void InputField::customUpDown(bool custom) { _customUpDown = custom; } -void InputField::keyPressEvent(QKeyEvent *e) { +void InputField::InputFieldInner::keyPressEvent(QKeyEvent *e) { bool shift = e->modifiers().testFlag(Qt::ShiftModifier); bool macmeta = (cPlatform() == dbipMac) && e->modifiers().testFlag(Qt::ControlModifier) && !e->modifiers().testFlag(Qt::MetaModifier) && !e->modifiers().testFlag(Qt::AltModifier); - bool ctrl = e->modifiers().testFlag(Qt::ControlModifier) || e->modifiers().testFlag(Qt::MetaModifier), ctrlGood = (ctrl && cCtrlEnter()) || (!ctrl && !shift && !cCtrlEnter()) || (ctrl && shift); + bool ctrl = e->modifiers().testFlag(Qt::ControlModifier) || e->modifiers().testFlag(Qt::MetaModifier), ctrlGood = true; bool enter = (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return); if (macmeta && e->key() == Qt::Key_Backspace) { @@ -937,17 +981,19 @@ void InputField::keyPressEvent(QKeyEvent *e) { tc.setPosition(start.position(), QTextCursor::KeepAnchor); tc.removeSelectedText(); } else if (enter && ctrlGood) { - emit submitted(ctrl && shift); + emit f()->submitted(ctrl && shift); } else if (e->key() == Qt::Key_Escape) { - emit cancelled(); + emit f()->cancelled(); } else if (e->key() == Qt::Key_Tab || (ctrl && e->key() == Qt::Key_Backtab)) { if (ctrl) { e->ignore(); } else { - emit tabbed(); + emit f()->tabbed(); } } else if (e->key() == Qt::Key_Search || e == QKeySequence::Find) { e->ignore(); + } else if (f()->_customUpDown && (e->key() == Qt::Key_Up || e->key() == Qt::Key_Down)) { + e->ignore(); } else { QTextCursor tc(textCursor()); if (enter && ctrl) { @@ -974,8 +1020,12 @@ void InputField::keyPressEvent(QKeyEvent *e) { } } +void InputField::InputFieldInner::paintEvent(QPaintEvent *e) { + return QTextEdit::paintEvent(e); +} + void InputField::resizeEvent(QResizeEvent *e) { _placeholder = _st->font->m.elidedText(_placeholderFull, Qt::ElideRight, width() - _st->textMargins.left() - _st->textMargins.right() - _st->placeholderMargins.left() - _st->placeholderMargins.right() - 1); - viewport()->setGeometry(rect().marginsRemoved(_st->textMargins)); - QTextEdit::resizeEvent(e); + _inner.setGeometry(rect().marginsRemoved(_st->textMargins)); + TWidget::resizeEvent(e); } diff --git a/Telegram/SourceFiles/gui/flatinput.h b/Telegram/SourceFiles/gui/flatinput.h index 871e45af5..62139e845 100644 --- a/Telegram/SourceFiles/gui/flatinput.h +++ b/Telegram/SourceFiles/gui/flatinput.h @@ -127,129 +127,24 @@ private: bool _nosignal; }; -// -//class InputField : public QTextEdit { -// Q_OBJECT -// -//public: -// -// InputField(QWidget *parent, const style::InputField &st, const QString &ph = QString(), const QString &val = QString()); -// -// bool event(QEvent *e); -// void touchEvent(QTouchEvent *e); -// void paintEvent(QPaintEvent *e); -// void focusInEvent(QFocusEvent *e); -// void focusOutEvent(QFocusEvent *e); -// void keyPressEvent(QKeyEvent *e); -// void resizeEvent(QResizeEvent *e); -// -// void setError(bool error); -// -// void updatePlaceholder(); -// -// QRect getTextRect() const; -// -// bool placeholderFgStep(float64 ms); -// bool placeholderShiftStep(float64 ms); -// bool borderStep(float64 ms); -// -// QSize sizeHint() const; -// QSize minimumSizeHint() const; -// -// void setCustomUpDown(bool customUpDown); -// -//public slots: -// -// void onTextChange(const QString &text); -// void onTextEdited(); -// -// void onTouchTimer(); -// -// void onDocumentContentsChange(int position, int charsRemoved, int charsAdded); -// void onDocumentContentsChanged(); -// -// void onUndoAvailable(bool avail); -// void onRedoAvailable(bool avail); -// -//signals: -// -// void changed(); -// void cancelled(); -// void accepted(); -// void focused(); -// void blurred(); -// -//protected: -// -// virtual void correctValue(QKeyEvent *e, const QString &was); -// -// void insertEmoji(EmojiPtr emoji, QTextCursor c); -// TWidget *tparent() { -// return qobject_cast(parentWidget()); -// } -// const TWidget *tparent() const { -// return qobject_cast(parentWidget()); -// } -// void enterEvent(QEvent *e) { -// TWidget *p(tparent()); -// if (p) p->leaveToChildEvent(e); -// return QTextEdit::enterEvent(e); -// } -// void leaveEvent(QEvent *e) { -// TWidget *p(tparent()); -// if (p) p->enterFromChildEvent(e); -// return QTextEdit::leaveEvent(e); -// } -// -// QVariant loadResource(int type, const QUrl &name); -// -//private: -// -// QString _lastText; -// QKeyEvent *_keyEvent; -// -// bool _customUpDown; -// -// QString _placeholder, _placeholderFull; -// bool _placeholderVisible; -// anim::ivalue a_placeholderLeft; -// anim::fvalue a_placeholderOpacity; -// anim::cvalue a_placeholderFg; -// Animation _placeholderFgAnim, _placeholderShiftAnim; -// -// anim::fvalue a_borderOpacityActive; -// anim::cvalue a_borderFg; -// Animation _borderAnim; -// -// bool _focused, _error; -// -// const style::InputField *_st; -// -// QTimer _touchTimer; -// bool _touchPress, _touchRightButton, _touchMove; -// QPoint _touchStart; -//}; - -class InputField : public QTextEdit { +class InputField : public TWidget { Q_OBJECT public: InputField(QWidget *parent, const style::InputField &st, const QString &ph = QString(), const QString &val = QString()); - bool viewportEvent(QEvent *e); void touchEvent(QTouchEvent *e); void paintEvent(QPaintEvent *e); void focusInEvent(QFocusEvent *e); - void focusOutEvent(QFocusEvent *e); - void keyPressEvent(QKeyEvent *e); + void mousePressEvent(QMouseEvent *e); + void contextMenuEvent(QContextMenuEvent *e); void resizeEvent(QResizeEvent *e); const QString &getLastText() const; void updatePlaceholder(); - QRect getTextRect() const; int32 fakeMargin() const; bool placeholderFgStep(float64 ms); @@ -265,10 +160,22 @@ public: bool isUndoAvailable() const; bool isRedoAvailable() const; - QMimeData *createMimeDataFromSelection() const; - void customUpDown(bool isCustom); + void setTextCursor(const QTextCursor &cursor) { + return _inner.setTextCursor(cursor); + } + QTextCursor textCursor() const { + return _inner.textCursor(); + } + void setText(const QString &text) { + return _inner.setText(text); + } + void clear() { + return _inner.clear(); + } + + public slots: void onTouchTimer(); @@ -300,21 +207,35 @@ protected: const TWidget *tparent() const { return qobject_cast(parentWidget()); } - void enterEvent(QEvent *e) { - TWidget *p(tparent()); - if (p) p->leaveToChildEvent(e); - return QTextEdit::enterEvent(e); - } - void leaveEvent(QEvent *e) { - TWidget *p(tparent()); - if (p) p->enterFromChildEvent(e); - return QTextEdit::leaveEvent(e); - } - - QVariant loadResource(int type, const QUrl &name); private: + friend class InputFieldInner; + class InputFieldInner : public QTextEdit { + public: + InputFieldInner(InputField *parent, const QString &val = QString()); + + bool viewportEvent(QEvent *e); + void focusInEvent(QFocusEvent *e); + void focusOutEvent(QFocusEvent *e); + void keyPressEvent(QKeyEvent *e); + void paintEvent(QPaintEvent *e); + + QMimeData *createMimeDataFromSelection() const; + + QVariant loadResource(int type, const QUrl &name); + + private: + + InputField *f() const { + return static_cast(parentWidget()); + } + friend class InputField; + }; + InputFieldInner _inner; + void focusInInner(); + void focusOutInner(); + void processDocumentContentsChange(int position, int charsAdded); QString _oldtext;