Allow direct terminal typing and collapsible session panels
This commit is contained in:
@@ -27,6 +27,8 @@ add_executable(orbithub
|
|||||||
src/session_backend_factory.h
|
src/session_backend_factory.h
|
||||||
src/session_tab.cpp
|
src/session_tab.cpp
|
||||||
src/session_tab.h
|
src/session_tab.h
|
||||||
|
src/terminal_view.cpp
|
||||||
|
src/terminal_view.h
|
||||||
src/session_window.cpp
|
src/session_window.cpp
|
||||||
src/session_window.h
|
src/session_window.h
|
||||||
src/ssh_session_backend.cpp
|
src/ssh_session_backend.cpp
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "session_tab.h"
|
#include "session_tab.h"
|
||||||
|
|
||||||
#include "session_backend_factory.h"
|
#include "session_backend_factory.h"
|
||||||
|
#include "terminal_view.h"
|
||||||
|
|
||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
@@ -17,6 +18,7 @@
|
|||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QTextCursor>
|
#include <QTextCursor>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
#include <QToolButton>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -30,14 +32,16 @@ SessionTab::SessionTab(const Profile& profile, QWidget* parent)
|
|||||||
m_statusLabel(nullptr),
|
m_statusLabel(nullptr),
|
||||||
m_errorLabel(nullptr),
|
m_errorLabel(nullptr),
|
||||||
m_terminalOutput(nullptr),
|
m_terminalOutput(nullptr),
|
||||||
m_terminalInput(nullptr),
|
|
||||||
m_eventLog(nullptr),
|
m_eventLog(nullptr),
|
||||||
m_connectButton(nullptr),
|
m_connectButton(nullptr),
|
||||||
m_disconnectButton(nullptr),
|
m_disconnectButton(nullptr),
|
||||||
m_reconnectButton(nullptr),
|
m_reconnectButton(nullptr),
|
||||||
m_copyErrorButton(nullptr),
|
m_copyErrorButton(nullptr),
|
||||||
m_sendInputButton(nullptr),
|
m_clearTerminalButton(nullptr),
|
||||||
m_clearTerminalButton(nullptr)
|
m_toggleDetailsButton(nullptr),
|
||||||
|
m_toggleEventsButton(nullptr),
|
||||||
|
m_detailsPanel(nullptr),
|
||||||
|
m_eventsPanel(nullptr)
|
||||||
{
|
{
|
||||||
qRegisterMetaType<SessionConnectOptions>("SessionConnectOptions");
|
qRegisterMetaType<SessionConnectOptions>("SessionConnectOptions");
|
||||||
qRegisterMetaType<SessionState>("SessionState");
|
qRegisterMetaType<SessionState>("SessionState");
|
||||||
@@ -164,17 +168,6 @@ void SessionTab::onCopyErrorClicked()
|
|||||||
appendEvent(QStringLiteral("Copied last error to clipboard."));
|
appendEvent(QStringLiteral("Copied last error to clipboard."));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SessionTab::onSendInputClicked()
|
|
||||||
{
|
|
||||||
const QString input = m_terminalInput->text();
|
|
||||||
if (input.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
emit requestInput(input + QStringLiteral("\n"));
|
|
||||||
m_terminalInput->clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SessionTab::onClearTerminalClicked()
|
void SessionTab::onClearTerminalClicked()
|
||||||
{
|
{
|
||||||
m_terminalOutput->clear();
|
m_terminalOutput->clear();
|
||||||
@@ -230,25 +223,35 @@ void SessionTab::setupUi()
|
|||||||
{
|
{
|
||||||
auto* rootLayout = new QVBoxLayout(this);
|
auto* rootLayout = new QVBoxLayout(this);
|
||||||
|
|
||||||
auto* profileLabel = new QLabel(QStringLiteral("Profile: %1").arg(m_profile.name), this);
|
auto* detailsHeader = new QHBoxLayout();
|
||||||
|
m_toggleDetailsButton = new QToolButton(this);
|
||||||
|
m_toggleDetailsButton->setCheckable(true);
|
||||||
|
detailsHeader->addWidget(m_toggleDetailsButton);
|
||||||
|
detailsHeader->addStretch();
|
||||||
|
|
||||||
|
m_detailsPanel = new QWidget(this);
|
||||||
|
auto* detailsLayout = new QVBoxLayout(m_detailsPanel);
|
||||||
|
detailsLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|
||||||
|
auto* profileLabel = new QLabel(QStringLiteral("Profile: %1").arg(m_profile.name), m_detailsPanel);
|
||||||
auto* endpointLabel = new QLabel(
|
auto* endpointLabel = new QLabel(
|
||||||
QStringLiteral("Endpoint: %1://%2@%3:%4")
|
QStringLiteral("Endpoint: %1://%2@%3:%4")
|
||||||
.arg(m_profile.protocol,
|
.arg(m_profile.protocol,
|
||||||
m_profile.username.isEmpty() ? QStringLiteral("<none>") : m_profile.username,
|
m_profile.username.isEmpty() ? QStringLiteral("<none>") : m_profile.username,
|
||||||
m_profile.host,
|
m_profile.host,
|
||||||
QString::number(m_profile.port)),
|
QString::number(m_profile.port)),
|
||||||
this);
|
m_detailsPanel);
|
||||||
auto* authLabel = new QLabel(QStringLiteral("Auth: %1").arg(m_profile.authMode), this);
|
auto* authLabel = new QLabel(QStringLiteral("Auth: %1").arg(m_profile.authMode), m_detailsPanel);
|
||||||
|
|
||||||
m_statusLabel = new QLabel(this);
|
m_statusLabel = new QLabel(m_detailsPanel);
|
||||||
m_errorLabel = new QLabel(QStringLiteral("Last Error: None"), this);
|
m_errorLabel = new QLabel(QStringLiteral("Last Error: None"), m_detailsPanel);
|
||||||
m_errorLabel->setWordWrap(true);
|
m_errorLabel->setWordWrap(true);
|
||||||
|
|
||||||
auto* actionRow = new QHBoxLayout();
|
auto* actionRow = new QHBoxLayout();
|
||||||
m_connectButton = new QPushButton(QStringLiteral("Connect"), this);
|
m_connectButton = new QPushButton(QStringLiteral("Connect"), m_detailsPanel);
|
||||||
m_disconnectButton = new QPushButton(QStringLiteral("Disconnect"), this);
|
m_disconnectButton = new QPushButton(QStringLiteral("Disconnect"), m_detailsPanel);
|
||||||
m_reconnectButton = new QPushButton(QStringLiteral("Reconnect"), this);
|
m_reconnectButton = new QPushButton(QStringLiteral("Reconnect"), m_detailsPanel);
|
||||||
m_copyErrorButton = new QPushButton(QStringLiteral("Copy Error"), this);
|
m_copyErrorButton = new QPushButton(QStringLiteral("Copy Error"), m_detailsPanel);
|
||||||
|
|
||||||
actionRow->addWidget(m_connectButton);
|
actionRow->addWidget(m_connectButton);
|
||||||
actionRow->addWidget(m_disconnectButton);
|
actionRow->addWidget(m_disconnectButton);
|
||||||
@@ -256,6 +259,13 @@ void SessionTab::setupUi()
|
|||||||
actionRow->addWidget(m_copyErrorButton);
|
actionRow->addWidget(m_copyErrorButton);
|
||||||
actionRow->addStretch();
|
actionRow->addStretch();
|
||||||
|
|
||||||
|
detailsLayout->addWidget(profileLabel);
|
||||||
|
detailsLayout->addWidget(endpointLabel);
|
||||||
|
detailsLayout->addWidget(authLabel);
|
||||||
|
detailsLayout->addWidget(m_statusLabel);
|
||||||
|
detailsLayout->addWidget(m_errorLabel);
|
||||||
|
detailsLayout->addLayout(actionRow);
|
||||||
|
|
||||||
auto* terminalHeader = new QHBoxLayout();
|
auto* terminalHeader = new QHBoxLayout();
|
||||||
auto* terminalLabel = new QLabel(QStringLiteral("SSH Terminal"), this);
|
auto* terminalLabel = new QLabel(QStringLiteral("SSH Terminal"), this);
|
||||||
m_clearTerminalButton = new QPushButton(QStringLiteral("Clear"), this);
|
m_clearTerminalButton = new QPushButton(QStringLiteral("Clear"), this);
|
||||||
@@ -263,49 +273,71 @@ void SessionTab::setupUi()
|
|||||||
terminalHeader->addStretch();
|
terminalHeader->addStretch();
|
||||||
terminalHeader->addWidget(m_clearTerminalButton);
|
terminalHeader->addWidget(m_clearTerminalButton);
|
||||||
|
|
||||||
m_terminalOutput = new QPlainTextEdit(this);
|
m_terminalOutput = new TerminalView(this);
|
||||||
m_terminalOutput->setReadOnly(true);
|
|
||||||
m_terminalOutput->setMaximumBlockCount(4000);
|
m_terminalOutput->setMaximumBlockCount(4000);
|
||||||
QFont terminalFont(QStringLiteral("Monospace"));
|
QFont terminalFont(QStringLiteral("Monospace"));
|
||||||
terminalFont.setStyleHint(QFont::TypeWriter);
|
terminalFont.setStyleHint(QFont::TypeWriter);
|
||||||
m_terminalOutput->setFont(terminalFont);
|
m_terminalOutput->setFont(terminalFont);
|
||||||
m_terminalOutput->setMinimumHeight(220);
|
m_terminalOutput->setMinimumHeight(260);
|
||||||
|
m_terminalOutput->setPlaceholderText(
|
||||||
|
QStringLiteral("Connect, then type directly here to interact with the SSH session."));
|
||||||
|
|
||||||
auto* terminalInputRow = new QHBoxLayout();
|
auto* eventsHeader = new QHBoxLayout();
|
||||||
m_terminalInput = new QLineEdit(this);
|
m_toggleEventsButton = new QToolButton(this);
|
||||||
m_terminalInput->setPlaceholderText(QStringLiteral("Type command and press Enter..."));
|
m_toggleEventsButton->setCheckable(true);
|
||||||
m_sendInputButton = new QPushButton(QStringLiteral("Send"), this);
|
eventsHeader->addWidget(m_toggleEventsButton);
|
||||||
terminalInputRow->addWidget(m_terminalInput, 1);
|
eventsHeader->addStretch();
|
||||||
terminalInputRow->addWidget(m_sendInputButton);
|
|
||||||
|
|
||||||
auto* eventHeader = new QLabel(QStringLiteral("Session Events"), this);
|
m_eventsPanel = new QWidget(this);
|
||||||
m_eventLog = new QPlainTextEdit(this);
|
auto* eventsLayout = new QVBoxLayout(m_eventsPanel);
|
||||||
|
eventsLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|
||||||
|
auto* eventTitle = new QLabel(QStringLiteral("Session Events"), m_eventsPanel);
|
||||||
|
m_eventLog = new QPlainTextEdit(m_eventsPanel);
|
||||||
m_eventLog->setReadOnly(true);
|
m_eventLog->setReadOnly(true);
|
||||||
m_eventLog->setPlaceholderText(QStringLiteral("Session event log..."));
|
m_eventLog->setPlaceholderText(QStringLiteral("Session event log..."));
|
||||||
m_eventLog->setMinimumHeight(140);
|
m_eventLog->setMinimumHeight(140);
|
||||||
|
|
||||||
rootLayout->addWidget(profileLabel);
|
eventsLayout->addWidget(eventTitle);
|
||||||
rootLayout->addWidget(endpointLabel);
|
eventsLayout->addWidget(m_eventLog);
|
||||||
rootLayout->addWidget(authLabel);
|
|
||||||
rootLayout->addWidget(m_statusLabel);
|
rootLayout->addLayout(detailsHeader);
|
||||||
rootLayout->addWidget(m_errorLabel);
|
rootLayout->addWidget(m_detailsPanel);
|
||||||
rootLayout->addLayout(actionRow);
|
|
||||||
rootLayout->addLayout(terminalHeader);
|
rootLayout->addLayout(terminalHeader);
|
||||||
rootLayout->addWidget(m_terminalOutput, 1);
|
rootLayout->addWidget(m_terminalOutput, 1);
|
||||||
rootLayout->addLayout(terminalInputRow);
|
rootLayout->addLayout(eventsHeader);
|
||||||
rootLayout->addWidget(eventHeader);
|
rootLayout->addWidget(m_eventsPanel);
|
||||||
rootLayout->addWidget(m_eventLog, 1);
|
|
||||||
|
setPanelExpanded(m_toggleDetailsButton, m_detailsPanel, QStringLiteral("Details"), true);
|
||||||
|
setPanelExpanded(m_toggleEventsButton, m_eventsPanel, QStringLiteral("Events"), false);
|
||||||
|
|
||||||
|
connect(m_toggleDetailsButton,
|
||||||
|
&QToolButton::toggled,
|
||||||
|
this,
|
||||||
|
[this](bool expanded) {
|
||||||
|
setPanelExpanded(
|
||||||
|
m_toggleDetailsButton, m_detailsPanel, QStringLiteral("Details"), expanded);
|
||||||
|
});
|
||||||
|
connect(m_toggleEventsButton,
|
||||||
|
&QToolButton::toggled,
|
||||||
|
this,
|
||||||
|
[this](bool expanded) {
|
||||||
|
setPanelExpanded(
|
||||||
|
m_toggleEventsButton, m_eventsPanel, QStringLiteral("Events"), expanded);
|
||||||
|
});
|
||||||
|
|
||||||
connect(m_connectButton, &QPushButton::clicked, this, &SessionTab::onConnectClicked);
|
connect(m_connectButton, &QPushButton::clicked, this, &SessionTab::onConnectClicked);
|
||||||
connect(m_disconnectButton, &QPushButton::clicked, this, &SessionTab::onDisconnectClicked);
|
connect(m_disconnectButton, &QPushButton::clicked, this, &SessionTab::onDisconnectClicked);
|
||||||
connect(m_reconnectButton, &QPushButton::clicked, this, &SessionTab::onReconnectClicked);
|
connect(m_reconnectButton, &QPushButton::clicked, this, &SessionTab::onReconnectClicked);
|
||||||
connect(m_copyErrorButton, &QPushButton::clicked, this, &SessionTab::onCopyErrorClicked);
|
connect(m_copyErrorButton, &QPushButton::clicked, this, &SessionTab::onCopyErrorClicked);
|
||||||
connect(m_sendInputButton, &QPushButton::clicked, this, &SessionTab::onSendInputClicked);
|
|
||||||
connect(m_terminalInput, &QLineEdit::returnPressed, this, &SessionTab::onSendInputClicked);
|
|
||||||
connect(m_clearTerminalButton,
|
connect(m_clearTerminalButton,
|
||||||
&QPushButton::clicked,
|
&QPushButton::clicked,
|
||||||
this,
|
this,
|
||||||
&SessionTab::onClearTerminalClicked);
|
&SessionTab::onClearTerminalClicked);
|
||||||
|
connect(m_terminalOutput,
|
||||||
|
&TerminalView::inputGenerated,
|
||||||
|
this,
|
||||||
|
[this](const QString& input) { emit requestInput(input); });
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<SessionConnectOptions> SessionTab::buildConnectOptions()
|
std::optional<SessionConnectOptions> SessionTab::buildConnectOptions()
|
||||||
@@ -455,6 +487,26 @@ void SessionTab::refreshActionButtons()
|
|||||||
|| m_state == SessionState::Disconnected);
|
|| m_state == SessionState::Disconnected);
|
||||||
m_copyErrorButton->setEnabled(!m_lastError.isEmpty());
|
m_copyErrorButton->setEnabled(!m_lastError.isEmpty());
|
||||||
|
|
||||||
m_terminalInput->setEnabled(isConnected);
|
m_terminalOutput->setEnabled(isConnected);
|
||||||
m_sendInputButton->setEnabled(isConnected);
|
if (isConnected) {
|
||||||
|
m_terminalOutput->setFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionTab::setPanelExpanded(QToolButton* button,
|
||||||
|
QWidget* panel,
|
||||||
|
const QString& name,
|
||||||
|
bool expanded)
|
||||||
|
{
|
||||||
|
if (button == nullptr || panel == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
button->blockSignals(true);
|
||||||
|
button->setChecked(expanded);
|
||||||
|
button->blockSignals(false);
|
||||||
|
|
||||||
|
panel->setVisible(expanded);
|
||||||
|
button->setText(expanded ? QStringLiteral("Hide %1").arg(name)
|
||||||
|
: QStringLiteral("Show %1").arg(name));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,14 +6,16 @@
|
|||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include <QPointer>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
class QLabel;
|
class QLabel;
|
||||||
class QLineEdit;
|
|
||||||
class QPlainTextEdit;
|
class QPlainTextEdit;
|
||||||
class QPushButton;
|
class QPushButton;
|
||||||
class QThread;
|
class QThread;
|
||||||
class SessionBackend;
|
class SessionBackend;
|
||||||
|
class TerminalView;
|
||||||
|
class QToolButton;
|
||||||
|
|
||||||
class SessionTab : public QWidget
|
class SessionTab : public QWidget
|
||||||
{
|
{
|
||||||
@@ -38,7 +40,6 @@ private slots:
|
|||||||
void onDisconnectClicked();
|
void onDisconnectClicked();
|
||||||
void onReconnectClicked();
|
void onReconnectClicked();
|
||||||
void onCopyErrorClicked();
|
void onCopyErrorClicked();
|
||||||
void onSendInputClicked();
|
|
||||||
void onClearTerminalClicked();
|
void onClearTerminalClicked();
|
||||||
|
|
||||||
void onBackendStateChanged(SessionState state, const QString& message);
|
void onBackendStateChanged(SessionState state, const QString& message);
|
||||||
@@ -56,15 +57,17 @@ private:
|
|||||||
|
|
||||||
QLabel* m_statusLabel;
|
QLabel* m_statusLabel;
|
||||||
QLabel* m_errorLabel;
|
QLabel* m_errorLabel;
|
||||||
QPlainTextEdit* m_terminalOutput;
|
TerminalView* m_terminalOutput;
|
||||||
QLineEdit* m_terminalInput;
|
|
||||||
QPlainTextEdit* m_eventLog;
|
QPlainTextEdit* m_eventLog;
|
||||||
QPushButton* m_connectButton;
|
QPushButton* m_connectButton;
|
||||||
QPushButton* m_disconnectButton;
|
QPushButton* m_disconnectButton;
|
||||||
QPushButton* m_reconnectButton;
|
QPushButton* m_reconnectButton;
|
||||||
QPushButton* m_copyErrorButton;
|
QPushButton* m_copyErrorButton;
|
||||||
QPushButton* m_sendInputButton;
|
|
||||||
QPushButton* m_clearTerminalButton;
|
QPushButton* m_clearTerminalButton;
|
||||||
|
QToolButton* m_toggleDetailsButton;
|
||||||
|
QToolButton* m_toggleEventsButton;
|
||||||
|
QWidget* m_detailsPanel;
|
||||||
|
QWidget* m_eventsPanel;
|
||||||
|
|
||||||
void setupUi();
|
void setupUi();
|
||||||
std::optional<SessionConnectOptions> buildConnectOptions();
|
std::optional<SessionConnectOptions> buildConnectOptions();
|
||||||
@@ -73,6 +76,7 @@ private:
|
|||||||
void setState(SessionState state, const QString& message);
|
void setState(SessionState state, const QString& message);
|
||||||
QString stateSuffix() const;
|
QString stateSuffix() const;
|
||||||
void refreshActionButtons();
|
void refreshActionButtons();
|
||||||
|
void setPanelExpanded(QToolButton* button, QWidget* panel, const QString& name, bool expanded);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
78
src/terminal_view.cpp
Normal file
78
src/terminal_view.cpp
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
#include "terminal_view.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QClipboard>
|
||||||
|
#include <QKeyEvent>
|
||||||
|
|
||||||
|
TerminalView::TerminalView(QWidget* parent) : QPlainTextEdit(parent)
|
||||||
|
{
|
||||||
|
setReadOnly(true);
|
||||||
|
setUndoRedoEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerminalView::keyPressEvent(QKeyEvent* event)
|
||||||
|
{
|
||||||
|
if (event == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Qt::KeyboardModifiers modifiers = event->modifiers();
|
||||||
|
|
||||||
|
if (modifiers == Qt::ControlModifier) {
|
||||||
|
switch (event->key()) {
|
||||||
|
case Qt::Key_C:
|
||||||
|
emit inputGenerated(QStringLiteral("\x03"));
|
||||||
|
return;
|
||||||
|
case Qt::Key_D:
|
||||||
|
emit inputGenerated(QStringLiteral("\x04"));
|
||||||
|
return;
|
||||||
|
case Qt::Key_L:
|
||||||
|
emit inputGenerated(QStringLiteral("\x0c"));
|
||||||
|
return;
|
||||||
|
case Qt::Key_V: {
|
||||||
|
const QString clipboardText = QApplication::clipboard()->text();
|
||||||
|
if (!clipboardText.isEmpty()) {
|
||||||
|
emit inputGenerated(clipboardText);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (event->key()) {
|
||||||
|
case Qt::Key_Return:
|
||||||
|
case Qt::Key_Enter:
|
||||||
|
emit inputGenerated(QStringLiteral("\n"));
|
||||||
|
return;
|
||||||
|
case Qt::Key_Backspace:
|
||||||
|
emit inputGenerated(QStringLiteral("\x7f"));
|
||||||
|
return;
|
||||||
|
case Qt::Key_Tab:
|
||||||
|
emit inputGenerated(QStringLiteral("\t"));
|
||||||
|
return;
|
||||||
|
case Qt::Key_Left:
|
||||||
|
emit inputGenerated(QStringLiteral("\x1b[D"));
|
||||||
|
return;
|
||||||
|
case Qt::Key_Right:
|
||||||
|
emit inputGenerated(QStringLiteral("\x1b[C"));
|
||||||
|
return;
|
||||||
|
case Qt::Key_Up:
|
||||||
|
emit inputGenerated(QStringLiteral("\x1b[A"));
|
||||||
|
return;
|
||||||
|
case Qt::Key_Down:
|
||||||
|
emit inputGenerated(QStringLiteral("\x1b[B"));
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString text = event->text();
|
||||||
|
if (!text.isEmpty()) {
|
||||||
|
emit inputGenerated(text);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPlainTextEdit::keyPressEvent(event);
|
||||||
|
}
|
||||||
20
src/terminal_view.h
Normal file
20
src/terminal_view.h
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#ifndef ORBITHUB_TERMINAL_VIEW_H
|
||||||
|
#define ORBITHUB_TERMINAL_VIEW_H
|
||||||
|
|
||||||
|
#include <QPlainTextEdit>
|
||||||
|
|
||||||
|
class TerminalView : public QPlainTextEdit
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit TerminalView(QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void inputGenerated(const QString& input);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void keyPressEvent(QKeyEvent* event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user