338 lines
7.9 KiB
C++
338 lines
7.9 KiB
C++
/*
|
|
* Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <functional>
|
|
|
|
#include <imgui.h>
|
|
|
|
#include <ox/std/bit.hpp>
|
|
|
|
#include <turbine/context.hpp>
|
|
#include <studio/context.hpp>
|
|
|
|
namespace studio::ig {
|
|
|
|
inline constexpr auto BtnSz = ImVec2{52, 22};
|
|
|
|
|
|
constexpr ImTextureID toImTextureID(ox::Unsigned_c auto id) noexcept
|
|
requires(sizeof(id) <= sizeof(ox::Uint<sizeof(ImTextureID)*8>)) {
|
|
return std::bit_cast<ImTextureID>(ox::Uint<sizeof(ImTextureID)*8>{id});
|
|
}
|
|
|
|
template<typename T>
|
|
ox::Result<T> getDragDropPayload(ox::CStringViewCR name) noexcept {
|
|
auto const payload = ImGui::AcceptDragDropPayload(name.c_str());
|
|
if (!payload) {
|
|
return ox::Error(1, "No drag/drop payload");
|
|
}
|
|
return ox::readClaw<T>({
|
|
reinterpret_cast<char const*>(payload->Data),
|
|
static_cast<size_t>(payload->DataSize)});
|
|
}
|
|
|
|
template<typename T>
|
|
ox::Result<T> getDragDropPayload() noexcept {
|
|
auto const payload = ImGui::AcceptDragDropPayload(ox::ModelTypeName_v<T>);
|
|
if (!payload) {
|
|
return ox::Error(1, "No drag/drop payload");
|
|
}
|
|
return ox::readClaw<T>({
|
|
reinterpret_cast<char const*>(payload->Data),
|
|
static_cast<size_t>(payload->DataSize)});
|
|
}
|
|
|
|
ox::Error setDragDropPayload(ox::CStringViewCR name, auto const&obj) noexcept {
|
|
OX_REQUIRE(buff, ox::writeClaw(obj, ox::ClawFormat::Metal));
|
|
ImGui::SetDragDropPayload(name.c_str(), buff.data(), buff.size());
|
|
return {};
|
|
}
|
|
|
|
template<typename T>
|
|
ox::Error setDragDropPayload(T const&obj) noexcept {
|
|
OX_REQUIRE(buff, ox::writeClaw(obj, ox::ClawFormat::Metal));
|
|
ImGui::SetDragDropPayload(ox::ModelTypeName_v<T>, buff.data(), buff.size());
|
|
return {};
|
|
}
|
|
|
|
|
|
class DragDropSource {
|
|
private:
|
|
bool const m_active{};
|
|
public:
|
|
DragDropSource(ImGuiDragDropFlags const flags = 0) noexcept:
|
|
m_active(ImGui::BeginDragDropSource(flags)) {
|
|
}
|
|
~DragDropSource() noexcept {
|
|
if (m_active) {
|
|
ImGui::EndDragDropSource();
|
|
}
|
|
}
|
|
constexpr operator bool() const noexcept {
|
|
return m_active;
|
|
}
|
|
};
|
|
|
|
auto dragDropSource(auto const&cb, ImGuiDragDropFlags const flags = 0) noexcept {
|
|
if constexpr(ox::is_same_v<decltype(cb()), ox::Error>) {
|
|
if (ig::DragDropSource const tgt{flags}; tgt) [[unlikely]] {
|
|
return cb();
|
|
}
|
|
return ox::Error{};
|
|
} else {
|
|
if (ig::DragDropSource const tgt{flags}; tgt) [[unlikely]] {
|
|
cb();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
class DragDropTarget {
|
|
private:
|
|
bool const m_active{};
|
|
public:
|
|
DragDropTarget() noexcept:
|
|
m_active(ImGui::BeginDragDropTarget()) {
|
|
}
|
|
~DragDropTarget() noexcept {
|
|
if (m_active) {
|
|
ImGui::EndDragDropTarget();
|
|
}
|
|
}
|
|
constexpr operator bool() const noexcept {
|
|
return m_active;
|
|
}
|
|
};
|
|
|
|
auto dragDropTarget(auto const&cb) noexcept {
|
|
if constexpr(ox::is_same_v<decltype(cb()), ox::Error>) {
|
|
if (ig::DragDropTarget const tgt; tgt) [[unlikely]] {
|
|
return cb();
|
|
}
|
|
return ox::Error{};
|
|
} else {
|
|
if (ig::DragDropTarget const tgt; tgt) [[unlikely]] {
|
|
cb();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
class ChildStackItem {
|
|
public:
|
|
explicit ChildStackItem(ox::CStringViewCR id, ImVec2 const&sz = {}) noexcept;
|
|
~ChildStackItem() noexcept;
|
|
};
|
|
|
|
class IDStackItem {
|
|
public:
|
|
explicit IDStackItem(int id) noexcept;
|
|
explicit IDStackItem(const char *id) noexcept;
|
|
explicit IDStackItem(ox::CStringViewCR id) noexcept;
|
|
~IDStackItem() noexcept;
|
|
};
|
|
|
|
class IndentStackItem {
|
|
private:
|
|
float m_indent{};
|
|
public:
|
|
explicit IndentStackItem(float id) noexcept;
|
|
~IndentStackItem() noexcept;
|
|
};
|
|
|
|
void centerNextWindow(turbine::Context &ctx) noexcept;
|
|
|
|
bool PushButton(ox::CStringViewCR lbl, ImVec2 const&btnSz = BtnSz) noexcept;
|
|
|
|
template<typename Str>
|
|
struct TextInput {
|
|
bool changed{};
|
|
Str text;
|
|
explicit constexpr operator ox::String() const noexcept { return ox::String{text}; }
|
|
constexpr operator ox::CStringView() const noexcept { return text; }
|
|
constexpr operator ox::StringView() const noexcept { return text; }
|
|
constexpr operator bool() const noexcept { return changed; }
|
|
};
|
|
|
|
template<size_t MaxChars = 50>
|
|
TextInput<ox::IString<MaxChars>> InputText(
|
|
ox::CStringViewCR label,
|
|
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::InputText(
|
|
label.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 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,
|
|
ox::IString<StrCap> &text,
|
|
ImGuiInputTextFlags const flags = 0,
|
|
ImGuiInputTextCallback const callback = nullptr,
|
|
void *user_data = nullptr) noexcept {
|
|
auto const out = ImGui::InputText(
|
|
label.c_str(), text.data(), StrCap + 1, flags, callback, user_data);
|
|
if (out) {
|
|
std::ignore = text.unsafeResize(ox::strlen(text.c_str()));
|
|
}
|
|
return out;
|
|
}
|
|
|
|
enum class PopupResponse {
|
|
None,
|
|
OK,
|
|
Cancel,
|
|
};
|
|
|
|
PopupResponse PopupControlsOkCancel(
|
|
float popupWidth,
|
|
bool &popupOpen,
|
|
ox::CStringViewCR ok = "OK",
|
|
ox::CStringViewCR cancel = "Cancel");
|
|
|
|
PopupResponse PopupControlsOkCancel(
|
|
bool &popupOpen,
|
|
ox::CStringViewCR ok = "OK",
|
|
ox::CStringViewCR cancel = "Cancel");
|
|
|
|
[[nodiscard]]
|
|
bool BeginPopup(turbine::Context &ctx, ox::CStringViewCR popupName, bool &show, ImVec2 const&sz = {285, 0});
|
|
|
|
/**
|
|
*
|
|
* @param lbl
|
|
* @param list
|
|
* @param selectedIdx
|
|
* @return true if new value selected, false otherwise
|
|
*/
|
|
bool ComboBox(
|
|
ox::CStringView lbl,
|
|
ox::SpanView<ox::CStringView> list,
|
|
size_t &selectedIdx) noexcept;
|
|
|
|
/**
|
|
*
|
|
* @param lbl
|
|
* @param list
|
|
* @param selectedIdx
|
|
* @return true if new value selected, false otherwise
|
|
*/
|
|
bool ComboBox(ox::CStringView lbl, ox::Span<const ox::String> list, size_t &selectedIdx) noexcept;
|
|
|
|
/**
|
|
*
|
|
* @param lbl
|
|
* @param callback
|
|
* @param selectedIdx
|
|
* @return true if new value selected, false otherwise
|
|
*/
|
|
bool ComboBox(
|
|
ox::CStringViewCR lbl,
|
|
std::function<ox::CStringView(size_t)> const&f,
|
|
size_t strCnt,
|
|
size_t &selectedIdx) noexcept;
|
|
|
|
bool FileComboBox(
|
|
ox::CStringViewCR lbl,
|
|
StudioContext &sctx,
|
|
ox::StringViewCR fileExt,
|
|
size_t &selectedIdx) noexcept;
|
|
|
|
bool ListBox(
|
|
ox::CStringViewCR name,
|
|
std::function<ox::CStringView(size_t)> const&f,
|
|
size_t strCnt,
|
|
size_t &selIdx,
|
|
ImVec2 const&sz = {0, 0}) noexcept;
|
|
|
|
/**
|
|
*
|
|
* @param name
|
|
* @param list
|
|
* @param selIdx
|
|
* @return true if new value selected, false otherwise
|
|
*/
|
|
bool ListBox(ox::CStringViewCR name, ox::SpanView<ox::String> const&list, size_t &selIdx) noexcept;
|
|
|
|
class FilePicker {
|
|
private:
|
|
bool m_show{};
|
|
studio::StudioContext &m_sctx;
|
|
ox::String const m_title;
|
|
ox::String const m_fileExt;
|
|
ImVec2 const m_size;
|
|
public:
|
|
ox::Signal<ox::Error(ox::StringView)> filePicked;
|
|
|
|
FilePicker(
|
|
studio::StudioContext &sctx,
|
|
ox::StringParam title,
|
|
ox::StringParam fileExt,
|
|
ImVec2 const&size = {}) noexcept;
|
|
|
|
void draw() noexcept;
|
|
|
|
void show() noexcept;
|
|
|
|
};
|
|
|
|
class QuestionPopup {
|
|
private:
|
|
enum class Stage {
|
|
Closed,
|
|
Opening,
|
|
Open,
|
|
};
|
|
Stage m_stage = Stage::Closed;
|
|
bool m_open{};
|
|
ox::String m_title;
|
|
ox::String m_question;
|
|
|
|
public:
|
|
ox::Signal<ox::Error(ig::PopupResponse)> response;
|
|
|
|
QuestionPopup(ox::StringParam title, ox::StringParam question) noexcept;
|
|
|
|
void open() noexcept;
|
|
|
|
void close() noexcept;
|
|
|
|
[[nodiscard]]
|
|
bool isOpen() const noexcept;
|
|
|
|
void draw(StudioContext &ctx, ImVec2 const &sz = {}) noexcept;
|
|
|
|
};
|
|
|
|
[[nodiscard]]
|
|
bool mainWinHasFocus() noexcept;
|
|
|
|
}
|