Skip to content

Commit

Permalink
Implement FlagMapper for oscimgui
Browse files Browse the repository at this point in the history
  • Loading branch information
adamkewley committed Sep 12, 2024
1 parent 8f5c2ed commit e95a418
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 49 deletions.
2 changes: 1 addition & 1 deletion src/OpenSimCreator/UI/Shared/ObjectPropertiesEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1578,7 +1578,7 @@ namespace

// create a type-erased entry from a known, concrete, editor
template<std::derived_from<IPropertyEditor> ConcretePropertyEditor>
constexpr static PropertyEditorRegistryEntry make_entry()
static constexpr PropertyEditorRegistryEntry make_entry()
{
const auto testerFn = ConcretePropertyEditor::IsCompatibleWith;
const auto typeErasedCtorFn = [](PropertyEditorArgs args) -> std::unique_ptr<IPropertyEditor>
Expand Down
2 changes: 1 addition & 1 deletion src/oscar/Platform/Log.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ namespace osc

namespace detail
{
constexpr static size_t c_max_log_traceback_messages = 512;
static constexpr size_t c_max_log_traceback_messages = 512;
}

[[nodiscard]] LogLevel global_get_traceback_level();
Expand Down
90 changes: 54 additions & 36 deletions src/oscar/UI/oscimgui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
#include <oscar/Maths/Quat.h>
#include <oscar/Maths/Transform.h>
#include <oscar/Maths/VecFunctions.h>
#include <oscar/Shims/Cpp23/utility.h>
#include <oscar/UI/ui_graphics_backend.h>
#include <oscar/Utils/Flags.h>
#include <oscar/Utils/EnumHelpers.h>
#include <oscar/Utils/Conversion.h>
#include <oscar/Utils/ScopeGuard.h>
Expand All @@ -28,11 +30,14 @@
#include <ImGuizmo.h>

#include <algorithm>
#include <array>
#include <cstddef>
#include <concepts>
#include <functional>
#include <initializer_list>
#include <ranges>
#include <string_view>
#include <utility>

using namespace osc;
using namespace osc::literals;
Expand Down Expand Up @@ -83,6 +88,39 @@ namespace
const Color saturated = saturate(brightened);
return ui::to_ImU32(saturated);
}

// maps between ui:: flag types and imgui flag types
template<FlagsEnum SourceFlagType, typename DestinationImGuiFlagsType>
requires std::is_enum_v<DestinationImGuiFlagsType> or std::is_integral_v<DestinationImGuiFlagsType>
class FlagMapper {
public:
constexpr FlagMapper(std::initializer_list<std::pair<SourceFlagType, DestinationImGuiFlagsType>> mappings)
{
if (mappings.size() != num_flags<SourceFlagType>()) {
throw std::runtime_error{"invalid number of flags passed to a FlagMapper"};
}

for (const auto& [source_flag, destination_flag] : mappings) {
const auto source_index = std::countr_zero(std::bit_floor(cpp23::to_underlying(source_flag)));
lut_[source_index] = destination_flag;
}
}

constexpr DestinationImGuiFlagsType operator()(Flags<SourceFlagType> flags) const
{
static_assert(std::is_unsigned_v<std::underlying_type_t<SourceFlagType>>);

DestinationImGuiFlagsType rv{};
for (auto v = to_underlying(flags); v;) {
const size_t index = std::countr_zero(v);
rv |= lut_[index];
v ^= 1<<index;
}
return rv;
}
private:
std::array<DestinationImGuiFlagsType, num_flags<SourceFlagType>()> lut_{};
};
}

template<>
Expand Down Expand Up @@ -114,46 +152,26 @@ struct osc::Converter<ui::GizmoMode, ImGuizmo::MODE> final {

template<>
struct osc::Converter<ui::TreeNodeFlags, ImGuiTreeNodeFlags> final {
ImGuiTreeNodeFlags operator()(ui::TreeNodeFlags flags) const
{
static_assert(num_flags<ui::TreeNodeFlag>() == 4);

ImGuiTreeNodeFlags rv = 0;
if (flags & ui::TreeNodeFlag::DefaultOpen) {
rv |= ImGuiTreeNodeFlags_DefaultOpen;
}
if (flags & ui::TreeNodeFlag::OpenOnArrow) {
rv |= ImGuiTreeNodeFlags_OpenOnArrow;
}
if (flags & ui::TreeNodeFlag::Leaf) {
rv |= ImGuiTreeNodeFlags_Leaf;
}
if (flags & ui::TreeNodeFlag::Bullet) {
rv |= ImGuiTreeNodeFlags_Bullet;
}
return rv;
}
ImGuiTreeNodeFlags operator()(ui::TreeNodeFlags flags) const { return c_mappings_(flags); }
private:
static constexpr FlagMapper<ui::TreeNodeFlag, ImGuiTreeNodeFlags> c_mappings_ = {
{ui::TreeNodeFlag::DefaultOpen, ImGuiTreeNodeFlags_DefaultOpen},
{ui::TreeNodeFlag::OpenOnArrow, ImGuiTreeNodeFlags_OpenOnArrow},
{ui::TreeNodeFlag::Leaf, ImGuiTreeNodeFlags_Leaf},
{ui::TreeNodeFlag::Bullet, ImGuiTreeNodeFlags_Bullet},
};
};

template<>
struct osc::Converter<ui::TabItemFlags, ImGuiTabItemFlags> final {
ImGuiTabItemFlags operator()(ui::TabItemFlags flags) const
{
ImGuiTabItemFlags rv = 0;
if (flags & ui::TabItemFlag::NoReorder) {
rv |= ImGuiTabItemFlags_NoReorder;
}
if (flags & ui::TabItemFlag::NoCloseButton) {
rv |= ImGuiTabItemFlags_NoCloseButton;
}
if (flags & ui::TabItemFlag::UnsavedDocument) {
rv |= ImGuiTabItemFlags_UnsavedDocument;
}
if (flags & ui::TabItemFlag::SetSelected) {
rv |= ImGuiTabItemFlags_SetSelected;
}
return rv;
}
ImGuiTabItemFlags operator()(ui::TabItemFlags flags) const { return c_mappings_(flags); }
private:
static constexpr FlagMapper<ui::TabItemFlag, ImGuiTabItemFlags> c_mappings_ = {
{ui::TabItemFlag::NoReorder, ImGuiTabItemFlags_NoReorder},
{ui::TabItemFlag::NoCloseButton, ImGuiTabItemFlags_NoCloseButton},
{ui::TabItemFlag::UnsavedDocument, ImGuiTabItemFlags_UnsavedDocument},
{ui::TabItemFlag::SetSelected, ImGuiTabItemFlags_SetSelected},
};
};

template<>
Expand Down
21 changes: 11 additions & 10 deletions src/oscar/UI/oscimgui.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ namespace osc::ui

bool draw_text_link(CStringView);

enum class TreeNodeFlag {
enum class TreeNodeFlag : unsigned {
None = 0,
DefaultOpen = 1<<0,
OpenOnArrow = 1<<1, // only open when clicking on the arrow part
Expand Down Expand Up @@ -143,12 +143,13 @@ namespace osc::ui
bool begin_tab_bar(CStringView str_id);
void end_tab_bar();

enum class TabItemFlag {
enum class TabItemFlag : unsigned {
None = 0,
NoReorder = 1<<0, // disable reordering this tab or having another tab cross over this tab
NoCloseButton = 1<<1, // track whether `p_open` was set or not (we'll need this info on the next frame to recompute ContentWidth during layout)
UnsavedDocument = 1<<2, // display a dot next to the title + (internally) set `ImGuiTabItemFlags_NoAssumedClosure`
SetSelected = 1<<3, // trigger flag to programmatically make the tab selected when calling `begin_tab_item`
NUM_FLAGS = 4,
};
using TabItemFlags = Flags<TabItemFlag>;

Expand Down Expand Up @@ -186,7 +187,7 @@ namespace osc::ui
bool is_mouse_down(MouseButton);
bool is_mouse_dragging(MouseButton, float lock_threshold = -1.0f);

enum class SliderFlag {
enum class SliderFlag : unsigned {
None = 0,
Logarithmic = 1<<0,
AlwaysClamp = 1<<1,
Expand All @@ -200,7 +201,7 @@ namespace osc::ui
NUM_OPTIONS
};

enum class TextInputFlag {
enum class TextInputFlag : unsigned {
None = 0,
ReadOnly = 1<<0,
EnterReturnsTrue = 1<<1,
Expand All @@ -227,7 +228,7 @@ namespace osc::ui
bool draw_collapsing_header(CStringView label, TreeNodeFlags = {});
void draw_dummy(const Vec2& size);

enum class ComboFlag {
enum class ComboFlag : unsigned {
None = 0,
NoArrowButton = 1<<0,
NUM_FLAGS = 1,
Expand All @@ -243,7 +244,7 @@ namespace osc::ui
Vec2 get_main_viewport_center();
void enable_dockspace_over_main_viewport();

enum class WindowFlag {
enum class WindowFlag : unsigned {
None = 0,

NoMove = 1<<0,
Expand All @@ -270,7 +271,7 @@ namespace osc::ui
bool begin_panel(CStringView name, bool* p_open = nullptr, WindowFlags = {});
void end_panel();

enum class ChildPanelFlag {
enum class ChildPanelFlag : unsigned {
None = 0,
Border = 1<<0,
NUM_FLAGS = 1,
Expand Down Expand Up @@ -326,7 +327,7 @@ namespace osc::ui

void set_next_panel_bg_alpha(float alpha);

enum class HoveredFlag {
enum class HoveredFlag : unsigned {
None = 0,
AllowWhenDisabled = 1<<0,
AllowWhenBlockedByPopup = 1<<1,
Expand Down Expand Up @@ -358,7 +359,7 @@ namespace osc::ui

ID get_id(std::string_view);

enum class ItemFlag {
enum class ItemFlag : unsigned {
None = 0,
Disabled = 1<<0,
Inputable = 1<<1,
Expand Down Expand Up @@ -782,7 +783,7 @@ namespace osc::ui
);

// an operation that a ui `Gizmo` shall perform
enum class GizmoOperation {
enum class GizmoOperation : unsigned {
None = 0,
Translate = 1<<0,
Rotate = 1<<1,
Expand Down
1 change: 1 addition & 0 deletions src/oscar/Utils/EnumHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ namespace osc
template<typename TEnum>
concept FlagsEnum = requires(TEnum v) {
requires std::is_enum_v<TEnum>;
requires std::is_unsigned_v<std::underlying_type_t<TEnum>>;
TEnum::NUM_FLAGS;
};

Expand Down
12 changes: 12 additions & 0 deletions src/oscar/Utils/Flags.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,21 @@ namespace osc
return Flags{value_ & ~static_cast<underlying_type>(flag)};
}

constexpr underlying_type underlying_value() const
{
return value_;
}

private:
explicit constexpr Flags(underlying_type value) : value_{value} {}

underlying_type value_{};
};

template<class TEnum>
requires std::is_enum_v<TEnum>
constexpr auto to_underlying(const Flags<TEnum>& e) noexcept
{
return e.underlying_value();
}
}
2 changes: 1 addition & 1 deletion tests/testoscar/Utils/TestEnumHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ TEST(num_options, ReturnsValueOfNUMOPTIONSWhenCustomized)

TEST(num_flags, ReturnsValueOfNUMFLAGSEnumMember)
{
enum class SomeFlagsEnum { First = 1<<0, Second = 1<<1, Third = 1<<2, NUM_FLAGS = 3};
enum class SomeFlagsEnum : unsigned { First = 1<<0, Second = 1<<1, Third = 1<<2, NUM_FLAGS = 3};
static_assert(num_flags<SomeFlagsEnum>() == 3);
}
10 changes: 10 additions & 0 deletions tests/testoscar/Utils/TestFlags.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <gtest/gtest.h>

#include <array>
#include <cstdint>

using namespace osc;

Expand Down Expand Up @@ -222,3 +223,12 @@ TEST(Flags, without_doesnt_set_already_unset_flag)
const Flags<ExampleDenseFlag> flags_after = flags.without(ExampleDenseFlag::Flag2);
ASSERT_EQ(flags, flags_after);
}

TEST(Flags, has_a_to_underlying_specialization)
{
enum class Some16BitEnum : uint16_t {};
static_assert(std::is_same_v<decltype(to_underlying(Flags<Some16BitEnum>{})), uint16_t>);

enum class SomeSigned32BitEnum : int32_t {};
static_assert(std::is_same_v<decltype(to_underlying(Flags<SomeSigned32BitEnum>{})), int32_t>);
}

0 comments on commit e95a418

Please sign in to comment.