/*
Copyright (C) 2012, University of Cambridge, Department of Psychiatry.
Created by Rudolf Cardinal (rnc1001@cam.ac.uk).
This file is part of CamCOPS.
CamCOPS is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
CamCOPS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CamCOPS. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <QLabel>
#include <QLineEdit>
#include <QPointer>
#include <QValidator>
#include <QWidget>
class FocusWatcher;
class ValidatingLineEdit : public QWidget
{
// One-line text editor with validation and visual feedback
Q_OBJECT
public:
ValidatingLineEdit(
QValidator* validator = nullptr,
const bool allow_empty = false, // Allow empty content
const bool read_only = false,
const bool delayed = false, // Delay validation by WRITE_DELAY_MS
const bool vertical = true,
// ... Validity label below (not to right of) text box?
QWidget* parent = nullptr
);
// If there is no validator, getState() will always return
// QValidator:Acceptable
// If there is a validator and validation has not yet been run, this will
// be a null QVariant.
// Otherwise the QVariant will contain a QValidator::State.
QVariant getState();
// If there is a validator, run it and set the visual feedback accordingly.
// Emit any relevant signals.
void validate();
// Add input methods hints to the underlying QLineEdit
void addInputMethodHints(Qt::InputMethodHints hints);
// Wrappers to methods on the underlying QLineEdit
QString text() const;
void setText(const QString& text);
void setPlaceholderText(const QString& text);
void setEchoMode(QLineEdit::EchoMode);
int cursorPosition();
// Set text on QLineEdit without emitting any signals. Avoids multiple
// validation.
void setTextBlockingSignals(const QString& text);
// Set missing CSS property (rendered yellow for mandatory fields)
void setPropertyMissing(bool missing, bool repolish = true);
void resetValidatorFeedback();
protected:
// May be implemented in derived class to change the text
// in some way before validation
virtual void processChangedText();
void setValidatorFeedback(const bool valid, const bool invalid);
protected slots:
// "A key has been pressed."
// In delayed mode initiates a delay (to prevent rapid typists from getting
// cross); then calls textChanged().
virtual void keystroke();
// Validate and emit valid() or invalid() accordingly
virtual void textChanged();
// Finished editing and valid. Emit valid() to anything interested.
virtual void widgetTextChangedAndValid();
// "The widget has gained or lost focus."
virtual void widgetFocusChanged(bool gaining_focus);
private:
bool m_allow_empty;
bool m_delayed; // Delay validation by WRITE_DELAY_MS
bool m_vertical;
QLabel* m_label;
QPointer<QLineEdit> m_line_edit;
QVariant m_state;
QSharedPointer<QTimer> m_timer; // used for typing delay, as above
QPointer<FocusWatcher> m_focus_watcher; // used to detect focus change
signals:
void focusLost();
void invalid();
void validated();
void valid();
#ifdef Q_OS_ANDROID
// Workaround problem where the cursor does not get updated properly
// if the text is modified in a textChanged signal, such as where
// ProquintLineEdit inserts dashes into the access key.
private:
bool m_ignore_next_input_event = false;
protected:
bool eventFilter(QObject* obj, QEvent* event) override;
void ignoreInputMethodEvents();
void maybeIgnoreNextInputEvent();
#endif
};