Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix glitch in Star rating #12582

Merged
merged 9 commits into from
Jan 19, 2024
2 changes: 1 addition & 1 deletion res/skins/LateNight/decks/deck_settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<Children>
<StarRating>
<TooltipId>starrating</TooltipId>
<Size>75f,15f</Size>
<Size>105f,15f</Size>
<Channel><Variable name="ChanNum"/></Channel>
</StarRating>
</Children>
Expand Down
17 changes: 1 addition & 16 deletions src/library/colordelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,6 @@ void ColorDelegate::paintItem(

// Draw a border if the color cell has focus
if (option.state & QStyle::State_HasFocus) {
// This uses a color from the stylesheet:
// WTrackTableView {
// qproperty-focusBorderColor: red;
// }
QPen borderPen(
m_pFocusBorderColor,
1,
Qt::SolidLine,
Qt::SquareCap);
painter->setPen(borderPen);
painter->setBrush(QBrush(Qt::transparent));
painter->drawRect(
option.rect.left(),
option.rect.top(),
option.rect.width() - 1,
option.rect.height() - 1);
drawBorder(painter, m_pFocusBorderColor, option.rect);
}
}
17 changes: 1 addition & 16 deletions src/library/coverartdelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,21 +163,6 @@ void CoverArtDelegate::paintItem(

// Draw a border if the cover art cell has focus
if (option.state & QStyle::State_HasFocus) {
// This uses a color from the stylesheet:
// WTrackTableView {
// qproperty-focusBorderColor: red;
// }
QPen borderPen(
m_pFocusBorderColor,
1,
Qt::SolidLine,
Qt::SquareCap);
painter->setPen(borderPen);
painter->setBrush(QBrush(Qt::transparent));
painter->drawRect(
option.rect.left(),
option.rect.top(),
option.rect.width() - 1,
option.rect.height() - 1);
drawBorder(painter, m_pFocusBorderColor, option.rect);
}
}
4 changes: 3 additions & 1 deletion src/library/stardelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ QWidget* StarDelegate::createEditor(QWidget* parent,
QStyleOptionViewItem newOption = option;
initStyleOption(&newOption, index);

StarEditor* editor = new StarEditor(parent, m_pTableView, index, newOption);
StarEditor* editor =
new StarEditor(parent, m_pTableView, index, newOption, m_pFocusBorderColor);
connect(editor,
&StarEditor::editingFinished,
this,
Expand Down Expand Up @@ -76,6 +77,7 @@ void StarDelegate::cellEntered(const QModelIndex& index) {
// StarRating.
if (index.data().canConvert<StarRating>()) {
if (m_isOneCellInEditMode) {
// Don't close other editors when hovering the stars cell!
m_pTableView->closePersistentEditor(m_currentEditedCellIndex);
}
m_pTableView->openPersistentEditor(index);
Expand Down
148 changes: 80 additions & 68 deletions src/library/stareditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

#include <QItemSelectionModel>
#include <QMouseEvent>
#include <QPainter>
#include <QTableView>

#include "library/starrating.h"
#include "library/tableitemdelegate.h"
#include "moc_stareditor.cpp"
#include "util/painterscope.h"

// We enable mouse tracking on the widget so we can follow the cursor even
// when the user doesn't hold down any mouse button. We also turn on
Expand All @@ -18,103 +19,114 @@
///
/// The class has been adapted from the official "Star Delegate Example",
/// see http://doc.trolltech.com/4.5/itemviews-stardelegate.html
StarEditor::StarEditor(QWidget *parent, QTableView* pTableView,
const QModelIndex& index,
const QStyleOptionViewItem& option)
StarEditor::StarEditor(QWidget* parent,
QTableView* pTableView,
const QModelIndex& index,
const QStyleOptionViewItem& option,
const QColor& focusBorderColor)
: QWidget(parent),
m_pTableView(pTableView),
m_index(index),
m_styleOption(option) {
m_styleOption(option),
m_pFocusBorderColor(focusBorderColor),
m_starCount(StarRating::kMinStarCount) {
DEBUG_ASSERT(m_pTableView);
setMouseTracking(true);
installEventFilter(this);
}

QSize StarEditor::sizeHint() const {
return m_starRating.sizeHint();
}

// static
void StarEditor::renderHelper(QPainter* painter,
QTableView* pTableView,
const QStyleOptionViewItem& option,
StarRating* pStarRating) {
PainterScope painterScope(painter);
void StarEditor::paintEvent(QPaintEvent*) {
// If a StarEditor is open, by definition the mouse is hovering over us.
m_styleOption.state |= QStyle::State_MouseOver;
m_styleOption.rect = rect();

painter->setClipRect(option.rect);
// If the editor cell is selected set the respective flag so we can use the
// palette's 'HighlightedText' font color for the brush StarRating will use
// to fill the star/diamond polygons with.
QItemSelectionModel* selectionModel = m_pTableView->selectionModel();
if (selectionModel && selectionModel->isSelected(m_index)) {
m_styleOption.state |= QStyle::State_Selected;
}

if (pTableView != nullptr) {
QStyle* style = pTableView->style();
if (style != nullptr) {
style->drawControl(QStyle::CE_ItemViewItem, &option, painter,
pTableView);
}
QPainter painter(this);

painter.setClipRect(m_styleOption.rect);

// Draw standard item with the table view's style
QStyle* style = m_pTableView->style();
if (style) {
style->drawControl(QStyle::CE_ItemViewItem, &m_styleOption, &painter, m_pTableView);
}

// Draw a border if the color cell has focus
if (m_styleOption.state & QStyle::State_Selected) {
// QPainterScope in drawBorder() and shift down?
TableItemDelegate::drawBorder(&painter, m_pFocusBorderColor, m_styleOption.rect);
}

// Starrating scales the painter so do this after painting the border.
// Set the palette appropriately based on whether the row is selected or
// not. We also have to check if it is inactive or not and use the
// appropriate ColorGroup.
QPalette::ColorGroup cg = option.state & QStyle::State_Enabled
? QPalette::Normal : QPalette::Disabled;
if (cg == QPalette::Normal && !(option.state & QStyle::State_Active)) {
QPalette::ColorGroup cg = m_styleOption.state & QStyle::State_Enabled
? QPalette::Normal
: QPalette::Disabled;
if (cg == QPalette::Normal && !(m_styleOption.state & QStyle::State_Active)) {
cg = QPalette::Inactive;
}

if (option.state & QStyle::State_Selected) {
painter->setBrush(option.palette.color(cg, QPalette::HighlightedText));
if (m_styleOption.state & QStyle::State_Selected) {
painter.setBrush(m_styleOption.palette.color(cg, QPalette::HighlightedText));
} else {
painter->setBrush(option.palette.color(cg, QPalette::Text));
painter.setBrush(m_styleOption.palette.color(cg, QPalette::Text));
}

pStarRating->paint(painter, option.rect);
m_starRating.paint(&painter, m_styleOption.rect);
}

void StarEditor::paintEvent(QPaintEvent*) {
// If a StarEditor is open, by definition the mouse is hovering over us.
m_styleOption.state |= QStyle::State_MouseOver;
m_styleOption.rect = rect();

if (m_pTableView) {
QItemSelectionModel* selectionModel = m_pTableView->selectionModel();
if (selectionModel && selectionModel->isSelected(m_index)) {
m_styleOption.state |= QStyle::State_Selected;
}
bool StarEditor::eventFilter(QObject* obj, QEvent* event) {
switch (event->type()) {
case QEvent::Leave:
case QEvent::ContextMenu: {
// Note: it seems with Qt5 we do not reliably get a Leave event when
// invoking the track menu via right click, so reset the rating now.
// The event is forwarded to parent QTableView.
resetRating();
break;
}

QPainter painter(this);
renderHelper(&painter, m_pTableView, m_styleOption, &m_starRating);
}

void StarEditor::mouseMoveEvent(QMouseEvent *event) {
case QEvent::MouseButtonRelease: {
emit editingFinished();
break;
}
case QEvent::MouseMove: {
// Change rating only if no button is pressed.
// This allows dragging the row also by grabbing the star cell
QMouseEvent* me = static_cast<QMouseEvent*>(event);
if (me->buttons().testFlag(Qt::NoButton)) {
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
const int eventPosition = static_cast<int>(event->position().x());
const int eventPosition = static_cast<int>(me->position().x());
#else
const int eventPosition = event->x();
const int eventPosition = me->x();
#endif
int star = starAtPosition(eventPosition);

if (star != m_starRating.starCount() && star != -1) {
m_starRating.setStarCount(star);
update();
int star = m_starRating.starAtPosition(eventPosition, m_styleOption.rect);

if (star <= StarRating::kInvalidStarCount) {
resetRating();
} else if (star != m_starRating.starCount()) {
// Apply star rating if it changed
m_starRating.setStarCount(star);
update();
}
}
break;
}
}

void StarEditor::leaveEvent(QEvent*) {
m_starRating.setStarCount(0);
update();
}

void StarEditor::mouseReleaseEvent(QMouseEvent* /* event */) {
emit editingFinished();
}

int StarEditor::starAtPosition(int x) {
// If the mouse is very close to the left edge, set 0 stars.
if (x < m_starRating.sizeHint().width() * 0.05) {
return 0;
default:
break;
}
int star = (x / (m_starRating.sizeHint().width() / m_starRating.maxStarCount())) + 1;

if (star <= 0 || star > m_starRating.maxStarCount()) {
return 0;
}
return star;
return QWidget::eventFilter(obj, event);
}
30 changes: 18 additions & 12 deletions src/library/stareditor.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,35 +12,41 @@ class QTableView;
class StarEditor : public QWidget {
Q_OBJECT
public:
StarEditor(QWidget* parent, QTableView* pTableView,
const QModelIndex& index,
const QStyleOptionViewItem& option);
StarEditor(QWidget* parent,
QTableView* pTableView,
const QModelIndex& index,
const QStyleOptionViewItem& option,
const QColor& focusBorderColor);

QSize sizeHint() const override;
void setStarRating(const StarRating& starRating) {
m_starRating = starRating;
int stars = m_starRating.starCount();
VERIFY_OR_DEBUG_ASSERT(m_starRating.verifyStarCount(stars)) {
return;
}
m_starCount = stars;
}
StarRating starRating() { return m_starRating; }

static void renderHelper(QPainter* painter, QTableView* pTableView,
const QStyleOptionViewItem& option,
StarRating* pStarRating);

signals:
void editingFinished();

protected:
void paintEvent(QPaintEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
//if the mouse leaves the editing index set starCount to 0
void leaveEvent(QEvent*) override;

bool eventFilter(QObject* obj, QEvent* event) override;

private:
int starAtPosition(int x);
void resetRating() {
m_starRating.setStarCount(m_starCount);
update();
}

QTableView* m_pTableView;
QModelIndex m_index;
QStyleOptionViewItem m_styleOption;
QColor m_pFocusBorderColor;
StarRating m_starRating;
int m_starCount;
};
41 changes: 36 additions & 5 deletions src/library/starrating.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <QRect>

#include "util/math.h"
#include "util/painterscope.h"

// Magic number? Explain what this factor affects and how
constexpr int PaintingScaleFactor = 15;
Expand All @@ -13,8 +14,7 @@ StarRating::StarRating(
int maxStarCount)
: m_starCount(starCount),
m_maxStarCount(maxStarCount) {
DEBUG_ASSERT(m_starCount >= kMinStarCount);
DEBUG_ASSERT(m_starCount <= m_maxStarCount);
DEBUG_ASSERT(verifyStarCount(m_starCount));
// 1st star cusp at 0° of the unit circle whose center is shifted to adapt the 0,0-based paint area
m_starPolygon << QPointF(1.0, 0.5);
for (int i = 1; i < 5; ++i) {
Expand All @@ -33,16 +33,20 @@ QSize StarRating::sizeHint() const {
return PaintingScaleFactor * QSize(m_maxStarCount, 1);
}

void StarRating::paint(QPainter *painter, const QRect &rect) const {
void StarRating::paint(QPainter* painter, const QRect& rect) const {
PainterScope painterScope(painter);
// Assume the painter is configured with the right brush.
painter->setRenderHint(QPainter::Antialiasing, true);
painter->setPen(Qt::NoPen);

// Center vertically inside the table cell, and also center horizontally
// if the cell is wider than the minimum stars width.
int xOffset = std::max((rect.width() - sizeHint().width()) / 2, 0);
int yOffset = (rect.height() - PaintingScaleFactor) / 2;
painter->translate(rect.x(), rect.y() + yOffset);
painter->translate(rect.x() + xOffset, rect.y() + yOffset);
painter->scale(PaintingScaleFactor, PaintingScaleFactor);

//determine number of stars that are possible to paint
// Determine number of stars that are possible to paint
int n = rect.width() / PaintingScaleFactor;

for (int i = 0; i < m_maxStarCount && i < n; ++i) {
Expand All @@ -54,3 +58,30 @@ void StarRating::paint(QPainter *painter, const QRect &rect) const {
painter->translate(1.0, 0.0);
}
}

int StarRating::starAtPosition(int x, const QRect& rect) const {
// The star rating is drawn centered in the parent (WStarrating or
// cell of StarDelegate, so we need to shift the x input as well.
int starsWidth = sizeHint().width();
int xOffset = std::max((rect.width() - starsWidth) / 2, 0);
// Only shift if the parent is wider than the star rating
x -= std::max(xOffset, 0);

// Return invalid if the pointer left the star rectangle at either side.
// If the the parent is wider than the star rating, add a half star margin
// at the left to simplify setting 0.
double leftVoid = xOffset > starsWidth * 0.05 ? starsWidth * -0.05 : 0;
if (x < leftVoid || x >= starsWidth) {
return StarRating::kInvalidStarCount;
} else if (x < starsWidth * 0.05) {
// If the pointer is very close to the left edge, set 0 stars.
return 0;
}

int star = (x / (starsWidth / maxStarCount())) + 1;

if (star <= 0 || star > maxStarCount()) {
return 0;
}
return star;
}
Loading
Loading