[vlc-commits] qt: implement mixed qml/native menubar
Pierre Lamot
git at videolan.org
Mon Oct 12 11:45:10 CEST 2020
vlc | branch: master | Pierre Lamot <pierre at videolabs.io> | Mon Oct 5 16:05:44 2020 +0200| [8922bbb0e07040c535fca293db5f0e8d9b8ba6bf] | committer: Pierre Lamot
qt: implement mixed qml/native menubar
> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=8922bbb0e07040c535fca293db5f0e8d9b8ba6bf
---
modules/gui/qt/Makefile.am | 1 +
modules/gui/qt/maininterface/mainui.cpp | 1 +
modules/gui/qt/menus/qml/Menubar.qml | 117 +++++++++++++++++++++++++
modules/gui/qt/menus/qml_menu_wrapper.cpp | 136 ++++++++++++++++++++++++++++++
modules/gui/qt/menus/qml_menu_wrapper.hpp | 57 ++++++++++++-
modules/gui/qt/vlc.qrc | 1 +
6 files changed, 312 insertions(+), 1 deletion(-)
diff --git a/modules/gui/qt/Makefile.am b/modules/gui/qt/Makefile.am
index e6cab60218..1cf5da7f59 100644
--- a/modules/gui/qt/Makefile.am
+++ b/modules/gui/qt/Makefile.am
@@ -641,6 +641,7 @@ libqt_plugin_la_QML = \
gui/qt/menus/qml/HelpMenu.qml \
gui/qt/menus/qml/MainDropdownMenu.qml \
gui/qt/menus/qml/MainMenubar.qml \
+ gui/qt/menus/qml/Menubar.qml \
gui/qt/menus/qml/MediaMenu.qml \
gui/qt/menus/qml/PlaybackMenu.qml \
gui/qt/menus/qml/PopupMenu.qml \
diff --git a/modules/gui/qt/maininterface/mainui.cpp b/modules/gui/qt/maininterface/mainui.cpp
index 500b547585..9b8bc9e93f 100644
--- a/modules/gui/qt/maininterface/mainui.cpp
+++ b/modules/gui/qt/maininterface/mainui.cpp
@@ -215,6 +215,7 @@ void MainUI::registerQMLTypes()
qmlRegisterType<PlayerControlBarModel>( "org.videolan.vlc", 0, 1, "PlayerControlBarModel");
qmlRegisterType<QmlGlobalMenu>( "org.videolan.vlc", 0, 1, "QmlGlobalMenu" );
+ qmlRegisterType<QmlMenuBar>( "org.videolan.vlc", 0, 1, "QmlMenuBar" );
qmlRegisterType<NetworkMediaContextMenu>( "org.videolan.vlc", 0, 1, "NetworkMediaContextMenu" );
qmlRegisterType<NetworkDeviceContextMenu>( "org.videolan.vlc", 0, 1, "NetworkDeviceContextMenu" );
qmlRegisterType<PlaylistContextMenu>( "org.videolan.vlc", 0, 1, "PlaylistContextMenu" );
diff --git a/modules/gui/qt/menus/qml/Menubar.qml b/modules/gui/qt/menus/qml/Menubar.qml
new file mode 100644
index 0000000000..97ffa4c486
--- /dev/null
+++ b/modules/gui/qt/menus/qml/Menubar.qml
@@ -0,0 +1,117 @@
+/*****************************************************************************
+ * Copyright (C) 2020 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.
+ *****************************************************************************/
+import QtQuick 2.11
+import QtQuick.Controls 2.4
+import QtQuick.Controls.impl 2.4
+import QtQuick.Templates 2.4 as T
+import org.videolan.vlc 0.1
+import QtQuick.Layouts 1.3
+
+import "qrc:///style/"
+
+
+Item {
+ id: root
+
+ implicitHeight: menubarLayout.implicitHeight
+ implicitWidth: menubarLayout.implicitWidth
+
+ property color textColor: VLCStyle.colors.text
+ property color bgColor: VLCStyle.colors.bgHover
+
+ Action{ id: mediaMenu; text: i18n.qtr("&Media") ; onTriggered: menubar.popupMediaMenu(source); }
+ Action{ id: playbackMenu; text: i18n.qtr("&Playback") ; onTriggered: menubar.popupPlaybackMenu(source);}
+ Action{ id: videoMenu; text: i18n.qtr("&Video") ; onTriggered: menubar.popupVideoMenu(source); }
+ Action{ id: audioMenu; text: i18n.qtr("&Audio") ; onTriggered: menubar.popupAudioMenu(source); }
+ Action{ id: subtitleMenu; text: i18n.qtr("&Subtitle") ; onTriggered: menubar.popupSubtitleMenu(source);}
+ Action{ id: toolMenu; text: i18n.qtr("&Tools") ; onTriggered: menubar.popupToolsMenu(source); }
+ Action{ id: viewMenu; text: i18n.qtr("V&iew") ; onTriggered: menubar.popupViewMenu(source); }
+ Action{ id: helpMenu; text: i18n.qtr("&Help") ; onTriggered: menubar.popupHelpMenu(source); }
+
+ property var toolbarModel: [
+ mediaMenu,
+ playbackMenu,
+ videoMenu,
+ audioMenu,
+ subtitleMenu,
+ toolMenu,
+ viewMenu,
+ helpMenu,
+ ]
+
+ property int _menuIndex: -1
+
+ function openMenu(obj, cb, index) {
+ cb.trigger(obj)
+ root._menuIndex = index
+ }
+
+ function updateHover(obj, cb, index, hovered ) {
+ if (hovered && menubar.openMenuOnHover) {
+ cb.trigger(obj)
+ root._menuIndex = index
+ }
+ }
+
+ QmlMenuBar {
+ id: menubar
+ ctx: mainctx
+ menubar: menubarLayout
+
+ onMenuClosed: _menuIndex = -1
+ onNavigateMenu: {
+ var i = (root._menuIndex + root.toolbarModel.length + direction) % root.toolbarModel.length
+ root.openMenu(menubarLayout.visibleChildren[i], root.toolbarModel[i], i)
+ }
+
+ }
+
+ RowLayout {
+ id: menubarLayout
+ spacing: 0
+ Repeater {
+ model: root.toolbarModel
+
+ T.Button {
+ id: control
+
+ text: modelData.text
+ onClicked: root.openMenu(this, modelData, index)
+ onHoveredChanged: root.updateHover(this, modelData, index, hovered)
+ font.pixelSize: VLCStyle.fontSize_normal
+
+ Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
+
+ implicitWidth: contentItem.implicitWidth + VLCStyle.margin_xsmall * 2
+ implicitHeight: contentItem.implicitHeight + VLCStyle.margin_xxxsmall * 2
+
+ contentItem: IconLabel {
+ text: control.text
+ font: control.font
+ opacity: enabled ? 1.0 : 0.3
+ color: root.textColor
+ }
+
+ background: Rectangle {
+ color: (control.hovered || index === root._menuIndex) ? root.bgColor
+ : "transparent"
+ }
+ }
+ }
+ }
+}
diff --git a/modules/gui/qt/menus/qml_menu_wrapper.cpp b/modules/gui/qt/menus/qml_menu_wrapper.cpp
index 01a85ba730..259b10c877 100644
--- a/modules/gui/qt/menus/qml_menu_wrapper.cpp
+++ b/modules/gui/qt/menus/qml_menu_wrapper.cpp
@@ -30,6 +30,7 @@
#include "playlist/playlist_controller.hpp"
#include "playlist/playlist_model.hpp"
#include "dialogs/dialogs_provider.hpp"
+#include "maininterface/main_interface.hpp"
#include <QSignalMapper>
@@ -95,6 +96,141 @@ void QmlGlobalMenu::popup(QPoint pos)
m_menu->popup(pos);
}
+QmlMenuBarMenu::QmlMenuBarMenu(QmlMenuBar* menubar, QWidget* parent)
+ : QMenu(parent)
+ , m_menubar(menubar)
+{}
+
+QmlMenuBarMenu::~QmlMenuBarMenu()
+{
+}
+
+void QmlMenuBarMenu::mouseMoveEvent(QMouseEvent* mouseEvent)
+{
+ QPoint globalPos =m_menubar-> m_menu->mapToGlobal(mouseEvent->pos());
+ if (m_menubar->getmenubar()->contains(m_menubar->getmenubar()->mapFromGlobal(globalPos))
+ && !m_menubar->m_button->contains(m_menubar->m_button->mapFromGlobal(globalPos)))
+ {
+ m_menubar->setopenMenuOnHover(true);
+ close();
+ return;
+ }
+ QMenu::mouseMoveEvent(mouseEvent);
+}
+
+void QmlMenuBarMenu::keyPressEvent(QKeyEvent * event)
+{
+ QMenu::keyPressEvent(event);
+ if (!event->isAccepted()
+ && (event->key() == Qt::Key_Left || event->key() == Qt::Key_Right))
+ {
+ event->accept();
+ emit m_menubar->navigateMenu(event->key() == Qt::Key_Left ? -1 : 1);
+ }
+}
+
+void QmlMenuBarMenu::keyReleaseEvent(QKeyEvent * event)
+{
+ QMenu::keyReleaseEvent(event);
+}
+
+QmlMenuBar::QmlMenuBar(QObject *parent)
+ : VLCMenuBar(parent)
+{
+}
+
+QmlMenuBar::~QmlMenuBar()
+{
+ if (m_menu)
+ delete m_menu;
+}
+
+void QmlMenuBar::popupMenuCommon( QQuickItem* button, std::function<void(QMenu*)> createMenuFunc)
+{
+ if (!m_ctx || !m_menubar || !button)
+ return;
+
+ intf_thread_t* p_intf = m_ctx->getIntf();
+ if (!p_intf)
+ return;
+
+ if (m_menu)
+ delete m_menu;
+
+ m_menu = new QmlMenuBarMenu(this, nullptr);
+ createMenuFunc(m_menu);
+ m_button = button;
+ m_openMenuOnHover = false;
+ connect(m_menu, &QMenu::aboutToHide, this, &QmlMenuBar::onMenuClosed);
+ QPointF position = button->mapToGlobal(QPoint(0, button->height()));
+ m_menu->popup(position.toPoint());
+}
+
+void QmlMenuBar::popupMediaMenu( QQuickItem* button )
+{
+ popupMenuCommon(button, [this](QMenu* menu) {
+ intf_thread_t* p_intf = m_ctx->getIntf();
+ FileMenu( p_intf, menu , p_intf->p_sys->p_mi );
+ });
+}
+
+void QmlMenuBar::popupPlaybackMenu( QQuickItem* button )
+{
+ popupMenuCommon(button, [this](QMenu* menu) {
+ NavigMenu( m_ctx->getIntf(), menu );
+ });
+}
+
+void QmlMenuBar::popupAudioMenu(QQuickItem* button )
+{
+ popupMenuCommon(button, [this](QMenu* menu) {
+ AudioMenu( m_ctx->getIntf(), menu );
+ });
+}
+
+void QmlMenuBar::popupVideoMenu( QQuickItem* button )
+{
+ popupMenuCommon(button, [this](QMenu* menu) {
+ VideoMenu( m_ctx->getIntf(), menu );
+ });
+}
+
+void QmlMenuBar::popupSubtitleMenu( QQuickItem* button )
+{
+ popupMenuCommon(button, [this](QMenu* menu) {
+ SubtitleMenu( m_ctx->getIntf(), menu );
+ });
+}
+
+
+void QmlMenuBar::popupToolsMenu( QQuickItem* button )
+{
+ popupMenuCommon(button, [this](QMenu* menu) {
+ ToolsMenu( m_ctx->getIntf(), menu );
+ });
+}
+
+void QmlMenuBar::popupViewMenu( QQuickItem* button )
+{
+ popupMenuCommon(button, [this](QMenu* menu) {
+ intf_thread_t* p_intf = m_ctx->getIntf();
+ ViewMenu( p_intf, menu, p_intf->p_sys->p_mi );
+ });
+}
+
+void QmlMenuBar::popupHelpMenu( QQuickItem* button )
+{
+ popupMenuCommon(button, [](QMenu* menu) {
+ HelpMenu(menu);
+ });
+}
+
+void QmlMenuBar::onMenuClosed()
+{
+ if (!m_openMenuOnHover)
+ emit menuClosed();
+}
+
BaseMedialibMenu::BaseMedialibMenu(QObject* parent)
: QObject(parent)
{}
diff --git a/modules/gui/qt/menus/qml_menu_wrapper.hpp b/modules/gui/qt/menus/qml_menu_wrapper.hpp
index a3031a4a7b..d9a87ddb7a 100644
--- a/modules/gui/qt/menus/qml_menu_wrapper.hpp
+++ b/modules/gui/qt/menus/qml_menu_wrapper.hpp
@@ -22,7 +22,7 @@
#include <QObject>
#include <QPoint>
-
+#include <QQuickItem>
#include "menus.hpp"
class MediaLib;
@@ -65,6 +65,61 @@ private:
QMenu* m_menu = nullptr;
};
+//inherit VLCMenuBar so we can access menu creation functions
+class QmlMenuBarMenu;
+class QmlMenuBar : public VLCMenuBar
+{
+ Q_OBJECT
+ SIMPLE_MENU_PROPERTY(QmlMainContext*, ctx, nullptr)
+ SIMPLE_MENU_PROPERTY(QQuickItem*, menubar, nullptr)
+ SIMPLE_MENU_PROPERTY(bool, openMenuOnHover, false)
+
+public:
+ explicit QmlMenuBar(QObject *parent = nullptr);
+ ~QmlMenuBar();
+
+signals:
+ //navigate to the left(-1)/right(1) menu
+ void navigateMenu(int direction);
+
+ void menuClosed();
+
+public slots:
+ void popupMediaMenu( QQuickItem* button);
+ void popupPlaybackMenu( QQuickItem* button);
+ void popupAudioMenu( QQuickItem* button );
+ void popupVideoMenu( QQuickItem* button );
+ void popupSubtitleMenu( QQuickItem* button );
+ void popupToolsMenu( QQuickItem* button );
+ void popupViewMenu( QQuickItem* button );
+ void popupHelpMenu( QQuickItem* button );
+
+private slots:
+ void onMenuClosed();
+
+private:
+ typedef QMenu* (*CreateMenuFunc)();
+ void popupMenuCommon( QQuickItem* button, std::function<void(QMenu*)> createMenuFunc);
+ QMenu* m_menu = nullptr;
+ QQuickItem* m_button = nullptr;
+ friend class QmlMenuBarMenu;
+};
+
+//specialized QMenu for QmlMenuBar
+class QmlMenuBarMenu : public QMenu
+{
+ Q_OBJECT
+public:
+ QmlMenuBarMenu(QmlMenuBar* menubar, QWidget* parent = nullptr);
+ ~QmlMenuBarMenu();
+protected:
+ void mouseMoveEvent(QMouseEvent* mouseEvent) override;
+ void keyPressEvent(QKeyEvent *) override;
+ void keyReleaseEvent(QKeyEvent *) override;
+private:
+ QmlMenuBar* m_menubar = nullptr;
+};
+
class BaseMedialibMenu : public QObject
{
Q_OBJECT
diff --git a/modules/gui/qt/vlc.qrc b/modules/gui/qt/vlc.qrc
index d9d9c8da66..1fc54805fe 100644
--- a/modules/gui/qt/vlc.qrc
+++ b/modules/gui/qt/vlc.qrc
@@ -287,6 +287,7 @@
<file alias="MainDropdownMenu.qml">menus/qml/MainDropdownMenu.qml</file>
<file alias="MainMenubar.qml">menus/qml/MainMenubar.qml</file>
<file alias="MediaMenu.qml">menus/qml/MediaMenu.qml</file>
+ <file alias="Menubar.qml">menus/qml/Menubar.qml</file>
<file alias="PlaybackMenu.qml">menus/qml/PlaybackMenu.qml</file>
<file alias="SubtitleMenu.qml">menus/qml/SubtitleMenu.qml</file>
<file alias="ToolsMenu.qml">menus/qml/ToolsMenu.qml</file>
More information about the vlc-commits
mailing list