[plasma/ksshaskpass] /: Add custom ClearText dialog

Kai Uwe Broulik <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit 712db7cf09e18eafe836b390c8a90c705f0c5637 by Kai Uwe Broulik.
Committed on 17/08/2026 at 14:10.
Pushed by broulik into branch 'master'.

Add custom ClearText dialog

ksshaskpass used KPasswordDialog for ClearText as well because it wants the
"Remember" checkbox. However, it is not great to use a password dialog when
it clearly should not use one.

Instead, implement a custom dialog (inspired by KPasswordDialog), including
the "Remember" checkbox but that can also show text in clear.

BUG: 343562

M  +2    -0    CMakeLists.txt
M  +3    -0    src/CMakeLists.txt
A  +168  -0    src/cleartextinputdialog.ui
M  +33   -18   src/main.cpp

https://invent.kde.org/plasma/ksshaskpass/-/commit/712db7cf09e18eafe836b390c8a90c705f0c5637

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7b9f85e..515e84c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -10,6 +10,8 @@ set(KDE_COMPILERSETTINGS_LEVEL "5.82")
 set(CMAKE_CXX_STANDARD 20)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
+set(CMAKE_AUTOUIC ON)
+
 find_package(ECM ${KF6_MIN_VERSION} REQUIRED NO_MODULE)
 set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH})
 
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 2ec6914..22a7f85 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -3,6 +3,9 @@
 
 add_library(ksshaskpass_prompt OBJECT prompt.cpp prompt.h)
 
+ki18n_wrap_ui(ui_SRCS cleartextinputdialog.ui)
+target_sources(ksshaskpass_prompt PRIVATE ${ui_SRCS})
+
 target_include_directories(ksshaskpass_prompt PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
 
 ecm_qt_declare_logging_category(ksshaskpass_prompt HEADER debug.h
diff --git a/src/cleartextinputdialog.ui b/src/cleartextinputdialog.ui
new file mode 100644
index 0000000..26e21ee
--- /dev/null
+++ b/src/cleartextinputdialog.ui
@@ -0,0 +1,168 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ClearTextInputDialog</class>
+ <widget class="QDialog" name="ClearTextInputDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>412</width>
+    <height>152</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="windowIcon">
+   <iconset theme="dialog-password"/>
+  </property>
+  <layout class="QVBoxLayout" name="vBoxLayout">
+   <property name="sizeConstraint">
+    <enum>QLayout::SizeConstraint::SetFixedSize</enum>
+   </property>
+   <item>
+    <layout class="QHBoxLayout" name="hboxLayout">
+     <item>
+      <widget class="QLabel" name="pixmapLabel">
+       <property name="alignment">
+        <set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="prompt">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+         <horstretch>10</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="text">
+        <string notr="true">PROMPT</string>
+       </property>
+       <property name="textFormat">
+        <enum>Qt::TextFormat::PlainText</enum>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter</set>
+       </property>
+       <property name="wordWrap">
+        <bool>true</bool>
+       </property>
+       <property name="textInteractionFlags">
+        <set>Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse</set>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <spacer>
+     <property name="orientation">
+      <enum>Qt::Orientation::Vertical</enum>
+     </property>
+     <property name="sizeType">
+      <enum>QSizePolicy::Policy::Ignored</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>400</width>
+       <height>16</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item>
+    <widget class="QWidget" name="credentialsGroup" native="true">
+     <layout class="QFormLayout" name="formLayout">
+      <property name="leftMargin">
+       <number>1</number>
+      </property>
+      <property name="topMargin">
+       <number>1</number>
+      </property>
+      <property name="rightMargin">
+       <number>1</number>
+      </property>
+      <property name="bottomMargin">
+       <number>1</number>
+      </property>
+      <item row="2" column="1">
+       <widget class="QCheckBox" name="keepCheckBox">
+        <property name="text">
+         <string comment="@option:check">Remember</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="0">
+       <widget class="QLabel" name="passwordLabel">
+        <property name="text">
+         <string comment="@label:textbox">Password:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="KPasswordLineEdit" name="lineEdit"/>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="standardButtons">
+      <set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>KPasswordLineEdit</class>
+   <extends>QWidget</extends>
+   <header>kpasswordlineedit.h</header>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>lineEdit</tabstop>
+  <tabstop>keepCheckBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>ClearTextInputDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>260</x>
+     <y>145</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>252</x>
+     <y>192</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>ClearTextInputDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>336</x>
+     <y>145</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>359</x>
+     <y>145</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/src/main.cpp b/src/main.cpp
index 119a82d..65ec991 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -17,12 +17,15 @@
 #include <QInputDialog>
 #include <QPointer>
 #include <QRegularExpression>
+#include <QStyle>
+#include <QStyleOption>
 #include <QTextStream>
 
 #include <qt6keychain/keychain.h>
 
 #include "debug.h"
 #include "prompt.h"
+#include "ui_cleartextinputdialog.h"
 
 constexpr const char *PROMPT_TYPE_ENV_VAR = "SSH_ASKPASS_PROMPT";
 
@@ -184,28 +187,40 @@ int main(int argc, char **argv)
     }
     case DisplayType::Unknown: // just in case.
     case DisplayType::ClearText:
-    // Should use a dialog with visible input, but KPasswordDialog doesn't support that and
-    // other available dialog types don't have a "Keep" checkbox.
-    /* fallthrough */
     case DisplayType::Password: {
-        // create the password dialog, but only show "Enable Keep" button, if the keychain has a working backend available
-        KPasswordDialog::KPasswordDialogFlag flag(KPasswordDialog::NoFlags);
-        if (!identifier.isNull() && QKeychain::isAvailable()) {
-            flag = KPasswordDialog::ShowKeepPassword;
-        }
-        QPointer<KPasswordDialog> kpd = new KPasswordDialog(nullptr, flag);
+        // custom dialog inspired by KPasswordDialog, including "Remember" option,
+        // but has the ability to show the password.
+        QDialog dlg;
+
+        Ui_ClearTextInputDialog ui;
+        ui.setupUi(&dlg);
+
+        ui.prompt->setText(dialog);
+        ui.keepCheckBox->setVisible(!identifier.isEmpty() && QKeychain::isAvailable());
+
+        QStyleOption option;
+        option.initFrom(&dlg);
+        const int iconSize = dlg.style()->pixelMetric(QStyle::PM_MessageBoxIconSize, &option, &dlg);
+        ui.pixmapLabel->setPixmap(QIcon::fromTheme(QIcon::ThemeIcon::DialogPassword).pixmap(iconSize));
 
-        kpd->setPrompt(dialog);
-        // We don't want to dump core when the password dialog is shown, because it could contain the entered password.
-        // KPasswordDialog::disableCoreDumps() seems to be gone in KDE 4 -- do it manually
-        struct rlimit rlim;
-        rlim.rlim_cur = rlim.rlim_max = 0;
-        setrlimit(RLIMIT_CORE, &rlim);
+        if (displayType == DisplayType::Password) {
+            ui.keepCheckBox->setText(i18nc("@option:check", "Remember password"));
+
+            // We don't want to dump core when the password dialog is shown, because it could contain the entered password.
+            // KPasswordDialog::disableCoreDumps() seems to be gone in KDE 4 -- do it manually
+            struct rlimit rlim;
+            rlim.rlim_cur = rlim.rlim_max = 0;
+            setrlimit(RLIMIT_CORE, &rlim);
+        } else {
+            ui.lineEdit->setEchoMode(QLineEdit::Normal);
+            ui.lineEdit->setRevealPasswordMode(KPassword::RevealMode::Never);
+            ui.passwordLabel->hide();
+        }
 
-        if (kpd->exec() == QDialog::Accepted) {
-            item = kpd->password();
+        if (dlg.exec() == QDialog::Accepted) {
+            item = ui.lineEdit->password();
             // If “Enable Keep” is enabled, store the password in keychain
-            if ((!identifier.isNull()) && kpd->keepPassword()) {
+            if ((!identifier.isNull()) && ui.keepCheckBox->isChecked()) {
                 QKeychain::WritePasswordJob job(app.applicationName());
                 job.setKey(identifier);
                 job.setTextData(item);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.