Skip to content

Commit

Permalink
feat: improve search in document
Browse files Browse the repository at this point in the history
- add a new interface for searching into document view
- move the textdocument search to this new interface
- implement the interface for QtUiDocument and QtTsDocument

Related to #25
  • Loading branch information
mgiroday authored and narnaud committed Oct 31, 2024
1 parent 3f4c814 commit 9e26f35
Show file tree
Hide file tree
Showing 17 changed files with 525 additions and 92 deletions.
5 changes: 5 additions & 0 deletions src/gui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ set(PROJECT_SOURCES
documentpalette.cpp
fileselector.h
fileselector.cpp
findinterface.h
findwidget.h
findinfilespanel.cpp
findinfilespanel.h
Expand All @@ -28,6 +29,8 @@ set(PROJECT_SOURCES
gui_constants.h
guisettings.h
guisettings.cpp
highlightdelegate.h
highlightdelegate.cpp
historypanel.h
historypanel.cpp
imageview.h
Expand Down Expand Up @@ -68,6 +71,8 @@ set(PROJECT_SOURCES
scriptpanel.cpp
scriptlistpanel.cpp
scriptlistpanel.h
searchabletableview.h
searchabletableview.cpp
shortcutmanager.h
shortcutmanager.cpp
shortcutsettings.h
Expand Down
51 changes: 51 additions & 0 deletions src/gui/findinterface.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
This file is part of Knut.
SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
SPDX-License-Identifier: GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/

#pragma once

#include <QString>

namespace Gui {

/**
* @brief Find interface to handle find and replace in views.
*/
class FindInterface
{
public:
enum FindFlag {
NoFind = 0x0,
CanSearch = 0x1,
CanReplace = 0x2,
};
FindInterface(int flags)
: m_findFlags(flags) {};

int findFlags() const { return m_findFlags; }

virtual void find(const QString &text, int options)
{
Q_UNUSED(text);
Q_UNUSED(options);
};
virtual void cancelFind() {};
virtual void replace(const QString &before, const QString &after, int options, bool replaceAll)
{
Q_UNUSED(before);
Q_UNUSED(after);
Q_UNUSED(options);
Q_UNUSED(replaceAll);
};

private:
int m_findFlags = NoFind;
};

} // namespace Gui
37 changes: 24 additions & 13 deletions src/gui/findwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@
#include "findwidget.h"
#include "core/logger.h"
#include "core/project.h"
#include "core/qttsdocument.h"
#include "core/qtuidocument.h"
#include "core/textdocument.h"
#include "guisettings.h"
#include "qttsview.h"
#include "qtuiview.h"
#include "ui_findwidget.h"

#include <QAction>
Expand Down Expand Up @@ -122,35 +126,42 @@ void FindWidget::find(int options)
{
if (ui->findEdit->text().isEmpty())
return;
auto document = Core::Project::instance()->currentDocument();
if (auto textDocument = qobject_cast<Core::TextDocument *>(document))
textDocument->find(findString(), options);
Q_EMIT findRequested(ui->findEdit->text(), options);
}

void FindWidget::replaceOne()
{
replace(true);
replace(false);
}

void FindWidget::replaceAll()
{
replace(false);
replace(true);
}

void FindWidget::replace(bool onlyOne)
void FindWidget::replace(bool replaceAll)
{
const QString &before = findString();
const QString &after = ui->replaceEdit->text();
if (before.isEmpty())
return;

auto document = Core::Project::instance()->currentDocument();
if (auto textDocument = qobject_cast<Core::TextDocument *>(document)) {
if (onlyOne)
textDocument->replaceOne(before, after, findFlags());
else
textDocument->replaceAll(before, after, findFlags());
}
Q_EMIT replaceRequested(before, after, findFlags(), replaceAll);
}

void FindWidget::hideEvent(QHideEvent *event)
{
Q_EMIT widgetClosed();
QWidget::hideEvent(event);
}

void FindWidget::setReplaceVisible(bool show)
{
// We don't want to use a wrapping frame here due to layouting issues...
ui->replaceWithLabel->setVisible(show);
ui->replaceEdit->setVisible(show);
ui->replaceAllbutton->setVisible(show);
ui->replaceButton->setVisible(show);
}

} // namespace Gui
12 changes: 11 additions & 1 deletion src/gui/findwidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,24 @@ class FindWidget : public QWidget

void open();

void setReplaceVisible(bool show = true);

signals:
void findRequested(const QString &text, int options);
void replaceRequested(const QString &before, const QString &after, int options, bool replaceAll);
void widgetClosed();

protected:
void hideEvent(QHideEvent *event) override;

private:
int findFlags() const;
QString findString();

void find(int options);
void replaceOne();
void replaceAll();
void replace(bool onlyOne);
void replace(bool replaceAll);

std::unique_ptr<Ui::FindWidget> ui;
QAction *m_matchCase = nullptr;
Expand Down
6 changes: 3 additions & 3 deletions src/gui/findwidget.ui
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<spacer name="replaceSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
Expand All @@ -117,14 +117,14 @@
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<widget class="QLabel" name="replaceWithLabel">
<property name="text">
<string>Replace with:</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<widget class="QLabel" name="findLabel">
<property name="text">
<string>Find:</string>
</property>
Expand Down
86 changes: 86 additions & 0 deletions src/gui/highlightdelegate.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
This file is part of Knut.
SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
SPDX-License-Identifier: GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/

#include "highlightdelegate.h"
#include "core/textdocument.h"

#include <QAbstractTextDocumentLayout>
#include <QPainter>
#include <QTextDocument>

namespace Gui {

HighlightDelegate::HighlightDelegate(QObject *parent)
: QItemDelegate(parent)
{
}

void HighlightDelegate::setHighlightedText(const QString &searchText, int options)
{
m_highlightedText = searchText;
m_options = options;
}

QString HighlightDelegate::transform(QString text, const QString &textColor, const QString &backgroundColor) const
{
if (m_highlightedText.isEmpty())
return text;

if (m_options & Core::TextDocument::FindRegexp) {
const auto re = QRegularExpression {m_highlightedText};
QRegularExpressionMatch match;
int index = text.indexOf(re, 0, &match);
while (index != -1) {
const auto oldText = match.captured(0);
const auto newText = QString("<span style='color: %1; background-color: %2;'>%3</span>")
.arg(textColor, backgroundColor, oldText);
text.replace(index, oldText.size(), newText);
index = text.indexOf(re, index + newText.size(), &match);
}
} else {
const bool caseSensitive = m_options & Core::TextDocument::FindCaseSensitively;
int index = text.indexOf(m_highlightedText, 0, caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
while (index != -1) {
const auto oldText = text.mid(index, m_highlightedText.size());
const auto newText = QString("<span style='color: %1; background-color: %2;'>%3</span>")
.arg(textColor, backgroundColor, oldText);
text.replace(index, oldText.size(), newText);
index = text.indexOf(m_highlightedText, index + newText.size(),
caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
}
}
return text;
}

void HighlightDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
const auto textColor = option.palette.color(QPalette::HighlightedText).name();
const auto backgroundColor = option.palette.color(QPalette::Highlight).name();

// Display the text with the search string highlighted.
QString text = transform(index.data(Qt::DisplayRole).toString(), textColor, backgroundColor);
QTextDocument doc;
doc.setHtml(text);

painter->save();

// Adjust the painter's transformation to fit the text within the given rectangle
painter->translate(option.rect.topLeft());
QRect clip(0, 0, option.rect.width(), option.rect.height());
doc.setTextWidth(option.rect.width());

QAbstractTextDocumentLayout::PaintContext ctx;
ctx.clip = clip;
doc.documentLayout()->draw(painter, ctx);

painter->restore();
}

} // namespace Gui
35 changes: 35 additions & 0 deletions src/gui/highlightdelegate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
This file is part of Knut.
SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
SPDX-License-Identifier: GPL-3.0-only
Contact KDAB at <info@kdab.com> for commercial licensing options.
*/

#pragma once

#include <QItemDelegate>

namespace Gui {

class HighlightDelegate : public QItemDelegate
{
Q_OBJECT

public:
explicit HighlightDelegate(QObject *parent = nullptr);

void setHighlightedText(const QString &searchText, int options);

protected:
QString transform(QString text, const QString &textColor, const QString &backgroundColor) const;
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;

private:
QString m_highlightedText;
int m_options;
};

} // namespace Gui
Loading

0 comments on commit 9e26f35

Please sign in to comment.