Implemented UI of Learn page

This commit is contained in:
Eism 2021-07-01 12:23:02 +02:00 committed by Igor Korsukov
parent 98c7a77487
commit ba2bb3e449
10 changed files with 569 additions and 9 deletions

View file

@ -125,7 +125,9 @@ DockPage {
Component {
id: learnComp
LearnPage {}
LearnPage {
item: root.subItem
}
}
Component {

View file

@ -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)

View file

@ -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>

View file

@ -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");
}

View file

@ -30,6 +30,7 @@ class LearnModule : public modularity::IModuleSetup
public:
std::string moduleName() const override;
void registerResources() override;
void registerUiTypes() override;
};
}

View file

@ -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 {

View 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)
}
}
}
}
}

View 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()
}
}
}

View 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;
}

View 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