def test_overlap(self): test_str = 'This is something, string, string.\nparagraph, paragraph!\n method?' test_fmt = [(0, 2, 1), (0, 2, 2), (0, 10, 3), (5, 15, 4), (34, 3, 5)] # output formats may be duplicated, because Qt store format this way true_result = [ (0, 2, 1), (0, 2, 2), (0, 2, 3), (2, 3, 3), (5, 5, 3), # from (0, 10, 3), broken into three parts (5, 5, 4), (10, 10, 4), # from (5, 15, 4) (35, 2, 5) # 34 is \n ] doc = NTextDocument() doc.setText(test_str, test_fmt) result = NTextDocument.getFormats(doc) self.assertEqual(true_result, result)
def test_overlap(self): test_str = 'This is something, string, string.\nparagraph, paragraph!\n method?' test_fmt = [(0, 2, 1), (0, 2, 2), (0, 10, 3), (5, 15, 4), (34, 3, 5)] # output formats may be duplicated, because Qt store format this way true_result = [ (0, 2, 1), (0, 2, 2), (0, 2, 3), (2, 3, 3), (5, 5, 3), # from (0, 10, 3), broken into three parts (5, 5, 4), (10, 10, 4), # from (5, 15, 4) (35, 2, 5) # 34 is \n ] doc = NTextDocument() doc.setText(test_str, test_fmt) result = NTextDocument.getFormats(doc) self.assertEqual(true_result, result)
class NTextEdit(QTextEdit, TextFormatter): """The widget used to edit diary contents in Editor window.""" # spaces that auto-indent can recognize SPACE_KINDS = (' ', '\u3000') # full width space U+3000 def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._doc = NTextDocument(self) self.setDocument(self._doc) # remove highlight color's alpha to avoid alpha loss in copy&paste. # NTextDocument should use this color too. hl, bg = self.HlColor, self.palette().base().color() fac = hl.alpha() / 255 self.HlColor = QColor(round(hl.red()*fac + bg.red()*(1-fac)), round(hl.green()*fac + bg.green()*(1-fac)), round(hl.blue()*fac + bg.blue()*(1-fac))) self.autoIndent = False # used by tab indent shortcut if QLocale().language() in (QLocale.Chinese, QLocale.Japanese): self._indent = ' ' # 2 full width spaces else: self._indent = ' ' # 4 spaces # setup format menu onHLAct = lambda: super(NTextEdit, self).setHL(self.hlAct.isChecked()) onBDAct = lambda: super(NTextEdit, self).setBD(self.bdAct.isChecked()) onSOAct = lambda: super(NTextEdit, self).setSO(self.soAct.isChecked()) onULAct = lambda: super(NTextEdit, self).setUL(self.ulAct.isChecked()) onItaAct = lambda: super(NTextEdit, self).setIta(self.itaAct.isChecked()) self.fmtMenu = QMenu(self.tr('Format'), self) # shortcuts of format actions only used to display shortcut-hint in menu self.hlAct = QAction(makeQIcon(':/menu/highlight.png'), self.tr('Highlight'), self, triggered=onHLAct, shortcut=QKeySequence('Ctrl+H')) self.bdAct = QAction(makeQIcon(':/menu/bold.png'), self.tr('Bold'), self, triggered=onBDAct, shortcut=QKeySequence.Bold) self.soAct = QAction(makeQIcon(':/menu/strikeout.png'), self.tr('Strike out'), self, triggered=onSOAct, shortcut=QKeySequence('Ctrl+T')) self.ulAct = QAction(makeQIcon(':/menu/underline.png'), self.tr('Underline'), self, triggered=onULAct, shortcut=QKeySequence.Underline) self.itaAct = QAction(makeQIcon(':/menu/italic.png'), self.tr('Italic'), self, triggered=onItaAct, shortcut=QKeySequence.Italic) self.clrAct = QAction(self.tr('Clear format'), self, shortcut=QKeySequence('Ctrl+D'), triggered=self.clearFormat) self.acts = (self.hlAct, self.bdAct, self.soAct, self.ulAct, self.itaAct) # excluding uncheckable clrAct for a in self.acts: self.fmtMenu.addAction(a) a.setCheckable(True) self.fmtMenu.addSeparator() self.addAction(self.clrAct) self.fmtMenu.addAction(self.clrAct) self.key2act = { Qt.Key_H: self.hlAct, Qt.Key_B: self.bdAct, Qt.Key_T: self.soAct, Qt.Key_U: self.ulAct, Qt.Key_I: self.itaAct} def contextMenuEvent(self, event): menu = self.createStandardContextMenu() setStdEditMenuIcons(menu) if not self.isReadOnly(): if self.textCursor().hasSelection(): self._setFmtActs() self.fmtMenu.setEnabled(True) else: self.fmtMenu.setEnabled(False) before = menu.actions()[2] menu.insertSeparator(before) menu.insertMenu(before, self.fmtMenu) menu.exec_(event.globalPos()) menu.deleteLater() def keyPressEvent(self, event): if self.isReadOnly(): return super().keyPressEvent(event) if event.modifiers() == Qt.ControlModifier and event.key() in self.key2act: # set actions before calling format methods self._setFmtActs() self.key2act[event.key()].trigger() elif event.key() == Qt.Key_Tab: # will not receive event if tabChangesFocus is True self.textCursor().insertText(self._indent) elif event.key() == Qt.Key_Return and self.autoIndent: # auto-indent support para = self.textCursor().block().text() if len(para) > 0 and para[0] in NTextEdit.SPACE_KINDS: space, spaceCount = para[0], 1 for c in para[1:]: if c != space: break spaceCount += 1 super().keyPressEvent(event) self.textCursor().insertText(space * spaceCount) else: super().keyPressEvent(event) else: super().keyPressEvent(event) def insertFromMimeData(self, source): """Disable some unsupported types""" self.insertHtml(source.html() or source.text()) def setRichText(self, text, formats): self._doc.setHlColor(self.HlColor) self._doc.setText(text, formats) def setAutoIndent(self, enabled): assert isinstance(enabled, (bool, int)) self.autoIndent = enabled def getRichText(self): # self.document() will return QTextDocument, not NTextDocument return self.toPlainText(), self._doc.getFormats() def _setFmtActs(self): """Check formats in current selection and check or uncheck actions""" fmts = [QTextFormat.BackgroundBrush, QTextFormat.FontWeight, QTextFormat.FontStrikeOut, QTextFormat.TextUnderlineStyle, QTextFormat.FontItalic] cur = self.textCursor() start, end = cur.anchor(), cur.position() if start > end: start, end = end, start results = [True] * 5 for pos in range(end, start, -1): cur.setPosition(pos) charFmt = cur.charFormat() for i, f in enumerate(fmts): if results[i] and not charFmt.hasProperty(f): results[i] = False if not any(results): break for i, c in enumerate(results): self.acts[i].setChecked(c) def clearFormat(self): fmt = QTextCharFormat() self.textCursor().setCharFormat(fmt)