Compare commits
24 Commits
4b9758f478
...
release-d2
| Author | SHA1 | Date | |
|---|---|---|---|
| 7f56a77e7d | |||
| 055d64b125 | |||
| de9f842640 | |||
| 200e586768 | |||
| f1609519a7 | |||
| e452d9db4f | |||
| 43a87b606e | |||
| 8acc6244d5 | |||
| bd2aeee276 | |||
| 89fab5cc20 | |||
| 1c06ea677f | |||
| 6b948ee069 | |||
| 72dddcaee5 | |||
| 7c824e910c | |||
| a0c8146396 | |||
| d5b232f5d5 | |||
| c79fe3be43 | |||
| 1df4e78084 | |||
| ffbdb09c31 | |||
| b35a956e4f | |||
| d3847caab4 | |||
| ae066a914c | |||
| 67543af806 | |||
| 6b774ec285 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,6 +2,7 @@
|
||||
.clangd
|
||||
.current_build
|
||||
.conanbuild
|
||||
.idea
|
||||
.mypy_cache
|
||||
.stfolder
|
||||
.stignore
|
||||
|
||||
@@ -15,6 +15,7 @@ probably differ), install the following additional packages:
|
||||
* pkg-config
|
||||
* xorg-dev
|
||||
* libgtk-3-dev
|
||||
* python3-mypy
|
||||
|
||||
## Build
|
||||
|
||||
|
||||
4
deps/buildcore/scripts/setup-build.py
vendored
4
deps/buildcore/scripts/setup-build.py
vendored
@@ -99,7 +99,9 @@ def main() -> int:
|
||||
if platform.system() == 'Windows':
|
||||
cmake_cmd.append('-A x64')
|
||||
|
||||
subprocess.run(cmake_cmd)
|
||||
cmake_err = subprocess.run(cmake_cmd).returncode
|
||||
if cmake_err != 0:
|
||||
return cmake_err
|
||||
|
||||
util.mkdir_p('dist')
|
||||
if int(args.current_build) != 0:
|
||||
|
||||
5
deps/ox/src/ox/claw/read.cpp
vendored
5
deps/ox/src/ox/claw/read.cpp
vendored
@@ -74,7 +74,10 @@ Result<Buffer> stripClawHeader(const ox::Buffer &buff) noexcept {
|
||||
|
||||
Result<ModelObject> readClaw(TypeStore &ts, const char *buff, std::size_t buffSz) noexcept {
|
||||
oxRequire(header, readClawHeader(buff, buffSz));
|
||||
oxRequire(t, ts.getLoad(header.typeName, header.typeVersion, header.typeParams));
|
||||
auto const [t, tdErr] = ts.getLoad(header.typeName, header.typeVersion, header.typeParams);
|
||||
if (tdErr) {
|
||||
return OxError(3, "Could not load type descriptor");
|
||||
}
|
||||
ModelObject obj;
|
||||
oxReturnError(obj.setType(t));
|
||||
switch (header.fmt) {
|
||||
|
||||
6
deps/ox/src/ox/fs/CMakeLists.txt
vendored
6
deps/ox/src/ox/fs/CMakeLists.txt
vendored
@@ -16,12 +16,6 @@ if(NOT MSVC)
|
||||
endif()
|
||||
|
||||
if(NOT OX_BARE_METAL)
|
||||
if(NOT APPLE AND NOT MSVC AND NOT ${OX_OS_FREEBSD})
|
||||
target_link_libraries(
|
||||
OxFS PUBLIC
|
||||
stdc++fs
|
||||
)
|
||||
endif()
|
||||
set_property(
|
||||
TARGET
|
||||
OxFS
|
||||
|
||||
6
deps/ox/src/ox/model/descwrite.hpp
vendored
6
deps/ox/src/ox/model/descwrite.hpp
vendored
@@ -219,9 +219,11 @@ template<typename T>
|
||||
constexpr Error TypeDescWriter::field(CRStringView name, const T *val) noexcept {
|
||||
if (m_type) {
|
||||
if constexpr(isVector_v<T> || isArray_v<T>) {
|
||||
return field(name, val->data(), 0, detail::buildSubscriptStack(val));
|
||||
typename T::value_type *data = nullptr;
|
||||
return field(name, data, 0, detail::buildSubscriptStack(val));
|
||||
} else if constexpr(isSmartPtr_v<T>) {
|
||||
return field(name, val->get(), 0, detail::buildSubscriptStack(val));
|
||||
typename T::value_type *data = nullptr;
|
||||
return field(name, data, 0, detail::buildSubscriptStack(val));
|
||||
} else if constexpr(is_pointer_v<T>) {
|
||||
return field(name, val, 0, detail::buildSubscriptStack(val));
|
||||
} else {
|
||||
|
||||
30
deps/ox/src/ox/oc/write.hpp
vendored
30
deps/ox/src/ox/oc/write.hpp
vendored
@@ -37,7 +37,7 @@ class OrganicClawWriter {
|
||||
explicit OrganicClawWriter(Json::Value json, int unionIdx = -1) noexcept;
|
||||
|
||||
Error field(const char *key, const int8_t *val) noexcept {
|
||||
if (*val) {
|
||||
if (targetValid() && (*val || m_json.isArray())) {
|
||||
value(key) = *val;
|
||||
}
|
||||
++m_fieldIt;
|
||||
@@ -45,7 +45,7 @@ class OrganicClawWriter {
|
||||
}
|
||||
|
||||
Error field(const char *key, const int16_t *val) noexcept {
|
||||
if (*val) {
|
||||
if (targetValid() && (*val || m_json.isArray())) {
|
||||
value(key) = *val;
|
||||
}
|
||||
++m_fieldIt;
|
||||
@@ -53,7 +53,7 @@ class OrganicClawWriter {
|
||||
}
|
||||
|
||||
Error field(const char *key, const int32_t *val) noexcept {
|
||||
if (*val) {
|
||||
if (targetValid() && (*val || m_json.isArray())) {
|
||||
value(key) = *val;
|
||||
}
|
||||
++m_fieldIt;
|
||||
@@ -61,7 +61,7 @@ class OrganicClawWriter {
|
||||
}
|
||||
|
||||
Error field(const char *key, const int64_t *val) noexcept {
|
||||
if (*val) {
|
||||
if (targetValid() && (*val || m_json.isArray())) {
|
||||
value(key) = *val;
|
||||
}
|
||||
++m_fieldIt;
|
||||
@@ -70,7 +70,7 @@ class OrganicClawWriter {
|
||||
|
||||
|
||||
Error field(const char *key, const uint8_t *val) noexcept {
|
||||
if (*val) {
|
||||
if (targetValid() && (*val || m_json.isArray())) {
|
||||
value(key) = *val;
|
||||
}
|
||||
++m_fieldIt;
|
||||
@@ -78,7 +78,7 @@ class OrganicClawWriter {
|
||||
}
|
||||
|
||||
Error field(const char *key, const uint16_t *val) noexcept {
|
||||
if (targetValid() && *val) {
|
||||
if (targetValid() && (*val || m_json.isArray())) {
|
||||
value(key) = *val;
|
||||
}
|
||||
++m_fieldIt;
|
||||
@@ -86,7 +86,7 @@ class OrganicClawWriter {
|
||||
}
|
||||
|
||||
Error field(const char *key, const uint32_t *val) noexcept {
|
||||
if (targetValid() && *val) {
|
||||
if (targetValid() && (*val || m_json.isArray())) {
|
||||
value(key) = *val;
|
||||
}
|
||||
++m_fieldIt;
|
||||
@@ -94,15 +94,15 @@ class OrganicClawWriter {
|
||||
}
|
||||
|
||||
Error field(const char *key, const uint64_t *val) noexcept {
|
||||
if (targetValid() && *val) {
|
||||
if (targetValid() && (*val || m_json.isArray())) {
|
||||
value(key) = *val;
|
||||
}
|
||||
++m_fieldIt;
|
||||
return OxError(0);
|
||||
}
|
||||
|
||||
Error field(const char *key, const bool *val) noexcept {
|
||||
if (targetValid() && *val) {
|
||||
Error field(char const*key, bool const*val) noexcept {
|
||||
if (targetValid() && (*val || m_json.isArray())) {
|
||||
value(key) = *val;
|
||||
}
|
||||
++m_fieldIt;
|
||||
@@ -110,10 +110,10 @@ class OrganicClawWriter {
|
||||
}
|
||||
|
||||
template<typename U, bool force = true>
|
||||
Error field(const char*, UnionView<U, force> val) noexcept;
|
||||
Error field(char const*, UnionView<U, force> val) noexcept;
|
||||
|
||||
template<typename T>
|
||||
Error field(const char *key, const HashMap<String, T> *val) noexcept {
|
||||
Error field(char const*key, HashMap<String, T> const*val) noexcept {
|
||||
if (targetValid()) {
|
||||
const auto &keys = val->keys();
|
||||
OrganicClawWriter w;
|
||||
@@ -132,7 +132,7 @@ class OrganicClawWriter {
|
||||
}
|
||||
|
||||
template<std::size_t L>
|
||||
Error field(const char *key, const BString<L> *val) noexcept {
|
||||
Error field(char const*key, BString<L> const*val) noexcept {
|
||||
if (targetValid() && val->len()) {
|
||||
value(key) = val->c_str();
|
||||
}
|
||||
@@ -141,7 +141,7 @@ class OrganicClawWriter {
|
||||
}
|
||||
|
||||
template<std::size_t L>
|
||||
Error field(const char *key, const BasicString<L> *val) noexcept {
|
||||
Error field(char const*key, BasicString<L> const*val) noexcept {
|
||||
if (targetValid() && val->len()) {
|
||||
value(key) = val->c_str();
|
||||
}
|
||||
@@ -199,7 +199,7 @@ Error OrganicClawWriter::field(const char *key, const T *val, std::size_t len) n
|
||||
OrganicClawWriter w((Json::Value(Json::arrayValue)));
|
||||
ModelHandlerInterface<OrganicClawWriter, OpType::Write> handler{&w};
|
||||
for (std::size_t i = 0; i < len; ++i) {
|
||||
oxReturnError(handler.field("", &val[i]));
|
||||
oxReturnError(handler.field({}, &val[i]));
|
||||
}
|
||||
value(key) = w.m_json;
|
||||
}
|
||||
|
||||
13
deps/ox/src/ox/std/memory.hpp
vendored
13
deps/ox/src/ox/std/memory.hpp
vendored
@@ -39,6 +39,7 @@ constexpr T *construct_at(T *p, Args &&...args ) {
|
||||
|
||||
#endif
|
||||
|
||||
#include "error.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
|
||||
@@ -74,6 +75,7 @@ class SharedPtr {
|
||||
int *m_refCnt = nullptr;
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
explicit constexpr SharedPtr(T *t = nullptr) noexcept: m_t(t), m_refCnt(new int) {
|
||||
}
|
||||
|
||||
@@ -183,6 +185,7 @@ class UniquePtr {
|
||||
T *m_t = nullptr;
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
explicit constexpr UniquePtr(T *t = nullptr) noexcept: m_t(t) {
|
||||
}
|
||||
|
||||
@@ -288,4 +291,14 @@ constexpr auto make_unique(Args&&... args) {
|
||||
return UniquePtr<U>(new T(ox::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template<typename T, typename U = T, typename ...Args>
|
||||
[[nodiscard]]
|
||||
constexpr Result<UniquePtr<U>> make_unique_catch(Args&&... args) noexcept {
|
||||
try {
|
||||
return UniquePtr<U>(new T(ox::forward<Args>(args)...));
|
||||
} catch (ox::Exception const&ex) {
|
||||
return ex.toError();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -63,6 +63,13 @@ All components have a platform indicator next to them:
|
||||
|
||||
### GBA
|
||||
|
||||
The GBA has two major resources for learning about its hardware:
|
||||
|
||||
* [Tonc](https://www.coranac.com/tonc/text/toc.htm) - This is basically a short
|
||||
book on the GBA and low level development.
|
||||
* [GBATEK](https://rust-console.github.io/gbatek-gbaonly/) - This is a more
|
||||
concise resource that mostly tells about memory ranges and registers.
|
||||
|
||||
#### Graphics
|
||||
|
||||
* Background Palette: 256 colors
|
||||
|
||||
45
jenkins/gba/Jenkinsfile
vendored
45
jenkins/gba/Jenkinsfile
vendored
@@ -1,45 +0,0 @@
|
||||
pipeline {
|
||||
agent {
|
||||
label 'gba'
|
||||
}
|
||||
stages {
|
||||
stage('Environment') {
|
||||
steps {
|
||||
load 'jenkins/shared/env.gy'
|
||||
sh 'make conan-config'
|
||||
sh 'make conan'
|
||||
}
|
||||
}
|
||||
stage('Build Tools Debug') {
|
||||
steps {
|
||||
sh 'make purge configure-debug'
|
||||
sh 'make install'
|
||||
}
|
||||
}
|
||||
stage('Build GBA Debug') {
|
||||
steps {
|
||||
sh 'make configure-gba-debug'
|
||||
sh 'make'
|
||||
sh 'make pkg-gba'
|
||||
}
|
||||
}
|
||||
stage('Build Tools Release') {
|
||||
steps {
|
||||
sh 'make purge configure-release'
|
||||
sh 'make install'
|
||||
}
|
||||
}
|
||||
stage('Build GBA Release') {
|
||||
steps {
|
||||
sh 'make configure-gba'
|
||||
sh 'make'
|
||||
sh 'make pkg-gba'
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
archiveArtifacts artifacts: 'nostalgia.gba', fingerprint: true
|
||||
}
|
||||
}
|
||||
}
|
||||
47
jenkins/linux/Jenkinsfile
vendored
47
jenkins/linux/Jenkinsfile
vendored
@@ -1,47 +0,0 @@
|
||||
pipeline {
|
||||
agent {
|
||||
label 'linux-x86_64'
|
||||
}
|
||||
stages {
|
||||
stage('Environment') {
|
||||
steps {
|
||||
load 'jenkins/shared/env.gy'
|
||||
sh 'make conan-config'
|
||||
sh 'make conan'
|
||||
}
|
||||
}
|
||||
stage('Build Asan') {
|
||||
steps {
|
||||
sh 'make purge configure-asan'
|
||||
sh 'make'
|
||||
}
|
||||
}
|
||||
stage('Test Asan') {
|
||||
steps {
|
||||
sh 'make test'
|
||||
}
|
||||
}
|
||||
stage('Build Debug') {
|
||||
steps {
|
||||
sh 'make purge configure-debug'
|
||||
sh 'make'
|
||||
}
|
||||
}
|
||||
stage('Test Debug') {
|
||||
steps {
|
||||
sh 'make test'
|
||||
}
|
||||
}
|
||||
stage('Build Release') {
|
||||
steps {
|
||||
sh 'make purge configure-release'
|
||||
sh 'make'
|
||||
}
|
||||
}
|
||||
stage('Test Release') {
|
||||
steps {
|
||||
sh 'make test'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
47
jenkins/mac/Jenkinsfile
vendored
47
jenkins/mac/Jenkinsfile
vendored
@@ -1,47 +0,0 @@
|
||||
pipeline {
|
||||
agent {
|
||||
label 'mac-x86_64'
|
||||
}
|
||||
stages {
|
||||
stage('Environment') {
|
||||
steps {
|
||||
load 'jenkins/shared/env.gy'
|
||||
sh 'make conan-config'
|
||||
sh 'make conan'
|
||||
}
|
||||
}
|
||||
stage('Build Asan') {
|
||||
steps {
|
||||
sh 'make purge configure-asan'
|
||||
sh 'make'
|
||||
}
|
||||
}
|
||||
stage('Test Asan') {
|
||||
steps {
|
||||
sh 'make test'
|
||||
}
|
||||
}
|
||||
stage('Build Debug') {
|
||||
steps {
|
||||
sh 'make purge configure-debug'
|
||||
sh 'make'
|
||||
}
|
||||
}
|
||||
stage('Test Debug') {
|
||||
steps {
|
||||
sh 'make test'
|
||||
}
|
||||
}
|
||||
stage('Build Release') {
|
||||
steps {
|
||||
sh 'make purge configure-release'
|
||||
sh 'make'
|
||||
}
|
||||
}
|
||||
stage('Test Release') {
|
||||
steps {
|
||||
sh 'make test'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
env.OX_NODEBUG = 1
|
||||
env.BUILDCORE_SUPPRESS_CCACHE = 1
|
||||
@@ -20,6 +20,8 @@ host_env = f'{os}-{arch}'
|
||||
# get current build type
|
||||
with open(".current_build", "r") as f:
|
||||
current_build = f.readlines()[0]
|
||||
if current_build[len(current_build) - 1] == '\n':
|
||||
current_build = current_build[:len(current_build) - 1]
|
||||
|
||||
project_dir = sys.argv[1]
|
||||
project_name = sys.argv[2]
|
||||
|
||||
@@ -147,7 +147,7 @@ void TileSheetEditorModel::deleteTiles(TileSheet::SubSheetIdx const&idx, std::si
|
||||
pushCommand(ox::make<DeleteTilesCommand>(m_img, idx, tileIdx, tileCnt));
|
||||
}
|
||||
|
||||
ox::Error TileSheetEditorModel::updateSubsheet(TileSheet::SubSheetIdx const&idx, const ox::StringView &name, int cols, int rows) noexcept {
|
||||
ox::Error TileSheetEditorModel::updateSubsheet(TileSheet::SubSheetIdx const&idx, ox::StringView const&name, int cols, int rows) noexcept {
|
||||
pushCommand(ox::make<UpdateSubSheetCommand>(m_img, idx, ox::String(name), cols, rows));
|
||||
return {};
|
||||
}
|
||||
@@ -239,7 +239,7 @@ void TileSheetEditorModel::ackUpdate() noexcept {
|
||||
|
||||
ox::Error TileSheetEditorModel::saveFile() noexcept {
|
||||
const auto sctx = applicationData<studio::StudioContext>(m_ctx);
|
||||
return sctx->project->writeObj(m_path, m_img);
|
||||
return sctx->project->writeObj(m_path, m_img, ox::ClawFormat::Metal);
|
||||
}
|
||||
|
||||
bool TileSheetEditorModel::pixelSelected(std::size_t idx) const noexcept {
|
||||
|
||||
@@ -61,21 +61,21 @@ class TileSheetEditorModel: public ox::SignalHandler {
|
||||
|
||||
ox::Error setPalette(ox::StringView path) noexcept;
|
||||
|
||||
void drawCommand(const ox::Point &pt, std::size_t palIdx) noexcept;
|
||||
void drawCommand(ox::Point const&pt, std::size_t palIdx) noexcept;
|
||||
|
||||
void endDrawCommand() noexcept;
|
||||
|
||||
void addSubsheet(const TileSheet::SubSheetIdx &parentIdx) noexcept;
|
||||
void addSubsheet(TileSheet::SubSheetIdx const&parentIdx) noexcept;
|
||||
|
||||
void rmSubsheet(const TileSheet::SubSheetIdx &idx) noexcept;
|
||||
void rmSubsheet(TileSheet::SubSheetIdx const&idx) noexcept;
|
||||
|
||||
void insertTiles(const TileSheet::SubSheetIdx &idx, std::size_t tileIdx, std::size_t tileCnt) noexcept;
|
||||
void insertTiles(TileSheet::SubSheetIdx const&idx, std::size_t tileIdx, std::size_t tileCnt) noexcept;
|
||||
|
||||
void deleteTiles(const TileSheet::SubSheetIdx &idx, std::size_t tileIdx, std::size_t tileCnt) noexcept;
|
||||
void deleteTiles(TileSheet::SubSheetIdx const&idx, std::size_t tileIdx, std::size_t tileCnt) noexcept;
|
||||
|
||||
ox::Error updateSubsheet(const TileSheet::SubSheetIdx &idx, const ox::StringView &name, int cols, int rows) noexcept;
|
||||
ox::Error updateSubsheet(TileSheet::SubSheetIdx const&idx, ox::StringView const&name, int cols, int rows) noexcept;
|
||||
|
||||
void setActiveSubsheet(const TileSheet::SubSheetIdx&) noexcept;
|
||||
void setActiveSubsheet(TileSheet::SubSheetIdx const&) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
const TileSheet::SubSheet *activeSubSheet() const noexcept {
|
||||
|
||||
@@ -9,6 +9,11 @@ target_link_libraries(
|
||||
OlympicApplib
|
||||
)
|
||||
|
||||
target_compile_definitions(
|
||||
NostalgiaStudio PUBLIC
|
||||
OLYMPIC_APP_VERSION="2023.12.0"
|
||||
)
|
||||
|
||||
install(
|
||||
TARGETS
|
||||
NostalgiaStudio
|
||||
|
||||
@@ -13,6 +13,10 @@
|
||||
#define OLYMPIC_APP_NAME "App"
|
||||
#endif
|
||||
|
||||
#ifndef OLYMPIC_APP_VERSION
|
||||
#define OLYMPIC_APP_VERSION "dev build"
|
||||
#endif
|
||||
|
||||
#ifndef OLYMPIC_PROJECT_NAMESPACE
|
||||
#define OLYMPIC_PROJECT_NAMESPACE project
|
||||
#endif
|
||||
@@ -31,6 +35,8 @@
|
||||
|
||||
namespace olympic {
|
||||
|
||||
ox::String s_version = ox::String(OLYMPIC_APP_VERSION);
|
||||
|
||||
ox::Error run(
|
||||
ox::StringView project,
|
||||
ox::StringView appName,
|
||||
|
||||
@@ -125,7 +125,11 @@ ox::Error preloadDir(
|
||||
auto const dir = ox::sfmt("{}{}/", path, name);
|
||||
oxReturnError(preloadDir(ts, romFs, pl, dir));
|
||||
} else {
|
||||
oxReturnError(preloadObj(ts, romFs, pl, filePath));
|
||||
auto const err = preloadObj(ts, romFs, pl, filePath);
|
||||
if (err) {
|
||||
oxErrf("\033[31;1;1mCould not preload {}:\n\t{}\n", filePath, toStr(err));
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
|
||||
@@ -120,7 +120,11 @@ static ox::Error transformClaw(
|
||||
auto const dir = ox::sfmt("{}{}/", path, name);
|
||||
oxReturnError(transformClaw(ctx, ts, dest, dir));
|
||||
} else {
|
||||
oxReturnError(doTransformations(ctx, ts, dest, filePath));
|
||||
auto const err = doTransformations(ctx, ts, dest, filePath);
|
||||
if (err) {
|
||||
oxErrf("\033[31;1;1mCould not do transformations for {}:\n\t{}\n", filePath, toStr(err));
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
|
||||
@@ -5,6 +5,7 @@ add_library(
|
||||
filedialogmanager.cpp
|
||||
main.cpp
|
||||
newmenu.cpp
|
||||
newproject.cpp
|
||||
projectexplorer.cpp
|
||||
projecttreemodel.cpp
|
||||
studioapp.cpp
|
||||
|
||||
@@ -7,10 +7,14 @@
|
||||
#include <studio/imguiuitl.hpp>
|
||||
#include "aboutpopup.hpp"
|
||||
|
||||
namespace olympic {
|
||||
extern ox::String s_version;
|
||||
}
|
||||
|
||||
namespace studio {
|
||||
|
||||
AboutPopup::AboutPopup(turbine::Context &ctx) noexcept {
|
||||
m_text = ox::sfmt("{} - dev build", keelCtx(ctx).appName);
|
||||
m_text = ox::sfmt("{} - {}", keelCtx(ctx).appName, olympic::s_version);
|
||||
}
|
||||
|
||||
void AboutPopup::open() noexcept {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <ctime>
|
||||
|
||||
#include <ox/logconn/logconn.hpp>
|
||||
#include <ox/logconn/def.hpp>
|
||||
#include <ox/std/trace.hpp>
|
||||
#include <ox/std/uuid.hpp>
|
||||
#include <keel/media.hpp>
|
||||
@@ -31,21 +30,19 @@ class StudioUIDrawer: public turbine::gl::Drawer {
|
||||
|
||||
static int updateHandler(turbine::Context &ctx) noexcept {
|
||||
auto sctx = turbine::applicationData<studio::StudioContext>(ctx);
|
||||
auto ui = dynamic_cast<StudioUI*>(sctx->ui);
|
||||
ui->update();
|
||||
sctx->ui->update();
|
||||
return 16;
|
||||
}
|
||||
|
||||
static void keyEventHandler(turbine::Context &ctx, turbine::Key key, bool down) noexcept {
|
||||
auto sctx = turbine::applicationData<studio::StudioContext>(ctx);
|
||||
auto ui = dynamic_cast<StudioUI*>(sctx->ui);
|
||||
ui->handleKeyEvent(key, down);
|
||||
sctx->ui->handleKeyEvent(key, down);
|
||||
}
|
||||
|
||||
static ox::Error runApp(
|
||||
ox::CRStringView appName,
|
||||
ox::CRStringView projectDataDir,
|
||||
ox::UniquePtr<ox::FileSystem> fs) noexcept {
|
||||
ox::UPtr<ox::FileSystem> &&fs) noexcept {
|
||||
oxRequireM(ctx, turbine::init(std::move(fs), appName));
|
||||
turbine::setWindowTitle(*ctx, keelCtx(*ctx).appName);
|
||||
turbine::setUpdateHandler(*ctx, updateHandler);
|
||||
|
||||
@@ -115,12 +115,12 @@ void NewMenu::drawLastPageButtons(turbine::Context &ctx) noexcept {
|
||||
}
|
||||
|
||||
void NewMenu::finish(turbine::Context &ctx) noexcept {
|
||||
auto const err = m_types[static_cast<std::size_t>(m_selectedType)]->write(ctx, m_itemName);
|
||||
auto const [path, err] = m_types[static_cast<std::size_t>(m_selectedType)]->write(ctx, m_itemName);
|
||||
if (err) {
|
||||
oxLogError(err);
|
||||
return;
|
||||
}
|
||||
finished.emit("");
|
||||
finished.emit(path);
|
||||
m_stage = Stage::Closed;
|
||||
}
|
||||
|
||||
|
||||
95
src/olympic/studio/applib/src/newproject.cpp
Normal file
95
src/olympic/studio/applib/src/newproject.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include <studio/imguiuitl.hpp>
|
||||
#include <utility>
|
||||
|
||||
#include "filedialogmanager.hpp"
|
||||
#include "newproject.hpp"
|
||||
|
||||
namespace studio {
|
||||
|
||||
NewProject::NewProject(ox::String projectDatadir) noexcept: m_projectDataDir(std::move(projectDatadir)) {
|
||||
setTitle(ox::String("New Project"));
|
||||
setSize({230, 140});
|
||||
}
|
||||
|
||||
void NewProject::open() noexcept {
|
||||
m_stage = Stage::Opening;
|
||||
m_projectPath = "";
|
||||
m_projectName = "";
|
||||
}
|
||||
|
||||
void NewProject::close() noexcept {
|
||||
m_stage = Stage::Closed;
|
||||
m_open = false;
|
||||
}
|
||||
|
||||
bool NewProject::isOpen() const noexcept {
|
||||
return m_open;
|
||||
}
|
||||
|
||||
void NewProject::draw(turbine::Context &ctx) noexcept {
|
||||
switch (m_stage) {
|
||||
case Stage::Opening:
|
||||
ImGui::OpenPopup(title().c_str());
|
||||
m_stage = Stage::NewItemName;
|
||||
m_open = true;
|
||||
[[fallthrough]];
|
||||
case Stage::NewItemName:
|
||||
drawNewProjectName(ctx);
|
||||
break;
|
||||
case Stage::Closed:
|
||||
m_open = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void NewProject::drawNewProjectName(turbine::Context &ctx) noexcept {
|
||||
drawWindow(ctx, &m_open, [this, &ctx] {
|
||||
ImGui::InputText("Name", m_projectName.data(), m_projectName.cap());
|
||||
ImGui::Text("Path: %s", m_projectPath.c_str());
|
||||
if (ImGui::Button("Browse")) {
|
||||
oxLogError(studio::chooseDirectory().moveTo(m_projectPath));
|
||||
}
|
||||
drawLastPageButtons(ctx);
|
||||
});
|
||||
}
|
||||
|
||||
void NewProject::drawFirstPageButtons() noexcept {
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 130);
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - 20);
|
||||
auto const btnSz = ImVec2(60, 20);
|
||||
if (ImGui::Button("Next", btnSz)) {
|
||||
m_stage = Stage::NewItemName;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel", btnSz)) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
m_stage = Stage::Closed;
|
||||
}
|
||||
}
|
||||
|
||||
void NewProject::drawLastPageButtons(turbine::Context&) noexcept {
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 95);
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - 20);
|
||||
if (ImGui::Button("Finish")) {
|
||||
finish();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Quit")) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
m_stage = Stage::Closed;
|
||||
}
|
||||
}
|
||||
|
||||
void NewProject::finish() noexcept {
|
||||
auto projectPath = ox::sfmt("{}/{}", m_projectPath, m_projectName);
|
||||
finished.emit(projectPath);
|
||||
m_stage = Stage::Closed;
|
||||
}
|
||||
|
||||
}
|
||||
58
src/olympic/studio/applib/src/newproject.hpp
Normal file
58
src/olympic/studio/applib/src/newproject.hpp
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/claw/claw.hpp>
|
||||
#include <ox/event/signal.hpp>
|
||||
#include <ox/std/string.hpp>
|
||||
|
||||
#include <studio/itemmaker.hpp>
|
||||
#include <studio/popup.hpp>
|
||||
|
||||
namespace studio {
|
||||
|
||||
class NewProject: public studio::Popup {
|
||||
public:
|
||||
enum class Stage {
|
||||
Closed,
|
||||
Opening,
|
||||
NewItemName,
|
||||
};
|
||||
|
||||
// emits path parameter
|
||||
ox::Signal<ox::Error(ox::StringView)> finished;
|
||||
|
||||
private:
|
||||
Stage m_stage = Stage::Closed;
|
||||
ox::String const m_projectDataDir;
|
||||
ox::String m_projectPath;
|
||||
ox::BString<255> m_projectName;
|
||||
ox::Vector<ox::UniquePtr<studio::ItemMaker>> m_types;
|
||||
bool m_open = false;
|
||||
|
||||
public:
|
||||
NewProject(ox::String projectDatadir) noexcept;
|
||||
|
||||
void open() noexcept override;
|
||||
|
||||
void close() noexcept override;
|
||||
|
||||
[[nodiscard]]
|
||||
bool isOpen() const noexcept override;
|
||||
|
||||
void draw(turbine::Context &ctx) noexcept override;
|
||||
|
||||
private:
|
||||
void drawNewProjectName(turbine::Context &ctx) noexcept;
|
||||
|
||||
void drawFirstPageButtons() noexcept;
|
||||
|
||||
void drawLastPageButtons(turbine::Context &ctx) noexcept;
|
||||
|
||||
void finish() noexcept;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include <keel/media.hpp>
|
||||
@@ -42,15 +44,18 @@ StudioUI::StudioUI(turbine::Context &ctx, ox::StringView projectDataDir) noexcep
|
||||
m_ctx(ctx),
|
||||
m_projectDataDir(projectDataDir),
|
||||
m_projectExplorer(m_ctx),
|
||||
m_newProject(ox::String(projectDataDir)),
|
||||
m_aboutPopup(m_ctx) {
|
||||
m_projectExplorer.fileChosen.connect(this, &StudioUI::openFile);
|
||||
m_newProject.finished.connect(this, &StudioUI::createOpenProject);
|
||||
m_newMenu.finished.connect(this, &StudioUI::openFile);
|
||||
ImGui::GetIO().IniFilename = nullptr;
|
||||
loadModules();
|
||||
// open project and files
|
||||
auto const [config, err] = studio::readConfig<StudioConfig>(keelCtx(m_ctx));
|
||||
m_showProjectExplorer = config.showProjectExplorer;
|
||||
if (!err) {
|
||||
auto const openProjErr = openProject(config.projectPath);
|
||||
auto const openProjErr = openProjectPath(config.projectPath);
|
||||
if (!openProjErr) {
|
||||
for (auto const&f: config.openFiles) {
|
||||
auto openFileErr = openFileActiveTab(f, config.activeTabItemName == f);
|
||||
@@ -95,10 +100,14 @@ void StudioUI::handleKeyEvent(turbine::Key key, bool down) noexcept {
|
||||
}
|
||||
break;
|
||||
case turbine::Key::Alpha_N:
|
||||
if (turbine::buttonDown(m_ctx, turbine::Key::Mod_Shift)) {
|
||||
m_newProject.open();
|
||||
} else {
|
||||
m_newMenu.open();
|
||||
}
|
||||
break;
|
||||
case turbine::Key::Alpha_O:
|
||||
m_taskRunner.add(*ox::make<FileDialogManager>(this, &StudioUI::openProject));
|
||||
m_taskRunner.add(*ox::make<FileDialogManager>(this, &StudioUI::openProjectPath));
|
||||
break;
|
||||
case turbine::Key::Alpha_Q:
|
||||
turbine::requestShutdown(m_ctx);
|
||||
@@ -168,8 +177,11 @@ void StudioUI::drawMenu() noexcept {
|
||||
if (ImGui::MenuItem("New...", "Ctrl+N")) {
|
||||
m_newMenu.open();
|
||||
}
|
||||
if (ImGui::MenuItem("New Project...", "Ctrl+Shift+N")) {
|
||||
m_newProject.open();
|
||||
}
|
||||
if (ImGui::MenuItem("Open Project...", "Ctrl+O")) {
|
||||
m_taskRunner.add(*ox::make<FileDialogManager>(this, &StudioUI::openProject));
|
||||
m_taskRunner.add(*ox::make<FileDialogManager>(this, &StudioUI::openProjectPath));
|
||||
}
|
||||
if (ImGui::MenuItem("Save", "Ctrl+S", false, m_activeEditor && m_activeEditor->unsavedChanges())) {
|
||||
m_activeEditor->save();
|
||||
@@ -317,19 +329,29 @@ void StudioUI::save() noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
ox::Error StudioUI::openProject(ox::CRStringView path) noexcept {
|
||||
ox::Error StudioUI::createOpenProject(ox::CRStringView path) noexcept {
|
||||
std::error_code ec;
|
||||
std::filesystem::create_directories(toStdStringView(path), ec);
|
||||
oxReturnError(OxError(ec.value() != 0, "Could not create project directory"));
|
||||
oxReturnError(openProjectPath(path));
|
||||
return m_project->writeTypeStore();
|
||||
}
|
||||
|
||||
ox::Error StudioUI::openProjectPath(ox::CRStringView path) noexcept {
|
||||
oxRequireM(fs, keel::loadRomFs(path));
|
||||
oxReturnError(keel::setRomFs(keelCtx(m_ctx), std::move(fs)));
|
||||
turbine::setWindowTitle(m_ctx, ox::sfmt("{} - {}", keelCtx(m_ctx).appName, path));
|
||||
m_project = ox::make_unique<studio::Project>(keelCtx(m_ctx), ox::String(path), m_projectDataDir);
|
||||
auto sctx = applicationData<studio::StudioContext>(m_ctx);
|
||||
oxReturnError(
|
||||
ox::make_unique_catch<studio::Project>(keelCtx(m_ctx), ox::String(path), m_projectDataDir)
|
||||
.moveTo(m_project));
|
||||
auto const sctx = applicationData<studio::StudioContext>(m_ctx);
|
||||
sctx->project = m_project.get();
|
||||
turbine::setWindowTitle(m_ctx, ox::sfmt("{} - {}", keelCtx(m_ctx).appName, m_project->projectPath()));
|
||||
m_project->fileAdded.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel);
|
||||
m_project->fileDeleted.connect(&m_projectExplorer, &ProjectExplorer::refreshProjectTreeModel);
|
||||
m_openFiles.clear();
|
||||
m_editors.clear();
|
||||
studio::editConfig<StudioConfig>(keelCtx(m_ctx), [&](StudioConfig *config) {
|
||||
config->projectPath = ox::String(path);
|
||||
config->projectPath = ox::String(m_project->projectPath());
|
||||
config->openFiles.clear();
|
||||
});
|
||||
return m_projectExplorer.refreshProjectTreeModel();
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <studio/task.hpp>
|
||||
#include "aboutpopup.hpp"
|
||||
#include "newmenu.hpp"
|
||||
#include "newproject.hpp"
|
||||
#include "projectexplorer.hpp"
|
||||
#include "projecttreemodel.hpp"
|
||||
|
||||
@@ -36,9 +37,11 @@ class StudioUI: public ox::SignalHandler {
|
||||
studio::BaseEditor *m_activeEditor = nullptr;
|
||||
studio::BaseEditor *m_activeEditorUpdatePending = nullptr;
|
||||
NewMenu m_newMenu;
|
||||
NewProject m_newProject;
|
||||
AboutPopup m_aboutPopup;
|
||||
ox::Array<studio::Popup*, 2> const m_popups = {
|
||||
ox::Array<studio::Popup*, 3> const m_popups = {
|
||||
&m_newMenu,
|
||||
&m_newProject,
|
||||
&m_aboutPopup
|
||||
};
|
||||
bool m_showProjectExplorer = true;
|
||||
@@ -79,7 +82,9 @@ class StudioUI: public ox::SignalHandler {
|
||||
|
||||
void save() noexcept;
|
||||
|
||||
ox::Error openProject(ox::CRStringView path) noexcept;
|
||||
ox::Error createOpenProject(ox::CRStringView path) noexcept;
|
||||
|
||||
ox::Error openProjectPath(ox::CRStringView path) noexcept;
|
||||
|
||||
ox::Error openFile(ox::CRStringView path) noexcept;
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
namespace studio {
|
||||
|
||||
struct StudioContext {
|
||||
ox::SignalHandler *ui = nullptr;
|
||||
class StudioUI *ui = nullptr;
|
||||
Project *project = nullptr;
|
||||
};
|
||||
|
||||
|
||||
@@ -24,7 +24,14 @@ class ItemMaker {
|
||||
fileExt(pFileExt) {
|
||||
}
|
||||
virtual ~ItemMaker() noexcept = default;
|
||||
virtual ox::Error write(turbine::Context &ctx, ox::CRStringView pName) const noexcept = 0;
|
||||
|
||||
/**
|
||||
* Returns path of the file created.
|
||||
* @param ctx
|
||||
* @param pName
|
||||
* @return path of file or error in Result
|
||||
*/
|
||||
virtual ox::Result<ox::String> write(turbine::Context &ctx, ox::CRStringView pName) const noexcept = 0;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
@@ -61,11 +68,12 @@ class ItemMakerT: public ItemMaker {
|
||||
m_item(std::move(pItem)),
|
||||
m_fmt(pFmt) {
|
||||
}
|
||||
ox::Error write(turbine::Context &ctx, ox::CRStringView pName) const noexcept override {
|
||||
ox::Result<ox::String> write(turbine::Context &ctx, ox::CRStringView pName) const noexcept override {
|
||||
auto const path = ox::sfmt("/{}/{}.{}", parentDir, pName, fileExt);
|
||||
auto sctx = turbine::applicationData<studio::StudioContext>(ctx);
|
||||
keel::createUuidMapping(keelCtx(ctx), path, ox::UUID::generate().unwrap());
|
||||
return sctx->project->writeObj(path, m_item, m_fmt);
|
||||
oxReturnError(sctx->project->writeObj(path, m_item, m_fmt));
|
||||
return path;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -36,20 +36,33 @@ constexpr ox::Result<ox::StringView> fileExt(ox::CRStringView path) noexcept {
|
||||
return substr(path, extStart + 1);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr ox::StringView parentDir(ox::StringView path) noexcept {
|
||||
if (path.len() && path[path.len() - 1] == '/') {
|
||||
path = substr(path, 0, path.len() - 1);
|
||||
}
|
||||
auto const extStart = ox::find(path.crbegin(), path.crend(), '/').offset();
|
||||
return substr(path, 0, extStart);
|
||||
}
|
||||
|
||||
class Project {
|
||||
private:
|
||||
keel::Context &m_ctx;
|
||||
ox::String m_path;
|
||||
ox::String m_projectDataDir;
|
||||
ox::String m_typeDescPath;
|
||||
mutable keel::TypeStore m_typeStore;
|
||||
ox::FileSystem &m_fs;
|
||||
ox::HashMap<ox::String, ox::Vector<ox::String>> m_fileExtFileMap;
|
||||
|
||||
public:
|
||||
explicit Project(keel::Context &ctx, ox::String path, ox::CRStringView projectDataDir) noexcept;
|
||||
explicit Project(keel::Context &ctx, ox::String path, ox::CRStringView projectDataDir);
|
||||
|
||||
ox::Error create() noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
ox::String const&projectPath() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
ox::FileSystem *romFs() noexcept;
|
||||
|
||||
@@ -78,6 +91,8 @@ class Project {
|
||||
[[nodiscard]]
|
||||
ox::Vector<ox::String> const&fileList(ox::CRStringView ext) noexcept;
|
||||
|
||||
ox::Error writeTypeStore() noexcept;
|
||||
|
||||
private:
|
||||
void buildFileIndex() noexcept;
|
||||
|
||||
@@ -108,21 +123,16 @@ template<typename T>
|
||||
ox::Error Project::writeObj(ox::CRStringView path, T const&obj, ox::ClawFormat fmt) noexcept {
|
||||
oxRequireM(buff, ox::writeClaw(obj, fmt));
|
||||
// write to FS
|
||||
oxReturnError(mkdir(parentDir(path)));
|
||||
oxReturnError(writeBuff(path, buff));
|
||||
// write type descriptor
|
||||
if (m_typeStore.get<T>().error) {
|
||||
oxReturnError(ox::buildTypeDef(&m_typeStore, &obj));
|
||||
}
|
||||
// write out type store
|
||||
auto const descPath = ox::sfmt("/{}/type_descriptors", m_projectDataDir);
|
||||
oxReturnError(mkdir(descPath));
|
||||
for (auto const&t : m_typeStore.typeList()) {
|
||||
oxRequireM(typeOut, ox::writeClaw(*t, ox::ClawFormat::Organic));
|
||||
// replace garbage last character with new line
|
||||
*typeOut.back().value = '\n';
|
||||
// write to FS
|
||||
auto const typePath = ox::sfmt("/{}/{}", descPath, buildTypeId(*t));
|
||||
oxReturnError(writeBuff(typePath, typeOut));
|
||||
oxRequire(desc, m_typeStore.get<T>());
|
||||
auto const descExists = m_fs.stat(ox::sfmt("{}/{}", m_typeDescPath, buildTypeId(*desc))).error != 0;
|
||||
if (!descExists) {
|
||||
oxReturnError(writeTypeStore());
|
||||
}
|
||||
oxReturnError(keel::setAsset(m_ctx, path, obj));
|
||||
fileUpdated.emit(path);
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
|
||||
namespace studio {
|
||||
|
||||
static_assert(fileExt("main.c").value == "c");
|
||||
static_assert(fileExt("a.b.c").value == "c");
|
||||
static_assert(parentDir("/a/b/c") == "/a/b");
|
||||
static_assert(parentDir("/a/b/c/") == "/a/b");
|
||||
|
||||
static void generateTypes(ox::TypeStore &ts) noexcept {
|
||||
for (auto const mod : keel::modules()) {
|
||||
for (auto gen : mod->types()) {
|
||||
@@ -21,14 +26,18 @@ static void generateTypes(ox::TypeStore &ts) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
Project::Project(keel::Context &ctx, ox::String path, ox::CRStringView projectDataDir) noexcept:
|
||||
Project::Project(keel::Context &ctx, ox::String path, ox::CRStringView projectDataDir):
|
||||
m_ctx(ctx),
|
||||
m_path(std::move(path)),
|
||||
m_projectDataDir(projectDataDir),
|
||||
m_typeDescPath(ox::sfmt("/{}/type_descriptors", m_projectDataDir)),
|
||||
m_typeStore(*m_ctx.rom, ox::sfmt("/{}/type_descriptors", projectDataDir)),
|
||||
m_fs(*m_ctx.rom) {
|
||||
oxTracef("studio", "Project: {}", m_path);
|
||||
generateTypes(m_typeStore);
|
||||
if (ox::defines::Debug) {
|
||||
oxThrowError(writeTypeStore());
|
||||
}
|
||||
buildFileIndex();
|
||||
}
|
||||
|
||||
@@ -38,6 +47,10 @@ ox::Error Project::create() noexcept {
|
||||
return OxError(static_cast<ox::ErrorCode>(ec.value()), "PassThroughFS: mkdir failed");
|
||||
}
|
||||
|
||||
ox::String const&Project::projectPath() const noexcept {
|
||||
return m_path;
|
||||
}
|
||||
|
||||
ox::FileSystem *Project::romFs() noexcept {
|
||||
return &m_fs;
|
||||
}
|
||||
@@ -60,6 +73,20 @@ ox::Vector<ox::String> const&Project::fileList(ox::CRStringView ext) noexcept {
|
||||
return m_fileExtFileMap[ext];
|
||||
}
|
||||
|
||||
ox::Error Project::writeTypeStore() noexcept {
|
||||
// write all descriptors because we don't know which types T depends on
|
||||
oxReturnError(mkdir(m_typeDescPath));
|
||||
for (auto const &t: m_typeStore.typeList()) {
|
||||
oxRequireM(typeOut, ox::writeClaw(*t, ox::ClawFormat::Organic));
|
||||
// replace garbage last character with new line
|
||||
*typeOut.back().value = '\n';
|
||||
// write to FS
|
||||
auto const typePath = ox::sfmt("{}/{}", m_typeDescPath, buildTypeId(*t));
|
||||
oxReturnError(writeBuff(typePath, typeOut));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void Project::buildFileIndex() noexcept {
|
||||
auto [files, err] = listFiles();
|
||||
if (err) {
|
||||
|
||||
@@ -54,6 +54,8 @@ static void handleKeyPress(Context &ctx, int key, bool down) noexcept {
|
||||
map[GLFW_KEY_RIGHT_CONTROL] = Key::Mod_Ctrl;
|
||||
map[GLFW_KEY_LEFT_SUPER] = Key::Mod_Super;
|
||||
map[GLFW_KEY_RIGHT_SUPER] = Key::Mod_Super;
|
||||
map[GLFW_KEY_LEFT_SHIFT] = Key::Mod_Shift;
|
||||
map[GLFW_KEY_RIGHT_SHIFT] = Key::Mod_Shift;
|
||||
map[GLFW_KEY_ESCAPE] = Key::Escape;
|
||||
return map;
|
||||
}();
|
||||
|
||||
Reference in New Issue
Block a user