Internal
Public Access
Session UX: inline password prompt and terminal font size controls
Replace the modal RDP/SSH password dialog with an inline prompt bar embedded in the session tab instead of a separate popup window. Add per-tab terminal font size controls (increase/decrease/reset/set exact point size) via the tab context menu, with the chosen size persisted across sessions. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+246
-81
@@ -13,7 +13,6 @@
|
|||||||
#include <QFont>
|
#include <QFont>
|
||||||
#include <QFontDatabase>
|
#include <QFontDatabase>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QInputDialog>
|
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
@@ -22,6 +21,7 @@
|
|||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
#include <QProcessEnvironment>
|
#include <QProcessEnvironment>
|
||||||
|
#include <QPushButton>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QToolButton>
|
#include <QToolButton>
|
||||||
@@ -71,6 +71,9 @@ SessionTab::SessionTab(const Profile& profile,
|
|||||||
m_terminalThemeName(preferences.terminalThemeName.trimmed().isEmpty()
|
m_terminalThemeName(preferences.terminalThemeName.trimmed().isEmpty()
|
||||||
? QStringLiteral("Dark")
|
? QStringLiteral("Dark")
|
||||||
: preferences.terminalThemeName.trimmed()),
|
: preferences.terminalThemeName.trimmed()),
|
||||||
|
m_terminalFontPointSize(preferences.terminalFontPointSize > 0
|
||||||
|
? preferences.terminalFontPointSize
|
||||||
|
: 0),
|
||||||
m_sshTerminal(nullptr),
|
m_sshTerminal(nullptr),
|
||||||
m_rdpDisplay(nullptr),
|
m_rdpDisplay(nullptr),
|
||||||
m_terminalOutput(nullptr),
|
m_terminalOutput(nullptr),
|
||||||
@@ -81,6 +84,11 @@ SessionTab::SessionTab(const Profile& profile,
|
|||||||
m_clearEventsButton(nullptr),
|
m_clearEventsButton(nullptr),
|
||||||
m_exportEventsButton(nullptr),
|
m_exportEventsButton(nullptr),
|
||||||
m_eventsPanel(nullptr),
|
m_eventsPanel(nullptr),
|
||||||
|
m_passwordPromptBar(nullptr),
|
||||||
|
m_passwordPromptLabel(nullptr),
|
||||||
|
m_passwordPromptInput(nullptr),
|
||||||
|
m_passwordPromptConnectButton(nullptr),
|
||||||
|
m_passwordPromptCancelButton(nullptr),
|
||||||
m_eventSeverityFilter(EventSeverity::Info),
|
m_eventSeverityFilter(EventSeverity::Info),
|
||||||
m_eventsPanelExpanded(preferences.eventsPanelExpanded)
|
m_eventsPanelExpanded(preferences.eventsPanelExpanded)
|
||||||
{
|
{
|
||||||
@@ -265,21 +273,20 @@ void SessionTab::connectSession()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::optional<SessionConnectOptions> options = buildConnectOptions();
|
requestConnectOptions([this](std::optional<SessionConnectOptions> options) {
|
||||||
if (!options.has_value()) {
|
if (!options.has_value()) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_lastConnectOptions = options.value();
|
|
||||||
|
|
||||||
if (m_useKodoTermForSsh) {
|
|
||||||
if (!startSshTerminal(options.value())) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
emit requestConnect(options.value());
|
m_lastConnectOptions = options.value();
|
||||||
|
|
||||||
|
if (m_useKodoTermForSsh) {
|
||||||
|
startSshTerminal(options.value());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit requestConnect(options.value());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void SessionTab::disconnectSession()
|
void SessionTab::disconnectSession()
|
||||||
@@ -305,24 +312,25 @@ void SessionTab::reconnectSession()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::optional<SessionConnectOptions> options = buildConnectOptions();
|
requestConnectOptions([this](std::optional<SessionConnectOptions> options) {
|
||||||
if (!options.has_value()) {
|
if (!options.has_value()) {
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
m_lastConnectOptions = options.value();
|
|
||||||
|
|
||||||
if (m_useKodoTermForSsh) {
|
|
||||||
if (m_sshTerminal != nullptr) {
|
|
||||||
m_sshTerminal->kill();
|
|
||||||
}
|
}
|
||||||
QTimer::singleShot(50,
|
|
||||||
this,
|
|
||||||
[this, options]() { startSshTerminal(options.value()); });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
emit requestReconnect(options.value());
|
m_lastConnectOptions = options.value();
|
||||||
|
|
||||||
|
if (m_useKodoTermForSsh) {
|
||||||
|
if (m_sshTerminal != nullptr) {
|
||||||
|
m_sshTerminal->kill();
|
||||||
|
}
|
||||||
|
QTimer::singleShot(50,
|
||||||
|
this,
|
||||||
|
[this, options]() { startSshTerminal(options.value()); });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit requestReconnect(options.value());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void SessionTab::clearTerminal()
|
void SessionTab::clearTerminal()
|
||||||
@@ -380,6 +388,84 @@ bool SessionTab::supportsClearAction() const
|
|||||||
return m_useKodoTermForSsh || m_terminalOutput != nullptr;
|
return m_useKodoTermForSsh || m_terminalOutput != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SessionTab::supportsZoom() const
|
||||||
|
{
|
||||||
|
return m_useKodoTermForSsh || m_terminalOutput != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionTab::zoomIn()
|
||||||
|
{
|
||||||
|
if (m_useKodoTermForSsh && m_sshTerminal != nullptr) {
|
||||||
|
m_sshTerminal->zoomIn();
|
||||||
|
m_terminalFontPointSize = m_sshTerminal->getConfig().font.pointSize();
|
||||||
|
} else if (m_terminalOutput != nullptr) {
|
||||||
|
m_terminalFontPointSize = m_terminalOutput->font().pointSize() + 1;
|
||||||
|
m_terminalOutput->setFontPointSize(m_terminalFontPointSize);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit terminalFontSizeChanged(m_terminalFontPointSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionTab::zoomOut()
|
||||||
|
{
|
||||||
|
if (m_useKodoTermForSsh && m_sshTerminal != nullptr) {
|
||||||
|
m_sshTerminal->zoomOut();
|
||||||
|
m_terminalFontPointSize = m_sshTerminal->getConfig().font.pointSize();
|
||||||
|
} else if (m_terminalOutput != nullptr) {
|
||||||
|
const int newSize = m_terminalOutput->font().pointSize() - 1;
|
||||||
|
if (newSize < 6) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_terminalFontPointSize = newSize;
|
||||||
|
m_terminalOutput->setFontPointSize(m_terminalFontPointSize);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit terminalFontSizeChanged(m_terminalFontPointSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionTab::resetZoom()
|
||||||
|
{
|
||||||
|
if (m_useKodoTermForSsh && m_sshTerminal != nullptr) {
|
||||||
|
m_sshTerminal->resetZoom();
|
||||||
|
m_terminalFontPointSize = m_sshTerminal->getConfig().font.pointSize();
|
||||||
|
} else if (m_terminalOutput != nullptr) {
|
||||||
|
m_terminalFontPointSize = defaultTerminalFont().pointSize();
|
||||||
|
m_terminalOutput->setFontPointSize(m_terminalFontPointSize);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit terminalFontSizeChanged(m_terminalFontPointSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionTab::setTerminalFontPointSize(int pointSize)
|
||||||
|
{
|
||||||
|
const int clamped = qBound(6, pointSize, 72);
|
||||||
|
|
||||||
|
if (m_useKodoTermForSsh && m_sshTerminal != nullptr) {
|
||||||
|
KodoTermConfig config = m_sshTerminal->getConfig();
|
||||||
|
config.font.setPointSize(clamped);
|
||||||
|
m_sshTerminal->setConfig(config);
|
||||||
|
m_terminalFontPointSize = clamped;
|
||||||
|
} else if (m_terminalOutput != nullptr) {
|
||||||
|
m_terminalFontPointSize = clamped;
|
||||||
|
m_terminalOutput->setFontPointSize(clamped);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit terminalFontSizeChanged(m_terminalFontPointSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
int SessionTab::terminalFontPointSize() const
|
||||||
|
{
|
||||||
|
return m_terminalFontPointSize;
|
||||||
|
}
|
||||||
|
|
||||||
bool SessionTab::isEventsPanelExpanded() const
|
bool SessionTab::isEventsPanelExpanded() const
|
||||||
{
|
{
|
||||||
return m_eventsPanelExpanded;
|
return m_eventsPanelExpanded;
|
||||||
@@ -535,7 +621,10 @@ void SessionTab::setupUi()
|
|||||||
|
|
||||||
if (m_useKodoTermForSsh) {
|
if (m_useKodoTermForSsh) {
|
||||||
m_sshTerminal = new KodoTerm(this);
|
m_sshTerminal = new KodoTerm(this);
|
||||||
const QFont terminalFont = defaultTerminalFont();
|
QFont terminalFont = defaultTerminalFont();
|
||||||
|
if (m_terminalFontPointSize > 0) {
|
||||||
|
terminalFont.setPointSize(m_terminalFontPointSize);
|
||||||
|
}
|
||||||
|
|
||||||
KodoTermConfig config = m_sshTerminal->getConfig();
|
KodoTermConfig config = m_sshTerminal->getConfig();
|
||||||
config.font = terminalFont;
|
config.font = terminalFont;
|
||||||
@@ -548,7 +637,11 @@ void SessionTab::setupUi()
|
|||||||
rootLayout->addWidget(m_rdpDisplay, 1);
|
rootLayout->addWidget(m_rdpDisplay, 1);
|
||||||
} else {
|
} else {
|
||||||
m_terminalOutput = new TerminalView(this);
|
m_terminalOutput = new TerminalView(this);
|
||||||
m_terminalOutput->setFont(defaultTerminalFont());
|
QFont fallbackFont = defaultTerminalFont();
|
||||||
|
if (m_terminalFontPointSize > 0) {
|
||||||
|
fallbackFont.setPointSize(m_terminalFontPointSize);
|
||||||
|
}
|
||||||
|
m_terminalOutput->setFont(fallbackFont);
|
||||||
m_terminalOutput->setMinimumHeight(260);
|
m_terminalOutput->setMinimumHeight(260);
|
||||||
m_terminalOutput->setReadOnly(true);
|
m_terminalOutput->setReadOnly(true);
|
||||||
if (m_profile.protocol.compare(QStringLiteral("VNC"), Qt::CaseInsensitive) == 0) {
|
if (m_profile.protocol.compare(QStringLiteral("VNC"), Qt::CaseInsensitive) == 0) {
|
||||||
@@ -563,6 +656,45 @@ void SessionTab::setupUi()
|
|||||||
|
|
||||||
applyTerminalTheme(m_terminalThemeName);
|
applyTerminalTheme(m_terminalThemeName);
|
||||||
|
|
||||||
|
auto* passwordPromptLayout = new QHBoxLayout();
|
||||||
|
m_passwordPromptLabel = new QLabel(this);
|
||||||
|
m_passwordPromptInput = new QLineEdit(this);
|
||||||
|
m_passwordPromptInput->setEchoMode(QLineEdit::Password);
|
||||||
|
m_passwordPromptConnectButton = new QPushButton(QStringLiteral("Connect"), this);
|
||||||
|
m_passwordPromptCancelButton = new QPushButton(QStringLiteral("Cancel"), this);
|
||||||
|
passwordPromptLayout->addWidget(m_passwordPromptLabel);
|
||||||
|
passwordPromptLayout->addWidget(m_passwordPromptInput, 1);
|
||||||
|
passwordPromptLayout->addWidget(m_passwordPromptConnectButton);
|
||||||
|
passwordPromptLayout->addWidget(m_passwordPromptCancelButton);
|
||||||
|
|
||||||
|
m_passwordPromptBar = new QWidget(this);
|
||||||
|
m_passwordPromptBar->setLayout(passwordPromptLayout);
|
||||||
|
m_passwordPromptBar->setAutoFillBackground(true);
|
||||||
|
m_passwordPromptBar->setVisible(false);
|
||||||
|
rootLayout->addWidget(m_passwordPromptBar);
|
||||||
|
|
||||||
|
connect(m_passwordPromptConnectButton, &QPushButton::clicked, this, [this]() {
|
||||||
|
if (!m_passwordPromptCallback) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const QString password = m_passwordPromptInput->text();
|
||||||
|
const auto callback = m_passwordPromptCallback;
|
||||||
|
hidePasswordPrompt();
|
||||||
|
callback(password);
|
||||||
|
});
|
||||||
|
connect(m_passwordPromptCancelButton, &QPushButton::clicked, this, [this]() {
|
||||||
|
if (!m_passwordPromptCallback) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto callback = m_passwordPromptCallback;
|
||||||
|
hidePasswordPrompt();
|
||||||
|
callback(std::nullopt);
|
||||||
|
});
|
||||||
|
connect(m_passwordPromptInput,
|
||||||
|
&QLineEdit::returnPressed,
|
||||||
|
m_passwordPromptConnectButton,
|
||||||
|
&QPushButton::click);
|
||||||
|
|
||||||
auto* eventsHeader = new QHBoxLayout();
|
auto* eventsHeader = new QHBoxLayout();
|
||||||
m_toggleEventsButton = new QToolButton(this);
|
m_toggleEventsButton = new QToolButton(this);
|
||||||
m_toggleEventsButton->setCheckable(true);
|
m_toggleEventsButton->setCheckable(true);
|
||||||
@@ -676,77 +808,85 @@ void SessionTab::setupUi()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<SessionConnectOptions> SessionTab::buildConnectOptions()
|
void SessionTab::requestConnectOptions(
|
||||||
|
std::function<void(std::optional<SessionConnectOptions>)> callback)
|
||||||
{
|
{
|
||||||
SessionConnectOptions options;
|
SessionConnectOptions baseOptions;
|
||||||
options.knownHostsPolicy = m_profile.knownHostsPolicy;
|
baseOptions.knownHostsPolicy = m_profile.knownHostsPolicy;
|
||||||
|
|
||||||
const bool isSsh = m_profile.protocol.compare(QStringLiteral("SSH"), Qt::CaseInsensitive) == 0;
|
const bool isSsh = m_profile.protocol.compare(QStringLiteral("SSH"), Qt::CaseInsensitive) == 0;
|
||||||
const bool isRdp = m_profile.protocol.compare(QStringLiteral("RDP"), Qt::CaseInsensitive) == 0;
|
const bool isRdp = m_profile.protocol.compare(QStringLiteral("RDP"), Qt::CaseInsensitive) == 0;
|
||||||
|
|
||||||
if (!isSsh && !isRdp) {
|
if (!isSsh && !isRdp) {
|
||||||
return options;
|
callback(baseOptions);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isRdp) {
|
if (isRdp) {
|
||||||
if (m_profile.authMode.compare(QStringLiteral("Password"), Qt::CaseInsensitive) != 0) {
|
if (m_profile.authMode.compare(QStringLiteral("Password"), Qt::CaseInsensitive) != 0) {
|
||||||
return options;
|
callback(baseOptions);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool accepted = false;
|
const QString label = QStringLiteral("RDP password for %1:")
|
||||||
const QString password = QInputDialog::getText(
|
.arg(m_profile.username.trimmed().isEmpty()
|
||||||
this,
|
? m_profile.host
|
||||||
QStringLiteral("RDP Password"),
|
: QStringLiteral("%1@%2").arg(m_profile.username, m_profile.host));
|
||||||
QStringLiteral("Password for %1:")
|
|
||||||
.arg(m_profile.username.trimmed().isEmpty()
|
|
||||||
? m_profile.host
|
|
||||||
: QStringLiteral("%1@%2").arg(m_profile.username, m_profile.host)),
|
|
||||||
QLineEdit::Password,
|
|
||||||
QString(),
|
|
||||||
&accepted);
|
|
||||||
if (!accepted) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (password.isEmpty()) {
|
showPasswordPrompt(
|
||||||
QMessageBox::warning(this,
|
label,
|
||||||
QStringLiteral("Connect"),
|
[this, baseOptions, callback](std::optional<QString> password) {
|
||||||
QStringLiteral("Password is required for password authentication."));
|
if (!password.has_value()) {
|
||||||
return std::nullopt;
|
callback(std::nullopt);
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
options.password = password;
|
if (password->isEmpty()) {
|
||||||
return options;
|
QMessageBox::warning(
|
||||||
|
this,
|
||||||
|
QStringLiteral("Connect"),
|
||||||
|
QStringLiteral("Password is required for password authentication."));
|
||||||
|
callback(std::nullopt);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SessionConnectOptions options = baseOptions;
|
||||||
|
options.password = password.value();
|
||||||
|
callback(options);
|
||||||
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_useKodoTermForSsh
|
if (m_useKodoTermForSsh
|
||||||
&& m_profile.authMode.compare(QStringLiteral("Password"), Qt::CaseInsensitive) == 0) {
|
&& m_profile.authMode.compare(QStringLiteral("Password"), Qt::CaseInsensitive) == 0) {
|
||||||
// Password is entered directly in terminal prompt.
|
// Password is entered directly in terminal prompt.
|
||||||
return options;
|
callback(baseOptions);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_profile.authMode.compare(QStringLiteral("Password"), Qt::CaseInsensitive) == 0) {
|
if (m_profile.authMode.compare(QStringLiteral("Password"), Qt::CaseInsensitive) == 0) {
|
||||||
bool accepted = false;
|
showPasswordPrompt(
|
||||||
const QString password = QInputDialog::getText(this,
|
QStringLiteral("SSH password for %1@%2:").arg(m_profile.username, m_profile.host),
|
||||||
QStringLiteral("SSH Password"),
|
[this, baseOptions, callback](std::optional<QString> password) {
|
||||||
QStringLiteral("Password for %1@%2:")
|
if (!password.has_value()) {
|
||||||
.arg(m_profile.username, m_profile.host),
|
callback(std::nullopt);
|
||||||
QLineEdit::Password,
|
return;
|
||||||
QString(),
|
}
|
||||||
&accepted);
|
|
||||||
if (!accepted) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (password.isEmpty()) {
|
if (password->isEmpty()) {
|
||||||
QMessageBox::warning(this,
|
QMessageBox::warning(
|
||||||
QStringLiteral("Connect"),
|
this,
|
||||||
QStringLiteral("Password is required for password authentication."));
|
QStringLiteral("Connect"),
|
||||||
return std::nullopt;
|
QStringLiteral("Password is required for password authentication."));
|
||||||
}
|
callback(std::nullopt);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
options.password = password;
|
SessionConnectOptions options = baseOptions;
|
||||||
return options;
|
options.password = password.value();
|
||||||
|
callback(options);
|
||||||
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString keyPath = m_profile.privateKeyPath.trimmed();
|
QString keyPath = m_profile.privateKeyPath.trimmed();
|
||||||
@@ -756,7 +896,8 @@ std::optional<SessionConnectOptions> SessionTab::buildConnectOptions()
|
|||||||
QString(),
|
QString(),
|
||||||
QStringLiteral("All Files (*)"));
|
QStringLiteral("All Files (*)"));
|
||||||
if (keyPath.isEmpty()) {
|
if (keyPath.isEmpty()) {
|
||||||
return std::nullopt;
|
callback(std::nullopt);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -764,11 +905,35 @@ std::optional<SessionConnectOptions> SessionTab::buildConnectOptions()
|
|||||||
QMessageBox::warning(this,
|
QMessageBox::warning(this,
|
||||||
QStringLiteral("Connect"),
|
QStringLiteral("Connect"),
|
||||||
QStringLiteral("Private key file not found: %1").arg(keyPath));
|
QStringLiteral("Private key file not found: %1").arg(keyPath));
|
||||||
return std::nullopt;
|
callback(std::nullopt);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SessionConnectOptions options = baseOptions;
|
||||||
options.privateKeyPath = keyPath;
|
options.privateKeyPath = keyPath;
|
||||||
return options;
|
callback(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionTab::showPasswordPrompt(const QString& labelText,
|
||||||
|
std::function<void(std::optional<QString>)> callback)
|
||||||
|
{
|
||||||
|
if (m_passwordPromptCallback) {
|
||||||
|
const auto previousCallback = m_passwordPromptCallback;
|
||||||
|
m_passwordPromptCallback = nullptr;
|
||||||
|
previousCallback(std::nullopt);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_passwordPromptCallback = std::move(callback);
|
||||||
|
m_passwordPromptLabel->setText(labelText);
|
||||||
|
m_passwordPromptInput->clear();
|
||||||
|
m_passwordPromptBar->setVisible(true);
|
||||||
|
m_passwordPromptInput->setFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionTab::hidePasswordPrompt()
|
||||||
|
{
|
||||||
|
m_passwordPromptBar->setVisible(false);
|
||||||
|
m_passwordPromptCallback = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SessionTab::validateProfileForConnect()
|
bool SessionTab::validateProfileForConnect()
|
||||||
|
|||||||
+22
-1
@@ -8,6 +8,7 @@
|
|||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -19,12 +20,15 @@ class RdpDisplayWidget;
|
|||||||
class QToolButton;
|
class QToolButton;
|
||||||
class QLineEdit;
|
class QLineEdit;
|
||||||
class QComboBox;
|
class QComboBox;
|
||||||
|
class QLabel;
|
||||||
|
class QPushButton;
|
||||||
class KodoTerm;
|
class KodoTerm;
|
||||||
|
|
||||||
struct SessionUiPreferences
|
struct SessionUiPreferences
|
||||||
{
|
{
|
||||||
QString terminalThemeName = QStringLiteral("Dark");
|
QString terminalThemeName = QStringLiteral("Dark");
|
||||||
bool eventsPanelExpanded = false;
|
bool eventsPanelExpanded = false;
|
||||||
|
int terminalFontPointSize = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SessionTab : public QWidget
|
class SessionTab : public QWidget
|
||||||
@@ -46,6 +50,12 @@ public:
|
|||||||
QString terminalThemeName() const;
|
QString terminalThemeName() const;
|
||||||
bool supportsThemeSelection() const;
|
bool supportsThemeSelection() const;
|
||||||
bool supportsClearAction() const;
|
bool supportsClearAction() const;
|
||||||
|
bool supportsZoom() const;
|
||||||
|
void zoomIn();
|
||||||
|
void zoomOut();
|
||||||
|
void resetZoom();
|
||||||
|
void setTerminalFontPointSize(int pointSize);
|
||||||
|
int terminalFontPointSize() const;
|
||||||
bool isEventsPanelExpanded() const;
|
bool isEventsPanelExpanded() const;
|
||||||
void setEventsPanelExpanded(bool expanded);
|
void setEventsPanelExpanded(bool expanded);
|
||||||
void clearEvents();
|
void clearEvents();
|
||||||
@@ -56,6 +66,7 @@ signals:
|
|||||||
void tabTitleChanged(const QString& title);
|
void tabTitleChanged(const QString& title);
|
||||||
void tabStateChanged(SessionState state);
|
void tabStateChanged(SessionState state);
|
||||||
void terminalThemeChanged(const QString& themeName);
|
void terminalThemeChanged(const QString& themeName);
|
||||||
|
void terminalFontSizeChanged(int pointSize);
|
||||||
void eventsPanelVisibilityChanged(bool expanded);
|
void eventsPanelVisibilityChanged(bool expanded);
|
||||||
void requestConnect(const SessionConnectOptions& options);
|
void requestConnect(const SessionConnectOptions& options);
|
||||||
void requestDisconnect();
|
void requestDisconnect();
|
||||||
@@ -88,6 +99,7 @@ private:
|
|||||||
QString m_lastError;
|
QString m_lastError;
|
||||||
SessionConnectOptions m_lastConnectOptions;
|
SessionConnectOptions m_lastConnectOptions;
|
||||||
QString m_terminalThemeName;
|
QString m_terminalThemeName;
|
||||||
|
int m_terminalFontPointSize;
|
||||||
|
|
||||||
KodoTerm* m_sshTerminal;
|
KodoTerm* m_sshTerminal;
|
||||||
RdpDisplayWidget* m_rdpDisplay;
|
RdpDisplayWidget* m_rdpDisplay;
|
||||||
@@ -99,6 +111,12 @@ private:
|
|||||||
QToolButton* m_clearEventsButton;
|
QToolButton* m_clearEventsButton;
|
||||||
QToolButton* m_exportEventsButton;
|
QToolButton* m_exportEventsButton;
|
||||||
QWidget* m_eventsPanel;
|
QWidget* m_eventsPanel;
|
||||||
|
QWidget* m_passwordPromptBar;
|
||||||
|
QLabel* m_passwordPromptLabel;
|
||||||
|
QLineEdit* m_passwordPromptInput;
|
||||||
|
QPushButton* m_passwordPromptConnectButton;
|
||||||
|
QPushButton* m_passwordPromptCancelButton;
|
||||||
|
std::function<void(std::optional<QString>)> m_passwordPromptCallback;
|
||||||
enum class EventSeverity {
|
enum class EventSeverity {
|
||||||
Info,
|
Info,
|
||||||
Warning,
|
Warning,
|
||||||
@@ -114,7 +132,10 @@ private:
|
|||||||
bool m_eventsPanelExpanded;
|
bool m_eventsPanelExpanded;
|
||||||
|
|
||||||
void setupUi();
|
void setupUi();
|
||||||
std::optional<SessionConnectOptions> buildConnectOptions();
|
void requestConnectOptions(std::function<void(std::optional<SessionConnectOptions>)> callback);
|
||||||
|
void showPasswordPrompt(const QString& labelText,
|
||||||
|
std::function<void(std::optional<QString>)> callback);
|
||||||
|
void hidePasswordPrompt();
|
||||||
bool validateProfileForConnect();
|
bool validateProfileForConnect();
|
||||||
void appendEvent(const QString& message);
|
void appendEvent(const QString& message);
|
||||||
void setState(SessionState state, const QString& message);
|
void setState(SessionState state, const QString& message);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
#include <QColor>
|
#include <QColor>
|
||||||
|
#include <QInputDialog>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QMenuBar>
|
#include <QMenuBar>
|
||||||
#include <QPalette>
|
#include <QPalette>
|
||||||
@@ -105,6 +106,18 @@ SessionWindow::SessionWindow(const Profile& profile, QWidget* parent)
|
|||||||
clearAction = menu.addAction(QStringLiteral("Clear"));
|
clearAction = menu.addAction(QStringLiteral("Clear"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QAction* zoomInAction = nullptr;
|
||||||
|
QAction* zoomOutAction = nullptr;
|
||||||
|
QAction* resetZoomAction = nullptr;
|
||||||
|
QAction* setFontSizeAction = nullptr;
|
||||||
|
if (tab->supportsZoom()) {
|
||||||
|
menu.addSeparator();
|
||||||
|
zoomInAction = menu.addAction(QStringLiteral("Increase Font Size"));
|
||||||
|
zoomOutAction = menu.addAction(QStringLiteral("Decrease Font Size"));
|
||||||
|
resetZoomAction = menu.addAction(QStringLiteral("Reset Font Size"));
|
||||||
|
setFontSizeAction = menu.addAction(QStringLiteral("Set Font Size..."));
|
||||||
|
}
|
||||||
|
|
||||||
QAction* chosen = menu.exec(m_tabs->tabBar()->mapToGlobal(pos));
|
QAction* chosen = menu.exec(m_tabs->tabBar()->mapToGlobal(pos));
|
||||||
if (chosen == disconnectAction) {
|
if (chosen == disconnectAction) {
|
||||||
tab->disconnectSession();
|
tab->disconnectSession();
|
||||||
@@ -120,6 +133,27 @@ SessionWindow::SessionWindow(const Profile& profile, QWidget* parent)
|
|||||||
tab->clearEvents();
|
tab->clearEvents();
|
||||||
} else if (clearAction != nullptr && chosen == clearAction) {
|
} else if (clearAction != nullptr && chosen == clearAction) {
|
||||||
tab->clearTerminal();
|
tab->clearTerminal();
|
||||||
|
} else if (zoomInAction != nullptr && chosen == zoomInAction) {
|
||||||
|
tab->zoomIn();
|
||||||
|
} else if (zoomOutAction != nullptr && chosen == zoomOutAction) {
|
||||||
|
tab->zoomOut();
|
||||||
|
} else if (resetZoomAction != nullptr && chosen == resetZoomAction) {
|
||||||
|
tab->resetZoom();
|
||||||
|
} else if (setFontSizeAction != nullptr && chosen == setFontSizeAction) {
|
||||||
|
bool accepted = false;
|
||||||
|
const int currentSize =
|
||||||
|
tab->terminalFontPointSize() > 0 ? tab->terminalFontPointSize() : 10;
|
||||||
|
const int newSize = QInputDialog::getInt(this,
|
||||||
|
QStringLiteral("Set Font Size"),
|
||||||
|
QStringLiteral("Font size (points):"),
|
||||||
|
currentSize,
|
||||||
|
6,
|
||||||
|
72,
|
||||||
|
1,
|
||||||
|
&accepted);
|
||||||
|
if (accepted) {
|
||||||
|
tab->setTerminalFontPointSize(newSize);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
for (QAction* themeAction : themeActions) {
|
for (QAction* themeAction : themeActions) {
|
||||||
if (chosen == themeAction) {
|
if (chosen == themeAction) {
|
||||||
@@ -185,6 +219,16 @@ void SessionWindow::addSessionTab(const Profile& profile)
|
|||||||
: themeName.trimmed();
|
: themeName.trimmed();
|
||||||
saveUiPreferences();
|
saveUiPreferences();
|
||||||
});
|
});
|
||||||
|
connect(tab,
|
||||||
|
&SessionTab::terminalFontSizeChanged,
|
||||||
|
this,
|
||||||
|
[this](int pointSize) {
|
||||||
|
if (pointSize <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_preferences.terminalFontPointSize = pointSize;
|
||||||
|
saveUiPreferences();
|
||||||
|
});
|
||||||
connect(tab,
|
connect(tab,
|
||||||
&SessionTab::eventsPanelVisibilityChanged,
|
&SessionTab::eventsPanelVisibilityChanged,
|
||||||
this,
|
this,
|
||||||
@@ -216,6 +260,8 @@ void SessionWindow::loadUiPreferences()
|
|||||||
}
|
}
|
||||||
m_preferences.eventsPanelExpanded =
|
m_preferences.eventsPanelExpanded =
|
||||||
settings.value(QStringLiteral("session/eventsPanelExpanded"), false).toBool();
|
settings.value(QStringLiteral("session/eventsPanelExpanded"), false).toBool();
|
||||||
|
m_preferences.terminalFontPointSize =
|
||||||
|
settings.value(QStringLiteral("session/terminalFontPointSize"), 0).toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SessionWindow::saveUiPreferences() const
|
void SessionWindow::saveUiPreferences() const
|
||||||
@@ -225,4 +271,6 @@ void SessionWindow::saveUiPreferences() const
|
|||||||
m_preferences.terminalThemeName);
|
m_preferences.terminalThemeName);
|
||||||
settings.setValue(QStringLiteral("session/eventsPanelExpanded"),
|
settings.setValue(QStringLiteral("session/eventsPanelExpanded"),
|
||||||
m_preferences.eventsPanelExpanded);
|
m_preferences.eventsPanelExpanded);
|
||||||
|
settings.setValue(QStringLiteral("session/terminalFontPointSize"),
|
||||||
|
m_preferences.terminalFontPointSize);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,14 @@ void TerminalView::setThemeName(const QString& themeName)
|
|||||||
applyThemePalette(paletteByName(themeName));
|
applyThemePalette(paletteByName(themeName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TerminalView::setFontPointSize(int pointSize)
|
||||||
|
{
|
||||||
|
QFont updatedFont = font();
|
||||||
|
updatedFont.setPointSize(pointSize);
|
||||||
|
setFont(updatedFont);
|
||||||
|
emitTerminalSize();
|
||||||
|
}
|
||||||
|
|
||||||
void TerminalView::appendTerminalData(const QString& data)
|
void TerminalView::appendTerminalData(const QString& data)
|
||||||
{
|
{
|
||||||
if (data.isEmpty()) {
|
if (data.isEmpty()) {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ public:
|
|||||||
static QStringList themeNames();
|
static QStringList themeNames();
|
||||||
void setThemeName(const QString& themeName);
|
void setThemeName(const QString& themeName);
|
||||||
void appendTerminalData(const QString& data);
|
void appendTerminalData(const QString& data);
|
||||||
|
void setFontPointSize(int pointSize);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void inputGenerated(const QString& input);
|
void inputGenerated(const QString& input);
|
||||||
|
|||||||
Reference in New Issue
Block a user