implemented template preview

fix codestyle

codereview fixes
This commit is contained in:
Roman Pudashkin 2020-08-13 11:49:37 +02:00 committed by pereverzev+v
parent d0899506df
commit 1033d4b514
30 changed files with 590 additions and 159 deletions

View file

@ -98,7 +98,7 @@ mu::io::path mu::io::basename(const mu::io::path& path)
mu::io::path mu::io::dirname(const mu::io::path& path)
{
return QDir(path.toQString()).dirName();
return QFileInfo(path.toQString()).dir().dirName();
}
mu::io::path mu::io::escapeFileName(const mu::io::path& fn_)

View file

@ -20,7 +20,7 @@
set(MODULE_TEST system_test)
set(MODULE_TEST_SRC
${CMAKE_CURRENT_LIST_DIR}/mocks/fsoperationsmock.h)
${CMAKE_CURRENT_LIST_DIR}/mocks/filesystemmock.h)
set(MODULE_TEST_LINK system)

View file

@ -16,8 +16,8 @@
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#ifndef MU_FRAMEWORK_FSOPERATIONSMOCK_H
#define MU_FRAMEWORK_FSOPERATIONSMOCK_H
#ifndef MU_FRAMEWORK_FILESYSTEMMOCK_H
#define MU_FRAMEWORK_FILESYSTEMMOCK_H
#include <gmock/gmock.h>
@ -25,23 +25,19 @@
namespace mu {
namespace framework {
class FsOperationsMock : public IFsOperations
class FileSystemMock : public IFileSystem
{
public:
MOCK_METHOD(Ret, exists, (const QString&), (const, override));
MOCK_METHOD(Ret, remove, (const QString&), (const, override));
MOCK_METHOD(Ret, exists, (const io::path&), (const, override));
MOCK_METHOD(Ret, remove, (const io::path&), (const, override));
MOCK_METHOD(QString, fileName, (const QString&), (const, override));
MOCK_METHOD(QString, baseName, (const QString&), (const, override));
MOCK_METHOD(QString, dirName, (const QString&), (const, override));
MOCK_METHOD(RetVal<QByteArray>, readFile, (const io::path&), (const, override));
MOCK_METHOD(RetVal<QByteArray>, readFile, (const QString&), (const, override));
MOCK_METHOD(Ret, makePath, (const io::path&), (const, override));
MOCK_METHOD(Ret, makePath, (const QString&), (const, override));
MOCK_METHOD(RetVal<QStringList>, scanFiles, (const QString&, const QStringList&, ScanMode), (const, override));
MOCK_METHOD(RetVal<io::paths>, scanFiles, (const io::path&, const QStringList&, ScanMode), (const, override));
};
}
}
#endif // MU_FRAMEWORK_FSOPERATIONSMOCK_H
#endif // MU_FRAMEWORK_FILESYSTEMMOCK_H

View file

@ -13,10 +13,10 @@ Rectangle {
name: "HORIZONTAL"
when: orientation == Qt.Horizontal
PropertyChanges {
target: anchors
left: parent.left
right: parent.right
AnchorChanges {
target: root
anchors.left: parent.left
anchors.right: parent.right
}
PropertyChanges {
@ -29,10 +29,10 @@ Rectangle {
name: "VERTICAL"
when: orientation == Qt.Vertical
PropertyChanges {
target: anchors
top: parent.top
bottom: parent.bottom
AnchorChanges {
target: root
anchors.top: parent.top
anchors.bottom: parent.bottom
}
PropertyChanges {

View file

@ -25,9 +25,8 @@ Rectangle {
RowLayout {
anchors.fill: parent
anchors.margins: 16
spacing: 16
spacing: 12
FamilyView {
id: familyView
@ -114,7 +113,7 @@ Rectangle {
Layout.preferredWidth: 30
anchors.verticalCenter: parent.verticalCenter
spacing: 4
spacing: 12
FlatButton {
enabled: selectedInstrumentsView.canLiftInstrument

View file

@ -48,7 +48,7 @@ Item {
id: familiesBox
anchors.top: familyLabel.bottom
anchors.topMargin: 20
anchors.topMargin: 16
anchors.left: parent.left
anchors.right: parent.right

View file

@ -43,7 +43,8 @@ public:
virtual Meta metaInfo() const = 0;
virtual void setViewSize(const QSizeF& vs) = 0;
virtual void paint(QPainter* p, const QRect& r) = 0;
virtual void paint(QPainter* painter) = 0;
virtual QRectF previewRect() const = 0;
// input (mouse)
virtual INotationInteraction* interaction() const = 0;

View file

@ -46,6 +46,8 @@ RetVal<Meta> MsczMetaReader::readMeta(const io::path& filePath) const
meta.val.fileName = fileInfo.baseName();
}
meta.val.filePath = fileInfo.absoluteFilePath();
return meta;
}

View file

@ -120,37 +120,48 @@ void Notation::setViewSize(const QSizeF& vs)
m_viewSize = vs;
}
void Notation::paint(QPainter* p, const QRect&)
QRectF Notation::previewRect() const
{
const QList<Ms::Page*>& pages = m_score->pages();
if (pages.isEmpty()) {
p->drawText(10, 10, "no pages");
return QRect();
}
return pages.first()->bbox();
}
void Notation::paint(QPainter* painter)
{
const QList<Ms::Page*>& mspages = m_score->pages();
if (mspages.isEmpty()) {
painter->drawText(10, 10, "no pages");
return;
}
Ms::Page* page = pages.first();
page->draw(p);
Ms::Page* page = mspages.first();
p->fillRect(page->bbox(), QColor("#ffffff"));
page->draw(painter);
QList<Ms::Element*> ell = page->elements();
for (const Ms::Element* e : ell) {
if (!e->visible()) {
painter->fillRect(page->bbox(), QColor("#ffffff"));
for (Ms::Element* element : page->elements()) {
if (!element->visible()) {
continue;
}
e->itemDiscovered = false;
QPointF pos(e->pagePos());
element->itemDiscovered = false;
QPointF pos(element->pagePos());
p->translate(pos);
painter->translate(pos);
e->draw(p);
element->draw(painter);
p->translate(-pos);
painter->translate(-pos);
}
m_interaction->paint(p);
m_interaction->paint(painter);
}
void Notation::notifyAboutNotationChanged()

View file

@ -44,7 +44,8 @@ public:
Meta metaInfo() const override;
void setViewSize(const QSizeF& vs) override;
void paint(QPainter* p, const QRect& r) override;
void paint(QPainter* painter) override;
QRectF previewRect() const override;
INotationInteraction* interaction() const override;

View file

@ -64,6 +64,7 @@ enum class MoveSelectionType {
struct Meta {
QString fileName;
QString filePath;
QString title;
QString subtitle;
QString composer;

View file

@ -181,7 +181,7 @@ void NotationPaintView::paint(QPainter* p)
p->setTransform(m_matrix);
if (m_notation) {
m_notation->paint(p, rect);
m_notation->paint(p);
m_playbackCursor->paint(p);
} else {

View file

@ -37,6 +37,8 @@ set(MODULE_SRC
${CMAKE_CURRENT_LIST_DIR}/view/scorethumbnail.h
${CMAKE_CURRENT_LIST_DIR}/view/templatesmodel.cpp
${CMAKE_CURRENT_LIST_DIR}/view/templatesmodel.h
${CMAKE_CURRENT_LIST_DIR}/view/templatepaintview.cpp
${CMAKE_CURRENT_LIST_DIR}/view/templatepaintview.h
${CMAKE_CURRENT_LIST_DIR}/internal/openscorecontroller.cpp
${CMAKE_CURRENT_LIST_DIR}/internal/openscorecontroller.h
${CMAKE_CURRENT_LIST_DIR}/internal/userscoresconfiguration.cpp

View file

@ -38,6 +38,7 @@ RetVal<Templates> TemplatesRepository::templates() const
if (!files.ret) {
LOGE() << files.ret.toString();
continue;
}
result << loadTemplates(files.val);

View file

@ -39,7 +39,7 @@ public:
RetVal<Templates> templates() const override;
private:
Templates loadTemplates(const io::paths &filePaths) const;
Templates loadTemplates(const io::paths& filePaths) const;
QString correctedTitle(const QString& title) const;
};
}

View file

@ -24,6 +24,7 @@
using namespace mu;
using namespace mu::framework;
using namespace mu::userscores;
using namespace mu::notation;
static std::string module_name("userscores");
@ -76,3 +77,13 @@ io::paths UserScoresConfiguration::templatesDirPaths() const
return dirs;
}
QColor UserScoresConfiguration::templatePreviewBackgroundColor() const
{
return notationConfiguration()->backgroundColor();
}
async::Channel<QColor> UserScoresConfiguration::templatePreviewBackgroundColorChanged() const
{
return notationConfiguration()->backgroundColorChanged();
}

View file

@ -23,6 +23,7 @@
#include "modularity/ioc.h"
#include "iglobalconfiguration.h"
#include "extensions/iextensionsconfiguration.h"
#include "notation/inotationconfiguration.h"
namespace mu {
namespace userscores {
@ -30,6 +31,7 @@ class UserScoresConfiguration : public IUserScoresConfiguration
{
INJECT(usescores, framework::IGlobalConfiguration, globalConfiguration)
INJECT(userscores, extensions::IExtensionsConfiguration, extensionsConfiguration)
INJECT(userscores, notation::INotationConfiguration, notationConfiguration)
public:
void init();
@ -39,6 +41,9 @@ public:
io::paths templatesDirPaths() const override;
QColor templatePreviewBackgroundColor() const override;
async::Channel<QColor> templatePreviewBackgroundColorChanged() const override;
private:
QStringList parseRecentList(const std::string& recents) const;

View file

@ -20,6 +20,7 @@
#define MU_USERSCORES_IUSERSCORESCONFIGURATION_H
#include <QStringList>
#include <QColor>
#include "modularity/imoduleexport.h"
#include "retval.h"
@ -38,6 +39,9 @@ public:
virtual void setRecentScoreList(const QStringList& recentScoreList) = 0;
virtual io::paths templatesDirPaths() const = 0;
virtual QColor templatePreviewBackgroundColor() const = 0;
virtual async::Channel<QColor> templatePreviewBackgroundColorChanged() const = 0;
};
}
}

View file

@ -6,8 +6,14 @@ import MuseScore.Ui 1.0
import MuseScore.UserScores 1.0
Item {
property alias selectedTemplatePath: model.currentTemplatePath
TemplatesModel {
id: model
onCurrentTemplateChanged: {
templatePreview.load(model.currentTemplatePath)
}
}
Component.onCompleted: {
@ -15,10 +21,11 @@ Item {
}
RowLayout {
anchors.fill: parent
anchors.margins: 16
id: row
spacing: 16
anchors.fill: parent
spacing: 12
TitleListView {
Layout.fillHeight: true
@ -27,7 +34,7 @@ Item {
listTitle: qsTrc("userscores", "Category")
model: model.categoriesTitles
searchEnabled: false
booldFont: true
boldFont: true
onTitleClicked: {
model.setCurrentCategory(index)
@ -52,31 +59,39 @@ Item {
}
}
SeparatorLine { orientation: Qt.Vertical }
Item { Layout.preferredWidth: 30 }
SeparatorLine { orientation: Qt.Vertical; Layout.rightMargin: 30 + row.spacing }
SeparatorLine { orientation: Qt.Vertical }
TemplatePreview {
id: templatePreview
Layout.fillHeight: true
Layout.preferredWidth: parent.width / 3
Layout.fillWidth: true
}
SeparatorLine { orientation: Qt.Vertical }
Column {
Layout.minimumWidth: 30
Layout.preferredWidth: 30
Layout.alignment: Qt.AlignVCenter
spacing: 16
spacing: 12
FlatButton {
icon: IconCode.ZOOM_IN
onClicked: {
templatePreview.zoomIn()
}
}
FlatButton {
icon: IconCode.ZOOM_OUT
onClicked: {
templatePreview.zoomOut()
}
}
}
}

View file

@ -30,7 +30,7 @@ QmlDialog {
anchors.right: parent.right
anchors.rightMargin: 16
anchors.bottom: buttons.top
anchors.bottomMargin: 10
anchors.bottomMargin: 39
ChooseInstrumentsAndTemplatesPage {
anchors.fill: parent

View file

@ -1,4 +1,5 @@
import QtQuick 2.7
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import MuseScore.UiComponents 1.0
@ -7,6 +8,18 @@ import MuseScore.UserScores 1.0
Item {
id: root
function load(templatePath) {
paintView.load(templatePath)
}
function zoomIn() {
paintView.zoomIn()
}
function zoomOut() {
paintView.zoomOut()
}
StyledTextLabel {
id: title
@ -16,13 +29,53 @@ Item {
font.bold: true
}
Rectangle {
Item {
anchors.top: title.bottom
anchors.topMargin: 16
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
width: parent.width
TemplatePaintView {
id: paintView
color: "blue"
anchors.fill: parent
}
ScrollBar {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
z: 1
orientation: Qt.Vertical
policy: ScrollBar.AlwaysOn
position: paintView.startVerticalScrollPosition
size: paintView.verticalScrollSize
onPositionChanged: {
if (pressed) {
paintView.scrollVertical(position)
}
}
}
ScrollBar {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
z: 1
orientation: Qt.Horizontal
policy: ScrollBar.AlwaysOn
position: paintView.startHorizontalScrollPosition
size: paintView.horizontalScrollSize
onPositionChanged: {
if (pressed) {
paintView.scrollHorizontal(position)
}
}
}
}
}

View file

@ -10,7 +10,7 @@ Item {
property alias searchEnabled: searchField.visible
property alias searchText: searchField.searchText
property bool booldFont: false
property bool boldFont: false
signal titleClicked(var index)
@ -44,6 +44,8 @@ Item {
boundsBehavior: ListView.StopAtBounds
clip: true
currentIndex: 0
delegate: Item {
width: parent.width
height: 30
@ -64,7 +66,7 @@ Item {
horizontalAlignment: Text.AlignLeft
text: modelData
font.bold: root.booldFont
font.bold: root.boldFont
}
MouseArea {

View file

@ -31,7 +31,10 @@ public:
MOCK_METHOD(ValCh<QStringList>, recentScoreList, (), (const, override));
MOCK_METHOD(void, setRecentScoreList, (const QStringList&), (override));
MOCK_METHOD(QStringList, templatesDirPaths, (), (const, override));
MOCK_METHOD(io::paths, templatesDirPaths, (), (const, override));
MOCK_METHOD(QColor, templatePreviewBackgroundColor, (), (const, override));
MOCK_METHOD(async::Channel<QColor>, templatePreviewBackgroundColorChanged, (), (const, override));
};
}
}

View file

@ -21,15 +21,15 @@
#include "userscores/internal/templatesrepository.h"
#include "domain/notation/tests/mocks/msczreadermock.h"
#include "notation/tests/mocks/msczreadermock.h"
#include "mocks/userscoresconfigurationmock.h"
#include "system/tests/mocks/fsoperationsmock.h"
#include "system/tests/mocks/filesystemmock.h"
using ::testing::_;
using ::testing::Return;
using namespace mu;
using namespace mu::domain::notation;
using namespace mu::notation;
using namespace mu::userscores;
using namespace mu::framework;
@ -40,12 +40,12 @@ protected:
{
m_repository = std::make_shared<TemplatesRepository>();
m_msczReader = std::make_shared<MsczReaderMock>();
m_fsOperations = std::make_shared<FsOperationsMock>();
m_fileSystem = std::make_shared<FileSystemMock>();
m_configuration = std::make_shared<UserScoresConfigurationMock>();
m_repository->setconfiguration(m_configuration);
m_repository->setmsczReader(m_msczReader);
m_repository->setfsOperations(m_fsOperations);
m_repository->setfileSystem(m_fileSystem);
}
Meta createMeta(const QString& title) const
@ -61,111 +61,73 @@ protected:
std::shared_ptr<TemplatesRepository> m_repository;
std::shared_ptr<UserScoresConfigurationMock> m_configuration;
std::shared_ptr<MsczReaderMock> m_msczReader;
std::shared_ptr<FsOperationsMock> m_fsOperations;
std::shared_ptr<FileSystemMock> m_fileSystem;
};
namespace mu {
namespace userscores {
}
namespace domain {
namespace notation {
bool operator==(const Meta& meta1, const Meta& meta2)
bool operator==(const Template& templ1, const Template& templ2)
{
bool equals = true;
equals &= (meta1.title == meta2.title);
equals &= (meta1.creationDate == meta2.creationDate);
equals &= (templ1.title == templ2.title);
equals &= (templ1.categoryTitle == templ2.categoryTitle);
equals &= (templ1.creationDate == templ2.creationDate);
return equals;
}
}
}
}
/*
TEST_F(TemplatesRepositoryTest, Categories)
TEST_F(TemplatesRepositoryTest, Templates)
{
// [GIVEN] All paths to mscz files dirs
QStringList templatesDirPaths {
"/path/to/templates/AAAA",
"/path/to/templates/01-some_category_name",
"/path/to/templates/99#another_category_name",
"/path/to/empty/dir"
// [GIVEN] All paths to templates dirs
io::paths templatesDirPaths {
"/path/to/templates",
"/path/to/user/templates"
"/extensions/templates"
};
ON_CALL(*m_configuration, templatesDirPaths())
.WillByDefault(Return(templatesDirPaths));
EXPECT_CALL(*m_configuration, templatesDirPaths())
.WillOnce(Return(templatesDirPaths));
ON_CALL(*m_fsOperations, dirName(templatesDirPaths[0]))
.WillByDefault(Return("AAAA"));
// [GIVEN] All paths to mscz files
io::paths allPathsToMsczFiles;
ON_CALL(*m_fsOperations, dirName(templatesDirPaths[1]))
.WillByDefault(Return("01-some_category_name"));
for (size_t i = 0; i < templatesDirPaths.size(); ++i) {
io::path dirPath = templatesDirPaths[i];
io::path filePath = dirPath + QString("/file%1.mscz").arg(i);
allPathsToMsczFiles.push_back(filePath);
ON_CALL(*m_fsOperations, dirName(templatesDirPaths[2]))
.WillByDefault(Return("99#another_category_name"));
QStringList filters = { "*.mscz", "*.mscx" };
// [GIVEN] Some dirs have MSCZ files
QStringList filters = { "*.mscz", "*.mscx" };
for (int i = 0; i < 3; ++i) {
ON_CALL(*m_fsOperations, scanFiles(templatesDirPaths[i], filters, IFsOperations::ScanMode::IncludeSubdirs))
.WillByDefault(Return(RetVal<QStringList>::make_ok(QStringList { "/some/path/to/file.mscz" })));
RetVal<io::paths> result = RetVal<io::paths>::make_ok(io::paths { filePath });
ON_CALL(*m_fileSystem, scanFiles(dirPath, filters, IFileSystem::ScanMode::IncludeSubdirs))
.WillByDefault(Return(result));
}
// [WHEN] Get templates categories
RetVal<TemplateCategoryList> categories = m_repository->categories();
// [THEN] Successfully got categories, empty dir was skipped
EXPECT_TRUE(categories.ret);
TemplateCategoryList expectedCategories;
expectedCategories << createCategory("AAAA", "/path/to/templates/AAAA")
<< createCategory("some category name", "/path/to/templates/01-some_category_name")
<< createCategory("another category name", "/path/to/templates/99#another_category_name");
EXPECT_EQ(categories.val.size(), expectedCategories.size());
for (const TemplateCategory& category: categories.val) {
EXPECT_TRUE(expectedCategories.contains(category));
}
}
TEST_F(TemplatesRepositoryTest, TemplatesMeta)
{
// [GIVEN] Category codeKey
QString codeKey = "/path/to/templates";
// [GIVEN] Category templates
QStringList pathsToMsczFiles;
for (int i = 0; i < 5; ++i) {
QString filePath = codeKey + QString("/file%1.mscz").arg(i);
pathsToMsczFiles << filePath;
}
QStringList filters = { "*.mscz", "*.mscx" };
ON_CALL(*m_fsOperations, scanFiles(codeKey, filters, IFsOperations::ScanMode::IncludeSubdirs))
.WillByDefault(Return(RetVal<QStringList>::make_ok(pathsToMsczFiles)));
// [GIVEN] Templates meta
MetaList expectedMetaList;
Templates expectedTemplates;
for (const QString& path: pathsToMsczFiles) {
Meta meta = createMeta(path);
expectedMetaList << meta;
for (const io::path& path: allPathsToMsczFiles) {
Meta meta = createMeta(path.toQString());
ON_CALL(*m_msczReader, readMeta(io::pathFromQString(path)))
ON_CALL(*m_msczReader, readMeta(path))
.WillByDefault(Return(RetVal<Meta>::make_ok(meta)));
Template templ(meta);
templ.categoryTitle = io::dirname(path).toQString();
expectedTemplates << templ;
}
// [WHEN] Get templates meta
RetVal<MetaList> metaList = m_repository->templatesMeta(codeKey);
RetVal<Templates> templates = m_repository->templates();
// [THEN] Successfully got templates meta
EXPECT_TRUE(metaList.ret);
EXPECT_TRUE(templates.ret);
EXPECT_EQ(metaList.val.size(), expectedMetaList.size());
for (const Meta& meta: metaList.val) {
EXPECT_TRUE(expectedMetaList.contains(meta));
EXPECT_EQ(templates.val.size(), expectedTemplates.size());
for (const Template& templ: templates.val) {
EXPECT_TRUE(expectedTemplates.contains(templ));
}
}*/
}

View file

@ -27,6 +27,7 @@
#include "view/newscoremodel.h"
#include "view/scorethumbnail.h"
#include "view/templatesmodel.h"
#include "view/templatepaintview.h"
#include "internal/openscorecontroller.h"
#include "internal/userscoresconfiguration.h"
#include "internal/templatesrepository.h"
@ -75,6 +76,7 @@ void UserScoresModule::registerUiTypes()
qmlRegisterType<NewScoreModel>("MuseScore.UserScores", 1, 0, "NewScoreModel");
qmlRegisterType<ScoreThumbnail>("MuseScore.UserScores", 1, 0, "ScoreThumbnail");
qmlRegisterType<TemplatesModel>("MuseScore.UserScores", 1, 0, "TemplatesModel");
qmlRegisterType<TemplatePaintView>("MuseScore.UserScores", 1, 0, "TemplatePaintView");
framework::ioc()->resolve<framework::IUiEngine>(moduleName())->addSourceImportPath(userscores_QML_IMPORT);
}

View file

@ -30,8 +30,8 @@ struct Template : public notation::Meta {
QString categoryTitle;
Template() = default;
Template(const notation::Meta& meta) : Meta(meta) {}
Template(const notation::Meta& meta)
: Meta(meta) {}
};
using Templates = QList<Template>;

View file

@ -0,0 +1,239 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 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 2.
//
// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#include "templatepaintview.h"
#include "log.h"
#include "io/path.h"
using namespace mu::userscores;
using namespace mu::notation;
//! NOTE: experimental values
static constexpr qreal INITIAL_SCALE_FACTOR = 0.05;
static constexpr qreal SCALE_FACTOR_STEP = 0.01;
static constexpr qreal MIN_SCROLL_SIZE = 0.1;
static constexpr qreal MAX_SCROLL_SIZE = 1.0;
static constexpr qreal MID_SCROLL_POSITION = (MAX_SCROLL_SIZE - MIN_SCROLL_SIZE) / 2.0;
TemplatePaintView::TemplatePaintView(QQuickItem* parent)
: QQuickPaintedItem(parent), m_currentScaleFactor(INITIAL_SCALE_FACTOR)
{
setAntialiasing(true);
m_backgroundColor = configuration()->templatePreviewBackgroundColor();
configuration()->templatePreviewBackgroundColorChanged().onReceive(this, [this](const QColor& color) {
m_backgroundColor = color;
update();
});
connect(this, &QQuickPaintedItem::widthChanged, this, &TemplatePaintView::onViewSizeChanged);
connect(this, &QQuickPaintedItem::heightChanged, this, &TemplatePaintView::onViewSizeChanged);
}
qreal TemplatePaintView::startHorizontalScrollPosition() const
{
if (horizontalScrollSize() == MIN_SCROLL_SIZE) {
return MID_SCROLL_POSITION;
}
return MAX_SCROLL_SIZE - horizontalScrollableAreaSize();
}
qreal TemplatePaintView::horizontalScrollSize() const
{
qreal area = horizontalScrollableAreaSize();
if (qFuzzyIsNull(area)) {
return 0;
}
qreal size = area - (MAX_SCROLL_SIZE - area);
size = std::max(size, MIN_SCROLL_SIZE);
return size;
}
qreal TemplatePaintView::horizontalScrollableAreaSize() const
{
if (canvasWidth() <= width() || !canvasScaled()) {
return 0;
}
return width() / canvasWidth();
}
qreal TemplatePaintView::startVerticalScrollPosition() const
{
if (verticalScrollSize() == MIN_SCROLL_SIZE) {
return MID_SCROLL_POSITION;
}
return MAX_SCROLL_SIZE - verticalScrollableAreaSize();
}
qreal TemplatePaintView::verticalScrollSize() const
{
qreal area = verticalScrollableAreaSize();
if (qFuzzyIsNull(area)) {
return 0;
}
qreal size = area - (MAX_SCROLL_SIZE - area);
size = std::max(size, MIN_SCROLL_SIZE);
return size;
}
qreal TemplatePaintView::verticalScrollableAreaSize() const
{
if (canvasHeight() <= height() || !canvasScaled()) {
return 0;
}
return height() / canvasHeight();
}
void TemplatePaintView::load(const QString& templatePath)
{
if (m_templatePath == templatePath) {
return;
}
m_templatePath = templatePath;
m_notation = notationCreator()->newMasterNotation();
Ret ret = m_notation->load(m_templatePath);
if (!ret) {
LOGE() << ret.toString();
}
scaleCanvas(INITIAL_SCALE_FACTOR);
}
void TemplatePaintView::moveCanvasToCenter()
{
m_dx = (width() - canvasWidth()) / 2.;
m_dy = (height() - canvasHeight()) / 2.;
m_previousHorizontalScrollPosition = 0;
m_previousVerticalScrollPosition = 0;
update();
}
void TemplatePaintView::onViewSizeChanged()
{
moveCanvasToCenter();
}
void TemplatePaintView::zoomIn()
{
scaleCanvas(m_currentScaleFactor + SCALE_FACTOR_STEP);
}
void TemplatePaintView::zoomOut()
{
scaleCanvas(m_currentScaleFactor - SCALE_FACTOR_STEP);
}
void TemplatePaintView::scaleCanvas(qreal scaleFactor)
{
if (scaleFactor < INITIAL_SCALE_FACTOR) {
scaleFactor = INITIAL_SCALE_FACTOR;
}
m_currentScaleFactor = scaleFactor;
moveCanvasToCenter();
emit horizontalScrollChanged();
emit verticalScrollChanged();
}
bool TemplatePaintView::canvasScaled() const
{
return !qFuzzyCompare(m_currentScaleFactor, INITIAL_SCALE_FACTOR);
}
void TemplatePaintView::scrollHorizontal(qreal position)
{
if (position == m_previousHorizontalScrollPosition) {
return;
}
if (qFuzzyIsNull(m_previousHorizontalScrollPosition)) {
m_previousHorizontalScrollPosition = position;
return;
}
qreal scrollStep = position - m_previousHorizontalScrollPosition;
m_dx -= canvasWidth() * scrollStep;
m_previousHorizontalScrollPosition = position;
update();
}
void TemplatePaintView::scrollVertical(qreal position)
{
if (position == m_previousVerticalScrollPosition) {
return;
}
if (qFuzzyIsNull(m_previousVerticalScrollPosition)) {
m_previousVerticalScrollPosition = position;
return;
}
qreal scrollStep = position - m_previousVerticalScrollPosition;
m_dy -= canvasHeight() * scrollStep;
m_previousVerticalScrollPosition = position;
update();
}
qreal TemplatePaintView::canvasWidth() const
{
if (m_notation) {
return m_notation->previewRect().width() * m_currentScaleFactor;
}
return 0;
}
qreal TemplatePaintView::canvasHeight() const
{
if (m_notation) {
return m_notation->previewRect().height() * m_currentScaleFactor;
}
return 0;
}
void TemplatePaintView::paint(QPainter* painter)
{
QRect rect(0, 0, width(), height());
painter->fillRect(rect, m_backgroundColor);
painter->translate(m_dx, m_dy);
painter->scale(m_currentScaleFactor, m_currentScaleFactor);
if (m_notation) {
m_notation->paint(painter);
}
}

View file

@ -0,0 +1,96 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2020 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 2.
//
// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
#ifndef MU_USERSCORES_TEMPLATEPAINTVIEW_H
#define MU_USERSCORES_TEMPLATEPAINTVIEW_H
#include <QQuickPaintedItem>
#include "modularity/ioc.h"
#include "notation/inotationcreator.h"
#include "notation/imasternotation.h"
#include "iuserscoresconfiguration.h"
#include "async/asyncable.h"
namespace mu {
namespace userscores {
class TemplatePaintView : public QQuickPaintedItem, public async::Asyncable
{
Q_OBJECT
INJECT(userscores, notation::INotationCreator, notationCreator)
INJECT(userscores, IUserScoresConfiguration, configuration)
Q_PROPERTY(qreal startHorizontalScrollPosition READ startHorizontalScrollPosition NOTIFY horizontalScrollChanged)
Q_PROPERTY(qreal horizontalScrollSize READ horizontalScrollSize NOTIFY horizontalScrollChanged)
Q_PROPERTY(qreal startVerticalScrollPosition READ startVerticalScrollPosition NOTIFY verticalScrollChanged)
Q_PROPERTY(qreal verticalScrollSize READ verticalScrollSize NOTIFY verticalScrollChanged)
public:
explicit TemplatePaintView(QQuickItem* parent = nullptr);
qreal startHorizontalScrollPosition() const;
qreal horizontalScrollSize() const;
qreal startVerticalScrollPosition() const;
qreal verticalScrollSize() const;
Q_INVOKABLE void load(const QString& templatePath);
Q_INVOKABLE void zoomIn();
Q_INVOKABLE void zoomOut();
Q_INVOKABLE void scrollHorizontal(qreal position);
Q_INVOKABLE void scrollVertical(qreal position);
signals:
void horizontalScrollChanged();
void verticalScrollChanged();
private:
qreal horizontalScrollableAreaSize() const;
qreal verticalScrollableAreaSize() const;
void paint(QPainter* painter) override;
qreal canvasWidth() const;
qreal canvasHeight() const;
void moveCanvasToCenter();
void scaleCanvas(qreal scaleFactor);
bool canvasScaled() const;
private slots:
void onViewSizeChanged();
private:
QString m_templatePath;
notation::IMasterNotationPtr m_notation;
QColor m_backgroundColor;
qreal m_previousVerticalScrollPosition = 0;
qreal m_previousHorizontalScrollPosition = 0;
qreal m_currentScaleFactor = 0;
qreal m_dx = 0;
qreal m_dy = 0;
};
}
}
#endif // MU_USERSCORES_TEMPLATEPAINTVIEW_H

View file

@ -8,7 +8,6 @@ using namespace mu::notation;
TemplatesModel::TemplatesModel(QObject* parent)
: QObject(parent)
{
}
void TemplatesModel::load()
@ -27,11 +26,7 @@ void TemplatesModel::load()
emit categoriesChanged();
emit templatesChanged();
}
void TemplatesModel::apply()
{
NOT_IMPLEMENTED;
emit currentTemplateChanged();
}
QStringList TemplatesModel::categoriesTitles() const
@ -39,16 +34,21 @@ QStringList TemplatesModel::categoriesTitles() const
return m_visibleCategoriesTitles.toList();
}
QString TemplatesModel::currentTemplatePath() const
{
if (m_visibleTemplates.isEmpty()) {
return QString();
}
return m_visibleTemplates[m_currentTemplateIndex].filePath;
}
QStringList TemplatesModel::templatesTitles() const
{
QStringList titles;
for (const Template& templ: m_visibleTemplates) {
bool acceptedByCategory = templ.categoryTitle == categoriesTitles()[m_currentCategoryIndex];
if (acceptedByCategory) {
titles << templ.title;
}
titles << templ.title;
}
return titles;
@ -61,7 +61,24 @@ void TemplatesModel::setCurrentCategory(int index)
}
m_currentCategoryIndex = index;
updateTemplatesByCategory();
}
void TemplatesModel::updateTemplatesByCategory()
{
m_visibleTemplates.clear();
m_currentTemplateIndex = 0;
QString currentCategoryTitle = categoriesTitles()[m_currentCategoryIndex];
for (const Template& templ: m_allTemplates) {
if (templ.categoryTitle == currentCategoryTitle) {
m_visibleTemplates << templ;
}
}
emit templatesChanged();
emit currentTemplateChanged();
}
void TemplatesModel::setCurrentTemplate(int index)
@ -71,6 +88,7 @@ void TemplatesModel::setCurrentTemplate(int index)
}
m_currentTemplateIndex = index;
emit currentTemplateChanged();
}
void TemplatesModel::setSearchText(const QString& text)
@ -80,10 +98,10 @@ void TemplatesModel::setSearchText(const QString& text)
}
m_searchText = text;
updateBySearch();
updateTemplatesAndCategoriesBySearch();
}
void TemplatesModel::updateBySearch()
void TemplatesModel::updateTemplatesAndCategoriesBySearch()
{
m_visibleTemplates.clear();
m_visibleCategoriesTitles.clear();
@ -100,6 +118,7 @@ void TemplatesModel::updateBySearch()
emit categoriesChanged();
emit templatesChanged();
emit currentTemplateChanged();
}
bool TemplatesModel::titleAccepted(const QString& title) const

View file

@ -33,14 +33,17 @@ class TemplatesModel : public QObject
Q_PROPERTY(QStringList categoriesTitles READ categoriesTitles NOTIFY categoriesChanged)
Q_PROPERTY(QStringList templatesTitles READ templatesTitles NOTIFY templatesChanged)
Q_PROPERTY(QString currentTemplatePath READ currentTemplatePath NOTIFY currentTemplateChanged)
public:
TemplatesModel(QObject* parent = nullptr);
QStringList categoriesTitles() const;
QStringList templatesTitles() const;
QString currentTemplatePath() const;
Q_INVOKABLE void load();
Q_INVOKABLE void apply();
Q_INVOKABLE void setCurrentCategory(int index);
Q_INVOKABLE void setCurrentTemplate(int index);
@ -49,9 +52,12 @@ public:
signals:
void categoriesChanged();
void templatesChanged();
void currentTemplateChanged();
private:
void updateBySearch();
void updateTemplatesByCategory();
void updateTemplatesAndCategoriesBySearch();
bool titleAccepted(const QString& title) const;
Templates m_allTemplates;