[vlc-devel] [PATCH 1/2] qt/dialogmodel: Update error handling before the GUI display

Benjamin Arnaud benjamin.arnaud at videolabs.io
Tue Mar 16 09:32:57 UTC 2021


---
 .../gui/qt/dialogs/dialogs/dialogmodel.cpp    | 246 ++++++++++++++----
 .../gui/qt/dialogs/dialogs/dialogmodel.hpp    | 177 ++++++++++---
 modules/gui/qt/maininterface/mainui.cpp       |   5 +-
 3 files changed, 333 insertions(+), 95 deletions(-)

diff --git a/modules/gui/qt/dialogs/dialogs/dialogmodel.cpp b/modules/gui/qt/dialogs/dialogs/dialogmodel.cpp
index 9db269a9de..e8bd9b8856 100644
--- a/modules/gui/qt/dialogs/dialogs/dialogmodel.cpp
+++ b/modules/gui/qt/dialogs/dialogs/dialogmodel.cpp
@@ -1,5 +1,7 @@
 /*****************************************************************************
- * Copyright (C) 2019 VLC authors and VideoLAN
+ * Copyright (C) 2021 VLC authors and VideoLAN
+ *
+ * Authors: Benjamin Arnaud <bunjee at omega.gg>
  *
  * 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
@@ -15,99 +17,235 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
+
 #include "dialogmodel.hpp"
 
-static void displayErrorCb(void *p_data, const char *psz_title, const char *psz_text)
+// VLC includes
+#include <vlc_dialog.h>
+#include <qt.hpp>
+
+//=================================================================================================
+// DialogErrorModel
+//=================================================================================================
+
+/* explicit */ DialogErrorModel::DialogErrorModel(QObject * parent)
+    : QAbstractListModel(parent), m_model(nullptr) {}
+
+//-------------------------------------------------------------------------------------------------
+// QAbstractItemModel implementation
+//-------------------------------------------------------------------------------------------------
+
+QVariant DialogErrorModel::data(const QModelIndex & index, int role) const /* override */
 {
-    DialogModel* that = static_cast<DialogModel*>(p_data);
-    emit that->errorDisplayed(psz_title, psz_text);
+    if (m_model == nullptr)
+        return QVariant();
+
+    const QList<DialogModel::DialogError> & data = m_model->m_data;
+
+    int row = index.row();
+
+    if (row < 0 || row >= data.count())
+        return QVariant();
+
+    switch (role)
+    {
+        case DIALOG_TITLE:
+            return QVariant::fromValue(data.at(row).title);
+        case DIALOG_TEXT:
+            return QVariant::fromValue(data.at(row).text);
+        default:
+            return QVariant();
+    }
 }
 
-static void displayLoginCb(void *p_data, vlc_dialog_id *dialogId,
-                        const char *psz_title, const char *psz_text,
-                        const char *psz_default_username,
-                        bool b_ask_store)
+int DialogErrorModel::rowCount(const QModelIndex &) const /* override */
 {
-    DialogModel* that = static_cast<DialogModel*>(p_data);
-    emit that->loginDisplayed( dialogId, psz_title, psz_text, psz_default_username, b_ask_store );
+    return count();
 }
 
-static void displayQuestionCb(void *p_data, vlc_dialog_id *dialogId,
-                       const char *psz_title, const char *psz_text,
-                       vlc_dialog_question_type i_type,
-                       const char *psz_cancel, const char *psz_action1,
-                       const char *psz_action2)
+//-------------------------------------------------------------------------------------------------
+// QAbstractItemModel reimplementation
+//-------------------------------------------------------------------------------------------------
+
+QHash<int, QByteArray> DialogErrorModel::roleNames() const /* override */
 {
-    DialogModel* that = static_cast<DialogModel*>(p_data);
-    emit that->questionDisplayed( dialogId, psz_title, psz_text, static_cast<int>(i_type), psz_cancel, psz_action1, psz_action2 );
+    QHash<int, QByteArray> roles;
+
+    roles.insert(DialogErrorModel::DIALOG_TITLE, "title");
+    roles.insert(DialogErrorModel::DIALOG_TEXT,  "text");
+
+    return roles;
 }
 
-static void displayProgressCb(void *p_data, vlc_dialog_id *dialogId,
-                            const char *psz_title, const char *psz_text,
-                            bool b_indeterminate, float f_position,
-                            const char *psz_cancel)
+//-------------------------------------------------------------------------------------------------
+// Private slots
+//-------------------------------------------------------------------------------------------------
+
+void DialogErrorModel::onErrorBegin()
 {
-    DialogModel* that = static_cast<DialogModel*>(p_data);
-    emit that->progressDisplayed( dialogId, psz_title, psz_text, b_indeterminate, f_position, psz_cancel);
+    int row = m_model->m_data.count();
+
+    beginInsertRows(QModelIndex(), row, row);
 }
 
-static void cancelCb(void *p_data, vlc_dialog_id *dialogId)
+void DialogErrorModel::onErrorEnd()
 {
-    DialogModel* that = static_cast<DialogModel*>(p_data);
-    emit that->cancelled(dialogId);
+    endInsertRows();
+
+    emit countChanged();
 }
 
-static void updateProgressCb(void *p_data, vlc_dialog_id *dialogId, float f_value, const char *psz_text)
+//-------------------------------------------------------------------------------------------------
+// Properties
+//-------------------------------------------------------------------------------------------------
+
+DialogModel * DialogErrorModel::model() const
 {
-    DialogModel* that = static_cast<DialogModel*>(p_data);
-    emit that->progressUpdated( dialogId, f_value, psz_text );
+    return m_model;
 }
 
-const vlc_dialog_cbs cbs = {
-    displayErrorCb,
-    displayLoginCb,
-    displayQuestionCb,
-    displayProgressCb,
-    cancelCb,
-    updateProgressCb
-};
-
-DialogModel::DialogModel(QObject *parent)
-    : QObject(parent)
+void DialogErrorModel::setModel(DialogModel * model)
 {
+    if (m_model == model) return;
+
+    if (m_model)
+        disconnect(model, 0, this, 0);
+
+    beginResetModel();
+
+    m_model = model;
+
+    endResetModel();
+
+    if (model)
+    {
+        connect(model, &DialogModel::errorBegin, this, &DialogErrorModel::onErrorBegin);
+        connect(model, &DialogModel::errorEnd,   this, &DialogErrorModel::onErrorEnd);
+    }
+
+    emit modelChanged();
 }
 
-DialogModel::~DialogModel()
+int DialogErrorModel::count() const
 {
-    if (m_mainCtx)
-        vlc_dialog_provider_set_callbacks(m_mainCtx->getIntf(), nullptr, nullptr);
+    if (m_model == nullptr)
+        return 0;
+
+    return m_model->m_data.count();
 }
 
-void DialogModel::dismiss(DialogId dialogId)
+//=================================================================================================
+// DialogModel
+//=================================================================================================
+
+/* explicit */ DialogModel::DialogModel(intf_thread_t * intf, QObject * parent)
+    : QObject(parent), m_intf(intf)
 {
-    vlc_dialog_id_dismiss(dialogId.m_id);
+    const vlc_dialog_cbs cbs =
+    {
+        onError, onLogin, onQuestion, onProgress, onCancelled, onProgressUpdated
+    };
+
+    vlc_dialog_provider_set_callbacks(intf, &cbs, this);
 }
 
-void DialogModel::post_login(DialogId dialogId, const QString& username, const QString& password, bool store)
+//-------------------------------------------------------------------------------------------------
+// Interface
+//-------------------------------------------------------------------------------------------------
+
+/* Q_INVOKABLE */ void DialogModel::post_login(DialogId dialogId, const QString & username,
+                                               const QString & password, bool store)
 {
     vlc_dialog_id_post_login(dialogId.m_id, qtu(username), qtu(password), store);
 }
 
-void DialogModel::post_action1(DialogId dialogId)
+/* Q_INVOKABLE */ void DialogModel::post_action1(DialogId dialogId)
 {
     vlc_dialog_id_post_action(dialogId.m_id, 1);
 }
 
-void DialogModel::post_action2(DialogId dialogId)
+/* Q_INVOKABLE */ void DialogModel::post_action2(DialogId dialogId)
 {
     vlc_dialog_id_post_action(dialogId.m_id, 2);
 }
 
-void DialogModel::setMainCtx(QmlMainContext* ctx)
+/* Q_INVOKABLE */ void DialogModel::dismiss(DialogId dialogId)
+{
+    vlc_dialog_id_dismiss(dialogId.m_id);
+}
+
+//-------------------------------------------------------------------------------------------------
+// Private functions
+//-------------------------------------------------------------------------------------------------
+
+void DialogModel::pushError(const DialogError & error)
+{
+    emit errorBegin();
+
+    m_data.append(error);
+
+    emit errorEnd();
+}
+
+//-------------------------------------------------------------------------------------------------
+// Private static functions
+//-------------------------------------------------------------------------------------------------
+
+/* static */ void DialogModel::onError(void * p_data,
+                                       const char * psz_title, const char * psz_text)
+{
+    DialogModel * model = static_cast<DialogModel *>(p_data);
+
+    DialogError error { psz_title, psz_text };
+
+    QMetaObject::invokeMethod(model, [model, error = std::move(error)]()
+    {
+        model->pushError(error);
+    });
+}
+
+/* static */ void DialogModel::onLogin(void * p_data, vlc_dialog_id * dialogId,
+                                       const char * psz_title, const char * psz_text,
+                                       const char * psz_default_username, bool b_ask_store)
 {
-    if (ctx)
-        vlc_dialog_provider_set_callbacks(ctx->getIntf(), &cbs, this);
-    else if (m_mainCtx)
-        vlc_dialog_provider_set_callbacks(m_mainCtx->getIntf(), nullptr, nullptr);
-    m_mainCtx = ctx;
+    DialogModel * model = static_cast<DialogModel *>(p_data);
+
+    emit model->login(dialogId, psz_title, psz_text, psz_default_username, b_ask_store);
+}
+
+/* static */ void DialogModel::onQuestion(void * p_data, vlc_dialog_id * dialogId,
+                                          const char * psz_title, const char * psz_text,
+                                          vlc_dialog_question_type i_type,
+                                          const char * psz_cancel, const char * psz_action1,
+                                          const char * psz_action2)
+{
+    DialogModel * model = static_cast<DialogModel *>(p_data);
+
+    emit model->question(dialogId, psz_title, psz_text, static_cast<int>(i_type), psz_cancel,
+                         psz_action1, psz_action2);
+}
+
+/* static */ void DialogModel::onProgress(void * p_data, vlc_dialog_id * dialogId,
+                                          const char * psz_title, const char * psz_text,
+                                          bool b_indeterminate, float f_position,
+                                          const char * psz_cancel)
+{
+    DialogModel * model = static_cast<DialogModel *>(p_data);
+
+    emit model->progress(dialogId, psz_title, psz_text, b_indeterminate, f_position, psz_cancel);
+}
+
+/* static */ void DialogModel::onProgressUpdated(void * p_data, vlc_dialog_id * dialogId,
+                                                 float f_value, const char * psz_text)
+{
+    DialogModel * model = static_cast<DialogModel *>(p_data);
+
+    emit model->progressUpdated(dialogId, f_value, psz_text);
+}
+
+/* static */ void DialogModel::onCancelled(void * p_data, vlc_dialog_id * dialogId)
+{
+    DialogModel * model = static_cast<DialogModel *>(p_data);
+
+    emit model->cancelled(dialogId);
 }
diff --git a/modules/gui/qt/dialogs/dialogs/dialogmodel.hpp b/modules/gui/qt/dialogs/dialogs/dialogmodel.hpp
index 40cd932f84..432746d2f1 100644
--- a/modules/gui/qt/dialogs/dialogs/dialogmodel.hpp
+++ b/modules/gui/qt/dialogs/dialogs/dialogmodel.hpp
@@ -1,5 +1,7 @@
 /*****************************************************************************
- * Copyright (C) 2019 VLC authors and VideoLAN
+ * Copyright (C) 2021 VLC authors and VideoLAN
+ *
+ * Authors: Benjamin Arnaud <bunjee at omega.gg>
  *
  * 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
@@ -15,6 +17,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
+
 #ifndef DIALOGMODEL_HPP
 #define DIALOGMODEL_HPP
 
@@ -22,79 +25,173 @@
 # include "config.h"
 #endif
 
+// VLC includes
 #include <vlc_common.h>
 #include <vlc_dialog.h>
 
-#include <QObject>
+// Qt includes
+#include <QAbstractListModel>
 
-#include "util/qml_main_context.hpp"
+// Forward declarations
+class DialogModel;
 
+//-------------------------------------------------------------------------------------------------
+// DialogId
+//-------------------------------------------------------------------------------------------------
 
-class DialogId {
+class DialogId
+{
     Q_GADGET
+
 public:
-    DialogId(vlc_dialog_id * id = nullptr)
-        : m_id(id)
-    {}
+    DialogId(vlc_dialog_id * id = nullptr) : m_id(id) {}
 
-    bool operator ==(const DialogId& other) const {
+public: // Operators
+    bool operator ==(const DialogId & other) const
+    {
         return m_id == other.m_id;
     }
-    vlc_dialog_id *m_id;
+
+public: // Variables
+    vlc_dialog_id * m_id;
 };
 
 Q_DECLARE_METATYPE(DialogId)
 
-class DialogModel : public QObject
+//-------------------------------------------------------------------------------------------------
+// DialogErrorModel
+//-------------------------------------------------------------------------------------------------
+
+class DialogErrorModel : public QAbstractListModel
 {
     Q_OBJECT
 
-public:
-    Q_PROPERTY(QmlMainContext* mainCtx READ getMainCtx WRITE setMainCtx NOTIFY mainCtxChanged)
+    Q_ENUMS(DialogRoles)
+
+    Q_PROPERTY(DialogModel * model READ model WRITE setModel NOTIFY modelChanged)
 
-    enum QuestionType {
-        QUESTION_NORMAL,
-        QUESTION_WARNING,
-        QUESTION_CRITICAL
+    Q_PROPERTY(int count READ count NOTIFY countChanged)
+
+public: // Enums
+    enum DialogRoles
+    {
+        DIALOG_TITLE = Qt::UserRole + 1,
+        DIALOG_TEXT
     };
-    Q_ENUM(QuestionType)
 
 public:
-    explicit DialogModel(QObject *parent = nullptr);
-    ~DialogModel();
+    explicit DialogErrorModel(QObject * parent = nullptr);
+
+public: // QAbstractItemModel implementation
+    QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override;
+
+    int rowCount(const QModelIndex & parent = QModelIndex()) const override;
 
-    inline QmlMainContext* getMainCtx() const { return m_mainCtx; }
-    void setMainCtx(QmlMainContext*);
+public: // QAbstractItemModel reimplementation
+    QHash<int, QByteArray> roleNames() const override;
+
+private slots:
+    void onErrorBegin();
+    void onErrorEnd  ();
 
 signals:
-    void errorDisplayed(const QString &title, const QString &text);
-    void loginDisplayed(DialogId dialogId, const QString &title,
-                        const QString &text, const QString &defaultUsername,
+    void modelChanged();
+
+    void countChanged();
+
+public: // Properties
+    DialogModel * model() const;
+    void setModel(DialogModel * model);
+
+    int count() const;
+
+private: // Variables
+    DialogModel * m_model;
+};
+
+//-------------------------------------------------------------------------------------------------
+// DialogModel
+//-------------------------------------------------------------------------------------------------
+
+class DialogModel : public QObject
+{
+    Q_OBJECT
+
+    Q_ENUMS(QuestionType)
+
+private:
+    struct DialogError
+    {
+        QString title;
+        QString text;
+    };
+
+public: // Enums
+    // NOTE: Is it really useful to have this declared here ?
+    enum QuestionType { QUESTION_NORMAL, QUESTION_WARNING, QUESTION_CRITICAL };
+
+public:
+    explicit DialogModel(intf_thread_t * intf, QObject * parent = nullptr);
+
+public: // Interface
+    Q_INVOKABLE void post_login(DialogId dialogId, const QString & username,
+                                const QString & password, bool store = false);
+
+    Q_INVOKABLE void post_action1(DialogId dialogId);
+    Q_INVOKABLE void post_action2(DialogId dialogId);
+
+    Q_INVOKABLE void dismiss(DialogId dialogId);
+
+private: // Functions
+    void pushError(const DialogError & error);
+
+private: // Static functions
+    static void onError(void * p_data, const char * psz_title, const char * psz_text);
+
+    static void onLogin(void * p_data, vlc_dialog_id * dialogId, const char * psz_title,
+                        const char * psz_text, const char * psz_default_username,
                         bool b_ask_store);
 
-    void questionDisplayed(DialogId dialogId, const QString &title,
-                           const QString &text, int type,
-                           const QString &cancel, const QString &action1,
-                           const QString &action2);
+    static void onQuestion(void * p_data, vlc_dialog_id * dialogId, const char * psz_title,
+                           const char * psz_text, vlc_dialog_question_type i_type,
+                           const char * psz_cancel, const char * psz_action1,
+                           const char * psz_action2);
 
-    void progressDisplayed(DialogId dialogId, const QString &title,
-                           const QString &text, bool b_indeterminate,
-                           float f_position, const QString &cancel);
+    static void onProgress(void * p_data, vlc_dialog_id * dialogId, const char * psz_title,
+                           const char * psz_text, bool b_indeterminate, float f_position,
+                           const char *psz_cancel);
 
-    void cancelled(DialogId dialogId);
+    static void onProgressUpdated(void * p_data, vlc_dialog_id * dialogId, float f_value,
+                                  const char * psz_text);
+
+    static void onCancelled(void * p_data, vlc_dialog_id * dialogId);
+
+
+signals:
+    void errorBegin();
+    void errorEnd  ();
 
-    void progressUpdated(DialogId dialogId, float f_value, const QString &text);
+    void login(DialogId dialogId, const QString & title,
+               const QString & text, const QString & defaultUsername,
+               bool b_ask_store);
+
+    void question(DialogId dialogId, const QString & title, const QString & text, int type,
+                  const QString & cancel, const QString & action1, const QString & action2);
+
+    void progress(DialogId dialogId, const QString & title, const QString & text,
+                  bool b_indeterminate, float f_position, const QString & cancel);
+
+    void progressUpdated(DialogId dialogId, float f_value, const QString & text);
+
+    void cancelled(DialogId dialogId);
 
-    void mainCtxChanged();
+private: // Variables
+    intf_thread_t * m_intf;
 
-public slots:
-    void dismiss(DialogId dialogId);
-    void post_login(DialogId dialogId, const QString& username, const QString& password, bool store = false);
-    void post_action1(DialogId dialogId);
-    void post_action2(DialogId dialogId);
+    QList<DialogError> m_data;
 
 private:
-    QmlMainContext* m_mainCtx;
+    friend class DialogErrorModel;
 };
 
 #endif // DIALOGMODEL_HPP
diff --git a/modules/gui/qt/maininterface/mainui.cpp b/modules/gui/qt/maininterface/mainui.cpp
index 1b06d85678..a1c9e07e80 100644
--- a/modules/gui/qt/maininterface/mainui.cpp
+++ b/modules/gui/qt/maininterface/mainui.cpp
@@ -103,6 +103,7 @@ bool MainUI::setup(QQmlEngine* engine)
     rootCtx->setContextProperty( "topWindow", m_interfaceWindow);
     rootCtx->setContextProperty( "dialogProvider", DialogsProvider::getInstance());
     rootCtx->setContextProperty( "systemPalette", new SystemPalette(this));
+    rootCtx->setContextProperty( "dialogModel", new DialogModel(m_intf, this));
 
     if (m_mainInterface->hasMediaLibrary())
         rootCtx->setContextProperty( "medialib", m_mainInterface->getMediaLibrary() );
@@ -227,8 +228,10 @@ void MainUI::registerQMLTypes()
     qmlRegisterType<PlaylistControllerModel>( "org.videolan.vlc", 0, 1, "PlaylistControllerModel" );
 
     qmlRegisterType<AboutModel>( "org.videolan.vlc", 0, 1, "AboutModel" );
+
+    qmlRegisterUncreatableType<DialogModel>("org.videolan.vlc", 0, 1, "DialogModel", "");
+    qmlRegisterType<DialogErrorModel>( "org.videolan.vlc", 0, 1, "DialogErrorModel");
     qRegisterMetaType<DialogId>();
-    qmlRegisterType<DialogModel>("org.videolan.vlc", 0, 1, "DialogModel");
 
     qmlRegisterType<QmlEventFilter>( "org.videolan.vlc", 0, 1, "EventFilter" );
 
-- 
2.25.1



More information about the vlc-devel mailing list