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): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
# TODO: make this differ by index self.baseXOffsets: dict[str, int] = {}
self.baseXOffset = 0 self.baseYOffsets: dict[str, int] = {}
self.baseYOffset = 0
self.verticalAlign = TextSegmentDelegateVerticalAlign.Middle 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"]): def setVerticalAlign(self, align: Literal["top", "middle", "bottom"]):
if not isinstance(align, str) and align not in ["top", "middle", "bottom"]: if not isinstance(align, str) and align not in ["top", "middle", "bottom"]:
raise ValueError( raise ValueError(
@ -60,12 +76,14 @@ class TextSegmentDelegate(QStyledItemDelegate):
]: ]:
return [] return []
def sizeHint(self, option, index) -> QSize: def textsSizeHint(self, option: QStyleOptionViewItem, index: QModelIndex) -> QSize:
width = 0 width = 0
height = self.VerticalPadding height = 0
fm: QFontMetrics = option.fontMetrics fm: QFontMetrics = option.fontMetrics
for line in self.getTextSegments(index, option): segments = self.getTextSegments(index, option)
lineWidth = 4 * self.HorizontalPadding for i in range(len(segments)):
line = segments[i]
lineWidth = 2 * self.HorizontalPadding
lineHeight = 0 lineHeight = 0
for textFrag in line: for textFrag in line:
font = textFrag.get(self.FontRole) font = textFrag.get(self.FontRole)
@ -76,14 +94,30 @@ class TextSegmentDelegate(QStyledItemDelegate):
lineWidth += textWidth lineWidth += textWidth
lineHeight = max(lineHeight, textHeight) lineHeight = max(lineHeight, textHeight)
width = max(lineWidth, width) width = max(lineWidth, width)
height += lineHeight + self.VerticalPadding height += lineHeight
if i != len(segments) - 1:
height += self.VerticalPadding
return QSize(width, height) 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): 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): 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: if self.verticalAlign != TextSegmentDelegateVerticalAlign.Top:
paintAreaSize: QSize = option.rect.size() paintAreaSize: QSize = option.rect.size()
delegateSize = self.sizeHint(option, index) delegateSize = self.sizeHint(option, index)
@ -94,7 +128,11 @@ class TextSegmentDelegate(QStyledItemDelegate):
return baseY return baseY
def textMaxWidth(self, option: QStyleOptionViewItem, index: QModelIndex): 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( def paint(
self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex

View File

@ -1,7 +1,7 @@
from arcaea_offline.models import Chart, Difficulty, Song from arcaea_offline.models import Chart, Difficulty, Song
from arcaea_offline.utils.rating import rating_class_to_short_text, rating_class_to_text from arcaea_offline.utils.rating import rating_class_to_short_text, rating_class_to_text
from PIL import Image 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.QtGui import QColor, QPainter, QPixmap
from PySide6.QtWidgets import ( from PySide6.QtWidgets import (
QFrame, QFrame,
@ -173,7 +173,9 @@ class ChartDelegate(TextSegmentDelegate):
def sizeHint(self, option, index): def sizeHint(self, option, index):
size = super().sizeHint(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 return size
def paint(self, painter, option, index): def paint(self, painter, option, index):
@ -193,12 +195,13 @@ class ChartDelegate(TextSegmentDelegate):
jacketPath = "__TEXT_ONLY__" jacketPath = "__TEXT_ONLY__"
if jacketPath == "__TEXT_ONLY__": if jacketPath == "__TEXT_ONLY__":
self.setBaseXOffset(index, 0)
super().paint(painter, option, index) super().paint(painter, option, index)
return return
textSizeHint = super().sizeHint(option, index) textsSizeHint = super().textsSizeHint(option, index)
jacketSize = textSizeHint.height() jacketSize = textsSizeHint.height()
self.baseXOffset = self.HorizontalPadding + jacketSize self.setBaseXOffset(index, self.HorizontalPadding + jacketSize)
jacketSizeTuple = (jacketSize, jacketSize) jacketSizeTuple = (jacketSize, jacketSize)
if jacketPath: if jacketPath:
@ -214,11 +217,21 @@ class ChartDelegate(TextSegmentDelegate):
.toqpixmap() .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.save()
painter.setRenderHint(QPainter.RenderHint.LosslessImageRendering, True) painter.setRenderHint(QPainter.RenderHint.LosslessImageRendering, True)
painter.setRenderHint(QPainter.RenderHint.Antialiasing, True) painter.setRenderHint(QPainter.RenderHint.Antialiasing, True)
pixmapBaseY = self.baseY(option, index)
painter.drawPixmap( painter.drawPixmap(
option.rect.x() + self.HorizontalPadding, self.baseY(option, index), pixmap option.rect.x() + self.HorizontalPadding,
pixmapBaseY,
pixmap,
) )
painter.restore() painter.restore()
super().paint(painter, option, index) super().paint(painter, option, index)