Skip to content

Commit a9473c3

Browse files
committed
feat: improve search in document (#25)
Fixes tasks: QtUiDocument QtTsDocument RCDocument text view RCDocument central view RCDocument left tree
1 parent 8f54cf5 commit a9473c3

12 files changed

+409
-19
lines changed

src/gui/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ set(PROJECT_SOURCES
2828
gui_constants.h
2929
guisettings.h
3030
guisettings.cpp
31+
highlightsearchdelegate.h
32+
highlightsearchdelegate.cpp
3133
historypanel.h
3234
historypanel.cpp
3335
imageview.h

src/gui/findwidget.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@
1111
#include "findwidget.h"
1212
#include "core/logger.h"
1313
#include "core/project.h"
14+
#include "core/qttsdocument.h"
15+
#include "core/qtuidocument.h"
1416
#include "core/textdocument.h"
1517
#include "guisettings.h"
18+
#include "qttsview.h"
19+
#include "qtuiview.h"
1620
#include "ui_findwidget.h"
1721

1822
#include <QAction>
@@ -86,11 +90,35 @@ QString FindWidget::findString()
8690

8791
void FindWidget::findNext()
8892
{
93+
auto qtUiDocument = qobject_cast<Core::QtUiDocument *>(Core::Project::instance()->currentDocument());
94+
if (qtUiDocument) {
95+
Q_EMIT findRequested(ui->findEdit->text(), Core::TextDocument::NoFindFlags);
96+
return;
97+
}
98+
99+
auto qtTsDocument = qobject_cast<Core::QtTsDocument *>(Core::Project::instance()->currentDocument());
100+
if (qtTsDocument) {
101+
Q_EMIT findRequested(ui->findEdit->text(), Core::TextDocument::NoFindFlags); // default is FindForward
102+
return;
103+
}
104+
89105
find(findFlags());
90106
}
91107

92108
void FindWidget::findPrevious()
93109
{
110+
auto qtUiDocument = qobject_cast<Core::QtUiDocument *>(Core::Project::instance()->currentDocument());
111+
if (qtUiDocument) {
112+
Q_EMIT findRequested(ui->findEdit->text(), Core::TextDocument::FindBackward);
113+
return;
114+
}
115+
116+
auto qtTsDocument = qobject_cast<Core::QtTsDocument *>(Core::Project::instance()->currentDocument());
117+
if (qtTsDocument) {
118+
Q_EMIT findRequested(ui->findEdit->text(), Core::TextDocument::FindBackward);
119+
return;
120+
}
121+
94122
find(findFlags() | Core::TextDocument::FindBackward);
95123
}
96124

@@ -153,4 +181,19 @@ void FindWidget::replace(bool onlyOne)
153181
}
154182
}
155183

184+
void FindWidget::hideEvent(QHideEvent *event)
185+
{
186+
Q_EMIT findWidgetClosed();
187+
QWidget::hideEvent(event);
188+
}
189+
190+
void FindWidget::showReplaceFeature(bool show)
191+
{
192+
// We don't want to use a wrapping frame here due to layouting issues...
193+
ui->replaceWithLabel->setVisible(show);
194+
ui->replaceEdit->setVisible(show);
195+
ui->replaceAllbutton->setVisible(show);
196+
ui->replaceButton->setVisible(show);
197+
}
198+
156199
} // namespace Gui

src/gui/findwidget.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,15 @@ class FindWidget : public QWidget
3131

3232
void open();
3333

34+
void showReplaceFeature(bool show = true);
35+
36+
signals:
37+
void findRequested(const QString &text, int options);
38+
void findWidgetClosed();
39+
40+
protected:
41+
void hideEvent(QHideEvent *event) override;
42+
3443
private:
3544
int findFlags() const;
3645
QString findString();

src/gui/findwidget.ui

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,14 +117,14 @@
117117
</layout>
118118
</item>
119119
<item row="1" column="0">
120-
<widget class="QLabel" name="label_2">
120+
<widget class="QLabel" name="replaceWithLabel">
121121
<property name="text">
122122
<string>Replace with:</string>
123123
</property>
124124
</widget>
125125
</item>
126126
<item row="0" column="0">
127-
<widget class="QLabel" name="label">
127+
<widget class="QLabel" name="findLabel">
128128
<property name="text">
129129
<string>Find:</string>
130130
</property>

src/gui/highlightsearchdelegate.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
This file is part of Knut.
3+
4+
SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
5+
6+
SPDX-License-Identifier: GPL-3.0-only
7+
8+
Contact KDAB at <[email protected]> for commercial licensing options.
9+
*/
10+
11+
#include "highlightsearchdelegate.h"
12+
#include <QPainter>
13+
14+
namespace Gui {
15+
16+
HighlightSearchDelegate::HighlightSearchDelegate(QObject *parent)
17+
: QItemDelegate(parent)
18+
{
19+
}
20+
21+
void HighlightSearchDelegate::setSearchText(const QString &searchText, int offset)
22+
{
23+
m_searchText = searchText;
24+
m_offset = offset;
25+
}
26+
27+
void HighlightSearchDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
28+
const QModelIndex &index) const
29+
{
30+
QString text = index.data().toString();
31+
if (m_searchText.isEmpty() || !text.contains(m_searchText, Qt::CaseInsensitive)) {
32+
QItemDelegate::paint(painter, option, index);
33+
return;
34+
}
35+
36+
// Check for multiline strings
37+
QStringList multilineStrings = text.split("\n", Qt::SkipEmptyParts);
38+
bool isMultiline = multilineStrings.count() > 1;
39+
if (isMultiline) {
40+
// Make it a one line text for the search highlight,
41+
// otherwise the painting is error prone (needs too much manipulation).
42+
// That does not impact the model itself.
43+
text = text.replace("\n", " - ");
44+
}
45+
// Find the substring to highlight
46+
const QRegularExpression regex(m_searchText, QRegularExpression::CaseInsensitiveOption);
47+
int start = regex.match(text).capturedStart();
48+
int end = start + regex.match(text).capturedLength();
49+
50+
// Calculate the drawing positions.
51+
// The TreeViews or TableView display its Items excentred in relation to the left side of the cell.
52+
// Correct the x position accordingly using the caller offset value.
53+
int x = option.rect.left() + m_offset;
54+
int y = option.rect.top();
55+
int height = option.rect.height();
56+
int width = painter->fontMetrics().horizontalAdvance(text.left(start));
57+
58+
// Paint the seach result string 'bold'.
59+
QFont highlightFont = option.font;
60+
highlightFont.setBold(true);
61+
62+
painter->save();
63+
// Make sure we don't paint over the selected item highlighted background.
64+
if (option.state & QStyle::State_Selected) {
65+
// Set the background color to the selection color
66+
painter->fillRect(option.rect, option.palette.brush(QPalette::Active, QPalette::Highlight));
67+
}
68+
69+
painter->drawText(x, y, width, height, option.displayAlignment, text.left(start));
70+
// Paint the searched string with bold font.
71+
painter->setFont(highlightFont);
72+
x += width;
73+
width = painter->fontMetrics().horizontalAdvance(text.mid(start, end - start));
74+
painter->drawText(x, y, width, height, option.displayAlignment, text.mid(start, end - start));
75+
// Reset the painter font to original font.
76+
painter->setFont(option.font);
77+
x += width;
78+
width = painter->fontMetrics().horizontalAdvance(text.right(text.length() - end));
79+
painter->drawText(x, y, width, height, option.displayAlignment, text.right(text.length() - end));
80+
painter->restore();
81+
}
82+
83+
} // namespace Gui

src/gui/highlightsearchdelegate.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
This file is part of Knut.
3+
4+
SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
5+
6+
SPDX-License-Identifier: GPL-3.0-only
7+
8+
Contact KDAB at <[email protected]> for commercial licensing options.
9+
*/
10+
11+
#pragma once
12+
13+
#include <QItemDelegate>
14+
15+
namespace Gui {
16+
17+
class HighlightSearchDelegate : public QItemDelegate
18+
{
19+
Q_OBJECT
20+
21+
public:
22+
explicit HighlightSearchDelegate(QObject *parent = nullptr);
23+
24+
void setSearchText(const QString &searchText, int offset = 0);
25+
26+
protected:
27+
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
28+
29+
private:
30+
QString m_searchText;
31+
int m_offset;
32+
};
33+
34+
} // namespace Gui

src/gui/mainwindow.cpp

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -572,12 +572,17 @@ void MainWindow::updateActions()
572572

573573
ui->actionCloseDocument->setEnabled(document != nullptr);
574574

575+
auto *qtTsDocument = qobject_cast<Core::QtTsDocument *>(document);
576+
auto *qtUiDocument = qobject_cast<Core::QtUiDocument *>(document);
575577
auto *textDocument = qobject_cast<Core::TextDocument *>(document);
578+
579+
const bool enableFindWidgetFeatures =
580+
(qtTsDocument != nullptr || qtUiDocument != nullptr || textDocument != nullptr);
576581
ui->actionSelectAll->setEnabled(textDocument != nullptr);
577-
ui->actionFind->setEnabled(textDocument != nullptr);
582+
ui->actionFind->setEnabled(enableFindWidgetFeatures);
578583
ui->actionReplace->setEnabled(textDocument != nullptr);
579-
ui->actionFindNext->setEnabled(textDocument != nullptr);
580-
ui->actionFindPrevious->setEnabled(textDocument != nullptr);
584+
ui->actionFindNext->setEnabled(enableFindWidgetFeatures);
585+
ui->actionFindPrevious->setEnabled(enableFindWidgetFeatures);
581586
ui->actionDeleteLine->setEnabled(textDocument != nullptr);
582587
ui->actionUndo->setEnabled(textDocument != nullptr);
583588
ui->actionRedo->setEnabled(textDocument != nullptr);
@@ -615,6 +620,18 @@ void MainWindow::updateActions()
615620
ui->actionCreateUi->setEnabled(rcEnabled);
616621
}
617622

623+
void MainWindow::updateFindWidgetDisplay()
624+
{
625+
// Handle the 'FindWidget' display depending on the document type.
626+
auto document = Core::Project::instance()->currentDocument();
627+
628+
auto *qtTsDocument = qobject_cast<Core::QtTsDocument *>(document);
629+
auto *qtUiDocument = qobject_cast<Core::QtUiDocument *>(document);
630+
// Hide the replace widgets.
631+
const bool hideReplaceFeature = (qtTsDocument != nullptr || qtUiDocument != nullptr);
632+
ui->findWidget->showReplaceFeature(!hideReplaceFeature);
633+
}
634+
618635
void MainWindow::updateScriptActions()
619636
{
620637
ui->actionStartRecordingScript->setEnabled(!m_historyPanel->isRecording());
@@ -694,6 +711,7 @@ QWidget *MainWindow::widgetForDocument(Core::Document *document)
694711
}
695712
case Core::Document::Type::QtUi: {
696713
auto uiview = new QtUiView();
714+
uiview->setFindWidget(ui->findWidget);
697715
uiview->setUiDocument(qobject_cast<Core::QtUiDocument *>(document));
698716
return uiview;
699717
}
@@ -715,6 +733,7 @@ QWidget *MainWindow::widgetForDocument(Core::Document *document)
715733
}
716734
case Core::Document::Type::QtTs: {
717735
auto tsView = new QtTsView();
736+
tsView->setFindWidget(ui->findWidget);
718737
tsView->setTsDocument(qobject_cast<Core::QtTsDocument *>(document));
719738
return tsView;
720739
}
@@ -792,6 +811,7 @@ void MainWindow::changeCurrentDocument()
792811
const QModelIndex &index = m_fileModel->index(fileName);
793812
m_projectView->setCurrentIndex(index);
794813
updateActions();
814+
updateFindWidgetDisplay();
795815
}
796816

797817
} // namespace Gui

src/gui/mainwindow.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ class MainWindow : public QMainWindow
101101

102102
void updateActions();
103103
void updateScriptActions();
104+
void updateFindWidgetDisplay();
104105

105106
void initProject(const QString &path);
106107
void openDocument(const QModelIndex &index);

0 commit comments

Comments
 (0)