Skip to content

Commit f1f3bfe

Browse files
committed
Improve value rendering
- Simplify LargeTextWrappingModel - Fix #4686 : Do not block UI on JSON rendering - Fix #4687 : Ignore chunck limit for HTML formatted values
1 parent 2e1ab42 commit f1f3bfe

File tree

7 files changed

+129
-127
lines changed

7 files changed

+129
-127
lines changed

src/app/qmlutils.cpp

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
#include <QApplication>
55
#include <QClipboard>
66
#include <QDateTime>
7-
#include <QFileInfo>
87
#include <QDebug>
8+
#include <QFileInfo>
99
#include <QtCharts/QDateTimeAxis>
1010

1111
#include "apputils.h"
@@ -105,12 +105,11 @@ QVariant QmlUtils::toUtf(const QVariant &value) {
105105
}
106106

107107
QString QmlUtils::getPathFromUrl(const QUrl &url) {
108-
return url.isLocalFile() ? url.toLocalFile() : url.path();
108+
return url.isLocalFile() ? url.toLocalFile() : url.path();
109109
}
110110

111-
bool QmlUtils::fileExists(const QString &path)
112-
{
113-
return QFileInfo::exists(path);
111+
bool QmlUtils::fileExists(const QString &path) {
112+
return QFileInfo::exists(path);
114113
}
115114

116115
void QmlUtils::copyToClipboard(const QString &text) {
@@ -157,9 +156,17 @@ void QmlUtils::addNewValueToDynamicChart(QtCharts::QXYSeries *series,
157156
}
158157

159158
QObject *QmlUtils::wrapLargeText(const QByteArray &text) {
160-
// NOTE(u_glide): Use 150Kb chunks
161-
auto w =
162-
new ValueEditor::LargeTextWrappingModel(QString::fromUtf8(text), 153600);
159+
// NOTE(u_glide): Use 150Kb chunks by default
160+
161+
int chunkSize = 153600;
162+
163+
// Work-around to prevent html corruption
164+
if (text.startsWith("<pre")) {
165+
chunkSize = text.size();
166+
}
167+
168+
auto w = new ValueEditor::LargeTextWrappingModel(QString::fromUtf8(text),
169+
chunkSize);
163170
w->setParent(this);
164171
return w;
165172
}
Lines changed: 31 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,53 @@
11
#include "largetextmodel.h"
22
#include <QDebug>
33

4-
ValueEditor::LargeTextWrappingModel::LargeTextWrappingModel(const QString &text, uint chunkSize)
5-
: m_chunkSize(chunkSize)
6-
{
7-
setText(text);
4+
ValueEditor::LargeTextWrappingModel::LargeTextWrappingModel(const QString &text,
5+
uint chunkSize)
6+
: m_chunkSize(chunkSize) {
7+
setText(text);
88
}
99

10-
ValueEditor::LargeTextWrappingModel::~LargeTextWrappingModel()
11-
{
12-
}
10+
ValueEditor::LargeTextWrappingModel::~LargeTextWrappingModel() {}
1311

14-
QHash<int, QByteArray> ValueEditor::LargeTextWrappingModel::roleNames() const
15-
{
16-
QHash<int, QByteArray> roles;
17-
roles[Qt::UserRole + 1] = "value";
18-
return roles;
12+
QHash<int, QByteArray> ValueEditor::LargeTextWrappingModel::roleNames() const {
13+
QHash<int, QByteArray> roles;
14+
roles[Qt::UserRole + 1] = "value";
15+
return roles;
1916
}
2017

21-
int ValueEditor::LargeTextWrappingModel::rowCount(const QModelIndex &) const
22-
{
23-
return m_textRows.size();
18+
int ValueEditor::LargeTextWrappingModel::rowCount(const QModelIndex &) const {
19+
return m_text.size() / m_chunkSize +
20+
(m_text.size() % m_chunkSize == 0 ? 0 : 1);
2421
}
2522

26-
QVariant ValueEditor::LargeTextWrappingModel::data(const QModelIndex &index, int role) const
27-
{
28-
if (!isIndexValid(index))
29-
return QVariant();
23+
QVariant ValueEditor::LargeTextWrappingModel::data(const QModelIndex &index,
24+
int role) const {
25+
if (!isIndexValid(index)) return QVariant();
3026

31-
if (role == Qt::UserRole + 1) {
32-
return m_textRows[index.row()];
33-
}
27+
if (role == Qt::UserRole + 1) {
28+
return m_text.mid(index.row() * m_chunkSize, m_chunkSize);
29+
}
3430

35-
return QVariant();
31+
return QVariant();
3632
}
3733

38-
void ValueEditor::LargeTextWrappingModel::setText(const QString &text)
39-
{
40-
m_textRows.reserve(text.size()/m_chunkSize);
41-
42-
for (uint chunkIndex=0; chunkIndex < text.size()/m_chunkSize + 1; chunkIndex++)
43-
{
44-
m_textRows.append(text.mid(chunkIndex * m_chunkSize, m_chunkSize));
45-
}
34+
void ValueEditor::LargeTextWrappingModel::setText(const QString &text) {
35+
m_text = text;
4636
}
4737

48-
void ValueEditor::LargeTextWrappingModel::cleanUp()
49-
{
50-
emit beginRemoveRows(QModelIndex(), 0, rowCount() - 1);
51-
m_textRows.clear();
52-
emit endRemoveRows();
38+
void ValueEditor::LargeTextWrappingModel::cleanUp() {
39+
emit beginRemoveRows(QModelIndex(), 0, rowCount() - 1);
40+
m_text.clear();
41+
emit endRemoveRows();
5342
}
5443

55-
QString ValueEditor::LargeTextWrappingModel::getText()
56-
{
57-
QString result;
58-
result.reserve(m_textRows.size() * m_chunkSize);
59-
60-
for(auto textRow : m_textRows)
61-
{
62-
result.append(textRow);
63-
}
64-
65-
return result;
66-
}
44+
QString ValueEditor::LargeTextWrappingModel::getText() { return m_text; }
6745

68-
void ValueEditor::LargeTextWrappingModel::setTextChunk(uint row, QString text)
69-
{
70-
if (row < m_textRows.size()) {
71-
m_textRows[row] = text;
72-
emit dataChanged(createIndex(row, 0), createIndex(row, 0));
73-
}
46+
void ValueEditor::LargeTextWrappingModel::setTextChunk(uint row, QString text) {
47+
m_text.replace(row * m_chunkSize, m_chunkSize, text);
7448
}
7549

76-
bool ValueEditor::LargeTextWrappingModel::isIndexValid(const QModelIndex &index) const
77-
{
78-
return 0 <= index.row() && index.row() < rowCount();
50+
bool ValueEditor::LargeTextWrappingModel::isIndexValid(
51+
const QModelIndex &index) const {
52+
return 0 <= index.row() && index.row() < rowCount();
7953
}
Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,42 @@
11
#pragma once
22
#include <QAbstractListModel>
3-
#include <QSharedPointer>
43
#include <QHash>
54
#include <QList>
5+
#include <QSharedPointer>
66

77
namespace ValueEditor {
88

9-
class LargeTextWrappingModel : public QAbstractListModel
10-
{
11-
// TODO(u_glide): Process out of memory exceptions
9+
class LargeTextWrappingModel : public QAbstractListModel {
10+
// TODO(u_glide): Process out of memory exceptions
1211

13-
Q_OBJECT
14-
public:
15-
LargeTextWrappingModel(const QString& text=QString(), uint chunkSize=10000);
12+
Q_OBJECT
13+
public:
14+
LargeTextWrappingModel(const QString &text = QString(),
15+
uint chunkSize = 10000);
1616

17-
~LargeTextWrappingModel();
17+
~LargeTextWrappingModel();
1818

19-
QHash<int, QByteArray> roleNames() const;
19+
QHash<int, QByteArray> roleNames() const;
2020

21-
int rowCount(const QModelIndex &parent= QModelIndex()) const override;
21+
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
2222

23-
QVariant data(const QModelIndex &index, int role) const;
23+
QVariant data(const QModelIndex &index, int role) const;
2424

25-
void setText(const QString& text);
25+
void setText(const QString &text);
2626

27-
public slots:
28-
void cleanUp();
27+
public slots:
28+
void cleanUp();
2929

30-
QString getText();
30+
QString getText();
3131

32-
void setTextChunk(uint row, QString text);
32+
void setTextChunk(uint row, QString text);
3333

34-
private:
35-
bool isIndexValid(const QModelIndex &index) const;
34+
private:
35+
bool isIndexValid(const QModelIndex &index) const;
3636

37-
private:
38-
uint m_chunkSize;
39-
QList<QString> m_textRows;
37+
private:
38+
uint m_chunkSize;
39+
QString m_text;
4040
};
4141

42-
}
42+
} // namespace ValueEditor

src/qml/value-editor/editors/MultilineEditor.qml

Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -174,35 +174,24 @@ ColumnLayout
174174

175175
uiBlocker.visible = true
176176

177-
formatter.instance.getFormatted(root.value, function (error, formatted, isReadOnly, format) {
178-
179-
function process(error, formatted, stub, format) {
180-
if (error || !formatted) {
181-
uiBlocker.visible = false
182-
formatterSelector.currentIndex = isBin? 2 : 0 // Reset formatter to plain text
183-
notification.showError(error || qsTranslate("RDM","Unknown formatter error (Empty response)"))
184-
return
185-
}
186-
187-
textView.textFormat = (format === "html")
188-
? TextEdit.RichText
189-
: TextEdit.PlainText;
177+
if (formatter['name'] === 'JSON') {
178+
jsonFormattingWorker.sendMessage(String(root.value))
179+
} else {
180+
formatter.instance.getFormatted(root.value, function (error, formatted, isReadOnly, format) {
190181

191-
defaultFormatterSettings.defaultFormatterIndex = formatterSelector.currentIndex
192-
textView.model = qmlUtils.wrapLargeText(formatted)
193-
textView.readOnly = isReadOnly
194-
root.isEdited = false
195-
uiBlocker.visible = false
196-
}
182+
function process(error, formatted, stub, format) {
183+
jsonFormattingWorker.processFormatted(error, formatted, stub, format)
184+
}
197185

198-
if (format === "json") {
199186
textView.format = format
200-
Formatters.json.getFormatted(formatted, process)
201-
} else {
202-
textView.format = format
203-
process(error, formatted, isReadOnly, format);
204-
}
205-
})
187+
188+
if (format === "json") {
189+
jsonFormattingWorker.sendMessage(String(formatted))
190+
} else {
191+
process(error, formatted, isReadOnly, format);
192+
}
193+
})
194+
}
206195
}
207196

208197
function reset() {
@@ -229,6 +218,36 @@ ColumnLayout
229218
validationError.visible = false
230219
}
231220

221+
WorkerScript {
222+
id: jsonFormattingWorker
223+
224+
source: "./formatters/json-tools.js"
225+
onMessage: {
226+
textView.format = messageObject.format
227+
processFormatted(messageObject.error, messageObject.formatted, messageObject.isReadOnly, messageObject.format);
228+
}
229+
230+
function processFormatted(error, formatted, isReadOnly, format) {
231+
if (error || !formatted) {
232+
uiBlocker.visible = false
233+
formatterSelector.currentIndex = isBin? 2 : 0 // Reset formatter to plain text
234+
notification.showError(error || qsTranslate("RDM","Unknown formatter error (Empty response)"))
235+
return
236+
}
237+
238+
textView.textFormat = (format === "html")
239+
? TextEdit.RichText
240+
: TextEdit.PlainText;
241+
242+
defaultFormatterSettings.defaultFormatterIndex = formatterSelector.currentIndex
243+
textView.model = qmlUtils.wrapLargeText(formatted)
244+
textView.readOnly = isReadOnly
245+
root.isEdited = false
246+
uiBlocker.visible = false
247+
}
248+
}
249+
250+
232251
RowLayout{
233252
Layout.fillWidth: true
234253

src/qml/value-editor/editors/editor.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ function getEditorByTypeString(keyType) {
1616
return "./editors/HashItemEditor.qml"
1717
} else if (keyType === "stream") {
1818
return "./editors/StreamItemEditor.qml"
19-
} else {
19+
} else if (keyType) {
2020
console.error("Editor for type " + keyType + " is not defined!")
2121
}
2222
}

src/qml/value-editor/editors/formatters/formatters.js

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -60,21 +60,11 @@ var json = {
6060
title: "JSON",
6161

6262
getFormatted: function (raw, callback) {
63-
try {
64-
// NOTE(u_glide): Minify json before processing to get rid of double formatted JSON
65-
return callback("", JSONFormatter.prettyPrint(JSONFormatter.minify(String(raw))), false, FORMAT_HTML)
66-
} catch (e) {
67-
return callback(qsTranslate("RDM", "Invalid JSON: ") + e)
68-
}
63+
console.error("Call JSON worker script")
6964
},
7065

7166
isValid: function (raw, callback) {
72-
try {
73-
JSONFormatter.prettyPrint(String(raw))
74-
return callback(true)
75-
} catch (e) {
76-
return callback(false)
77-
}
67+
console.error("Call JSON worker script")
7868
},
7969

8070
getRaw: function (formatted, callback) {

src/qml/value-editor/editors/formatters/json-tools.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
.pragma library
2+
13
/*
24
Based on:
35
json-format v.1.1
@@ -84,7 +86,7 @@ var prettyPrint = function( json ) {
8486
}
8587
return '<font color="' + colorMap[type] + '">' + match + '</font>';
8688
});
87-
}
89+
}
8890

8991
var highlighted = syntaxHighlight(out);
9092

@@ -121,3 +123,13 @@ var minify = function(json) {
121123
.replace(/\]\s{0,},\s{0,}\[/g,'],[');
122124
};
123125

126+
127+
WorkerScript.onMessage = function(msg) {
128+
WorkerScript.sendMessage({
129+
'error': "",
130+
// NOTE(u_glide): Minify json before processing to get rid of double formatted JSON
131+
'formatted': prettyPrint(minify(String(msg))),
132+
'isReadOnly': false,
133+
'format': "html"
134+
});
135+
}

0 commit comments

Comments
 (0)