Implemented UI of Learn page
This commit is contained in:
parent
98c7a77487
commit
ba2bb3e449
10 changed files with 569 additions and 9 deletions
|
@ -125,7 +125,9 @@ DockPage {
|
|||
Component {
|
||||
id: learnComp
|
||||
|
||||
LearnPage {}
|
||||
LearnPage {
|
||||
item: root.subItem
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
|
|
|
@ -27,6 +27,8 @@ set(MODULE_QML_IMPORT ${CMAKE_CURRENT_LIST_DIR}/qml)
|
|||
set(MODULE_SRC
|
||||
${CMAKE_CURRENT_LIST_DIR}/learnmodule.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/learnmodule.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/view/learnpagemodel.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/view/learnpagemodel.h
|
||||
)
|
||||
|
||||
include(${PROJECT_SOURCE_DIR}/build/module.cmake)
|
||||
|
|
|
@ -2,5 +2,7 @@
|
|||
<qresource prefix="/">
|
||||
<file>qml/MuseScore/Learn/LearnPage.qml</file>
|
||||
<file>qml/MuseScore/Learn/qmldir</file>
|
||||
<file>qml/MuseScore/Learn/internal/Playlist.qml</file>
|
||||
<file>qml/MuseScore/Learn/internal/PlaylistItem.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
|
||||
#include <QQmlEngine>
|
||||
|
||||
#include "view/learnpagemodel.h"
|
||||
|
||||
using namespace mu::learn;
|
||||
|
||||
static void learn_init_qrc()
|
||||
|
@ -39,3 +41,8 @@ void LearnModule::registerResources()
|
|||
{
|
||||
learn_init_qrc();
|
||||
}
|
||||
|
||||
void LearnModule::registerUiTypes()
|
||||
{
|
||||
qmlRegisterType<LearnPageModel>("MuseScore.Learn", 1, 0, "LearnPageModel");
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ class LearnModule : public modularity::IModuleSetup
|
|||
public:
|
||||
std::string moduleName() const override;
|
||||
void registerResources() override;
|
||||
void registerUiTypes() override;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,9 @@ import QtQuick.Layouts 1.3
|
|||
|
||||
import MuseScore.Ui 1.0
|
||||
import MuseScore.UiComponents 1.0
|
||||
import MuseScore.Learn 1.0
|
||||
|
||||
import "internal"
|
||||
|
||||
FocusScope {
|
||||
id: root
|
||||
|
@ -60,6 +63,14 @@ FocusScope {
|
|||
bar.selectPage(root.item)
|
||||
}
|
||||
|
||||
LearnPageModel {
|
||||
id: pageModel
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
pageModel.load()
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: root.color
|
||||
|
@ -106,6 +117,10 @@ FocusScope {
|
|||
navigation.panel: navSearchPanel
|
||||
navigation.order: 1
|
||||
accessible.name: qsTrc("learn", "Learn search")
|
||||
|
||||
onSearchTextChanged: {
|
||||
pageModel.setSearchText(searchText)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -193,25 +208,50 @@ FocusScope {
|
|||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 24
|
||||
|
||||
currentIndex: bar.currentIndex
|
||||
|
||||
Rectangle {
|
||||
Playlist {
|
||||
id: getStartedComp
|
||||
|
||||
color: root.color
|
||||
playlist: pageModel.startedPlaylist
|
||||
|
||||
// search: searchField.searchText
|
||||
// backgroundColor: root.color
|
||||
navigation.section: navSec
|
||||
navigation.order: 3
|
||||
navigation.name: "LearnGetStarted"
|
||||
navigation.accessible.name: qsTrc("learn", "Get Started") + navigation.directionInfo
|
||||
|
||||
sideMargin: prv.sideMargin
|
||||
|
||||
onRequestOpenVideo: {
|
||||
pageModel.openVideo(videoId)
|
||||
}
|
||||
|
||||
onRequestActiveFocus: {
|
||||
root.requestActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Playlist {
|
||||
id: advancedComp
|
||||
|
||||
color: root.color
|
||||
playlist: pageModel.advancedPlaylist
|
||||
|
||||
// search: searchField.searchText
|
||||
// backgroundColor: root.color
|
||||
navigation.section: navSec
|
||||
navigation.order: 4
|
||||
navigation.name: "LearnAdvanced"
|
||||
navigation.accessible.name: qsTrc("learn", "Advanced") + navigation.directionInfo
|
||||
|
||||
sideMargin: prv.sideMargin
|
||||
|
||||
onRequestOpenVideo: {
|
||||
pageModel.openVideo(videoId)
|
||||
}
|
||||
|
||||
onRequestActiveFocus: {
|
||||
root.requestActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
|
140
src/learn/qml/MuseScore/Learn/internal/Playlist.qml
Normal file
140
src/learn/qml/MuseScore/Learn/internal/Playlist.qml
Normal file
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* 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 QtQuick.Layouts 1.3
|
||||
import QtQuick.Controls 2.2
|
||||
|
||||
import MuseScore.Ui 1.0
|
||||
import MuseScore.UiComponents 1.0
|
||||
import MuseScore.Learn 1.0
|
||||
|
||||
FocusScope {
|
||||
id: root
|
||||
|
||||
property alias playlist: view.model
|
||||
|
||||
property alias navigation: navPanel
|
||||
property int sideMargin: 46
|
||||
|
||||
signal requestOpenVideo(string videoId)
|
||||
signal requestActiveFocus()
|
||||
|
||||
NavigationPanel {
|
||||
id: navPanel
|
||||
direction: NavigationPanel.Both
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: background
|
||||
anchors.fill: parent
|
||||
|
||||
color: ui.theme.backgroundSecondaryColor
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
root.requestActiveFocus()
|
||||
root.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.top: view.top
|
||||
|
||||
width: parent.width
|
||||
height: 8
|
||||
z: 1
|
||||
|
||||
gradient: Gradient {
|
||||
GradientStop {
|
||||
position: 0.0
|
||||
color: background.color
|
||||
}
|
||||
|
||||
GradientStop {
|
||||
position: 1.0
|
||||
color: "transparent"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GridView {
|
||||
id: view
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: root.sideMargin - itemMargin
|
||||
anchors.rightMargin: root.sideMargin - itemMargin
|
||||
anchors.topMargin: -itemMargin
|
||||
anchors.bottomMargin: -itemMargin
|
||||
|
||||
clip: true
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
|
||||
cellHeight: itemMargin + itemHeight + itemMargin
|
||||
cellWidth: itemMargin + itemWidth + itemMargin
|
||||
|
||||
property int itemMargin: 24
|
||||
property int itemWidth: 250
|
||||
property int itemHeight: 224
|
||||
|
||||
property int rows: Math.max(0, Math.floor(root.height / root.cellHeight))
|
||||
property int columns: Math.max(0, Math.floor(root.width / root.cellWidth))
|
||||
|
||||
ScrollBar.vertical: StyledScrollBar {
|
||||
parent: root
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 16
|
||||
|
||||
visible: view.contentHeight > view.height
|
||||
z: 1
|
||||
}
|
||||
|
||||
delegate: Item {
|
||||
height: view.cellHeight
|
||||
width: view.cellWidth
|
||||
|
||||
PlaylistItem {
|
||||
anchors.centerIn: parent
|
||||
|
||||
width: view.itemWidth
|
||||
height: view.itemHeight
|
||||
|
||||
navigation.panel: navPanel
|
||||
navigation.row: root.columns === 0 ? 0 : Math.floor(model.index / root.columns)
|
||||
navigation.column: model.index - (navigation.row * root.columns)
|
||||
|
||||
title: modelData.title
|
||||
author: modelData.author
|
||||
duration: modelData.duration
|
||||
thumbnail: modelData.thumbnailUrl
|
||||
|
||||
onClicked: {
|
||||
root.requestOpenVideo(modelData.videoId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
175
src/learn/qml/MuseScore/Learn/internal/PlaylistItem.qml
Normal file
175
src/learn/qml/MuseScore/Learn/internal/PlaylistItem.qml
Normal file
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
* 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 QtGraphicalEffects 1.0
|
||||
|
||||
import MuseScore.Ui 1.0
|
||||
import MuseScore.UiComponents 1.0
|
||||
import MuseScore.UserScores 1.0
|
||||
|
||||
FocusScope {
|
||||
id: root
|
||||
|
||||
property string title: ""
|
||||
property string author: ""
|
||||
property string duration: ""
|
||||
property alias thumbnail: thumbnailItem.source
|
||||
|
||||
property alias navigation: navCtrl
|
||||
|
||||
signal clicked()
|
||||
|
||||
NavigationControl {
|
||||
id: navCtrl
|
||||
name: root.title
|
||||
|
||||
accessible.role: MUAccessible.Button
|
||||
accessible.name: root.title
|
||||
|
||||
onActiveChanged: {
|
||||
if (active) {
|
||||
root.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
onTriggered: root.clicked()
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
|
||||
spacing: 8
|
||||
|
||||
Item {
|
||||
id: scoreRect
|
||||
|
||||
height: 150
|
||||
width: 250
|
||||
|
||||
opacity: 0.9
|
||||
|
||||
property int borderWidth: 0
|
||||
readonly property int radius: 3
|
||||
|
||||
Image {
|
||||
id: thumbnailItem
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.top: parent.top
|
||||
|
||||
height: parent.height + parent.borderWidth
|
||||
width: parent.width
|
||||
|
||||
color: "transparent"
|
||||
radius: parent.radius
|
||||
|
||||
border.color: navCtrl.active ? ui.theme.focusColor : ui.theme.strokeColor
|
||||
border.width: navCtrl.active ? 2 : parent.borderWidth
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "HOVERED"
|
||||
when: mouseArea.containsMouse && !mouseArea.pressed
|
||||
|
||||
PropertyChanges {
|
||||
target: scoreRect
|
||||
opacity: 1
|
||||
borderWidth: 1
|
||||
}
|
||||
},
|
||||
|
||||
State {
|
||||
name: "PRESSED"
|
||||
when: mouseArea.pressed
|
||||
|
||||
PropertyChanges {
|
||||
target: scoreRect
|
||||
opacity: 0.5
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
RectangularGlow {
|
||||
anchors.fill: scoreRect
|
||||
z: -1
|
||||
|
||||
glowRadius: 20
|
||||
color: "#08000000"
|
||||
cornerRadius: scoreRect.radius + glowRadius
|
||||
}
|
||||
}
|
||||
|
||||
StyledTextLabel {
|
||||
anchors.left: parent.left
|
||||
width: parent.width
|
||||
|
||||
text: root.title
|
||||
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
wrapMode: Text.WrapAnywhere
|
||||
maximumLineCount: 1
|
||||
|
||||
font: ui.theme.tabBoldFont
|
||||
}
|
||||
|
||||
StyledTextLabel {
|
||||
anchors.left: parent.left
|
||||
width: parent.width
|
||||
|
||||
text: root.author
|
||||
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
wrapMode: Text.WrapAnywhere
|
||||
maximumLineCount: 1
|
||||
|
||||
font: ui.theme.bodyBoldFont
|
||||
}
|
||||
|
||||
StyledTextLabel {
|
||||
anchors.left: parent.left
|
||||
width: parent.width
|
||||
|
||||
text: root.duration
|
||||
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
wrapMode: Text.WrapAnywhere
|
||||
maximumLineCount: 1
|
||||
|
||||
font: ui.theme.bodyFont
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
hoverEnabled: true
|
||||
|
||||
onClicked: {
|
||||
root.clicked()
|
||||
}
|
||||
}
|
||||
}
|
120
src/learn/view/learnpagemodel.cpp
Normal file
120
src/learn/view/learnpagemodel.cpp
Normal file
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* 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 "learnpagemodel.h"
|
||||
|
||||
#include <QVariant>
|
||||
|
||||
using namespace mu::learn;
|
||||
|
||||
LearnPageModel::LearnPageModel(QObject* parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
QVariantList LearnPageModel::startedPlaylist() const
|
||||
{
|
||||
Playlist filteredPlaylist = filterPlaylistBySearch(m_startedPlaylist);
|
||||
return playlistToVariantList(filteredPlaylist);
|
||||
}
|
||||
|
||||
QVariantList LearnPageModel::advancedPlaylist() const
|
||||
{
|
||||
Playlist filteredPlaylist = filterPlaylistBySearch(m_advancedPlaylist);
|
||||
return playlistToVariantList(filteredPlaylist);
|
||||
}
|
||||
|
||||
void LearnPageModel::load()
|
||||
{
|
||||
setStartedPlaylist(learnService()->startedPlaylist());
|
||||
setAdvancedPlaylist(learnService()->advancedPlaylist());
|
||||
}
|
||||
|
||||
void LearnPageModel::openVideo(const QString& videoId) const
|
||||
{
|
||||
learnService()->openVideo(videoId.toStdString());
|
||||
}
|
||||
|
||||
void LearnPageModel::setSearchText(const QString& text)
|
||||
{
|
||||
if (m_searchText == text) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_searchText = text;
|
||||
|
||||
emit startedPlaylistChanged();
|
||||
emit advancedPlaylistChanged();
|
||||
}
|
||||
|
||||
void LearnPageModel::setStartedPlaylist(Playlist startedPlaylist)
|
||||
{
|
||||
if (m_startedPlaylist == startedPlaylist) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_startedPlaylist = startedPlaylist;
|
||||
emit startedPlaylistChanged();
|
||||
}
|
||||
|
||||
void LearnPageModel::setAdvancedPlaylist(Playlist advancedPlaylist)
|
||||
{
|
||||
if (m_advancedPlaylist == advancedPlaylist) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_advancedPlaylist = advancedPlaylist;
|
||||
emit advancedPlaylistChanged();
|
||||
}
|
||||
|
||||
QVariantList LearnPageModel::playlistToVariantList(const Playlist& playlist) const
|
||||
{
|
||||
QVariantList result;
|
||||
|
||||
for (const PlaylistItem& item : playlist) {
|
||||
QVariantMap itemObj;
|
||||
itemObj["videoId"] = QString::fromStdString(item.videoId);
|
||||
itemObj["title"] = QString::fromStdString(item.title);
|
||||
itemObj["author"] =QString::fromStdString(item.author);
|
||||
itemObj["thumbnailUrl"] = QString::fromStdString(item.thumbnailUrl);
|
||||
itemObj["duration"] = item.durationSec; // todo
|
||||
|
||||
result << itemObj;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Playlist LearnPageModel::filterPlaylistBySearch(const Playlist& playlist) const
|
||||
{
|
||||
Playlist result;
|
||||
|
||||
for (const PlaylistItem& playlistItem : playlist) {
|
||||
QString title = QString::fromStdString(playlistItem.title);
|
||||
QString author = QString::fromStdString(playlistItem.author);
|
||||
if (title.contains(m_searchText, Qt::CaseInsensitive)
|
||||
|| author.contains(m_searchText, Qt::CaseInsensitive)) {
|
||||
result.push_back(playlistItem);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
71
src/learn/view/learnpagemodel.h
Normal file
71
src/learn/view/learnpagemodel.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
#ifndef MU_LEARN_LEARNPAGEMODEL_H
|
||||
#define MU_LEARN_LEARNPAGEMODEL_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "modularity/ioc.h"
|
||||
#include "ilearnservice.h"
|
||||
|
||||
namespace mu::learn {
|
||||
class LearnPageModel : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QVariantList startedPlaylist READ startedPlaylist NOTIFY startedPlaylistChanged)
|
||||
Q_PROPERTY(QVariantList advancedPlaylist READ advancedPlaylist NOTIFY advancedPlaylistChanged)
|
||||
|
||||
INJECT(learn, ILearnService, learnService)
|
||||
|
||||
public:
|
||||
explicit LearnPageModel(QObject* parent = nullptr);
|
||||
|
||||
QVariantList startedPlaylist() const;
|
||||
QVariantList advancedPlaylist() const;
|
||||
|
||||
Q_INVOKABLE void load();
|
||||
Q_INVOKABLE void openVideo(const QString& videoId) const;
|
||||
|
||||
Q_INVOKABLE void setSearchText(const QString& text);
|
||||
|
||||
private slots:
|
||||
void setStartedPlaylist(Playlist startedPlaylist);
|
||||
void setAdvancedPlaylist(Playlist advancedPlaylist);
|
||||
|
||||
signals:
|
||||
void startedPlaylistChanged();
|
||||
void advancedPlaylistChanged();
|
||||
|
||||
private:
|
||||
QVariantList playlistToVariantList(const Playlist& playlist) const;
|
||||
|
||||
Playlist filterPlaylistBySearch(const Playlist& playlist) const;
|
||||
|
||||
Playlist m_startedPlaylist;
|
||||
Playlist m_advancedPlaylist;
|
||||
|
||||
QString m_searchText;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // MU_LEARN_LEARNPAGEMODEL_H
|
Loading…
Reference in a new issue