Skip to content

Fix #15473: No platform menu bar support on Linux #15826

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jan 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions build/cmake/FindQt5.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ if (WIN32)
)
endif(WIN32)

if (OS_IS_LIN)
set(_components
${_components}
DBus
)
endif()

find_package(Qt5Core ${QT_MIN_VERSION} REQUIRED)

foreach(_component ${_components})
Expand Down
1 change: 1 addition & 0 deletions src/appshell/appshell.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
<file>qml/platform/win/AppSystemButtons.qml</file>
<file>qml/platform/AppMenuBar.qml</file>
<file>qml/platform/AppButtonBackground.qml</file>
<file>qml/platform/PlatformMenuBar.qml</file>
<file>resources/LoadingScreen.svg</file>
</qresource>
</RCC>
7 changes: 5 additions & 2 deletions src/appshell/appshellmodule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,11 @@ void AppShellModule::registerUiTypes()
qmlRegisterType<IOPreferencesModel>("MuseScore.Preferences", 1, 0, "IOPreferencesModel");
qmlRegisterType<CommonAudioApiConfigurationModel>("MuseScore.Preferences", 1, 0, "CommonAudioApiConfigurationModel");

#ifdef Q_OS_MACOS
qmlRegisterType<AppMenuModel>("MuseScore.AppShell", 1, 0, "AppMenuModel");
#if defined(Q_OS_MACOS)
qmlRegisterType<AppMenuModel>("MuseScore.AppShell", 1, 0, "PlatformAppMenuModel");
#elif defined(Q_OS_LINUX)
qmlRegisterType<AppMenuModel>("MuseScore.AppShell", 1, 0, "PlatformAppMenuModel");
qmlRegisterType<NavigableAppMenuModel>("MuseScore.AppShell", 1, 0, "AppMenuModel");
#else
qmlRegisterType<NavigableAppMenuModel>("MuseScore.AppShell", 1, 0, "AppMenuModel");
#endif
Expand Down
138 changes: 138 additions & 0 deletions src/appshell/qml/platform/PlatformMenuBar.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
* SPDX-License-Identifier: GPL-3.0-only
* MuseScore-CLA-applies
*
* MuseScore
* Music Composition & Notation
*
* Copyright (C) 2021 MuseScore BVBA and others
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* 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, see <https://www.gnu.org/licenses/>.
*/
import QtQuick 2.15
import Qt.labs.platform 1.1 as PLATFORM

import MuseScore.AppShell 1.0

Item {
// Loading subitems on Linux causes crashes when adding menus, but it works without it
property bool loadSubitems: true
readonly property bool available: menuModel.isGlobalMenuAvailable()

PLATFORM.MenuBar {
id: menuBar
}

PlatformAppMenuModel {
id: menuModel
}

function load() {
menuModel.load()

var items = menuModel.items
for (var i in items) {
var item = items[i]
var menu = makeMenu(item)

if (loadSubitems) {
for (var j in item.subitems) {
var menuItem = makeMenuItem(menu, item.subitems[j])
menu.addItem(menuItem)
}
}

item.subitemsChanged.connect(function(subitems, menuId) {
for (var l in menuBar.menus) {
var menu = menuBar.menus[l]
if (menu.id === menuId) {
menuBar.menus[l].subitems = subitems
}
}
})

menuBar.addMenu(menu)
}

menuModel.itemsChanged.connect(function() {
for (var i in menuModel.items) {
menuBar.menus[i].subitems = menuModel.items[i].subitems
}
})
}

function makeMenu(menuInfo) {
var menu = menuComponent.createObject(menuBar)

menu.id = menuInfo.id
menu.title = menuInfo.title
menu.enabled = menuInfo.enabled
menu.subitems = menuInfo.subitems

return menu
}

function makeMenuItem(parentMenu, itemInfo) {
var menuItem = menuItemComponent.createObject(parentMenu)

menuItem.id = itemInfo.id
menuItem.text = itemInfo.title + "\t" + itemInfo.portableShortcuts
menuItem.enabled = itemInfo.enabled
menuItem.checked = itemInfo.checked
menuItem.checkable = itemInfo.checkable
menuItem.separator = !Boolean(itemInfo.title)
menuItem.role = itemInfo.role

return menuItem
}

Component {
id: menuComponent

PLATFORM.Menu {
property string id: ""
property var subitems: []

onAboutToShow: {
clear()

for (var i in subitems) {
var item = subitems[i]
var isMenu = Boolean(item.subitems) && item.subitems.length > 0

if (isMenu) {
var subMenu = makeMenu(item)

addMenu(subMenu)
} else {
var menuItem = makeMenuItem(this, item)

addItem(menuItem)
}
}
}
}
}

Component {
id: menuItemComponent

PLATFORM.MenuItem {
property string id: ""

onTriggered: {
Qt.callLater(menuModel.handleMenuItem, id)
}
}
}
}
15 changes: 13 additions & 2 deletions src/appshell/qml/platform/linux/Main.qml
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,28 @@ import "../../"
AppWindow {
id: root

AppMenuBar {
Loader {
id: appMenuBar

anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
}

appWindow: root
Loader {
id: platformMenuBar
}

Component.onCompleted: {
platformMenuBar.setSource("../PlatformMenuBar.qml", { "loadSubitems": false });
if (platformMenuBar.item.available) {
platformMenuBar.item.load();
appMenuBar.active = 0;
} else {
appMenuBar.setSource("../AppMenuBar.qml", { "appWindow": root });
platformMenuBar.active = 0;
}

window.init()
}

Expand Down
104 changes: 3 additions & 101 deletions src/appshell/qml/platform/mac/Main.qml
Original file line number Diff line number Diff line change
Expand Up @@ -20,122 +20,24 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import QtQuick 2.15
import Qt.labs.platform 1.1 as PLATFORM

import MuseScore.AppShell 1.0

import "../"
import "../../"

AppWindow {
id: root

PLATFORM.MenuBar {
PlatformMenuBar {
id: menuBar
}

AppMenuModel {
id: menuModel
}

Component.onCompleted: {
menuModel.load()

var items = menuModel.items
for (var i in items) {
var item = items[i]
var menu = makeMenu(item)

for (var j in item.subitems) {
var menuItem = makeMenuItem(menu, item.subitems[j])
menu.addItem(menuItem)
}

item.subitemsChanged.connect(function(subitems, menuId) {
for (var l in menuBar.menus) {
var menu = menuBar.menus[l]
if (menu.id === menuId) {
menuBar.menus[l].subitems = subitems
}
}
})

menuBar.addMenu(menu)
}

menuModel.itemsChanged.connect(function() {
for (var i in menuModel.items) {
menuBar.menus[i].subitems = menuModel.items[i].subitems
}
})

menuBar.load();
window.init()
}

function makeMenu(menuInfo) {
var menu = menuComponent.createObject(menuBar)

menu.id = menuInfo.id
menu.title = menuInfo.title
menu.enabled = menuInfo.enabled
menu.subitems = menuInfo.subitems

return menu
}

function makeMenuItem(parentMenu, itemInfo) {
var menuItem = menuItemComponent.createObject(parentMenu)

menuItem.id = itemInfo.id
menuItem.text = itemInfo.title + "\t" + itemInfo.portableShortcuts
menuItem.enabled = itemInfo.enabled
menuItem.checked = itemInfo.checked
menuItem.checkable = itemInfo.checkable
menuItem.separator = !Boolean(itemInfo.title)
menuItem.role = itemInfo.role

return menuItem
}

Component {
id: menuComponent

PLATFORM.Menu {
property string id: ""
property var subitems: []

onAboutToShow: {
clear()

for (var i in subitems) {
var item = subitems[i]
var isMenu = Boolean(item.subitems) && item.subitems.length > 0

if (isMenu) {
var subMenu = makeMenu(item)

addMenu(subMenu)
} else {
var menuItem = makeMenuItem(this, item)

addItem(menuItem)
}
}
}
}
}

Component {
id: menuItemComponent

PLATFORM.MenuItem {
property string id: ""

onTriggered: {
Qt.callLater(menuModel.handleMenuItem, id)
}
}
}

WindowContent {
id: window

Expand Down
5 changes: 5 additions & 0 deletions src/appshell/view/appmenumodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ void AppMenuModel::load()
appMenuModelHook()->onAppMenuInited();
}

bool AppMenuModel::isGlobalMenuAvailable()
{
return uiConfiguration()->isGlobalMenuAvailable();
}

void AppMenuModel::setupConnections()
{
recentProjectsProvider()->recentProjectListChanged().onNotify(this, [this]() {
Expand Down
3 changes: 3 additions & 0 deletions src/appshell/view/appmenumodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "ui/imainwindow.h"
#include "ui/iuiactionsregister.h"
#include "ui/inavigationcontroller.h"
#include "ui/iuiconfiguration.h"
#include "actions/iactionsdispatcher.h"
#include "workspace/iworkspacemanager.h"
#include "iappshellconfiguration.h"
Expand All @@ -47,6 +48,7 @@ class AppMenuModel : public uicomponents::AbstractMenuModel
INJECT(appshell, ui::IMainWindow, mainWindow)
INJECT(appshell, ui::IUiActionsRegister, uiActionsRegister)
INJECT(appshell, ui::INavigationController, navigationController)
INJECT(notation, ui::IUiConfiguration, uiConfiguration)
INJECT(appshell, actions::IActionsDispatcher, actionsDispatcher)
INJECT(appshell, workspace::IWorkspaceManager, workspacesManager)
INJECT(appshell, IAppShellConfiguration, configuration)
Expand All @@ -59,6 +61,7 @@ class AppMenuModel : public uicomponents::AbstractMenuModel
explicit AppMenuModel(QObject* parent = nullptr);

Q_INVOKABLE void load() override;
Q_INVOKABLE bool isGlobalMenuAvailable();

private:
void setupConnections();
Expand Down
5 changes: 5 additions & 0 deletions src/framework/ui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ elseif(OS_IS_WIN)
${CMAKE_CURRENT_LIST_DIR}/internal/platform/windows/windowsplatformtheme.cpp
${CMAKE_CURRENT_LIST_DIR}/internal/platform/windows/windowsplatformtheme.h
)
elseif(OS_IS_LIN)
set(PLATFORM_THEME_SRC
${CMAKE_CURRENT_LIST_DIR}/internal/platform/linux/linuxplatformtheme.cpp
${CMAKE_CURRENT_LIST_DIR}/internal/platform/linux/linuxplatformtheme.h
)
else()
set(PLATFORM_THEME_SRC
${CMAKE_CURRENT_LIST_DIR}/internal/platform/stub/stubplatformtheme.cpp
Expand Down
2 changes: 2 additions & 0 deletions src/framework/ui/internal/iplatformtheme.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ class IPlatformTheme : MODULE_EXPORT_INTERFACE
virtual bool isSystemThemeDark() const = 0;
virtual async::Notification platformThemeChanged() const = 0;

virtual bool isGlobalMenuAvailable() const = 0;

virtual void applyPlatformStyleOnAppForTheme(const ThemeCode& themeCode) = 0;
virtual void applyPlatformStyleOnWindowForTheme(QWindow* window, const ThemeCode& themeCode) = 0;
};
Expand Down
Loading