[vlc-devel] [PATCH 05/29] qt: qml: refactor player control handling
Fatih Uzunoglu
fuzun54 at outlook.com
Thu Apr 1 23:15:47 UTC 2021
* PlayerControlbarModel class is repurposed
and renamed to "player_controlbar_model.cpp/hpp".
PlayerControlbarModel is now a supermodel that
instantiates and handles three ControlListModel
for its 'left', 'center', and 'right' properties.
ControlListModel is the stripped down version of
the old PlayerControlbarModel.
* ControlbarProfileModel is created during
MainInterface initialization. ControlbarProfileModel
creates and handles toolbar/controlbar profiles
that act as a wrapper of PlayerControlbarModel
instances. Saving and loading from settings
happens in ControlbarProfileModel.
* Default control layout is now defined in
ControlbarProfile class. ControlbarProfile
injects the default configuration
during construction.
* Default profiles are now defined in
ControlbarProfileModel class.
ControlbarProfileModel class automatically
matches defaults based on player identifiers.
---
modules/gui/qt/Makefile.am | 12 +-
.../qt/dialogs/toolbar/controlbar_profile.cpp | 232 ++++++
.../qt/dialogs/toolbar/controlbar_profile.hpp | 91 +++
.../toolbar/controlbar_profile_model.cpp | 692 ++++++++++++++++++
.../toolbar/controlbar_profile_model.hpp | 110 +++
.../gui/qt/maininterface/main_interface.cpp | 5 +
.../gui/qt/maininterface/main_interface.hpp | 6 +
modules/gui/qt/maininterface/mainui.cpp | 12 +-
modules/gui/qt/player/control_list_model.cpp | 155 ++++
...rolbarmodel.hpp => control_list_model.hpp} | 63 +-
.../gui/qt/player/player_controlbar_model.cpp | 91 +++
.../gui/qt/player/player_controlbar_model.hpp | 68 ++
.../gui/qt/player/playercontrolbarmodel.cpp | 326 ---------
modules/gui/qt/player/qml/ControlBar.qml | 23 +-
modules/gui/qt/player/qml/ControlButtons.qml | 128 ++--
modules/gui/qt/player/qml/MiniPlayer.qml | 2 +-
modules/gui/qt/player/qml/Player.qml | 2 +
.../gui/qt/player/qml/PlayerButtonsLayout.qml | 86 ++-
modules/gui/qt/player/qml/TopBar.qml | 2 +-
19 files changed, 1604 insertions(+), 502 deletions(-)
create mode 100644 modules/gui/qt/dialogs/toolbar/controlbar_profile.cpp
create mode 100644 modules/gui/qt/dialogs/toolbar/controlbar_profile.hpp
create mode 100644 modules/gui/qt/dialogs/toolbar/controlbar_profile_model.cpp
create mode 100644 modules/gui/qt/dialogs/toolbar/controlbar_profile_model.hpp
create mode 100644 modules/gui/qt/player/control_list_model.cpp
rename modules/gui/qt/player/{playercontrolbarmodel.hpp => control_list_model.hpp} (63%)
create mode 100644 modules/gui/qt/player/player_controlbar_model.cpp
create mode 100644 modules/gui/qt/player/player_controlbar_model.hpp
delete mode 100644 modules/gui/qt/player/playercontrolbarmodel.cpp
diff --git a/modules/gui/qt/Makefile.am b/modules/gui/qt/Makefile.am
index add710cd4b..5412589ca8 100644
--- a/modules/gui/qt/Makefile.am
+++ b/modules/gui/qt/Makefile.am
@@ -112,6 +112,10 @@ libqt_plugin_la_SOURCES = \
gui/qt/dialogs/sout/sout.cpp gui/qt/dialogs/sout/sout.hpp \
gui/qt/dialogs/sout/sout_widgets.cpp \
gui/qt/dialogs/sout/sout_widgets.hpp \
+ gui/qt/dialogs/toolbar/controlbar_profile.hpp \
+ gui/qt/dialogs/toolbar/controlbar_profile.cpp \
+ gui/qt/dialogs/toolbar/controlbar_profile_model.cpp \
+ gui/qt/dialogs/toolbar/controlbar_profile_model.hpp \
gui/qt/dialogs/vlm/vlm.cpp gui/qt/dialogs/vlm/vlm.hpp \
gui/qt/dialogs/playlists/playlists.cpp gui/qt/dialogs/playlists/playlists.hpp \
gui/qt/maininterface/compositor.hpp \
@@ -193,7 +197,8 @@ libqt_plugin_la_SOURCES = \
gui/qt/network/servicesdiscoverymodel.hpp \
gui/qt/player/input_models.cpp gui/qt/player/input_models.hpp \
gui/qt/player/player_controller.cpp gui/qt/player/player_controller.hpp gui/qt/player/player_controller_p.hpp \
- gui/qt/player/playercontrolbarmodel.cpp gui/qt/player/playercontrolbarmodel.hpp \
+ gui/qt/player/player_controlbar_model.cpp gui/qt/player/player_controlbar_model.hpp \
+ gui/qt/player/control_list_model.cpp gui/qt/player/control_list_model.hpp \
gui/qt/playlist/media.hpp \
gui/qt/playlist/playlist_common.cpp \
gui/qt/playlist/playlist_common.hpp \
@@ -315,6 +320,8 @@ nodist_libqt_plugin_la_SOURCES = \
gui/qt/dialogs/sout/profile_selector.moc.cpp \
gui/qt/dialogs/sout/sout.moc.cpp \
gui/qt/dialogs/sout/sout_widgets.moc.cpp \
+ gui/qt/dialogs/toolbar/controlbar_profile.moc.cpp \
+ gui/qt/dialogs/toolbar/controlbar_profile_model.moc.cpp \
gui/qt/dialogs/playlists/playlists.moc.cpp \
gui/qt/maininterface/compositor_dummy.moc.cpp \
gui/qt/maininterface/interface_window_handler.moc.cpp \
@@ -351,7 +358,8 @@ nodist_libqt_plugin_la_SOURCES = \
gui/qt/network/servicesdiscoverymodel.moc.cpp \
gui/qt/player/input_models.moc.cpp \
gui/qt/player/player_controller.moc.cpp \
- gui/qt/player/playercontrolbarmodel.moc.cpp \
+ gui/qt/player/player_controlbar_model.moc.cpp \
+ gui/qt/player/control_list_model.moc.cpp \
gui/qt/playlist/playlist_common.moc.cpp \
gui/qt/playlist/playlist_controller.moc.cpp \
gui/qt/playlist/playlist_item.moc.cpp \
diff --git a/modules/gui/qt/dialogs/toolbar/controlbar_profile.cpp b/modules/gui/qt/dialogs/toolbar/controlbar_profile.cpp
new file mode 100644
index 0000000000..4c3e82fb38
--- /dev/null
+++ b/modules/gui/qt/dialogs/toolbar/controlbar_profile.cpp
@@ -0,0 +1,232 @@
+/*****************************************************************************
+ * Copyright (C) 2021 VLC authors and VideoLAN
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#include "controlbar_profile.hpp"
+
+#include "player/control_list_model.hpp"
+#include "player/player_controlbar_model.hpp"
+
+
+decltype(ControlbarProfile::m_defaults)
+ ControlbarProfile::m_defaults =
+ {
+ {
+ {
+ "MainPlayer"
+ },
+ {
+ {
+ {
+ ControlListModel::LANG_BUTTON,
+ ControlListModel::MENU_BUTTON
+ },
+ {
+ ControlListModel::RANDOM_BUTTON,
+ ControlListModel::PREVIOUS_BUTTON,
+ ControlListModel::PLAY_BUTTON,
+ ControlListModel::NEXT_BUTTON,
+ ControlListModel::LOOP_BUTTON
+ },
+ {
+ ControlListModel::VOLUME,
+ ControlListModel::FULLSCREEN_BUTTON
+ }
+ }
+ }
+ },
+ {
+ {
+ "MiniPlayer"
+ },
+ {
+ {
+ {
+ ControlListModel::ARTWORK_INFO
+ },
+ {
+ ControlListModel::RANDOM_BUTTON,
+ ControlListModel::PREVIOUS_BUTTON,
+ ControlListModel::PLAY_BUTTON,
+ ControlListModel::NEXT_BUTTON,
+ ControlListModel::LOOP_BUTTON
+ },
+ {
+ ControlListModel::VOLUME,
+ ControlListModel::PLAYER_SWITCH_BUTTON
+ }
+ }
+ }
+ }
+ };
+
+
+ControlbarProfile::ControlbarProfile(QObject *parent) : QObject(parent)
+{
+ injectDefaults();
+}
+
+PlayerControlbarModel *ControlbarProfile::newModel(const QString &identifier)
+{
+ if (identifier.isEmpty())
+ return nullptr;
+
+ if (m_models.contains(identifier))
+ return nullptr; // can not allow the same identifier
+
+ const auto model = new PlayerControlbarModel(this);
+
+ connect(model, &PlayerControlbarModel::controlListChanged, this, &ControlbarProfile::generateLinearControlList);
+
+ connect(model, &PlayerControlbarModel::dirtyChanged, this, [this](bool dirty) {
+ if (dirty)
+ ++m_dirty;
+ else
+ --m_dirty;
+
+ emit dirtyChanged( this->dirty() );
+ });
+
+ m_models.insert(identifier, model);
+
+ return model;
+}
+
+PlayerControlbarModel *ControlbarProfile::getModel(const QString &identifier) const
+{
+ if (m_models.contains(identifier))
+ {
+ return m_models[identifier];
+ }
+ else
+ {
+ return nullptr;
+ }
+}
+
+void ControlbarProfile::setModelData(const QString &identifier, const std::array<QVector<int>, 3> &data)
+{
+ auto ptrModel = getModel(identifier);
+
+ if (ptrModel)
+ {
+ ptrModel->loadModels(data);
+ }
+ else
+ {
+ ptrModel = newModel(identifier);
+
+ if (!ptrModel)
+ return;
+
+ ptrModel->loadModels(data);
+ }
+
+ ptrModel->setDirty(true);
+}
+
+std::array<QVector<int>, 3> ControlbarProfile::getModelData(const QString &identifier) const
+{
+ const auto ptrModel = getModel(identifier);
+
+ if (!ptrModel)
+ return {};
+
+ return ptrModel->serializeModels();
+}
+
+void ControlbarProfile::deleteModel(const QString &identifier)
+{
+ if (m_models.contains(identifier))
+ {
+ m_models[identifier]->deleteLater();
+ m_models.remove(identifier);
+ }
+}
+
+void ControlbarProfile::setName(const QString &name)
+{
+ if (name == m_name)
+ return;
+
+ m_name = name;
+
+ emit nameChanged(m_name);
+}
+
+bool ControlbarProfile::dirty() const
+{
+ return (m_dirty > 0);
+}
+
+QString ControlbarProfile::name() const
+{
+ return m_name;
+}
+
+void ControlbarProfile::injectDefaults(bool resetDirty)
+{
+ injectModel(m_defaults);
+
+ if (resetDirty)
+ this->resetDirty(); // defaults normally should not make the profile dirty
+}
+
+void ControlbarProfile::injectModel(const QVector<ControlbarProfile::Configuration> &modelData)
+{
+ m_pauseControlListGeneration = true;
+
+ for (const auto& i : modelData)
+ {
+ setModelData(i.identifier, i.data);
+ }
+
+ m_pauseControlListGeneration = false;
+
+ generateLinearControlList();
+}
+
+void ControlbarProfile::generateLinearControlList()
+{
+ if (m_pauseControlListGeneration)
+ return;
+
+ // Don't bother if there is no receiver (connection):
+ if (receivers(SIGNAL(controlListChanged (const QVector<int>&) )) <= 0)
+ return;
+
+ QVector<int> linearControls;
+
+ for (const auto& i : m_models)
+ {
+ linearControls.append(i->serializeModels()[0] + i->serializeModels()[1] + i->serializeModels()[2]);
+ }
+
+ emit controlListChanged(linearControls);
+}
+
+void ControlbarProfile::resetDirty()
+{
+ if (dirty() == false)
+ return;
+
+ for (auto it = m_models.constBegin(); it != m_models.constEnd(); ++it)
+ {
+ it.value()->setDirty(false);
+ }
+
+ emit dirtyChanged(dirty());
+}
diff --git a/modules/gui/qt/dialogs/toolbar/controlbar_profile.hpp b/modules/gui/qt/dialogs/toolbar/controlbar_profile.hpp
new file mode 100644
index 0000000000..e9c486dd01
--- /dev/null
+++ b/modules/gui/qt/dialogs/toolbar/controlbar_profile.hpp
@@ -0,0 +1,91 @@
+/*****************************************************************************
+ * Copyright (C) 2021 VLC authors and VideoLAN
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifndef ControlbarProfile_HPP
+#define ControlbarProfile_HPP
+
+#include <QObject>
+#include <QMap>
+#include <QVector>
+#include <array>
+
+class PlayerControlbarModel;
+
+class ControlbarProfile : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(bool dirty READ dirty RESET resetDirty NOTIFY dirtyChanged)
+ Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
+
+ friend class ControlbarProfileModel;
+
+public:
+ explicit ControlbarProfile(QObject *parent = nullptr);
+
+ PlayerControlbarModel* newModel(const QString& identifier);
+ Q_INVOKABLE PlayerControlbarModel* getModel(const QString& identifier) const;
+
+ void setModelData(const QString& identifier, const std::array<QVector<int>, 3>& data);
+ std::array<QVector<int>, 3> getModelData(const QString& identifier) const;
+
+ void deleteModel(const QString& identifier);
+
+ Q_INVOKABLE void injectDefaults(bool resetDirty = true);
+
+ bool dirty() const;
+ QString name() const;
+
+public slots:
+ void resetDirty();
+ void setName(const QString& name);
+
+private:
+ // m_dirty indicates the count of PlayerControlbarModel
+ // residing in m_models which has the dirty property
+ // set true.
+ int m_dirty = 0;
+
+ QString m_name {"N/A"};
+ bool m_pauseControlListGeneration = false;
+
+ // According to benchmarks, QMap performs better than
+ // QHash when item count is less than 32.
+ // Assuming model (player) count to stay below that,
+ // QMap is used here.
+ QMap<QString, PlayerControlbarModel *> m_models;
+
+ struct Configuration {
+ QString identifier;
+ std::array<QVector<int>, 3> data;
+ };
+ static const QVector<Configuration> m_defaults;
+
+private:
+ void injectModel(const QVector<Configuration>& modelData);
+
+private slots:
+ void generateLinearControlList();
+
+signals:
+ void dirtyChanged(bool dirty);
+ void nameChanged(QString name);
+
+ void controlListChanged(const QVector<int>& linearControlList);
+};
+
+#endif // ControlbarProfile_HPP
diff --git a/modules/gui/qt/dialogs/toolbar/controlbar_profile_model.cpp b/modules/gui/qt/dialogs/toolbar/controlbar_profile_model.cpp
new file mode 100644
index 0000000000..a052048bb0
--- /dev/null
+++ b/modules/gui/qt/dialogs/toolbar/controlbar_profile_model.cpp
@@ -0,0 +1,692 @@
+/*****************************************************************************
+ * Copyright (C) 2021 VLC authors and VideoLAN
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#include "controlbar_profile_model.hpp"
+
+#include <QSettings>
+
+#include "qt.hpp"
+#include "controlbar_profile.hpp"
+#include "player/control_list_model.hpp"
+
+#define SETTINGS_KEY_SELECTEDPROFILE "SelectedProfile"
+#define SETTINGS_ARRAYNAME_PROFILES "Profiles"
+#define SETTINGS_KEY_NAME "Name"
+#define SETTINGS_KEY_MODEL "Model"
+
+#define SETTINGS_CONTROL_SEPARATOR ","
+#define SETTINGS_CONFIGURATION_SEPARATOR "|"
+#define SETTINGS_PROFILE_SEPARATOR "$"
+
+decltype (ControlbarProfileModel::m_defaults)
+ ControlbarProfileModel::m_defaults =
+ {
+ {
+ {
+ "Minimalist Style"
+ },
+ {
+ {
+ {
+ "MainPlayer"
+ },
+ {
+ {
+ {
+ ControlListModel::PLAY_BUTTON,
+ ControlListModel::WIDGET_SPACER,
+ ControlListModel::PREVIOUS_BUTTON,
+ ControlListModel::STOP_BUTTON,
+ ControlListModel::NEXT_BUTTON,
+ ControlListModel::WIDGET_SPACER,
+ ControlListModel::RECORD_BUTTON,
+ ControlListModel::WIDGET_SPACER,
+ ControlListModel::TELETEXT_BUTTONS,
+ ControlListModel::WIDGET_SPACER,
+ ControlListModel::PLAYLIST_BUTTON,
+ ControlListModel::WIDGET_SPACER,
+ ControlListModel::VOLUME
+ },
+ {
+
+ },
+ {
+
+ }
+ }
+ }
+ },
+ {
+ {
+ "MiniPlayer"
+ },
+ {
+ {
+ {
+ ControlListModel::PREVIOUS_BUTTON,
+ ControlListModel::PLAY_BUTTON,
+ ControlListModel::STOP_BUTTON,
+ ControlListModel::NEXT_BUTTON
+ },
+ {
+
+ },
+ {
+
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ {
+ "One-liner Style"
+ },
+ {
+ {
+ {
+ "MainPlayer"
+ },
+ {
+ {
+ {
+ ControlListModel::PLAY_BUTTON,
+ ControlListModel::WIDGET_SPACER,
+ ControlListModel::PREVIOUS_BUTTON,
+ ControlListModel::STOP_BUTTON,
+ ControlListModel::NEXT_BUTTON,
+ ControlListModel::WIDGET_SPACER,
+ ControlListModel::FULLSCREEN_BUTTON,
+ ControlListModel::PLAYLIST_BUTTON,
+ ControlListModel::EXTENDED_BUTTON,
+ ControlListModel::WIDGET_SPACER,
+ ControlListModel::WIDGET_SPACER,
+ ControlListModel::RECORD_BUTTON,
+ ControlListModel::SNAPSHOT_BUTTON,
+ ControlListModel::ATOB_BUTTON,
+ ControlListModel::FRAME_BUTTON
+ },
+ {
+ ControlListModel::VOLUME
+ },
+ {
+
+ }
+ }
+ }
+ },
+ {
+ {
+ "MiniPlayer"
+ },
+ {
+ {
+ {
+ ControlListModel::RANDOM_BUTTON,
+ ControlListModel::PREVIOUS_BUTTON,
+ ControlListModel::PLAY_BUTTON,
+ ControlListModel::STOP_BUTTON,
+ ControlListModel::NEXT_BUTTON,
+ ControlListModel::LOOP_BUTTON
+ },
+ {
+
+ },
+ {
+
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ {
+ "Simplest Style"
+ },
+ {
+ {
+ {
+ "MainPlayer"
+ },
+ {
+ {
+ {
+ ControlListModel::VOLUME
+ },
+ {
+ ControlListModel::PLAY_BUTTON,
+ ControlListModel::NEXT_BUTTON,
+ ControlListModel::STOP_BUTTON
+ },
+ {
+ ControlListModel::FULLSCREEN_BUTTON
+ }
+ }
+ }
+ },
+ {
+ {
+ "MiniPlayer"
+ },
+ {
+ {
+ {
+ ControlListModel::PREVIOUS_BUTTON,
+ ControlListModel::PLAY_BUTTON,
+ ControlListModel::NEXT_BUTTON
+ },
+ {
+
+ },
+ {
+
+ }
+ }
+ }
+ }
+ }
+ }
+ };
+
+
+ControlbarProfileModel::ControlbarProfileModel(intf_thread_t *p_intf, QObject *parent)
+ : QAbstractListModel(parent),
+ m_intf(p_intf)
+{
+ assert(m_intf);
+
+ connect(this, &QAbstractListModel::rowsInserted, this, &ControlbarProfileModel::countChanged);
+ connect(this, &QAbstractListModel::rowsRemoved, this, &ControlbarProfileModel::countChanged);
+ connect(this, &QAbstractListModel::modelReset, this, &ControlbarProfileModel::countChanged);
+
+ // To make the QML player controlbars update when model is Reset
+ connect(this, &QAbstractListModel::modelReset, this, &ControlbarProfileModel::selectedProfileChanged);
+
+ // When all profiles are removed, insert defaults:
+ // Maybe add a dedicate button for this purpose and don't allow removing all profiles ?
+ connect(this, &ControlbarProfileModel::countChanged, this, [this] () {
+ if (rowCount() == 0)
+ insertDefaults();
+ });
+
+ if (reload() == false)
+ {
+ // If initial reload fails, load the default profiles:
+ insertDefaults();
+ }
+}
+
+void ControlbarProfileModel::insertDefaults()
+{
+ // First, add a blank new profile:
+ // ControlbarProfile will inject the default configurations during its construction.
+ newProfile(tr("Default Profile"));
+
+ // Add default profiles:
+ for (const auto& i : m_defaults)
+ {
+ const auto ptrNewProfile = newProfile(i.name);
+ if (!ptrNewProfile)
+ continue;
+
+ ptrNewProfile->injectModel(i.modelData);
+ ptrNewProfile->resetDirty(); // default profiles should not be dirty initially
+ }
+
+ setSelectedProfile(0);
+}
+
+QString ControlbarProfileModel::generateUniqueName(const QString &name)
+{
+ const auto sameNameCount = std::count_if(m_profiles.begin(),
+ m_profiles.end(),
+ [name](const ControlbarProfile* i) {
+ return i->name() == name;
+ });
+
+ if (sameNameCount > 0)
+ return QString("%1 (%2)").arg(name).arg(sameNameCount + 1);
+ else
+ return name;
+}
+
+int ControlbarProfileModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.isValid())
+ return 0;
+
+ return m_profiles.size();
+}
+
+QVariant ControlbarProfileModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ const auto ptrProfile = m_profiles.at(index.row());
+
+ if (!ptrProfile)
+ return QVariant();
+
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ return ptrProfile->name();
+ case MODEL_ROLE:
+ return QVariant::fromValue(ptrProfile);
+ }
+
+ return QVariant();
+}
+
+QHash<int, QByteArray> ControlbarProfileModel::roleNames() const
+{
+ return {
+ {
+ Qt::DisplayRole, "name"
+ },
+ {
+ MODEL_ROLE, "model"
+ }
+ };
+}
+
+bool ControlbarProfileModel::insertRows(int row, int count, const QModelIndex &parent)
+{
+ if (row < 0 || row > m_profiles.size())
+ return false;
+
+ beginInsertRows(parent, row, row + count - 1);
+
+ for (int i = 0; i < count; ++i)
+ {
+ const auto profile = new ControlbarProfile(this);
+ profile->setName(tr("Profile %1").arg(m_profiles.size()));
+
+ m_profiles.insert(row, profile);
+ }
+
+ endInsertRows();
+
+ return true;
+}
+
+bool ControlbarProfileModel::removeRows(int row, int count, const QModelIndex &parent)
+{
+ if (row < 0 || count < 1 || row + count > m_profiles.size())
+ return false;
+
+ beginRemoveRows(parent, row, row + count - 1);
+
+ auto from = m_profiles.begin() + row;
+ auto to = from + count - 1;
+ std::for_each(from, to, [](auto* item) {
+ assert(item);
+ item->deleteLater();
+ });
+ m_profiles.erase(from, to);
+
+ endRemoveRows();
+
+ return true;
+}
+
+bool ControlbarProfileModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ if (data(index, role) != value)
+ {
+ auto ptrProfile = m_profiles.at(index.row());
+
+ if (!ptrProfile)
+ return false;
+
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ if (value.canConvert(QVariant::String))
+ ptrProfile->setName(value.toString());
+ else
+ return false;
+ break;
+ case MODEL_ROLE:
+ if (value.canConvert<ControlbarProfile*>())
+ ptrProfile = qvariant_cast<ControlbarProfile*>(value);
+ else
+ return false;
+ break;
+ default:
+ return false;
+ }
+
+ m_profiles.replace(index.row(), ptrProfile);
+
+ emit dataChanged(index, index, { role });
+ return true;
+ }
+ return false;
+}
+
+Qt::ItemFlags ControlbarProfileModel::flags(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return Qt::NoItemFlags;
+
+ return (Qt::ItemIsEditable | Qt::ItemNeverHasChildren);
+}
+
+int ControlbarProfileModel::selectedProfile() const
+{
+ return m_selectedProfile;
+}
+
+ControlbarProfile* ControlbarProfileModel::currentModel() const
+{
+ return getProfile(selectedProfile());
+}
+
+void ControlbarProfileModel::save(bool clearDirty) const
+{
+ assert(m_intf->p_sys);
+ assert(m_intf->p_sys->mainSettings);
+
+ if (!m_intf || !m_intf->p_sys || !m_intf->p_sys->mainSettings)
+ return;
+
+ const auto settings = m_intf->p_sys->mainSettings;
+ const auto groupName = metaObject()->className();
+
+ settings->beginGroup(groupName);
+ settings->remove(""); // clear the group before save
+
+ settings->setValue(SETTINGS_KEY_SELECTEDPROFILE, selectedProfile());
+
+ settings->beginWriteArray(SETTINGS_ARRAYNAME_PROFILES);
+
+ for (int i = 0; i < m_profiles.size(); ++i)
+ {
+ settings->setArrayIndex(i);
+
+ const auto& ptrModelMap = m_profiles.at(i)->m_models;
+
+ QString val;
+ for (auto it = ptrModelMap.constBegin(); it != ptrModelMap.end(); ++it)
+ {
+ const QString identifier = it.key();
+
+ const auto serializedModels = m_profiles.at(i)->getModelData(identifier);
+
+ static const auto join = [](const QVector<int>& list) {
+ QString ret;
+ for (auto i : list)
+ {
+ ret += QString::number(i) + SETTINGS_CONTROL_SEPARATOR;
+ }
+ if (!ret.isEmpty())
+ ret.chop(1);
+ return ret;
+ };
+
+ val += QString(SETTINGS_PROFILE_SEPARATOR
+ "%1"
+ SETTINGS_CONFIGURATION_SEPARATOR
+ "%2"
+ SETTINGS_CONFIGURATION_SEPARATOR
+ "%3"
+ SETTINGS_CONFIGURATION_SEPARATOR
+ "%4").arg(identifier,
+ join(serializedModels[0]),
+ join(serializedModels[1]),
+ join(serializedModels[2]));
+ }
+
+ if (clearDirty)
+ m_profiles.at(i)->resetDirty();
+
+ settings->setValue(SETTINGS_KEY_NAME, m_profiles.at(i)->name());
+ settings->setValue(SETTINGS_KEY_MODEL, val);
+ }
+
+ settings->endArray();
+ settings->endGroup();
+}
+
+bool ControlbarProfileModel::reload()
+{
+ assert(m_intf->p_sys);
+ assert(m_intf->p_sys->mainSettings);
+
+ if (!m_intf || !m_intf->p_sys || !m_intf->p_sys->mainSettings)
+ return false;
+
+ const auto settings = m_intf->p_sys->mainSettings;
+ const auto groupName = metaObject()->className();
+
+ settings->beginGroup(groupName);
+
+ const int size = settings->beginReadArray(SETTINGS_ARRAYNAME_PROFILES);
+
+ if (size <= 0)
+ {
+ settings->endArray();
+ settings->endGroup();
+
+ return false;
+ }
+
+ beginResetModel();
+
+ decltype (m_profiles) profiles;
+ for (int i = 0; i < size; ++i)
+ {
+ settings->setArrayIndex(i);
+
+ const QString modelValue = settings->value(SETTINGS_KEY_MODEL).toString();
+ if (modelValue.isEmpty())
+ continue;
+
+ const auto val = modelValue.splitRef(SETTINGS_PROFILE_SEPARATOR);
+ if (val.isEmpty())
+ continue;
+
+ const auto ptrNewProfile = new ControlbarProfile(this);
+ ptrNewProfile->setName(settings->value(SETTINGS_KEY_NAME).toString());
+
+ for (auto j : val)
+ {
+ if (j.isEmpty())
+ continue;
+
+ const auto alignments = j.split(SETTINGS_CONFIGURATION_SEPARATOR);
+
+ if (alignments.length() != 4)
+ continue;
+
+ if (alignments[0].toString().isEmpty())
+ continue;
+
+ static const auto split = [](auto ref) {
+ QVector<int> list;
+
+ if (ref.isEmpty())
+ return list;
+
+ for (auto i : ref.split(SETTINGS_CONTROL_SEPARATOR))
+ {
+ bool ok = false;
+ int k = i.toInt(&ok);
+
+ if (ok)
+ list.append(k);
+ }
+ return list;
+ };
+
+ const std::array<QVector<int>, 3> data { split(alignments[1]),
+ split(alignments[2]),
+ split(alignments[3]) };
+
+ ptrNewProfile->setModelData(alignments[0].toString(), data);
+ ptrNewProfile->resetDirty(); // Newly loaded model can not be dirty
+ }
+
+ profiles.append(ptrNewProfile);
+ }
+
+ settings->endArray();
+
+ m_selectedProfile = -1;
+ std::for_each(m_profiles.begin(), m_profiles.end(), [](auto i) { delete i; });
+
+ m_profiles = std::move(profiles);
+
+ endResetModel();
+
+ bool ok = false;
+ int index = settings->value(SETTINGS_KEY_SELECTEDPROFILE).toInt(&ok);
+
+ if (ok)
+ setSelectedProfile(index);
+ else
+ setSelectedProfile(0);
+
+ settings->endGroup();
+
+ return true;
+}
+
+bool ControlbarProfileModel::setSelectedProfile(int selectedProfile)
+{
+ if (m_selectedProfile == selectedProfile)
+ return false;
+
+ const auto ptrProfileNew = getProfile(selectedProfile);
+ const auto ptrProfileOld = getProfile(m_selectedProfile);
+
+ assert(ptrProfileNew);
+
+ if (!ptrProfileNew)
+ return false;
+
+ connect(ptrProfileNew, &ControlbarProfile::controlListChanged, this, &ControlbarProfileModel::selectedProfileControlListChanged);
+ connect(this, &QAbstractListModel::modelReset, ptrProfileNew, &ControlbarProfile::generateLinearControlList);
+ connect(this, &ControlbarProfileModel::selectedProfileChanged, ptrProfileNew, &ControlbarProfile::generateLinearControlList);
+
+ if (ptrProfileOld && (ptrProfileNew != ptrProfileOld))
+ {
+ disconnect(ptrProfileOld, &ControlbarProfile::controlListChanged, this, &ControlbarProfileModel::selectedProfileControlListChanged);
+ disconnect(this, &QAbstractListModel::modelReset, ptrProfileOld, &ControlbarProfile::generateLinearControlList);
+ disconnect(this, &ControlbarProfileModel::selectedProfileChanged, ptrProfileOld, &ControlbarProfile::generateLinearControlList);
+ }
+
+ m_selectedProfile = selectedProfile;
+
+ emit selectedProfileChanged();
+
+ return true;
+}
+
+ControlbarProfile *ControlbarProfileModel::getProfile(int index) const
+{
+ if (index < 0 || index >= m_profiles.size())
+ return nullptr;
+
+ return m_profiles.at(index);
+}
+
+ControlbarProfile *ControlbarProfileModel::newProfile(const QString &name)
+{
+ if (name.isEmpty())
+ return nullptr;
+
+ const auto ptrProfile = newProfile();
+
+ ptrProfile->setName(generateUniqueName(name));
+
+ return ptrProfile;
+}
+
+ControlbarProfile *ControlbarProfileModel::newProfile()
+{
+ const auto ptrNewProfile = new ControlbarProfile(this);
+
+ beginInsertRows(QModelIndex(), m_profiles.size(), m_profiles.size());
+
+ m_profiles.append(ptrNewProfile);
+
+ endInsertRows();
+
+ return ptrNewProfile;
+}
+
+ControlbarProfile *ControlbarProfileModel::cloneProfile(const ControlbarProfile *profile)
+{
+ const auto ptrNewProfile = newProfile(profile->name());
+
+ if (!ptrNewProfile)
+ return nullptr;
+
+ for (auto it = profile->m_models.constBegin(); it != profile->m_models.constEnd(); ++it)
+ {
+ ptrNewProfile->setModelData(it.key(), profile->getModelData(it.key()));
+ ptrNewProfile->resetDirty();
+ }
+
+ return ptrNewProfile;
+}
+
+void ControlbarProfileModel::cloneSelectedProfile(const QString &newProfileName)
+{
+ const auto ptrModel = currentModel();
+
+ assert(ptrModel);
+ if (!ptrModel)
+ return;
+
+ const auto ptrNewModel = cloneProfile(ptrModel);
+
+ assert(ptrNewModel);
+ if (!ptrNewModel)
+ return;
+
+ ptrNewModel->setName(generateUniqueName(newProfileName));
+}
+
+void ControlbarProfileModel::deleteSelectedProfile()
+{
+ const auto ptrSelectedProfile = getProfile(m_selectedProfile);
+
+ if (!ptrSelectedProfile)
+ return;
+
+ const auto _selectedProfile = m_selectedProfile;
+
+ beginRemoveRows(QModelIndex(), _selectedProfile, _selectedProfile);
+
+ m_selectedProfile = -1;
+
+ delete ptrSelectedProfile;
+ m_profiles.removeAt(_selectedProfile);
+
+ endRemoveRows();
+
+ if (getProfile(_selectedProfile - 1))
+ setSelectedProfile(_selectedProfile - 1);
+ else
+ setSelectedProfile(_selectedProfile);
+}
diff --git a/modules/gui/qt/dialogs/toolbar/controlbar_profile_model.hpp b/modules/gui/qt/dialogs/toolbar/controlbar_profile_model.hpp
new file mode 100644
index 0000000000..108229bbe7
--- /dev/null
+++ b/modules/gui/qt/dialogs/toolbar/controlbar_profile_model.hpp
@@ -0,0 +1,110 @@
+/*****************************************************************************
+ * Copyright (C) 2021 VLC authors and VideoLAN
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifndef CONTROLBARPROFILEMODEL_H
+#define CONTROLBARPROFILEMODEL_H
+
+#include <QAbstractListModel>
+#include <array>
+
+#include "controlbar_profile.hpp"
+
+struct intf_thread_t;
+
+class ControlbarProfileModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+ Q_PROPERTY(int selectedProfile READ selectedProfile WRITE setSelectedProfile NOTIFY selectedProfileChanged)
+ Q_PROPERTY(ControlbarProfile* currentModel READ currentModel NOTIFY selectedProfileChanged)
+
+ Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
+
+public:
+ explicit ControlbarProfileModel(intf_thread_t *p_intf, QObject *parent = nullptr);
+
+ // Basic functionality:
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+
+ QHash<int, QByteArray> roleNames() const override;
+
+ // Editable:
+ Q_INVOKABLE bool setData(const QModelIndex &index, const QVariant &value,
+ int role = Qt::DisplayRole) override;
+
+ Qt::ItemFlags flags(const QModelIndex& index) const override;
+
+ // Add data:
+ Q_INVOKABLE bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
+
+ // Remove data:
+ bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
+
+public:
+ enum Roles {
+ MODEL_ROLE = Qt::UserRole,
+ };
+
+ int selectedProfile() const;
+ ControlbarProfile* currentModel() const;
+
+ ControlbarProfile* cloneProfile(const ControlbarProfile* profile);
+ Q_INVOKABLE void cloneSelectedProfile(const QString& newProfileName);
+
+ Q_INVOKABLE ControlbarProfile* getProfile(int index) const;
+
+ Q_INVOKABLE ControlbarProfile* newProfile(const QString& name);
+ ControlbarProfile* newProfile();
+
+ Q_INVOKABLE void deleteSelectedProfile();
+
+public slots:
+ void save(bool clearDirty = true) const;
+ bool reload();
+
+ bool setSelectedProfile(int selectedProfile);
+
+signals:
+ void countChanged();
+ void selectedProfileChanged();
+
+ void selectedProfileControlListChanged(const QVector<int>& linearControlList);
+
+private:
+ QVector<ControlbarProfile *> m_profiles;
+
+ int m_selectedProfile = -1;
+
+ struct Profile {
+ QString name;
+ QVector<ControlbarProfile::Configuration> modelData;
+ };
+
+ static const QVector<Profile> m_defaults;
+
+private:
+ void insertDefaults();
+
+ QString generateUniqueName(const QString& name);
+
+protected:
+ intf_thread_t *m_intf = nullptr;
+};
+
+#endif // CONTROLBARPROFILEMODEL_H
diff --git a/modules/gui/qt/maininterface/main_interface.cpp b/modules/gui/qt/maininterface/main_interface.cpp
index e0bcdcb0a4..206cedd25d 100644
--- a/modules/gui/qt/maininterface/main_interface.cpp
+++ b/modules/gui/qt/maininterface/main_interface.cpp
@@ -50,6 +50,8 @@
#include "vlc_media_library.h"
+#include "dialogs/toolbar/controlbar_profile_model.hpp"
+
#include <QCloseEvent>
#include <QKeyEvent>
@@ -155,6 +157,9 @@ MainInterface::MainInterface(intf_thread_t *_p_intf , QWidget* parent, Qt::Windo
m_colorScheme = new ColorSchemeModel(this);
m_colorScheme->setCurrent(currentColorScheme);
+ /* Controlbar Profile Model Creation */
+ m_controlbarProfileModel = new ControlbarProfileModel(p_intf, this);
+
/* Should the UI stays on top of other windows */
b_interfaceOnTop = var_InheritBool( p_intf, "video-on-top" );
diff --git a/modules/gui/qt/maininterface/main_interface.hpp b/modules/gui/qt/maininterface/main_interface.hpp
index d8deb10c47..1af9804d11 100644
--- a/modules/gui/qt/maininterface/main_interface.hpp
+++ b/modules/gui/qt/maininterface/main_interface.hpp
@@ -59,6 +59,7 @@ class QTimer;
class StandardPLPanel;
struct vout_window_t;
class VideoSurfaceProvider;
+class ControlbarProfileModel;
class WindowStateHolder : public QObject
{
@@ -159,6 +160,8 @@ class MainInterface : public QVLCMW
Q_PROPERTY(bool hasToolbarMenu READ hasToolbarMenu NOTIFY hasToolbarMenuChanged)
Q_PROPERTY(bool canShowVideoPIP READ canShowVideoPIP CONSTANT)
Q_PROPERTY(bool pinVideoControls READ pinVideoControls WRITE setPinVideoControls NOTIFY pinVideoControlsChanged)
+ Q_PROPERTY(ControlbarProfileModel* controlbarProfileModel READ controlbarProfileModel CONSTANT)
+
public:
/* tors */
@@ -207,6 +210,7 @@ public:
inline bool canShowVideoPIP() const { return m_canShowVideoPIP; }
inline void setCanShowVideoPIP(bool canShowVideoPIP) { m_canShowVideoPIP = canShowVideoPIP; }
inline bool pinVideoControls() const { return m_pinVideoControls; }
+ inline ControlbarProfileModel* controlbarProfileModel() const { return m_controlbarProfileModel; }
bool hasEmbededVideo() const;
VideoSurfaceProvider* getVideoSurfaceProvider() const;
@@ -290,6 +294,8 @@ protected:
VLCVarChoiceModel* m_extraInterfaces;
+ ControlbarProfileModel* m_controlbarProfileModel;
+
public slots:
void toggleUpdateSystrayMenu();
void showUpdateSystrayMenu();
diff --git a/modules/gui/qt/maininterface/mainui.cpp b/modules/gui/qt/maininterface/mainui.cpp
index 678b2f87e0..0c518b2599 100644
--- a/modules/gui/qt/maininterface/mainui.cpp
+++ b/modules/gui/qt/maininterface/mainui.cpp
@@ -18,7 +18,11 @@
#include "medialibrary/mlplaylist.hpp"
#include "player/player_controller.hpp"
-#include "player/playercontrolbarmodel.hpp"
+#include "player/player_controlbar_model.hpp"
+#include "player/control_list_model.hpp"
+
+#include "dialogs/toolbar/controlbar_profile_model.hpp"
+#include "dialogs/toolbar/controlbar_profile.hpp"
#include "playlist/playlist_model.hpp"
#include "playlist/playlist_controller.hpp"
@@ -235,7 +239,11 @@ void MainUI::registerQMLTypes()
qmlRegisterType<QmlEventFilter>( "org.videolan.vlc", 0, 1, "EventFilter" );
- qmlRegisterType<PlayerControlBarModel>( "org.videolan.vlc", 0, 1, "PlayerControlBarModel");
+ qRegisterMetaType<ControlbarProfile*>();
+ qRegisterMetaType<ControlbarProfileModel*>();
+ qmlRegisterUncreatableType<ControlbarProfile>("org.videolan.vlc", 0, 1, "ControlbarProfile", "");
+ qmlRegisterUncreatableType<PlayerControlbarModel>("org.videolan.vlc", 0, 1, "PlayerControlbarModel", "");
+ qmlRegisterUncreatableType<ControlListModel>( "org.videolan.vlc", 0, 1, "ControlListModel", "" );
qRegisterMetaType<QmlMainContext*>();
qmlRegisterType<QmlGlobalMenu>( "org.videolan.vlc", 0, 1, "QmlGlobalMenu" );
diff --git a/modules/gui/qt/player/control_list_model.cpp b/modules/gui/qt/player/control_list_model.cpp
new file mode 100644
index 0000000000..4c9d8e5eb5
--- /dev/null
+++ b/modules/gui/qt/player/control_list_model.cpp
@@ -0,0 +1,155 @@
+/*****************************************************************************
+ * Copyright (C) 2021 VLC authors and VideoLAN
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#include "control_list_model.hpp"
+
+ControlListModel::ControlListModel(QObject *parent) : QAbstractListModel(parent)
+{
+ connect(this, &QAbstractListModel::rowsInserted, this, &ControlListModel::countChanged);
+ connect(this, &QAbstractListModel::rowsRemoved, this, &ControlListModel::countChanged);
+ connect(this, &QAbstractListModel::modelReset, this, &ControlListModel::countChanged);
+}
+
+int ControlListModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.isValid())
+ return 0;
+
+ return m_controls.size();
+}
+
+QVariant ControlListModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ const ControlType control = m_controls.at(index.row());
+
+ switch (role) {
+ case ID_ROLE:
+ return QVariant(control);
+ }
+ return QVariant();
+}
+
+bool ControlListModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ ControlType control = m_controls.at(index.row());
+
+ switch (role) {
+ case ID_ROLE:
+ if (value.canConvert(QVariant::Int))
+ control = static_cast<ControlType>(value.toInt());
+ else
+ return false;
+ break;
+ }
+
+ if (setButtonAt(index.row(), control)) {
+ emit dataChanged(index, index, { role });
+ return true;
+ }
+ return false;
+}
+
+Qt::ItemFlags ControlListModel::flags(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return Qt::NoItemFlags;
+
+ return (Qt::ItemIsEditable | Qt::ItemNeverHasChildren);
+}
+
+QHash<int, QByteArray> ControlListModel::roleNames() const
+{
+ return {
+ {
+ ID_ROLE, "id"
+ }
+ };
+}
+
+QVector<int> ControlListModel::getControls() const
+{
+ QVector<int> list;
+
+ for (auto i : m_controls)
+ {
+ list.append(static_cast<int>(i));
+ }
+
+ return list;
+}
+
+void ControlListModel::setControls(const QVector<int> &list)
+{
+ beginResetModel();
+
+ m_controls.resize(list.size());
+
+ for (int i = 0; i < list.size(); ++i)
+ {
+ m_controls[i] = static_cast<ControlType>(list.at(i));
+ }
+
+ endResetModel();
+}
+
+bool ControlListModel::setButtonAt(int index, const ControlType &button)
+{
+ if(index < 0 || index >= m_controls.size())
+ return false;
+
+ const ControlType oldControl = m_controls.at(index);
+
+ if (button == oldControl)
+ return false;
+
+ m_controls[index] = button;
+ return true;
+}
+
+void ControlListModel::insert(int index, QVariantMap bdata)
+{
+ beginInsertRows(QModelIndex(), index, index);
+ m_controls.insert(index, static_cast<ControlType>(bdata.value("id").toInt()));
+ endInsertRows();
+}
+void ControlListModel::move(int src, int dest)
+{
+ if(src == dest)
+ return;
+
+ beginMoveRows(QModelIndex(), src, src, QModelIndex(), dest + (src < dest ? 1 : 0));
+ m_controls.move(src, dest);
+ endMoveRows();
+}
+
+void ControlListModel::remove(int index)
+{
+ beginRemoveRows(QModelIndex(), index, index);
+ m_controls.remove(index);
+ endRemoveRows();
+}
+
+void ControlListModel::clear()
+{
+ beginResetModel();
+ m_controls.clear();
+ endResetModel();
+}
diff --git a/modules/gui/qt/player/playercontrolbarmodel.hpp b/modules/gui/qt/player/control_list_model.hpp
similarity index 63%
rename from modules/gui/qt/player/playercontrolbarmodel.hpp
rename to modules/gui/qt/player/control_list_model.hpp
index d439bbbdb0..34d2f1aebe 100644
--- a/modules/gui/qt/player/playercontrolbarmodel.hpp
+++ b/modules/gui/qt/player/control_list_model.hpp
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (C) 2019 VLC authors and VideoLAN
+ * Copyright (C) 2021 VLC authors and VideoLAN
*
* 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
@@ -16,32 +16,25 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
-#ifndef CONTROLLERMODEL_H
-#define CONTROLLERMODEL_H
+#ifndef CONTROLLISTMODEL_HPP
+#define CONTROLLISTMODEL_HPP
#include <QAbstractListModel>
#include <QVector>
-#include "util/qml_main_context.hpp"
-
-class PlayerControlBarModel : public QAbstractListModel
+class ControlListModel : public QAbstractListModel
{
Q_OBJECT
- Q_PROPERTY(QmlMainContext* mainCtx READ getMainCtx WRITE setMainCtx NOTIFY ctxChanged)
- Q_PROPERTY(QString configName READ getConfigName WRITE setConfigName NOTIFY configNameChanged)
Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
-
public:
- explicit PlayerControlBarModel(QObject *_parent = nullptr);
- struct IconToolButton
- {
- int id;
- };
- enum{
- ID_ROLE
+ explicit ControlListModel(QObject *parent = nullptr);
+
+ enum Roles {
+ ID_ROLE = Qt::UserRole
};
- enum ButtonType_e
+
+ enum ControlType
{
PLAY_BUTTON,
STOP_BUTTON,
@@ -82,7 +75,7 @@ public:
WIDGET_SPACER_EXTEND,
WIDGET_MAX
};
- Q_ENUM(ButtonType_e)
+ Q_ENUM(ControlType)
// Basic functionality:
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
@@ -97,43 +90,21 @@ public:
virtual QHash<int, QByteArray> roleNames() const override;
- inline QmlMainContext* getMainCtx() const { return m_mainCtx; }
- void setMainCtx(QmlMainContext*);
-
- inline QString getConfigName() { return configName; }
- void setConfigName(QString name);
-
- static QString getSerializedDefaultStyle();
+ QVector<int> getControls() const;
+ void setControls(const QVector<int>& list);
signals:
- void ctxChanged(QmlMainContext*);
- void configNameChanged(QString);
void countChanged();
-protected:
- intf_thread_t *p_intf = nullptr;
-
private:
- QVector<IconToolButton> mButtons;
- QString configName;
-
- void parseAndAdd(const QString& config);
- void parseDefault(const QVector<IconToolButton>& config);
-
- bool setButtonAt(int index, const IconToolButton &button);
- void addProfiles();
- void loadConfig();
-
- QmlMainContext* m_mainCtx = nullptr;
+ QVector<ControlType> m_controls;
+ bool setButtonAt(int index, const ControlType &button);
public slots:
Q_INVOKABLE void insert(int index, QVariantMap bdata);
Q_INVOKABLE void move(int src,int dest);
Q_INVOKABLE void remove(int index);
- Q_INVOKABLE void reloadConfig(QString config);
- Q_INVOKABLE void saveConfig();
- Q_INVOKABLE QString getConfig();
- Q_INVOKABLE void reloadModel();
+ Q_INVOKABLE void clear();
};
-#endif // CONTROLLERMODEL_H
+#endif // CONTROLLISTMODEL_HPP
diff --git a/modules/gui/qt/player/player_controlbar_model.cpp b/modules/gui/qt/player/player_controlbar_model.cpp
new file mode 100644
index 0000000000..9496e8f7ba
--- /dev/null
+++ b/modules/gui/qt/player/player_controlbar_model.cpp
@@ -0,0 +1,91 @@
+/*****************************************************************************
+ * Copyright (C) 2021 VLC authors and VideoLAN
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#include "player_controlbar_model.hpp"
+
+#include "control_list_model.hpp"
+
+PlayerControlbarModel::PlayerControlbarModel(QObject *parent) : QObject(parent)
+{
+ m_left = new ControlListModel(this);
+ m_center = new ControlListModel(this);
+ m_right = new ControlListModel(this);
+
+ connect(m_left, &ControlListModel::countChanged, this, &PlayerControlbarModel::contentChanged);
+ connect(m_center, &ControlListModel::countChanged, this, &PlayerControlbarModel::contentChanged);
+ connect(m_right, &ControlListModel::countChanged, this, &PlayerControlbarModel::contentChanged);
+
+ connect(m_left, &QAbstractListModel::dataChanged, this, &PlayerControlbarModel::contentChanged);
+ connect(m_center, &QAbstractListModel::dataChanged, this, &PlayerControlbarModel::contentChanged);
+ connect(m_right, &QAbstractListModel::dataChanged, this, &PlayerControlbarModel::contentChanged);
+}
+
+PlayerControlbarModel::~PlayerControlbarModel()
+{
+ setDirty(false);
+}
+
+bool PlayerControlbarModel::dirty() const
+{
+ return m_dirty;
+}
+
+std::array<QVector<int>, 3> PlayerControlbarModel::serializeModels() const
+{
+ return { left()->getControls(),
+ center()->getControls(),
+ right()->getControls() };
+}
+
+void PlayerControlbarModel::loadModels(const std::array<QVector<int>, 3> &array)
+{
+ left()->setControls(array.at(0));
+ center()->setControls(array.at(1));
+ right()->setControls(array.at(2));
+}
+
+ControlListModel *PlayerControlbarModel::left() const
+{
+ return m_left;
+}
+
+ControlListModel *PlayerControlbarModel::center() const
+{
+ return m_center;
+}
+
+ControlListModel *PlayerControlbarModel::right() const
+{
+ return m_right;
+}
+
+void PlayerControlbarModel::setDirty(bool dirty)
+{
+ if (m_dirty == dirty)
+ return;
+
+ m_dirty = dirty;
+ emit dirtyChanged(m_dirty);
+}
+
+void PlayerControlbarModel::contentChanged()
+{
+ setDirty(true);
+
+ emit controlListChanged();
+}
diff --git a/modules/gui/qt/player/player_controlbar_model.hpp b/modules/gui/qt/player/player_controlbar_model.hpp
new file mode 100644
index 0000000000..a6b71319cf
--- /dev/null
+++ b/modules/gui/qt/player/player_controlbar_model.hpp
@@ -0,0 +1,68 @@
+/*****************************************************************************
+ * Copyright (C) 2021 VLC authors and VideoLAN
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifndef PLAYERCONTROLBARMODEL_HPP
+#define PLAYERCONTROLBARMODEL_HPP
+
+#include <QObject>
+#include <array>
+
+class ControlListModel;
+
+class PlayerControlbarModel : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(bool dirty READ dirty WRITE setDirty NOTIFY dirtyChanged)
+
+ Q_PROPERTY(ControlListModel* left READ left CONSTANT)
+ Q_PROPERTY(ControlListModel* center READ center CONSTANT)
+ Q_PROPERTY(ControlListModel* right READ right CONSTANT)
+
+public:
+ explicit PlayerControlbarModel(QObject *parent = nullptr);
+ ~PlayerControlbarModel();
+
+ bool dirty() const;
+
+ std::array<QVector<int>, 3> serializeModels() const;
+ void loadModels(const std::array<QVector<int>, 3>& array);
+
+ ControlListModel* left() const;
+ ControlListModel* center() const;
+ ControlListModel* right() const;
+
+public slots:
+ void setDirty(bool dirty);
+
+signals:
+ void dirtyChanged(bool dirty);
+ void controlListChanged();
+
+private:
+ bool m_dirty = false;
+
+ ControlListModel* m_left = nullptr;
+ ControlListModel* m_center = nullptr;
+ ControlListModel* m_right = nullptr;
+
+private slots:
+ void contentChanged();
+};
+
+#endif
diff --git a/modules/gui/qt/player/playercontrolbarmodel.cpp b/modules/gui/qt/player/playercontrolbarmodel.cpp
deleted file mode 100644
index 6aa486cfa5..0000000000
--- a/modules/gui/qt/player/playercontrolbarmodel.cpp
+++ /dev/null
@@ -1,326 +0,0 @@
-/*****************************************************************************
- * Copyright (C) 2019 VLC authors and VideoLAN
- *
- * 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 2 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, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
- *****************************************************************************/
-#include <QSettings>
-
-#include "qt.hpp"
-#include "playercontrolbarmodel.hpp"
-
-enum default_align {
- ALIGN_LEFT = 0,
- ALIGN_CENTER,
- ALIGN_RIGHT,
- ALIGN_SIZE
-};
-
-static const QVector<PlayerControlBarModel::IconToolButton> MAIN_TB_DEFAULT[default_align::ALIGN_SIZE] = {
- {
- // left
- {PlayerControlBarModel::LANG_BUTTON},
- {PlayerControlBarModel::MENU_BUTTON}
- },
- {
- // center
- {PlayerControlBarModel::RANDOM_BUTTON},
- {PlayerControlBarModel::PREVIOUS_BUTTON},
- {PlayerControlBarModel::PLAY_BUTTON},
- {PlayerControlBarModel::NEXT_BUTTON},
- {PlayerControlBarModel::LOOP_BUTTON}
- },
- {
- // right
- {PlayerControlBarModel::VOLUME},
- {PlayerControlBarModel::FULLSCREEN_BUTTON}
- }
-};
-
-static const QVector<PlayerControlBarModel::IconToolButton> MINI_TB_DEFAULT[default_align::ALIGN_SIZE] = {
- {
- // left
- {PlayerControlBarModel::ARTWORK_INFO}
- },
- {
- // center
- {PlayerControlBarModel::RANDOM_BUTTON},
- {PlayerControlBarModel::PREVIOUS_BUTTON},
- {PlayerControlBarModel::PLAY_BUTTON},
- {PlayerControlBarModel::NEXT_BUTTON},
- {PlayerControlBarModel::LOOP_BUTTON}
- },
- {
- // right
- {PlayerControlBarModel::VOLUME},
- {PlayerControlBarModel::PLAYER_SWITCH_BUTTON}
- }
-};
-
-
-PlayerControlBarModel::PlayerControlBarModel(QObject *_parent) : QAbstractListModel(_parent)
-{
- configName = "MainPlayerToolbar";
-
- connect(this, &QAbstractListModel::rowsInserted, this, &PlayerControlBarModel::countChanged);
- connect(this, &QAbstractListModel::rowsRemoved, this, &PlayerControlBarModel::countChanged);
- connect(this, &QAbstractListModel::modelReset, this, &PlayerControlBarModel::countChanged);
-}
-
-void PlayerControlBarModel::saveConfig()
-{
- getSettings()->setValue(configName,getConfig());
-}
-
-QString PlayerControlBarModel::getConfig()
-{
- QString config="";
- for (IconToolButton it: mButtons) {
- config += QString::number(it.id);
- config += ";";
- }
- return config;
-}
-
-void PlayerControlBarModel::reloadConfig(QString config)
-{
- beginResetModel();
- mButtons.clear();
- if (!config.isEmpty())
- parseAndAdd(config);
- endResetModel();
-}
-
-void PlayerControlBarModel::reloadModel()
-{
- beginResetModel();
- mButtons.clear();
-
- QVariant config = getSettings() ->value(configName);
-
- if (!config.isNull() && config.canConvert<QString>())
- parseAndAdd(config.toString());
- else
- {
- const auto configAndAlignment = configName.split("-");
- if (configAndAlignment.size() == 2)
- {
- const auto alignment = configAndAlignment[1];
- if (configAndAlignment[0] == QLatin1String("MainPlayerToolbar"))
- {
- if (alignment == "left")
- parseDefault(MAIN_TB_DEFAULT[default_align::ALIGN_LEFT]);
- else if (alignment == "center")
- parseDefault(MAIN_TB_DEFAULT[default_align::ALIGN_CENTER]);
- else if (alignment == "right")
- parseDefault(MAIN_TB_DEFAULT[default_align::ALIGN_RIGHT]);
- }
- else
- {
- if (alignment == "left")
- parseDefault(MINI_TB_DEFAULT[default_align::ALIGN_LEFT]);
- else if (alignment == "center")
- parseDefault(MINI_TB_DEFAULT[default_align::ALIGN_CENTER]);
- else if (alignment == "right")
- parseDefault(MINI_TB_DEFAULT[default_align::ALIGN_RIGHT]);
-
- }
- }
- }
- endResetModel();
-}
-
-void PlayerControlBarModel::parseDefault(const QVector<PlayerControlBarModel::IconToolButton>& config)
-{
- beginInsertRows(QModelIndex(),rowCount(),rowCount() + config.size());
- for (const auto& i : config)
- mButtons.append(i);
- endInsertRows();
-}
-
-void PlayerControlBarModel::parseAndAdd(const QString &config)
-{
- beginInsertRows(QModelIndex(),rowCount(),rowCount()+config.split(";",
- #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
- Qt::SkipEmptyParts
- #else
- QString::SkipEmptyParts
- #endif
- ).length() - 1);
-
- for (const QString& iconPropertyTxt : config.split( ";",
- #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
- Qt::SkipEmptyParts
- #else
- QString::SkipEmptyParts
- #endif
- ) )
- {
- QStringList list2 = iconPropertyTxt.trimmed().split( "-" );
-
- if( list2.count() < 1 )
- {
- msg_Warn( p_intf, "Parsing error 1. Please, report this." );
- continue;
- }
- bool ok;
- ButtonType_e i_type = static_cast<ButtonType_e>(list2.at( 0 ).toInt( &ok ));
- if( !ok )
- {
- msg_Warn( p_intf, "Parsing error 2. Please, report this." );
- continue;
- }
-
- IconToolButton itButton = {i_type};
- mButtons.append(itButton);
- }
-
- endInsertRows();
-}
-
-int PlayerControlBarModel::rowCount(const QModelIndex &parent) const
-{
- if (parent.isValid() )
- return 0;
-
- return mButtons.size();
-}
-
-QVariant PlayerControlBarModel::data(const QModelIndex &index, int role) const
-{
- if (!index.isValid() )
- return QVariant();
-
- const IconToolButton button = mButtons.at(index.row());
-
- switch (role) {
- case ID_ROLE:
- return QVariant(button.id);
- }
- return QVariant();
-}
-
-bool PlayerControlBarModel::setData(const QModelIndex &index, const QVariant &value, int role)
-{
- IconToolButton button = mButtons.at(index.row());
- switch (role) {
- case ID_ROLE:
- button.id = value.toInt();
- break;
- }
-
- if (setButtonAt(index.row(),button)) {
- emit dataChanged(index, index, QVector<int>() << role);
- return true;
- }
- return false;
-}
-
-Qt::ItemFlags PlayerControlBarModel::flags(const QModelIndex &index) const
-{
- if (!index.isValid())
- return Qt::NoItemFlags;
-
- return Qt::ItemIsEditable;
-}
-
-QHash<int, QByteArray> PlayerControlBarModel::roleNames() const
-{
- QHash<int, QByteArray> names;
-
- names[ID_ROLE] = "id";
-
- return names;
-}
-bool PlayerControlBarModel::setButtonAt(int index, const IconToolButton &button)
-{
- if(index < 0 || index >= mButtons.size())
- return false;
- const IconToolButton &oldButton = mButtons.at(index);
-
- if (button.id == oldButton.id)
- return false;
-
- mButtons[index] = button;
- return true;
-}
-
-void PlayerControlBarModel::setMainCtx(QmlMainContext* ctx)
-{
- if(ctx == nullptr && m_mainCtx == ctx)
- return;
- m_mainCtx = ctx;
- p_intf = m_mainCtx->getIntf();
- assert(p_intf != nullptr);
- reloadModel();
- emit ctxChanged(ctx);
-}
-
-void PlayerControlBarModel::setConfigName(QString name)
-{
- if(configName == name)
- return;
- configName = name;
- if (m_mainCtx)
- reloadModel();
- emit configNameChanged(name);
-}
-
-QString PlayerControlBarModel::getSerializedDefaultStyle()
-{
- QString out;
-
- auto serialize = [](auto style)
- {
- QString _out;
- for (size_t i = 0; i < default_align::ALIGN_SIZE; i++)
- {
- for (const auto& it : style[i])
- {
- _out += QString::number(it.id) + ";";
- }
- _out.chop(1);
- _out += "#";
- }
- _out.chop(1);
- return _out;
- };
-
- out += serialize(MAIN_TB_DEFAULT);
- out += " | ";
- out += serialize(MINI_TB_DEFAULT);
-
- return out;
-}
-
-void PlayerControlBarModel::insert(int index, QVariantMap bdata)
-{
- beginInsertRows(QModelIndex(),index,index);
- mButtons.insert(index, { bdata.value("id").toInt() });
- endInsertRows();
-}
-void PlayerControlBarModel::move(int src, int dest)
-{
- if(src == dest) return;
- beginMoveRows(QModelIndex(),src,src,QModelIndex(),dest + (src < dest ? 1:0));
- mButtons.move(src,dest);
- endMoveRows();
-}
-
-void PlayerControlBarModel::remove(int index)
-{
- beginRemoveRows(QModelIndex(),index,index);
- mButtons.remove(index);
- endRemoveRows();
-}
diff --git a/modules/gui/qt/player/qml/ControlBar.qml b/modules/gui/qt/player/qml/ControlBar.qml
index a867bca770..17655e3578 100644
--- a/modules/gui/qt/player/qml/ControlBar.qml
+++ b/modules/gui/qt/player/qml/ControlBar.qml
@@ -40,7 +40,7 @@ Widgets.NavigableFocusScope {
readonly property alias sliderY: row2.y
property int textPosition: ControlBar.TimeTextPosition.AboveSlider
property VLCColors colors: VLCStyle.nightColors
- property var configs: ["MainPlayerToolbar-left", "MainPlayerToolbar-center", "MainPlayerToolbar-right"]
+ property alias identifier: playerButtonsLayout.identifier
property alias sliderHeight: trackPositionSlider.barHeight
property alias sliderBackgroundColor: trackPositionSlider.backgroundColor
property alias sliderProgressColor: trackPositionSlider.progressBarColor
@@ -145,8 +145,6 @@ Widgets.NavigableFocusScope {
bottomMargin: VLCStyle.applicationVerticalMargin
}
- models: [playerControlBarModel_left, playerControlBarModel_center, playerControlBarModel_right]
-
navigationUpItem: trackPositionSlider.enabled ? trackPositionSlider : root.navigationUpItem
colors: root.colors
@@ -194,23 +192,4 @@ Widgets.NavigableFocusScope {
Keys.onDownPressed: playerButtonsLayout.focus = true
}
-
-
- PlayerControlBarModel{
- id:playerControlBarModel_left
- mainCtx: mainctx
- configName: root.configs[0]
- }
-
- PlayerControlBarModel{
- id:playerControlBarModel_center
- mainCtx: mainctx
- configName: root.configs[1]
- }
-
- PlayerControlBarModel{
- id:playerControlBarModel_right
- mainCtx: mainctx
- configName: root.configs[2]
- }
}
diff --git a/modules/gui/qt/player/qml/ControlButtons.qml b/modules/gui/qt/player/qml/ControlButtons.qml
index e04c00d45c..c3f3dac142 100644
--- a/modules/gui/qt/player/qml/ControlButtons.qml
+++ b/modules/gui/qt/player/qml/ControlButtons.qml
@@ -35,74 +35,74 @@ Item{
signal requestLockUnlockAutoHide(bool lock, var source)
property var buttonL: [
- { id: PlayerControlBarModel.PLAY_BUTTON, label: VLCIcons.play, text: i18n.qtr("Play")},
- { id: PlayerControlBarModel.STOP_BUTTON, label: VLCIcons.stop, text: i18n.qtr("Stop")},
- { id: PlayerControlBarModel.OPEN_BUTTON, label: VLCIcons.eject, text: i18n.qtr("Open")},
- { id: PlayerControlBarModel.PREVIOUS_BUTTON, label: VLCIcons.previous, text: i18n.qtr("Previous")},
- { id: PlayerControlBarModel.NEXT_BUTTON, label: VLCIcons.next, text: i18n.qtr("Next")},
- { id: PlayerControlBarModel.SLOWER_BUTTON, label: VLCIcons.slower, text: i18n.qtr("Slower")},
- { id: PlayerControlBarModel.FASTER_BUTTON, label: VLCIcons.faster, text: i18n.qtr("Faster")},
- { id: PlayerControlBarModel.FULLSCREEN_BUTTON, label: VLCIcons.fullscreen, text: i18n.qtr("Fullscreen")},
- { id: PlayerControlBarModel.EXTENDED_BUTTON, label: VLCIcons.extended, text: i18n.qtr("Extended panel")},
- { id: PlayerControlBarModel.PLAYLIST_BUTTON, label: VLCIcons.playlist, text: i18n.qtr("Playlist")},
- { id: PlayerControlBarModel.SNAPSHOT_BUTTON, label: VLCIcons.snapshot, text: i18n.qtr("Snapshot")},
- { id: PlayerControlBarModel.RECORD_BUTTON, label: VLCIcons.record, text: i18n.qtr("Record")},
- { id: PlayerControlBarModel.ATOB_BUTTON, label: VLCIcons.atob, text: i18n.qtr("A-B Loop")},
- { id: PlayerControlBarModel.FRAME_BUTTON, label: VLCIcons.frame_by_frame, text: i18n.qtr("Frame By Frame")},
- { id: PlayerControlBarModel.SKIP_BACK_BUTTON, label: VLCIcons.skip_back, text: i18n.qtr("Step backward")},
- { id: PlayerControlBarModel.SKIP_FW_BUTTON, label: VLCIcons.skip_for, text: i18n.qtr("Step forward")},
- { id: PlayerControlBarModel.QUIT_BUTTON, label: VLCIcons.clear, text: i18n.qtr("Quit")},
- { id: PlayerControlBarModel.RANDOM_BUTTON, label: VLCIcons.shuffle_on, text: i18n.qtr("Random")},
- { id: PlayerControlBarModel.LOOP_BUTTON, label: VLCIcons.repeat_all, text: i18n.qtr("Loop")},
- { id: PlayerControlBarModel.INFO_BUTTON, label: VLCIcons.info, text: i18n.qtr("Information")},
- { id: PlayerControlBarModel.LANG_BUTTON, label: VLCIcons.audiosub, text: i18n.qtr("Open subtitles")},
- { id: PlayerControlBarModel.MENU_BUTTON, label: VLCIcons.menu, text: i18n.qtr("Menu Button")},
- { id: PlayerControlBarModel.BACK_BUTTON, label: VLCIcons.exit, text: i18n.qtr("Back Button")},
- { id: PlayerControlBarModel.CHAPTER_PREVIOUS_BUTTON, label: VLCIcons.dvd_prev, text: i18n.qtr("Previous chapter")},
- { id: PlayerControlBarModel.CHAPTER_NEXT_BUTTON, label: VLCIcons.dvd_next, text: i18n.qtr("Next chapter")},
- { id: PlayerControlBarModel.VOLUME, label: VLCIcons.volume_high, text: i18n.qtr("Volume Widget")},
- { id: PlayerControlBarModel.TELETEXT_BUTTONS, label: VLCIcons.tvtelx, text: i18n.qtr("Teletext")},
- { id: PlayerControlBarModel.ASPECT_RATIO_COMBOBOX, label: VLCIcons.aspect_ratio, text: i18n.qtr("Aspect Ratio")},
- { id: PlayerControlBarModel.WIDGET_SPACER, label: VLCIcons.space, text: i18n.qtr("Spacer")},
- { id: PlayerControlBarModel.WIDGET_SPACER_EXTEND, label: VLCIcons.space, text: i18n.qtr("Expanding Spacer")},
- { id: PlayerControlBarModel.PLAYER_SWITCH_BUTTON, label: VLCIcons.fullscreen, text: i18n.qtr("Switch Player")},
- { id: PlayerControlBarModel.ARTWORK_INFO, label: VLCIcons.info, text: i18n.qtr("Artwork Info")}
+ { id: ControlListModel.PLAY_BUTTON, label: VLCIcons.play, text: i18n.qtr("Play")},
+ { id: ControlListModel.STOP_BUTTON, label: VLCIcons.stop, text: i18n.qtr("Stop")},
+ { id: ControlListModel.OPEN_BUTTON, label: VLCIcons.eject, text: i18n.qtr("Open")},
+ { id: ControlListModel.PREVIOUS_BUTTON, label: VLCIcons.previous, text: i18n.qtr("Previous")},
+ { id: ControlListModel.NEXT_BUTTON, label: VLCIcons.next, text: i18n.qtr("Next")},
+ { id: ControlListModel.SLOWER_BUTTON, label: VLCIcons.slower, text: i18n.qtr("Slower")},
+ { id: ControlListModel.FASTER_BUTTON, label: VLCIcons.faster, text: i18n.qtr("Faster")},
+ { id: ControlListModel.FULLSCREEN_BUTTON, label: VLCIcons.fullscreen, text: i18n.qtr("Fullscreen")},
+ { id: ControlListModel.EXTENDED_BUTTON, label: VLCIcons.extended, text: i18n.qtr("Extended panel")},
+ { id: ControlListModel.PLAYLIST_BUTTON, label: VLCIcons.playlist, text: i18n.qtr("Playlist")},
+ { id: ControlListModel.SNAPSHOT_BUTTON, label: VLCIcons.snapshot, text: i18n.qtr("Snapshot")},
+ { id: ControlListModel.RECORD_BUTTON, label: VLCIcons.record, text: i18n.qtr("Record")},
+ { id: ControlListModel.ATOB_BUTTON, label: VLCIcons.atob, text: i18n.qtr("A-B Loop")},
+ { id: ControlListModel.FRAME_BUTTON, label: VLCIcons.frame_by_frame, text: i18n.qtr("Frame By Frame")},
+ { id: ControlListModel.SKIP_BACK_BUTTON, label: VLCIcons.skip_back, text: i18n.qtr("Step backward")},
+ { id: ControlListModel.SKIP_FW_BUTTON, label: VLCIcons.skip_for, text: i18n.qtr("Step forward")},
+ { id: ControlListModel.QUIT_BUTTON, label: VLCIcons.clear, text: i18n.qtr("Quit")},
+ { id: ControlListModel.RANDOM_BUTTON, label: VLCIcons.shuffle_on, text: i18n.qtr("Random")},
+ { id: ControlListModel.LOOP_BUTTON, label: VLCIcons.repeat_all, text: i18n.qtr("Loop")},
+ { id: ControlListModel.INFO_BUTTON, label: VLCIcons.info, text: i18n.qtr("Information")},
+ { id: ControlListModel.LANG_BUTTON, label: VLCIcons.audiosub, text: i18n.qtr("Open subtitles")},
+ { id: ControlListModel.MENU_BUTTON, label: VLCIcons.menu, text: i18n.qtr("Menu Button")},
+ { id: ControlListModel.BACK_BUTTON, label: VLCIcons.exit, text: i18n.qtr("Back Button")},
+ { id: ControlListModel.CHAPTER_PREVIOUS_BUTTON, label: VLCIcons.dvd_prev, text: i18n.qtr("Previous chapter")},
+ { id: ControlListModel.CHAPTER_NEXT_BUTTON, label: VLCIcons.dvd_next, text: i18n.qtr("Next chapter")},
+ { id: ControlListModel.VOLUME, label: VLCIcons.volume_high, text: i18n.qtr("Volume Widget")},
+ { id: ControlListModel.TELETEXT_BUTTONS, label: VLCIcons.tvtelx, text: i18n.qtr("Teletext")},
+ { id: ControlListModel.ASPECT_RATIO_COMBOBOX, label: VLCIcons.aspect_ratio, text: i18n.qtr("Aspect Ratio")},
+ { id: ControlListModel.WIDGET_SPACER, label: VLCIcons.space, text: i18n.qtr("Spacer")},
+ { id: ControlListModel.WIDGET_SPACER_EXTEND, label: VLCIcons.space, text: i18n.qtr("Expanding Spacer")},
+ { id: ControlListModel.PLAYER_SWITCH_BUTTON, label: VLCIcons.fullscreen, text: i18n.qtr("Switch Player")},
+ { id: ControlListModel.ARTWORK_INFO, label: VLCIcons.info, text: i18n.qtr("Artwork Info")}
]
function returnbuttondelegate(inpID){
switch (inpID){
- case PlayerControlBarModel.RANDOM_BUTTON: return randomBtnDelegate
- case PlayerControlBarModel.PREVIOUS_BUTTON: return prevBtnDelegate
- case PlayerControlBarModel.PLAY_BUTTON: return playBtnDelegate
- case PlayerControlBarModel.NEXT_BUTTON: return nextBtnDelegate
- case PlayerControlBarModel.LOOP_BUTTON: return repeatBtnDelegate
- case PlayerControlBarModel.LANG_BUTTON: return langBtnDelegate
- case PlayerControlBarModel.PLAYLIST_BUTTON:return playlistBtnDelegate
- case PlayerControlBarModel.MENU_BUTTON:return menuBtnDelegate
- case PlayerControlBarModel.CHAPTER_PREVIOUS_BUTTON:return chapterPreviousBtnDelegate
- case PlayerControlBarModel.CHAPTER_NEXT_BUTTON:return chapterNextBtnDelegate
- case PlayerControlBarModel.BACK_BUTTON:return backBtnDelegate
- case PlayerControlBarModel.WIDGET_SPACER:return spacerDelegate
- case PlayerControlBarModel.WIDGET_SPACER_EXTEND:return extendiblespacerDelegate
- case PlayerControlBarModel.RECORD_BUTTON: return recordBtnDelegate
- case PlayerControlBarModel.FULLSCREEN_BUTTON: return fullScreenBtnDelegate
- case PlayerControlBarModel.ATOB_BUTTON: return toggleABloopstateDelegate
- case PlayerControlBarModel.SNAPSHOT_BUTTON: return snapshotBtnDelegate
- case PlayerControlBarModel.STOP_BUTTON: return stopBtndelgate
- case PlayerControlBarModel.INFO_BUTTON: return mediainfoBtnDelegate
- case PlayerControlBarModel.FRAME_BUTTON: return framebyframeDelegate
- case PlayerControlBarModel.FASTER_BUTTON: return fasterBtnDelegate
- case PlayerControlBarModel.SLOWER_BUTTON: return slowerBtnDelegate
- case PlayerControlBarModel.OPEN_BUTTON: return openmediaBtnDelegate
- case PlayerControlBarModel.EXTENDED_BUTTON: return extdSettingsBtnDelegate
- case PlayerControlBarModel.SKIP_FW_BUTTON: return stepFwdBtnDelegate
- case PlayerControlBarModel.SKIP_BACK_BUTTON: return stepBackBtnDelegate
- case PlayerControlBarModel.QUIT_BUTTON: return quitBtnDelegate
- case PlayerControlBarModel.VOLUME: return volumeBtnDelegate
- case PlayerControlBarModel.ASPECT_RATIO_COMBOBOX: return aspectRatioDelegate
- case PlayerControlBarModel.TELETEXT_BUTTONS: return teletextdelegate
- case PlayerControlBarModel.PLAYER_SWITCH_BUTTON: return playerSwitchBtnDelegate
- case PlayerControlBarModel.ARTWORK_INFO: return artworkInfoDelegate
+ case ControlListModel.RANDOM_BUTTON: return randomBtnDelegate
+ case ControlListModel.PREVIOUS_BUTTON: return prevBtnDelegate
+ case ControlListModel.PLAY_BUTTON: return playBtnDelegate
+ case ControlListModel.NEXT_BUTTON: return nextBtnDelegate
+ case ControlListModel.LOOP_BUTTON: return repeatBtnDelegate
+ case ControlListModel.LANG_BUTTON: return langBtnDelegate
+ case ControlListModel.PLAYLIST_BUTTON:return playlistBtnDelegate
+ case ControlListModel.MENU_BUTTON:return menuBtnDelegate
+ case ControlListModel.CHAPTER_PREVIOUS_BUTTON:return chapterPreviousBtnDelegate
+ case ControlListModel.CHAPTER_NEXT_BUTTON:return chapterNextBtnDelegate
+ case ControlListModel.BACK_BUTTON:return backBtnDelegate
+ case ControlListModel.WIDGET_SPACER:return spacerDelegate
+ case ControlListModel.WIDGET_SPACER_EXTEND:return extendiblespacerDelegate
+ case ControlListModel.RECORD_BUTTON: return recordBtnDelegate
+ case ControlListModel.FULLSCREEN_BUTTON: return fullScreenBtnDelegate
+ case ControlListModel.ATOB_BUTTON: return toggleABloopstateDelegate
+ case ControlListModel.SNAPSHOT_BUTTON: return snapshotBtnDelegate
+ case ControlListModel.STOP_BUTTON: return stopBtndelgate
+ case ControlListModel.INFO_BUTTON: return mediainfoBtnDelegate
+ case ControlListModel.FRAME_BUTTON: return framebyframeDelegate
+ case ControlListModel.FASTER_BUTTON: return fasterBtnDelegate
+ case ControlListModel.SLOWER_BUTTON: return slowerBtnDelegate
+ case ControlListModel.OPEN_BUTTON: return openmediaBtnDelegate
+ case ControlListModel.EXTENDED_BUTTON: return extdSettingsBtnDelegate
+ case ControlListModel.SKIP_FW_BUTTON: return stepFwdBtnDelegate
+ case ControlListModel.SKIP_BACK_BUTTON: return stepBackBtnDelegate
+ case ControlListModel.QUIT_BUTTON: return quitBtnDelegate
+ case ControlListModel.VOLUME: return volumeBtnDelegate
+ case ControlListModel.ASPECT_RATIO_COMBOBOX: return aspectRatioDelegate
+ case ControlListModel.TELETEXT_BUTTONS: return teletextdelegate
+ case ControlListModel.PLAYER_SWITCH_BUTTON: return playerSwitchBtnDelegate
+ case ControlListModel.ARTWORK_INFO: return artworkInfoDelegate
}
console.log("button delegate id " + inpID + " doesn't exists")
return spacerDelegate
diff --git a/modules/gui/qt/player/qml/MiniPlayer.qml b/modules/gui/qt/player/qml/MiniPlayer.qml
index 748751da31..9cc983dc05 100644
--- a/modules/gui/qt/player/qml/MiniPlayer.qml
+++ b/modules/gui/qt/player/qml/MiniPlayer.qml
@@ -83,7 +83,7 @@ Widgets.NavigableFocusScope {
sliderHeight: VLCStyle.dp(3, VLCStyle.scale)
sliderBackgroundColor: colors.sliderBarMiniplayerBgColor
sliderProgressColor: colors.accent
- configs: ["MiniPlayerToolbar-left", "MiniPlayerToolbar-center", "MiniPlayerToolbar-right"]
+ identifier: "MiniPlayer"
navigationParent: root
Keys.onPressed: {
diff --git a/modules/gui/qt/player/qml/Player.qml b/modules/gui/qt/player/qml/Player.qml
index e7190fcd34..ebe95f5792 100644
--- a/modules/gui/qt/player/qml/Player.qml
+++ b/modules/gui/qt/player/qml/Player.qml
@@ -499,6 +499,8 @@ Widgets.NavigableFocusScope {
navigationUpItem: playlistpopup.showPlaylist ? playlistpopup : (audioControls.visible ? audioControls : topcontrolView)
onRequestLockUnlockAutoHide: rootPlayer.lockUnlockAutoHide(lock, source)
+
+ identifier: "MainPlayer"
}
}
}
diff --git a/modules/gui/qt/player/qml/PlayerButtonsLayout.qml b/modules/gui/qt/player/qml/PlayerButtonsLayout.qml
index b6016988cd..15f11ae293 100644
--- a/modules/gui/qt/player/qml/PlayerButtonsLayout.qml
+++ b/modules/gui/qt/player/qml/PlayerButtonsLayout.qml
@@ -42,24 +42,20 @@ Widgets.NavigableFocusScope {
property real spacing: VLCStyle.margin_normal // spacing between controls
property real layoutSpacing: VLCStyle.margin_xlarge // spacing between layouts (left, center, and right)
- signal requestLockUnlockAutoHide(bool lock, var source)
-
- enum Alignment {
- Left = 0,
- Center = 1,
- Right = 2
+ property string identifier
+ readonly property var model: {
+ if (!!mainInterface.controlbarProfileModel.currentModel)
+ mainInterface.controlbarProfileModel.currentModel.getModel(identifier)
+ else
+ undefined
}
- property var models: []
+ signal requestLockUnlockAutoHide(bool lock, var source)
- Connections {
- target: mainInterface
- onToolBarConfUpdated: {
- models[PlayerButtonsLayout.Alignment.Left].reloadModel()
- models[PlayerButtonsLayout.Alignment.Center].reloadModel()
- models[PlayerButtonsLayout.Alignment.Right].reloadModel()
- }
+ Component.onCompleted: {
+ console.assert(!!identifier)
+ console.assert(identifier.length > 0)
}
ControlButtons {
@@ -70,13 +66,9 @@ Widgets.NavigableFocusScope {
onRequestLockUnlockAutoHide: playerButtonsLayout.requestLockUnlockAutoHide(lock, source)
}
- ButtonsLayout {
+ Loader {
id: buttonrow_left
- model: models[PlayerButtonsLayout.Alignment.Left]
-
- extraWidth: (buttonrow_center.x - buttonrow_left.x - minimumWidth - layoutSpacing)
-
anchors {
left: parent.left
verticalCenter: parent.verticalCenter
@@ -86,20 +78,26 @@ Widgets.NavigableFocusScope {
bottomMargin: marginBottom
rightMargin: layoutSpacing
}
-
- visible: extraWidth < 0 ? false : true // extraWidth < 0 means there is not even available space for minimumSize
- navigationParent: playerButtonsLayout
- navigationRightItem: buttonrow_center
+ active: !!playerButtonsLayout.model && !!playerButtonsLayout.model.left
- focus: true
+ sourceComponent: ButtonsLayout {
+ model: playerButtonsLayout.model.left
+
+ extraWidth: (buttonrow_center.x - buttonrow_left.x - minimumWidth - layoutSpacing)
+
+ visible: extraWidth < 0 ? false : true // extraWidth < 0 means there is not even available space for minimumSize
+
+ navigationParent: playerButtonsLayout
+ navigationRightItem: buttonrow_center
+
+ focus: true
+ }
}
- ButtonsLayout {
+ Loader {
id: buttonrow_center
- model: models[PlayerButtonsLayout.Alignment.Center]
-
anchors {
centerIn: parent
@@ -107,17 +105,19 @@ Widgets.NavigableFocusScope {
bottomMargin: playerButtonsLayout.marginBottom
}
- navigationParent: playerButtonsLayout
- navigationLeftItem: buttonrow_left
- navigationRightItem: buttonrow_right
- }
+ active: !!playerButtonsLayout.model && !!playerButtonsLayout.model.center
- ButtonsLayout {
- id: buttonrow_right
+ sourceComponent: ButtonsLayout {
+ model: playerButtonsLayout.model.center
- model: models[PlayerButtonsLayout.Alignment.Right]
+ navigationParent: playerButtonsLayout
+ navigationLeftItem: buttonrow_left
+ navigationRightItem: buttonrow_right
+ }
+ }
- extraWidth: (playerButtonsLayout.width - (buttonrow_center.x + buttonrow_center.width) - minimumWidth - (2 * layoutSpacing))
+ Loader {
+ id: buttonrow_right
anchors {
right: parent.right
@@ -129,9 +129,19 @@ Widgets.NavigableFocusScope {
leftMargin: layoutSpacing
}
- visible: extraWidth < 0 ? false : true // extraWidth < 0 means there is not even available space for minimumSize
+ active: !!playerButtonsLayout.model && !!playerButtonsLayout.model.right
+
+ sourceComponent: ButtonsLayout {
- navigationParent: playerButtonsLayout
- navigationLeftItem: buttonrow_center
+
+ model: playerButtonsLayout.model.right
+
+ extraWidth: (playerButtonsLayout.width - (buttonrow_center.x + buttonrow_center.width) - minimumWidth - (2 * layoutSpacing))
+
+ visible: extraWidth < 0 ? false : true // extraWidth < 0 means there is not even available space for minimumSize
+
+ navigationParent: playerButtonsLayout
+ navigationLeftItem: buttonrow_center
+ }
}
}
diff --git a/modules/gui/qt/player/qml/TopBar.qml b/modules/gui/qt/player/qml/TopBar.qml
index 4aa53d15a8..c621c63f1f 100644
--- a/modules/gui/qt/player/qml/TopBar.qml
+++ b/modules/gui/qt/player/qml/TopBar.qml
@@ -269,7 +269,7 @@ Widgets.NavigableFocusScope{
Widgets.IconToolButton {
id: playlistButton
- objectName: PlayerControlBarModel.PLAYLIST_BUTTON
+ objectName: ControlListModel.PLAYLIST_BUTTON
size: VLCStyle.banner_icon_size
iconText: VLCIcons.playlist
text: i18n.qtr("Playlist")
--
2.27.0
More information about the vlc-devel
mailing list