Commit 593deedf authored by Karel Slaný's avatar Karel Slaný

Recreated QML text input dialogue from various commits.

parent e11a8116
......@@ -83,6 +83,10 @@ TRANSLATIONS_FILES += \
SOURCES += \
src/accounts.cpp \
src/dialogues/input_dialogue.cpp \
src/dialogues/qml_dialogue_helper.cpp \
src/dialogues/qml_input_dialogue.cpp \
src/dialogues/widget_input_dialogue.cpp \
src/files.cpp \
src/io/filesystem.cpp \
src/io/sqlite/db.cpp \
......@@ -110,12 +114,15 @@ SOURCES += \
src/net/isds_wrapper.cpp \
src/net/isds_session.cpp \
src/net/db_wrapper.cpp \
src/setwrapper.cpp \
src/widgets/paste_input_dialogue.cpp
src/setwrapper.cpp
HEADERS += \
src/accounts.h \
src/common.h \
src/dialogues/input_dialogue.h \
src/dialogues/qml_dialogue_helper.h \
src/dialogues/qml_input_dialogue.h \
src/dialogues/widget_input_dialogue.h \
src/files.h \
src/io/filesystem.h \
src/io/sqlite/db.h \
......@@ -143,8 +150,7 @@ HEADERS += \
src/net/isds_wrapper.h \
src/net/isds_session.h \
src/net/db_wrapper.h \
src/setwrapper.h \
src/widgets/paste_input_dialogue.h
src/setwrapper.h
android {
SOURCES += \
......
/*
* Copyright (C) 2014-2017 CZ.NIC
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* OpenSSL library under certain conditions as described in each
* individual source file, and distribute linked combinations including
* the two.
*/
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Dialogs 1.2
import cz.nic.mobileDatovka.qmlDialogue 1.0
/*
* All objectName properties are used to navigate through the structure from
* within C++ code.
*/
Item {
id: root
objectName: "root"
/* Expose children. */
property alias dialogue: dialogue
signal dialogueClosed()
Dialog {
id: dialogue
objectName: "dialogue"
property alias content: content
property bool explicitPasteMenu: false
property int dlgEchoMode: QmlDlgEchoMode.EM_NORMAL
//visible: true /* Set from C++. */
title: ""
modality: Qt.ApplicationModal
/*
* https://forum.qt.io/topic/39465/solved-custom-qtquick-messagedialog/9
* says that there is a bug in Dialog implementation on iOS.
* There should be a workaround by setting the buttons just before
* displaying the dialogue.
*/
//standardButtons: StandardButton.Ok | StandardButton.Cancel
Column {
id: content
property alias messageText: messageText
property alias textInput: textInput
anchors.fill: parent
Text {
id: messageText
objectName: "messageText"
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width
text: ""
//color: datovkaPalette.mid /* TODO */
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap
}
TextField {
id: textInput
objectName: "textInput"
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width * 0.75
text: ""
placeholderText: ""
focus: true
echoMode: (dialogue.dlgEchoMode == QmlDlgEchoMode.EM_NORMAL) ? TextInput.Normal :
(dialogue.dlgEchoMode == QmlDlgEchoMode.EM_PWD) ? TextInput.Password :
(dialogue.dlgEchoMode == QmlDlgEchoMode.NOECHO) ? TextInput.NoEcho :
(dialogue.dlgEchoMode == QmlDlgEchoMode.EM_PWD_ECHOONEDIT) ? TextInput.PasswordEchoOnEdit : TextInput.Normal
passwordMaskDelay: 500 // milliseconds
Menu {
id: textInputMenu
implicitWidth: 800 // Chosen to be large enough
MenuItem {
text: qsTr("Clear")
enabled: textInput.text != ""
onTriggered: {
textInput.clear()
}
}
MenuItem {
text: qsTr("Paste")
enabled: textInput.canPaste
onTriggered: {
textInput.paste()
}
}
}
onPressAndHold: {
if (dialogue.explicitPasteMenu) {
textInputMenu.implicitWidth = content.computeMenuWidth(textInputMenu)
textInputMenu.x = mouse.x
textInputMenu.y = mouse.y
textInputMenu.open()
}
}
}
/* TODO -- Defined also in main.qml . */
Text {
id: dummyText
text: ""
visible: false
wrapMode: Text.NoWrap
elide: Text.ElideNone
}
function computeMenuWidth(menu) {
var w = 0.0
for (var i = 0; i < menu.contentData.length; i++) {
dummyText.text = menu.contentData[i].text + "www"
if (w < dummyText.width) {
w = dummyText.width
}
}
dummyText.text = ""
return Math.round(w)
}
}
onVisibleChanged: {
if (!visible) {
root.dialogueClosed()
}
}
}
}
......@@ -315,6 +315,76 @@ ApplicationWindow {
}
}
Connections {
target: dlgEmitter
onDlgGetText: {
/*
* Create a dynamic dialogue object set content and connect
* functionality.
*/
var dlgComponent = Qt.createComponent("qrc:/qml/dialogues/PasteInputDialogue.qml", Component.PreferSynchronous);
var dlgObj;
if (dlgComponent.status == Component.Ready) {
finishCreation();
} else {
dlgComponent.statusChanged.connect(finishCreation);
}
function finishCreation() {
if (dlgComponent.status == Component.Error) {
// Error handling.
console.log("Error loading dlgComponent:", dlgComponent.errorString());
return;
} else if (dlgComponent.status != Component.Ready) {
return;
}
dlgObj = dlgComponent.createObject(mainWindow, {"objectName": "textInputDialogue"});
if (dlgObj == null) {
// Error handling.
console.log("Error creating dialogue object");
return;
}
dlgObj.dialogue.title = title
dlgObj.dialogue.content.messageText.text = message
dlgObj.dialogue.content.textInput.inputMethodHints = inputMethodHints /* Must be set before setting echoMode. */
dlgObj.dialogue.dlgEchoMode = echoMode
dlgObj.dialogue.content.textInput.text = text
dlgObj.dialogue.content.textInput.placeholderText = placeholderText
dlgObj.dialogue.explicitPasteMenu = explicitPasteMenu
/* See PasteInputDialogue.qml for explanation. */
dlgObj.dialogue.standardButtons = StandardButton.Ok | StandardButton.Cancel
/* Connect signals. */
dlgObj.dialogueClosed.connect(deleteObject);
dlgObj.dialogue.accepted.connect(emitAccepted);
dlgObj.dialogue.rejected.connect(emitRejected);
dlgObj.dialogue.open();
}
function deleteObject() {
console.log("Destroying dialogue.");
dlgObj.destroy();
mainStack.forceActiveFocus(); /* Dialogue stole focus. */
}
function emitAccepted() {
console.log("Accepted " + dlgObj.dialogue.content.textInput.text);
dlgEmitter.emitAccepted(dlgObj.dialogue.content.textInput.text);
}
function emitRejected() {
console.log("Rejected");
dlgEmitter.emitRejected();
}
}
}
function navigateBack(mainStack, nestedStack, event) {
if (event.key === Qt.Key_Back) {
event.accepted = true
......
......@@ -98,6 +98,7 @@
<file>../qml/components/MessageList.qml</file>
<file>../qml/components/OverlaidImage.qml</file>
<file>../qml/components/SpinBoxZeroMax.qml</file>
<file>../qml/dialogues/PasteInputDialogue.qml</file>
<file>../qml/pages/PageAboutApp.qml</file>
<file>../qml/pages/PageAccountDetail.qml</file>
<file>../qml/pages/PageAccountList.qml</file>
......
/*
* Copyright (C) 2014-2017 CZ.NIC
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* OpenSSL library under certain conditions as described in each
* individual source file, and distribute linked combinations including
* the two.
*/
#if !defined(Q_OS_IOS)
#define USE_QML_DIALOGUES 1
#endif /* !defined(Q_OS_IOS) */
#include "src/dialogues/input_dialogue.h"
#include "src/dialogues/qml_input_dialogue.h"
#include "src/dialogues/widget_input_dialogue.h"
#if !defined(USE_QML_DIALOGUES)
/*!
* @brief Converts echo mode from helper class definition.
*
* @param[in] echoMode Echo mode as defined in QmlDlgHelper.
* @return Echo mode as defined in QLineEdit.
*/
static
QLineEdit::EchoMode toLineEditEchoMode(
enum QmlDlgHelper::QmlDlgEchoMode echoMode)
{
switch (echoMode) {
case QmlDlgHelper::EM_NORMAL:
return QLineEdit::Normal;
break;
case QmlDlgHelper::EM_PWD:
return QLineEdit::NoEcho;
break;
case QmlDlgHelper::EM_NOECHO:
return QLineEdit::Password;
break;
case QmlDlgHelper::EM_PWD_ECHOONEDIT:
return QLineEdit::PasswordEchoOnEdit;
break;
default:
Q_ASSERT(0);
return QLineEdit::Normal;
break;
}
}
#endif /* !defined(USE_QML_DIALOGUES) */
QString InputDialogue::getText(QObject *parent, const QString &title,
const QString &message, enum QmlDlgHelper::QmlDlgEchoMode echoMode,
const QString &text, const QString &placeholderText, bool *ok,
Qt::InputMethodHints inputMethodHints)
{
#if defined(USE_QML_DIALOGUES)
return QmlInputDialogue::getText(qobject_cast<QWindow *>(parent),
title, message, echoMode, text, placeholderText, ok,
inputMethodHints);
#else /* !defined(USE_QML_DIALOGUES) */
Q_UNUSED(placeholderText);
return WidgetInputDialogue::getText(qobject_cast<QWidget *>(parent),
title, message, toLineEditEchoMode(echoMode), text, ok,
Qt::Dialog, inputMethodHints);
#endif /* defined(USE_QML_DIALOGUES) */
}
/*
* Copyright (C) 2014-2017 CZ.NIC
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* OpenSSL library under certain conditions as described in each
* individual source file, and distribute linked combinations including
* the two.
*/
#ifndef _INPUT_DIALOGUE_H_
#define _INPUT_DIALOGUE_H_
#include <QObject>
#include <QWindow>
#include "src/dialogues/qml_dialogue_helper.h"
/*!
* @brief Encapsulates input dialogues.
*/
class InputDialogue : public QObject {
Q_OBJECT
private:
/*!
* @brief Private constructor.
*/
explicit InputDialogue(QObject *parent = Q_NULLPTR);
public:
/*!
* @brief Generates a text edit dialogue. Paste button is added on
* Android devices.
*
* @param[in] parent Parent window or widget.
* @param[in] title Window title.
* @param[in] message Text shown in dialogue window.
* @param[in] echoMode Text input filed echo mode.
* @param[in] text Text put into text field.
* @param[in] placeholedText Placeholder text displayed in text field.
* @param[out] ok If is not null, then will be set to true if ok was
* pressed.
* @param[in] inputMethodHints Hints for input method.
* @return Text from text field of dialogue was accepted.
*/
static
QString getText(QObject *parent, const QString &title,
const QString &message,
enum QmlDlgHelper::QmlDlgEchoMode echoMode = QmlDlgHelper::EM_NORMAL,
const QString &text = QString(),
const QString &placeholderText = QString(), bool *ok = Q_NULLPTR,
Qt::InputMethodHints inputMethodHints = Qt::ImhNone);
};
#endif /* _INPUT_DIALOGUE_H_ */
/*
* Copyright (C) 2014-2017 CZ.NIC
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* OpenSSL library under certain conditions as described in each
* individual source file, and distribute linked combinations including
* the two.
*/
#include <QQmlEngine> /* qmlRegisterType */
#include "src/dialogues/qml_dialogue_helper.h"
void QmlDlgHelper::declareQML(void)
{
qmlRegisterType<QmlDlgHelper>("cz.nic.mobileDatovka.qmlDialogue", 1, 0, "QmlDlgEchoMode");
}
void QmlDlgHelper::emitAccepted(const QString &input)
{
emit accepted(input);
}
void QmlDlgHelper::emitRejected(void)
{
emit rejected();
}
/*
* Copyright (C) 2014-2017 CZ.NIC
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* OpenSSL library under certain conditions as described in each
* individual source file, and distribute linked combinations including
* the two.
*/
#ifndef _QML_DIALOGUE_HELPER_H_
#define _QML_DIALOGUE_HELPER_H_
#include <QObject>
#include <QString>
/*!
* @brief Class defining enumeration types.
*
* @note The enumeration types cannot be defined in a class with a private
* constructor because of qmlRegisterType(). Also, because the types are
* registered before main window is loaded the constructor is called and that
* causes trouble on Android and iOS devices.
*/
class QmlDlgHelper : public QObject {
Q_OBJECT
public:
/*
* See TextInput documentation.
* QQuickTextField header file, where similar enum values are defined,
* cannot be accessed directly.
*/
enum QmlDlgEchoMode {
EM_NORMAL,
EM_PWD,
EM_NOECHO,
EM_PWD_ECHOONEDIT
};
Q_ENUMS(QmlDlgEchoMode)
/* Don't forget to declare various properties to the QML system. */
static
void declareQML(void);
/*!
* @brief Emits accepted signal.
*
* @param[in] input Value to emit together with the signal.
*/
Q_INVOKABLE
void emitAccepted(const QString &input);
/*!
* @brief Emits rejected signal.
*/
Q_INVOKABLE
void emitRejected(void);
signals:
/*!
* @brief This signal should be emitted when QML text input dialogue
* should be displayed.
*/
void dlgGetText(const QString &title, const QString &message,
int echoMode, const QString &text, const QString &placeholderText,
int inputMethodHints, bool explicitPasteMenu);
/*!
* @brief This signal should be emitted when QML text input dialogue
* was accepted.
*/
void accepted(const QString &input);
/*!
* @brief This signal should be emitted when QML text input dialogue
* was rejected.
*/
void rejected(void);
};
#endif /* _QML_DIALOGUE_HELPER_H_ */
/*
* Copyright (C) 2014-2017 CZ.NIC
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* OpenSSL library under certain conditions as described in each
* individual source file, and distribute linked combinations including
* the two.
*/
#include <QGuiApplication>
#include <QQmlApplicationEngine>
//#include <QQuickItem>
#include "src/dialogues/qml_input_dialogue.h"
QmlDlgHelper *QmlInputDialogue::dlgEmitter = Q_NULLPTR;
QObject *QmlInputDialogue::s_dlgTextInput = Q_NULLPTR;
QmlInputDialogue::QmlInputDialogue(QObject *parent, QWindow *window)
: QObject(parent),
m_loop(this),
m_dlgTextInputElement(Q_NULLPTR),
m_ok(false),
m_readText()
{
Q_UNUSED(window)
}
#if 0
/* Paint dialogue using QQuickView -- buggy on mobile devices. */
QQuickView view;
view.setSource(QUrl("qrc:/qml/dialogues/PasteInputDialogue.qml"));
view.setModality(Qt::ApplicationModal);
//view.show();
QObject *root = view.rootObject(); /* Is QQuickItem. */
QOBject *dlg = root->findChild<QObject *>("dialogue", Qt::FindChildrenRecursively); /* Is not QQuickItem. */
#endif /* USE_QUICK_VUEW */
#if 0
/* Paint dialogue using QQmlEngine -- buggy on mobile devices. */
QQmlEngine engine;
QQmlComponent component(&qmlEngine, QUrl("qrc:/qml/dialogues/PasteInputDialogue.qml"));
QObject *root = component.create();
QOBject *dlg = root->findChild<QObject *>("dialogue", Qt::FindChildrenRecursively);
#endif /* USE_QML_ENGINE */
#if 0
/* Obtain objects whose values are going to be set. */
QObject *dlgMessage = dlg->findChild<QObject *>("messageText",
Qt::FindChildrenRecursively);
dialogue.m_dlgTextInputElement = dlg->findChild<QObject *>("textInput",
Qt::FindChildrenRecursively);
if ((dlg == Q_NULLPTR) || (dlgMessage == Q_NULLPTR) ||
(dialogue.m_dlgTextInputElement == Q_NULLPTR)) {
Q_ASSERT(0);
if (ok != Q_NULLPTR) {
*ok = dialogue.m_ok;
}
return QString();
}
/* Set dialogue content. */
/* Must be set before setting echo mode. */
dialogue.m_dlgTextInputElement->setProperty("inputMethodHints",
(int)inputMethodHints);
#if defined Q_OS_ANDROID
/* Display explicit context paste menu on Android devices. */
dlg->setProperty("explicitPasteMenu", true);
#endif /* Q_OS_ANDROID */
dlg->setProperty("dlgEchoMode", echoMode);
dlg->setProperty("title", title);
dlgMessage->setProperty("text", message);
dialogue.m_dlgTextInputElement->setProperty("text", text);
dialogue.m_dlgTextInputElement->setProperty("placeholderText",
placeholderText);
/*
* Show dialogue.
* NOTE: Use signals/slots for invoking dialogues?
*/
QMetaObject::invokeMethod(dlg, "open");
//dlg->setProperty("visible", true);
connect(dlg, SIGNAL(accepted()), &dialogue, SLOT(readGetText()));
connect(dlg, SIGNAL(rejected()), &dialogue.m_loop, SLOT(quit()));
dialogue.m_loop.exec();
#endif
QString QmlInputDialogue::getText(QWindow *parent, const QString &title,
const QString &message,
enum QmlDlgHelper::QmlDlgEchoMode echoMode, const QString &text,
const QString &placeholderText, bool *ok,
Qt::InputMethodHints inputMethodHints)
{
QmlInputDialogue dialogue(parent, topLevelWindow());
bool explicitPasteMenu = false;
#if defined Q_OS_ANDROID
explicitPasteMenu = true;
#endif /* Q_OS_ANDROID */
if (dlgEmitter == Q_NULLPTR) {
Q_ASSERT(0);
return QString();
}
/* Cause QML window to pop up. */
emit dlgEmitter->dlgGetText(title, message, echoMode, text,
placeholderText, inputMethodHints, explicitPasteMenu);
/* Connects signals and slots that interrupt the event loop. */
connect(dlgEmitter, SIGNAL(accepted(QString)),
&dialogue, SLOT(readGetText(QString)));
connect(dlgEmitter, SIGNAL(rejected()),
&dialogue.m_loop, SLOT(quit()));
dialogue.m_loop.exec();
if (ok != Q_NULLPTR) {
*ok = dialogue.m_ok;
}
return dialogue.m_readText;
}
QWindow *QmlInputDialogue::topLevelWindow(void)
{
foreach (QWindow *w, QGuiApplication::topLevelWindows()) {
if ((w != Q_NULLPTR) && (w->parent() == Q_NULLPTR)) {
return w;
}
}
return Q_NULLPTR;
}
void QmlInputDialogue::searchPersistentDialogues(QQmlApplicationEngine *engine)
{
if (engine == Q_NULLPTR) {
Q_ASSERT(0);
return;
}
QList<QObject *> objList = engine->rootObjects();
if (objList.size() == 0) {
return;
}
QObject *root = objList.first();