[vlc-devel] [PATCH 1/2] qt/dialogmodel: Update error handling before the GUI display
Romain Vimont
rom1v at videolabs.io
Tue Mar 16 09:40:30 UTC 2021
On Tue, Mar 16, 2021 at 10:32:57AM +0100, Benjamin Arnaud wrote:
> ---
> .../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();
Why do you use signals/slots here for errorBegin() and errorEnd()?
I think yoy could directly call the function (currently called
onErrorBegin()), right?
Regards
> +
> + 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
>
> _______________________________________________
> vlc-devel mailing list
> To unsubscribe or modify your subscription options:
> https://mailman.videolan.org/listinfo/vlc-devel
More information about the vlc-devel
mailing list