implement text field

This commit is contained in:
laserpants 2016-06-16 19:08:54 +03:00
parent 9a544efa2c
commit b0d844e5ff
6 changed files with 420 additions and 53 deletions

View File

@ -1,22 +1,28 @@
#include "textfield.h" #include "textfield.h"
#include <QPainter> #include <QPainter>
#include <QPaintEvent>
#include <QApplication> #include <QApplication>
#include "textfield_p.h" #include "textfield_p.h"
#include "textfield_internal.h" #include "textfield_internal.h"
#include "lib/style.h"
TextFieldPrivate::TextFieldPrivate(TextField *q) TextFieldPrivate::TextFieldPrivate(TextField *q)
: q_ptr(q) : q_ptr(q),
label(0),
labelFontSize(11),
showLabel(false),
useThemeColors(true)
{ {
q->setFrame(false); q->setFrame(false);
q->setTextMargins(0, 1, 0, 1);
} }
void TextFieldPrivate::init() void TextFieldPrivate::init()
{ {
Q_Q(TextField); Q_Q(TextField);
machine = new TextFieldStateMachine(q); q->setTextMargins(0, 2, 0, 0);
machine = new TextFieldStateMachine(q);
machine->start(); machine->start();
QCoreApplication::processEvents(); QCoreApplication::processEvents();
@ -27,51 +33,198 @@ TextField::TextField(QWidget *parent)
d_ptr(new TextFieldPrivate(this)) d_ptr(new TextFieldPrivate(this))
{ {
d_func()->init(); d_func()->init();
//
setPlaceholderText("This is a placeholder");
QPalette p;
p.setColor(QPalette::Normal, QPalette::Base, p.color(QPalette::Window));
// p.setColor(QPalette::Normal, QPalette::Text, Qt::blue);
setPalette(p);
} }
TextField::~TextField() TextField::~TextField()
{ {
} }
void TextField::setUseThemeColors(bool value)
{
Q_D(TextField);
d->useThemeColors = value;
d->machine->assignProperties();
}
bool TextField::useThemeColors() const
{
Q_D(const TextField);
return d->useThemeColors;
}
void TextField::setShowLabel(bool value)
{
Q_D(TextField);
if (!d->label) {
d->label = new TextFieldLabel(this);
d->machine->setLabel(d->label);
}
d->showLabel = value;
if (value) {
setContentsMargins(0, 23, 0, 0);
} else {
setContentsMargins(0, 0, 0, 0);
}
}
bool TextField::hasLabel() const
{
Q_D(const TextField);
return d->showLabel;
}
void TextField::setLabelFontSize(qreal size)
{
Q_D(TextField);
d->labelFontSize = size;
if (d->label) {
QFont f(d->label->font());
f.setPointSizeF(size);
d->label->setFont(f);
}
}
qreal TextField::labelFontSize() const
{
Q_D(const TextField);
return d->labelFontSize;
}
void TextField::setLabel(const QString &label)
{
Q_D(TextField);
d->labelString = label;
setShowLabel(true);
}
QString TextField::label() const
{
Q_D(const TextField);
return d->labelString;
}
void TextField::setTextColor(const QColor &color) void TextField::setTextColor(const QColor &color)
{ {
Q_D(TextField);
d->textColor = color;
setUseThemeColors(false);
} }
QColor TextField::textColor() const QColor TextField::textColor() const
{ {
Q_D(const TextField);
if (d->useThemeColors || !d->textColor.isValid()) {
return Style::instance().themeColor("text");
} else {
return d->textColor;
}
} }
void TextField::setBackgroundColor(const QColor &color) void TextField::setBackgroundColor(const QColor &color)
{ {
Q_D(TextField);
d->backgroundColor = color;
setUseThemeColors(false);
} }
QColor TextField::backgroundColor() const QColor TextField::backgroundColor() const
{ {
Q_D(const TextField);
if (d->useThemeColors || !d->backgroundColor.isValid()) {
return palette().color(QPalette::Window);
} else {
return d->backgroundColor;
}
} }
void TextField::setInkColor(const QColor &color) void TextField::setInkColor(const QColor &color)
{ {
Q_D(TextField);
d->inkColor = color;
setUseThemeColors(false);
} }
QColor TextField::inkColor() const QColor TextField::inkColor() const
{ {
Q_D(const TextField);
if (d->useThemeColors || !d->inkColor.isValid()) {
return Style::instance().themeColor("primary1");
} else {
return d->inkColor;
}
} }
void TextField::setUnderlineColor(const QColor &color) void TextField::setUnderlineColor(const QColor &color)
{ {
Q_D(TextField);
d->underlineColor = color;
setUseThemeColors(false);
} }
QColor TextField::underlineColor() const QColor TextField::underlineColor() const
{ {
Q_D(const TextField);
if (d->useThemeColors || !d->underlineColor.isValid()) {
return Style::instance().themeColor("border");
} else {
return d->underlineColor;
}
}
void TextField::setHintColor(const QColor &color)
{
Q_D(TextField);
d->hintColor = color;
setUseThemeColors(false);
}
QColor TextField::hintColor() const
{
Q_D(const TextField);
if (d->useThemeColors || !d->hintColor.isValid()) {
return Style::instance().themeColor("accent3");
} else {
return d->hintColor;
}
}
bool TextField::event(QEvent *event)
{
Q_D(TextField);
switch (event->type())
{
case QEvent::Resize:
case QEvent::Move: {
if (d->label) {
d->label->setGeometry(rect());
}
}
default:
break;
}
return QLineEdit::event(event);
} }
void TextField::paintEvent(QPaintEvent *event) void TextField::paintEvent(QPaintEvent *event)
@ -82,31 +235,37 @@ void TextField::paintEvent(QPaintEvent *event)
QPainter painter(this); QPainter painter(this);
// { if (text().isEmpty())
// painter.drawText(0, height()/2, text()); {
// } QBrush bgBrush;
bgBrush.setStyle(Qt::SolidPattern);
//QBrush bgBrush; bgBrush.setColor(backgroundColor());
//bgBrush.setStyle(Qt::SolidPattern); painter.setOpacity(1-d->machine->progress());
//bgBrush.setColor(palette().color(QPalette::Window)); painter.fillRect(rect(), bgBrush);
//painter.fillRect(rect(), bgBrush); }
const int y = height()-1; const int y = height()-1;
painter.drawLine(0, y, width(), y); const int wd = width()-5;
QPen pen;
pen.setWidth(1);
pen.setColor(underlineColor());
painter.setPen(pen);
painter.setOpacity(1);
painter.drawLine(2.5, y, wd, y);
QBrush brush; QBrush brush;
brush.setStyle(Qt::SolidPattern); brush.setStyle(Qt::SolidPattern);
brush.setColor(inkColor());
const qreal progress = d->machine->progress(); const qreal progress = d->machine->progress();
if (progress > 0) { if (progress > 0)
{
painter.setPen(Qt::NoPen); painter.setPen(Qt::NoPen);
painter.setBrush(brush); painter.setBrush(brush);
int w = (1-progress)*static_cast<qreal>(width()/2); int w = (1-progress)*static_cast<qreal>(wd/2);
painter.drawRect(w+2.5, height()-2, wd-w*2, 2);
painter.drawRect(w, height()-2, width()-w*2, 2);
} }
} }

View File

@ -9,15 +9,28 @@ class TextField : public QLineEdit
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QColor textColor WRITE setTextColor READ progress NOTIFY textColor) Q_PROPERTY(QColor textColor WRITE setTextColor READ textColor)
Q_PROPERTY(QColor backgroundColor WRITE setBackgroundColor READ progress NOTIFY backgroundColor) Q_PROPERTY(QColor backgroundColor WRITE setBackgroundColor READ backgroundColor)
Q_PROPERTY(QColor inkColor WRITE setInkColor READ progress NOTIFY inkColor) Q_PROPERTY(QColor inkColor WRITE setInkColor READ inkColor)
Q_PROPERTY(QColor underlineColor WRITE setUnderlineColor READ progress NOTIFY underlineColor) Q_PROPERTY(QColor underlineColor WRITE setUnderlineColor READ underlineColor)
Q_PROPERTY(QColor hintColor WRITE setHintColor READ hintColor)
public: public:
explicit TextField(QWidget *parent = 0); explicit TextField(QWidget *parent = 0);
~TextField(); ~TextField();
void setUseThemeColors(bool value);
bool useThemeColors() const;
void setShowLabel(bool value);
bool hasLabel() const;
void setLabelFontSize(qreal size);
qreal labelFontSize() const;
void setLabel(const QString &label);
QString label() const;
void setTextColor(const QColor &color); void setTextColor(const QColor &color);
QColor textColor() const; QColor textColor() const;
@ -30,7 +43,11 @@ public:
void setUnderlineColor(const QColor &color); void setUnderlineColor(const QColor &color);
QColor underlineColor() const; QColor underlineColor() const;
void setHintColor(const QColor &color);
QColor hintColor() const;
protected: protected:
bool event(QEvent *event) Q_DECL_OVERRIDE;
void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE; void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE;
const QScopedPointer<TextFieldPrivate> d_ptr; const QScopedPointer<TextFieldPrivate> d_ptr;

View File

@ -1,48 +1,140 @@
#include "textfield_internal.h" #include "textfield_internal.h"
#include <QEventTransition> #include <QEventTransition>
#include <QPropertyAnimation> #include <QPropertyAnimation>
#include <QPainter>
#include <QDebug> #include <QDebug>
#include "textfield.h" #include "textfield.h"
TextFieldStateMachine::TextFieldStateMachine(TextField *parent) TextFieldStateMachine::TextFieldStateMachine(TextField *parent)
: QStateMachine(parent), : QStateMachine(parent),
textField(parent), textField(parent),
label(0),
_normalState(new QState),
_focusedState(new QState),
_progress(0) _progress(0)
{ {
QState *normalState = new QState; addState(_normalState);
QState *focusedState = new QState; addState(_focusedState);
addState(normalState); setInitialState(_normalState);
addState(focusedState);
setInitialState(normalState);
QEventTransition *transition; QEventTransition *transition;
QPropertyAnimation *animation; QPropertyAnimation *animation;
transition = new QEventTransition(parent, QEvent::FocusIn); transition = new QEventTransition(parent, QEvent::FocusIn);
transition->setTargetState(focusedState); transition->setTargetState(_focusedState);
normalState->addTransition(transition); _normalState->addTransition(transition);
transition = new QEventTransition(parent, QEvent::FocusOut);
transition->setTargetState(normalState);
focusedState->addTransition(transition);
normalState->assignProperty(this, "progress", 0);
focusedState->assignProperty(this, "progress", 1);
animation = new QPropertyAnimation(this, "progress"); animation = new QPropertyAnimation(this, "progress");
animation->setEasingCurve(QEasingCurve::InCubic); animation->setEasingCurve(QEasingCurve::InCubic);
animation->setDuration(340); animation->setDuration(340);
addDefaultAnimation(animation); transition->addAnimation(animation);
transition = new QEventTransition(parent, QEvent::FocusOut);
transition->setTargetState(_normalState);
_focusedState->addTransition(transition);
animation = new QPropertyAnimation(this, "progress");
animation->setEasingCurve(QEasingCurve::OutCubic);
animation->setDuration(340);
transition->addAnimation(animation);
_normalState->assignProperty(this, "progress", 0);
_focusedState->assignProperty(this, "progress", 1);
assignProperties();
connect(textField, SIGNAL(textChanged(QString)), this, SLOT(assignProperties()));
} }
TextFieldStateMachine::~TextFieldStateMachine() TextFieldStateMachine::~TextFieldStateMachine()
{ {
} }
void TextFieldStateMachine::setLabel(TextFieldLabel *widget)
{
label = widget;
if (label)
{
QPropertyAnimation *animation;
animation = new QPropertyAnimation(label, "offset");
animation->setDuration(210);
animation->setEasingCurve(QEasingCurve::OutCubic);
addDefaultAnimation(animation);
animation = new QPropertyAnimation(label, "color");
animation->setDuration(210);
addDefaultAnimation(animation);
}
assignProperties();
}
void TextFieldStateMachine::setProgress(qreal progress) void TextFieldStateMachine::setProgress(qreal progress)
{ {
_progress = progress; _progress = progress;
textField->update(); textField->update();
} }
void TextFieldStateMachine::assignProperties()
{
QPalette p;
p.setColor(QPalette::Normal, QPalette::Base, textField->backgroundColor());
textField->setPalette(p);
if (label)
{
const int m = textField->textMargins().top();
_focusedState->assignProperty(label, "offset", QPointF(0, 0-m));
if (textField->text().isEmpty()) {
_normalState->assignProperty(label, "offset", QPointF(0, 26));
} else {
_normalState->assignProperty(label, "offset", QPointF(0, 0-m));
}
_focusedState->assignProperty(label, "color", textField->inkColor());
_normalState->assignProperty(label, "color", textField->hintColor());
}
}
TextFieldLabel::TextFieldLabel(TextField *parent)
: QWidget(parent),
label(parent),
_scale(1),
_posX(0),
_posY(26),
_color(parent->hintColor())
{
QFont f;
f.setPointSizeF(parent->labelFontSize());
f.setStyleName("Medium");
setFont(f);
}
TextFieldLabel::~TextFieldLabel()
{
}
void TextFieldLabel::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event)
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.scale(_scale, _scale);
painter.setPen(_color);
painter.setOpacity(1);
QPointF pos(2 + _posX, height()-36 + _posY);
painter.drawText(pos.x(), pos.y(), label->label());
#ifdef DEBUG_LAYOUT
painter.setPen(Qt::red);
painter.setBrush(Qt::NoBrush);
painter.drawRect(rect());
#endif
}

View File

@ -2,8 +2,10 @@
#define TEXTFIELD_INTERNAL_H #define TEXTFIELD_INTERNAL_H
#include <QStateMachine> #include <QStateMachine>
#include <QWidget>
class TextField; class TextField;
class TextFieldLabel;
class TextFieldStateMachine : public QStateMachine class TextFieldStateMachine : public QStateMachine
{ {
@ -15,14 +17,58 @@ public:
TextFieldStateMachine(TextField *parent); TextFieldStateMachine(TextField *parent);
~TextFieldStateMachine(); ~TextFieldStateMachine();
void setLabel(TextFieldLabel *widget);
void setProgress(qreal progress); void setProgress(qreal progress);
inline qreal progress() const { return _progress; } inline qreal progress() const { return _progress; }
protected slots:
void assignProperties();
private: private:
Q_DISABLE_COPY(TextFieldStateMachine) Q_DISABLE_COPY(TextFieldStateMachine)
friend class TextField;
TextField *const textField; TextField *const textField;
TextFieldLabel *label;
QState *const _normalState;
QState *const _focusedState;
qreal _progress; qreal _progress;
}; };
class TextFieldLabel : public QWidget
{
Q_OBJECT
Q_PROPERTY(qreal scale WRITE setScale READ scale)
Q_PROPERTY(QPointF offset WRITE setOffset READ offset)
Q_PROPERTY(QColor color WRITE setColor READ color)
public:
TextFieldLabel(TextField *parent);
~TextFieldLabel();
inline void setScale(qreal scale) { _scale = scale; update(); }
inline qreal scale() const { return _scale; }
inline void setOffset(const QPointF &pos) { _posX = pos.x(); _posY = pos.y(); update(); }
inline QPointF offset() const { return QPointF(_posX, _posY); }
inline void setColor(const QColor &color) { _color = color; update(); }
inline QColor color() const { return _color; }
protected:
void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE;
private:
Q_DISABLE_COPY(TextFieldLabel)
TextField *const label;
qreal _scale;
qreal _posX;
qreal _posY;
QColor _color;
};
#endif // TEXTFIELD_INTERNAL_H #endif // TEXTFIELD_INTERNAL_H

View File

@ -5,6 +5,7 @@
class TextField; class TextField;
class TextFieldStateMachine; class TextFieldStateMachine;
class TextFieldLabel;
class TextFieldPrivate class TextFieldPrivate
{ {
@ -16,11 +17,17 @@ public:
void init(); void init();
TextField *const q_ptr; TextField *const q_ptr;
TextFieldLabel *label;
TextFieldStateMachine *machine; TextFieldStateMachine *machine;
QColor textColor; QColor textColor;
QColor backgroundColor; QColor backgroundColor;
QColor inkColor; QColor inkColor;
QColor underlineColor; QColor underlineColor;
QColor hintColor;
QString labelString;
qreal labelFontSize;
bool showLabel;
bool useThemeColors;
}; };
#endif // TEXTFIELD_P_H #endif // TEXTFIELD_P_H

View File

@ -11,6 +11,52 @@ TextFieldExamples::TextFieldExamples(QWidget *parent)
{ {
QLayout *layout = widget()->layout(); QLayout *layout = widget()->layout();
{
TextField *textField = new TextField;
textField->setPlaceholderText("This is a placeholder");
textField->setLabel("Enter your name");
ExampleView *view = new ExampleView;
view->setWidget(textField);
Frame *frame = new Frame;
frame->setCodeSnippet(
"hello"
);
frame->setWidget(view);
layout->addWidget(frame);
}
{
TextField *textField = new TextField;
textField->setLabel("Enter your name");
ExampleView *view = new ExampleView;
view->setWidget(textField);
Frame *frame = new Frame;
frame->setCodeSnippet(
"hello"
);
frame->setWidget(view);
layout->addWidget(frame);
}
{
TextField *textField = new TextField;
textField->setPlaceholderText("No label, only placeholder");
ExampleView *view = new ExampleView;
view->setWidget(textField);
Frame *frame = new Frame;
frame->setCodeSnippet(
"hello"
);
frame->setWidget(view);
layout->addWidget(frame);
}
{ {
TextField *textField = new TextField; TextField *textField = new TextField;