summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--editor/inspect.cpp124
-rw-r--r--editor/inspect.hpp23
-rw-r--r--entity/accessor.hpp8
-rw-r--r--entity/erased-constraints.hpp36
4 files changed, 157 insertions, 34 deletions
diff --git a/editor/inspect.cpp b/editor/inspect.cpp
index 5a34d906..69734866 100644
--- a/editor/inspect.cpp
+++ b/editor/inspect.cpp
@@ -1,33 +1,137 @@
#include "inspect.hpp"
#include "compat/assert.hpp"
+#include "compat/defs.hpp"
#include "entity/accessor.hpp"
#include "imgui-raii.hpp"
+#include <Corrade/Containers/String.h>
+#include <Corrade/Containers/StringIterable.h>
+#include <Magnum/Math/Vector.h>
+#include <Magnum/Math/Vector2.h>
+#include <Magnum/Math/Vector3.h>
+#include <Magnum/Math/Vector4.h>
+#include <algorithm>
namespace floormat {
+namespace {
+
+String label_left(StringView label)
+{
+ float width = ImGui::CalcItemWidth(), x = ImGui::GetCursorPosX();
+ ImGui::Text("%s", label.data());
+ ImGui::SameLine();
+ ImGui::SetCursorPosX(x + width*.5f + ImGui::GetStyle().ItemInnerSpacing.x);
+ ImGui::SetNextItemWidth(-1);
+ return ""_s.join(StringIterable({ "##"_s, label }));
+}
+
+template<typename T> struct is_magnum_vector_ final : std::false_type {};
+template<std::size_t N, typename T> struct is_magnum_vector_<Math::Vector<N, T>> : std::true_type {};
+template<typename T> struct is_magnum_vector_<Math::Vector2<T>> : std::true_type {};
+template<typename T> struct is_magnum_vector_<Math::Vector3<T>> : std::true_type {};
+template<typename T> struct is_magnum_vector_<Math::Vector4<T>> : std::true_type {};
+template<typename T> constexpr bool is_magnum_vector = is_magnum_vector_<T>::value;
+
+template<typename T> struct IGDT_;
+template<> struct IGDT_<std::uint8_t> : std::integral_constant<int, ImGuiDataType_U8> {};
+template<> struct IGDT_<std::int8_t> : std::integral_constant<int, ImGuiDataType_S8> {};
+template<> struct IGDT_<std::uint16_t> : std::integral_constant<int, ImGuiDataType_U16> {};
+template<> struct IGDT_<std::int16_t> : std::integral_constant<int, ImGuiDataType_S16> {};
+template<> struct IGDT_<std::uint32_t> : std::integral_constant<int, ImGuiDataType_U32> {};
+template<> struct IGDT_<std::int32_t> : std::integral_constant<int, ImGuiDataType_S32> {};
+template<> struct IGDT_<float> : std::integral_constant<int, ImGuiDataType_Float> {};
+template<typename T> constexpr auto IGDT = IGDT_<T>::value;
+
using namespace imgui;
using namespace entities;
-template<typename T>
-static void do_inspect_field(const void* datum, const erased_accessor& accessor)
+template<typename T> void do_inspect_field(void* datum, const erased_accessor& accessor, field_repr repr)
{
fm_assert(accessor.check_field_name<T>());
- raii_wrapper disabler;
+ bool should_disable;
switch (accessor.is_enabled(datum))
{
using enum field_status;
case hidden: return;
- case readonly: disabler = begin_disabled(); break;
- case enabled: break;
+ case readonly: should_disable = true; break;
+ case enabled: should_disable = false; break;
}
+ should_disable = should_disable || !accessor.can_write();
+ [[maybe_unused]] auto disabler = begin_disabled(should_disable);
+ bool ret = false;
+ const auto label = label_left(accessor.field_name);
+ T value{};
+ accessor.read_fun(datum, accessor.reader, &value);
+ auto orig = value;
- auto [min, max] = accessor.get_range(datum).convert<T>();
-}
+ if constexpr (!is_magnum_vector<T>)
+ {
+ auto [min, max] = accessor.get_range(datum).convert<T>();
+ constexpr auto igdt = IGDT<T>;
+ switch (repr)
+ {
+ case field_repr::input: ret = ImGui::InputScalar(label.data(), igdt, &value); break;
+ case field_repr::slider: ret = ImGui::SliderScalar(label.data(), igdt, &value, &min, &max); break;
+ case field_repr::drag: ret = ImGui::DragScalar(label.data(), igdt, &value, 1, &min, &max); break;
+ }
+ value = std::clamp(value, min, max);
+ }
+ else
+ {
+ auto [min_, max_] = accessor.get_range(datum).convert<typename T::Type>();
+ Math::Vector<T::Size, typename T::Type> min(min_), max(max_);
+ constexpr auto igdt = IGDT<typename T::Type>;
+ switch (repr)
+ {
+ case field_repr::input:
+ ret = ImGui::InputScalarN(label.data(), igdt, &value, T::Size);
+ break;
+ case field_repr::drag:
+ fm_warn_once("can't use imgui input drag mode for vector type");
+ [[fallthrough]];
+ case field_repr::slider:
+ ret = ImGui::SliderScalarN(label.data(), igdt, &value, T::Size, &min, &max);
+ break;
+ }
+ }
-template<> void inspect_field<int>(const void* datum, const erased_accessor& accessor)
-{
- do_inspect_field<int>(datum, accessor);
+ ImGui::NewLine();
+
+ if (ret && !should_disable && value != orig)
+ if (accessor.is_enabled(datum) >= field_status::enabled && accessor.can_write())
+ accessor.write_fun(datum, accessor.writer, &value);
}
+} // namespace
+
+#define MAKE_SPEC(type, repr) \
+ template<> void inspect_field<type>(void* datum, const erased_accessor& accessor) { \
+ do_inspect_field<type>(datum, accessor, (repr)); \
+ }
+
+#define MAKE_SPEC_REPR(type, repr) \
+ template<> void inspect_field<field_repr_<type, field_repr, repr>>(void* datum, const erased_accessor& accessor) { \
+ do_inspect_field<type>(datum, accessor, (repr)); \
+ }
+
+#define MAKE_SPEC_REPRS(type) \
+ MAKE_SPEC_REPR(type, field_repr::input) \
+ MAKE_SPEC_REPR(type, field_repr::slider) \
+ MAKE_SPEC_REPR(type, field_repr::drag) \
+ MAKE_SPEC(type, field_repr::input)
+
+#define MAKE_SPEC_REPRS2(type) \
+ MAKE_SPEC_REPRS(Math::Vector2<type>) \
+ MAKE_SPEC_REPRS(Math::Vector3<type>) \
+ MAKE_SPEC_REPRS(Math::Vector4<type>) \
+ MAKE_SPEC_REPRS(type)
+
+MAKE_SPEC_REPRS2(std::uint8_t)
+MAKE_SPEC_REPRS2(std::int8_t)
+MAKE_SPEC_REPRS2(std::uint16_t)
+MAKE_SPEC_REPRS2(std::int16_t)
+MAKE_SPEC_REPRS2(std::uint32_t)
+MAKE_SPEC_REPRS2(std::int32_t)
+
} // namespace floormat
diff --git a/editor/inspect.hpp b/editor/inspect.hpp
index feca67d1..d2824f2c 100644
--- a/editor/inspect.hpp
+++ b/editor/inspect.hpp
@@ -4,6 +4,27 @@ namespace floormat::entities { struct erased_accessor; }
namespace floormat {
-template<typename T> void inspect_field(const void* datum, const entities::erased_accessor& accessor);
+template<typename T, typename Enum, Enum As>
+struct field_repr_ final {
+ field_repr_(T x) : value{x} {}
+ field_repr_(const field_repr_<T, Enum, As>& other) noexcept = default;
+ field_repr_& operator=(T x) noexcept { value = x; return *this; }
+ field_repr_& operator=(const field_repr_<T, Enum, As>& x) noexcept = default;
+ field_repr_& operator*() const noexcept { return value; }
+ operator T() const noexcept { return value; }
+
+ using Type = T;
+ static constexpr Enum Repr = As;
+private:
+ T value;
+};
+
+enum class field_repr : unsigned char { input, slider, drag, };
+
+template<typename T> using field_repr_input = field_repr_<T, field_repr, field_repr::input>;
+template<typename T> using field_repr_slider = field_repr_<T, field_repr, field_repr::slider>;
+template<typename T> using field_repr_drag = field_repr_<T, field_repr, field_repr::drag>;
+
+template<typename T> void inspect_field(void* datum, const entities::erased_accessor& accessor);
} // namespace floormat
diff --git a/entity/accessor.hpp b/entity/accessor.hpp
index 3b979a16..f70dee72 100644
--- a/entity/accessor.hpp
+++ b/entity/accessor.hpp
@@ -16,7 +16,7 @@ struct group;
namespace floormat::entities {
-enum class field_status : unsigned char { enabled, hidden, readonly, };
+enum class field_status : unsigned char { hidden, readonly, enabled, };
struct erased_accessor final {
using reader_t = void;
@@ -170,11 +170,7 @@ void erased_accessor::write(Obj& x, move_qualified<FieldType> value) const noexc
write_unchecked<Obj, FieldType>(x, value);
}
-field_status erased_accessor::is_enabled(const void* x) const noexcept
-{
- return predicate_fun(&x, predicate);
-}
-
+field_status erased_accessor::is_enabled(const void* x) const noexcept { return predicate_fun(&x, predicate); }
erased_constraints::range erased_accessor::get_range(const void* x) const noexcept { return range_fun(x,range); }
erased_constraints::max_length erased_accessor::get_max_length(const void* x) const noexcept { return length_fun(x,length); }
erased_constraints::group erased_accessor::get_group(const void* x) const noexcept { return group_fun(x, group); }
diff --git a/entity/erased-constraints.hpp b/entity/erased-constraints.hpp
index c0ff3e9f..4fe5c853 100644
--- a/entity/erased-constraints.hpp
+++ b/entity/erased-constraints.hpp
@@ -29,26 +29,28 @@ struct range final
template<typename T> constexpr std::pair<T, T> range::convert() const
{
- if constexpr (!std::is_floating_point_v<T> && !std::is_integral_v<T>)
- return {{}, {}};
-
- using std::size_t;
- static_assert(sizeof(T) <= sizeof(size_t) || !std::is_integral_v<T>);
-
- constexpr auto min_ = []<typename V>(V a, V b) { return a < b ? a : b; };
- constexpr auto max_ = []<typename V>(V a, V b) { return a > b ? a : b; };
+ static_assert(sizeof(T) <= sizeof(std::size_t));
using limits = std::numeric_limits<T>;
- constexpr auto lmin = limits::min(), lmax = limits::max();
- switch (type) {
- case type_float:
- if constexpr (limits::is_integer)
- return { T(std::floor(min.f)), T(std::ceil(max.f)) };
+ if (type == type_none)
+ return { limits::min(), limits::max() };
+ else
+ {
+ if constexpr (std::is_integral_v<T> && std::is_signed_v<T>)
+ {
+ fm_assert(type == type_int);
+ return { T(min.i), T(max.i) };
+ }
+ else if constexpr (std::is_integral_v<T> && std::is_unsigned_v<T>)
+ {
+ fm_assert(type == type_uint);
+ return { T(min.u), T(max.u) };
+ }
else
- return { T(min.f), T(max.f) };
- case type_uint: return { max_(T(min.u), lmin), T(min_(size_t(max.u), size_t(lmax))) };
- case type_int: return { max_(T(min.i), lmin), T(min_(size_t(max.i), size_t(lmax))) };
- default: case type_none: return { lmin, lmax };
+ {
+ fm_assert(type == type_float);
+ return { T(min.i), T(max.i) };
+ }
}
}