impr: TextSegmentDelegate improvements

This commit is contained in:
283375 2023-10-14 16:52:41 +08:00
parent cf913d296e
commit 8628399469
Signed by: 283375
SSH Key Fingerprint: SHA256:UcX0qg6ZOSDOeieKPGokA5h7soykG61nz2uxuQgVLSk
2 changed files with 68 additions and 17 deletions

View File

@ -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

View File

@ -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)