Gary Talent e773d6f0ee
All checks were successful
Build / build (push) Successful in 1m16s
[studio] Rename StudioContext to Context
2025-05-06 22:37:21 -05:00

374 lines
8.6 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>
#include <studio/widget.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) {
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
std::ignore = out.text.unsafeResize(ox::strlen(out.text.c_str()));
OX_ALLOW_UNSAFE_BUFFERS_END
}
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) {
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
std::ignore = out.text.unsafeResize(ox::strlen(out.text.c_str()));
OX_ALLOW_UNSAFE_BUFFERS_END
}
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) {
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
std::ignore = text.unsafeResize(ox::strlen(text.c_str()));
OX_ALLOW_UNSAFE_BUFFERS_END
}
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");
PopupResponse PopupControlsOk(
bool &popupOpen,
ox::CStringViewCR ok);
[[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 f callback function
* @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,
Context &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{};
Context &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::Context &sctx,
ox::StringParam title,
ox::StringParam fileExt,
ImVec2 const&size = {}) noexcept;
void draw() noexcept;
void show() noexcept;
};
class Popup: public Widget {
protected:
enum class Stage {
Closed,
Opening,
Open,
};
Stage m_stage = Stage::Closed;
bool m_open{};
ox::String m_title;
public:
ox::Signal<ox::Error(PopupResponse)> response;
explicit Popup(ox::StringParam title) noexcept;
void open() noexcept;
void close() noexcept;
[[nodiscard]]
bool isOpen() const noexcept;
};
class QuestionPopup: public Popup {
private:
ox::String m_question;
public:
ox::Signal<ox::Error(PopupResponse)> response;
QuestionPopup(ox::StringParam title, ox::StringParam question) noexcept;
void draw(Context &ctx) noexcept override;
};
class MessagePopup: public Popup {
private:
ox::String m_msg;
public:
ox::Signal<ox::Error(PopupResponse)> response;
MessagePopup(ox::StringParam title, ox::StringParam msg) noexcept;
void show(ox::StringParam msg) noexcept;
void draw(Context &ctx) noexcept override;
};
[[nodiscard]]
bool mainWinHasFocus() noexcept;
}