[studio] Add FilePickerPopup
This commit is contained in:
parent
e8a0ce88c5
commit
e2f2a17315
@ -22,6 +22,10 @@ void ProjectExplorer::fileOpened(ox::StringViewCR path) const noexcept {
|
||||
fileChosen.emit(path);
|
||||
}
|
||||
|
||||
void ProjectExplorer::fileDeleted(ox::StringViewCR path) const noexcept {
|
||||
deleteItem.emit(path);
|
||||
}
|
||||
|
||||
void ProjectExplorer::fileContextMenu(ox::StringViewCR path) const noexcept {
|
||||
if (ImGui::BeginPopupContextItem("FileMenu", ImGuiPopupFlags_MouseButtonRight)) {
|
||||
if (ImGui::MenuItem("Delete")) {
|
||||
|
@ -28,6 +28,8 @@ class ProjectExplorer final: public FileExplorer {
|
||||
protected:
|
||||
void fileOpened(ox::StringViewCR path) const noexcept override;
|
||||
|
||||
void fileDeleted(ox::StringViewCR path) const noexcept override;
|
||||
|
||||
void fileContextMenu(ox::StringViewCR path) const noexcept override;
|
||||
|
||||
void dirContextMenu(ox::StringViewCR path) const noexcept override;
|
||||
|
38
src/olympic/studio/modlib/include/studio/filepickerpopup.hpp
Normal file
38
src/olympic/studio/modlib/include/studio/filepickerpopup.hpp
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "popup.hpp"
|
||||
#include "filetreemodel.hpp"
|
||||
|
||||
namespace studio {
|
||||
|
||||
class FilePickerPopup {
|
||||
|
||||
private:
|
||||
ox::String m_name;
|
||||
FileExplorer m_explorer;
|
||||
ox::Vector<ox::String> const m_fileExts;
|
||||
bool m_open{};
|
||||
|
||||
public:
|
||||
explicit FilePickerPopup(ox::StringParam name, keel::Context &kctx, ox::StringParam fileExt) noexcept;
|
||||
|
||||
explicit FilePickerPopup(ox::StringParam name, keel::Context &kctx, ox::Vector<ox::String> fileExts) noexcept;
|
||||
|
||||
void refresh() noexcept;
|
||||
|
||||
void open() noexcept;
|
||||
|
||||
void close() noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
bool isOpen() const noexcept;
|
||||
|
||||
ox::Optional<ox::String> draw(StudioContext &ctx) noexcept;
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -15,7 +15,7 @@
|
||||
|
||||
namespace studio {
|
||||
|
||||
constexpr void safeDelete(class FileTreeModel *m) noexcept;
|
||||
constexpr void safeDelete(class FileTreeModel const *m) noexcept;
|
||||
|
||||
class FileExplorer: public ox::SignalHandler {
|
||||
|
||||
@ -32,8 +32,6 @@ class FileExplorer: public ox::SignalHandler {
|
||||
m_kctx{kctx},
|
||||
m_fileDraggable{fileDraggable} {}
|
||||
|
||||
virtual ~FileExplorer() = default;
|
||||
|
||||
void draw(StudioContext &ctx, ImVec2 const &sz) const noexcept;
|
||||
|
||||
void setModel(ox::UPtr<FileTreeModel> &&model, bool selectRoot = false) noexcept;
|
||||
@ -45,6 +43,8 @@ class FileExplorer: public ox::SignalHandler {
|
||||
|
||||
virtual void fileOpened(ox::StringViewCR path) const noexcept;
|
||||
|
||||
virtual void fileDeleted(ox::StringViewCR path) const noexcept;
|
||||
|
||||
void drawFileContextMenu(ox::CStringViewCR path) const noexcept;
|
||||
|
||||
void drawDirContextMenu(ox::CStringViewCR path) const noexcept;
|
||||
@ -116,7 +116,7 @@ class FileTreeModel {
|
||||
|
||||
};
|
||||
|
||||
constexpr void safeDelete(FileTreeModel *m) noexcept {
|
||||
constexpr void safeDelete(FileTreeModel const *m) noexcept {
|
||||
delete m;
|
||||
}
|
||||
|
||||
|
@ -155,6 +155,23 @@ TextInput<ox::IString<MaxChars>> InputText(
|
||||
return out;
|
||||
}
|
||||
|
||||
template<size_t MaxChars = 50>
|
||||
TextInput<ox::IString<MaxChars>> InputTextWithHint(
|
||||
ox::CStringViewCR label,
|
||||
ox::CStringViewCR hint,
|
||||
ox::StringViewCR currentText,
|
||||
ImGuiInputTextFlags const flags = 0,
|
||||
ImGuiInputTextCallback const callback = nullptr,
|
||||
void *user_data = nullptr) noexcept {
|
||||
TextInput<ox::IString<MaxChars>> out = {.text = currentText};
|
||||
out.changed = ImGui::InputTextWithHint(
|
||||
label.c_str(), hint.c_str(), out.text.data(), MaxChars + 1, flags, callback, user_data);
|
||||
if (out.changed) {
|
||||
std::ignore = out.text.unsafeResize(ox::strlen(out.text.c_str()));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
template<size_t StrCap>
|
||||
bool InputText(
|
||||
ox::CStringViewCR label,
|
||||
|
@ -2,6 +2,7 @@ add_library(
|
||||
Studio
|
||||
configio.cpp
|
||||
editor.cpp
|
||||
filepickerpopup.cpp
|
||||
filetreemodel.cpp
|
||||
imguiutil.cpp
|
||||
module.cpp
|
||||
|
75
src/olympic/studio/modlib/src/filepickerpopup.cpp
Normal file
75
src/olympic/studio/modlib/src/filepickerpopup.cpp
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <studio/imguiutil.hpp>
|
||||
|
||||
#include <studio/filepickerpopup.hpp>
|
||||
|
||||
namespace studio {
|
||||
|
||||
FilePickerPopup::FilePickerPopup(
|
||||
ox::StringParam name,
|
||||
keel::Context &kctx,
|
||||
ox::StringParam fileExt) noexcept:
|
||||
m_name{std::move(name)},
|
||||
m_explorer{kctx},
|
||||
m_fileExts{std::move(fileExt)} {
|
||||
}
|
||||
|
||||
FilePickerPopup::FilePickerPopup(
|
||||
ox::StringParam name,
|
||||
keel::Context &kctx,
|
||||
ox::Vector<ox::String> fileExts) noexcept:
|
||||
m_name{std::move(name)},
|
||||
m_explorer{kctx},
|
||||
m_fileExts{std::move(fileExts)} {
|
||||
}
|
||||
|
||||
void FilePickerPopup::refresh() noexcept {
|
||||
m_explorer.setModel(buildFileTreeModel(
|
||||
m_explorer,
|
||||
[this](ox::StringViewCR path, ox::FileStat const &s) {
|
||||
auto const [ext, err] = fileExt(path);
|
||||
return
|
||||
s.fileType == ox::FileType::Directory ||
|
||||
(s.fileType == ox::FileType::NormalFile && !err && m_fileExts.contains(ext));
|
||||
},
|
||||
false).or_value(ox::UPtr<FileTreeModel>{}));
|
||||
}
|
||||
|
||||
void FilePickerPopup::open() noexcept {
|
||||
refresh();
|
||||
m_open = true;
|
||||
}
|
||||
|
||||
void FilePickerPopup::close() noexcept {
|
||||
m_explorer.setModel(ox::UPtr<FileTreeModel>{});
|
||||
m_open = false;
|
||||
}
|
||||
|
||||
bool FilePickerPopup::isOpen() const noexcept {
|
||||
return m_open;
|
||||
}
|
||||
|
||||
ox::Optional<ox::String> FilePickerPopup::draw(StudioContext &ctx) noexcept {
|
||||
ox::Optional<ox::String> out;
|
||||
if (!m_open) {
|
||||
return out;
|
||||
}
|
||||
if (ig::BeginPopup(ctx.tctx, m_name, m_open, {380, 340})) {
|
||||
auto const vp = ImGui::GetContentRegionAvail();
|
||||
m_explorer.draw(ctx, {vp.x, vp.y - 30});
|
||||
if (ig::PopupControlsOkCancel(m_open) == ig::PopupResponse::OK) {
|
||||
auto p = m_explorer.selectedPath();
|
||||
if (p) {
|
||||
out.emplace(*p);
|
||||
}
|
||||
close();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
@ -39,6 +39,8 @@ ox::Optional<ox::String> FileExplorer::selectedPath() const {
|
||||
|
||||
void FileExplorer::fileOpened(ox::StringViewCR) const noexcept {}
|
||||
|
||||
void FileExplorer::fileDeleted(ox::StringViewCR) const noexcept {}
|
||||
|
||||
void FileExplorer::drawFileContextMenu(ox::CStringViewCR path) const noexcept {
|
||||
ig::IDStackItem const idStackItem{path};
|
||||
fileContextMenu(path);
|
||||
@ -84,7 +86,7 @@ void FileTreeModel::draw(turbine::Context &tctx) const noexcept {
|
||||
auto const selected = m_explorer.selected(this) ? ImGuiTreeNodeFlags_Selected : 0;
|
||||
if (!m_children.empty()) {
|
||||
auto const nodeOpen = ImGui::TreeNodeEx(m_name.c_str(), dirFlags | selected);
|
||||
if (ImGui::IsItemClicked()) {
|
||||
if (ImGui::IsItemActivated() || ImGui::IsItemClicked(1)) {
|
||||
m_explorer.setSelectedNode(this);
|
||||
}
|
||||
ig::IDStackItem const idStackItem{m_name};
|
||||
@ -97,11 +99,15 @@ void FileTreeModel::draw(turbine::Context &tctx) const noexcept {
|
||||
}
|
||||
} else {
|
||||
if (ImGui::TreeNodeEx(m_name.c_str(), ImGuiTreeNodeFlags_Leaf | selected)) {
|
||||
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0)) {
|
||||
if (ImGui::IsItemActivated() || ImGui::IsItemClicked(1)) {
|
||||
m_explorer.setSelectedNode(this);
|
||||
}
|
||||
if ((ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0)) ||
|
||||
(ImGui::IsItemFocused() && ImGui::IsKeyPressed(ImGuiKey_Enter))) {
|
||||
m_explorer.fileOpened(m_fullPath);
|
||||
}
|
||||
if (ImGui::IsItemClicked()) {
|
||||
m_explorer.setSelectedNode(this);
|
||||
if (ImGui::IsItemFocused() && ImGui::IsKeyPressed(ImGuiKey_Delete)) {
|
||||
m_explorer.fileDeleted(m_fullPath);
|
||||
}
|
||||
m_explorer.drawFileContextMenu(m_fullPath);
|
||||
ImGui::TreePop();
|
||||
|
Loading…
x
Reference in New Issue
Block a user