diff --git a/components/textfield.cpp b/components/textfield.cpp index a0ef6c3..6a9ad41 100644 --- a/components/textfield.cpp +++ b/components/textfield.cpp @@ -1,22 +1,28 @@ #include "textfield.h" #include +#include #include #include "textfield_p.h" #include "textfield_internal.h" +#include "lib/style.h" TextFieldPrivate::TextFieldPrivate(TextField *q) - : q_ptr(q) + : q_ptr(q), + label(0), + labelFontSize(11), + showLabel(false), + useThemeColors(true) { q->setFrame(false); - q->setTextMargins(0, 1, 0, 1); } void TextFieldPrivate::init() { Q_Q(TextField); - machine = new TextFieldStateMachine(q); + q->setTextMargins(0, 2, 0, 0); + machine = new TextFieldStateMachine(q); machine->start(); QCoreApplication::processEvents(); @@ -27,51 +33,198 @@ TextField::TextField(QWidget *parent) d_ptr(new TextFieldPrivate(this)) { 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() { } +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) { + Q_D(TextField); + + d->textColor = color; + setUseThemeColors(false); } 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) { + Q_D(TextField); + + d->backgroundColor = color; + setUseThemeColors(false); } 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) { + Q_D(TextField); + + d->inkColor = color; + setUseThemeColors(false); } 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) { + Q_D(TextField); + + d->underlineColor = color; + setUseThemeColors(false); } 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) @@ -82,31 +235,37 @@ void TextField::paintEvent(QPaintEvent *event) QPainter painter(this); -// { -// painter.drawText(0, height()/2, text()); -// } - - //QBrush bgBrush; - //bgBrush.setStyle(Qt::SolidPattern); - //bgBrush.setColor(palette().color(QPalette::Window)); - //painter.fillRect(rect(), bgBrush); + if (text().isEmpty()) + { + QBrush bgBrush; + bgBrush.setStyle(Qt::SolidPattern); + bgBrush.setColor(backgroundColor()); + painter.setOpacity(1-d->machine->progress()); + painter.fillRect(rect(), bgBrush); + } 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; brush.setStyle(Qt::SolidPattern); + brush.setColor(inkColor()); const qreal progress = d->machine->progress(); - if (progress > 0) { - + if (progress > 0) + { painter.setPen(Qt::NoPen); painter.setBrush(brush); - int w = (1-progress)*static_cast(width()/2); - - painter.drawRect(w, height()-2, width()-w*2, 2); - + int w = (1-progress)*static_cast(wd/2); + painter.drawRect(w+2.5, height()-2, wd-w*2, 2); } } diff --git a/components/textfield.h b/components/textfield.h index 17ae2c7..7b6c6a5 100644 --- a/components/textfield.h +++ b/components/textfield.h @@ -9,15 +9,28 @@ class TextField : public QLineEdit { Q_OBJECT - Q_PROPERTY(QColor textColor WRITE setTextColor READ progress NOTIFY textColor) - Q_PROPERTY(QColor backgroundColor WRITE setBackgroundColor READ progress NOTIFY backgroundColor) - Q_PROPERTY(QColor inkColor WRITE setInkColor READ progress NOTIFY inkColor) - Q_PROPERTY(QColor underlineColor WRITE setUnderlineColor READ progress NOTIFY underlineColor) + Q_PROPERTY(QColor textColor WRITE setTextColor READ textColor) + Q_PROPERTY(QColor backgroundColor WRITE setBackgroundColor READ backgroundColor) + Q_PROPERTY(QColor inkColor WRITE setInkColor READ inkColor) + Q_PROPERTY(QColor underlineColor WRITE setUnderlineColor READ underlineColor) + Q_PROPERTY(QColor hintColor WRITE setHintColor READ hintColor) public: explicit TextField(QWidget *parent = 0); ~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); QColor textColor() const; @@ -30,7 +43,11 @@ public: void setUnderlineColor(const QColor &color); QColor underlineColor() const; + void setHintColor(const QColor &color); + QColor hintColor() const; + protected: + bool event(QEvent *event) Q_DECL_OVERRIDE; void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE; const QScopedPointer d_ptr; diff --git a/components/textfield_internal.cpp b/components/textfield_internal.cpp index 732acd3..a7aefdf 100644 --- a/components/textfield_internal.cpp +++ b/components/textfield_internal.cpp @@ -1,48 +1,140 @@ #include "textfield_internal.h" #include #include +#include #include #include "textfield.h" TextFieldStateMachine::TextFieldStateMachine(TextField *parent) : QStateMachine(parent), textField(parent), + label(0), + _normalState(new QState), + _focusedState(new QState), _progress(0) { - QState *normalState = new QState; - QState *focusedState = new QState; + addState(_normalState); + addState(_focusedState); - addState(normalState); - addState(focusedState); - - setInitialState(normalState); + setInitialState(_normalState); QEventTransition *transition; QPropertyAnimation *animation; transition = new QEventTransition(parent, QEvent::FocusIn); - transition->setTargetState(focusedState); - 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); + transition->setTargetState(_focusedState); + _normalState->addTransition(transition); animation = new QPropertyAnimation(this, "progress"); animation->setEasingCurve(QEasingCurve::InCubic); 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() { } +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) { _progress = progress; 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 +} diff --git a/components/textfield_internal.h b/components/textfield_internal.h index e984732..b4d2346 100644 --- a/components/textfield_internal.h +++ b/components/textfield_internal.h @@ -2,8 +2,10 @@ #define TEXTFIELD_INTERNAL_H #include +#include class TextField; +class TextFieldLabel; class TextFieldStateMachine : public QStateMachine { @@ -15,14 +17,58 @@ public: TextFieldStateMachine(TextField *parent); ~TextFieldStateMachine(); + void setLabel(TextFieldLabel *widget); + void setProgress(qreal progress); inline qreal progress() const { return _progress; } +protected slots: + void assignProperties(); + private: Q_DISABLE_COPY(TextFieldStateMachine) - TextField *const textField; - qreal _progress; + friend class TextField; + + TextField *const textField; + TextFieldLabel *label; + QState *const _normalState; + QState *const _focusedState; + 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 diff --git a/components/textfield_p.h b/components/textfield_p.h index 9d10c07..f892085 100644 --- a/components/textfield_p.h +++ b/components/textfield_p.h @@ -5,6 +5,7 @@ class TextField; class TextFieldStateMachine; +class TextFieldLabel; class TextFieldPrivate { @@ -16,11 +17,17 @@ public: void init(); TextField *const q_ptr; + TextFieldLabel *label; TextFieldStateMachine *machine; - QColor textColor; - QColor backgroundColor; - QColor inkColor; - QColor underlineColor; + QColor textColor; + QColor backgroundColor; + QColor inkColor; + QColor underlineColor; + QColor hintColor; + QString labelString; + qreal labelFontSize; + bool showLabel; + bool useThemeColors; }; #endif // TEXTFIELD_P_H diff --git a/examples/textfieldexamples.cpp b/examples/textfieldexamples.cpp index 35c0b08..93ce6f4 100644 --- a/examples/textfieldexamples.cpp +++ b/examples/textfieldexamples.cpp @@ -11,6 +11,52 @@ TextFieldExamples::TextFieldExamples(QWidget *parent) { 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;