This commit is contained in:
Christopher T. Johnson
2024-04-16 11:50:26 -04:00
parent f1ad24d70a
commit 51b1121176
6 changed files with 422 additions and 274 deletions

View File

@@ -1,27 +1,40 @@
import re
from typing import Any, Optional, Self, cast, overload
import re
from PyQt6.QtCore import QMargins, QPoint, QRect, QSize, QUrl, Qt, pyqtSignal
from PyQt6.QtGui import QColor, QFont, QFontMetrics, QMouseEvent, QPaintEvent, QPainter, QResizeEvent, QTextOption, QTransform, QBrush
from typing import Any, Callable, Optional, Self, cast, overload
from PyQt6.QtCore import QMargins, QPoint, QRect, QSize, Qt, QUrl, pyqtSignal
from PyQt6.QtGui import (
QBrush,
QColor,
QFont,
QFontMetrics,
QMouseEvent,
QPainter,
QPaintEvent,
QResizeEvent,
QTextOption,
QTransform,
)
from PyQt6.QtWidgets import QWidget
class Fragment:
"""A fragment of text to be displayed"""
_indentAmount = 35
def __init__(
self,
which: str|Self,
font: QFont|None = None,
audio: str = "",
color: Optional[QColor] = None,
asis: bool = False,
self,
which: str | Self,
font: QFont | None = None,
audio: str = "",
color: Optional[QColor] = None,
asis: bool = False,
) -> None:
if isinstance(which, Fragment):
for k,v in which.__dict__.items():
for k, v in which.__dict__.items():
self.__dict__[k] = v
return
self._text:str = which
self._text: str = which
if font is None:
raise TypeError("Missing required parameter 'font'")
self._font = font
@@ -63,13 +76,14 @@ class Fragment:
return f"({self._position.x()}, {self._position.y()}): {self._text}"
@overload
def paintEvent(self, widthSrc:int) -> QSize:
def paintEvent(self, widthSrc: int) -> QSize:
...
@overload
def paintEvent(self, widthSrc: QPainter) -> int:
...
def paintEvent(self, widthSrc) -> int|QSize:
def paintEvent(self, widthSrc: QPainter | int) -> int | QSize:
if isinstance(widthSrc, QPainter):
viewportWidth = widthSrc.viewport().width()
painter = widthSrc
@@ -77,28 +91,21 @@ class Fragment:
viewportWidth = widthSrc
painter = None
fm = QFontMetrics(self._font)
top = (
self._position.y()
+ fm.descent()
- fm.height()
)
top = self._position.y() + fm.descent() - fm.height()
left = self._position.x()
width = viewportWidth - left
height = 2000
rect = QRect(left, top, width, height)
indent = self._indent * self._indentAmount
flags = (
Qt.AlignmentFlag.AlignLeft
| Qt.AlignmentFlag.AlignBaseline
)
flags = Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignBaseline
boundingNoWrap = fm.boundingRect(
rect, flags|Qt.TextFlag.TextSingleLine, self._text
rect, flags | Qt.TextFlag.TextSingleLine, self._text
)
bounding = fm.boundingRect(
rect, flags|Qt.TextFlag.TextWordWrap, self._text
rect, flags | Qt.TextFlag.TextWordWrap, self._text
)
text = self._text
remainingText = ''
remainingText = ""
if boundingNoWrap.height() < bounding.height():
#
# This is not optimal, but it is only a few iterations
@@ -107,39 +114,29 @@ class Fragment:
char = 0
pos = rect.x()
while pos < rect.right():
if text[char] == ' ':
if text[char] == " ":
lastSpace = char
pos += fm.horizontalAdvance(
text[char]
)
pos += fm.horizontalAdvance(text[char])
char += 1
if lastSpace > 0:
remainingText = text[lastSpace+1:]
remainingText = text[lastSpace + 1 :]
text = text[:lastSpace]
size = boundingNoWrap.size()
boundingNoWrap = fm.boundingRect(
rect, flags|Qt.TextFlag.TextSingleLine, text
rect, flags | Qt.TextFlag.TextSingleLine, text
)
rect.setSize(boundingNoWrap.size())
if remainingText != '':
if remainingText != "":
top += size.height()
remainingRect = QRect(
indent, top,
viewportWidth - indent, height
)
remainingRect = QRect(indent, top, viewportWidth - indent, height)
boundingRemaingRect = fm.boundingRect(
remainingRect, flags | Qt.TextFlag.TextWordWrap, remainingText
)
size = size.grownBy(
QMargins(
0,0,0, boundingRemaingRect.height()
)
)
size = size.grownBy(QMargins(0, 0, 0, boundingRemaingRect.height()))
remainingRect.setSize(boundingRemaingRect.size())
size = size.grownBy(self._margin)
size = size.grownBy(self._border)
@@ -163,12 +160,14 @@ class Fragment:
brush.setColor(self._background)
brush.setStyle(Qt.BrushStyle.SolidPattern)
painter.setBrush(brush)
painter.fillRect(rect,brush)
painter.fillRect(rect, brush)
painter.drawText(rect, flags, text)
if remainingText:
if self._background.isValid():
painter.fillRect(remainingRect, brush)
painter.drawText(remainingRect, flags|Qt.TextFlag.TextWordWrap, remainingText)
painter.drawText(
remainingRect, flags | Qt.TextFlag.TextWordWrap, remainingText
)
painter.restore()
return size.height()
@@ -310,6 +309,7 @@ class Fragment:
def setBackground(self, color: QColor) -> None:
self._background = color
return
def setIndent(self, indent: int) -> None:
self._indent = indent
return
@@ -365,8 +365,10 @@ class Fragment:
def pixelIndent(self) -> int:
return self._indent * self._indentAmount
class Line:
parseText = None
def __init__(self) -> None:
self._maxHeight = -1
self._baseLine = -1
@@ -379,11 +381,12 @@ class Line:
"|".join([x.text() for x in self._fragments])
+ f"|{self._maxHeight}"
)
@classmethod
def setParseText(cls, call) -> None:
def setParseText(cls, call: Callable) -> None:
cls.parseText = call
return
def paintEvent(self, painter: QPainter) -> int:
#
# we do not have an event field because we are not a true widget
@@ -395,12 +398,16 @@ class Line:
lineSpacing = ls
return lineSpacing
def addFragment(self, frags: Fragment|list[Fragment],) -> None:
def addFragment(
self,
frags: Fragment | list[Fragment],
) -> None:
SPEAKER = "\U0001F508"
if not isinstance(frags, list):
frags = [frags, ]
frags = [
frags,
]
for frag in frags:
if frag.audio().isValid():
frag.setText(frag.text() + " " + SPEAKER)
@@ -485,6 +492,7 @@ class Line:
def getLineSpacing(self) -> int:
return self._leading + self._maxHeight
class Definition(QWidget):
pronounce = pyqtSignal(str)
@@ -499,7 +507,7 @@ class Definition(QWidget):
def setWord(self, word: Any) -> None:
self._word = word
lines:list[Line] = word.get_def()
lines: list[Line] = word.get_def()
assert lines is not None
self._lines = lines
self._buttons: list[Fragment] = []