Skip to content

Commit 861826e

Browse files
authored
Add File > Open... and browse modal (#59)
* WIP: Add env; Browse / Open File dialog for .gltf * Add BrowseFile, move logic from main * Interactive current path in File > Open - Show drop-down listing parent paths recursively. - Dynamically trim long paths to the form `.../current/path`. - Move `Popup` ownership to user of `BrowseFile`. - Fix window sizing.
1 parent 458feff commit 861826e

File tree

14 files changed

+308
-57
lines changed

14 files changed

+308
-57
lines changed

lib/engine/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ target_sources(${PROJECT_NAME} PRIVATE
4040
include/${target_prefix}/engine/engine.hpp
4141
include/${target_prefix}/engine/scene_renderer.hpp
4242

43+
include/${target_prefix}/engine/editor/browse_file.hpp
4344
include/${target_prefix}/engine/editor/common.hpp
4445
include/${target_prefix}/engine/editor/inspector.hpp
4546
include/${target_prefix}/engine/editor/log.hpp
@@ -48,6 +49,7 @@ target_sources(${PROJECT_NAME} PRIVATE
4849
src/scene_renderer.cpp
4950
src/engine.cpp
5051

52+
src/editor/browse_file.cpp
5153
src/editor/common.cpp
5254
src/editor/inspector.cpp
5355
src/editor/log.cpp
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#pragma once
2+
#include <facade/engine/editor/common.hpp>
3+
#include <facade/util/env.hpp>
4+
5+
namespace facade::editor {
6+
struct BrowseFile {
7+
env::DirEntries& out_entries;
8+
env::MatchList extensions{};
9+
float parent_indent{5.0f};
10+
11+
std::string operator()(NotClosed<Popup> popup, std::string& out_path);
12+
};
13+
} // namespace facade::editor

lib/engine/include/facade/engine/editor/common.hpp

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,20 @@ struct NotClosed {
3535
NotClosed(NotClosed<Derived>) {}
3636
};
3737

38+
class Canvas : public Openable {
39+
protected:
40+
using Openable::Openable;
41+
};
42+
3843
///
3944
/// \brief RAII Dear ImGui window
4045
///
41-
class Window : public Openable {
46+
class Window : public Canvas {
4247
public:
4348
class Menu;
4449

4550
explicit Window(char const* label, bool* open_if = {}, int flags = {});
46-
Window(NotClosed<Window> parent, char const* label, glm::vec2 size = {}, Bool border = {}, int flags = {});
51+
Window(NotClosed<Canvas> parent, char const* label, glm::vec2 size = {}, Bool border = {}, int flags = {});
4752
~Window();
4853

4954
private:
@@ -83,7 +88,7 @@ class Menu : public Openable {
8388
///
8489
class Window::Menu : public MenuBar {
8590
public:
86-
explicit Menu(NotClosed<Window>);
91+
explicit Menu(NotClosed<Canvas>);
8792
~Menu();
8893
};
8994

@@ -99,24 +104,24 @@ class MainMenu : public MenuBar {
99104
///
100105
/// \brief RAII Dear ImGui Popup
101106
///
102-
class Popup : public Openable {
107+
class Popup : public Canvas {
103108
public:
104-
explicit Popup(char const* id, Bool centered = {}, int flags = {}) : Popup(id, {}, centered, flags) {}
109+
explicit Popup(char const* id, Bool centered = {}, int flags = {}) : Popup(id, {}, {}, centered, flags) {}
105110
~Popup();
106111

107112
static void open(char const* id);
108113
static void close_current();
109114

110115
protected:
111-
explicit Popup(char const* id, Bool modal, Bool centered, int flags);
116+
explicit Popup(char const* id, Bool modal, Bool closeable, Bool centered, int flags);
112117
};
113118

114119
///
115120
/// \brief RAII Dear ImGui PopupModal
116121
///
117122
class Modal : public Popup {
118123
public:
119-
explicit Modal(char const* id, Bool centered = {true}, int flags = {}) : Popup(id, {true}, centered, flags) {}
124+
explicit Modal(char const* id, Bool centered = {true}, Bool closeable = {true}, int flags = {}) : Popup(id, {true}, closeable, centered, flags) {}
120125
};
121126

122127
///

lib/engine/src/editor/browse_file.cpp

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#include <fmt/format.h>
2+
#include <imgui.h>
3+
#include <facade/engine/editor/browse_file.hpp>
4+
#include <facade/util/env.hpp>
5+
#include <filesystem>
6+
7+
namespace facade::editor {
8+
namespace fs = std::filesystem;
9+
10+
namespace {
11+
constexpr bool drop_leading_dir(std::string_view& out) {
12+
if (!out.empty() && out[0] == '/') { out = out.substr(1); }
13+
if (auto const i = out.find_first_of('/'); i != std::string_view::npos) {
14+
out = out.substr(i + 1);
15+
return true;
16+
}
17+
return false;
18+
}
19+
20+
std::string truncate(std::string_view in, std::size_t limit) {
21+
if (in.size() <= limit) { return std::string{in}; }
22+
while (in.size() + 4 > limit && drop_leading_dir(in))
23+
;
24+
return fmt::format(".../{}", in);
25+
}
26+
} // namespace
27+
28+
std::string BrowseFile::operator()(NotClosed<Popup> popup, std::string& out_path) {
29+
auto const fs_path = [&] {
30+
auto ret = fs::absolute(out_path);
31+
if (!fs::is_directory(ret)) {
32+
ret = fs::current_path();
33+
out_path = ret.generic_string();
34+
}
35+
return ret;
36+
}();
37+
auto label = out_path;
38+
auto const text_size = ImGui::CalcTextSize(label.c_str());
39+
ImGui::PushItemWidth(-50.0f);
40+
if (auto const ratio = (ImGui::GetWindowWidth() - 100.0f) / text_size.x; ratio > 0.0f) {
41+
auto const limit = static_cast<std::size_t>(ratio * static_cast<float>(label.size()));
42+
label = truncate(label, limit);
43+
}
44+
if (ImGui::BeginCombo("Path", label.c_str())) {
45+
auto item_width = 0.0f;
46+
for (auto p = fs_path; !p.empty(); p = p.parent_path()) {
47+
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + item_width);
48+
item_width += parent_indent;
49+
auto name = p.filename().generic_string();
50+
name += "/";
51+
if (ImGui::Selectable(name.c_str(), false)) {
52+
out_path = p.generic_string();
53+
break;
54+
}
55+
if (p.parent_path() == p) { break; }
56+
}
57+
ImGui::EndCombo();
58+
}
59+
if (fs_path.has_parent_path() && ImGui::Button("Up")) { out_path = fs_path.parent_path().generic_string(); }
60+
if (auto documents = env::documents_path(); !documents.empty()) {
61+
ImGui::SameLine();
62+
if (ImGui::Button("Documents")) { out_path = std::move(documents); }
63+
}
64+
if (auto downloads = env::downloads_path(); !downloads.empty()) {
65+
ImGui::SameLine();
66+
if (ImGui::Button("Downloads")) { out_path = std::move(downloads); }
67+
}
68+
69+
ImGui::Separator();
70+
if (auto window = editor::Window{popup, "File Tree"}) {
71+
env::GetDirEntries{.extensions = extensions}(out_entries, out_path.c_str());
72+
if (fs_path.has_parent_path() && ImGui::Selectable("..", false, ImGuiSelectableFlags_DontClosePopups)) {
73+
out_path = fs_path.parent_path().generic_string();
74+
}
75+
for (auto const& dir : out_entries.dirs) {
76+
auto filename = env::to_filename(dir);
77+
filename += "/";
78+
if (ImGui::Selectable(filename.c_str(), false, ImGuiSelectableFlags_DontClosePopups)) { out_path = dir; }
79+
}
80+
for (auto const& file : out_entries.files) {
81+
if (ImGui::Selectable(env::to_filename(file).c_str(), false, ImGuiSelectableFlags_DontClosePopups)) { return file; }
82+
}
83+
}
84+
85+
return {};
86+
}
87+
} // namespace facade::editor

lib/engine/src/editor/common.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
namespace facade::editor {
66
Openable::Openable(bool is_open) : m_open(is_open) {}
77

8-
Window::Window(char const* label, bool* open_if, int flags) : Openable(ImGui::Begin(label, open_if, flags)) {}
8+
Window::Window(char const* label, bool* open_if, int flags) : Canvas(ImGui::Begin(label, open_if, flags)) {}
99

10-
Window::Window(NotClosed<Window>, char const* label, glm::vec2 size, Bool border, int flags)
11-
: Openable(ImGui::BeginChild(label, {size.x, size.y}, border.value, flags)), m_child(true) {}
10+
Window::Window(NotClosed<Canvas>, char const* label, glm::vec2 size, Bool border, int flags)
11+
: Canvas(ImGui::BeginChild(label, {size.x, size.y}, border.value, flags)), m_child(true) {}
1212

1313
// ImGui windows requires End() even if Begin() returned false
1414
Window::~Window() {
@@ -30,7 +30,7 @@ bool TreeNode::leaf(char const* label, int flags) {
3030
return false;
3131
}
3232

33-
Window::Menu::Menu(NotClosed<Window>) : MenuBar(ImGui::BeginMenuBar()) {}
33+
Window::Menu::Menu(NotClosed<Canvas>) : MenuBar(ImGui::BeginMenuBar()) {}
3434

3535
Window::Menu::~Menu() {
3636
if (m_open) { ImGui::EndMenuBar(); }
@@ -42,13 +42,13 @@ MainMenu::~MainMenu() {
4242
if (m_open) { ImGui::EndMainMenuBar(); }
4343
}
4444

45-
Popup::Popup(char const* id, Bool modal, Bool centered, int flags) {
45+
Popup::Popup(char const* id, Bool modal, Bool closeable, Bool centered, int flags) {
4646
if (centered) {
4747
auto const center = ImGui::GetMainViewport()->GetCenter();
4848
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2{0.5f, 0.5f});
4949
}
5050
if (modal) {
51-
m_open = ImGui::BeginPopupModal(id, {}, flags);
51+
m_open = ImGui::BeginPopupModal(id, &closeable.value, flags);
5252
} else {
5353
m_open = ImGui::BeginPopup(id, flags);
5454
}

lib/engine/src/engine.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <facade/glfw/glfw_wsi.hpp>
99
#include <facade/render/renderer.hpp>
1010
#include <facade/util/data_provider.hpp>
11+
#include <facade/util/env.hpp>
1112
#include <facade/util/error.hpp>
1213
#include <facade/util/logger.hpp>
1314
#include <facade/vk/cmd.hpp>
@@ -276,7 +277,7 @@ bool Engine::load_async(std::string gltf_json_path, UniqueTask<void()> on_loaded
276277
}
277278

278279
// ready to start loading
279-
logger::info("[Engine] Loading GLTF [{}]...", State::to_filename(gltf_json_path));
280+
logger::info("[Engine] Loading GLTF [{}]...", env::to_filename(gltf_json_path));
280281
// populate load request
281282
m_impl->load.callback = std::move(on_loaded);
282283
m_impl->load.request.path = std::move(gltf_json_path);
@@ -321,7 +322,7 @@ void Engine::update_load_request() {
321322
auto const duration = time::since_start() - m_impl->load.request.start_time;
322323
// unlock mutex to prevent possible deadlock (eg callback calls load_gltf again)
323324
lock.unlock();
324-
logger::info("...GLTF [{}] loaded in [{:.2f}s]", State::to_filename(path), duration);
325+
logger::info("...GLTF [{}] loaded in [{:.2f}s]", env::to_filename(path), duration);
325326
// invoke callback
326327
if (callback) { callback(); }
327328
}

lib/glfw/include/facade/glfw/glfw.hpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,6 @@ struct Glfw::State {
6868
/// \brief Delta-time between polls.
6969
///
7070
float dt{};
71-
72-
///
73-
/// \brief Obtain the filename of a file drop path.
74-
/// \param path Path to extract filename from
75-
///
76-
/// Passed path must be in generic form.
77-
///
78-
static std::string to_filename(std::string_view path);
7971
};
8072

8173
///

lib/glfw/src/glfw.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,6 @@ void Glfw::poll_events() {
5555

5656
void Glfw::reset_dt() { g_states.dt = {}; }
5757

58-
std::string Glfw::State::to_filename(std::string_view path) {
59-
if (auto const i = path.find_last_of('/'); i != std::string_view::npos) { path = path.substr(i + 1); }
60-
return std::string{path};
61-
}
62-
6358
auto Glfw::Window::make() -> UniqueWin {
6459
auto ret = Window{};
6560
ret.glfw = get_or_make_glfw();

lib/util/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ target_sources(${PROJECT_NAME} PRIVATE
7070
include/${target_prefix}/util/data_provider.hpp
7171
include/${target_prefix}/util/enum_array.hpp
7272
include/${target_prefix}/util/enumerate.hpp
73+
include/${target_prefix}/util/env.hpp
7374
include/${target_prefix}/util/error.hpp
7475
include/${target_prefix}/util/fixed_string.hpp
7576
include/${target_prefix}/util/flex_array.hpp
@@ -89,6 +90,7 @@ target_sources(${PROJECT_NAME} PRIVATE
8990
include/${target_prefix}/util/visitor.hpp
9091

9192
src/data_provider.cpp
93+
src/env.cpp
9294
src/image.cpp
9395
src/logger.cpp
9496
src/rgb.cpp

lib/util/include/facade/util/env.hpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#pragma once
2+
#include <span>
3+
#include <string>
4+
#include <vector>
5+
6+
namespace facade::env {
7+
using MatchList = std::span<std::string_view const>;
8+
9+
struct DirEntries {
10+
std::vector<std::string> dirs{};
11+
std::vector<std::string> files{};
12+
};
13+
14+
struct GetDirEntries {
15+
MatchList extensions{};
16+
bool sort{true};
17+
bool show_hidden{false};
18+
19+
bool operator()(DirEntries& out_entries, char const* path) const;
20+
};
21+
22+
///
23+
/// \brief Obtain the filename of a file drop path.
24+
/// \param path Path to extract filename from
25+
/// \returns Filename
26+
///
27+
std::string to_filename(std::string_view path);
28+
29+
///
30+
/// \brief Obtain the user documents path.
31+
/// \returns Documents path in generic form
32+
///
33+
std::string documents_path();
34+
///
35+
/// \brief Obtain the user documents path.
36+
/// \returns Downloads path in generic form
37+
///
38+
std::string downloads_path();
39+
} // namespace facade::env

0 commit comments

Comments
 (0)