Lazy rendering of message contents

Message will render as user is scrolling down. This prevents interface
freezes on long messages (such as inline img in text mode). Fixes
Bitmessage##366

Also a minor fix in text mode rendering.
This commit is contained in:
mailchuck 2015-12-15 03:51:06 +01:00 committed by Peter Surda
parent 6557681a6c
commit 7ef94b446d
2 changed files with 44 additions and 10 deletions

View File

@ -17,6 +17,11 @@ class MessageView(QtGui.QTextBrowser):
self.setOpenExternalLinks(False) self.setOpenExternalLinks(False)
self.setOpenLinks(False) self.setOpenLinks(False)
self.anchorClicked.connect(self.confirmURL) self.anchorClicked.connect(self.confirmURL)
self.out = ""
self.outpos = 0
self.document().setUndoRedoEnabled(False)
self.rendering = False
self.verticalScrollBar().valueChanged.connect(self.lazyRender)
def mousePressEvent(self, event): def mousePressEvent(self, event):
#text = textCursor.block().text() #text = textCursor.block().text()
@ -36,22 +41,49 @@ class MessageView(QtGui.QTextBrowser):
if reply == QtGui.QMessageBox.Yes: if reply == QtGui.QMessageBox.Yes:
QtGui.QDesktopServices.openUrl(link) QtGui.QDesktopServices.openUrl(link)
def lazyRender(self):
if self.rendering:
return
self.rendering = True
position = self.verticalScrollBar().value()
cursor = QtGui.QTextCursor(self.document())
while self.outpos < len(self.out) and self.verticalScrollBar().value() >= self.document().size().height() - 2 * self.size().height():
startpos = self.outpos
self.outpos += 10240
# find next end of tag
if self.mode == MessageView.MODE_HTML:
pos = self.out.find(">", self.outpos)
if pos > self.outpos:
self.outpos = pos
cursor.movePosition(QtGui.QTextCursor.End, QtGui.QTextCursor.MoveAnchor)
cursor.insertHtml(QtCore.QString(self.out[startpos:self.outpos]))
self.verticalScrollBar().setValue(position)
self.rendering = False
def showPlain(self): def showPlain(self):
self.mode = MessageView.MODE_PLAIN self.mode = MessageView.MODE_PLAIN
out = self.html.raw out = self.html.raw
if self.html.has_html: if self.html.has_html:
out = "<div align=\"center\" style=\"text-decoration: underline;\"><b>" + QtGui.QApplication.translate(type(self).__name__, MessageView.TEXT_PLAIN) + "</b></div><br/>" + out out = "<div align=\"center\" style=\"text-decoration: underline;\"><b>" + str(QtGui.QApplication.translate(type(self).__name__, MessageView.TEXT_PLAIN)) + "</b></div><br/>" + out
self.setHtml(QtCore.QString(out)) self.out = out
self.outpos = 0
self.setHtml("")
self.lazyRender()
def showHTML(self): def showHTML(self):
self.mode = MessageView.MODE_HTML self.mode = MessageView.MODE_HTML
out = self.html.sanitised out = self.html.sanitised
out = "<div align=\"center\" style=\"text-decoration: underline;\"><b>" + QtGui.QApplication.translate(type(self).__name__, MessageView.TEXT_HTML) + "</b></div><br/>" + out out = "<div align=\"center\" style=\"text-decoration: underline;\"><b>" + str(QtGui.QApplication.translate(type(self).__name__, MessageView.TEXT_HTML)) + "</b></div><br/>" + out
self.setHtml(QtCore.QString(out)) self.out = out
self.outpos = 0
self.setHtml("")
self.lazyRender()
def setContent(self, data): def setContent(self, data):
self.html = SafeHTMLParser() self.html = SafeHTMLParser()
self.html.allow_picture = True self.html.allow_picture = True
self.html.reset()
self.html.reset_safe()
self.html.feed(data) self.html.feed(data)
self.html.close() self.html.close()
self.showPlain() self.showPlain()

View File

@ -25,8 +25,10 @@ class SafeHTMLParser(HTMLParser):
self.has_html = False self.has_html = False
def reset_safe(self): def reset_safe(self):
self.elements = set()
self.raw = u"" self.raw = u""
self.sanitised = u"" self.sanitised = u""
self.has_html = False
def add_if_acceptable(self, tag, attrs = None): def add_if_acceptable(self, tag, attrs = None):
if not tag in self.acceptable_elements: if not tag in self.acceptable_elements:
@ -35,12 +37,12 @@ class SafeHTMLParser(HTMLParser):
if inspect.stack()[1][3] == "handle_endtag": if inspect.stack()[1][3] == "handle_endtag":
self.sanitised += "/" self.sanitised += "/"
self.sanitised += tag self.sanitised += tag
if attrs is not None: if not attrs is None:
for attr in attrs: for attr in attrs:
if tag == "img" and attr[0] == "src" and not self.allow_picture: if tag == "img" and attr[0] == "src" and not self.allow_picture:
attr[1] = "" attr[1] = ""
self.sanitised += " " + quote_plus(attr[0]) self.sanitised += " " + quote_plus(attr[0])
if attr[1] is not None: if not (attr[1] is None):
self.sanitised += "=\"" + attr[1] + "\"" self.sanitised += "=\"" + attr[1] + "\""
if inspect.stack()[1][3] == "handle_startendtag": if inspect.stack()[1][3] == "handle_startendtag":
self.sanitised += "/" self.sanitised += "/"
@ -51,13 +53,13 @@ class SafeHTMLParser(HTMLParser):
if inspect.stack()[1][3] == "handle_endtag": if inspect.stack()[1][3] == "handle_endtag":
self.raw += "/" self.raw += "/"
self.raw += tag self.raw += tag
if attrs is not None: if not attrs is None:
for attr in attrs: for attr in attrs:
if tag == "img" and attr[0] == "src" and not self.allow_picture: if tag == "img" and attr[0] == "src" and not self.allow_picture:
attr[1] = "" attr[1] = ""
self.raw += " " + attr[0] self.raw += " " + attr[0]
if attr[1] is not None: if not (attr[1] is None):
self.raw + "=&quot;" + attr[1] + "&quot;" self.raw += "=&quot;" + attr[1] + "&quot;"
if inspect.stack()[1][3] == "handle_startendtag": if inspect.stack()[1][3] == "handle_startendtag":
self.raw += "/" self.raw += "/"
self.raw += "&gt;" self.raw += "&gt;"