From 862839946940d87121d02fcef072b5dbce855da0 Mon Sep 17 00:00:00 2001 From: 283375 Date: Sat, 14 Oct 2023 16:52:41 +0800 Subject: [PATCH] impr: `TextSegmentDelegate` improvements --- ui/extends/shared/delegates/base.py | 60 ++++++++++++++++---- ui/extends/shared/delegates/chartDelegate.py | 25 ++++++-- 2 files changed, 68 insertions(+), 17 deletions(-) diff --git a/ui/extends/shared/delegates/base.py b/ui/extends/shared/delegates/base.py index b1e7243..42b4c4a 100644 --- a/ui/extends/shared/delegates/base.py +++ b/ui/extends/shared/delegates/base.py @@ -25,12 +25,28 @@ class TextSegmentDelegate(QStyledItemDelegate): def __init__(self, parent=None): super().__init__(parent) - # TODO: make this differ by index - self.baseXOffset = 0 - self.baseYOffset = 0 + self.baseXOffsets: dict[str, int] = {} + self.baseYOffsets: dict[str, int] = {} self.verticalAlign = TextSegmentDelegateVerticalAlign.Middle + def indexOffsetKey(self, index: QModelIndex): + return f"{index.row()},{index.column()}" + + def setBaseXOffset(self, index: QModelIndex, offset: int): + key = self.indexOffsetKey(index) + if not offset: + self.baseXOffsets.pop(key, None) + else: + self.baseXOffsets[key] = offset + + def setBaseYOffset(self, index: QModelIndex, offset: int): + key = self.indexOffsetKey(index) + if not offset: + self.baseYOffsets.pop(key, None) + else: + self.baseYOffsets[key] = offset + def setVerticalAlign(self, align: Literal["top", "middle", "bottom"]): if not isinstance(align, str) and align not in ["top", "middle", "bottom"]: raise ValueError( @@ -60,12 +76,14 @@ class TextSegmentDelegate(QStyledItemDelegate): ]: return [] - def sizeHint(self, option, index) -> QSize: + def textsSizeHint(self, option: QStyleOptionViewItem, index: QModelIndex) -> QSize: width = 0 - height = self.VerticalPadding + height = 0 fm: QFontMetrics = option.fontMetrics - for line in self.getTextSegments(index, option): - lineWidth = 4 * self.HorizontalPadding + segments = self.getTextSegments(index, option) + for i in range(len(segments)): + line = segments[i] + lineWidth = 2 * self.HorizontalPadding lineHeight = 0 for textFrag in line: font = textFrag.get(self.FontRole) @@ -76,14 +94,30 @@ class TextSegmentDelegate(QStyledItemDelegate): lineWidth += textWidth lineHeight = max(lineHeight, textHeight) width = max(lineWidth, width) - height += lineHeight + self.VerticalPadding + height += lineHeight + if i != len(segments) - 1: + height += self.VerticalPadding return QSize(width, height) + def sizeHint(self, option: QStyleOptionViewItem, index: QModelIndex) -> QSize: + width = self.HorizontalPadding * 2 + height = self.VerticalPadding * 2 + textsSizeHint = self.textsSizeHint(option, index) + return QSize(textsSizeHint.width() + width, textsSizeHint.height() + height) + def baseX(self, option: QStyleOptionViewItem, index: QModelIndex): - return option.rect.x() + self.HorizontalPadding + self.baseXOffset + return ( + option.rect.x() + + self.HorizontalPadding + + self.baseXOffsets.get(self.indexOffsetKey(index), 0) + ) def baseY(self, option: QStyleOptionViewItem, index: QModelIndex): - baseY = option.rect.y() + self.VerticalPadding + self.baseYOffset + baseY = ( + option.rect.y() + + self.VerticalPadding + + self.baseYOffsets.get(self.indexOffsetKey(index), 0) + ) if self.verticalAlign != TextSegmentDelegateVerticalAlign.Top: paintAreaSize: QSize = option.rect.size() delegateSize = self.sizeHint(option, index) @@ -94,7 +128,11 @@ class TextSegmentDelegate(QStyledItemDelegate): return baseY def textMaxWidth(self, option: QStyleOptionViewItem, index: QModelIndex): - return option.rect.width() - (2 * self.HorizontalPadding) - self.baseXOffset + return ( + option.rect.width() + - (2 * self.HorizontalPadding) + - self.baseXOffsets.get(self.indexOffsetKey(index), 0) + ) def paint( self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex diff --git a/ui/extends/shared/delegates/chartDelegate.py b/ui/extends/shared/delegates/chartDelegate.py index d5b4cdc..cb9d738 100644 --- a/ui/extends/shared/delegates/chartDelegate.py +++ b/ui/extends/shared/delegates/chartDelegate.py @@ -1,7 +1,7 @@ from arcaea_offline.models import Chart, Difficulty, Song from arcaea_offline.utils.rating import rating_class_to_short_text, rating_class_to_text from PIL import Image -from PySide6.QtCore import QModelIndex, Qt, Signal +from PySide6.QtCore import QModelIndex, QRect, Qt, Signal from PySide6.QtGui import QColor, QPainter, QPixmap from PySide6.QtWidgets import ( QFrame, @@ -173,7 +173,9 @@ class ChartDelegate(TextSegmentDelegate): def sizeHint(self, option, index): size = super().sizeHint(option, index) - size.setWidth(size.width() + self.HorizontalPadding + size.height()) + minWidth = size.height() + 2 * self.HorizontalPadding # jacket size + width = size.width() + self.HorizontalPadding + size.height() + size.setWidth(max(minWidth, width)) return size def paint(self, painter, option, index): @@ -193,12 +195,13 @@ class ChartDelegate(TextSegmentDelegate): jacketPath = "__TEXT_ONLY__" if jacketPath == "__TEXT_ONLY__": + self.setBaseXOffset(index, 0) super().paint(painter, option, index) return - textSizeHint = super().sizeHint(option, index) - jacketSize = textSizeHint.height() - self.baseXOffset = self.HorizontalPadding + jacketSize + textsSizeHint = super().textsSizeHint(option, index) + jacketSize = textsSizeHint.height() + self.setBaseXOffset(index, self.HorizontalPadding + jacketSize) jacketSizeTuple = (jacketSize, jacketSize) if jacketPath: @@ -214,11 +217,21 @@ class ChartDelegate(TextSegmentDelegate): .toqpixmap() ) + pixmapAvailableWidth = option.rect.width() - self.HorizontalPadding + pixmapAvailableHeight = option.rect.height() + + if pixmapAvailableWidth < jacketSize or pixmapAvailableHeight < jacketSize: + cropRect = QRect(0, 0, pixmapAvailableWidth, pixmapAvailableHeight) + pixmap = pixmap.copy(cropRect) + painter.save() painter.setRenderHint(QPainter.RenderHint.LosslessImageRendering, True) painter.setRenderHint(QPainter.RenderHint.Antialiasing, True) + pixmapBaseY = self.baseY(option, index) painter.drawPixmap( - option.rect.x() + self.HorizontalPadding, self.baseY(option, index), pixmap + option.rect.x() + self.HorizontalPadding, + pixmapBaseY, + pixmap, ) painter.restore() super().paint(painter, option, index)