class NDocumentLabel(QFrame): """Simple widget to draw QTextDocument. sizeHint will always related to fixed number of lines set. If font fallback happen, it may look bad.""" def __init__(self, parent=None, lines=None, **kwargs): super().__init__(parent, **kwargs) self._lines = self._heightHint = None self.doc = NTextDocument(self) self.doc.setDocumentMargin(0) self.doc.setUndoRedoEnabled(False) self.setLines(lines if lines else 4) self.doc.documentLayout().setPaintDevice(self) # make difference on high DPI def setFont(self, f): self.doc.setDefaultFont(f) super().setFont(f) self.setLines(self._lines) # refresh size hint def setText(self, text, formats): self.doc.setText(text, formats) # delete exceed lines here using QTextCursor will slow down def setLines(self, lines): self._lines = lines self.doc.setText('\n' * (lines - 1), None) self._heightHint = int(self.doc.size().height()) self.updateGeometry() def paintEvent(self, event): painter = QPainter(self) rect = self.contentsRect() painter.translate(rect.topLeft()) rect.moveTo(0, 0) # become clip rect self.doc.drawContentsPalette(painter, rect, self.palette()) def resizeEvent(self, event): self.doc.setTextWidth(self.contentsRect().width()) super().resizeEvent(event) def sizeHint(self): __, top, __, bottom = self.getContentsMargins() return QSize(-1, self._heightHint + top + bottom)
class DiaryListDelegate(QStyledItemDelegate): """ItemDelegate of old theme 'one-pixel-rect' for DiaryList, Using 'traditional' painting method compared to colorful theme.""" def __init__(self): super().__init__() # don't pass parent because of mem problem # Because some fonts have much more space at top and bottom, we use ascent instead # of height, and add it with a small number. magic = int(4 * scaleRatio) self.title_h = max(font.title_m.ascent(), font.datetime_m.ascent()) + magic self.titleArea_h = self.title_h + 4 self.text_h = font.text_m.lineSpacing() * settings['Main'].getint('previewLines') self.tagPath_h = font.default_m.ascent() + magic self.tag_h = self.tagPath_h + 4 self.dt_w = font.datetime_m.width(datetimeTrans('2000-01-01 00:00')) + 40 self.all_h = None # updated in sizeHint before each item being painting # doc is used to draw text(diary's body) self.doc = NTextDocument() self.doc.setDefaultFont(font.text) self.doc.setUndoRedoEnabled(False) self.doc.setDocumentMargin(0) self.doc.documentLayout().setPaintDevice(qApp.desktop()) # refer actual list will cause segfault # setup colors self.c_text = Qt.black self.c_bg = QColor(255, 236, 176) self.c_border = QColor(214, 172, 41) self.c_inActBg = QColor(255, 236, 176, 40) self.c_gray = QColor(93, 73, 57) def paint(self, painter, option, index): x, y, w = option.rect.x(), option.rect.y(), option.rect.width()-1 row = index.row() dt, text, title, tags, formats = (index.sibling(row, i).data() for i in range(1, 6)) selected = bool(option.state & QStyle.State_Selected) active = bool(option.state & QStyle.State_Active) # draw border and background painter.setPen(self.c_border) painter.setBrush(self.c_bg if selected and active else self.c_inActBg) painter.drawRect(x+1, y, w-2, self.all_h) # outer border if selected: # draw inner border pen = QPen() pen.setStyle(Qt.DashLine) pen.setColor(self.c_gray) painter.setPen(pen) painter.drawRect(x+2, y+1, w-4, self.all_h-2) # draw datetime and title painter.setPen(self.c_gray) painter.drawLine(x+10, y+self.titleArea_h, x+w-10, y+self.titleArea_h) painter.setPen(self.c_text) painter.setFont(font.datetime) painter.drawText(x+14, y+self.titleArea_h-self.title_h, self.dt_w, self.title_h, Qt.AlignVCenter, datetimeTrans(dt)) if title: painter.setFont(font.title) title_w = w - self.dt_w - 13 title = font.title_m.elidedText(title, Qt.ElideRight, title_w) painter.drawText(x+self.dt_w, y+self.titleArea_h-self.title_h, title_w, self.title_h, Qt.AlignVCenter | Qt.AlignRight, title) # draw text self.doc.setText(text, formats) self.doc.setTextWidth(w-26) painter.translate(x+14, y+self.titleArea_h+2) self.doc.drawContentsColor(painter, QRect(0, 0, w-26, self.text_h), self.c_text) painter.resetTransform() # draw tags if tags: painter.setPen(self.c_gray) painter.setFont(font.default) painter.translate(x + 15, y+self.titleArea_h+6+self.text_h) real_x, max_x = x+15, w-10 for t in tags.split(): oneTag_w = font.default_m.width(t) + 4 real_x += oneTag_w + 15 if real_x > max_x: break tagPath = QPainterPath() tagPath.moveTo(8, 0) tagPath.lineTo(8+oneTag_w, 0) tagPath.lineTo(8+oneTag_w, self.tagPath_h) tagPath.lineTo(8, self.tagPath_h) tagPath.lineTo(0, self.tagPath_h/2) tagPath.closeSubpath() painter.drawPath(tagPath) painter.drawText(8, 0, oneTag_w, self.tagPath_h, Qt.AlignCenter, t) painter.translate(oneTag_w+15, 0) # translate by offset else: painter.resetTransform() return # draw ellipsis if too many tags painter.setPen(Qt.DotLine) painter.drawLine(-4, self.tagPath_h/2, 2, self.tagPath_h/2) painter.resetTransform() def sizeHint(self, option, index): tag_h = self.tag_h if index.sibling(index.row(), 4).data() else 0 self.all_h = self.titleArea_h + 2 + self.text_h + tag_h + 6 return QSize(-1, self.all_h+3) # 3 is spacing between entries
class DiaryListDelegate(QStyledItemDelegate): """ItemDelegate of old theme 'one-pixel-rect' for DiaryList, Using 'traditional' painting method compared to colorful theme.""" def __init__(self): super().__init__() # don't pass parent because of mem problem # Because some fonts have much more space at top and bottom, we use ascent instead # of height, and add it with a small number. magic = int(4 * scaleRatio) self.title_h = max(font.title_m.ascent(), font.datetime_m.ascent()) + magic self.titleArea_h = self.title_h + 4 self.text_h = font.text_m.lineSpacing() * settings['Main'].getint( 'previewLines') self.tagPath_h = font.default_m.ascent() + magic self.tag_h = self.tagPath_h + 4 self.dt_w = font.datetime_m.width( datetimeTrans('2000-01-01 00:00')) + 40 self.all_h = None # updated in sizeHint before each item being painting # doc is used to draw text(diary's body) self.doc = NTextDocument() self.doc.setDefaultFont(font.text) self.doc.setUndoRedoEnabled(False) self.doc.setDocumentMargin(0) self.doc.documentLayout().setPaintDevice( qApp.desktop()) # refer actual list will cause segfault # setup colors self.c_text = Qt.black self.c_bg = QColor(255, 236, 176) self.c_border = QColor(214, 172, 41) self.c_inActBg = QColor(255, 236, 176, 40) self.c_gray = QColor(93, 73, 57) def paint(self, painter, option, index): x, y, w = option.rect.x(), option.rect.y(), option.rect.width() - 1 row = index.row() dt, text, title, tags, formats = (index.sibling(row, i).data() for i in range(1, 6)) selected = bool(option.state & QStyle.State_Selected) active = bool(option.state & QStyle.State_Active) # draw border and background painter.setPen(self.c_border) painter.setBrush(self.c_bg if selected and active else self.c_inActBg) painter.drawRect(x + 1, y, w - 2, self.all_h) # outer border if selected: # draw inner border pen = QPen() pen.setStyle(Qt.DashLine) pen.setColor(self.c_gray) painter.setPen(pen) painter.drawRect(x + 2, y + 1, w - 4, self.all_h - 2) # draw datetime and title painter.setPen(self.c_gray) painter.drawLine(x + 10, y + self.titleArea_h, x + w - 10, y + self.titleArea_h) painter.setPen(self.c_text) painter.setFont(font.datetime) painter.drawText(x + 14, y + self.titleArea_h - self.title_h, self.dt_w, self.title_h, Qt.AlignVCenter, datetimeTrans(dt)) if title: painter.setFont(font.title) title_w = w - self.dt_w - 13 title = font.title_m.elidedText(title, Qt.ElideRight, title_w) painter.drawText(x + self.dt_w, y + self.titleArea_h - self.title_h, title_w, self.title_h, Qt.AlignVCenter | Qt.AlignRight, title) # draw text self.doc.setText(text, formats) self.doc.setTextWidth(w - 26) painter.translate(x + 14, y + self.titleArea_h + 2) self.doc.drawContentsColor(painter, QRect(0, 0, w - 26, self.text_h), self.c_text) painter.resetTransform() # draw tags if tags: painter.setPen(self.c_gray) painter.setFont(font.default) painter.translate(x + 15, y + self.titleArea_h + 6 + self.text_h) real_x, max_x = x + 15, w - 10 for t in tags.split(): oneTag_w = font.default_m.width(t) + 4 real_x += oneTag_w + 15 if real_x > max_x: break tagPath = QPainterPath() tagPath.moveTo(8, 0) tagPath.lineTo(8 + oneTag_w, 0) tagPath.lineTo(8 + oneTag_w, self.tagPath_h) tagPath.lineTo(8, self.tagPath_h) tagPath.lineTo(0, self.tagPath_h / 2) tagPath.closeSubpath() painter.drawPath(tagPath) painter.drawText(8, 0, oneTag_w, self.tagPath_h, Qt.AlignCenter, t) painter.translate(oneTag_w + 15, 0) # translate by offset else: painter.resetTransform() return # draw ellipsis if too many tags painter.setPen(Qt.DotLine) painter.drawLine(-4, self.tagPath_h / 2, 2, self.tagPath_h / 2) painter.resetTransform() def sizeHint(self, option, index): tag_h = self.tag_h if index.sibling(index.row(), 4).data() else 0 self.all_h = self.titleArea_h + 2 + self.text_h + tag_h + 6 return QSize(-1, self.all_h + 3) # 3 is spacing between entries