Skip to content

Commit

Permalink
Merge pull request #12582 from ronso0/stars-leave-reset-fix
Browse files Browse the repository at this point in the history
fix glitch in Star rating
  • Loading branch information
daschuer committed Jan 19, 2024
2 parents 6841ecf + bff3f2a commit 9ee9f53
Show file tree
Hide file tree
Showing 12 changed files with 197 additions and 159 deletions.
2 changes: 1 addition & 1 deletion res/skins/LateNight/decks/deck_settings.xml
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
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
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
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
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
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
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;
}

0 comments on commit 9ee9f53

Please sign in to comment.