Skip to content

Commit 450b921

Browse files
authored
Merge pull request #33 from scratchcpp/load_from_file
Add load from file option
2 parents f0ab317 + 55bbedf commit 450b921

File tree

13 files changed

+313
-9
lines changed

13 files changed

+313
-9
lines changed

CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
77

88
option(SCRATCHCPP_PLAYER_BUILD_UNIT_TESTS "Build unit tests" ON)
99

10-
find_package(Qt6 6.6 COMPONENTS Quick QuickControls2 REQUIRED)
10+
find_package(Qt6 6.6 COMPONENTS Quick QuickControls2 Widgets REQUIRED)
11+
set(QT_LIBS Qt6::Quick Qt6::QuickControls2 Qt6::Widgets)
1112

1213
if (SCRATCHCPP_PLAYER_BUILD_UNIT_TESTS)
1314
set(GTEST_DIR thirdparty/googletest)

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ When the project loads, click on the green flag button to run it.
103103
## Roadmap
104104

105105
- [x] Loading from URL
106-
- [ ] Loading from a local file (path to the file can be used as a URL until this is implemented)
106+
- [x] Loading from a local file
107107
- [x] Green flag button
108108
- [x] Stop button
109109
- [ ] Turbo mode

build/module.cmake

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ set(QML_IMPORT_PATH "${QML_IMPORT_PATH};${CMAKE_CURRENT_LIST_DIR}"
1919
FORCE
2020
)
2121

22+
target_link_libraries(${MODULE} PRIVATE ${QT_LIBS})
23+
2224
list(APPEND QML_IMPORT_PATH ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
2325
list(REMOVE_DUPLICATES QML_IMPORT_PATH)
2426
set(QML_IMPORT_PATH ${QML_IMPORT_PATH} CACHE STRING "" FORCE)

src/app/CMakeLists.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,7 @@ set_target_properties(${APP_TARGET} PROPERTIES
3434
target_compile_definitions(${APP_TARGET}
3535
PRIVATE $<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:QT_QML_DEBUG>)
3636
target_compile_definitions(${APP_TARGET} PRIVATE BUILD_VERSION="${CMAKE_PROJECT_VERSION}")
37-
target_link_libraries(${APP_TARGET}
38-
PRIVATE Qt6::Quick Qt6::QuickControls2)
37+
target_link_libraries(${APP_TARGET} PRIVATE ${QT_LIBS})
3938
target_include_directories(${APP_TARGET} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/..)
4039
target_include_directories(${APP_TARGET} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../global)
4140

src/app/app.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// SPDX-License-Identifier: GPL-3.0-or-later
22

3-
#include <QGuiApplication>
3+
#include <QApplication>
44
#include <QQmlApplicationEngine>
55
#include <QQuickStyle>
66
#include <QIcon>
@@ -23,7 +23,7 @@ int App::run(int argc, char **argv)
2323
qputenv("QSG_RENDER_LOOP", "basic");
2424

2525
// Set up application object
26-
QGuiApplication app(argc, argv);
26+
QApplication app(argc, argv);
2727
QCoreApplication::setOrganizationDomain("scratchcpp.github.io");
2828
QCoreApplication::setOrganizationName("ScratchCPP");
2929
QCoreApplication::setApplicationName("ScratchCPP");

src/app/appmenubar.cpp

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,70 @@
11
// SPDX-License-Identifier: GPL-3.0-or-later
22

3+
#include <QTemporaryFile>
4+
35
#include "appmenubar.h"
46
#include "uicomponents/menubarmodel.h"
57
#include "uicomponents/menumodel.h"
68
#include "uicomponents/menuitemmodel.h"
9+
#include "uicomponents/filedialog.h"
710

811
using namespace scratchcpp;
912
using namespace scratchcpp::uicomponents;
1013

1114
AppMenuBar::AppMenuBar(QObject *parent) :
1215
QObject(parent),
13-
m_model(new MenuBarModel(this))
16+
m_model(new MenuBarModel(this)),
17+
m_openFileDialog(new FileDialog(this))
1418
{
19+
m_openFileDialog->setShowAllFiles(false);
20+
m_openFileDialog->setNameFilters({ tr("Scratch 3 projects (%1)").arg("*.sb3") });
21+
22+
// File menu
23+
m_fileMenu = new MenuModel(m_model);
24+
m_fileMenu->setTitle(tr("File"));
25+
m_model->addMenu(m_fileMenu);
26+
27+
// File -> Open
28+
m_openFileItem = new MenuItemModel(m_fileMenu);
29+
m_openFileItem->setText(tr("Open..."));
30+
m_fileMenu->addItem(m_openFileItem);
31+
32+
connect(m_openFileItem, &MenuItemModel::clicked, this, &AppMenuBar::openFile);
33+
#ifdef Q_OS_WASM
34+
connect(m_openFileDialog, &FileDialog::fileContentReady, this, &AppMenuBar::loadOpenedFile);
35+
#endif
1536
}
1637

1738
MenuBarModel *AppMenuBar::model() const
1839
{
1940
return m_model;
2041
}
42+
43+
void AppMenuBar::openFile()
44+
{
45+
#ifdef Q_OS_WASM
46+
m_openFileDialog->getOpenFileContent();
47+
#else
48+
QString fileName = m_openFileDialog->getOpenFileName();
49+
50+
if (!fileName.isEmpty())
51+
emit fileOpened(fileName);
52+
#endif
53+
}
54+
55+
#ifdef Q_OS_WASM
56+
void AppMenuBar::loadOpenedFile(const QByteArray &content)
57+
{
58+
if (m_tmpFile)
59+
m_tmpFile->deleteLater();
60+
61+
m_tmpFile = new QTemporaryFile(this);
62+
63+
if (m_tmpFile->open()) {
64+
m_tmpFile->write(content);
65+
m_tmpFile->close();
66+
emit fileOpened(m_tmpFile->fileName());
67+
} else
68+
qWarning("Failed to create temporary file.");
69+
}
70+
#endif

src/app/appmenubar.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,20 @@
66

77
Q_MOC_INCLUDE("uicomponents/menubarmodel.h")
88

9+
class QTemporaryFile;
10+
911
namespace scratchcpp
1012
{
1113

1214
namespace uicomponents
1315
{
1416

1517
class MenuBarModel;
18+
class MenuModel;
19+
class MenuItemModel;
20+
class FileDialog;
1621

17-
}
22+
} // namespace uicomponents
1823

1924
class AppMenuBar : public QObject
2025
{
@@ -30,9 +35,19 @@ class AppMenuBar : public QObject
3035

3136
signals:
3237
void modelChanged();
38+
void fileOpened(const QString &fileName);
3339

3440
private:
41+
void openFile();
42+
#ifdef Q_OS_WASM
43+
void loadOpenedFile(const QByteArray &content);
44+
#endif
45+
3546
uicomponents::MenuBarModel *m_model = nullptr;
47+
uicomponents::MenuModel *m_fileMenu = nullptr;
48+
uicomponents::MenuItemModel *m_openFileItem = nullptr;
49+
uicomponents::FileDialog *m_openFileDialog = nullptr;
50+
QTemporaryFile *m_tmpFile = nullptr;
3651
};
3752

3853
} // namespace scratchcpp

src/app/main.qml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import ScratchCPP.Render
1010
ApplicationWindow {
1111
id: root
1212
minimumWidth: layout.implicitWidth + layout.anchors.margins * 2
13-
minimumHeight: layout.implicitHeight + layout.anchors.margins * 2
13+
minimumHeight: menuBar.height + layout.implicitHeight + layout.anchors.margins * 2
1414
visible: true
1515
title: "ScratchCPP"
1616
color: Material.background
@@ -20,6 +20,15 @@ ApplicationWindow {
2020
menuBar: CustomMenuBar {
2121
width: root.width
2222
model: AppMenuBar.model
23+
24+
Connections {
25+
target: AppMenuBar
26+
27+
function onFileOpened(fileName) {
28+
urlField.text = fileName;
29+
player.fileName = fileName;
30+
}
31+
}
2332
}
2433

2534
ColumnLayout {

src/uicomponents/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ set(MODULE_SRC
1616
menumodel.h
1717
menuitemmodel.cpp
1818
menuitemmodel.h
19+
filedialog.cpp
20+
filedialog.h
1921
)
2022

2123
include(${PROJECT_SOURCE_DIR}/build/module.cmake)

src/uicomponents/filedialog.cpp

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
// SPDX-License-Identifier: GPL-3.0-or-later
2+
3+
#include <QFileInfo>
4+
#include <QFileDialog>
5+
#include <QStandardPaths>
6+
7+
#include "filedialog.h"
8+
9+
using namespace scratchcpp::uicomponents;
10+
11+
FileDialog::FileDialog(QObject *parent) :
12+
QObject(parent)
13+
{
14+
}
15+
16+
const QStringList &FileDialog::nameFilters(void) const
17+
{
18+
return m_nameFilters;
19+
}
20+
21+
void FileDialog::setNameFilters(const QStringList &filters)
22+
{
23+
m_nameFilters = filters;
24+
emit nameFiltersChanged();
25+
}
26+
27+
bool FileDialog::showAllFiles(void) const
28+
{
29+
return m_showAllFiles;
30+
}
31+
32+
void FileDialog::setShowAllFiles(bool value)
33+
{
34+
m_showAllFiles = value;
35+
emit showAllFilesChanged();
36+
}
37+
38+
const QString &FileDialog::fileName(void) const
39+
{
40+
return m_fileName;
41+
}
42+
43+
QString FileDialog::shortFileName(void) const
44+
{
45+
QFileInfo fileInfo(m_fileName);
46+
return fileInfo.fileName();
47+
}
48+
49+
const QString &FileDialog::defaultSuffix() const
50+
{
51+
return m_defaultSuffix;
52+
}
53+
54+
void FileDialog::setDefaultSuffix(const QString &newDefaultSuffix)
55+
{
56+
if (m_defaultSuffix == newDefaultSuffix)
57+
return;
58+
59+
m_defaultSuffix = newDefaultSuffix;
60+
emit defaultSuffixChanged();
61+
}
62+
63+
void FileDialog::getOpenFileContent(void)
64+
{
65+
auto fileContentReadyLambda = [this](const QString &fileName, const QByteArray &fileContent) {
66+
if (!fileName.isEmpty()) {
67+
m_fileName = fileName;
68+
emit fileNameChanged();
69+
emit shortFileNameChanged();
70+
emit fileContentReady(fileContent);
71+
}
72+
};
73+
74+
#ifdef Q_OS_WASM
75+
QFileDialog::getOpenFileContent(QString(), fileContentReadyLambda);
76+
#else
77+
QString fileName = QFileDialog::getOpenFileName(nullptr, QString(), QStandardPaths::standardLocations(QStandardPaths::HomeLocation)[0], getFilters());
78+
79+
if (fileName != "") {
80+
QFile file(fileName);
81+
82+
if (file.open(QIODevice::ReadOnly))
83+
fileContentReadyLambda(fileName, file.readAll());
84+
}
85+
#endif
86+
}
87+
88+
QString FileDialog::getOpenFileName() const
89+
{
90+
QFileDialog dialog(nullptr, QString(), QStandardPaths::standardLocations(QStandardPaths::HomeLocation)[0], getFilters());
91+
dialog.setFileMode(QFileDialog::AnyFile);
92+
dialog.setAcceptMode(QFileDialog::AcceptOpen);
93+
dialog.setDefaultSuffix(m_defaultSuffix);
94+
95+
if (dialog.exec() == QDialog::Accepted)
96+
return dialog.selectedFiles().at(0);
97+
else
98+
return "";
99+
}
100+
101+
QString FileDialog::getSaveFileName() const
102+
{
103+
QFileDialog dialog(nullptr, QString(), QStandardPaths::standardLocations(QStandardPaths::HomeLocation)[0], getFilters());
104+
dialog.setFileMode(QFileDialog::AnyFile);
105+
dialog.setAcceptMode(QFileDialog::AcceptSave);
106+
dialog.setDefaultSuffix(m_defaultSuffix);
107+
108+
if (dialog.exec() == QDialog::Accepted)
109+
return dialog.selectedFiles().at(0);
110+
else
111+
return "";
112+
}
113+
114+
QString FileDialog::getFilters() const
115+
{
116+
QString filtersStr = m_nameFilters.join(";;");
117+
118+
if (m_showAllFiles)
119+
filtersStr += ";;" + tr("All files") + " (*)";
120+
121+
return filtersStr;
122+
}

0 commit comments

Comments
 (0)