Moved out MaskedInputField to separate module.
This commit is contained in:
parent
2c5923dc79
commit
faa67d73c5
9 changed files with 726 additions and 662 deletions
|
|
@ -202,6 +202,8 @@ PRIVATE
|
|||
ui/widgets/input_fields.h
|
||||
ui/widgets/labels.cpp
|
||||
ui/widgets/labels.h
|
||||
ui/widgets/fields/masked_input_field.cpp
|
||||
ui/widgets/fields/masked_input_field.h
|
||||
ui/widgets/fields/number_input.cpp
|
||||
ui/widgets/fields/number_input.h
|
||||
ui/widgets/fields/password_input.cpp
|
||||
|
|
|
|||
546
ui/widgets/fields/masked_input_field.cpp
Normal file
546
ui/widgets/fields/masked_input_field.cpp
Normal file
|
|
@ -0,0 +1,546 @@
|
|||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#include "ui/widgets/fields/masked_input_field.h"
|
||||
|
||||
#include "base/qt/qt_common_adapters.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "styles/palette.h"
|
||||
#include "styles/style_widgets.h"
|
||||
|
||||
#include <QtWidgets/QCommonStyle>
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtGui/QClipboard>
|
||||
|
||||
namespace Ui {
|
||||
namespace {
|
||||
|
||||
class InputStyle final : public QCommonStyle {
|
||||
public:
|
||||
InputStyle() {
|
||||
setParent(QCoreApplication::instance());
|
||||
}
|
||||
|
||||
void drawPrimitive(
|
||||
PrimitiveElement element,
|
||||
const QStyleOption *option,
|
||||
QPainter *painter,
|
||||
const QWidget *widget = nullptr) const override {
|
||||
}
|
||||
|
||||
static InputStyle *instance() {
|
||||
if (!_instance) {
|
||||
if (!QGuiApplication::instance()) {
|
||||
return nullptr;
|
||||
}
|
||||
_instance = new InputStyle();
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
|
||||
~InputStyle() {
|
||||
_instance = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
static InputStyle *_instance;
|
||||
|
||||
};
|
||||
|
||||
InputStyle *InputStyle::_instance = nullptr;
|
||||
|
||||
} // namespace
|
||||
|
||||
MaskedInputField::MaskedInputField(
|
||||
QWidget *parent,
|
||||
const style::InputField &st,
|
||||
rpl::producer<QString> placeholder,
|
||||
const QString &val)
|
||||
: Parent(val, parent)
|
||||
, _st(st)
|
||||
, _oldtext(val)
|
||||
, _placeholderFull(std::move(placeholder)) {
|
||||
resize(_st.width, _st.heightMin);
|
||||
|
||||
setFont(_st.font);
|
||||
setAlignment(_st.textAlign);
|
||||
|
||||
_placeholderFull.value(
|
||||
) | rpl::start_with_next([=](const QString &text) {
|
||||
refreshPlaceholder(text);
|
||||
}, lifetime());
|
||||
|
||||
style::PaletteChanged(
|
||||
) | rpl::start_with_next([=] {
|
||||
updatePalette();
|
||||
}, lifetime());
|
||||
updatePalette();
|
||||
|
||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
|
||||
connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(onTextChange(const QString&)));
|
||||
connect(this, SIGNAL(cursorPositionChanged(int,int)), this, SLOT(onCursorPositionChanged(int,int)));
|
||||
|
||||
connect(this, SIGNAL(textEdited(const QString&)), this, SLOT(onTextEdited()));
|
||||
connect(this, &MaskedInputField::selectionChanged, [] {
|
||||
Integration::Instance().textActionsUpdated();
|
||||
});
|
||||
|
||||
setStyle(InputStyle::instance());
|
||||
QLineEdit::setTextMargins(0, 0, 0, 0);
|
||||
setContentsMargins(_textMargins + QMargins(-2, -1, -2, -1));
|
||||
setFrame(false);
|
||||
|
||||
setAttribute(Qt::WA_AcceptTouchEvents);
|
||||
_touchTimer.setSingleShot(true);
|
||||
connect(&_touchTimer, SIGNAL(timeout()), this, SLOT(onTouchTimer()));
|
||||
|
||||
setTextMargins(_st.textMargins);
|
||||
|
||||
startPlaceholderAnimation();
|
||||
startBorderAnimation();
|
||||
finishAnimating();
|
||||
}
|
||||
|
||||
void MaskedInputField::updatePalette() {
|
||||
auto p = palette();
|
||||
p.setColor(QPalette::Text, _st.textFg->c);
|
||||
p.setColor(QPalette::Highlight, st::msgInBgSelected->c);
|
||||
p.setColor(QPalette::HighlightedText, st::historyTextInFgSelected->c);
|
||||
setPalette(p);
|
||||
}
|
||||
|
||||
void MaskedInputField::setCorrectedText(QString &now, int &nowCursor, const QString &newText, int newPos) {
|
||||
if (newPos < 0 || newPos > newText.size()) {
|
||||
newPos = newText.size();
|
||||
}
|
||||
auto updateText = (newText != now);
|
||||
if (updateText) {
|
||||
now = newText;
|
||||
setText(now);
|
||||
startPlaceholderAnimation();
|
||||
}
|
||||
auto updateCursorPosition = (newPos != nowCursor) || updateText;
|
||||
if (updateCursorPosition) {
|
||||
nowCursor = newPos;
|
||||
setCursorPosition(nowCursor);
|
||||
}
|
||||
}
|
||||
|
||||
void MaskedInputField::customUpDown(bool custom) {
|
||||
_customUpDown = custom;
|
||||
}
|
||||
|
||||
int MaskedInputField::borderAnimationStart() const {
|
||||
return _borderAnimationStart;
|
||||
}
|
||||
|
||||
void MaskedInputField::setTextMargins(const QMargins &mrg) {
|
||||
_textMargins = mrg;
|
||||
setContentsMargins(_textMargins + QMargins(-2, -1, -2, -1));
|
||||
refreshPlaceholder(_placeholderFull.current());
|
||||
}
|
||||
|
||||
void MaskedInputField::onTouchTimer() {
|
||||
_touchRightButton = true;
|
||||
}
|
||||
|
||||
bool MaskedInputField::eventHook(QEvent *e) {
|
||||
auto type = e->type();
|
||||
if (type == QEvent::TouchBegin
|
||||
|| type == QEvent::TouchUpdate
|
||||
|| type == QEvent::TouchEnd
|
||||
|| type == QEvent::TouchCancel) {
|
||||
auto event = static_cast<QTouchEvent*>(e);
|
||||
if (event->device()->type() == base::TouchDevice::TouchScreen) {
|
||||
touchEvent(event);
|
||||
}
|
||||
}
|
||||
return Parent::eventHook(e);
|
||||
}
|
||||
|
||||
void MaskedInputField::touchEvent(QTouchEvent *e) {
|
||||
switch (e->type()) {
|
||||
case QEvent::TouchBegin: {
|
||||
if (_touchPress || e->touchPoints().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
_touchTimer.start(QApplication::startDragTime());
|
||||
_touchPress = true;
|
||||
_touchMove = _touchRightButton = _mousePressedInTouch = false;
|
||||
_touchStart = e->touchPoints().cbegin()->screenPos().toPoint();
|
||||
} break;
|
||||
|
||||
case QEvent::TouchUpdate: {
|
||||
if (!e->touchPoints().isEmpty()) {
|
||||
touchUpdate(e->touchPoints().cbegin()->screenPos().toPoint());
|
||||
}
|
||||
} break;
|
||||
|
||||
case QEvent::TouchEnd: {
|
||||
touchFinish();
|
||||
} break;
|
||||
|
||||
case QEvent::TouchCancel: {
|
||||
_touchPress = false;
|
||||
_touchTimer.stop();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void MaskedInputField::touchUpdate(QPoint globalPosition) {
|
||||
if (_touchPress
|
||||
&& !_touchMove
|
||||
&& ((globalPosition - _touchStart).manhattanLength()
|
||||
>= QApplication::startDragDistance())) {
|
||||
_touchMove = true;
|
||||
}
|
||||
}
|
||||
|
||||
void MaskedInputField::touchFinish() {
|
||||
if (!_touchPress) {
|
||||
return;
|
||||
}
|
||||
const auto weak = MakeWeak(this);
|
||||
if (!_touchMove && window()) {
|
||||
QPoint mapped(mapFromGlobal(_touchStart));
|
||||
|
||||
if (_touchRightButton) {
|
||||
QContextMenuEvent contextEvent(
|
||||
QContextMenuEvent::Mouse,
|
||||
mapped,
|
||||
_touchStart);
|
||||
contextMenuEvent(&contextEvent);
|
||||
} else {
|
||||
QGuiApplication::inputMethod()->show();
|
||||
}
|
||||
}
|
||||
if (weak) {
|
||||
_touchTimer.stop();
|
||||
_touchPress
|
||||
= _touchMove
|
||||
= _touchRightButton
|
||||
= _mousePressedInTouch = false;
|
||||
}
|
||||
}
|
||||
|
||||
void MaskedInputField::paintEvent(QPaintEvent *e) {
|
||||
auto p = QPainter(this);
|
||||
|
||||
auto r = rect().intersected(e->rect());
|
||||
p.fillRect(r, _st.textBg);
|
||||
if (_st.border) {
|
||||
p.fillRect(0, height() - _st.border, width(), _st.border, _st.borderFg->b);
|
||||
}
|
||||
auto errorDegree = _a_error.value(_error ? 1. : 0.);
|
||||
auto focusedDegree = _a_focused.value(_focused ? 1. : 0.);
|
||||
auto borderShownDegree = _a_borderShown.value(1.);
|
||||
auto borderOpacity = _a_borderOpacity.value(_borderVisible ? 1. : 0.);
|
||||
if (_st.borderActive && (borderOpacity > 0.)) {
|
||||
auto borderStart = std::clamp(_borderAnimationStart, 0, width());
|
||||
auto borderFrom = qRound(borderStart * (1. - borderShownDegree));
|
||||
auto borderTo = borderStart + qRound((width() - borderStart) * borderShownDegree);
|
||||
if (borderTo > borderFrom) {
|
||||
auto borderFg = anim::brush(_st.borderFgActive, _st.borderFgError, errorDegree);
|
||||
p.setOpacity(borderOpacity);
|
||||
p.fillRect(borderFrom, height() - _st.borderActive, borderTo - borderFrom, _st.borderActive, borderFg);
|
||||
p.setOpacity(1);
|
||||
}
|
||||
}
|
||||
|
||||
p.setClipRect(r);
|
||||
if (_st.placeholderScale > 0. && !_placeholderPath.isEmpty()) {
|
||||
auto placeholderShiftDegree = _a_placeholderShifted.value(_placeholderShifted ? 1. : 0.);
|
||||
p.save();
|
||||
p.setClipRect(r);
|
||||
|
||||
auto placeholderTop = anim::interpolate(0, _st.placeholderShift, placeholderShiftDegree);
|
||||
|
||||
QRect r(rect().marginsRemoved(_textMargins + _st.placeholderMargins));
|
||||
r.moveTop(r.top() + placeholderTop);
|
||||
if (style::RightToLeft()) r.moveLeft(width() - r.left() - r.width());
|
||||
|
||||
auto placeholderScale = 1. - (1. - _st.placeholderScale) * placeholderShiftDegree;
|
||||
auto placeholderFg = anim::color(_st.placeholderFg, _st.placeholderFgActive, focusedDegree);
|
||||
placeholderFg = anim::color(placeholderFg, _st.placeholderFgError, errorDegree);
|
||||
|
||||
PainterHighQualityEnabler hq(p);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(placeholderFg);
|
||||
p.translate(r.topLeft());
|
||||
p.scale(placeholderScale, placeholderScale);
|
||||
p.drawPath(_placeholderPath);
|
||||
|
||||
p.restore();
|
||||
} else if (!_placeholder.isEmpty()) {
|
||||
auto placeholderHiddenDegree = _a_placeholderShifted.value(_placeholderShifted ? 1. : 0.);
|
||||
if (placeholderHiddenDegree < 1.) {
|
||||
p.setOpacity(1. - placeholderHiddenDegree);
|
||||
p.save();
|
||||
p.setClipRect(r);
|
||||
|
||||
auto placeholderLeft = anim::interpolate(0, -_st.placeholderShift, placeholderHiddenDegree);
|
||||
|
||||
QRect r(rect().marginsRemoved(_textMargins + _st.placeholderMargins));
|
||||
r.moveLeft(r.left() + placeholderLeft);
|
||||
if (style::RightToLeft()) r.moveLeft(width() - r.left() - r.width());
|
||||
|
||||
p.setFont(_st.placeholderFont);
|
||||
p.setPen(anim::pen(_st.placeholderFg, _st.placeholderFgActive, focusedDegree));
|
||||
p.drawText(r, _placeholder, _st.placeholderAlign);
|
||||
|
||||
p.restore();
|
||||
p.setOpacity(1.);
|
||||
}
|
||||
}
|
||||
|
||||
paintAdditionalPlaceholder(p);
|
||||
QLineEdit::paintEvent(e);
|
||||
}
|
||||
|
||||
void MaskedInputField::mousePressEvent(QMouseEvent *e) {
|
||||
if (_touchPress && e->button() == Qt::LeftButton) {
|
||||
_mousePressedInTouch = true;
|
||||
_touchStart = e->globalPos();
|
||||
}
|
||||
return QLineEdit::mousePressEvent(e);
|
||||
}
|
||||
|
||||
void MaskedInputField::mouseReleaseEvent(QMouseEvent *e) {
|
||||
if (_mousePressedInTouch) {
|
||||
touchFinish();
|
||||
}
|
||||
return QLineEdit::mouseReleaseEvent(e);
|
||||
}
|
||||
|
||||
void MaskedInputField::mouseMoveEvent(QMouseEvent *e) {
|
||||
if (_mousePressedInTouch) {
|
||||
touchUpdate(e->globalPos());
|
||||
}
|
||||
return QLineEdit::mouseMoveEvent(e);
|
||||
}
|
||||
|
||||
QString MaskedInputField::getDisplayedText() const {
|
||||
auto result = getLastText();
|
||||
if (!_lastPreEditText.isEmpty()) {
|
||||
result = result.mid(0, _oldcursor)
|
||||
+ _lastPreEditText
|
||||
+ result.mid(_oldcursor);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void MaskedInputField::startBorderAnimation() {
|
||||
auto borderVisible = (_error || _focused);
|
||||
if (_borderVisible != borderVisible) {
|
||||
_borderVisible = borderVisible;
|
||||
if (_borderVisible) {
|
||||
if (_a_borderOpacity.animating()) {
|
||||
_a_borderOpacity.start([this] { update(); }, 0., 1., _st.duration);
|
||||
} else {
|
||||
_a_borderShown.start([this] { update(); }, 0., 1., _st.duration);
|
||||
}
|
||||
} else if (qFuzzyCompare(_a_borderShown.value(1.), 0.)) {
|
||||
_a_borderShown.stop();
|
||||
_a_borderOpacity.stop();
|
||||
} else {
|
||||
_a_borderOpacity.start([this] { update(); }, 1., 0., _st.duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MaskedInputField::focusInEvent(QFocusEvent *e) {
|
||||
_borderAnimationStart = (e->reason() == Qt::MouseFocusReason) ? mapFromGlobal(QCursor::pos()).x() : (width() / 2);
|
||||
setFocused(true);
|
||||
QLineEdit::focusInEvent(e);
|
||||
focused();
|
||||
}
|
||||
|
||||
void MaskedInputField::focusOutEvent(QFocusEvent *e) {
|
||||
setFocused(false);
|
||||
QLineEdit::focusOutEvent(e);
|
||||
blurred();
|
||||
}
|
||||
|
||||
void MaskedInputField::setFocused(bool focused) {
|
||||
if (_focused != focused) {
|
||||
_focused = focused;
|
||||
_a_focused.start([this] { update(); }, _focused ? 0. : 1., _focused ? 1. : 0., _st.duration);
|
||||
startPlaceholderAnimation();
|
||||
startBorderAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
void MaskedInputField::resizeEvent(QResizeEvent *e) {
|
||||
refreshPlaceholder(_placeholderFull.current());
|
||||
_borderAnimationStart = width() / 2;
|
||||
QLineEdit::resizeEvent(e);
|
||||
}
|
||||
|
||||
void MaskedInputField::refreshPlaceholder(const QString &text) {
|
||||
const auto availableWidth = width() - _textMargins.left() - _textMargins.right() - _st.placeholderMargins.left() - _st.placeholderMargins.right() - 1;
|
||||
if (_st.placeholderScale > 0.) {
|
||||
auto placeholderFont = _st.placeholderFont->f;
|
||||
placeholderFont.setStyleStrategy(QFont::PreferMatch);
|
||||
const auto metrics = QFontMetrics(placeholderFont);
|
||||
_placeholder = metrics.elidedText(text, Qt::ElideRight, availableWidth);
|
||||
_placeholderPath = QPainterPath();
|
||||
if (!_placeholder.isEmpty()) {
|
||||
_placeholderPath.addText(0, QFontMetrics(placeholderFont).ascent(), placeholderFont, _placeholder);
|
||||
}
|
||||
} else {
|
||||
_placeholder = _st.placeholderFont->elided(text, availableWidth);
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void MaskedInputField::setPlaceholder(rpl::producer<QString> placeholder) {
|
||||
_placeholderFull = std::move(placeholder);
|
||||
}
|
||||
|
||||
void MaskedInputField::contextMenuEvent(QContextMenuEvent *e) {
|
||||
if (const auto menu = createStandardContextMenu()) {
|
||||
_contextMenu = base::make_unique_q<PopupMenu>(this, menu);
|
||||
_contextMenu->popup(e->globalPos());
|
||||
}
|
||||
}
|
||||
|
||||
void MaskedInputField::inputMethodEvent(QInputMethodEvent *e) {
|
||||
QLineEdit::inputMethodEvent(e);
|
||||
_lastPreEditText = e->preeditString();
|
||||
update();
|
||||
}
|
||||
|
||||
void MaskedInputField::showError() {
|
||||
showErrorNoFocus();
|
||||
if (!hasFocus()) {
|
||||
setFocus();
|
||||
}
|
||||
}
|
||||
|
||||
void MaskedInputField::showErrorNoFocus() {
|
||||
setErrorShown(true);
|
||||
}
|
||||
|
||||
void MaskedInputField::hideError() {
|
||||
setErrorShown(false);
|
||||
}
|
||||
|
||||
void MaskedInputField::setErrorShown(bool error) {
|
||||
if (_error != error) {
|
||||
_error = error;
|
||||
_a_error.start([this] { update(); }, _error ? 0. : 1., _error ? 1. : 0., _st.duration);
|
||||
startBorderAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
QSize MaskedInputField::sizeHint() const {
|
||||
return geometry().size();
|
||||
}
|
||||
|
||||
QSize MaskedInputField::minimumSizeHint() const {
|
||||
return geometry().size();
|
||||
}
|
||||
|
||||
void MaskedInputField::setDisplayFocused(bool focused) {
|
||||
setFocused(focused);
|
||||
finishAnimating();
|
||||
}
|
||||
|
||||
void MaskedInputField::finishAnimating() {
|
||||
_a_focused.stop();
|
||||
_a_error.stop();
|
||||
_a_placeholderShifted.stop();
|
||||
_a_borderShown.stop();
|
||||
_a_borderOpacity.stop();
|
||||
update();
|
||||
}
|
||||
|
||||
void MaskedInputField::setPlaceholderHidden(bool forcePlaceholderHidden) {
|
||||
_forcePlaceholderHidden = forcePlaceholderHidden;
|
||||
startPlaceholderAnimation();
|
||||
}
|
||||
|
||||
void MaskedInputField::startPlaceholderAnimation() {
|
||||
auto placeholderShifted = _forcePlaceholderHidden || (_focused && _st.placeholderScale > 0.) || !getLastText().isEmpty();
|
||||
if (_placeholderShifted != placeholderShifted) {
|
||||
_placeholderShifted = placeholderShifted;
|
||||
_a_placeholderShifted.start([this] { update(); }, _placeholderShifted ? 0. : 1., _placeholderShifted ? 1. : 0., _st.duration);
|
||||
}
|
||||
}
|
||||
|
||||
QRect MaskedInputField::placeholderRect() const {
|
||||
return rect().marginsRemoved(_textMargins + _st.placeholderMargins);
|
||||
}
|
||||
|
||||
style::font MaskedInputField::phFont() {
|
||||
return _st.font;
|
||||
}
|
||||
|
||||
void MaskedInputField::placeholderAdditionalPrepare(QPainter &p) {
|
||||
p.setFont(_st.font);
|
||||
p.setPen(_st.placeholderFg);
|
||||
}
|
||||
|
||||
void MaskedInputField::keyPressEvent(QKeyEvent *e) {
|
||||
QString wasText(_oldtext);
|
||||
int32 wasCursor(_oldcursor);
|
||||
|
||||
if (_customUpDown && (e->key() == Qt::Key_Up || e->key() == Qt::Key_Down || e->key() == Qt::Key_PageUp || e->key() == Qt::Key_PageDown)) {
|
||||
e->ignore();
|
||||
} else {
|
||||
QLineEdit::keyPressEvent(e);
|
||||
}
|
||||
|
||||
auto newText = text();
|
||||
auto newCursor = cursorPosition();
|
||||
if (wasText == newText && wasCursor == newCursor) { // call correct manually
|
||||
correctValue(wasText, wasCursor, newText, newCursor);
|
||||
_oldtext = newText;
|
||||
_oldcursor = newCursor;
|
||||
if (wasText != _oldtext) changed();
|
||||
startPlaceholderAnimation();
|
||||
}
|
||||
if (e->key() == Qt::Key_Escape) {
|
||||
e->ignore();
|
||||
cancelled();
|
||||
} else if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) {
|
||||
submitted(e->modifiers());
|
||||
#ifdef Q_OS_MAC
|
||||
} else if (e->key() == Qt::Key_E && e->modifiers().testFlag(Qt::ControlModifier)) {
|
||||
auto selected = selectedText();
|
||||
if (!selected.isEmpty() && echoMode() == QLineEdit::Normal) {
|
||||
QGuiApplication::clipboard()->setText(selected, QClipboard::FindBuffer);
|
||||
}
|
||||
#endif // Q_OS_MAC
|
||||
}
|
||||
}
|
||||
|
||||
void MaskedInputField::onTextEdited() {
|
||||
QString wasText(_oldtext), newText(text());
|
||||
int32 wasCursor(_oldcursor), newCursor(cursorPosition());
|
||||
|
||||
correctValue(wasText, wasCursor, newText, newCursor);
|
||||
_oldtext = newText;
|
||||
_oldcursor = newCursor;
|
||||
if (wasText != _oldtext) changed();
|
||||
startPlaceholderAnimation();
|
||||
|
||||
Integration::Instance().textActionsUpdated();
|
||||
}
|
||||
|
||||
void MaskedInputField::onTextChange(const QString &text) {
|
||||
_oldtext = QLineEdit::text();
|
||||
setErrorShown(false);
|
||||
Integration::Instance().textActionsUpdated();
|
||||
}
|
||||
|
||||
void MaskedInputField::onCursorPositionChanged(int oldPosition, int position) {
|
||||
_oldcursor = position;
|
||||
}
|
||||
|
||||
} // namespace Ui
|
||||
174
ui/widgets/fields/masked_input_field.h
Normal file
174
ui/widgets/fields/masked_input_field.h
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
// This file is part of Desktop App Toolkit,
|
||||
// a set of libraries for developing nice desktop applications.
|
||||
//
|
||||
// For license and copyright information please follow this link:
|
||||
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "ui/rp_widget.h"
|
||||
#include "ui/effects/animations.h"
|
||||
|
||||
#include <QtWidgets/QLineEdit>
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
namespace style {
|
||||
struct InputField;
|
||||
} // namespace style
|
||||
|
||||
namespace Ui {
|
||||
|
||||
class PopupMenu;
|
||||
|
||||
class MaskedInputField : public RpWidgetBase<QLineEdit> {
|
||||
Q_OBJECT
|
||||
|
||||
using Parent = RpWidgetBase<QLineEdit>;
|
||||
public:
|
||||
MaskedInputField(
|
||||
QWidget *parent,
|
||||
const style::InputField &st,
|
||||
rpl::producer<QString> placeholder = nullptr,
|
||||
const QString &val = QString());
|
||||
|
||||
void showError();
|
||||
void showErrorNoFocus();
|
||||
void hideError();
|
||||
|
||||
QSize sizeHint() const override;
|
||||
QSize minimumSizeHint() const override;
|
||||
|
||||
void customUpDown(bool isCustom);
|
||||
int borderAnimationStart() const;
|
||||
|
||||
const QString &getLastText() const {
|
||||
return _oldtext;
|
||||
}
|
||||
void setPlaceholder(rpl::producer<QString> placeholder);
|
||||
void setPlaceholderHidden(bool forcePlaceholderHidden);
|
||||
void setDisplayFocused(bool focused);
|
||||
void finishAnimating();
|
||||
void setFocusFast() {
|
||||
setDisplayFocused(true);
|
||||
setFocus();
|
||||
}
|
||||
|
||||
void setText(const QString &text) {
|
||||
QLineEdit::setText(text);
|
||||
startPlaceholderAnimation();
|
||||
}
|
||||
void clear() {
|
||||
QLineEdit::clear();
|
||||
startPlaceholderAnimation();
|
||||
}
|
||||
|
||||
public Q_SLOTS:
|
||||
void onTextChange(const QString &text);
|
||||
void onCursorPositionChanged(int oldPosition, int position);
|
||||
|
||||
void onTextEdited();
|
||||
|
||||
void onTouchTimer();
|
||||
|
||||
Q_SIGNALS:
|
||||
void changed();
|
||||
void cancelled();
|
||||
void submitted(Qt::KeyboardModifiers);
|
||||
void focused();
|
||||
void blurred();
|
||||
|
||||
protected:
|
||||
QString getDisplayedText() const;
|
||||
void startBorderAnimation();
|
||||
void startPlaceholderAnimation();
|
||||
|
||||
bool eventHook(QEvent *e) override;
|
||||
void touchEvent(QTouchEvent *e);
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
void focusInEvent(QFocusEvent *e) override;
|
||||
void focusOutEvent(QFocusEvent *e) override;
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
void contextMenuEvent(QContextMenuEvent *e) override;
|
||||
void inputMethodEvent(QInputMethodEvent *e) override;
|
||||
|
||||
void mousePressEvent(QMouseEvent *e) override;
|
||||
void mouseReleaseEvent(QMouseEvent *e) override;
|
||||
void mouseMoveEvent(QMouseEvent *e) override;
|
||||
|
||||
virtual void correctValue(
|
||||
const QString &was,
|
||||
int wasCursor,
|
||||
QString &now,
|
||||
int &nowCursor) {
|
||||
}
|
||||
void setCorrectedText(
|
||||
QString &now,
|
||||
int &nowCursor,
|
||||
const QString &newText,
|
||||
int newPos);
|
||||
|
||||
virtual void paintAdditionalPlaceholder(QPainter &p) {
|
||||
}
|
||||
|
||||
style::font phFont();
|
||||
|
||||
void placeholderAdditionalPrepare(QPainter &p);
|
||||
QRect placeholderRect() const;
|
||||
|
||||
void setTextMargins(const QMargins &mrg);
|
||||
const style::InputField &_st;
|
||||
|
||||
private:
|
||||
void updatePalette();
|
||||
void refreshPlaceholder(const QString &text);
|
||||
void setErrorShown(bool error);
|
||||
|
||||
void touchUpdate(QPoint globalPosition);
|
||||
void touchFinish();
|
||||
|
||||
void setFocused(bool focused);
|
||||
|
||||
int _maxLength = -1;
|
||||
bool _forcePlaceholderHidden = false;
|
||||
|
||||
QString _oldtext;
|
||||
int _oldcursor = 0;
|
||||
QString _lastPreEditText;
|
||||
|
||||
bool _undoAvailable = false;
|
||||
bool _redoAvailable = false;
|
||||
|
||||
bool _customUpDown = false;
|
||||
|
||||
rpl::variable<QString> _placeholderFull;
|
||||
QString _placeholder;
|
||||
Animations::Simple _a_placeholderShifted;
|
||||
bool _placeholderShifted = false;
|
||||
QPainterPath _placeholderPath;
|
||||
|
||||
Animations::Simple _a_borderShown;
|
||||
int _borderAnimationStart = 0;
|
||||
Animations::Simple _a_borderOpacity;
|
||||
bool _borderVisible = false;
|
||||
|
||||
Animations::Simple _a_focused;
|
||||
Animations::Simple _a_error;
|
||||
|
||||
bool _focused = false;
|
||||
bool _error = false;
|
||||
|
||||
style::margins _textMargins;
|
||||
|
||||
QTimer _touchTimer;
|
||||
bool _touchPress = false;
|
||||
bool _touchRightButton = false;
|
||||
bool _touchMove = false;
|
||||
bool _mousePressedInTouch = false;
|
||||
QPoint _touchStart;
|
||||
|
||||
base::unique_qptr<PopupMenu> _contextMenu;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Ui
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
//
|
||||
#pragma once
|
||||
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "ui/widgets/fields/masked_input_field.h"
|
||||
|
||||
namespace style {
|
||||
struct InputField;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
//
|
||||
#pragma once
|
||||
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "ui/widgets/fields/masked_input_field.h"
|
||||
|
||||
namespace style {
|
||||
struct InputField;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
//
|
||||
#include "ui/widgets/fields/time_part_input.h"
|
||||
|
||||
#include "base/qt/qt_string_view.h"
|
||||
#include "ui/ui_utility.h" // WheelDirection
|
||||
|
||||
#include <QtCore/QRegularExpression>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
//
|
||||
#pragma once
|
||||
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "ui/widgets/fields/masked_input_field.h"
|
||||
|
||||
namespace Ui {
|
||||
|
||||
|
|
|
|||
|
|
@ -659,36 +659,6 @@ private:
|
|||
|
||||
};
|
||||
|
||||
class InputStyle : public QCommonStyle {
|
||||
public:
|
||||
InputStyle() {
|
||||
setParent(QCoreApplication::instance());
|
||||
}
|
||||
|
||||
void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = nullptr) const override {
|
||||
}
|
||||
|
||||
static InputStyle *instance() {
|
||||
if (!_instance) {
|
||||
if (!QGuiApplication::instance()) {
|
||||
return nullptr;
|
||||
}
|
||||
_instance = new InputStyle();
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
|
||||
~InputStyle() {
|
||||
_instance = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
static InputStyle *_instance;
|
||||
|
||||
};
|
||||
|
||||
InputStyle *InputStyle::_instance = nullptr;
|
||||
|
||||
template <typename Iterator>
|
||||
QString AccumulateText(Iterator begin, Iterator end) {
|
||||
auto result = QString();
|
||||
|
|
@ -3875,478 +3845,4 @@ void InputField::setErrorShown(bool error) {
|
|||
|
||||
InputField::~InputField() = default;
|
||||
|
||||
MaskedInputField::MaskedInputField(
|
||||
QWidget *parent,
|
||||
const style::InputField &st,
|
||||
rpl::producer<QString> placeholder,
|
||||
const QString &val)
|
||||
: Parent(val, parent)
|
||||
, _st(st)
|
||||
, _oldtext(val)
|
||||
, _placeholderFull(std::move(placeholder)) {
|
||||
resize(_st.width, _st.heightMin);
|
||||
|
||||
setFont(_st.font);
|
||||
setAlignment(_st.textAlign);
|
||||
|
||||
_placeholderFull.value(
|
||||
) | rpl::start_with_next([=](const QString &text) {
|
||||
refreshPlaceholder(text);
|
||||
}, lifetime());
|
||||
|
||||
style::PaletteChanged(
|
||||
) | rpl::start_with_next([=] {
|
||||
updatePalette();
|
||||
}, lifetime());
|
||||
updatePalette();
|
||||
|
||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
|
||||
connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(onTextChange(const QString&)));
|
||||
connect(this, SIGNAL(cursorPositionChanged(int,int)), this, SLOT(onCursorPositionChanged(int,int)));
|
||||
|
||||
connect(this, SIGNAL(textEdited(const QString&)), this, SLOT(onTextEdited()));
|
||||
connect(this, &MaskedInputField::selectionChanged, [] {
|
||||
Integration::Instance().textActionsUpdated();
|
||||
});
|
||||
|
||||
setStyle(InputStyle::instance());
|
||||
QLineEdit::setTextMargins(0, 0, 0, 0);
|
||||
setContentsMargins(_textMargins + QMargins(-2, -1, -2, -1));
|
||||
setFrame(false);
|
||||
|
||||
setAttribute(Qt::WA_AcceptTouchEvents);
|
||||
_touchTimer.setSingleShot(true);
|
||||
connect(&_touchTimer, SIGNAL(timeout()), this, SLOT(onTouchTimer()));
|
||||
|
||||
setTextMargins(_st.textMargins);
|
||||
|
||||
startPlaceholderAnimation();
|
||||
startBorderAnimation();
|
||||
finishAnimating();
|
||||
}
|
||||
|
||||
void MaskedInputField::updatePalette() {
|
||||
auto p = palette();
|
||||
p.setColor(QPalette::Text, _st.textFg->c);
|
||||
p.setColor(QPalette::Highlight, st::msgInBgSelected->c);
|
||||
p.setColor(QPalette::HighlightedText, st::historyTextInFgSelected->c);
|
||||
setPalette(p);
|
||||
}
|
||||
|
||||
void MaskedInputField::setCorrectedText(QString &now, int &nowCursor, const QString &newText, int newPos) {
|
||||
if (newPos < 0 || newPos > newText.size()) {
|
||||
newPos = newText.size();
|
||||
}
|
||||
auto updateText = (newText != now);
|
||||
if (updateText) {
|
||||
now = newText;
|
||||
setText(now);
|
||||
startPlaceholderAnimation();
|
||||
}
|
||||
auto updateCursorPosition = (newPos != nowCursor) || updateText;
|
||||
if (updateCursorPosition) {
|
||||
nowCursor = newPos;
|
||||
setCursorPosition(nowCursor);
|
||||
}
|
||||
}
|
||||
|
||||
void MaskedInputField::customUpDown(bool custom) {
|
||||
_customUpDown = custom;
|
||||
}
|
||||
|
||||
int MaskedInputField::borderAnimationStart() const {
|
||||
return _borderAnimationStart;
|
||||
}
|
||||
|
||||
void MaskedInputField::setTextMargins(const QMargins &mrg) {
|
||||
_textMargins = mrg;
|
||||
setContentsMargins(_textMargins + QMargins(-2, -1, -2, -1));
|
||||
refreshPlaceholder(_placeholderFull.current());
|
||||
}
|
||||
|
||||
void MaskedInputField::onTouchTimer() {
|
||||
_touchRightButton = true;
|
||||
}
|
||||
|
||||
bool MaskedInputField::eventHook(QEvent *e) {
|
||||
auto type = e->type();
|
||||
if (type == QEvent::TouchBegin
|
||||
|| type == QEvent::TouchUpdate
|
||||
|| type == QEvent::TouchEnd
|
||||
|| type == QEvent::TouchCancel) {
|
||||
auto event = static_cast<QTouchEvent*>(e);
|
||||
if (event->device()->type() == base::TouchDevice::TouchScreen) {
|
||||
touchEvent(event);
|
||||
}
|
||||
}
|
||||
return Parent::eventHook(e);
|
||||
}
|
||||
|
||||
void MaskedInputField::touchEvent(QTouchEvent *e) {
|
||||
switch (e->type()) {
|
||||
case QEvent::TouchBegin: {
|
||||
if (_touchPress || e->touchPoints().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
_touchTimer.start(QApplication::startDragTime());
|
||||
_touchPress = true;
|
||||
_touchMove = _touchRightButton = _mousePressedInTouch = false;
|
||||
_touchStart = e->touchPoints().cbegin()->screenPos().toPoint();
|
||||
} break;
|
||||
|
||||
case QEvent::TouchUpdate: {
|
||||
if (!e->touchPoints().isEmpty()) {
|
||||
touchUpdate(e->touchPoints().cbegin()->screenPos().toPoint());
|
||||
}
|
||||
} break;
|
||||
|
||||
case QEvent::TouchEnd: {
|
||||
touchFinish();
|
||||
} break;
|
||||
|
||||
case QEvent::TouchCancel: {
|
||||
_touchPress = false;
|
||||
_touchTimer.stop();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void MaskedInputField::touchUpdate(QPoint globalPosition) {
|
||||
if (_touchPress
|
||||
&& !_touchMove
|
||||
&& ((globalPosition - _touchStart).manhattanLength()
|
||||
>= QApplication::startDragDistance())) {
|
||||
_touchMove = true;
|
||||
}
|
||||
}
|
||||
|
||||
void MaskedInputField::touchFinish() {
|
||||
if (!_touchPress) {
|
||||
return;
|
||||
}
|
||||
const auto weak = MakeWeak(this);
|
||||
if (!_touchMove && window()) {
|
||||
QPoint mapped(mapFromGlobal(_touchStart));
|
||||
|
||||
if (_touchRightButton) {
|
||||
QContextMenuEvent contextEvent(
|
||||
QContextMenuEvent::Mouse,
|
||||
mapped,
|
||||
_touchStart);
|
||||
contextMenuEvent(&contextEvent);
|
||||
} else {
|
||||
QGuiApplication::inputMethod()->show();
|
||||
}
|
||||
}
|
||||
if (weak) {
|
||||
_touchTimer.stop();
|
||||
_touchPress
|
||||
= _touchMove
|
||||
= _touchRightButton
|
||||
= _mousePressedInTouch = false;
|
||||
}
|
||||
}
|
||||
|
||||
void MaskedInputField::paintEvent(QPaintEvent *e) {
|
||||
auto p = QPainter(this);
|
||||
|
||||
auto r = rect().intersected(e->rect());
|
||||
p.fillRect(r, _st.textBg);
|
||||
if (_st.border) {
|
||||
p.fillRect(0, height() - _st.border, width(), _st.border, _st.borderFg->b);
|
||||
}
|
||||
auto errorDegree = _a_error.value(_error ? 1. : 0.);
|
||||
auto focusedDegree = _a_focused.value(_focused ? 1. : 0.);
|
||||
auto borderShownDegree = _a_borderShown.value(1.);
|
||||
auto borderOpacity = _a_borderOpacity.value(_borderVisible ? 1. : 0.);
|
||||
if (_st.borderActive && (borderOpacity > 0.)) {
|
||||
auto borderStart = std::clamp(_borderAnimationStart, 0, width());
|
||||
auto borderFrom = qRound(borderStart * (1. - borderShownDegree));
|
||||
auto borderTo = borderStart + qRound((width() - borderStart) * borderShownDegree);
|
||||
if (borderTo > borderFrom) {
|
||||
auto borderFg = anim::brush(_st.borderFgActive, _st.borderFgError, errorDegree);
|
||||
p.setOpacity(borderOpacity);
|
||||
p.fillRect(borderFrom, height() - _st.borderActive, borderTo - borderFrom, _st.borderActive, borderFg);
|
||||
p.setOpacity(1);
|
||||
}
|
||||
}
|
||||
|
||||
p.setClipRect(r);
|
||||
if (_st.placeholderScale > 0. && !_placeholderPath.isEmpty()) {
|
||||
auto placeholderShiftDegree = _a_placeholderShifted.value(_placeholderShifted ? 1. : 0.);
|
||||
p.save();
|
||||
p.setClipRect(r);
|
||||
|
||||
auto placeholderTop = anim::interpolate(0, _st.placeholderShift, placeholderShiftDegree);
|
||||
|
||||
QRect r(rect().marginsRemoved(_textMargins + _st.placeholderMargins));
|
||||
r.moveTop(r.top() + placeholderTop);
|
||||
if (style::RightToLeft()) r.moveLeft(width() - r.left() - r.width());
|
||||
|
||||
auto placeholderScale = 1. - (1. - _st.placeholderScale) * placeholderShiftDegree;
|
||||
auto placeholderFg = anim::color(_st.placeholderFg, _st.placeholderFgActive, focusedDegree);
|
||||
placeholderFg = anim::color(placeholderFg, _st.placeholderFgError, errorDegree);
|
||||
|
||||
PainterHighQualityEnabler hq(p);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(placeholderFg);
|
||||
p.translate(r.topLeft());
|
||||
p.scale(placeholderScale, placeholderScale);
|
||||
p.drawPath(_placeholderPath);
|
||||
|
||||
p.restore();
|
||||
} else if (!_placeholder.isEmpty()) {
|
||||
auto placeholderHiddenDegree = _a_placeholderShifted.value(_placeholderShifted ? 1. : 0.);
|
||||
if (placeholderHiddenDegree < 1.) {
|
||||
p.setOpacity(1. - placeholderHiddenDegree);
|
||||
p.save();
|
||||
p.setClipRect(r);
|
||||
|
||||
auto placeholderLeft = anim::interpolate(0, -_st.placeholderShift, placeholderHiddenDegree);
|
||||
|
||||
QRect r(rect().marginsRemoved(_textMargins + _st.placeholderMargins));
|
||||
r.moveLeft(r.left() + placeholderLeft);
|
||||
if (style::RightToLeft()) r.moveLeft(width() - r.left() - r.width());
|
||||
|
||||
p.setFont(_st.placeholderFont);
|
||||
p.setPen(anim::pen(_st.placeholderFg, _st.placeholderFgActive, focusedDegree));
|
||||
p.drawText(r, _placeholder, _st.placeholderAlign);
|
||||
|
||||
p.restore();
|
||||
p.setOpacity(1.);
|
||||
}
|
||||
}
|
||||
|
||||
paintAdditionalPlaceholder(p);
|
||||
QLineEdit::paintEvent(e);
|
||||
}
|
||||
|
||||
void MaskedInputField::mousePressEvent(QMouseEvent *e) {
|
||||
if (_touchPress && e->button() == Qt::LeftButton) {
|
||||
_mousePressedInTouch = true;
|
||||
_touchStart = e->globalPos();
|
||||
}
|
||||
return QLineEdit::mousePressEvent(e);
|
||||
}
|
||||
|
||||
void MaskedInputField::mouseReleaseEvent(QMouseEvent *e) {
|
||||
if (_mousePressedInTouch) {
|
||||
touchFinish();
|
||||
}
|
||||
return QLineEdit::mouseReleaseEvent(e);
|
||||
}
|
||||
|
||||
void MaskedInputField::mouseMoveEvent(QMouseEvent *e) {
|
||||
if (_mousePressedInTouch) {
|
||||
touchUpdate(e->globalPos());
|
||||
}
|
||||
return QLineEdit::mouseMoveEvent(e);
|
||||
}
|
||||
|
||||
void MaskedInputField::startBorderAnimation() {
|
||||
auto borderVisible = (_error || _focused);
|
||||
if (_borderVisible != borderVisible) {
|
||||
_borderVisible = borderVisible;
|
||||
if (_borderVisible) {
|
||||
if (_a_borderOpacity.animating()) {
|
||||
_a_borderOpacity.start([this] { update(); }, 0., 1., _st.duration);
|
||||
} else {
|
||||
_a_borderShown.start([this] { update(); }, 0., 1., _st.duration);
|
||||
}
|
||||
} else if (qFuzzyCompare(_a_borderShown.value(1.), 0.)) {
|
||||
_a_borderShown.stop();
|
||||
_a_borderOpacity.stop();
|
||||
} else {
|
||||
_a_borderOpacity.start([this] { update(); }, 1., 0., _st.duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MaskedInputField::focusInEvent(QFocusEvent *e) {
|
||||
_borderAnimationStart = (e->reason() == Qt::MouseFocusReason) ? mapFromGlobal(QCursor::pos()).x() : (width() / 2);
|
||||
setFocused(true);
|
||||
QLineEdit::focusInEvent(e);
|
||||
focused();
|
||||
}
|
||||
|
||||
void MaskedInputField::focusOutEvent(QFocusEvent *e) {
|
||||
setFocused(false);
|
||||
QLineEdit::focusOutEvent(e);
|
||||
blurred();
|
||||
}
|
||||
|
||||
void MaskedInputField::setFocused(bool focused) {
|
||||
if (_focused != focused) {
|
||||
_focused = focused;
|
||||
_a_focused.start([this] { update(); }, _focused ? 0. : 1., _focused ? 1. : 0., _st.duration);
|
||||
startPlaceholderAnimation();
|
||||
startBorderAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
void MaskedInputField::resizeEvent(QResizeEvent *e) {
|
||||
refreshPlaceholder(_placeholderFull.current());
|
||||
_borderAnimationStart = width() / 2;
|
||||
QLineEdit::resizeEvent(e);
|
||||
}
|
||||
|
||||
void MaskedInputField::refreshPlaceholder(const QString &text) {
|
||||
const auto availableWidth = width() - _textMargins.left() - _textMargins.right() - _st.placeholderMargins.left() - _st.placeholderMargins.right() - 1;
|
||||
if (_st.placeholderScale > 0.) {
|
||||
auto placeholderFont = _st.placeholderFont->f;
|
||||
placeholderFont.setStyleStrategy(QFont::PreferMatch);
|
||||
const auto metrics = QFontMetrics(placeholderFont);
|
||||
_placeholder = metrics.elidedText(text, Qt::ElideRight, availableWidth);
|
||||
_placeholderPath = QPainterPath();
|
||||
if (!_placeholder.isEmpty()) {
|
||||
_placeholderPath.addText(0, QFontMetrics(placeholderFont).ascent(), placeholderFont, _placeholder);
|
||||
}
|
||||
} else {
|
||||
_placeholder = _st.placeholderFont->elided(text, availableWidth);
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void MaskedInputField::setPlaceholder(rpl::producer<QString> placeholder) {
|
||||
_placeholderFull = std::move(placeholder);
|
||||
}
|
||||
|
||||
void MaskedInputField::contextMenuEvent(QContextMenuEvent *e) {
|
||||
if (const auto menu = createStandardContextMenu()) {
|
||||
_contextMenu = base::make_unique_q<PopupMenu>(this, menu);
|
||||
_contextMenu->popup(e->globalPos());
|
||||
}
|
||||
}
|
||||
|
||||
void MaskedInputField::inputMethodEvent(QInputMethodEvent *e) {
|
||||
QLineEdit::inputMethodEvent(e);
|
||||
_lastPreEditText = e->preeditString();
|
||||
update();
|
||||
}
|
||||
|
||||
void MaskedInputField::showError() {
|
||||
showErrorNoFocus();
|
||||
if (!hasFocus()) {
|
||||
setFocus();
|
||||
}
|
||||
}
|
||||
|
||||
void MaskedInputField::showErrorNoFocus() {
|
||||
setErrorShown(true);
|
||||
}
|
||||
|
||||
void MaskedInputField::hideError() {
|
||||
setErrorShown(false);
|
||||
}
|
||||
|
||||
void MaskedInputField::setErrorShown(bool error) {
|
||||
if (_error != error) {
|
||||
_error = error;
|
||||
_a_error.start([this] { update(); }, _error ? 0. : 1., _error ? 1. : 0., _st.duration);
|
||||
startBorderAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
QSize MaskedInputField::sizeHint() const {
|
||||
return geometry().size();
|
||||
}
|
||||
|
||||
QSize MaskedInputField::minimumSizeHint() const {
|
||||
return geometry().size();
|
||||
}
|
||||
|
||||
void MaskedInputField::setDisplayFocused(bool focused) {
|
||||
setFocused(focused);
|
||||
finishAnimating();
|
||||
}
|
||||
|
||||
void MaskedInputField::finishAnimating() {
|
||||
_a_focused.stop();
|
||||
_a_error.stop();
|
||||
_a_placeholderShifted.stop();
|
||||
_a_borderShown.stop();
|
||||
_a_borderOpacity.stop();
|
||||
update();
|
||||
}
|
||||
|
||||
void MaskedInputField::setPlaceholderHidden(bool forcePlaceholderHidden) {
|
||||
_forcePlaceholderHidden = forcePlaceholderHidden;
|
||||
startPlaceholderAnimation();
|
||||
}
|
||||
|
||||
void MaskedInputField::startPlaceholderAnimation() {
|
||||
auto placeholderShifted = _forcePlaceholderHidden || (_focused && _st.placeholderScale > 0.) || !getLastText().isEmpty();
|
||||
if (_placeholderShifted != placeholderShifted) {
|
||||
_placeholderShifted = placeholderShifted;
|
||||
_a_placeholderShifted.start([this] { update(); }, _placeholderShifted ? 0. : 1., _placeholderShifted ? 1. : 0., _st.duration);
|
||||
}
|
||||
}
|
||||
|
||||
QRect MaskedInputField::placeholderRect() const {
|
||||
return rect().marginsRemoved(_textMargins + _st.placeholderMargins);
|
||||
}
|
||||
|
||||
void MaskedInputField::placeholderAdditionalPrepare(QPainter &p) {
|
||||
p.setFont(_st.font);
|
||||
p.setPen(_st.placeholderFg);
|
||||
}
|
||||
|
||||
void MaskedInputField::keyPressEvent(QKeyEvent *e) {
|
||||
QString wasText(_oldtext);
|
||||
int32 wasCursor(_oldcursor);
|
||||
|
||||
if (_customUpDown && (e->key() == Qt::Key_Up || e->key() == Qt::Key_Down || e->key() == Qt::Key_PageUp || e->key() == Qt::Key_PageDown)) {
|
||||
e->ignore();
|
||||
} else {
|
||||
QLineEdit::keyPressEvent(e);
|
||||
}
|
||||
|
||||
auto newText = text();
|
||||
auto newCursor = cursorPosition();
|
||||
if (wasText == newText && wasCursor == newCursor) { // call correct manually
|
||||
correctValue(wasText, wasCursor, newText, newCursor);
|
||||
_oldtext = newText;
|
||||
_oldcursor = newCursor;
|
||||
if (wasText != _oldtext) changed();
|
||||
startPlaceholderAnimation();
|
||||
}
|
||||
if (e->key() == Qt::Key_Escape) {
|
||||
e->ignore();
|
||||
cancelled();
|
||||
} else if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) {
|
||||
submitted(e->modifiers());
|
||||
#ifdef Q_OS_MAC
|
||||
} else if (e->key() == Qt::Key_E && e->modifiers().testFlag(Qt::ControlModifier)) {
|
||||
auto selected = selectedText();
|
||||
if (!selected.isEmpty() && echoMode() == QLineEdit::Normal) {
|
||||
QGuiApplication::clipboard()->setText(selected, QClipboard::FindBuffer);
|
||||
}
|
||||
#endif // Q_OS_MAC
|
||||
}
|
||||
}
|
||||
|
||||
void MaskedInputField::onTextEdited() {
|
||||
QString wasText(_oldtext), newText(text());
|
||||
int32 wasCursor(_oldcursor), newCursor(cursorPosition());
|
||||
|
||||
correctValue(wasText, wasCursor, newText, newCursor);
|
||||
_oldtext = newText;
|
||||
_oldcursor = newCursor;
|
||||
if (wasText != _oldtext) changed();
|
||||
startPlaceholderAnimation();
|
||||
|
||||
Integration::Instance().textActionsUpdated();
|
||||
}
|
||||
|
||||
void MaskedInputField::onTextChange(const QString &text) {
|
||||
_oldtext = QLineEdit::text();
|
||||
setErrorShown(false);
|
||||
Integration::Instance().textActionsUpdated();
|
||||
}
|
||||
|
||||
void MaskedInputField::onCursorPositionChanged(int oldPosition, int position) {
|
||||
_oldcursor = position;
|
||||
}
|
||||
|
||||
} // namespace Ui
|
||||
|
|
|
|||
|
|
@ -563,159 +563,4 @@ private:
|
|||
|
||||
};
|
||||
|
||||
class MaskedInputField : public RpWidgetBase<QLineEdit> {
|
||||
Q_OBJECT
|
||||
|
||||
using Parent = RpWidgetBase<QLineEdit>;
|
||||
public:
|
||||
MaskedInputField(
|
||||
QWidget *parent,
|
||||
const style::InputField &st,
|
||||
rpl::producer<QString> placeholder = nullptr,
|
||||
const QString &val = QString());
|
||||
|
||||
void showError();
|
||||
void showErrorNoFocus();
|
||||
void hideError();
|
||||
|
||||
QSize sizeHint() const override;
|
||||
QSize minimumSizeHint() const override;
|
||||
|
||||
void customUpDown(bool isCustom);
|
||||
int borderAnimationStart() const;
|
||||
|
||||
const QString &getLastText() const {
|
||||
return _oldtext;
|
||||
}
|
||||
void setPlaceholder(rpl::producer<QString> placeholder);
|
||||
void setPlaceholderHidden(bool forcePlaceholderHidden);
|
||||
void setDisplayFocused(bool focused);
|
||||
void finishAnimating();
|
||||
void setFocusFast() {
|
||||
setDisplayFocused(true);
|
||||
setFocus();
|
||||
}
|
||||
|
||||
void setText(const QString &text) {
|
||||
QLineEdit::setText(text);
|
||||
startPlaceholderAnimation();
|
||||
}
|
||||
void clear() {
|
||||
QLineEdit::clear();
|
||||
startPlaceholderAnimation();
|
||||
}
|
||||
|
||||
public Q_SLOTS:
|
||||
void onTextChange(const QString &text);
|
||||
void onCursorPositionChanged(int oldPosition, int position);
|
||||
|
||||
void onTextEdited();
|
||||
|
||||
void onTouchTimer();
|
||||
|
||||
Q_SIGNALS:
|
||||
void changed();
|
||||
void cancelled();
|
||||
void submitted(Qt::KeyboardModifiers);
|
||||
void focused();
|
||||
void blurred();
|
||||
|
||||
protected:
|
||||
QString getDisplayedText() const {
|
||||
auto result = getLastText();
|
||||
if (!_lastPreEditText.isEmpty()) {
|
||||
result = result.mid(0, _oldcursor) + _lastPreEditText + result.mid(_oldcursor);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
void startBorderAnimation();
|
||||
void startPlaceholderAnimation();
|
||||
|
||||
bool eventHook(QEvent *e) override;
|
||||
void touchEvent(QTouchEvent *e);
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
void focusInEvent(QFocusEvent *e) override;
|
||||
void focusOutEvent(QFocusEvent *e) override;
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
void contextMenuEvent(QContextMenuEvent *e) override;
|
||||
void inputMethodEvent(QInputMethodEvent *e) override;
|
||||
|
||||
void mousePressEvent(QMouseEvent *e) override;
|
||||
void mouseReleaseEvent(QMouseEvent *e) override;
|
||||
void mouseMoveEvent(QMouseEvent *e) override;
|
||||
|
||||
virtual void correctValue(
|
||||
const QString &was,
|
||||
int wasCursor,
|
||||
QString &now,
|
||||
int &nowCursor) {
|
||||
}
|
||||
void setCorrectedText(QString &now, int &nowCursor, const QString &newText, int newPos);
|
||||
|
||||
virtual void paintAdditionalPlaceholder(QPainter &p) {
|
||||
}
|
||||
|
||||
style::font phFont() {
|
||||
return _st.font;
|
||||
}
|
||||
|
||||
void placeholderAdditionalPrepare(QPainter &p);
|
||||
QRect placeholderRect() const;
|
||||
|
||||
void setTextMargins(const QMargins &mrg);
|
||||
const style::InputField &_st;
|
||||
|
||||
private:
|
||||
void updatePalette();
|
||||
void refreshPlaceholder(const QString &text);
|
||||
void setErrorShown(bool error);
|
||||
|
||||
void touchUpdate(QPoint globalPosition);
|
||||
void touchFinish();
|
||||
|
||||
void setFocused(bool focused);
|
||||
|
||||
int _maxLength = -1;
|
||||
bool _forcePlaceholderHidden = false;
|
||||
|
||||
QString _oldtext;
|
||||
int _oldcursor = 0;
|
||||
QString _lastPreEditText;
|
||||
|
||||
bool _undoAvailable = false;
|
||||
bool _redoAvailable = false;
|
||||
|
||||
bool _customUpDown = false;
|
||||
|
||||
rpl::variable<QString> _placeholderFull;
|
||||
QString _placeholder;
|
||||
Animations::Simple _a_placeholderShifted;
|
||||
bool _placeholderShifted = false;
|
||||
QPainterPath _placeholderPath;
|
||||
|
||||
Animations::Simple _a_borderShown;
|
||||
int _borderAnimationStart = 0;
|
||||
Animations::Simple _a_borderOpacity;
|
||||
bool _borderVisible = false;
|
||||
|
||||
Animations::Simple _a_focused;
|
||||
Animations::Simple _a_error;
|
||||
|
||||
bool _focused = false;
|
||||
bool _error = false;
|
||||
|
||||
style::margins _textMargins;
|
||||
|
||||
QTimer _touchTimer;
|
||||
bool _touchPress = false;
|
||||
bool _touchRightButton = false;
|
||||
bool _touchMove = false;
|
||||
bool _mousePressedInTouch = false;
|
||||
QPoint _touchStart;
|
||||
|
||||
base::unique_qptr<PopupMenu> _contextMenu;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Ui
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue