2017-04-06 02:06:42 +03:00
|
|
|
#include "TextField.h"
|
|
|
|
|
|
|
|
#include <QApplication>
|
|
|
|
#include <QEventTransition>
|
|
|
|
#include <QFontDatabase>
|
|
|
|
#include <QPaintEvent>
|
|
|
|
#include <QPainter>
|
|
|
|
#include <QPropertyAnimation>
|
|
|
|
|
|
|
|
TextField::TextField(QWidget *parent)
|
2017-08-20 13:47:22 +03:00
|
|
|
: QLineEdit(parent)
|
2017-04-06 02:06:42 +03:00
|
|
|
{
|
2017-08-12 13:21:52 +03:00
|
|
|
// Get rid of the focus border on macOS.
|
|
|
|
setAttribute(Qt::WA_MacShowFocusRect, 0);
|
|
|
|
|
2017-04-06 02:06:42 +03:00
|
|
|
state_machine_ = new TextFieldStateMachine(this);
|
|
|
|
label_ = 0;
|
2017-08-12 13:21:52 +03:00
|
|
|
label_font_size_ = 13;
|
2017-04-06 02:06:42 +03:00
|
|
|
show_label_ = false;
|
|
|
|
background_color_ = QColor("white");
|
|
|
|
|
|
|
|
setFrame(false);
|
|
|
|
setAttribute(Qt::WA_Hover);
|
|
|
|
setMouseTracking(true);
|
2017-08-12 13:21:52 +03:00
|
|
|
setTextMargins(0, 4, 0, 6);
|
2017-04-06 02:06:42 +03:00
|
|
|
|
2017-08-12 13:21:52 +03:00
|
|
|
QFont font("Open Sans");
|
|
|
|
font.setPixelSize(12);
|
2017-04-06 02:06:42 +03:00
|
|
|
setFont(font);
|
|
|
|
|
|
|
|
state_machine_->start();
|
|
|
|
QCoreApplication::processEvents();
|
|
|
|
}
|
|
|
|
|
|
|
|
TextField::~TextField()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-08-20 13:47:22 +03:00
|
|
|
void
|
|
|
|
TextField::setBackgroundColor(const QColor &color)
|
2017-04-06 02:06:42 +03:00
|
|
|
{
|
|
|
|
background_color_ = color;
|
|
|
|
}
|
|
|
|
|
2017-08-20 13:47:22 +03:00
|
|
|
QColor
|
|
|
|
TextField::backgroundColor() const
|
2017-04-06 02:06:42 +03:00
|
|
|
{
|
|
|
|
return background_color_;
|
|
|
|
}
|
|
|
|
|
2017-08-20 13:47:22 +03:00
|
|
|
void
|
|
|
|
TextField::setShowLabel(bool value)
|
2017-04-06 02:06:42 +03:00
|
|
|
{
|
|
|
|
if (show_label_ == value) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
show_label_ = value;
|
|
|
|
|
|
|
|
if (!label_ && value) {
|
|
|
|
label_ = new TextFieldLabel(this);
|
|
|
|
state_machine_->setLabel(label_);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (value) {
|
|
|
|
setContentsMargins(0, 23, 0, 0);
|
|
|
|
} else {
|
|
|
|
setContentsMargins(0, 0, 0, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-20 13:47:22 +03:00
|
|
|
bool
|
|
|
|
TextField::hasLabel() const
|
2017-04-06 02:06:42 +03:00
|
|
|
{
|
|
|
|
return show_label_;
|
|
|
|
}
|
|
|
|
|
2017-08-20 13:47:22 +03:00
|
|
|
void
|
|
|
|
TextField::setLabelFontSize(qreal size)
|
2017-04-06 02:06:42 +03:00
|
|
|
{
|
|
|
|
label_font_size_ = size;
|
|
|
|
|
|
|
|
if (label_) {
|
|
|
|
QFont font(label_->font());
|
2017-07-15 17:11:46 +03:00
|
|
|
font.setPixelSize(size);
|
2017-04-06 02:06:42 +03:00
|
|
|
label_->setFont(font);
|
|
|
|
label_->update();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-20 13:47:22 +03:00
|
|
|
qreal
|
|
|
|
TextField::labelFontSize() const
|
2017-04-06 02:06:42 +03:00
|
|
|
{
|
|
|
|
return label_font_size_;
|
|
|
|
}
|
|
|
|
|
2017-08-20 13:47:22 +03:00
|
|
|
void
|
|
|
|
TextField::setLabel(const QString &label)
|
2017-04-06 02:06:42 +03:00
|
|
|
{
|
|
|
|
label_text_ = label;
|
|
|
|
setShowLabel(true);
|
|
|
|
label_->update();
|
|
|
|
}
|
|
|
|
|
2017-08-20 13:47:22 +03:00
|
|
|
QString
|
|
|
|
TextField::label() const
|
2017-04-06 02:06:42 +03:00
|
|
|
{
|
|
|
|
return label_text_;
|
|
|
|
}
|
|
|
|
|
2017-08-20 13:47:22 +03:00
|
|
|
void
|
|
|
|
TextField::setTextColor(const QColor &color)
|
2017-04-06 02:06:42 +03:00
|
|
|
{
|
|
|
|
text_color_ = color;
|
|
|
|
setStyleSheet(QString("QLineEdit { color: %1; }").arg(color.name()));
|
|
|
|
}
|
|
|
|
|
2017-08-20 13:47:22 +03:00
|
|
|
QColor
|
|
|
|
TextField::textColor() const
|
2017-04-06 02:06:42 +03:00
|
|
|
{
|
|
|
|
if (!text_color_.isValid()) {
|
|
|
|
return QColor("black");
|
|
|
|
}
|
|
|
|
|
|
|
|
return text_color_;
|
|
|
|
}
|
|
|
|
|
2017-08-20 13:47:22 +03:00
|
|
|
void
|
|
|
|
TextField::setLabelColor(const QColor &color)
|
2017-04-06 02:06:42 +03:00
|
|
|
{
|
|
|
|
label_color_ = color;
|
|
|
|
}
|
|
|
|
|
2017-08-20 13:47:22 +03:00
|
|
|
QColor
|
|
|
|
TextField::labelColor() const
|
2017-04-06 02:06:42 +03:00
|
|
|
{
|
|
|
|
if (!label_color_.isValid()) {
|
2017-08-20 13:47:22 +03:00
|
|
|
return QColor("#abb"); // TODO: Move this into Theme.h
|
2017-04-06 02:06:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return label_color_;
|
|
|
|
}
|
|
|
|
|
2017-08-20 13:47:22 +03:00
|
|
|
void
|
|
|
|
TextField::setInkColor(const QColor &color)
|
2017-04-06 02:06:42 +03:00
|
|
|
{
|
|
|
|
ink_color_ = color;
|
|
|
|
}
|
|
|
|
|
2017-08-20 13:47:22 +03:00
|
|
|
QColor
|
|
|
|
TextField::inkColor() const
|
2017-04-06 02:06:42 +03:00
|
|
|
{
|
|
|
|
if (!ink_color_.isValid()) {
|
|
|
|
return QColor("black");
|
|
|
|
}
|
|
|
|
|
|
|
|
return ink_color_;
|
|
|
|
}
|
|
|
|
|
2017-08-20 13:47:22 +03:00
|
|
|
void
|
|
|
|
TextField::setUnderlineColor(const QColor &color)
|
2017-04-06 02:06:42 +03:00
|
|
|
{
|
|
|
|
underline_color_ = color;
|
|
|
|
}
|
|
|
|
|
2017-08-20 13:47:22 +03:00
|
|
|
QColor
|
|
|
|
TextField::underlineColor() const
|
2017-04-06 02:06:42 +03:00
|
|
|
{
|
|
|
|
if (!underline_color_.isValid()) {
|
|
|
|
return QColor("black");
|
|
|
|
}
|
|
|
|
|
|
|
|
return underline_color_;
|
|
|
|
}
|
|
|
|
|
2017-08-20 13:47:22 +03:00
|
|
|
bool
|
|
|
|
TextField::event(QEvent *event)
|
2017-04-06 02:06:42 +03:00
|
|
|
{
|
|
|
|
switch (event->type()) {
|
|
|
|
case QEvent::Resize:
|
|
|
|
case QEvent::Move: {
|
|
|
|
if (label_)
|
|
|
|
label_->setGeometry(rect());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return QLineEdit::event(event);
|
|
|
|
}
|
|
|
|
|
2017-08-20 13:47:22 +03:00
|
|
|
void
|
|
|
|
TextField::paintEvent(QPaintEvent *event)
|
2017-04-06 02:06:42 +03:00
|
|
|
{
|
|
|
|
QLineEdit::paintEvent(event);
|
|
|
|
|
|
|
|
QPainter painter(this);
|
|
|
|
|
|
|
|
if (text().isEmpty()) {
|
|
|
|
painter.setOpacity(1 - state_machine_->progress());
|
2017-08-20 13:47:22 +03:00
|
|
|
// painter.fillRect(rect(), parentWidget()->palette().color(backgroundRole()));
|
2017-04-06 02:06:42 +03:00
|
|
|
painter.fillRect(rect(), backgroundColor());
|
|
|
|
}
|
|
|
|
|
|
|
|
const int y = height() - 1;
|
|
|
|
const int wd = width() - 5;
|
|
|
|
|
|
|
|
QPen pen;
|
|
|
|
pen.setWidth(1);
|
|
|
|
pen.setColor(underlineColor());
|
|
|
|
painter.setPen(pen);
|
|
|
|
painter.setOpacity(1);
|
2017-04-09 03:54:39 +03:00
|
|
|
painter.drawLine(2, y, wd, y);
|
2017-04-06 02:06:42 +03:00
|
|
|
|
|
|
|
QBrush brush;
|
|
|
|
brush.setStyle(Qt::SolidPattern);
|
|
|
|
brush.setColor(inkColor());
|
|
|
|
|
|
|
|
const qreal progress = state_machine_->progress();
|
|
|
|
|
|
|
|
if (progress > 0) {
|
|
|
|
painter.setPen(Qt::NoPen);
|
|
|
|
painter.setBrush(brush);
|
|
|
|
const int w = (1 - progress) * static_cast<qreal>(wd / 2);
|
|
|
|
painter.drawRect(w + 2.5, height() - 2, wd - 2 * w, 2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TextFieldStateMachine::TextFieldStateMachine(TextField *parent)
|
2017-08-20 13:47:22 +03:00
|
|
|
: QStateMachine(parent)
|
|
|
|
, text_field_(parent)
|
2017-04-06 02:06:42 +03:00
|
|
|
{
|
|
|
|
normal_state_ = new QState;
|
|
|
|
focused_state_ = new QState;
|
|
|
|
|
|
|
|
label_ = 0;
|
|
|
|
offset_anim_ = 0;
|
|
|
|
color_anim_ = 0;
|
|
|
|
progress_ = 0.0;
|
|
|
|
|
|
|
|
addState(normal_state_);
|
|
|
|
addState(focused_state_);
|
|
|
|
|
|
|
|
setInitialState(normal_state_);
|
|
|
|
|
|
|
|
QEventTransition *transition;
|
|
|
|
QPropertyAnimation *animation;
|
|
|
|
|
|
|
|
transition = new QEventTransition(parent, QEvent::FocusIn);
|
|
|
|
transition->setTargetState(focused_state_);
|
|
|
|
normal_state_->addTransition(transition);
|
|
|
|
|
|
|
|
animation = new QPropertyAnimation(this, "progress", this);
|
|
|
|
animation->setEasingCurve(QEasingCurve::InCubic);
|
|
|
|
animation->setDuration(310);
|
|
|
|
transition->addAnimation(animation);
|
|
|
|
|
|
|
|
transition = new QEventTransition(parent, QEvent::FocusOut);
|
|
|
|
transition->setTargetState(normal_state_);
|
|
|
|
focused_state_->addTransition(transition);
|
|
|
|
|
|
|
|
animation = new QPropertyAnimation(this, "progress", this);
|
|
|
|
animation->setEasingCurve(QEasingCurve::OutCubic);
|
|
|
|
animation->setDuration(310);
|
|
|
|
transition->addAnimation(animation);
|
|
|
|
|
|
|
|
normal_state_->assignProperty(this, "progress", 0);
|
|
|
|
focused_state_->assignProperty(this, "progress", 1);
|
|
|
|
|
|
|
|
setupProperties();
|
|
|
|
|
|
|
|
connect(text_field_, SIGNAL(textChanged(QString)), this, SLOT(setupProperties()));
|
|
|
|
}
|
|
|
|
|
|
|
|
TextFieldStateMachine::~TextFieldStateMachine()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-08-20 13:47:22 +03:00
|
|
|
void
|
|
|
|
TextFieldStateMachine::setLabel(TextFieldLabel *label)
|
2017-04-06 02:06:42 +03:00
|
|
|
{
|
|
|
|
if (label_) {
|
|
|
|
delete label_;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (offset_anim_) {
|
|
|
|
removeDefaultAnimation(offset_anim_);
|
|
|
|
delete offset_anim_;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (color_anim_) {
|
|
|
|
removeDefaultAnimation(color_anim_);
|
|
|
|
delete color_anim_;
|
|
|
|
}
|
|
|
|
|
|
|
|
label_ = label;
|
|
|
|
|
|
|
|
if (label_) {
|
|
|
|
offset_anim_ = new QPropertyAnimation(label_, "offset", this);
|
|
|
|
offset_anim_->setDuration(210);
|
|
|
|
offset_anim_->setEasingCurve(QEasingCurve::OutCubic);
|
|
|
|
addDefaultAnimation(offset_anim_);
|
|
|
|
|
|
|
|
color_anim_ = new QPropertyAnimation(label_, "color", this);
|
|
|
|
color_anim_->setDuration(210);
|
|
|
|
addDefaultAnimation(color_anim_);
|
|
|
|
}
|
|
|
|
|
|
|
|
setupProperties();
|
|
|
|
}
|
|
|
|
|
2017-08-20 13:47:22 +03:00
|
|
|
void
|
|
|
|
TextFieldStateMachine::setupProperties()
|
2017-04-06 02:06:42 +03:00
|
|
|
{
|
|
|
|
if (label_) {
|
|
|
|
const int m = text_field_->textMargins().top();
|
|
|
|
|
|
|
|
if (text_field_->text().isEmpty()) {
|
|
|
|
normal_state_->assignProperty(label_, "offset", QPointF(0, 26));
|
|
|
|
} else {
|
|
|
|
normal_state_->assignProperty(label_, "offset", QPointF(0, 0 - m));
|
|
|
|
}
|
|
|
|
|
|
|
|
focused_state_->assignProperty(label_, "offset", QPointF(0, 0 - m));
|
|
|
|
focused_state_->assignProperty(label_, "color", text_field_->inkColor());
|
|
|
|
normal_state_->assignProperty(label_, "color", text_field_->labelColor());
|
|
|
|
|
|
|
|
if (0 != label_->offset().y() && !text_field_->text().isEmpty()) {
|
|
|
|
label_->setOffset(QPointF(0, 0 - m));
|
|
|
|
} else if (!text_field_->hasFocus() && label_->offset().y() <= 0 && text_field_->text().isEmpty()) {
|
|
|
|
label_->setOffset(QPointF(0, 26));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
text_field_->update();
|
|
|
|
}
|
|
|
|
|
|
|
|
TextFieldLabel::TextFieldLabel(TextField *parent)
|
2017-08-20 13:47:22 +03:00
|
|
|
: QWidget(parent)
|
|
|
|
, text_field_(parent)
|
2017-04-06 02:06:42 +03:00
|
|
|
{
|
|
|
|
x_ = 0;
|
|
|
|
y_ = 26;
|
|
|
|
scale_ = 1;
|
|
|
|
color_ = parent->labelColor();
|
|
|
|
|
|
|
|
QFontDatabase db;
|
|
|
|
QFont font(db.font("Open Sans", "Medium", parent->labelFontSize()));
|
|
|
|
font.setLetterSpacing(QFont::PercentageSpacing, 102);
|
|
|
|
setFont(font);
|
|
|
|
}
|
|
|
|
|
|
|
|
TextFieldLabel::~TextFieldLabel()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-08-20 13:47:22 +03:00
|
|
|
void
|
|
|
|
TextFieldLabel::paintEvent(QPaintEvent *)
|
2017-04-06 02:06:42 +03:00
|
|
|
{
|
|
|
|
if (!text_field_->hasLabel())
|
|
|
|
return;
|
|
|
|
|
|
|
|
QPainter painter(this);
|
|
|
|
painter.setRenderHint(QPainter::Antialiasing);
|
|
|
|
painter.scale(scale_, scale_);
|
|
|
|
painter.setPen(color_);
|
|
|
|
painter.setOpacity(1);
|
|
|
|
|
|
|
|
QPointF pos(2 + x_, height() - 36 + y_);
|
|
|
|
painter.drawText(pos.x(), pos.y(), text_field_->label());
|
|
|
|
}
|