QtPass Developer documentation
QtPass test coverage
Current view: top level - src/fieldlabel.cpp (source / functions) Coverage Total Hit
Test: QtPass Lines: 100.0 % 68 68
Test Date: 2026-11-14 13:19:18 Functions: 100.0 % 7 7

            Line data    Source code
       1              : // SPDX-FileCopyrightText: 2026 Anne Jan Brouwer
       2              : // SPDX-License-Identifier: GPL-3.0-or-later
       3              : /**
       4              :  * @file
       5              :  * @brief Implements FieldLabel; startEdit() and finishEdit() swap the label
       6              :  *        and its editor in and out of the parent's QFormLayout cell.
       7              :  */
       8              : #include "fieldlabel.h"
       9              : 
      10              : #include <QContextMenuEvent>
      11              : #include <QFormLayout>
      12              : #include <QKeyEvent>
      13              : #include <QLineEdit>
      14              : #include <QMenu>
      15              : 
      16           34 : FieldLabel::FieldLabel(const QString &name, QWidget *parent) : QLabel(parent) {
      17              :   // A field name comes from the entry, which a shared store's other writers
      18              :   // control: as rich text, <img src="/dev/zero"> would have Qt read that
      19              :   // file without end. Plain text before the name is set.
      20           34 :   setTextFormat(Qt::PlainText);
      21           34 :   setText(name);
      22           34 :   setToolTip(tr("Double-click to rename this field"));
      23           34 : }
      24              : 
      25            2 : void FieldLabel::mouseDoubleClickEvent(QMouseEvent *event) {
      26            2 :   if (event->button() == Qt::LeftButton) {
      27            1 :     startEdit();
      28            1 :     return;
      29              :   }
      30            1 :   QLabel::mouseDoubleClickEvent(event);
      31              : }
      32              : 
      33            2 : void FieldLabel::contextMenuEvent(QContextMenuEvent *event) {
      34            2 :   QMenu menu(this);
      35            2 :   menu.addAction(tr("Rename field…"), this, &FieldLabel::startEdit);
      36            4 :   menu.addAction(tr("Remove field"), this, &FieldLabel::removeRequested);
      37            2 :   menu.exec(event->globalPos());
      38            2 : }
      39              : 
      40          102 : FieldLabel::~FieldLabel() { delete m_editor.data(); }
      41              : 
      42           12 : void FieldLabel::startEdit() {
      43           12 :   if (m_editor != nullptr) {
      44            1 :     m_editor->setFocus();
      45            1 :     return;
      46              :   }
      47           22 :   m_editor = new QLineEdit(text(), parentWidget());
      48           22 :   m_editor->setObjectName(QStringLiteral("fieldNameEditor"));
      49           11 :   m_editor->installEventFilter(this);
      50           11 :   connect(m_editor, &QLineEdit::editingFinished, this,
      51           30 :           [this] { finishEdit(true); });
      52              :   // Take the label's cell in the form, so the editor gets the label column
      53              :   // (which widens for it) instead of floating over the value next to it.
      54           11 :   auto *form = qobject_cast<QFormLayout *>(parentWidget()->layout());
      55           11 :   int row = -1;
      56           11 :   QFormLayout::ItemRole role{};
      57           11 :   if (form != nullptr) {
      58            1 :     form->getWidgetPosition(this, &row, &role);
      59              :   }
      60           11 :   if (row >= 0) {
      61            1 :     form->removeWidget(this);
      62            1 :     hide();
      63            1 :     form->setWidget(row, role, m_editor);
      64              :   } else {
      65           10 :     m_editor->setGeometry(geometry());
      66              :   }
      67           11 :   m_editor->show();
      68           11 :   m_editor->selectAll();
      69           11 :   m_editor->setFocus();
      70              : }
      71              : 
      72          188 : auto FieldLabel::eventFilter(QObject *watched, QEvent *event) -> bool {
      73          188 :   if (watched != m_editor || event->type() != QEvent::KeyPress) {
      74          176 :     return QLabel::eventFilter(watched, event);
      75              :   }
      76           12 :   switch (static_cast<QKeyEvent *>(event)->key()) {
      77            1 :   case Qt::Key_Escape:
      78            1 :     finishEdit(false);
      79            1 :     return true;
      80            6 :   case Qt::Key_Return:
      81              :   case Qt::Key_Enter:
      82              :     // Enter confirms the name only; left alone it would also reach the
      83              :     // dialog's default button and save and close the whole entry.
      84            6 :     finishEdit(true);
      85            6 :     return true;
      86            5 :   default:
      87            5 :     return QLabel::eventFilter(watched, event);
      88              :   }
      89              : }
      90              : 
      91           15 : void FieldLabel::finishEdit(bool commit) {
      92           15 :   if (m_editor == nullptr) {
      93            6 :     return;
      94              :   }
      95              :   // editingFinished fires again when the editor loses focus on hide; take
      96              :   // the pointer first so the second call finds nothing to do.
      97              :   QLineEdit *editor = m_editor;
      98              :   m_editor = nullptr;
      99              :   // A colon would start the value early when the line is written back.
     100           18 :   const QString name = editor->text().remove(u':').trimmed();
     101            9 :   auto *form = qobject_cast<QFormLayout *>(parentWidget()->layout());
     102            9 :   int row = -1;
     103            9 :   QFormLayout::ItemRole role{};
     104            9 :   if (form != nullptr) {
     105            1 :     form->getWidgetPosition(editor, &row, &role);
     106              :   }
     107            9 :   editor->hide();
     108            9 :   if (row >= 0) {
     109            1 :     form->removeWidget(editor);
     110            1 :     form->setWidget(row, role, this);
     111            1 :     show();
     112              :   }
     113            9 :   editor->deleteLater();
     114           17 :   if (commit && !name.isEmpty() && name != text()) {
     115           16 :     emit renamed(text(), name);
     116              :   }
     117              : }
        

Generated by: LCOV version 2.0-1