Compare commits
17 Commits
12e5623fe6
...
release-d2
Author | SHA1 | Date | |
---|---|---|---|
1057099fcf | |||
17577b6e8b | |||
db442e2775 | |||
23cd50d29e | |||
28088d1ed1 | |||
baf5fa3199 | |||
857587c18b | |||
eb3d53c955 | |||
14d58f3f5b | |||
5f2397903a | |||
58e0ecb469 | |||
8838bf420e | |||
bddc544d7c | |||
a9437191bf | |||
9d8da7ccda | |||
394b568e72 | |||
78e9f70db6 |
5
deps/ox/src/ox/std/stringview.hpp
vendored
5
deps/ox/src/ox/std/stringview.hpp
vendored
@@ -104,13 +104,16 @@ constexpr ox::Result<int> strToInt(StringViewCR str) noexcept {
|
|||||||
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
||||||
int total = 0;
|
int total = 0;
|
||||||
int multiplier = 1;
|
int multiplier = 1;
|
||||||
|
if (str.len() == 0) [[unlikely]] {
|
||||||
|
return Error{1, "Empty string passed to strToInt"};
|
||||||
|
}
|
||||||
for (auto i = static_cast<int64_t>(str.len()) - 1; i != -1; --i) {
|
for (auto i = static_cast<int64_t>(str.len()) - 1; i != -1; --i) {
|
||||||
auto s = static_cast<std::size_t>(i);
|
auto s = static_cast<std::size_t>(i);
|
||||||
if (str[s] >= '0' && str[s] <= '9') {
|
if (str[s] >= '0' && str[s] <= '9') {
|
||||||
total += (str[s] - '0') * multiplier;
|
total += (str[s] - '0') * multiplier;
|
||||||
multiplier *= 10;
|
multiplier *= 10;
|
||||||
} else {
|
} else {
|
||||||
return ox::Error(1);
|
return ox::Error{1};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return total;
|
return total;
|
||||||
|
@@ -1,15 +1,19 @@
|
|||||||
# d2025.06.0
|
# d2025.06.0
|
||||||
|
|
||||||
* Add ability to remember recent projects in config
|
* Add ability to remember recent projects in config
|
||||||
|
* Add navigation support (back and forward)
|
||||||
|
* Fix file deletion to close file even if not active
|
||||||
|
* Fix file copy to work when creating a copy with the name of a previously
|
||||||
|
deleted file
|
||||||
|
* Fix crash that could occur after switching projects
|
||||||
|
* Make file picker popup accept on double click of a file
|
||||||
|
* TileSheetEditor: Fix copy/cut/paste enablement when there is no selection
|
||||||
|
* TileSheetEditor: Fix manual redo of draw actions, fix drawing to pixel 0, 0
|
||||||
|
as first action
|
||||||
|
* TileSheetEditor: Fix draw command to work on same pixel after switching
|
||||||
|
subsheets
|
||||||
* PaletteEditor: Add RGB key shortcuts for focusing color channels
|
* PaletteEditor: Add RGB key shortcuts for focusing color channels
|
||||||
* PaletteEditor: Add color preview to color editor
|
* PaletteEditor: Add color preview to color editor
|
||||||
* PaletteEditor: Make RGB key shortcuts work when color channel inputs are
|
|
||||||
focused
|
|
||||||
|
|
||||||
# d2025.05.2
|
|
||||||
|
|
||||||
* TileSheetEditor: Fix manual redo of draw actions, fix drawing to pixel 0, 0 as first action (cce5f52f96511694afd98f0b9b6b1f19c06ecd20)
|
|
||||||
* TileSheetEditor: Fix draw command to work on same pixel after switching subsheets (514cb978351ee4b0a5335c22a506a6d9f608f0a7)
|
|
||||||
|
|
||||||
# d2025.05.1
|
# d2025.05.1
|
||||||
|
|
||||||
|
@@ -13,7 +13,7 @@ AddSubSheetCommand::AddSubSheetCommand(
|
|||||||
auto &parent = getSubSheet(m_img, m_parentIdx);
|
auto &parent = getSubSheet(m_img, m_parentIdx);
|
||||||
if (!parent.subsheets.empty()) {
|
if (!parent.subsheets.empty()) {
|
||||||
auto idx = m_parentIdx;
|
auto idx = m_parentIdx;
|
||||||
idx.emplace_back(parent.subsheets.size());
|
idx.emplace_back(static_cast<uint32_t>(parent.subsheets.size()));
|
||||||
m_addedSheets.push_back(idx);
|
m_addedSheets.push_back(idx);
|
||||||
} else {
|
} else {
|
||||||
auto idx = m_parentIdx;
|
auto idx = m_parentIdx;
|
||||||
|
@@ -192,6 +192,9 @@ void TileSheetEditorImGui::keyStateChanged(turbine::Key const key, bool const do
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TileSheetEditorImGui::draw(studio::Context&) noexcept {
|
void TileSheetEditorImGui::draw(studio::Context&) noexcept {
|
||||||
|
setCopyEnabled(m_model.hasSelection());
|
||||||
|
setCutEnabled(m_model.hasSelection());
|
||||||
|
setPasteEnabled(m_model.hasSelection());
|
||||||
if (ig::mainWinHasFocus() && m_tool == TileSheetTool::Select) {
|
if (ig::mainWinHasFocus() && m_tool == TileSheetTool::Select) {
|
||||||
if (ImGui::IsKeyDown(ImGuiKey_ModCtrl) && !m_palPathFocused) {
|
if (ImGui::IsKeyDown(ImGuiKey_ModCtrl) && !m_palPathFocused) {
|
||||||
if (ImGui::IsKeyPressed(ImGuiKey_A)) {
|
if (ImGui::IsKeyPressed(ImGuiKey_A)) {
|
||||||
@@ -276,7 +279,7 @@ void TileSheetEditorImGui::draw(studio::Context&) noexcept {
|
|||||||
auto insertOnIdx = m_model.activeSubSheetIdx();
|
auto insertOnIdx = m_model.activeSubSheetIdx();
|
||||||
auto const &parent = m_model.activeSubSheet();
|
auto const &parent = m_model.activeSubSheet();
|
||||||
m_model.addSubsheet(insertOnIdx);
|
m_model.addSubsheet(insertOnIdx);
|
||||||
insertOnIdx.emplace_back(parent.subsheets.size() - 1);
|
insertOnIdx.emplace_back(static_cast<uint32_t>(parent.subsheets.size() - 1));
|
||||||
setActiveSubsheet(insertOnIdx);
|
setActiveSubsheet(insertOnIdx);
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
@@ -315,6 +315,10 @@ void TileSheetEditorModel::clearSelection() noexcept {
|
|||||||
m_selection.reset();
|
m_selection.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TileSheetEditorModel::hasSelection() const noexcept {
|
||||||
|
return m_selection.has_value();
|
||||||
|
}
|
||||||
|
|
||||||
bool TileSheetEditorModel::updated() const noexcept {
|
bool TileSheetEditorModel::updated() const noexcept {
|
||||||
return m_updated;
|
return m_updated;
|
||||||
}
|
}
|
||||||
|
@@ -118,6 +118,9 @@ class TileSheetEditorModel final: public ox::SignalHandler {
|
|||||||
|
|
||||||
void clearSelection() noexcept;
|
void clearSelection() noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
bool hasSelection() const noexcept;
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
bool updated() const noexcept;
|
bool updated() const noexcept;
|
||||||
|
|
||||||
|
@@ -15,7 +15,7 @@ target_link_libraries(
|
|||||||
|
|
||||||
target_compile_definitions(
|
target_compile_definitions(
|
||||||
NostalgiaStudio PUBLIC
|
NostalgiaStudio PUBLIC
|
||||||
OLYMPIC_APP_VERSION="dev build"
|
OLYMPIC_APP_VERSION="d2025.06.0"
|
||||||
)
|
)
|
||||||
|
|
||||||
install(
|
install(
|
||||||
|
@@ -18,7 +18,7 @@
|
|||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
|
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>dev build</string>
|
<string>d2025.06.0</string>
|
||||||
|
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
<string>12.0.0</string>
|
<string>12.0.0</string>
|
||||||
|
@@ -28,10 +28,15 @@ class StudioUIDrawer: public turbine::gl::Drawer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static void keyEventHandler(turbine::Context &ctx, turbine::Key key, bool down) noexcept {
|
static void keyEventHandler(turbine::Context &ctx, turbine::Key key, bool down) noexcept {
|
||||||
auto sctx = turbine::applicationData<studio::Context>(ctx);
|
auto const sctx = turbine::applicationData<studio::Context>(ctx);
|
||||||
sctx->ui.handleKeyEvent(key, down);
|
sctx->ui.handleKeyEvent(key, down);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void mouseButtonEventHandler(turbine::Context &ctx, int const btn, bool const down) noexcept {
|
||||||
|
auto const sctx = turbine::applicationData<studio::Context>(ctx);
|
||||||
|
sctx->ui.handleMouseButtonEvent(btn, down);
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
ox::Vector<ox::SpanView<uint8_t>> WindowIcons() noexcept;
|
ox::Vector<ox::SpanView<uint8_t>> WindowIcons() noexcept;
|
||||||
|
|
||||||
@@ -43,6 +48,7 @@ static ox::Error runApp(
|
|||||||
oxLogError(turbine::setWindowIcon(*ctx, WindowIcons()));
|
oxLogError(turbine::setWindowIcon(*ctx, WindowIcons()));
|
||||||
turbine::setWindowTitle(*ctx, keelCtx(*ctx).appName);
|
turbine::setWindowTitle(*ctx, keelCtx(*ctx).appName);
|
||||||
turbine::setKeyEventHandler(*ctx, keyEventHandler);
|
turbine::setKeyEventHandler(*ctx, keyEventHandler);
|
||||||
|
turbine::setMouseButtonEventHandler(*ctx, mouseButtonEventHandler);
|
||||||
turbine::requireRefreshWithin(*ctx, 0);
|
turbine::requireRefreshWithin(*ctx, 0);
|
||||||
StudioUI ui(*ctx, projectDataDir);
|
StudioUI ui(*ctx, projectDataDir);
|
||||||
StudioUIDrawer drawer(ui);
|
StudioUIDrawer drawer(ui);
|
||||||
|
@@ -80,9 +80,9 @@ void NewMenu::addItemMaker(ox::UPtr<ItemMaker> &&im) noexcept {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void NewMenu::installItemTemplate(ox::UPtr<ItemTemplate> &tmplt) noexcept {
|
void NewMenu::installItemTemplate(ox::UPtr<ItemTemplate> &&tmplt) noexcept {
|
||||||
for (auto const&im : m_types) {
|
for (auto const&im : m_types) {
|
||||||
if (im->installTemplate(tmplt)) {
|
if (im->installTemplate(std::move(tmplt))) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -72,7 +72,7 @@ class NewMenu final: public Popup {
|
|||||||
|
|
||||||
void addItemMaker(ox::UPtr<ItemMaker> &&im) noexcept;
|
void addItemMaker(ox::UPtr<ItemMaker> &&im) noexcept;
|
||||||
|
|
||||||
void installItemTemplate(ox::UPtr<ItemTemplate> &tmplt) noexcept;
|
void installItemTemplate(ox::UPtr<ItemTemplate> &&tmplt) noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void drawNewItemType(Context const&sctx) noexcept;
|
void drawNewItemType(Context const&sctx) noexcept;
|
||||||
|
@@ -34,18 +34,6 @@ static bool shutdownHandler(turbine::Context &ctx) {
|
|||||||
return sctx->ui.handleShutdown();
|
return sctx->ui.handleShutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
void navigateTo(Context &ctx, ox::StringParam filePath, ox::StringParam navArgs) noexcept {
|
|
||||||
ox::String path = std::move(filePath);
|
|
||||||
if (beginsWith(path, "uuid://")) {
|
|
||||||
auto [p, err] = keel::uuidUrlToPath(keelCtx(ctx), path);
|
|
||||||
if (err) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
path = p;
|
|
||||||
}
|
|
||||||
ctx.ui.navigateTo(std::move(path), std::move(navArgs));
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace ig {
|
namespace ig {
|
||||||
extern bool s_mainWinHasFocus;
|
extern bool s_mainWinHasFocus;
|
||||||
}
|
}
|
||||||
@@ -147,6 +135,9 @@ StudioUI::StudioUI(turbine::Context &tctx, ox::StringParam projectDataDir) noexc
|
|||||||
oxLogError(headerizeConfigFile<StudioConfigV1>(kctx));
|
oxLogError(headerizeConfigFile<StudioConfigV1>(kctx));
|
||||||
turbine::setApplicationData(m_tctx, &m_sctx);
|
turbine::setApplicationData(m_tctx, &m_sctx);
|
||||||
turbine::setShutdownHandler(m_tctx, shutdownHandler);
|
turbine::setShutdownHandler(m_tctx, shutdownHandler);
|
||||||
|
m_sctx.navCallback = [this](ox::StringParam filePath, ox::StringParam navArgs) {
|
||||||
|
handleNavigationChange(std::move(filePath), std::move(navArgs));
|
||||||
|
};
|
||||||
m_projectExplorer.fileChosen.connect(this, &StudioUI::openFile);
|
m_projectExplorer.fileChosen.connect(this, &StudioUI::openFile);
|
||||||
m_projectExplorer.addDir.connect(this, &StudioUI::addDir);
|
m_projectExplorer.addDir.connect(this, &StudioUI::addDir);
|
||||||
m_projectExplorer.addItem.connect(this, &StudioUI::addFile);
|
m_projectExplorer.addItem.connect(this, &StudioUI::addFile);
|
||||||
@@ -186,7 +177,17 @@ void StudioUI::handleKeyEvent(turbine::Key const key, bool const down) noexcept
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void StudioUI::navigateTo(ox::StringParam path, ox::StringParam navArgs) noexcept {
|
void StudioUI::handleMouseButtonEvent(int const btn, bool const down) noexcept {
|
||||||
|
if (down) {
|
||||||
|
if (btn == 3) { // back button
|
||||||
|
navigateBack(m_sctx);
|
||||||
|
} else if (btn == 4) { // forward button
|
||||||
|
navigateForward(m_sctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StudioUI::handleNavigationChange(ox::StringParam path, ox::StringParam navArgs) noexcept {
|
||||||
m_navAction.emplace(std::move(path), std::move(navArgs));
|
m_navAction.emplace(std::move(path), std::move(navArgs));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,7 +271,7 @@ void StudioUI::drawMenu() noexcept {
|
|||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
if (ImGui::BeginMenu("Edit")) {
|
if (ImGui::BeginMenu("Edit")) {
|
||||||
auto undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr;
|
auto const undoStack = m_activeEditor ? m_activeEditor->undoStack() : nullptr;
|
||||||
if (ImGui::MenuItem(
|
if (ImGui::MenuItem(
|
||||||
"Undo", STUDIO_CTRL "+Z", false, undoStack && undoStack->canUndo())) {
|
"Undo", STUDIO_CTRL "+Z", false, undoStack && undoStack->canUndo())) {
|
||||||
oxLogError(undoStack->undo());
|
oxLogError(undoStack->undo());
|
||||||
@@ -310,6 +311,19 @@ void StudioUI::drawMenu() noexcept {
|
|||||||
}
|
}
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
|
if (ImGui::BeginMenu("Navigate")) {
|
||||||
|
constexpr auto backShortcut = ox::defines::OS == ox::OS::Darwin ? "Cmd+[" : "Alt+Left Arrow";
|
||||||
|
constexpr auto fwdShortcut = ox::defines::OS == ox::OS::Darwin ? "Cmd+]" : "Alt+Right Arrow";
|
||||||
|
if (ImGui::MenuItem("Back", backShortcut, false, m_sctx.navIdx > 1)) {
|
||||||
|
navigateBack(m_sctx);
|
||||||
|
}
|
||||||
|
if (ImGui::MenuItem(
|
||||||
|
"Forward", fwdShortcut, false,
|
||||||
|
m_sctx.navIdx < m_sctx.navStack.size())) {
|
||||||
|
navigateForward(m_sctx);
|
||||||
|
}
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
if (ImGui::BeginMenu("Help")) {
|
if (ImGui::BeginMenu("Help")) {
|
||||||
if (ImGui::MenuItem("About")) {
|
if (ImGui::MenuItem("About")) {
|
||||||
m_aboutPopup.open();
|
m_aboutPopup.open();
|
||||||
@@ -353,6 +367,13 @@ void StudioUI::drawTabs() noexcept {
|
|||||||
}
|
}
|
||||||
if (m_activeEditorOnLastDraw != e.get()) [[unlikely]] {
|
if (m_activeEditorOnLastDraw != e.get()) [[unlikely]] {
|
||||||
m_activeEditor->onActivated();
|
m_activeEditor->onActivated();
|
||||||
|
if (!m_sctx.navIdx ||
|
||||||
|
(m_sctx.navIdx <= m_sctx.navStack.size() &&
|
||||||
|
m_sctx.navStack[m_sctx.navIdx - 1].filePath != m_activeEditor->itemPath())) {
|
||||||
|
m_sctx.navStack.resize(m_sctx.navIdx);
|
||||||
|
++m_sctx.navIdx;
|
||||||
|
m_sctx.navStack.emplace_back(ox::String{m_activeEditor->itemPath()}, ox::String{""});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (m_closeActiveTab) [[unlikely]] {
|
if (m_closeActiveTab) [[unlikely]] {
|
||||||
ImGui::SetTabItemClosed(e->itemDisplayName().c_str());
|
ImGui::SetTabItemClosed(e->itemDisplayName().c_str());
|
||||||
@@ -395,9 +416,10 @@ void StudioUI::drawTabs() noexcept {
|
|||||||
m_closeActiveTab = false;
|
m_closeActiveTab = false;
|
||||||
}
|
}
|
||||||
if (m_navAction) {
|
if (m_navAction) {
|
||||||
oxLogError(openFile(m_navAction->path));
|
if (!openFile(m_navAction->path)) {
|
||||||
m_activeEditor->navigateTo(m_navAction->args);
|
m_activeEditor->navigateTo(m_navAction->args);
|
||||||
m_navAction.reset();
|
m_navAction.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -416,7 +438,7 @@ void StudioUI::loadModule(Module const &mod) noexcept {
|
|||||||
}
|
}
|
||||||
auto tmplts = mod.itemTemplates(m_sctx);
|
auto tmplts = mod.itemTemplates(m_sctx);
|
||||||
for (auto &t : tmplts) {
|
for (auto &t : tmplts) {
|
||||||
m_newMenu.installItemTemplate(t);
|
m_newMenu.installItemTemplate(std::move(t));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -506,6 +528,22 @@ void StudioUI::handleKeyInput() noexcept {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if constexpr (ox::defines::OS == ox::OS::Darwin) {
|
||||||
|
if (ImGui::IsKeyPressed(ImGuiKey_LeftBracket)) {
|
||||||
|
navigateBack(m_sctx);
|
||||||
|
} else if (ImGui::IsKeyPressed(ImGuiKey_RightBracket)) {
|
||||||
|
navigateForward(m_sctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if constexpr (ox::defines::OS != ox::OS::Darwin) {
|
||||||
|
if (ImGui::IsKeyDown(ImGuiKey_ModAlt)) {
|
||||||
|
if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow)) {
|
||||||
|
navigateBack(m_sctx);
|
||||||
|
} else if (ImGui::IsKeyPressed(ImGuiKey_RightArrow)) {
|
||||||
|
navigateForward(m_sctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -611,6 +649,9 @@ ox::Error StudioUI::openProjectPath(ox::StringParam path) noexcept {
|
|||||||
ox::make_unique_catch<Project>(keelCtx(m_tctx), std::move(path), m_projectDataDir)
|
ox::make_unique_catch<Project>(keelCtx(m_tctx), std::move(path), m_projectDataDir)
|
||||||
.moveTo(m_project));
|
.moveTo(m_project));
|
||||||
m_sctx.project = m_project.get();
|
m_sctx.project = m_project.get();
|
||||||
|
m_activeEditor = nullptr;
|
||||||
|
m_activeEditorOnLastDraw = nullptr;
|
||||||
|
m_activeEditorUpdatePending = nullptr;
|
||||||
turbine::setWindowTitle(
|
turbine::setWindowTitle(
|
||||||
m_tctx, ox::sfmt("{} - {}", keelCtx(m_tctx).appName, m_project->projectPath()));
|
m_tctx, ox::sfmt("{} - {}", keelCtx(m_tctx).appName, m_project->projectPath()));
|
||||||
m_deleteConfirmation.deleteFile.connect(m_sctx.project, &Project::deleteItem);
|
m_deleteConfirmation.deleteFile.connect(m_sctx.project, &Project::deleteItem);
|
||||||
@@ -664,7 +705,10 @@ ox::Error StudioUI::openFile(ox::StringViewCR path) noexcept {
|
|||||||
|
|
||||||
ox::Error StudioUI::openFileActiveTab(ox::StringViewCR path, bool const makeActiveTab) noexcept {
|
ox::Error StudioUI::openFileActiveTab(ox::StringViewCR path, bool const makeActiveTab) noexcept {
|
||||||
if (!m_project) {
|
if (!m_project) {
|
||||||
return ox::Error(1, "No project open to open a file from");
|
return ox::Error(1, "no project open to open a file from");
|
||||||
|
}
|
||||||
|
if (!m_project->romFs().exists(path)) {
|
||||||
|
return ox::Error{1, "file does note exist"};
|
||||||
}
|
}
|
||||||
if (m_openFiles.contains(path)) {
|
if (m_openFiles.contains(path)) {
|
||||||
for (auto &e : m_editors) {
|
for (auto &e : m_editors) {
|
||||||
|
@@ -87,7 +87,9 @@ class StudioUI final: public ox::SignalHandler {
|
|||||||
|
|
||||||
void handleKeyEvent(turbine::Key, bool down) noexcept;
|
void handleKeyEvent(turbine::Key, bool down) noexcept;
|
||||||
|
|
||||||
void navigateTo(ox::StringParam path, ox::StringParam navArgs) noexcept;
|
void handleMouseButtonEvent(int btn, bool down) noexcept;
|
||||||
|
|
||||||
|
void handleNavigationChange(ox::StringParam path, ox::StringParam navArgs) noexcept;
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
constexpr Project *project() noexcept {
|
constexpr Project *project() noexcept {
|
||||||
|
@@ -16,11 +16,21 @@ class StudioUI;
|
|||||||
|
|
||||||
struct Context {
|
struct Context {
|
||||||
public:
|
public:
|
||||||
|
friend void navigateTo(Context &ctx, ox::StringParam filePath, ox::StringParam navArgs) noexcept;
|
||||||
|
friend void navigateBack(Context &ctx) noexcept;
|
||||||
|
friend void navigateForward(Context &ctx) noexcept;
|
||||||
friend StudioUI;
|
friend StudioUI;
|
||||||
StudioUI &ui;
|
StudioUI &ui;
|
||||||
Project *project = nullptr;
|
Project *project = nullptr;
|
||||||
turbine::Context &tctx;
|
turbine::Context &tctx;
|
||||||
protected:
|
protected:
|
||||||
|
struct NavPath {
|
||||||
|
ox::String filePath;
|
||||||
|
ox::String navArgs;
|
||||||
|
};
|
||||||
|
size_t navIdx{};
|
||||||
|
ox::Vector<NavPath> navStack;
|
||||||
|
std::function<void(ox::StringParam filePath, ox::StringParam navArgs)> navCallback;
|
||||||
Context(StudioUI &pUi, turbine::Context &pTctx) noexcept:
|
Context(StudioUI &pUi, turbine::Context &pTctx) noexcept:
|
||||||
ui{pUi}, tctx{pTctx} {}
|
ui{pUi}, tctx{pTctx} {}
|
||||||
};
|
};
|
||||||
@@ -37,4 +47,8 @@ inline keel::Context const &keelCtx(Context const &ctx) noexcept {
|
|||||||
|
|
||||||
void navigateTo(Context &ctx, ox::StringParam filePath, ox::StringParam navArgs = "") noexcept;
|
void navigateTo(Context &ctx, ox::StringParam filePath, ox::StringParam navArgs = "") noexcept;
|
||||||
|
|
||||||
|
void navigateBack(Context &ctx) noexcept;
|
||||||
|
|
||||||
|
void navigateForward(Context &ctx) noexcept;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -13,7 +13,11 @@ class FilePickerPopup {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
ox::String m_name;
|
ox::String m_name;
|
||||||
FileExplorer m_explorer;
|
struct Explorer: public FileExplorer {
|
||||||
|
mutable bool opened{};
|
||||||
|
explicit Explorer(keel::Context &kctx);
|
||||||
|
void fileOpened(ox::StringViewCR path) const noexcept override;
|
||||||
|
} m_explorer;
|
||||||
ox::Vector<ox::String> const m_fileExts;
|
ox::Vector<ox::String> const m_fileExts;
|
||||||
bool m_open{};
|
bool m_open{};
|
||||||
|
|
||||||
@@ -33,6 +37,10 @@ class FilePickerPopup {
|
|||||||
|
|
||||||
ox::Optional<ox::String> draw(Context &ctx) noexcept;
|
ox::Optional<ox::String> draw(Context &ctx) noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
[[nodiscard]]
|
||||||
|
ox::Optional<ox::String> handlePick() noexcept;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -46,14 +46,14 @@ ox::Result<T> getDragDropPayload() noexcept {
|
|||||||
static_cast<size_t>(payload->DataSize)});
|
static_cast<size_t>(payload->DataSize)});
|
||||||
}
|
}
|
||||||
|
|
||||||
ox::Error setDragDropPayload(ox::CStringViewCR name, auto const&obj) noexcept {
|
ox::Error setDragDropPayload(ox::CStringViewCR name, auto const &obj) noexcept {
|
||||||
OX_REQUIRE(buff, ox::writeClaw(obj, ox::ClawFormat::Metal));
|
OX_REQUIRE(buff, ox::writeClaw(obj, ox::ClawFormat::Metal));
|
||||||
ImGui::SetDragDropPayload(name.c_str(), buff.data(), buff.size());
|
ImGui::SetDragDropPayload(name.c_str(), buff.data(), buff.size());
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
ox::Error setDragDropPayload(T const&obj) noexcept {
|
ox::Error setDragDropPayload(T const &obj) noexcept {
|
||||||
OX_REQUIRE(buff, ox::writeClaw(obj, ox::ClawFormat::Metal));
|
OX_REQUIRE(buff, ox::writeClaw(obj, ox::ClawFormat::Metal));
|
||||||
ImGui::SetDragDropPayload(ox::ModelTypeName_v<T>, buff.data(), buff.size());
|
ImGui::SetDragDropPayload(ox::ModelTypeName_v<T>, buff.data(), buff.size());
|
||||||
return {};
|
return {};
|
||||||
@@ -77,7 +77,7 @@ class DragDropSource {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto dragDropSource(auto const&cb, ImGuiDragDropFlags const flags = 0) noexcept {
|
auto dragDropSource(auto const &cb, ImGuiDragDropFlags const flags = 0) noexcept {
|
||||||
if constexpr(ox::is_same_v<decltype(cb()), ox::Error>) {
|
if constexpr(ox::is_same_v<decltype(cb()), ox::Error>) {
|
||||||
if (ig::DragDropSource const tgt{flags}; tgt) [[unlikely]] {
|
if (ig::DragDropSource const tgt{flags}; tgt) [[unlikely]] {
|
||||||
return cb();
|
return cb();
|
||||||
@@ -108,7 +108,7 @@ class DragDropTarget {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto dragDropTarget(auto const&cb) noexcept {
|
auto dragDropTarget(auto const &cb) noexcept {
|
||||||
if constexpr(ox::is_same_v<decltype(cb()), ox::Error>) {
|
if constexpr(ox::is_same_v<decltype(cb()), ox::Error>) {
|
||||||
if (ig::DragDropTarget const tgt; tgt) [[unlikely]] {
|
if (ig::DragDropTarget const tgt; tgt) [[unlikely]] {
|
||||||
return cb();
|
return cb();
|
||||||
@@ -124,7 +124,7 @@ auto dragDropTarget(auto const&cb) noexcept {
|
|||||||
|
|
||||||
class ChildStackItem {
|
class ChildStackItem {
|
||||||
public:
|
public:
|
||||||
explicit ChildStackItem(ox::CStringViewCR id, ImVec2 const&sz = {}) noexcept;
|
explicit ChildStackItem(ox::CStringViewCR id, ImVec2 const &sz = {}) noexcept;
|
||||||
~ChildStackItem() noexcept;
|
~ChildStackItem() noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -146,7 +146,7 @@ class IndentStackItem {
|
|||||||
|
|
||||||
void centerNextWindow(turbine::Context &ctx) noexcept;
|
void centerNextWindow(turbine::Context &ctx) noexcept;
|
||||||
|
|
||||||
bool PushButton(ox::CStringViewCR lbl, ImVec2 const&btnSz = BtnSz) noexcept;
|
bool PushButton(ox::CStringViewCR lbl, ImVec2 const &btnSz = BtnSz) noexcept;
|
||||||
|
|
||||||
template<typename Str>
|
template<typename Str>
|
||||||
struct TextInput {
|
struct TextInput {
|
||||||
@@ -234,7 +234,7 @@ PopupResponse PopupControlsOk(
|
|||||||
ox::CStringViewCR ok);
|
ox::CStringViewCR ok);
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
bool BeginPopup(turbine::Context &ctx, ox::CStringViewCR popupName, bool &show, ImVec2 const&sz = {285, 0});
|
bool BeginPopup(turbine::Context &ctx, ox::CStringViewCR popupName, bool &show, ImVec2 const &sz = {285, 0});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -266,7 +266,7 @@ bool ComboBox(ox::CStringView lbl, ox::Span<const ox::String> list, size_t &sele
|
|||||||
*/
|
*/
|
||||||
bool ComboBox(
|
bool ComboBox(
|
||||||
ox::CStringViewCR lbl,
|
ox::CStringViewCR lbl,
|
||||||
std::function<ox::CStringView(size_t)> const&f,
|
std::function<ox::CStringView(size_t)> const &f,
|
||||||
size_t strCnt,
|
size_t strCnt,
|
||||||
size_t &selectedIdx) noexcept;
|
size_t &selectedIdx) noexcept;
|
||||||
|
|
||||||
@@ -278,10 +278,10 @@ bool FileComboBox(
|
|||||||
|
|
||||||
bool ListBox(
|
bool ListBox(
|
||||||
ox::CStringViewCR name,
|
ox::CStringViewCR name,
|
||||||
std::function<ox::CStringView(size_t)> const&f,
|
std::function<ox::CStringView(size_t)> const &f,
|
||||||
size_t strCnt,
|
size_t strCnt,
|
||||||
size_t &selIdx,
|
size_t &selIdx,
|
||||||
ImVec2 const&sz = {0, 0}) noexcept;
|
ImVec2 const &sz = {0, 0}) noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -290,7 +290,7 @@ bool ListBox(
|
|||||||
* @param selIdx
|
* @param selIdx
|
||||||
* @return true if new value selected, false otherwise
|
* @return true if new value selected, false otherwise
|
||||||
*/
|
*/
|
||||||
bool ListBox(ox::CStringViewCR name, ox::SpanView<ox::String> const&list, size_t &selIdx) noexcept;
|
bool ListBox(ox::CStringViewCR name, ox::SpanView<ox::String> const &list, size_t &selIdx) noexcept;
|
||||||
|
|
||||||
class FilePicker {
|
class FilePicker {
|
||||||
private:
|
private:
|
||||||
@@ -306,7 +306,7 @@ class FilePicker {
|
|||||||
studio::Context &sctx,
|
studio::Context &sctx,
|
||||||
ox::StringParam title,
|
ox::StringParam title,
|
||||||
ox::StringParam fileExt,
|
ox::StringParam fileExt,
|
||||||
ImVec2 const&size = {}) noexcept;
|
ImVec2 const &size = {}) noexcept;
|
||||||
|
|
||||||
void draw() noexcept;
|
void draw() noexcept;
|
||||||
|
|
||||||
@@ -348,6 +348,8 @@ class QuestionPopup: public Popup {
|
|||||||
|
|
||||||
QuestionPopup(ox::StringParam title, ox::StringParam question) noexcept;
|
QuestionPopup(ox::StringParam title, ox::StringParam question) noexcept;
|
||||||
|
|
||||||
|
void setQuestion(ox::StringParam msg) noexcept;
|
||||||
|
|
||||||
void draw(Context &ctx) noexcept override;
|
void draw(Context &ctx) noexcept override;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@@ -109,7 +109,7 @@ class ItemMaker {
|
|||||||
return m_typeDisplayName;
|
return m_typeDisplayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool installTemplate(ox::UPtr<ItemTemplate> &tmpl) {
|
bool installTemplate(ox::UPtr<ItemTemplate> &&tmpl) {
|
||||||
if (typeName() == tmpl->typeName() &&
|
if (typeName() == tmpl->typeName() &&
|
||||||
typeVersion() <= tmpl->typeVersion()) {
|
typeVersion() <= tmpl->typeVersion()) {
|
||||||
m_templates.emplace_back(std::move(tmpl));
|
m_templates.emplace_back(std::move(tmpl));
|
||||||
@@ -120,10 +120,6 @@ class ItemMaker {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool installTemplate(ox::UPtr<ItemTemplate> &&tmpl) {
|
|
||||||
return installTemplate(tmpl);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr ox::Vector<ox::UPtr<ItemTemplate>> const&itemTemplates() const noexcept {
|
constexpr ox::Vector<ox::UPtr<ItemTemplate>> const&itemTemplates() const noexcept {
|
||||||
return m_templates;
|
return m_templates;
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
add_library(
|
add_library(
|
||||||
Studio
|
Studio
|
||||||
configio.cpp
|
configio.cpp
|
||||||
|
context.cpp
|
||||||
editor.cpp
|
editor.cpp
|
||||||
filepickerpopup.cpp
|
filepickerpopup.cpp
|
||||||
filetreemodel.cpp
|
filetreemodel.cpp
|
||||||
|
69
src/olympic/studio/modlib/src/context.cpp
Normal file
69
src/olympic/studio/modlib/src/context.cpp
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <studio/context.hpp>
|
||||||
|
|
||||||
|
namespace studio {
|
||||||
|
|
||||||
|
void navigateTo(Context &ctx, ox::StringParam filePath, ox::StringParam navArgs) noexcept {
|
||||||
|
ox::String path = std::move(filePath);
|
||||||
|
if (beginsWith(path, "uuid://")) {
|
||||||
|
auto const [p, err] = keel::uuidUrlToPath(keelCtx(ctx), path);
|
||||||
|
if (err) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
path = p;
|
||||||
|
}
|
||||||
|
ctx.navStack.resize(ctx.navIdx + 1);
|
||||||
|
ctx.navStack.emplace_back(ox::String{path}, ox::String{navArgs.view()});
|
||||||
|
try {
|
||||||
|
ctx.navCallback(std::move(path), std::move(navArgs));
|
||||||
|
} catch (std::exception const &e) {
|
||||||
|
oxErrf("Nav error: {}", e.what());
|
||||||
|
oxAssert(false, "Nav error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void navigateBack(Context &ctx) noexcept {
|
||||||
|
if (!ctx.navIdx) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
--ctx.navIdx;
|
||||||
|
while (ctx.navIdx < ctx.navStack.size() && ctx.navIdx) {
|
||||||
|
auto const i = ctx.navIdx - 1;
|
||||||
|
auto const &n = ctx.navStack[i];
|
||||||
|
if (!ctx.project->exists(n.filePath)) {
|
||||||
|
std::ignore = ctx.navStack.erase(i);
|
||||||
|
--ctx.navIdx;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
ctx.navCallback(n.filePath, n.navArgs);
|
||||||
|
} catch (std::exception const &e) {
|
||||||
|
oxAssert(ctx.navCallback != nullptr, "navCallback is null");
|
||||||
|
oxErrf("navigateForward failed: {}", e.what());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void navigateForward(Context &ctx) noexcept {
|
||||||
|
while (ctx.navIdx < ctx.navStack.size()) {
|
||||||
|
auto const &n = ctx.navStack[ctx.navIdx];
|
||||||
|
try {
|
||||||
|
ctx.navCallback(n.filePath, n.navArgs);
|
||||||
|
} catch (std::exception const &e) {
|
||||||
|
oxAssert(ctx.navCallback != nullptr, "navCallback is null");
|
||||||
|
oxErrf("navigateForward failed: {}", e.what());
|
||||||
|
}
|
||||||
|
if (!ctx.project->exists(n.filePath)) {
|
||||||
|
std::ignore = ctx.navStack.erase(ctx.navIdx);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
++ctx.navIdx;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -72,8 +72,10 @@ bool BaseEditor::exportable() const noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BaseEditor::setCutEnabled(bool v) {
|
void BaseEditor::setCutEnabled(bool v) {
|
||||||
m_cutEnabled = v;
|
if (m_cutEnabled != v) {
|
||||||
cutEnabledChanged.emit(v);
|
m_cutEnabled = v;
|
||||||
|
cutEnabledChanged.emit(v);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BaseEditor::cutEnabled() const noexcept {
|
bool BaseEditor::cutEnabled() const noexcept {
|
||||||
@@ -81,8 +83,10 @@ bool BaseEditor::cutEnabled() const noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BaseEditor::setCopyEnabled(bool v) {
|
void BaseEditor::setCopyEnabled(bool v) {
|
||||||
m_copyEnabled = v;
|
if (m_copyEnabled != v) {
|
||||||
copyEnabledChanged.emit(v);
|
m_copyEnabled = v;
|
||||||
|
copyEnabledChanged.emit(v);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BaseEditor::copyEnabled() const noexcept {
|
bool BaseEditor::copyEnabled() const noexcept {
|
||||||
@@ -90,8 +94,10 @@ bool BaseEditor::copyEnabled() const noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BaseEditor::setPasteEnabled(bool v) {
|
void BaseEditor::setPasteEnabled(bool v) {
|
||||||
m_pasteEnabled = v;
|
if (m_pasteEnabled != v) {
|
||||||
pasteEnabledChanged.emit(v);
|
m_pasteEnabled = v;
|
||||||
|
pasteEnabledChanged.emit(v);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BaseEditor::pasteEnabled() const noexcept {
|
bool BaseEditor::pasteEnabled() const noexcept {
|
||||||
|
@@ -8,6 +8,15 @@
|
|||||||
|
|
||||||
namespace studio {
|
namespace studio {
|
||||||
|
|
||||||
|
FilePickerPopup::Explorer::Explorer(keel::Context &kctx):
|
||||||
|
FileExplorer{kctx} {
|
||||||
|
}
|
||||||
|
|
||||||
|
void FilePickerPopup::Explorer::fileOpened(ox::StringViewCR) const noexcept {
|
||||||
|
opened = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
FilePickerPopup::FilePickerPopup(
|
FilePickerPopup::FilePickerPopup(
|
||||||
ox::StringParam name,
|
ox::StringParam name,
|
||||||
keel::Context &kctx,
|
keel::Context &kctx,
|
||||||
@@ -41,6 +50,7 @@ void FilePickerPopup::refresh() noexcept {
|
|||||||
void FilePickerPopup::open() noexcept {
|
void FilePickerPopup::open() noexcept {
|
||||||
refresh();
|
refresh();
|
||||||
m_open = true;
|
m_open = true;
|
||||||
|
m_explorer.opened = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilePickerPopup::close() noexcept {
|
void FilePickerPopup::close() noexcept {
|
||||||
@@ -60,16 +70,22 @@ ox::Optional<ox::String> FilePickerPopup::draw(Context &ctx) noexcept {
|
|||||||
if (ig::BeginPopup(ctx.tctx, m_name, m_open, {380, 340})) {
|
if (ig::BeginPopup(ctx.tctx, m_name, m_open, {380, 340})) {
|
||||||
auto const vp = ImGui::GetContentRegionAvail();
|
auto const vp = ImGui::GetContentRegionAvail();
|
||||||
m_explorer.draw(ctx, {vp.x, vp.y - 30});
|
m_explorer.draw(ctx, {vp.x, vp.y - 30});
|
||||||
if (ig::PopupControlsOkCancel(m_open) == ig::PopupResponse::OK) {
|
if (ig::PopupControlsOkCancel(m_open) == ig::PopupResponse::OK || m_explorer.opened) {
|
||||||
auto p = m_explorer.selectedPath();
|
out = handlePick();
|
||||||
if (p) {
|
|
||||||
out.emplace(*p);
|
|
||||||
}
|
|
||||||
close();
|
|
||||||
}
|
}
|
||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ox::Optional<ox::String> FilePickerPopup::handlePick() noexcept {
|
||||||
|
ox::Optional<ox::String> out;
|
||||||
|
auto p = m_explorer.selectedPath();
|
||||||
|
if (p) {
|
||||||
|
out.emplace(*p);
|
||||||
|
}
|
||||||
|
close();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
namespace studio::ig {
|
namespace studio::ig {
|
||||||
|
|
||||||
ChildStackItem::ChildStackItem(ox::CStringViewCR id, ImVec2 const&sz) noexcept {
|
ChildStackItem::ChildStackItem(ox::CStringViewCR id, ImVec2 const &sz) noexcept {
|
||||||
ImGui::BeginChild(id.c_str(), sz);
|
ImGui::BeginChild(id.c_str(), sz);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ void centerNextWindow(turbine::Context &ctx) noexcept {
|
|||||||
ImGui::SetNextWindowPos(ImVec2(screenW / mod, screenH / mod), ImGuiCond_Always, ImVec2(0.5f, 0.5f));
|
ImGui::SetNextWindowPos(ImVec2(screenW / mod, screenH / mod), ImGuiCond_Always, ImVec2(0.5f, 0.5f));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PushButton(ox::CStringViewCR lbl, ImVec2 const&btnSz) noexcept {
|
bool PushButton(ox::CStringViewCR lbl, ImVec2 const &btnSz) noexcept {
|
||||||
return ImGui::Button(lbl.c_str(), btnSz);
|
return ImGui::Button(lbl.c_str(), btnSz);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +101,7 @@ PopupResponse PopupControlsOk(
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BeginPopup(turbine::Context &ctx, ox::CStringViewCR popupName, bool &show, ImVec2 const&sz) {
|
bool BeginPopup(turbine::Context &ctx, ox::CStringViewCR popupName, bool &show, ImVec2 const &sz) {
|
||||||
constexpr auto modalFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
|
constexpr auto modalFlags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
|
||||||
centerNextWindow(ctx);
|
centerNextWindow(ctx);
|
||||||
ImGui::OpenPopup(popupName.c_str());
|
ImGui::OpenPopup(popupName.c_str());
|
||||||
@@ -149,7 +149,7 @@ bool ComboBox(
|
|||||||
|
|
||||||
bool ComboBox(
|
bool ComboBox(
|
||||||
ox::CStringViewCR lbl,
|
ox::CStringViewCR lbl,
|
||||||
std::function<ox::CStringView(size_t)> const&f,
|
std::function<ox::CStringView(size_t)> const &f,
|
||||||
size_t strCnt,
|
size_t strCnt,
|
||||||
size_t &selectedIdx) noexcept {
|
size_t &selectedIdx) noexcept {
|
||||||
bool out{};
|
bool out{};
|
||||||
@@ -172,16 +172,16 @@ bool FileComboBox(
|
|||||||
Context &sctx,
|
Context &sctx,
|
||||||
ox::StringViewCR fileExt,
|
ox::StringViewCR fileExt,
|
||||||
size_t &selectedIdx) noexcept {
|
size_t &selectedIdx) noexcept {
|
||||||
auto const&list = sctx.project->fileList(fileExt);
|
auto const &list = sctx.project->fileList(fileExt);
|
||||||
return ComboBox(lbl, list, selectedIdx);
|
return ComboBox(lbl, list, selectedIdx);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ListBox(
|
bool ListBox(
|
||||||
ox::CStringViewCR name,
|
ox::CStringViewCR name,
|
||||||
std::function<ox::CStringView(size_t)> const&f,
|
std::function<ox::CStringView(size_t)> const &f,
|
||||||
size_t const strCnt,
|
size_t const strCnt,
|
||||||
size_t &selIdx,
|
size_t &selIdx,
|
||||||
ImVec2 const&sz) noexcept {
|
ImVec2 const &sz) noexcept {
|
||||||
auto out = false;
|
auto out = false;
|
||||||
if (ImGui::BeginListBox(name.c_str(), sz)) {
|
if (ImGui::BeginListBox(name.c_str(), sz)) {
|
||||||
for (size_t i = 0; i < strCnt; ++i) {
|
for (size_t i = 0; i < strCnt; ++i) {
|
||||||
@@ -199,13 +199,13 @@ bool ListBox(
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ListBox(ox::CStringViewCR name, ox::SpanView<ox::String> const&list, size_t &selIdx) noexcept {
|
bool ListBox(ox::CStringViewCR name, ox::SpanView<ox::String> const &list, size_t &selIdx) noexcept {
|
||||||
return ListBox(name, [list](size_t i) -> ox::CStringView {
|
return ListBox(name, [list](size_t i) -> ox::CStringView {
|
||||||
return list[i];
|
return list[i];
|
||||||
}, list.size(), selIdx);
|
}, list.size(), selIdx);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ListBox(ox::CStringViewCR name, ox::SpanView<ox::CStringView> const&list, size_t &selIdx) noexcept {
|
bool ListBox(ox::CStringViewCR name, ox::SpanView<ox::CStringView> const &list, size_t &selIdx) noexcept {
|
||||||
return ListBox(name, [list](size_t i) -> ox::CStringView {
|
return ListBox(name, [list](size_t i) -> ox::CStringView {
|
||||||
return list[i];
|
return list[i];
|
||||||
}, list.size(), selIdx);
|
}, list.size(), selIdx);
|
||||||
@@ -216,7 +216,7 @@ FilePicker::FilePicker(
|
|||||||
Context &sctx,
|
Context &sctx,
|
||||||
ox::StringParam title,
|
ox::StringParam title,
|
||||||
ox::StringParam fileExt,
|
ox::StringParam fileExt,
|
||||||
ImVec2 const&size) noexcept:
|
ImVec2 const &size) noexcept:
|
||||||
m_sctx(sctx),
|
m_sctx(sctx),
|
||||||
m_title(std::move(title)),
|
m_title(std::move(title)),
|
||||||
m_fileExt(std::move(fileExt)),
|
m_fileExt(std::move(fileExt)),
|
||||||
@@ -230,7 +230,7 @@ void FilePicker::draw() noexcept {
|
|||||||
auto constexpr popupSz = ImVec2{450.f, 0};
|
auto constexpr popupSz = ImVec2{450.f, 0};
|
||||||
IDStackItem const idStackItem(m_title);
|
IDStackItem const idStackItem(m_title);
|
||||||
if (BeginPopup(m_sctx.tctx, m_title, m_show, popupSz)) {
|
if (BeginPopup(m_sctx.tctx, m_title, m_show, popupSz)) {
|
||||||
auto const&list = m_sctx.project->fileList(m_fileExt);
|
auto const &list = m_sctx.project->fileList(m_fileExt);
|
||||||
size_t selIdx{};
|
size_t selIdx{};
|
||||||
ComboBox(m_title, list, selIdx);
|
ComboBox(m_title, list, selIdx);
|
||||||
if (PopupControlsOkCancel(popupSz.x, m_show) == ig::PopupResponse::OK) {
|
if (PopupControlsOkCancel(popupSz.x, m_show) == ig::PopupResponse::OK) {
|
||||||
@@ -267,6 +267,10 @@ QuestionPopup::QuestionPopup(ox::StringParam title, ox::StringParam question) no
|
|||||||
m_question{std::move(question)} {
|
m_question{std::move(question)} {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QuestionPopup::setQuestion(ox::StringParam msg) noexcept {
|
||||||
|
m_question = std::move(msg);
|
||||||
|
}
|
||||||
|
|
||||||
void QuestionPopup::draw(Context &ctx) noexcept {
|
void QuestionPopup::draw(Context &ctx) noexcept {
|
||||||
switch (m_stage) {
|
switch (m_stage) {
|
||||||
case Stage::Closed:
|
case Stage::Closed:
|
||||||
|
@@ -101,8 +101,8 @@ ox::Result<ox::FileStat> Project::stat(ox::StringViewCR path) const noexcept {
|
|||||||
ox::Error Project::copyItem(ox::StringViewCR src, ox::StringViewCR dest) noexcept {
|
ox::Error Project::copyItem(ox::StringViewCR src, ox::StringViewCR dest) noexcept {
|
||||||
OX_REQUIRE_M(buff, loadBuff(src));
|
OX_REQUIRE_M(buff, loadBuff(src));
|
||||||
OX_REQUIRE(id, keel::regenerateUuidHeader(buff));
|
OX_REQUIRE(id, keel::regenerateUuidHeader(buff));
|
||||||
OX_RETURN_ERROR(writeBuff(dest, buff));
|
|
||||||
createUuidMapping(m_kctx, dest, id);
|
createUuidMapping(m_kctx, dest, id);
|
||||||
|
OX_RETURN_ERROR(writeBuff(dest, ox::BufferView{buff} + keel::K1HdrSz));
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -79,11 +79,14 @@ enum Key {
|
|||||||
};
|
};
|
||||||
|
|
||||||
using KeyEventHandler = void(*)(Context&, Key, bool);
|
using KeyEventHandler = void(*)(Context&, Key, bool);
|
||||||
|
using MouseButtonEventHandler = void(*)(Context&, int btn, bool);
|
||||||
|
|
||||||
void setKeyEventHandler(Context &ctx, KeyEventHandler h) noexcept;
|
void setKeyEventHandler(Context &ctx, KeyEventHandler h) noexcept;
|
||||||
|
|
||||||
|
void setMouseButtonEventHandler(Context &ctx, MouseButtonEventHandler h) noexcept;
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
KeyEventHandler keyEventHandler(Context &ctx) noexcept;
|
KeyEventHandler keyEventHandler(Context const &ctx) noexcept;
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
bool buttonDown(Context const&ctx, Key) noexcept;
|
bool buttonDown(Context const&ctx, Key) noexcept;
|
||||||
|
@@ -133,7 +133,9 @@ void setKeyEventHandler(Context &ctx, KeyEventHandler h) noexcept {
|
|||||||
ctx.keyEventHandler = h;
|
ctx.keyEventHandler = h;
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyEventHandler keyEventHandler(Context &ctx) noexcept {
|
void setMouseButtonEventHandler(Context&, MouseButtonEventHandler) noexcept {}
|
||||||
|
|
||||||
|
KeyEventHandler keyEventHandler(Context const &ctx) noexcept {
|
||||||
return ctx.keyEventHandler;
|
return ctx.keyEventHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -16,6 +16,7 @@ class Context {
|
|||||||
UpdateHandler updateHandler = [](Context&) -> int {return -1;};
|
UpdateHandler updateHandler = [](Context&) -> int {return -1;};
|
||||||
keel::Context keelCtx;
|
keel::Context keelCtx;
|
||||||
KeyEventHandler keyEventHandler = nullptr;
|
KeyEventHandler keyEventHandler = nullptr;
|
||||||
|
MouseButtonEventHandler mouseButtonEventHandler = nullptr;
|
||||||
ox::AnyPtr applicationData;
|
ox::AnyPtr applicationData;
|
||||||
|
|
||||||
// GLFW impl data ////////////////////////////////////////////////////////
|
// GLFW impl data ////////////////////////////////////////////////////////
|
||||||
|
@@ -299,9 +299,14 @@ static void handleKeyPress(Context &ctx, int const key, bool const down) noexcep
|
|||||||
static void handleGlfwCursorPosEvent(GLFWwindow*, double, double) noexcept {
|
static void handleGlfwCursorPosEvent(GLFWwindow*, double, double) noexcept {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handleGlfwMouseButtonEvent(GLFWwindow *window, int, int, int) noexcept {
|
static void handleGlfwMouseButtonEvent(
|
||||||
|
GLFWwindow *window,
|
||||||
|
int const btn,
|
||||||
|
int const action,
|
||||||
|
int) noexcept {
|
||||||
auto &ctx = *static_cast<Context*>(glfwGetWindowUserPointer(window));
|
auto &ctx = *static_cast<Context*>(glfwGetWindowUserPointer(window));
|
||||||
setMandatoryRefreshPeriod(ctx, ticksMs(ctx) + config::MandatoryRefreshPeriod);
|
setMandatoryRefreshPeriod(ctx, ticksMs(ctx) + config::MandatoryRefreshPeriod);
|
||||||
|
ctx.mouseButtonEventHandler(ctx, btn, action == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handleGlfwKeyEvent(GLFWwindow *window, int const key, int, int const action, int) noexcept {
|
static void handleGlfwKeyEvent(GLFWwindow *window, int const key, int, int const action, int) noexcept {
|
||||||
@@ -339,7 +344,8 @@ ox::Result<ox::UPtr<Context>> init(
|
|||||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
|
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
|
||||||
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
|
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
|
||||||
constexpr auto Scale = 5;
|
constexpr auto Scale = 5;
|
||||||
ctx->window = glfwCreateWindow(240 * Scale, 160 * Scale, ctx->keelCtx.appName.c_str(), nullptr, nullptr);
|
ctx->window = glfwCreateWindow(
|
||||||
|
240 * Scale, 160 * Scale, ctx->keelCtx.appName.c_str(), nullptr, nullptr);
|
||||||
//ctx.window = glfwCreateWindow(876, 743, ctx.keelCtx.appName.c_str(), nullptr, nullptr);
|
//ctx.window = glfwCreateWindow(876, 743, ctx.keelCtx.appName.c_str(), nullptr, nullptr);
|
||||||
if (ctx->window == nullptr) {
|
if (ctx->window == nullptr) {
|
||||||
return ox::Error(1, "Could not open GLFW window");
|
return ox::Error(1, "Could not open GLFW window");
|
||||||
@@ -402,14 +408,14 @@ ox::Error run(Context &ctx) noexcept {
|
|||||||
glfwPollEvents();
|
glfwPollEvents();
|
||||||
auto const ticks = ticksMs(ctx);
|
auto const ticks = ticksMs(ctx);
|
||||||
if (ctx.wakeupTime <= ticks) {
|
if (ctx.wakeupTime <= ticks) {
|
||||||
auto const st = ctx.updateHandler(ctx);
|
auto const st = ctx.updateHandler(ctx);
|
||||||
if (st >= 0) {
|
if (st >= 0) {
|
||||||
ctx.wakeupTime = ticks + static_cast<unsigned>(st);
|
ctx.wakeupTime = ticks + static_cast<unsigned>(st);
|
||||||
sleepTime = static_cast<uint64_t>(st);
|
sleepTime = static_cast<uint64_t>(st);
|
||||||
} else {
|
} else {
|
||||||
ctx.wakeupTime = ~uint64_t{0};
|
ctx.wakeupTime = ~uint64_t{0};
|
||||||
sleepTime = ctx.wakeupTime - ticks;
|
sleepTime = ctx.wakeupTime - ticks;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sleepTime = ctx.wakeupTime - ticks;
|
sleepTime = ctx.wakeupTime - ticks;
|
||||||
}
|
}
|
||||||
@@ -447,15 +453,19 @@ void setShutdownHandler(Context &ctx, ShutdownHandler const handler) noexcept {
|
|||||||
ctx.shutdownHandler = handler;
|
ctx.shutdownHandler = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setUpdateHandler(Context &ctx, UpdateHandler h) noexcept {
|
void setUpdateHandler(Context &ctx, UpdateHandler const h) noexcept {
|
||||||
ctx.updateHandler = h;
|
ctx.updateHandler = h;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setKeyEventHandler(Context &ctx, KeyEventHandler h) noexcept {
|
void setKeyEventHandler(Context &ctx, KeyEventHandler const h) noexcept {
|
||||||
ctx.keyEventHandler = h;
|
ctx.keyEventHandler = h;
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyEventHandler keyEventHandler(Context &ctx) noexcept {
|
void setMouseButtonEventHandler(Context &ctx, MouseButtonEventHandler const h) noexcept {
|
||||||
|
ctx.mouseButtonEventHandler = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyEventHandler keyEventHandler(Context const &ctx) noexcept {
|
||||||
return ctx.keyEventHandler;
|
return ctx.keyEventHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user