diff --git a/modules/gui/qt/dialogs/dialogs/dialogmodel.cpp b/modules/gui/qt/dialogs/dialogs/dialogmodel.cpp index 9db269a9de..4c33d45ba9 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 * * 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,195 @@ * 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 +#include + +//================================================================================================= +// DialogErrorModel +//================================================================================================= + +/* explicit */ DialogErrorModel::DialogErrorModel(QObject * parent) : QAbstractListModel(parent) {} + +//------------------------------------------------------------------------------------------------- +// QAbstractItemModel implementation +//------------------------------------------------------------------------------------------------- + +QVariant DialogErrorModel::data(const QModelIndex & index, int role) const /* override */ { - DialogModel* that = static_cast(p_data); - emit that->errorDisplayed(psz_title, psz_text); + int row = index.row(); + + if (row < 0 || row >= m_data.count()) + return QVariant(); + + switch (role) + { + case DIALOG_TITLE: + return QVariant::fromValue(m_data.at(row).title); + case DIALOG_TEXT: + return QVariant::fromValue(m_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(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 DialogErrorModel::roleNames() const /* override */ { - DialogModel* that = static_cast(p_data); - emit that->questionDisplayed( dialogId, psz_title, psz_text, static_cast(i_type), psz_cancel, psz_action1, psz_action2 ); + return + { + { DialogErrorModel::DIALOG_TITLE, "title" }, + { DialogErrorModel::DIALOG_TEXT, "text" } + }; } -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 functions +//------------------------------------------------------------------------------------------------- + +void DialogErrorModel::pushError(const DialogError & error) { - DialogModel* that = static_cast(p_data); - emit that->progressDisplayed( dialogId, psz_title, psz_text, b_indeterminate, f_position, psz_cancel); + int row = m_data.count(); + + beginInsertRows(QModelIndex(), row, row); + + m_data.append(error); + + endInsertRows(); + + emit countChanged(); } -static void cancelCb(void *p_data, vlc_dialog_id *dialogId) +//------------------------------------------------------------------------------------------------- +// Properties +//------------------------------------------------------------------------------------------------- + +int DialogErrorModel::count() const { - DialogModel* that = static_cast(p_data); - emit that->cancelled(dialogId); + return m_data.count(); } -static void updateProgressCb(void *p_data, vlc_dialog_id *dialogId, float f_value, const char *psz_text) +//================================================================================================= +// DialogModel +//================================================================================================= + +/* explicit */ DialogModel::DialogModel(intf_thread_t * intf, QObject * parent) + : QObject(parent), m_intf(intf) { - DialogModel* that = static_cast(p_data); - emit that->progressUpdated( dialogId, f_value, psz_text ); + m_model = new DialogErrorModel(this); + + const vlc_dialog_cbs cbs = + { + onError, onLogin, onQuestion, onProgress, onCancelled, onProgressUpdated + }; + + vlc_dialog_provider_set_callbacks(intf, &cbs, this); } -const vlc_dialog_cbs cbs = { - displayErrorCb, - displayLoginCb, - displayQuestionCb, - displayProgressCb, - cancelCb, - updateProgressCb -}; +//------------------------------------------------------------------------------------------------- +// Interface +//------------------------------------------------------------------------------------------------- -DialogModel::DialogModel(QObject *parent) - : QObject(parent) -{ -} - -DialogModel::~DialogModel() -{ - if (m_mainCtx) - vlc_dialog_provider_set_callbacks(m_mainCtx->getIntf(), nullptr, nullptr); -} - -void DialogModel::dismiss(DialogId dialogId) -{ - vlc_dialog_id_dismiss(dialogId.m_id); -} - -void DialogModel::post_login(DialogId dialogId, const QString& username, const QString& password, bool store) +/* 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) { - 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; + vlc_dialog_id_dismiss(dialogId.m_id); +} + +//------------------------------------------------------------------------------------------------- +// Private static functions +//------------------------------------------------------------------------------------------------- + +/* static */ void DialogModel::onError(void * p_data, + const char * psz_title, const char * psz_text) +{ + DialogModel * model = static_cast(p_data); + + DialogErrorModel::DialogError error { psz_title, psz_text }; + + QMetaObject::invokeMethod(model, [model, error = std::move(error)]() + { + model->m_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) +{ + DialogModel * model = static_cast(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(p_data); + + emit model->question(dialogId, psz_title, psz_text, static_cast(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(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(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(p_data); + + emit model->cancelled(dialogId); +} + +//------------------------------------------------------------------------------------------------- +// Properties +//------------------------------------------------------------------------------------------------- + +DialogErrorModel * DialogModel::model() const +{ + return m_model; } diff --git a/modules/gui/qt/dialogs/dialogs/dialogmodel.hpp b/modules/gui/qt/dialogs/dialogs/dialogmodel.hpp index 40cd932f84..9a3cacf7df 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 * * 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,169 @@ # include "config.h" #endif +// VLC includes #include #include -#include +// Qt includes +#include -#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) - {} - bool operator ==(const DialogId& other) const { +public: + DialogId(vlc_dialog_id * id = nullptr) : m_id(id) {} + +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) +//------------------------------------------------------------------------------------------------- +// DialogErrorModel +//------------------------------------------------------------------------------------------------- + +class DialogErrorModel : public QAbstractListModel +{ + Q_OBJECT + + Q_ENUMS(DialogRoles) + + Q_PROPERTY(int count READ count NOTIFY countChanged) + +public: // Enums + enum DialogRoles + { + DIALOG_TITLE = Qt::UserRole + 1, + DIALOG_TEXT + }; + +private: + struct DialogError + { + QString title; + QString text; + }; + +public: + 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; + +public: // QAbstractItemModel reimplementation + QHash roleNames() const override; + +private: // Functions + void pushError(const DialogError & error); + +signals: + void modelChanged(); + + void countChanged(); + +public: // Properties + int count() const; + +private: // Variables + QList m_data; + +private: + friend class DialogModel; +}; + +//------------------------------------------------------------------------------------------------- +// DialogModel +//------------------------------------------------------------------------------------------------- + class DialogModel : public QObject { Q_OBJECT -public: - Q_PROPERTY(QmlMainContext* mainCtx READ getMainCtx WRITE setMainCtx NOTIFY mainCtxChanged) + Q_ENUMS(QuestionType) - enum QuestionType { - QUESTION_NORMAL, - QUESTION_WARNING, - QUESTION_CRITICAL - }; - Q_ENUM(QuestionType) + Q_PROPERTY(DialogErrorModel * model READ model CONSTANT) + +public: // Enums + // NOTE: Is it really useful to have this declared here ? + enum QuestionType { QUESTION_NORMAL, QUESTION_WARNING, QUESTION_CRITICAL }; public: - explicit DialogModel(QObject *parent = nullptr); - ~DialogModel(); + explicit DialogModel(intf_thread_t * intf, QObject * parent = nullptr); - inline QmlMainContext* getMainCtx() const { return m_mainCtx; } - void setMainCtx(QmlMainContext*); +public: // Interface + Q_INVOKABLE void post_login(DialogId dialogId, const QString & username, + const QString & password, bool store = false); -signals: - void errorDisplayed(const QString &title, const QString &text); - void loginDisplayed(DialogId dialogId, const QString &title, - const QString &text, const QString &defaultUsername, + Q_INVOKABLE void post_action1(DialogId dialogId); + Q_INVOKABLE void post_action2(DialogId dialogId); + + Q_INVOKABLE void dismiss(DialogId dialogId); + +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); + + 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 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 progressUpdated(DialogId dialogId, float f_value, const QString &text); +public: // Properties + DialogErrorModel * model() const; - void mainCtxChanged(); +private: // Variables + DialogErrorModel * m_model; -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); - -private: - QmlMainContext* m_mainCtx; + intf_thread_t * m_intf; }; #endif // DIALOGMODEL_HPP diff --git a/modules/gui/qt/maininterface/mainui.cpp b/modules/gui/qt/maininterface/mainui.cpp index 1b06d85678..678b2f87e0 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( "org.videolan.vlc", 0, 1, "PlaylistControllerModel" ); qmlRegisterType( "org.videolan.vlc", 0, 1, "AboutModel" ); + + qmlRegisterUncreatableType("org.videolan.vlc", 0, 1, "DialogModel", ""); + qmlRegisterUncreatableType( "org.videolan.vlc", 0, 1, "DialogErrorModel", ""); qRegisterMetaType(); - qmlRegisterType("org.videolan.vlc", 0, 1, "DialogModel"); qmlRegisterType( "org.videolan.vlc", 0, 1, "EventFilter" );