diff --git a/CMakeLists.txt b/CMakeLists.txt index 704e8e9f..332c9e92 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -338,7 +338,6 @@ set(SRC_FILES src/ui/NhekoDropArea.cpp src/ui/NhekoGlobalObject.cpp src/ui/RoomSettings.cpp - src/ui/TextField.cpp src/ui/Theme.cpp src/ui/ThemeManager.cpp src/ui/UIA.cpp @@ -532,7 +531,6 @@ qt5_wrap_cpp(MOC_HEADERS src/ui/NhekoDropArea.h src/ui/NhekoGlobalObject.h src/ui/RoomSettings.h - src/ui/TextField.h src/ui/Theme.h src/ui/ThemeManager.h src/ui/UIA.h diff --git a/resources/qml/dialogs/RoomSettings.qml b/resources/qml/dialogs/RoomSettings.qml index a5ca0b47..110475c7 100644 --- a/resources/qml/dialogs/RoomSettings.qml +++ b/resources/qml/dialogs/RoomSettings.qml @@ -107,17 +107,58 @@ ApplicationWindow { hideErrorAnimation.restart(); } } - Label { - text: roomSettings.roomName - Layout.alignment: Qt.AlignHCenter - font.pixelSize: fontMetrics.font.pixelSize * 2 - Layout.fillWidth: true - horizontalAlignment: TextEdit.AlignHCenter - color: Nheko.colors.text - wrapMode: Text.Wrap - textFormat: Text.RichText + + TextEdit { + id: roomName + + property bool isNameEditingAllowed: false + + readOnly: !isNameEditingAllowed + textFormat: isNameEditingAllowed ? TextEdit.PlainText : TextEdit.RichText + text: isNameEditingAllowed ? roomSettings.plainRoomName : roomSettings.roomName + font.pixelSize: fontMetrics.font.pixelSize * 2 + color: Nheko.colors.text + + Layout.alignment: Qt.AlignHCenter + Layout.maximumWidth: parent.width - (Nheko.paddingSmall * 2) - nameChangeButton.anchors.leftMargin - (nameChangeButton.width * 2) + horizontalAlignment: TextEdit.AlignHCenter + wrapMode: TextEdit.Wrap + selectByMouse: true + + Keys.onShortcutOverride: event.key === Qt.Key_Enter + Keys.onPressed: { + if (event.matches(StandardKey.InsertLineSeparator) || event.matches(StandardKey.InsertParagraphSeparator)) { + roomSettings.changeName(roomName.text); + roomName.isNameEditingAllowed = false; + event.accepted = true; + } } + ImageButton { + id: nameChangeButton + visible: roomSettings.canChangeName + anchors.leftMargin: Nheko.paddingSmall + anchors.left: roomName.right + anchors.verticalCenter: roomName.verticalCenter + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: qsTr("Change name of this room") + ToolTip.delay: Nheko.tooltipDelay + image: roomName.isNameEditingAllowed ? ":/icons/icons/ui/checkmark.svg" : ":/icons/icons/ui/edit.svg" + onClicked: { + if (roomName.isNameEditingAllowed) { + roomSettings.changeName(roomName.text); + roomName.isNameEditingAllowed = false; + } else { + roomName.isNameEditingAllowed = true; + roomName.focus = true; + roomName.selectAll(); + } + } + } + + } + Label { text: qsTr("%n member(s)", "", roomSettings.memberCount) Layout.alignment: Qt.AlignHCenter @@ -134,17 +175,10 @@ ApplicationWindow { } - ImageButton { - Layout.alignment: Qt.AlignHCenter - image: ":/icons/icons/ui/edit.svg" - visible: roomSettings.canChangeNameAndTopic - onClicked: roomSettings.openEditModal() - } - TextArea { id: roomTopic property bool cut: implicitHeight > 100 - property bool showMore + property bool showMore: false clip: true Layout.maximumHeight: showMore? Number.POSITIVE_INFINITY : 100 Layout.preferredHeight: implicitHeight @@ -153,10 +187,12 @@ ApplicationWindow { Layout.leftMargin: Nheko.paddingLarge Layout.rightMargin: Nheko.paddingLarge - text: TimelineManager.escapeEmoji(roomSettings.roomTopic) + property bool isTopicEditingAllowed: false + + readOnly: !isTopicEditingAllowed + textFormat: isTopicEditingAllowed ? TextEdit.PlainText : TextEdit.RichText + text: isTopicEditingAllowed ? roomSettings.plainRoomTopic : roomSettings.roomTopic wrapMode: TextEdit.WordWrap - textFormat: TextEdit.RichText - readOnly: true background: null selectByMouse: !Settings.mobileMode color: Nheko.colors.text @@ -169,6 +205,29 @@ ApplicationWindow { } } + + ImageButton { + id: topicChangeButton + Layout.alignment: Qt.AlignHCenter + visible: roomSettings.canChangeTopic + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: qsTr("Change topic of this room") + ToolTip.delay: Nheko.tooltipDelay + image: roomTopic.isTopicEditingAllowed ? ":/icons/icons/ui/checkmark.svg" : ":/icons/icons/ui/edit.svg" + onClicked: { + if (roomTopic.isTopicEditingAllowed) { + roomSettings.changeTopic(roomTopic.text); + roomTopic.isTopicEditingAllowed = false; + } else { + roomTopic.isTopicEditingAllowed = true; + roomTopic.showMore = true; + roomTopic.focus = true; + //roomTopic.selectAll(); + } + } + } + Item { Layout.alignment: Qt.AlignHCenter id: showMorePlaceholder diff --git a/src/ui/RoomSettings.cpp b/src/ui/RoomSettings.cpp index 43a11b7f..42db1955 100644 --- a/src/ui/RoomSettings.cpp +++ b/src/ui/RoomSettings.cpp @@ -5,13 +5,10 @@ #include "RoomSettings.h" -#include #include -#include #include #include #include -#include #include #include #include @@ -23,153 +20,9 @@ #include "Logging.h" #include "MatrixClient.h" #include "Utils.h" -#include "ui/TextField.h" using namespace mtx::events; -EditModal::EditModal(const QString &roomId, QWidget *parent) - : QWidget(parent) - , roomId_{roomId} -{ - setAutoFillBackground(true); - setAttribute(Qt::WA_DeleteOnClose, true); - setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); - setWindowModality(Qt::WindowModal); - - QFont largeFont; - largeFont.setPointSizeF(largeFont.pointSizeF() * 1.4); - setMinimumWidth(conf::window::minModalWidth); - setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); - - auto layout = new QVBoxLayout(this); - - applyBtn_ = new QPushButton(tr("Apply"), this); - cancelBtn_ = new QPushButton(tr("Cancel"), this); - cancelBtn_->setDefault(true); - - auto btnLayout = new QHBoxLayout; - btnLayout->addStretch(1); - btnLayout->setSpacing(15); - btnLayout->addWidget(cancelBtn_); - btnLayout->addWidget(applyBtn_); - - nameInput_ = new TextField(this); - nameInput_->setLabel(tr("Name").toUpper()); - topicInput_ = new TextField(this); - topicInput_->setLabel(tr("Topic").toUpper()); - - errorField_ = new QLabel(this); - errorField_->setWordWrap(true); - errorField_->hide(); - - layout->addWidget(nameInput_); - layout->addWidget(topicInput_); - layout->addLayout(btnLayout, 1); - - auto labelLayout = new QHBoxLayout; - labelLayout->setAlignment(Qt::AlignHCenter); - labelLayout->addWidget(errorField_); - layout->addLayout(labelLayout); - - connect(applyBtn_, &QPushButton::clicked, this, &EditModal::applyClicked); - connect(cancelBtn_, &QPushButton::clicked, this, &EditModal::close); - - auto window = QApplication::activeWindow(); - - if (window != nullptr) { - auto center = window->frameGeometry().center(); - move(center.x() - (width() * 0.5), center.y() - (height() * 0.5)); - } -} - -void -EditModal::topicEventSent(const QString &topic) -{ - errorField_->hide(); - emit topicChanged(topic); - close(); -} - -void -EditModal::nameEventSent(const QString &name) -{ - errorField_->hide(); - emit nameChanged(name); - close(); -} - -void -EditModal::error(const QString &msg) -{ - errorField_->setText(msg); - errorField_->show(); -} - -void -EditModal::applyClicked() -{ - // Check if the values are changed from the originals. - auto newName = nameInput_->text().trimmed(); - auto newTopic = topicInput_->text().trimmed(); - - errorField_->hide(); - - if (newName == initialName_ && newTopic == initialTopic_) { - close(); - return; - } - - using namespace mtx::events; - auto proxy = std::make_shared(); - connect(proxy.get(), &ThreadProxy::topicEventSent, this, &EditModal::topicEventSent); - connect(proxy.get(), &ThreadProxy::nameEventSent, this, &EditModal::nameEventSent); - connect(proxy.get(), &ThreadProxy::error, this, &EditModal::error); - - if (newName != initialName_ && !newName.isEmpty()) { - state::Name body; - body.name = newName.toStdString(); - - http::client()->send_state_event( - roomId_.toStdString(), - body, - [proxy, newName](const mtx::responses::EventId &, mtx::http::RequestErr err) { - if (err) { - emit proxy->error(QString::fromStdString(err->matrix_error.error)); - return; - } - - emit proxy->nameEventSent(newName); - }); - } - - if (newTopic != initialTopic_ && !newTopic.isEmpty()) { - state::Topic body; - body.topic = newTopic.toStdString(); - - http::client()->send_state_event( - roomId_.toStdString(), - body, - [proxy, newTopic](const mtx::responses::EventId &, mtx::http::RequestErr err) { - if (err) { - emit proxy->error(QString::fromStdString(err->matrix_error.error)); - return; - } - - emit proxy->topicEventSent(newTopic); - }); - } -} - -void -EditModal::setFields(const QString &roomName, const QString &roomTopic) -{ - initialName_ = roomName; - initialTopic_ = roomTopic; - - nameInput_->setText(roomName); - topicInput_->setText(roomTopic); -} - RoomSettings::RoomSettings(QString roomid, QObject *parent) : QObject(parent) , roomid_{std::move(roomid)} @@ -244,6 +97,18 @@ RoomSettings::roomTopic() const .replace(QLatin1String("\n"), QLatin1String("
")))); } +QString +RoomSettings::plainRoomName() const +{ + return QString::fromStdString(info_.name); +} + +QString +RoomSettings::plainRoomTopic() const +{ + return QString::fromStdString(info_.topic); +} + QString RoomSettings::roomId() const { @@ -340,12 +205,24 @@ RoomSettings::canChangeJoinRules() const } bool -RoomSettings::canChangeNameAndTopic() const +RoomSettings::canChangeName() const { try { - return cache::hasEnoughPowerLevel({EventType::RoomName, EventType::RoomTopic}, - roomid_.toStdString(), - utils::localUser().toStdString()); + return cache::hasEnoughPowerLevel( + {EventType::RoomName}, roomid_.toStdString(), utils::localUser().toStdString()); + } catch (const lmdb::error &e) { + nhlog::db()->warn("lmdb error: {}", e.what()); + } + + return false; +} + +bool +RoomSettings::canChangeTopic() const +{ + try { + return cache::hasEnoughPowerLevel( + {EventType::RoomTopic}, roomid_.toStdString(), utils::localUser().toStdString()); } catch (const lmdb::error &e) { nhlog::db()->warn("lmdb error: {}", e.what()); } @@ -387,26 +264,6 @@ RoomSettings::supportsRestricted() const info_.version != "6" && info_.version != "7"; } -void -RoomSettings::openEditModal() -{ - retrieveRoomInfo(); - - auto modal = new EditModal(roomid_); - modal->setFields(QString::fromStdString(info_.name), QString::fromStdString(info_.topic)); - modal->raise(); - modal->show(); - connect(modal, &EditModal::nameChanged, this, [this](const QString &newName) { - info_.name = newName.toStdString(); - emit roomNameChanged(); - }); - - connect(modal, &EditModal::topicChanged, this, [this](const QString &newTopic) { - info_.topic = newTopic.toStdString(); - emit roomTopicChanged(); - }); -} - void RoomSettings::changeNotifications(int currentIndex) { @@ -502,6 +359,74 @@ RoomSettings::changeAccessRules(int index) updateAccessRules(roomid_.toStdString(), join_rule, guest_access); } +void +RoomSettings::changeName(QString name) +{ + // Check if the values are changed from the originals. + auto newName = name.trimmed().toStdString(); + + if (newName == info_.name) { + return; + } + + using namespace mtx::events; + auto proxy = std::make_shared(); + connect(proxy.get(), &ThreadProxy::nameEventSent, this, [this](QString newRoomName) { + this->info_.name = newRoomName.toStdString(); + emit roomNameChanged(); + }); + connect(proxy.get(), &ThreadProxy::error, this, &RoomSettings::displayError); + + state::Name body; + body.name = newName; + + http::client()->send_state_event( + roomid_.toStdString(), + body, + [proxy, newName](const mtx::responses::EventId &, mtx::http::RequestErr err) { + if (err) { + emit proxy->error(QString::fromStdString(err->matrix_error.error)); + return; + } + + emit proxy->nameEventSent(QString::fromStdString(newName)); + }); +} + +void +RoomSettings::changeTopic(QString topic) +{ + // Check if the values are changed from the originals. + auto newTopic = topic.trimmed().toStdString(); + + if (newTopic == info_.topic) { + return; + } + + using namespace mtx::events; + auto proxy = std::make_shared(); + connect(proxy.get(), &ThreadProxy::topicEventSent, this, [this](QString newRoomTopic) { + this->info_.topic = newRoomTopic.toStdString(); + emit roomTopicChanged(); + }); + connect(proxy.get(), &ThreadProxy::error, this, &RoomSettings::displayError); + + state::Topic body; + body.topic = newTopic; + + http::client()->send_state_event( + roomid_.toStdString(), + body, + [proxy, newTopic](const mtx::responses::EventId &, mtx::http::RequestErr err) { + if (err) { + emit proxy->error(QString::fromStdString(err->matrix_error.error)); + return; + } + + emit proxy->topicEventSent(QString::fromStdString(newTopic)); + }); +} + void RoomSettings::updateAccessRules(const std::string &room_id, const mtx::events::state::JoinRules &join_rule, diff --git a/src/ui/RoomSettings.h b/src/ui/RoomSettings.h index f79aa3f7..9912cfd6 100644 --- a/src/ui/RoomSettings.h +++ b/src/ui/RoomSettings.h @@ -5,9 +5,7 @@ #pragma once -#include #include -#include #include #include @@ -16,8 +14,6 @@ #include "CacheStructs.h" -class TextField; - /// Convenience class which connects events emmited from threads /// outside of main with the UI code. class ThreadProxy : public QObject @@ -31,40 +27,6 @@ signals: void stopLoading(); }; -class EditModal : public QWidget -{ - Q_OBJECT - -public: - EditModal(const QString &roomId, QWidget *parent = nullptr); - - void setFields(const QString &roomName, const QString &roomTopic); - -signals: - void nameChanged(const QString &roomName); - void topicChanged(const QString &topic); - -private slots: - void topicEventSent(const QString &topic); - void nameEventSent(const QString &name); - void error(const QString &msg); - - void applyClicked(); - -private: - QString roomId_; - QString initialName_; - QString initialTopic_; - - QLabel *errorField_; - - TextField *nameInput_; - TextField *topicInput_; - - QPushButton *applyBtn_; - QPushButton *cancelBtn_; -}; - class RoomSettings : public QObject { Q_OBJECT @@ -72,6 +34,8 @@ class RoomSettings : public QObject Q_PROPERTY(QString roomVersion READ roomVersion CONSTANT) Q_PROPERTY(QString roomName READ roomName NOTIFY roomNameChanged) Q_PROPERTY(QString roomTopic READ roomTopic NOTIFY roomTopicChanged) + Q_PROPERTY(QString plainRoomName READ plainRoomName NOTIFY roomNameChanged) + Q_PROPERTY(QString plainRoomTopic READ plainRoomTopic NOTIFY roomTopicChanged) Q_PROPERTY(QString roomAvatarUrl READ roomAvatarUrl NOTIFY avatarUrlChanged) Q_PROPERTY(int memberCount READ memberCount CONSTANT) Q_PROPERTY(int notifications READ notifications NOTIFY notificationsChanged) @@ -79,7 +43,8 @@ class RoomSettings : public QObject Q_PROPERTY(bool isLoading READ isLoading NOTIFY loadingChanged) Q_PROPERTY(bool canChangeAvatar READ canChangeAvatar CONSTANT) Q_PROPERTY(bool canChangeJoinRules READ canChangeJoinRules CONSTANT) - Q_PROPERTY(bool canChangeNameAndTopic READ canChangeNameAndTopic CONSTANT) + Q_PROPERTY(bool canChangeName READ canChangeName CONSTANT) + Q_PROPERTY(bool canChangeTopic READ canChangeTopic CONSTANT) Q_PROPERTY(bool isEncryptionEnabled READ isEncryptionEnabled NOTIFY encryptionChanged) Q_PROPERTY(bool supportsKnocking READ supportsKnocking CONSTANT) Q_PROPERTY(bool supportsRestricted READ supportsRestricted CONSTANT) @@ -90,6 +55,8 @@ public: QString roomId() const; QString roomName() const; QString roomTopic() const; + QString plainRoomName() const; + QString plainRoomTopic() const; QString roomVersion() const; QString roomAvatarUrl(); int memberCount() const; @@ -98,8 +65,10 @@ public: bool isLoading() const; //! Whether the user has enough power level to send m.room.join_rules events. bool canChangeJoinRules() const; - //! Whether the user has enough power level to send m.room.name & m.room.topic events. - bool canChangeNameAndTopic() const; + //! Whether the user has enough power level to send m.room.name. + bool canChangeName() const; + //! Whether the user has enough power level to send m.room.topic events. + bool canChangeTopic() const; //! Whether the user has enough power level to send m.room.avatar event. bool canChangeAvatar() const; bool isEncryptionEnabled() const; @@ -108,9 +77,10 @@ public: Q_INVOKABLE void enableEncryption(); Q_INVOKABLE void updateAvatar(); - Q_INVOKABLE void openEditModal(); Q_INVOKABLE void changeAccessRules(int index); Q_INVOKABLE void changeNotifications(int currentIndex); + Q_INVOKABLE void changeTopic(QString topic); + Q_INVOKABLE void changeName(QString name); signals: void loadingChanged(); diff --git a/src/ui/TextField.cpp b/src/ui/TextField.cpp deleted file mode 100644 index f09237a0..00000000 --- a/src/ui/TextField.cpp +++ /dev/null @@ -1,382 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Nheko Contributors -// SPDX-FileCopyrightText: 2022 Nheko Contributors -// -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "TextField.h" - -#include -#include -#include -#include -#include -#include -#include - -TextField::TextField(QWidget *parent) - : QLineEdit(parent) -{ - // Get rid of the focus border on macOS. - setAttribute(Qt::WA_MacShowFocusRect, 0); - - QPalette pal; - - state_machine_ = new TextFieldStateMachine(this); - label_ = nullptr; - label_font_size_ = 15; - show_label_ = false; - background_color_ = pal.color(QPalette::Window); - is_valid_ = true; - - setFrame(false); - setAttribute(Qt::WA_Hover); - setMouseTracking(true); - setTextMargins(0, 4, 0, 6); - - state_machine_->start(); - QCoreApplication::processEvents(); -} - -void -TextField::setBackgroundColor(const QColor &color) -{ - background_color_ = color; - emit backgroundColorChanged(); -} - -QColor -TextField::backgroundColor() const -{ - return background_color_; -} - -void -TextField::setShowLabel(bool value) -{ - 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); - } -} - -bool -TextField::hasLabel() const -{ - return show_label_; -} - -void -TextField::setValid(bool valid) -{ - is_valid_ = valid; -} - -bool -TextField::isValid() const -{ - QString s = text(); - int pos = 0; - if (regexp_.pattern().isEmpty()) { - return is_valid_; - } - QRegularExpressionValidator v(QRegularExpression(regexp_), 0); - return v.validate(s, pos) == QValidator::Acceptable; -} - -void -TextField::setLabelFontSize(qreal size) -{ - label_font_size_ = size; - - if (label_) { - QFont font(label_->font()); - font.setPointSizeF(size); - label_->setFont(font); - label_->update(); - } -} - -qreal -TextField::labelFontSize() const -{ - return label_font_size_; -} - -void -TextField::setLabel(const QString &label) -{ - label_text_ = label; - setShowLabel(true); - label_->update(); -} - -QString -TextField::label() const -{ - return label_text_; -} - -void -TextField::setLabelColor(const QColor &color) -{ - label_color_ = color; - emit labelColorChanged(); - update(); -} - -QColor -TextField::labelColor() const -{ - if (!label_color_.isValid()) { - return QPalette().color(QPalette::Text); - } - - return label_color_; -} - -void -TextField::setInkColor(const QColor &color) -{ - ink_color_ = color; - emit inkColorChanged(); - update(); -} - -QColor -TextField::inkColor() const -{ - if (!ink_color_.isValid()) { - return QPalette().color(QPalette::Text); - } - - return ink_color_; -} - -void -TextField::setUnderlineColor(const QColor &color) -{ - underline_color_ = color; - emit underlineColorChanged(); - update(); -} - -void -TextField::setRegexp(const QRegularExpression ®exp) -{ - regexp_ = regexp; -} - -QColor -TextField::underlineColor() const -{ - if (!underline_color_.isValid()) { - if ((hasAcceptableInput() && isValid()) || !isModified()) - return QPalette().color(QPalette::Highlight); - else - return Qt::red; - } - - return underline_color_; -} - -bool -TextField::event(QEvent *event) -{ - switch (event->type()) { - case QEvent::Resize: - case QEvent::Move: { - if (label_) - label_->setGeometry(rect()); - break; - } - default: - break; - } - - return QLineEdit::event(event); -} - -void -TextField::paintEvent(QPaintEvent *event) -{ - QLineEdit::paintEvent(event); - - QPainter painter(this); - - if (text().isEmpty()) { - painter.setOpacity(1 - state_machine_->progress()); - 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); - painter.drawLine(2, y, wd, y); - - 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(wd / 2); - painter.drawRect(w + 2.5, height() - 2, wd - 2 * w, 2); - } -} - -TextFieldStateMachine::TextFieldStateMachine(TextField *parent) - : QStateMachine(parent) - , text_field_(parent) -{ - normal_state_ = new QState; - focused_state_ = new QState; - - label_ = nullptr; - offset_anim_ = nullptr; - color_anim_ = nullptr; - 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())); -} - -void -TextFieldStateMachine::setLabel(TextFieldLabel *label) -{ - 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(); -} - -void -TextFieldStateMachine::setupProperties() -{ - 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) - : QWidget(parent) - , text_field_(parent) -{ - x_ = 0; - y_ = 26; - scale_ = 1; - color_ = parent->labelColor(); - - QFont font; - font.setWeight(60); - font.setLetterSpacing(QFont::PercentageSpacing, 102); - setFont(font); -} - -void -TextFieldLabel::paintEvent(QPaintEvent *) -{ - 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()); -} diff --git a/src/ui/TextField.h b/src/ui/TextField.h deleted file mode 100644 index 6bc5c160..00000000 --- a/src/ui/TextField.h +++ /dev/null @@ -1,201 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Nheko Contributors -// SPDX-FileCopyrightText: 2022 Nheko Contributors -// -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -class TextField; -class TextFieldLabel; -class TextFieldStateMachine; - -class TextField : public QLineEdit -{ - Q_OBJECT - - Q_PROPERTY(QColor inkColor WRITE setInkColor READ inkColor NOTIFY inkColorChanged) - Q_PROPERTY(QColor labelColor WRITE setLabelColor READ labelColor NOTIFY labelColorChanged) - Q_PROPERTY(QColor underlineColor WRITE setUnderlineColor READ underlineColor NOTIFY - underlineColorChanged) - Q_PROPERTY(QColor backgroundColor WRITE setBackgroundColor READ backgroundColor NOTIFY - backgroundColorChanged) - -public: - explicit TextField(QWidget *parent = nullptr); - - void setInkColor(const QColor &color); - void setBackgroundColor(const QColor &color); - void setLabel(const QString &label); - void setLabelColor(const QColor &color); - void setLabelFontSize(qreal size); - void setShowLabel(bool value); - void setUnderlineColor(const QColor &color); - void setRegexp(const QRegularExpression ®exp); - void setValid(bool valid); - - QColor inkColor() const; - QColor labelColor() const; - QColor underlineColor() const; - QColor backgroundColor() const; - QString label() const; - bool hasLabel() const; - bool isValid() const; - qreal labelFontSize() const; - -protected: - bool event(QEvent *event) override; - void paintEvent(QPaintEvent *event) override; - -signals: - void inkColorChanged(); - void labelColorChanged(); - void underlineColorChanged(); - void backgroundColorChanged(); - -private: - void init(); - - QColor ink_color_; - QColor background_color_; - QColor label_color_; - QColor underline_color_; - QString label_text_; - TextFieldLabel *label_; - TextFieldStateMachine *state_machine_; - bool show_label_; - QRegularExpression regexp_; - bool is_valid_; - qreal label_font_size_; -}; - -class TextFieldLabel : public QWidget -{ - Q_OBJECT - - Q_PROPERTY(qreal scale WRITE setScale READ scale NOTIFY scaleChanged) - Q_PROPERTY(QPointF offset WRITE setOffset READ offset NOTIFY offsetChanged) - Q_PROPERTY(QColor color WRITE setColor READ color NOTIFY colorChanged) - -public: - TextFieldLabel(TextField *parent); - - inline void setColor(const QColor &color); - inline void setOffset(QPointF pos); - inline void setScale(qreal scale); - - inline QColor color() const; - inline QPointF offset() const; - inline qreal scale() const; - -protected: - void paintEvent(QPaintEvent *event) override; - -signals: - void scaleChanged(); - void offsetChanged(); - void colorChanged(); - -private: - TextField *const text_field_; - - QColor color_; - qreal scale_; - qreal x_; - qreal y_; -}; - -inline void -TextFieldLabel::setColor(const QColor &color) -{ - color_ = color; - emit colorChanged(); - update(); -} - -inline void -TextFieldLabel::setOffset(QPointF pos) -{ - x_ = pos.x(); - y_ = pos.y(); - emit offsetChanged(); - update(); -} - -inline void -TextFieldLabel::setScale(qreal scale) -{ - scale_ = scale; - emit scaleChanged(); - update(); -} - -inline QPointF -TextFieldLabel::offset() const -{ - return QPointF(x_, y_); -} -inline qreal -TextFieldLabel::scale() const -{ - return scale_; -} -inline QColor -TextFieldLabel::color() const -{ - return color_; -} - -class TextFieldStateMachine : public QStateMachine -{ - Q_OBJECT - - Q_PROPERTY(qreal progress WRITE setProgress READ progress NOTIFY progressChanged) - -public: - TextFieldStateMachine(TextField *parent); - - inline void setProgress(qreal progress); - void setLabel(TextFieldLabel *label); - - inline qreal progress() const; - -public slots: - void setupProperties(); - -signals: - void progressChanged(); - -private: - QPropertyAnimation *color_anim_; - QPropertyAnimation *offset_anim_; - - QState *focused_state_; - QState *normal_state_; - - TextField *text_field_; - TextFieldLabel *label_; - - qreal progress_; -}; - -inline void -TextFieldStateMachine::setProgress(qreal progress) -{ - progress_ = progress; - emit progressChanged(); - text_field_->update(); -} - -inline qreal -TextFieldStateMachine::progress() const -{ - return progress_; -} diff --git a/src/ui/Theme.h b/src/ui/Theme.h index 2ad781c6..2c85c61e 100644 --- a/src/ui/Theme.h +++ b/src/ui/Theme.h @@ -8,54 +8,6 @@ #include #include -namespace ui { -// Default font size. -const int FontSize = 16; - -// Default avatar size. Width and height. -const int AvatarSize = 40; - -enum class ButtonPreset -{ - FlatPreset, - CheckablePreset -}; - -enum class RippleStyle -{ - CenteredRipple, - PositionedRipple, - NoRipple -}; - -enum class OverlayStyle -{ - NoOverlay, - TintedOverlay, - GrayOverlay -}; - -enum class Role -{ - Default, - Primary, - Secondary -}; - -enum class ButtonIconPlacement -{ - LeftIcon, - RightIcon -}; - -enum class ProgressType -{ - DeterminateProgress, - IndeterminateProgress -}; - -} // namespace ui - class Theme : public QPalette { Q_GADGET diff --git a/src/ui/ThemeManager.cpp b/src/ui/ThemeManager.cpp index d9b599ff..e275fa90 100644 --- a/src/ui/ThemeManager.cpp +++ b/src/ui/ThemeManager.cpp @@ -3,8 +3,6 @@ // // SPDX-License-Identifier: GPL-3.0-or-later -#include - #include "ThemeManager.h" QColor