diff --git a/au4/src/appshell/qml/ProjectPage/ProjectPage.qml b/au4/src/appshell/qml/ProjectPage/ProjectPage.qml index 1ee9c98c8cc56a1d56fe4bda39d3ba4c0d16c40f..637fc97b96c54176aea76174199ef15a031e4cd5 100644 --- a/au4/src/appshell/qml/ProjectPage/ProjectPage.qml +++ b/au4/src/appshell/qml/ProjectPage/ProjectPage.qml @@ -148,7 +148,6 @@ DockPage { ] PlaybackToolBar { - orientation: playbackToolBar.orientation floating: playbackToolBar.floating maximumWidth: playbackToolBar.width diff --git a/au4/src/playback/CMakeLists.txt b/au4/src/playback/CMakeLists.txt index fa21a273bc4644274fbb16254b4564f0a09dbeae..72dc27b16f76495852803944c49542c7875a2978 100644 --- a/au4/src/playback/CMakeLists.txt +++ b/au4/src/playback/CMakeLists.txt @@ -22,10 +22,15 @@ set(MODULE_SRC ${CMAKE_CURRENT_LIST_DIR}/view/toolbars/playbacktoolbarmodel.cpp ${CMAKE_CURRENT_LIST_DIR}/view/toolbars/playbacktoolbarmodel.h - ${CMAKE_CURRENT_LIST_DIR}/view/toolbars/playbacktoolbarcustomisemodel.h + ${CMAKE_CURRENT_LIST_DIR}/view/toolbars/playbacktoolbarabstractitem.cpp + ${CMAKE_CURRENT_LIST_DIR}/view/toolbars/playbacktoolbarabstractitem.h + ${CMAKE_CURRENT_LIST_DIR}/view/toolbars/playbacktoolbarlevelitem.cpp + ${CMAKE_CURRENT_LIST_DIR}/view/toolbars/playbacktoolbarlevelitem.h + ${CMAKE_CURRENT_LIST_DIR}/view/toolbars/playbacktoolbarcustomisemodel.cpp - ${CMAKE_CURRENT_LIST_DIR}/view/toolbars/playbacktoolbarcustomiseitem.h + ${CMAKE_CURRENT_LIST_DIR}/view/toolbars/playbacktoolbarcustomisemodel.h ${CMAKE_CURRENT_LIST_DIR}/view/toolbars/playbacktoolbarcustomiseitem.cpp + ${CMAKE_CURRENT_LIST_DIR}/view/toolbars/playbacktoolbarcustomiseitem.h ) diff --git a/au4/src/playback/internal/playbackcontroller.cpp b/au4/src/playback/internal/playbackcontroller.cpp index 6a0ea48263052b385ba56752a534d06b0c51ef22..b32fedac43f0ef78aaee5cb0e436101193f3a188 100644 --- a/au4/src/playback/internal/playbackcontroller.cpp +++ b/au4/src/playback/internal/playbackcontroller.cpp @@ -13,7 +13,7 @@ using namespace muse::actions; static const ActionCode PLAY_CODE("play"); static const ActionCode STOP_CODE("stop"); -static const ActionCode REWIND_CODE("rewind"); +static const ActionCode REWIND_START_CODE("rewind-start"); static const ActionCode LOOP_CODE("loop"); static const ActionCode LOOP_IN_CODE("loop-in"); static const ActionCode LOOP_OUT_CODE("loop-out"); @@ -31,7 +31,7 @@ void PlaybackController::init() { dispatcher()->reg(this, PLAY_CODE, this, &PlaybackController::togglePlay); dispatcher()->reg(this, STOP_CODE, this, &PlaybackController::pause); - dispatcher()->reg(this, REWIND_CODE, this, &PlaybackController::rewind); + dispatcher()->reg(this, REWIND_START_CODE, this, &PlaybackController::rewindToStart); dispatcher()->reg(this, LOOP_CODE, this, &PlaybackController::toggleLoopPlayback); // dispatcher()->reg(this, LOOP_IN_CODE, [this]() { addLoopBoundary(LoopBoundaryType::LoopIn); }); // dispatcher()->reg(this, LOOP_OUT_CODE, [this]() { addLoopBoundary(LoopBoundaryType::LoopOut); }); @@ -187,7 +187,7 @@ void PlaybackController::play() setCurrentPlaybackStatus(PlaybackStatus::Running); } -void PlaybackController::rewind(const ActionData& args) +void PlaybackController::rewindToStart(const ActionData& args) { //! NOTE: In Audacity 3 we can't rewind while playing stop(); diff --git a/au4/src/playback/internal/playbackcontroller.h b/au4/src/playback/internal/playbackcontroller.h index c132abd12294cefa8fbbb5402256fb84c6792708..740b5612c2e40e4a0b2b1e2e294ed39e07e615e9 100644 --- a/au4/src/playback/internal/playbackcontroller.h +++ b/au4/src/playback/internal/playbackcontroller.h @@ -70,7 +70,7 @@ private: void seekRangeSelection(); void togglePlay(); - void rewind(const muse::actions::ActionData& args); + void rewindToStart(const muse::actions::ActionData& args); void play(); void pause(); void stop(); diff --git a/au4/src/playback/playbackmodule.cpp b/au4/src/playback/playbackmodule.cpp index 1a0c12ae529fb513c9969e1d921e3ff71f875ec5..92f49c7ed164b5215a4caaf631449edc8eb86e01 100644 --- a/au4/src/playback/playbackmodule.cpp +++ b/au4/src/playback/playbackmodule.cpp @@ -14,6 +14,7 @@ #include "internal/playbackuiactions.h" #include "view/toolbars/playbacktoolbarmodel.h" +#include "view/toolbars/playbacktoolbarabstractitem.h" #include "view/toolbars/playbacktoolbarcustomisemodel.h" #include "view/toolbars/playbacktoolbarcustomiseitem.h" @@ -58,6 +59,7 @@ void PlaybackModule::registerUiTypes() { // toolbars qmlRegisterType<PlaybackToolBarModel>("Audacity.Playback", 1, 0, "PlaybackToolBarModel"); + qmlRegisterUncreatableType<PlaybackToolBarAbstractItem>("Muse.Ui", 1, 0, "PlaybackToolBarItem", "Cannot create an PlaybackToolBarItem"); qmlRegisterType<PlaybackToolBarCustomiseModel>("Audacity.Playback", 1, 0, "PlaybackToolBarCustomiseModel"); qmlRegisterUncreatableType<PlaybackToolBarCustomiseItem>("Audacity.Playback", 1, 0, "PlaybackToolBarCustomiseItem", "Cannot create"); diff --git a/au4/src/playback/qml/Audacity/Playback/PlaybackToolBar.qml b/au4/src/playback/qml/Audacity/Playback/PlaybackToolBar.qml index bebe85ddcd268c323611357148748da280e36466..fac3914e542f274a489b753494343a33ead714d8 100644 --- a/au4/src/playback/qml/Audacity/Playback/PlaybackToolBar.qml +++ b/au4/src/playback/qml/Audacity/Playback/PlaybackToolBar.qml @@ -13,15 +13,13 @@ import "internal" Item { id: root - property alias orientation: gridView.orientation - property bool floating: false property int maximumWidth: 0 property int maximumHeight: 0 - width: gridView.isHorizontal ? childrenRect.width : 76 - height: !gridView.isHorizontal ? childrenRect.height : 40 + width: gridView.width + /*spacing*/ 4 + customizeButton.width + height: gridView.height property NavigationPanel navigationPanel: NavigationPanel { name: "PlaybackToolBar" @@ -33,178 +31,202 @@ Item { id: toolbarModel } - QtObject { - id: prv - - function resolveHorizontalGridViewWidth() { - if (root.floating) { - return gridView.contentWidth - } - - var requiredFreeSpace = gridView.cellWidth * 3 + gridView.rowSpacing * 4 - - if (root.maximumWidth - gridView.contentWidth < requiredFreeSpace) { - return gridView.contentWidth - requiredFreeSpace - } - - return gridView.contentWidth - } - - function resolveVerticalGridViewHeight() { - if (root.floating) { - return gridView.contentHeight - } - - var requiredFreeSpace = gridView.cellHeight * 3 + gridView.rowSpacing * 4 - - if (root.maximumHeight - gridView.contentHeight < requiredFreeSpace) { - return gridView.contentHeight - requiredFreeSpace - } - - return gridView.contentHeight - } - } - Component.onCompleted: { toolbarModel.load() } - GridViewSectional { + Flow { id: gridView - sectionRole: "section" - - rowSpacing: 4 - columnSpacing: 4 - - cellWidth: 32 - cellHeight: cellWidth - clip: true - model: toolbarModel + spacing: 4 - sectionDelegate: SeparatorLine { - orientation: gridView.orientation - visible: itemIndex !== 0 - } - - itemDelegate: FlatButton { - id: btn - - property var item: Boolean(itemModel) ? itemModel.itemRole : null - property var hasMenu: Boolean(item) && item.subitems.length !== 0 + Repeater { + model: toolbarModel - width: gridView.cellWidth - height: gridView.cellWidth + Loader { + id: loader - accentButton: (Boolean(item) && item.checked) || menuLoader.isMenuOpened - transparent: !accentButton + property var itemData: Boolean(model) ? model.item : null + property var itemOrder: Boolean(model) ? model.order : 0 + property var itemIsMenuSecondary: Boolean(model) ? model.isMenuSecondary : false - icon: Boolean(item) ? item.icon : IconCode.NONE - iconFont: ui.theme.toolbarIconsFont - - toolTipTitle: Boolean(item) ? item.title : "" - toolTipDescription: Boolean(item) ? item.description : "" - toolTipShortcut: Boolean(item) ? item.shortcuts : "" - - navigation.panel: root.navigationPanel - navigation.name: Boolean(item) ? item.id : "" - navigation.order: Boolean(itemModel) ? itemModel.order : 0 - isClickOnKeyNavTriggered: false - navigation.onTriggered: { - if (menuLoader.isMenuOpened || hasMenu) { - toggleMenuOpened() - } else { - handleMenuItem() - } - } - - mouseArea.acceptedButtons: hasMenu && itemModel.isMenuSecondary - ? Qt.LeftButton | Qt.RightButton - : Qt.LeftButton - - function toggleMenuOpened() { - menuLoader.toggleOpened(item.subitems) - } - - function handleMenuItem() { - Qt.callLater(toolbarModel.handleMenuItem, item.id) - } + property var itemSize: { + if (!Boolean(loader.itemData)) { + return null + } - onClicked: function(mouse) { - if (menuLoader.isMenuOpened // If already menu open, close it - || (hasMenu // Or if can open menu - && (!itemModel.isMenuSecondary // And _should_ open menu - || mouse.button === Qt.RightButton))) { - toggleMenuOpened() - return - } + switch(loader.itemData.type) { + case PlaybackToolBarItem.SECTION: return Qt.size(1, 32) + case PlaybackToolBarItem.ACTION: return Qt.size(32, 32) + case PlaybackToolBarItem.PLAYBACK_LEVEL: return Qt.size(128, 32) + } - if (mouse.button === Qt.LeftButton) { - handleMenuItem() + return null } - } - Connections { - target: btn.mouseArea + width: Boolean(item) ? item.width : 0 + height: 32 - // Make sure we only connect to `pressAndHold` if necessary - // See https://github.com/musescore/MuseScore/issues/16012 - enabled: btn.hasMenu && !menuLoader.isMenuOpened + sourceComponent: { + if (!Boolean(loader.itemData)) { + return null + } - function onPressAndHold() { - if (menuLoader.isMenuOpened || !btn.hasMenu) { - return + switch(loader.itemData.type) { + case PlaybackToolBarItem.SECTION: return sectionComp + case PlaybackToolBarItem.ACTION: return actionComp + case PlaybackToolBarItem.PLAYBACK_LEVEL: return playbackLevelComp } - btn.toggleMenuOpened() + return null } - } - Canvas { - visible: Boolean(itemModel) && itemModel.isMenuSecondary + Component { + id: sectionComp - property color fillColor: ui.theme.fontPrimaryColor - onFillColorChanged: { - requestPaint() + SeparatorLine { + orientation: Qt.Vertical + } } - width: 4 - height: 4 - - anchors.margins: 2 - anchors.right: parent.right - anchors.bottom: parent.bottom - - onPaint: { - const ctx = getContext("2d"); - ctx.fillStyle = fillColor; - ctx.moveTo(width, 0); - ctx.lineTo(width, height); - ctx.lineTo(0, height); - ctx.closePath(); - ctx.fill(); + Component { + id: actionComp + + FlatButton { + id: btn + + property var item: loader.itemData + property int order: loader.itemOrder + property var isMenuSecondary: loader.itemIsMenuSecondary + + property var hasMenu: Boolean(item) && item.subitems.length !== 0 + + width: 32 + height: width + + accentButton: item.checked || menuLoader.isMenuOpened + transparent: !accentButton + + icon: item.icon + iconFont: ui.theme.toolbarIconsFont + + toolTipTitle: item.title + toolTipDescription: item.description + toolTipShortcut: item.shortcuts + + navigation.panel: root.navigationPanel + navigation.name: item.id + navigation.order: order + isClickOnKeyNavTriggered: false + navigation.onTriggered: { + if (menuLoader.isMenuOpened || hasMenu) { + toggleMenuOpened() + } else { + handleMenuItem() + } + } + + mouseArea.acceptedButtons: hasMenu && isMenuSecondary + ? Qt.LeftButton | Qt.RightButton + : Qt.LeftButton + + function toggleMenuOpened() { + menuLoader.toggleOpened(item.subitems) + } + + function handleMenuItem() { + Qt.callLater(toolbarModel.handleMenuItem, item.id) + } + + onClicked: function(mouse) { + if (menuLoader.isMenuOpened // If already menu open, close it + || (hasMenu // Or if can open menu + && (!isMenuSecondary // And _should_ open menu + || mouse.button === Qt.RightButton))) { + toggleMenuOpened() + return + } + + if (mouse.button === Qt.LeftButton) { + handleMenuItem() + } + } + + Connections { + target: btn.mouseArea + + // Make sure we only connect to `pressAndHold` if necessary + // See https://github.com/musescore/MuseScore/issues/16012 + enabled: btn.hasMenu && !menuLoader.isMenuOpened + + function onPressAndHold() { + if (menuLoader.isMenuOpened || !btn.hasMenu) { + return + } + + btn.toggleMenuOpened() + } + } + + Canvas { + visible: isMenuSecondary + + property color fillColor: ui.theme.fontPrimaryColor + onFillColorChanged: { + requestPaint() + } + + width: 4 + height: 4 + + anchors.margins: 2 + anchors.right: parent.right + anchors.bottom: parent.bottom + + onPaint: { + const ctx = getContext("2d"); + ctx.fillStyle = fillColor; + ctx.moveTo(width, 0); + ctx.lineTo(width, height); + ctx.lineTo(0, height); + ctx.closePath(); + ctx.fill(); + } + } + + StyledMenuLoader { + id: menuLoader + + onHandleMenuItem: function(itemId) { + toolbarModel.handleMenuItem(itemId) + } + } + } } - } - StyledMenuLoader { - id: menuLoader + Component { + id: playbackLevelComp - onHandleMenuItem: function(itemId) { - toolbarModel.handleMenuItem(itemId) + PlaybackLevel { + width: 128 + } } } } + } FlatButton { id: customizeButton anchors.margins: 4 + anchors.left: gridView.right + anchors.verticalCenter: root.verticalCenter - width: gridView.cellWidth - height: gridView.cellHeight + width: 32 + height: width icon: IconCode.SETTINGS_COG iconFont: ui.theme.toolbarIconsFont @@ -226,45 +248,4 @@ Item { anchorItem: !root.floating ? ui.rootItem : null } } - - states: [ - State { - when: gridView.isHorizontal - - PropertyChanges { - target: gridView - width: prv.resolveHorizontalGridViewWidth() - height: root.height - sectionWidth: 1 - sectionHeight: root.height - rows: 1 - columns: gridView.noLimit - } - - AnchorChanges { - target: customizeButton - anchors.left: gridView.right - anchors.verticalCenter: root.verticalCenter - } - }, - State { - when: !gridView.isHorizontal - - PropertyChanges { - target: gridView - width: root.width - height: prv.resolveVerticalGridViewHeight() - sectionWidth: root.width - sectionHeight: 1 - rows: gridView.noLimit - columns: 2 - } - - AnchorChanges { - target: customizeButton - anchors.top: gridView.bottom - anchors.right: parent.right - } - } - ] } diff --git a/au4/src/playback/view/toolbars/playbacktoolbarabstractitem.cpp b/au4/src/playback/view/toolbars/playbacktoolbarabstractitem.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0981491528bee15f84da10bd8f4061989fbb3182 --- /dev/null +++ b/au4/src/playback/view/toolbars/playbacktoolbarabstractitem.cpp @@ -0,0 +1,32 @@ +/* +* Audacity: A Digital Audio Editor +*/ +#include "playbacktoolbarabstractitem.h" + +using namespace au::playback; +using namespace muse::uicomponents; + +PlaybackToolBarAbstractItem::PlaybackToolBarAbstractItem(const muse::ui::UiAction& action, const ItemType& type, QObject* parent) + : muse::uicomponents::MenuItem(action, parent), m_type(type) +{ +} + +PlaybackToolBarAbstractItem::ItemType PlaybackToolBarAbstractItem::type() const +{ + return m_type; +} + +void PlaybackToolBarAbstractItem::setType(ItemType type) +{ + if (m_type == type) { + return; + } + + m_type = type; + emit typeChanged(); +} + +int PlaybackToolBarAbstractItem::type_property() const +{ + return static_cast<int>(m_type); +} diff --git a/au4/src/playback/view/toolbars/playbacktoolbarabstractitem.h b/au4/src/playback/view/toolbars/playbacktoolbarabstractitem.h new file mode 100644 index 0000000000000000000000000000000000000000..a9e9dafb9016e7611f3e7efff5e1d3bb3bf0bfe6 --- /dev/null +++ b/au4/src/playback/view/toolbars/playbacktoolbarabstractitem.h @@ -0,0 +1,42 @@ +/* +* Audacity: A Digital Audio Editor +*/ +#ifndef AU_PLAYBACK_PLAYBACKTOOLBARABSTRACTITEM_H +#define AU_PLAYBACK_PLAYBACKTOOLBARABSTRACTITEM_H + +#include <QString> + +#include "uicomponents/view/menuitem.h" + +namespace au::playback { +class PlaybackToolBarAbstractItem : public muse::uicomponents::MenuItem +{ + Q_OBJECT + + Q_PROPERTY(int type READ type_property NOTIFY typeChanged FINAL) + +public: + enum ItemType { + UNDEFINED = -1, + SECTION, + ACTION, + PLAYBACK_LEVEL + }; + Q_ENUM(ItemType) + + explicit PlaybackToolBarAbstractItem(const muse::ui::UiAction& action, const ItemType& type, QObject* parent = nullptr); + + ItemType type() const; + void setType(ItemType type); + +signals: + void typeChanged(); + +private: + int type_property() const; + + ItemType m_type = ItemType::UNDEFINED; +}; +} + +#endif // AU_PLAYBACK_PLAYBACKTOOLBARABSTRACTITEM_H diff --git a/au4/src/playback/view/toolbars/playbacktoolbaractionitem.cpp b/au4/src/playback/view/toolbars/playbacktoolbaractionitem.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d379457a6a0363afe263f893b14a430c21431577 --- /dev/null +++ b/au4/src/playback/view/toolbars/playbacktoolbaractionitem.cpp @@ -0,0 +1,72 @@ +/* +* Audacity: A Digital Audio Editor +*/ +#include "playbacktoolbarcustomiseitem.h" + +using namespace au::playback; +using namespace muse::uicomponents; + +PlaybackToolBarCustomiseItem::PlaybackToolBarCustomiseItem(const ItemType& type, QObject* parent) + : SelectableItemListModel::Item(parent), m_type(type) +{ +} + +QString PlaybackToolBarCustomiseItem::id() const +{ + return m_id; +} + +QString PlaybackToolBarCustomiseItem::title() const +{ + return m_title; +} + +PlaybackToolBarCustomiseItem::ItemType PlaybackToolBarCustomiseItem::type() const +{ + return m_type; +} + +void PlaybackToolBarCustomiseItem::setTitle(QString title) +{ + if (m_title == title) { + return; + } + + m_title = title; + emit titleChanged(); +} + +void PlaybackToolBarCustomiseItem::setId(const QString& id) +{ + m_id = id; +} + +int PlaybackToolBarCustomiseItem::icon() const +{ + return static_cast<int>(m_icon); +} + +bool PlaybackToolBarCustomiseItem::checked() const +{ + return m_checked; +} + +void PlaybackToolBarCustomiseItem::setIcon(muse::ui::IconCode::Code icon) +{ + if (m_icon == icon) { + return; + } + + m_icon = icon; + emit iconChanged(); +} + +void PlaybackToolBarCustomiseItem::setChecked(bool checked) +{ + if (m_checked == checked) { + return; + } + + m_checked = checked; + emit checkedChanged(m_checked); +} diff --git a/au4/src/playback/view/toolbars/playbacktoolbaractionitem.h b/au4/src/playback/view/toolbars/playbacktoolbaractionitem.h new file mode 100644 index 0000000000000000000000000000000000000000..b7146785527e0de6dfbda7430d76d048e8f1f655 --- /dev/null +++ b/au4/src/playback/view/toolbars/playbacktoolbaractionitem.h @@ -0,0 +1,60 @@ +/* +* Audacity: A Digital Audio Editor +*/ +#ifndef AU_PROJECTSCENE_PLAYBACKTOOLBARCUSTOMISEITEM_H +#define AU_PROJECTSCENE_PLAYBACKTOOLBARCUSTOMISEITEM_H + +#include <QString> + +#include "ui/view/iconcodes.h" +#include "uicomponents/view/selectableitemlistmodel.h" + +namespace au::playback { +class PlaybackToolBarCustomiseItem : public muse::uicomponents::SelectableItemListModel::Item +{ + Q_OBJECT + + Q_PROPERTY(ItemType type READ type CONSTANT) + Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged) + Q_PROPERTY(int icon READ icon NOTIFY iconChanged) + Q_PROPERTY(bool checked READ checked WRITE setChecked NOTIFY checkedChanged) + +public: + enum ItemType { + UNDEFINED = -1, + ACTION, + SEPARATOR + }; + Q_ENUM(ItemType) + + explicit PlaybackToolBarCustomiseItem(const ItemType& type, QObject* parent = nullptr); + + ItemType type() const; + QString title() const; + int icon() const; + bool checked() const; + + Q_INVOKABLE QString id() const; + void setId(const QString& id); + +public slots: + void setTitle(QString title); + void setIcon(muse::ui::IconCode::Code icon); + void setChecked(bool checked); + +signals: + void titleChanged(); + void iconChanged(); + void checkedChanged(bool checked); + +private: + + QString m_id; + ItemType m_type = ItemType::UNDEFINED; + QString m_title; + muse::ui::IconCode::Code m_icon = muse::ui::IconCode::Code::NONE; + bool m_checked = false; +}; +} + +#endif // AU_PROJECTSCENE_PLAYBACKTOOLBARCUSTOMISEITEM_H diff --git a/au4/src/playback/view/toolbars/playbacktoolbarlevelitem.cpp b/au4/src/playback/view/toolbars/playbacktoolbarlevelitem.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3ae7102a222dc351787ed08f8e1dcc6cf4df61d4 --- /dev/null +++ b/au4/src/playback/view/toolbars/playbacktoolbarlevelitem.cpp @@ -0,0 +1,46 @@ +/* + * 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/>. + */ +#include "playbacktoolbarlevelitem.h" + +#include <QVariantMap> + +using namespace au::playback; + +PlaybackToolBarLevelItem::PlaybackToolBarLevelItem(const muse::ui::UiAction& action, const ItemType& type, QObject* parent) + : PlaybackToolBarAbstractItem(action, type, parent) +{ +} + +int PlaybackToolBarLevelItem::level() const +{ + return m_level; +} + +void PlaybackToolBarLevelItem::setLevel(int newLevel) +{ + if (m_level == newLevel) { + return; + } + + m_level = newLevel; + emit levelChanged(); +} diff --git a/au4/src/playback/view/toolbars/playbacktoolbarlevelitem.h b/au4/src/playback/view/toolbars/playbacktoolbarlevelitem.h new file mode 100644 index 0000000000000000000000000000000000000000..8fe2453a809b7cd97963705985f7f4249b768f42 --- /dev/null +++ b/au4/src/playback/view/toolbars/playbacktoolbarlevelitem.h @@ -0,0 +1,32 @@ +/* +* Audacity: A Digital Audio Editor +*/ +#ifndef AU_PLAYBACK_PLAYBACKTOOLBARLEVELITEM_H +#define AU_PLAYBACK_PLAYBACKTOOLBARLEVELITEM_H + +#include <QString> + +#include "playbacktoolbarabstractitem.h" + +namespace au::playback { +class PlaybackToolBarLevelItem : public PlaybackToolBarAbstractItem +{ + Q_OBJECT + + Q_PROPERTY(int level READ level WRITE setLevel NOTIFY levelChanged FINAL) + +public: + explicit PlaybackToolBarLevelItem(const muse::ui::UiAction& action, const ItemType& type, QObject* parent = nullptr); + + int level() const; + void setLevel(int newLevel); + +signals: + void levelChanged(); + +private: + int m_level = 0; +}; +} + +#endif // AU_PLAYBACK_PLAYBACKTOOLBARLEVELITEM_H diff --git a/au4/src/playback/view/toolbars/playbacktoolbarmodel.cpp b/au4/src/playback/view/toolbars/playbacktoolbarmodel.cpp index 8447a4430c53b7fdaea7742ed4f8b2db88348aba..a4a2d43383c78aaeb48d7dbfadb8e9b3fe4a1260 100644 --- a/au4/src/playback/view/toolbars/playbacktoolbarmodel.cpp +++ b/au4/src/playback/view/toolbars/playbacktoolbarmodel.cpp @@ -5,7 +5,7 @@ #include "internal/playbackuiactions.h" -#include "log.h" +#include "view/toolbars/playbacktoolbarabstractitem.h" using namespace muse::uicomponents; using namespace muse::ui; @@ -14,10 +14,29 @@ using namespace au::playback; static const QString TOOLBAR_NAME("playbackToolBar"); +static const ActionCode SECTION_CODE(""); static const ActionCode PLAY_ACTION_CODE("play"); +static const ActionCode PLAYBACK_LEVEL("playback-level"); + +static PlaybackToolBarAbstractItem::ItemType itemType(const ActionCode& actionCode) +{ + if (actionCode == SECTION_CODE) { + return PlaybackToolBarAbstractItem::ItemType::SECTION; + } + + if (actionCode == PLAYBACK_LEVEL) { + return PlaybackToolBarAbstractItem::ItemType::PLAYBACK_LEVEL; + } + + return PlaybackToolBarAbstractItem::ItemType::ACTION; +} PlaybackToolBarModel::PlaybackToolBarModel(QObject* parent) - : AbstractMenuModel(parent) + : QAbstractListModel(parent) +{ +} + +void PlaybackToolBarModel::load() { uiConfiguration()->toolConfigChanged(TOOLBAR_NAME).onNotify(this, [this]() { load(); @@ -26,37 +45,59 @@ PlaybackToolBarModel::PlaybackToolBarModel(QObject* parent) context()->currentProjectChanged().onNotify(this, [this]() { onProjectChanged(); }); -} -void PlaybackToolBarModel::load() -{ - AbstractMenuModel::load(); updateActions(); setupConnections(); } +void PlaybackToolBarModel::handleMenuItem(const QString& itemId) +{ + PlaybackToolBarAbstractItem* item = findItem(itemId.toStdString()); + dispatcher()->dispatch(item->action().code, item->args()); +} + +QVariantMap PlaybackToolBarModel::get(int index) +{ + QVariantMap result; + + QHash<int, QByteArray> names = roleNames(); + QHashIterator<int, QByteArray> i(names); + while (i.hasNext()) { + i.next(); + QModelIndex idx = this->index(index, 0); + QVariant data = idx.data(i.key()); + result[i.value()] = data; + } + + return result; +} + QVariant PlaybackToolBarModel::data(const QModelIndex& index, int role) const { TRACEFUNC; int row = index.row(); - if (!isIndexValid(row)) { + if (!index.isValid() || row >= rowCount()) { return QVariant(); } - const MenuItem* item = items()[row]; + const PlaybackToolBarAbstractItem* item = m_items[row]; + const ActionCode actionCode = item->action().code; + switch (role) { - case IsMenuSecondaryRole: return isMenuSecondary(item->action().code); + case ItemRole: return QVariant::fromValue(item); + case IsMenuSecondaryRole: return isMenuSecondary(actionCode); case OrderRole: return row; case SectionRole: return item->section(); - default: return AbstractMenuModel::data(index, role); + default: return QVariant(); } } QHash<int, QByteArray> PlaybackToolBarModel::roleNames() const { - QHash<int, QByteArray> roles = AbstractMenuModel::roleNames(); + QHash<int, QByteArray> roles; + roles[ItemRole] = "item"; roles[IsMenuSecondaryRole] = "isMenuSecondary"; roles[OrderRole] = "order"; roles[SectionRole] = "section"; @@ -64,13 +105,27 @@ QHash<int, QByteArray> PlaybackToolBarModel::roleNames() const return roles; } -void PlaybackToolBarModel::onActionsStateChanges(const muse::actions::ActionCodeList& codes) +int PlaybackToolBarModel::rowCount(const QModelIndex&) const +{ + return m_items.size(); +} + +PlaybackToolBarAbstractItem* PlaybackToolBarModel::findItem(const muse::actions::ActionCode& actionCode) { - AbstractMenuModel::onActionsStateChanges(codes); + for (PlaybackToolBarAbstractItem* item : m_items) { + if (item->action().code == actionCode) { + return item; + } + } + + return nullptr; +} +void PlaybackToolBarModel::onActionsStateChanges(const muse::actions::ActionCodeList& codes) +{ if (containsAction(codes, PLAY_ACTION_CODE)) { - MenuItem& item = findItem(PLAY_ACTION_CODE); - item.setAction(playAction()); + PlaybackToolBarAbstractItem* item = findItem(PLAY_ACTION_CODE); + item->setAction(playAction()); } } @@ -83,12 +138,13 @@ void PlaybackToolBarModel::setupConnections() void PlaybackToolBarModel::onProjectChanged() { - updateState(); } void PlaybackToolBarModel::updateActions() { - MenuItemList items; + m_items.clear(); + + beginResetModel(); muse::ui::ToolConfig playbackConfig = uiConfiguration()->toolConfig(TOOLBAR_NAME, PlaybackUiActions::defaultPlaybackToolConfig()); @@ -99,31 +155,18 @@ void PlaybackToolBarModel::updateActions() continue; } - if (citem.action.empty()) { - section++; - continue; - } - - MenuItem* item = makeActionItem(uiActionsRegister()->action(citem.action), QString::number(section)); + PlaybackToolBarAbstractItem* item = makeItem(uiActionsRegister()->action(citem.action), QString::number(section)); if (citem.action == PLAY_ACTION_CODE) { item->setAction(playAction()); } - items << item; + m_items << item; } - setItems(items); -} + endResetModel(); -void PlaybackToolBarModel::updateState() -{ - for (int i = 0; i < rowCount(); ++i) { - MenuItem& item = this->item(i); - muse::ui::UiActionState state = item.state(); - state.checked = false; - item.setState(state); - } + emit itemsChanged(); } bool PlaybackToolBarModel::isMenuSecondary(const muse::actions::ActionCode& actionCode) const @@ -133,12 +176,10 @@ bool PlaybackToolBarModel::isMenuSecondary(const muse::actions::ActionCode& acti return false; } -MenuItem* PlaybackToolBarModel::makeActionItem(const muse::ui::UiAction& action, const QString& section, - const muse::uicomponents::MenuItemList& subitems) +PlaybackToolBarAbstractItem* PlaybackToolBarModel::makeItem(const muse::ui::UiAction& action, const QString& section) { - MenuItem* item = new MenuItem(action, this); + PlaybackToolBarAbstractItem* item = new PlaybackToolBarAbstractItem(action, itemType(action.code), this); item->setSection(section); - item->setSubitems(subitems); return item; } diff --git a/au4/src/playback/view/toolbars/playbacktoolbarmodel.h b/au4/src/playback/view/toolbars/playbacktoolbarmodel.h index 8da212a22a4da83e74b4ef2d4c560e66e4b78de1..b3a7a12e3d2c3d05373ded945ec9df499a0dd1a8 100644 --- a/au4/src/playback/view/toolbars/playbacktoolbarmodel.h +++ b/au4/src/playback/view/toolbars/playbacktoolbarmodel.h @@ -5,54 +5,70 @@ #define AU_PROJECTSCENE_PLAYBACKTOOLBARMODEL_H #include <QHash> +#include <QAbstractListModel> + +#include "async/asyncable.h" #include "modularity/ioc.h" #include "context/iglobalcontext.h" #include "ui/iuiactionsregister.h" #include "ui/iuiconfiguration.h" +#include "actions/iactionsdispatcher.h" #include "playback/iplaybackcontroller.h" -#include "uicomponents/view/abstractmenumodel.h" - namespace au::playback { -class PlaybackToolBarModel : public muse::uicomponents::AbstractMenuModel +class PlaybackToolBarAbstractItem; +class PlaybackToolBarModel : public QAbstractListModel, public muse::async::Asyncable { Q_OBJECT - muse::Inject<au::context::IGlobalContext> context; muse::Inject<muse::ui::IUiActionsRegister> actionsRegister; muse::Inject<muse::ui::IUiConfiguration> uiConfiguration; + muse::Inject<muse::ui::IUiActionsRegister> uiActionsRegister; + muse::Inject<muse::actions::IActionsDispatcher> dispatcher; + muse::Inject<au::context::IGlobalContext> context; muse::Inject<au::playback::IPlaybackController> controller; + Q_PROPERTY(int length READ rowCount NOTIFY itemsChanged) + public: explicit PlaybackToolBarModel(QObject* parent = nullptr); - Q_INVOKABLE void load() override; + Q_INVOKABLE void load(); + Q_INVOKABLE void handleMenuItem(const QString& itemId); + Q_INVOKABLE QVariantMap get(int index); QVariant data(const QModelIndex& index, int role) const override; QHash<int, QByteArray> roleNames() const override; + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + +signals: + void itemsChanged(); private: enum InputRoles { - IsMenuSecondaryRole = AbstractMenuModel::Roles::UserRole + 1, + ItemRole = Qt::UserRole + 1, + IsMenuSecondaryRole, OrderRole, - SectionRole + SectionRole, }; - void onActionsStateChanges(const muse::actions::ActionCodeList& codes) override; + PlaybackToolBarAbstractItem* findItem(const muse::actions::ActionCode& actionCode); + + void onActionsStateChanges(const muse::actions::ActionCodeList& codes); void setupConnections(); void onProjectChanged(); void updateActions(); - void updateState(); bool isMenuSecondary(const muse::actions::ActionCode& actionCode) const; - muse::uicomponents::MenuItem* makeActionItem(const muse::ui::UiAction& action, const QString& section, - const muse::uicomponents::MenuItemList& subitems = {}); + PlaybackToolBarAbstractItem* makeItem(const muse::ui::UiAction& action, const QString& section); muse::ui::UiAction playAction() const; + + QList<PlaybackToolBarAbstractItem*> m_items; }; } diff --git a/au4/src/projectscene/internal/projectsceneuiactions.cpp b/au4/src/projectscene/internal/projectsceneuiactions.cpp index 619f5136e08443616efa5d9078399e6b335365e9..8daaecc45e3349bbf17ffe6ef1c6082dc458a089 100644 --- a/au4/src/projectscene/internal/projectsceneuiactions.cpp +++ b/au4/src/projectscene/internal/projectsceneuiactions.cpp @@ -28,7 +28,7 @@ const UiActionList ProjectSceneUiActions::m_actions = { au::context::CTX_ANY, TranslatableString("action", "Zoom"), TranslatableString("action", "Zoom"), - IconCode::Code::ZOOM + IconCode::Code::ZOOM_TOGGLE ), UiAction("zoomin", au::context::UiCtxNotationOpened,