Skip to content

Commit eda225d

Browse files
committed
add Goto Line and Find/Replace to RemoteEditWidget
1 parent a86804c commit eda225d

7 files changed

Lines changed: 564 additions & 1 deletion

File tree

src/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,10 @@ set(mmapper_SRCS
378378
mpi/remoteeditsession.h
379379
mpi/remoteeditwidget.cpp
380380
mpi/remoteeditwidget.h
381+
mpi/gotowidget.cpp
382+
mpi/gotowidget.h
383+
mpi/findreplacewidget.cpp
384+
mpi/findreplacewidget.h
381385
observer/gameobserver.cpp
382386
observer/gameobserver.h
383387
opengl/Font.cpp

src/mpi/findreplacewidget.cpp

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
// Copyright (C) 2025 The MMapper Authors
3+
4+
#include "findreplacewidget.h"
5+
6+
#include <QCheckBox>
7+
#include <QGridLayout>
8+
#include <QIcon>
9+
#include <QKeyEvent>
10+
#include <QLabel>
11+
#include <QLineEdit>
12+
#include <QMetaObject>
13+
#include <QSizePolicy>
14+
#include <QToolButton>
15+
16+
FindReplaceWidget::FindReplaceWidget(bool allowReplace, QWidget *const parent)
17+
: QWidget(parent)
18+
{
19+
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
20+
21+
QGridLayout *layout = new QGridLayout(this);
22+
layout->setContentsMargins(2, 2, 2, 2);
23+
layout->setSpacing(4);
24+
25+
m_findLineEdit = new QLineEdit(this);
26+
m_findLineEdit->setPlaceholderText("Find");
27+
connect(m_findLineEdit,
28+
&QLineEdit::textChanged,
29+
this,
30+
&FindReplaceWidget::slot_updateButtonStates);
31+
layout->addWidget(m_findLineEdit, 0, 0);
32+
33+
m_findPreviousButton = createActionButton("go-previous",
34+
":/icons/layerup.png",
35+
"",
36+
"Find Previous",
37+
true,
38+
Qt::ToolButtonIconOnly);
39+
layout->addWidget(m_findPreviousButton, 0, 1, Qt::AlignVCenter);
40+
41+
m_findNextButton = createActionButton("go-next",
42+
":/icons/layerdown.png",
43+
"",
44+
"Find Next (Enter)",
45+
true,
46+
Qt::ToolButtonIconOnly);
47+
layout->addWidget(m_findNextButton, 0, 2, Qt::AlignVCenter);
48+
49+
m_replaceToggleCheckBox = new QCheckBox("Replace", this);
50+
m_replaceToggleCheckBox->setToolTip("Show/Hide Replace Options");
51+
connect(m_replaceToggleCheckBox, &QCheckBox::toggled, this, [this](bool checked) {
52+
m_replaceLineEdit->setHidden(!checked);
53+
m_replaceCurrentButton->setHidden(!checked);
54+
m_replaceAllButton->setHidden(!checked);
55+
slot_updateButtonStates();
56+
});
57+
m_replaceToggleCheckBox->setEnabled(allowReplace);
58+
layout->addWidget(m_replaceToggleCheckBox, 0, 3, Qt::AlignVCenter | Qt::AlignRight);
59+
60+
QToolButton *closeButton = createActionButton("window-close",
61+
":/icons/cancel.png",
62+
"",
63+
"Close (Esc)",
64+
true,
65+
Qt::ToolButtonIconOnly);
66+
layout->addWidget(closeButton, 0, 4, Qt::AlignVCenter | Qt::AlignRight);
67+
68+
m_replaceLineEdit = new QLineEdit(this);
69+
m_replaceLineEdit->setPlaceholderText("Replace");
70+
layout->addWidget(m_replaceLineEdit, 1, 0);
71+
72+
m_replaceCurrentButton = createActionButton("edit-find-replace",
73+
":/icons/findreplace.png",
74+
"Replace",
75+
"Replace Current");
76+
layout->addWidget(m_replaceCurrentButton, 1, 1, 1, 2, Qt::AlignVCenter);
77+
78+
m_replaceAllButton = createActionButton("dialog-ok-apply",
79+
":/icons/apply.png",
80+
"All",
81+
"Replace All");
82+
layout->addWidget(m_replaceAllButton, 1, 3, 1, 2, Qt::AlignVCenter);
83+
84+
layout->setColumnStretch(0, 10);
85+
layout->setColumnStretch(1, 0);
86+
layout->setColumnStretch(2, 0);
87+
layout->setColumnStretch(3, 0);
88+
layout->setColumnStretch(4, 0);
89+
90+
setLayout(layout);
91+
92+
m_replaceLineEdit->hide();
93+
m_replaceCurrentButton->hide();
94+
m_replaceAllButton->hide();
95+
96+
auto findRequested = [this](QTextDocument::FindFlags flags) {
97+
if (!m_findLineEdit->text().isEmpty()) {
98+
emit sig_findRequested(m_findLineEdit->text(), flags);
99+
}
100+
};
101+
102+
auto replaceCurrent = [this]() {
103+
if (!m_findLineEdit->text().isEmpty() && m_replaceToggleCheckBox->isChecked()) {
104+
emit sig_replaceCurrentRequested(m_findLineEdit->text(),
105+
m_replaceLineEdit->text(),
106+
QTextDocument::FindFlags());
107+
}
108+
};
109+
110+
connect(m_findLineEdit, &QLineEdit::returnPressed, this, [findRequested]() {
111+
findRequested(QTextDocument::FindFlags());
112+
});
113+
connect(m_findNextButton, &QToolButton::clicked, this, [findRequested]() {
114+
findRequested(QTextDocument::FindFlags());
115+
});
116+
connect(m_findPreviousButton, &QToolButton::clicked, this, [findRequested]() {
117+
findRequested(QTextDocument::FindBackward);
118+
});
119+
connect(m_replaceLineEdit, &QLineEdit::returnPressed, this, replaceCurrent);
120+
connect(m_replaceCurrentButton, &QToolButton::clicked, this, replaceCurrent);
121+
connect(m_replaceAllButton, &QToolButton::clicked, this, [this]() {
122+
if (!m_findLineEdit->text().isEmpty() && m_replaceToggleCheckBox->isChecked()) {
123+
emit sig_replaceAllRequested(m_findLineEdit->text(),
124+
m_replaceLineEdit->text(),
125+
QTextDocument::FindFlags());
126+
}
127+
});
128+
connect(closeButton, &QToolButton::clicked, this, [this]() { emit sig_closeRequested(); });
129+
}
130+
131+
FindReplaceWidget::~FindReplaceWidget() = default;
132+
133+
QToolButton *FindReplaceWidget::createActionButton(const QString &themeIcon,
134+
const QString &qrcFallbackIcon,
135+
const QString &text,
136+
const QString &tooltip,
137+
bool autoRaise,
138+
Qt::ToolButtonStyle buttonStyle,
139+
Qt::FocusPolicy focusPolicy)
140+
{
141+
QToolButton *button = new QToolButton(this);
142+
button->setIcon(QIcon::fromTheme(themeIcon, QIcon(qrcFallbackIcon)));
143+
if (!text.isEmpty()) {
144+
button->setText(text);
145+
}
146+
button->setToolTip(tooltip);
147+
button->setAutoRaise(autoRaise);
148+
button->setToolButtonStyle(buttonStyle);
149+
button->setFocusPolicy(focusPolicy);
150+
return button;
151+
}
152+
153+
void FindReplaceWidget::setFocusToFindInput()
154+
{
155+
m_findLineEdit->setFocus(Qt::OtherFocusReason);
156+
m_findLineEdit->selectAll();
157+
}
158+
159+
void FindReplaceWidget::slot_updateButtonStates()
160+
{
161+
const bool findTextNotEmpty = !m_findLineEdit->text().isEmpty();
162+
m_findNextButton->setEnabled(findTextNotEmpty);
163+
m_findPreviousButton->setEnabled(findTextNotEmpty);
164+
165+
const bool replaceChecked = m_replaceToggleCheckBox->isChecked();
166+
const bool enableReplaceButtons = findTextNotEmpty && replaceChecked;
167+
168+
m_replaceCurrentButton->setEnabled(enableReplaceButtons);
169+
m_replaceAllButton->setEnabled(enableReplaceButtons);
170+
}
171+
172+
void FindReplaceWidget::keyPressEvent(QKeyEvent *const event)
173+
{
174+
if (event->key() == Qt::Key_Escape) {
175+
emit sig_closeRequested();
176+
event->accept();
177+
return;
178+
}
179+
QWidget::keyPressEvent(event);
180+
}

src/mpi/findreplacewidget.h

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#pragma once
2+
// SPDX-License-Identifier: GPL-2.0-or-later
3+
// Copyright (C) 2025 The MMapper Authors
4+
5+
#include "../global/macros.h"
6+
7+
#include <QCheckBox>
8+
#include <QLineEdit>
9+
#include <QTextDocument>
10+
#include <QToolButton>
11+
#include <QWidget>
12+
13+
class QKeyEvent;
14+
15+
class NODISCARD_QOBJECT FindReplaceWidget final : public QWidget
16+
{
17+
Q_OBJECT
18+
19+
private:
20+
QLineEdit *m_findLineEdit;
21+
QToolButton *m_findPreviousButton;
22+
QToolButton *m_findNextButton;
23+
QCheckBox *m_replaceToggleCheckBox;
24+
QLineEdit *m_replaceLineEdit;
25+
QToolButton *m_replaceCurrentButton;
26+
QToolButton *m_replaceAllButton;
27+
28+
public:
29+
explicit FindReplaceWidget(bool allowReplace, QWidget *parent = nullptr);
30+
~FindReplaceWidget() override;
31+
32+
void setFocusToFindInput();
33+
34+
signals:
35+
void sig_findRequested(const QString &term, QTextDocument::FindFlags flags);
36+
void sig_replaceCurrentRequested(const QString &findTerm,
37+
const QString &replaceTerm,
38+
QTextDocument::FindFlags flags);
39+
void sig_replaceAllRequested(const QString &findTerm,
40+
const QString &replaceTerm,
41+
QTextDocument::FindFlags flags);
42+
void sig_closeRequested();
43+
void sig_statusMessage(const QString &message);
44+
45+
private slots:
46+
void slot_updateButtonStates();
47+
48+
private:
49+
NODISCARD QToolButton *createActionButton(
50+
const QString &themeIcon,
51+
const QString &qrcFallbackIcon,
52+
const QString &text,
53+
const QString &tooltip,
54+
bool autoRaise = true,
55+
Qt::ToolButtonStyle buttonStyle = Qt::ToolButtonTextBesideIcon,
56+
Qt::FocusPolicy focusPolicy = Qt::NoFocus);
57+
58+
protected:
59+
void keyPressEvent(QKeyEvent *event) override;
60+
};

src/mpi/gotowidget.cpp

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
// Copyright (C) 2025 The MMapper Authors
3+
4+
#include "gotowidget.h"
5+
6+
#include <QHBoxLayout>
7+
#include <QIcon>
8+
#include <QIntValidator>
9+
#include <QKeyEvent>
10+
#include <QLabel>
11+
#include <QLineEdit>
12+
#include <QToolButton>
13+
14+
GotoWidget::GotoWidget(QWidget *const parent)
15+
: QWidget(parent)
16+
{
17+
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
18+
19+
QHBoxLayout *layout = new QHBoxLayout(this);
20+
layout->setContentsMargins(2, 2, 2, 2);
21+
layout->setSpacing(4);
22+
23+
m_lineEdit = new QLineEdit(this);
24+
m_lineEdit->setPlaceholderText("Go to line");
25+
m_lineEdit->setValidator(new QIntValidator(1, 1000000, this));
26+
m_lineEdit->setMinimumWidth(80);
27+
layout->addWidget(m_lineEdit, 1);
28+
29+
QToolButton *goButton = new QToolButton(this);
30+
goButton->setText("Go");
31+
goButton->setIcon(QIcon::fromTheme("go-jump", QIcon(":/icons/goto.png")));
32+
goButton->setToolTip("Go to specified line number");
33+
goButton->setAutoRaise(true);
34+
goButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
35+
goButton->setFocusPolicy(Qt::NoFocus);
36+
layout->addWidget(goButton);
37+
38+
QToolButton *closeButton = new QToolButton(this);
39+
closeButton->setIcon(QIcon::fromTheme("window-close", QIcon(":/icons/cancel.png")));
40+
closeButton->setToolTip("Close (Esc)");
41+
closeButton->setAutoRaise(true);
42+
closeButton->setToolButtonStyle(Qt::ToolButtonIconOnly);
43+
closeButton->setFocusPolicy(Qt::NoFocus);
44+
layout->addWidget(closeButton);
45+
46+
setLayout(layout);
47+
48+
auto gotoLine = [this]() {
49+
if (!m_lineEdit)
50+
return;
51+
bool ok;
52+
int lineNum = m_lineEdit->text().toInt(&ok);
53+
if (ok && lineNum > 0) {
54+
emit sig_gotoLineRequested(lineNum);
55+
} else {
56+
m_lineEdit->selectAll();
57+
m_lineEdit->setFocus();
58+
}
59+
};
60+
61+
connect(m_lineEdit, &QLineEdit::returnPressed, this, gotoLine);
62+
connect(goButton, &QToolButton::clicked, this, gotoLine);
63+
connect(closeButton, &QToolButton::clicked, this, [this]() { emit sig_closeRequested(); });
64+
}
65+
66+
GotoWidget::~GotoWidget() = default;
67+
68+
void GotoWidget::setFocusToInput()
69+
{
70+
if (m_lineEdit) {
71+
m_lineEdit->setFocus(Qt::OtherFocusReason);
72+
m_lineEdit->clear();
73+
}
74+
}
75+
76+
void GotoWidget::keyPressEvent(QKeyEvent *const event)
77+
{
78+
if (event->key() == Qt::Key_Escape) {
79+
emit sig_closeRequested();
80+
event->accept();
81+
return;
82+
}
83+
QWidget::keyPressEvent(event);
84+
}

src/mpi/gotowidget.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#pragma once
2+
// SPDX-License-Identifier: GPL-2.0-or-later
3+
// Copyright (C) 2025 The MMapper Authors
4+
5+
#include "../global/macros.h"
6+
7+
#include <QLineEdit>
8+
#include <QWidget>
9+
10+
class QKeyEvent;
11+
12+
class NODISCARD_QOBJECT GotoWidget final : public QWidget
13+
{
14+
Q_OBJECT
15+
16+
private:
17+
QLineEdit *m_lineEdit;
18+
19+
public:
20+
explicit GotoWidget(QWidget *parent = nullptr);
21+
~GotoWidget() override;
22+
23+
void setFocusToInput();
24+
25+
signals:
26+
void sig_gotoLineRequested(int lineNum);
27+
void sig_closeRequested();
28+
29+
protected:
30+
void keyPressEvent(QKeyEvent *event) override;
31+
};

0 commit comments

Comments
 (0)