EditTagDialog: Make reset feedback work by calling

`set_reset_button()` in `UpdateModifiedField()` and catering for
  non-text in `IsValueModified()` (-1 original being same as 0);
  use new `ExtendedEditor::set_font()`;
  connect reset for "rating".
  Make "comment" `tabChangesFocus` to keep tab chain.
ExtendedEditor: New `set_font()` to get emboldened font to work and
  make reset feedback work for `CheckBox` and `RatingBox` by
  overriding `Resize()`.
RatingWidget: Allow tabbed focus and implement keyboard input.
This commit is contained in:
gitlost
2025-05-19 22:27:25 +01:00
committed by Jonas Kvinge
parent a86ba4dffc
commit 340bc21537
6 changed files with 157 additions and 22 deletions

View File

@@ -199,6 +199,7 @@ EditTagDialog::EditTagDialog(const SharedPtr<NetworkAccessManager> network,
} }
else if (RatingBox *ratingbox = qobject_cast<RatingBox*>(widget)) { else if (RatingBox *ratingbox = qobject_cast<RatingBox*>(widget)) {
QObject::connect(ratingbox, &RatingWidget::RatingChanged, this, &EditTagDialog::FieldValueEdited); QObject::connect(ratingbox, &RatingWidget::RatingChanged, this, &EditTagDialog::FieldValueEdited);
QObject::connect(ratingbox, &RatingBox::Reset, this, &EditTagDialog::ResetField);
} }
} }
} }
@@ -544,6 +545,20 @@ bool EditTagDialog::DoesValueVary(const QModelIndexList &sel, const QString &id)
bool EditTagDialog::IsValueModified(const QModelIndexList &sel, const QString &id) const { bool EditTagDialog::IsValueModified(const QModelIndexList &sel, const QString &id) const {
if (id == u"track"_s || id == u"disc"_s || id == u"year"_s) {
return std::any_of(sel.begin(), sel.end(), [this, id](const QModelIndex &i) {
const int original = data_[i.row()].original_value(id).toInt();
const int current = data_[i.row()].current_value(id).toInt();
return original != current && (original != -1 || current != 0);
});
}
else if (id == u"rating"_s) {
return std::any_of(sel.begin(), sel.end(), [this, id](const QModelIndex &i) {
const float original = data_[i.row()].original_value(id).toFloat();
const float current = data_[i.row()].current_value(id).toFloat();
return original != current && (original != -1 || current != 0);
});
}
return std::any_of(sel.begin(), sel.end(), [this, id](const QModelIndex &i) { return data_[i.row()].original_value(id) != data_[i.row()].current_value(id); }); return std::any_of(sel.begin(), sel.end(), [this, id](const QModelIndex &i) { return data_[i.row()].original_value(id) != data_[i.row()].current_value(id); });
} }
@@ -605,7 +620,15 @@ void EditTagDialog::UpdateModifiedField(const FieldData &field, const QModelInde
QFont new_font(font()); QFont new_font(font());
new_font.setBold(modified); new_font.setBold(modified);
field.label_->setFont(new_font); field.label_->setFont(new_font);
if (field.editor_) field.editor_->setFont(new_font); if (field.editor_) {
if (ExtendedEditor *editor = dynamic_cast<ExtendedEditor*>(field.editor_)) {
editor->set_font(new_font);
editor->set_reset_button(modified);
}
else {
field.editor_->setFont(new_font);
}
}
} }

View File

@@ -944,7 +944,7 @@
<item row="15" column="1"> <item row="15" column="1">
<widget class="CheckBox" name="compilation"> <widget class="CheckBox" name="compilation">
<property name="has_reset_button" stdset="0"> <property name="has_reset_button" stdset="0">
<bool>false</bool> <bool>true</bool>
</property> </property>
<property name="has_clear_button" stdset="0"> <property name="has_clear_button" stdset="0">
<bool>false</bool> <bool>false</bool>
@@ -1011,6 +1011,9 @@
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="tabChangesFocus">
<bool>true</bool>
</property>
<property name="has_reset_button" stdset="0"> <property name="has_reset_button" stdset="0">
<bool>true</bool> <bool>true</bool>
</property> </property>
@@ -1072,7 +1075,20 @@
</widget> </widget>
</item> </item>
<item row="16" column="1"> <item row="16" column="1">
<widget class="RatingBox" name="rating" native="true"/> <widget class="RatingBox" name="rating" native="true">
<property name="maximumSize">
<size>
<width>140</width>
<height>16777215</height>
</size>
</property>
<property name="has_reset_button" stdset="0">
<bool>true</bool>
</property>
<property name="has_clear_button" stdset="0">
<bool>false</bool>
</property>
</widget>
</item> </item>
</layout> </layout>
</item> </item>
@@ -1217,6 +1233,7 @@
<tabstop>grouping</tabstop> <tabstop>grouping</tabstop>
<tabstop>genre</tabstop> <tabstop>genre</tabstop>
<tabstop>compilation</tabstop> <tabstop>compilation</tabstop>
<tabstop>rating</tabstop>
<tabstop>fetch_tag</tabstop> <tabstop>fetch_tag</tabstop>
<tabstop>comment</tabstop> <tabstop>comment</tabstop>
<tabstop>lyrics</tabstop> <tabstop>lyrics</tabstop>

View File

@@ -37,12 +37,18 @@
#include <QFlags> #include <QFlags>
#include <QPaintEvent> #include <QPaintEvent>
#include <QResizeEvent> #include <QResizeEvent>
#include <QGuiApplication>
#include "core/iconloader.h" #include "core/iconloader.h"
#include "lineedit.h" #include "lineedit.h"
using namespace Qt::Literals::StringLiterals; using namespace Qt::Literals::StringLiterals;
namespace {
constexpr int kClearIconSize = 16;
constexpr int kResetIconSize = 16;
} // namespace
ExtendedEditor::ExtendedEditor(QWidget *widget, int extra_right_padding, bool draw_hint) ExtendedEditor::ExtendedEditor(QWidget *widget, int extra_right_padding, bool draw_hint)
: LineEditInterface(widget), : LineEditInterface(widget),
has_clear_button_(true), has_clear_button_(true),
@@ -54,7 +60,7 @@ ExtendedEditor::ExtendedEditor(QWidget *widget, int extra_right_padding, bool dr
is_rtl_(false) { is_rtl_(false) {
clear_button_->setIcon(IconLoader::Load(u"edit-clear-locationbar-ltr"_s)); clear_button_->setIcon(IconLoader::Load(u"edit-clear-locationbar-ltr"_s));
clear_button_->setIconSize(QSize(16, 16)); clear_button_->setIconSize(QSize(kClearIconSize, kClearIconSize));
clear_button_->setCursor(Qt::ArrowCursor); clear_button_->setCursor(Qt::ArrowCursor);
clear_button_->setStyleSheet(u"QToolButton { border: none; padding: 0px; }"_s); clear_button_->setStyleSheet(u"QToolButton { border: none; padding: 0px; }"_s);
clear_button_->setToolTip(QWidget::tr("Clear")); clear_button_->setToolTip(QWidget::tr("Clear"));
@@ -64,7 +70,7 @@ ExtendedEditor::ExtendedEditor(QWidget *widget, int extra_right_padding, bool dr
opt.initFrom(widget); opt.initFrom(widget);
reset_button_->setIcon(widget->style()->standardIcon(QStyle::SP_DialogResetButton, &opt, widget)); reset_button_->setIcon(widget->style()->standardIcon(QStyle::SP_DialogResetButton, &opt, widget));
reset_button_->setIconSize(QSize(16, 16)); reset_button_->setIconSize(QSize(kResetIconSize, kResetIconSize));
reset_button_->setCursor(Qt::ArrowCursor); reset_button_->setCursor(Qt::ArrowCursor);
reset_button_->setStyleSheet(u"QToolButton { border: none; padding: 0px; }"_s); reset_button_->setStyleSheet(u"QToolButton { border: none; padding: 0px; }"_s);
reset_button_->setToolTip(QWidget::tr("Reset")); reset_button_->setToolTip(QWidget::tr("Reset"));
@@ -220,9 +226,21 @@ SpinBox::SpinBox(QWidget *parent)
: QSpinBox(parent), : QSpinBox(parent),
ExtendedEditor(this, 14, false) { ExtendedEditor(this, 14, false) {
if (QGuiApplication::isRightToLeft()) {
extra_right_padding_ = 0; // Up/down arrows on left
}
QObject::connect(reset_button_, &QToolButton::clicked, this, &SpinBox::Reset); QObject::connect(reset_button_, &QToolButton::clicked, this, &SpinBox::Reset);
} }
QString SpinBox::textFromValue(int val) const {
if (val <= 0 && !hint_.isEmpty()) {
return u"-"_s;
}
return QSpinBox::textFromValue(val);
}
void SpinBox::paintEvent(QPaintEvent *e) { void SpinBox::paintEvent(QPaintEvent *e) {
QSpinBox::paintEvent(e); QSpinBox::paintEvent(e);
Paint(this); Paint(this);
@@ -234,8 +252,10 @@ void SpinBox::resizeEvent(QResizeEvent *e) {
} }
CheckBox::CheckBox(QWidget *parent) CheckBox::CheckBox(QWidget *parent)
: QCheckBox(parent), ExtendedEditor(this, 14, false) { : QCheckBox(parent), ExtendedEditor(this, 4, false) {
has_clear_button_ = false;
is_rtl_ = QGuiApplication::isRightToLeft();
QObject::connect(reset_button_, &QToolButton::clicked, this, &CheckBox::Reset); QObject::connect(reset_button_, &QToolButton::clicked, this, &CheckBox::Reset);
} }
@@ -250,20 +270,46 @@ void CheckBox::resizeEvent(QResizeEvent *e) {
Resize(); Resize();
} }
QString SpinBox::textFromValue(int val) const { void CheckBox::Resize() {
if (val <= 0 && !hint_.isEmpty()) { const QSize sz = widget_->sizeHint();
return u"-"_s; const int frame_width = widget_->style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
const int y = (rect().height() - sz.height()) / 2 - frame_width; // Less frame width as outside
if (!is_rtl_) {
reset_button_->move(frame_width + sz.width() + extra_right_padding_, y); // Using `extra_right_padding_` as how far to right of checkbox
}
else {
reset_button_->move(rect().width() - (frame_width + sz.width() + kResetIconSize + extra_right_padding_), y);
} }
return QSpinBox::textFromValue(val);
} }
RatingBox::RatingBox(QWidget *parent) RatingBox::RatingBox(QWidget *parent)
: RatingWidget(parent), : RatingWidget(parent),
ExtendedEditor(this) { ExtendedEditor(this, 6) {
clear_button_->hide(); has_clear_button_ = false;
reset_button_->hide(); QObject::connect(reset_button_, &QToolButton::clicked, this, &RatingBox::Reset);
}
void RatingBox::paintEvent(QPaintEvent *e) {
RatingWidget::paintEvent(e);
Paint(this);
}
void RatingBox::resizeEvent(QResizeEvent *e) {
RatingWidget::resizeEvent(e);
Resize();
}
void RatingBox::Resize() {
const QSize sz = widget_->sizeHint();
const int frame_width = widget_->style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
const int y = (rect().height() - sz.height()) / 2 + frame_width; // Plus frame width as inside
reset_button_->move(frame_width + rect().width() - (kResetIconSize + extra_right_padding_), y);
} }

View File

@@ -48,6 +48,7 @@ class LineEditInterface {
virtual ~LineEditInterface() {} virtual ~LineEditInterface() {}
virtual void set_enabled(const bool enabled) = 0; virtual void set_enabled(const bool enabled) = 0;
virtual void set_font(const QFont &font) = 0;
virtual void set_focus() = 0; virtual void set_focus() = 0;
virtual void clear() = 0; virtual void clear() = 0;
@@ -90,7 +91,7 @@ class ExtendedEditor : public LineEditInterface {
protected: protected:
void Paint(QPaintDevice *device); void Paint(QPaintDevice *device);
void Resize(); virtual void Resize();
private: private:
void UpdateButtonGeometry(); void UpdateButtonGeometry();
@@ -121,6 +122,7 @@ class LineEdit : public QLineEdit, public ExtendedEditor {
// ExtendedEditor // ExtendedEditor
void set_enabled(bool enabled) override { QLineEdit::setEnabled(enabled); } void set_enabled(bool enabled) override { QLineEdit::setEnabled(enabled); }
void set_font(const QFont &font) override { QLineEdit::setFont(font); }
QVariant value() const override { return QLineEdit::text(); } QVariant value() const override { return QLineEdit::text(); }
void set_value(const QVariant &value) override { QLineEdit::setText(value.toString()); } void set_value(const QVariant &value) override { QLineEdit::setText(value.toString()); }
@@ -130,8 +132,8 @@ class LineEdit : public QLineEdit, public ExtendedEditor {
void clear() override { QLineEdit::clear(); } void clear() override { QLineEdit::clear(); }
protected: protected:
void paintEvent(QPaintEvent*) override; void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent*) override; void resizeEvent(QResizeEvent *e) override;
private: private:
bool is_rtl() const { return is_rtl_; } bool is_rtl() const { return is_rtl_; }
@@ -155,6 +157,7 @@ class TextEdit : public QPlainTextEdit, public ExtendedEditor {
// ExtendedEditor // ExtendedEditor
void set_enabled(bool enabled) override { QPlainTextEdit::setEnabled(enabled); } void set_enabled(bool enabled) override { QPlainTextEdit::setEnabled(enabled); }
void set_font(const QFont &font) override { QPlainTextEdit::setFont(font); }
QVariant value() const override { return QPlainTextEdit::toPlainText(); } QVariant value() const override { return QPlainTextEdit::toPlainText(); }
void set_value(const QVariant &value) override { QPlainTextEdit::setPlainText(value.toString()); } void set_value(const QVariant &value) override { QPlainTextEdit::setPlainText(value.toString()); }
@@ -164,8 +167,8 @@ class TextEdit : public QPlainTextEdit, public ExtendedEditor {
void clear() override { QPlainTextEdit::clear(); } void clear() override { QPlainTextEdit::clear(); }
protected: protected:
void paintEvent(QPaintEvent*) override; void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent*) override; void resizeEvent(QResizeEvent *e) override;
Q_SIGNALS: Q_SIGNALS:
void Reset(); void Reset();
@@ -185,6 +188,7 @@ class SpinBox : public QSpinBox, public ExtendedEditor {
// ExtendedEditor // ExtendedEditor
void set_enabled(bool enabled) override { QSpinBox::setEnabled(enabled); } void set_enabled(bool enabled) override { QSpinBox::setEnabled(enabled); }
void set_font(const QFont &font) override { QSpinBox::setFont(font); }
QVariant value() const override { return QSpinBox::value(); } QVariant value() const override { return QSpinBox::value(); }
void set_value(const QVariant &value) override { QSpinBox::setValue(value.toInt()); } void set_value(const QVariant &value) override { QSpinBox::setValue(value.toInt()); }
@@ -195,8 +199,8 @@ class SpinBox : public QSpinBox, public ExtendedEditor {
void clear() override { QSpinBox::clear(); } void clear() override { QSpinBox::clear(); }
protected: protected:
void paintEvent(QPaintEvent*) override; void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent*) override; void resizeEvent(QResizeEvent *e) override;
Q_SIGNALS: Q_SIGNALS:
void Reset(); void Reset();
@@ -213,19 +217,23 @@ class CheckBox : public QCheckBox, public ExtendedEditor {
// ExtendedEditor // ExtendedEditor
void set_enabled(bool enabled) override { QCheckBox::setEnabled(enabled); } void set_enabled(bool enabled) override { QCheckBox::setEnabled(enabled); }
void set_font(const QFont &font) override { QCheckBox::setFont(font); }
bool is_empty() const override { return text().isEmpty() || text() == QStringLiteral("0"); } bool is_empty() const override { return text().isEmpty() || text() == QStringLiteral("0"); }
QVariant value() const override { return QCheckBox::isChecked(); } QVariant value() const override { return QCheckBox::isChecked(); }
void set_value(const QVariant &value) override { QCheckBox::setCheckState(value.toBool() ? Qt::Checked : Qt::Unchecked); } void set_value(const QVariant &value) override { QCheckBox::setCheckState(value.toBool() ? Qt::Checked : Qt::Unchecked); }
void set_partially() override { QCheckBox::setCheckState(Qt::PartiallyChecked); } void set_partially() override { QCheckBox::setCheckState(Qt::PartiallyChecked); }
protected:
void Resize() override;
public Q_SLOTS: public Q_SLOTS:
void set_focus() override { QCheckBox::setFocus(); } void set_focus() override { QCheckBox::setFocus(); }
void clear() override { QCheckBox::setChecked(false); } void clear() override { QCheckBox::setChecked(false); }
protected: protected:
void paintEvent(QPaintEvent*) override; void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent*) override; void resizeEvent(QResizeEvent *e) override;
Q_SIGNALS: Q_SIGNALS:
void Reset(); void Reset();
@@ -240,16 +248,27 @@ class RatingBox : public RatingWidget, public ExtendedEditor {
explicit RatingBox(QWidget *parent = nullptr); explicit RatingBox(QWidget *parent = nullptr);
void set_enabled(bool enabled) override { RatingWidget::setEnabled(enabled); } void set_enabled(bool enabled) override { RatingWidget::setEnabled(enabled); }
void set_font(const QFont &font) override { RatingWidget::setFont(font); }
QVariant value() const override { return RatingWidget::rating(); } QVariant value() const override { return RatingWidget::rating(); }
void set_value(const QVariant &value) override { RatingWidget::set_rating(value.toFloat()); } void set_value(const QVariant &value) override { RatingWidget::set_rating(value.toFloat()); }
void set_partially() override { RatingWidget::set_rating(0.0F); } void set_partially() override { RatingWidget::set_rating(0.0F); }
protected:
void Resize() override;
public Q_SLOTS: public Q_SLOTS:
void set_focus() override { RatingWidget::setFocus(); } void set_focus() override { RatingWidget::setFocus(); }
void clear() override {} void clear() override {}
protected:
void paintEvent(QPaintEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
Q_SIGNALS:
void Reset();
}; };
#endif // LINEEDIT_H #endif // LINEEDIT_H

View File

@@ -113,6 +113,7 @@ RatingWidget::RatingWidget(QWidget *parent) : QWidget(parent), rating_(0.0), hov
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
setMouseTracking(true); setMouseTracking(true);
setFocusPolicy(Qt::StrongFocus);
} }
@@ -173,3 +174,31 @@ void RatingWidget::leaveEvent(QEvent *e) {
update(); update();
} }
void RatingWidget::keyPressEvent(QKeyEvent *e) {
constexpr float arrow_incr = 0.5f / RatingPainter::kStarCount;
float rating = -1.0f;
if (e->key() >= Qt::Key_0 && e->key() <= Qt::Key_9) {
rating = qBound(0.0f, static_cast<float>(e->key() - Qt::Key_0) / RatingPainter::kStarCount, 1.0f);
}
else if (e->key() == Qt::Key_Left) {
rating = qBound(0.0f, rating_ - arrow_incr, 1.0f);
}
else if (e->key() == Qt::Key_Right) {
rating = qBound(0.0f, rating_ + arrow_incr, 1.0f);
}
if (rating != -1.0f) {
if (rating != rating_) {
rating_ = rating;
Q_EMIT RatingChanged(rating_);
}
}
else {
QWidget::keyPressEvent(e);
}
}

View File

@@ -63,6 +63,7 @@ class RatingWidget : public QWidget {
void mousePressEvent(QMouseEvent *e) override; void mousePressEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override;
void leaveEvent(QEvent *e) override; void leaveEvent(QEvent *e) override;
void keyPressEvent(QKeyEvent *e) override;
private: private:
RatingPainter painter_; RatingPainter painter_;