21 changes: 21 additions & 0 deletions opengl/DearImGuiToggle/imgui_toggle_math.h
@@ -0,0 +1,21 @@
#pragma once

#include "DearImGui/imgui.h"

#ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS
#endif // IMGUI_DEFINE_MATH_OPERATORS
#include "DearImGui/imgui_internal.h"

namespace ImGuiToggleMath
{
// lerp, but backwards!
template<typename T> constexpr inline T ImInvLerp(T a, T b, float value) { return (T)((value - a) / (b - a)); }

// float comparison w/tolerance - can't constexper as ImFabs isn't constexpr.
inline bool ImApproximately(float a, float b, float tolerance = 0.0001f) { return ImAbs(a - b) < tolerance; }

// helpers for checking if an ImVec4 is zero or not.
constexpr inline bool IsZero(const ImVec4& v) { return v.w == 0 && v.x == 0 && v.y == 0 && v.z == 0; }
constexpr inline bool IsNonZero(const ImVec4& v) { return v.w != 0 || v.x != 0 || v.y != 0 || v.z != 0; }
} // namespace
84 changes: 84 additions & 0 deletions opengl/DearImGuiToggle/imgui_toggle_palette.cpp
@@ -0,0 +1,84 @@
#include "imgui_toggle_palette.h"
#include "imgui_toggle_math.h"

#include "DearImGui/imgui.h"
#ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS
#endif // IMGUI_DEFINE_MATH_OPERATORS
#include "DearImGui/imgui_internal.h"

using namespace ImGuiToggleMath;

void ImGui::UnionPalette(ImGuiTogglePalette* target, const ImGuiTogglePalette* candidate, const ImVec4 colors[], bool v)
{

target->Knob = colors[ImGuiCol_Text];
target->KnobHover = colors[ImGuiCol_Text];
target->Frame = colors[!v ? ImGuiCol_FrameBg : ImGuiCol_Button];
target->FrameHover = colors[!v ? ImGuiCol_FrameBgHovered : ImGuiCol_ButtonHovered];
target->FrameBorder = colors[ImGuiCol_Border];
target->FrameShadow = colors[ImGuiCol_BorderShadow];
target->KnobBorder = colors[ImGuiCol_Border];
target->KnobShadow = colors[ImGuiCol_BorderShadow];
target->A11yGlyph = colors[!v ? ImGuiCol_FrameBg : ImGuiCol_Text];

// if the user didn't provide a candidate, just provide the theme colored palette.
if (candidate == nullptr)
{
return;
}

// if the user did provide a candidate, populate all non-zero colors
#define GET_PALETTE_POPULATE_NONZERO(member) \
do { \
if (IsNonZero(candidate->member)) \
{ \
target->member = candidate->member; \
} \
} while (0)

GET_PALETTE_POPULATE_NONZERO(Knob);
GET_PALETTE_POPULATE_NONZERO(KnobHover);
GET_PALETTE_POPULATE_NONZERO(Frame);
GET_PALETTE_POPULATE_NONZERO(FrameHover);
GET_PALETTE_POPULATE_NONZERO(FrameBorder);
GET_PALETTE_POPULATE_NONZERO(FrameShadow);
GET_PALETTE_POPULATE_NONZERO(KnobBorder);
GET_PALETTE_POPULATE_NONZERO(KnobShadow);
GET_PALETTE_POPULATE_NONZERO(A11yGlyph);

#undef GET_PALETTE_POPULATE_NONZERO
}

void ImGui::BlendPalettes(ImGuiTogglePalette* result, const ImGuiTogglePalette& a, const ImGuiTogglePalette& b, float blend_amount)
{
// a quick out for if we are at either end of the blend.
if (ImApproximately(blend_amount, 0.0f))
{
*result = a;
return;
}
else if (ImApproximately(blend_amount, 1.0f))
{
*result = b;
return;
}


#define BLEND_PALETTES_LERP(member) \
do { \
result->member = ImLerp(a.member, b.member, blend_amount); \
} while (0)

BLEND_PALETTES_LERP(Knob);
BLEND_PALETTES_LERP(KnobHover);
BLEND_PALETTES_LERP(Frame);
BLEND_PALETTES_LERP(FrameHover);
BLEND_PALETTES_LERP(FrameBorder);
BLEND_PALETTES_LERP(FrameShadow);
BLEND_PALETTES_LERP(KnobBorder);
BLEND_PALETTES_LERP(KnobShadow);
BLEND_PALETTES_LERP(A11yGlyph);

#undef BLEND_PALETTES_LERP
}
41 changes: 41 additions & 0 deletions opengl/DearImGuiToggle/imgui_toggle_palette.h
@@ -0,0 +1,41 @@
#pragma once

#include "DearImGui/imgui.h"

// ImGuiTogglePalette: A collection of colors used to customize the rendering of a toggle widget.
// Leaving any ImVec4 as default (zero) will allow the theme color to be used for that member.
struct ImGuiTogglePalette
{
// The default knob color.
ImVec4 Knob;

// The default knob color, used when when the knob is hovered.
ImVec4 KnobHover;

// The background color of the toggle frame.
ImVec4 Frame;

// The background color of the toggle frame when the toggle is hovered.
ImVec4 FrameHover;

// The background color of the toggle frame's border used when ImGuiToggleFlags_BorderedFrame is specified.
ImVec4 FrameBorder;

// The shadow color of the toggle frame.
ImVec4 FrameShadow;

// The background color of the toggle knob's border used when ImGuiToggleFlags_BorderedKnob is specified.
ImVec4 KnobBorder;

// The shadow color of the toggle knob.
ImVec4 KnobShadow;

// The color of the accessibility label or glyph.
ImVec4 A11yGlyph;
};

namespace ImGui
{
void UnionPalette(ImGuiTogglePalette* target, const ImGuiTogglePalette* candidate, const ImVec4 colors[], bool v);
void BlendPalettes(ImGuiTogglePalette* result, const ImGuiTogglePalette& a, const ImGuiTogglePalette& b, float blend_amount);
}
205 changes: 205 additions & 0 deletions opengl/DearImGuiToggle/imgui_toggle_presets.cpp
@@ -0,0 +1,205 @@
#include "imgui_toggle_presets.h"
#include "imgui_toggle_palette.h"

namespace
{
// some color values shared between styles
const ImVec4 White(1.0f, 1.0f, 1.0f, 1.0f);
const ImVec4 Black(0.0f, 0.0f, 0.0f, 1.0f);
const ImVec4 Green(0.24f, 0.52f, 0.15f, 1.0f);
const ImVec4 GreenBorder(0.39f, 0.62f, 0.32f, 1.0f);
const ImVec4 GreenHighlight(0.3f, 1.0f, 0.0f, 0.75f);
const ImVec4 RedHighlight(1.0f, 0.3f, 0.0f, 0.75f);

} // namespace

ImGuiToggleConfig ImGuiTogglePresets::DefaultStyle()
{
return ImGuiToggleConfig();
}

ImGuiToggleConfig ImGuiTogglePresets::RectangleStyle()
{
ImGuiToggleConfig config;
config.Flags |= ImGuiToggleFlags_Animated;
config.FrameRounding = 0.1f;
config.KnobRounding = 0.3f;
config.AnimationDuration = 0.5f;

return config;
}

ImGuiToggleConfig ImGuiTogglePresets::GlowingStyle()
{
static ImGuiTogglePalette palette_on;
palette_on.FrameShadow = ::GreenHighlight;
palette_on.KnobShadow = ::GreenHighlight;

static ImGuiTogglePalette palette_off;
palette_off.FrameShadow = ::RedHighlight;
palette_off.KnobShadow = ::RedHighlight;

ImGuiToggleConfig config;
config.Flags |= ImGuiToggleFlags_Animated | ImGuiToggleFlags_Shadowed;
config.On.Palette = &palette_on;
config.Off.Palette = &palette_off;

return config;
}

ImGuiToggleConfig ImGuiTogglePresets::iOSStyle(const float size_scale /*= 1.0f*/, bool light_mode /*= false*/)
{
const ImVec4 frame_on(0.3f, 0.85f, 0.39f, 1.0f);
const ImVec4 frame_on_hover(0.0f, 1.0f, 0.57f, 1.0f);
const ImVec4 dark_mode_frame_off(0.22f, 0.22f, 0.24f, 1.0f);
const ImVec4 light_mode_frame_off(0.91f, 0.91f, 0.92f, 1.0f);
const ImVec4 dark_mode_frame_off_hover(0.4f, 0.4f, 0.4f, 1.0f);
const ImVec4 light_mode_frame_off_hover(0.7f, 0.7f, 0.7f, 1.0f);
const ImVec4 light_gray(0.89f, 0.89f, 0.89f, 1.0f);
const ImVec4 a11y_glyph_on(1.0f, 1.0f, 1.0f, 1.0f);
const ImVec4 a11y_glyph_off(0.4f, 0.4f, 0.4f, 1.0f);

const float ios_width = 153 * size_scale;
const float ios_height = 93 * size_scale;
const float ios_frame_border_thickness = 0.0f * size_scale;
const float ios_border_thickness = 0.0f * size_scale;
const float ios_offset = 0.0f * size_scale;
const float ios_inset = 6.0f * size_scale;

// setup 'on' colors
static ImGuiTogglePalette ios_palette_on;
ios_palette_on.Knob = ::White;
ios_palette_on.Frame = frame_on;
ios_palette_on.FrameHover = frame_on_hover;
ios_palette_on.KnobBorder = light_gray;
ios_palette_on.FrameBorder = light_gray;
ios_palette_on.A11yGlyph = a11y_glyph_on;

// setup 'off' colors
static ImGuiTogglePalette ios_palette_off;
ios_palette_off.Knob = ::White;
ios_palette_off.Frame = light_mode ? light_mode_frame_off : dark_mode_frame_off;
ios_palette_off.FrameHover = light_mode ? light_mode_frame_off_hover : light_mode_frame_off_hover;
ios_palette_off.KnobBorder = light_gray;
ios_palette_off.FrameBorder = light_gray;
ios_palette_off.A11yGlyph = a11y_glyph_off;

// setup config
ImGuiToggleConfig config;
config.Size = ImVec2(ios_width, ios_height);
config.Flags |= ImGuiToggleFlags_A11y
| ImGuiToggleFlags_Animated
| (light_mode ? ImGuiToggleFlags_Bordered : 0);
config.A11yStyle = ImGuiToggleA11yStyle_Glyph;

// setup 'on' config
config.On.FrameBorderThickness = 0;
config.On.KnobBorderThickness = ios_border_thickness;
config.On.KnobOffset = ImVec2(ios_offset, 0);
config.On.KnobInset = ios_inset;
config.On.Palette = &ios_palette_on;

// setup 'off' config
config.Off.FrameBorderThickness = ios_frame_border_thickness;
config.Off.KnobBorderThickness = ios_border_thickness;
config.Off.KnobOffset = ImVec2(ios_offset, 0);
config.Off.KnobInset = ios_inset;
config.Off.Palette = &ios_palette_off;

return config;
}

ImGuiToggleConfig ImGuiTogglePresets::MaterialStyle(float size_scale /*= 1.0f*/)
{
const ImVec4 purple(0.4f, 0.08f, 0.97f, 1.0f);
const ImVec4 purple_dim(0.78f, 0.65f, 0.99f, 1.0f);
const ImVec4 purple_hover(0.53f, 0.08f, 1.0f, 1.0f);

const ImVec2 material_size(37 * size_scale, 16 * size_scale);
const float material_inset = -2.5f * size_scale;

static ImGuiTogglePalette material_palette_on;
material_palette_on.Frame = purple_dim;
material_palette_on.FrameHover = purple_dim;
material_palette_on.Knob = purple;
material_palette_on.KnobHover = purple_hover;

// setup config
ImGuiToggleConfig config;
config.Flags |= ImGuiToggleFlags_Animated;
config.Size = material_size;
config.On.KnobInset = config.Off.KnobInset = material_inset;
config.On.KnobOffset = config.Off.KnobOffset = ImVec2(-material_inset, 0);
config.On.Palette = &material_palette_on;

return config;
}

ImGuiToggleConfig ImGuiTogglePresets::MinecraftStyle(float size_scale /*= 1.0f*/)
{
const ImVec4 gray(0.35f, 0.35f, 0.35f, 1.0f);
const ImVec4 dark_gray(0.12f, 0.12f, 0.12f, 1.0f);
const ImVec4 frame_border_off(0.6f, 0.6f, 0.61f, 1.0f);
const ImVec4 toggle_frame_off(0.55f, 0.55f, 0.56f, 1.0f);
const ImVec4 gray_knob(0.82f, 0.82f, 0.83f, 1.0f);

const ImVec2 minecraft_knob_size(56.0f * size_scale, 40.0f * size_scale);
const ImVec2 minecraft_size(104.0f * size_scale, 40.0f * size_scale); // 112x48
const float minecraft_borders = 4.0f * size_scale;
const ImVec2 minecraft_offset(0.0f * size_scale, -minecraft_borders * 2.0f);
const ImOffsetRect minecraft_inset(
0.0f * size_scale, // top
-16.0f * size_scale, // left
0.0f * size_scale, // bottom
0.0f * size_scale // right
);
const float minecraft_rounding = 0.0f; // disable rounding
const float minecraft_shadows = 4.0f * size_scale;

// set up the "on" palette.
static ImGuiTogglePalette minecraft_palette_on;
minecraft_palette_on.Frame = ::Green;
minecraft_palette_on.FrameHover = ::Green;
minecraft_palette_on.FrameBorder = ::GreenBorder;
minecraft_palette_on.FrameShadow = ::Black;
minecraft_palette_on.Knob = gray_knob;
minecraft_palette_on.KnobHover = gray_knob;
minecraft_palette_on.A11yGlyph = ::White;
minecraft_palette_on.KnobBorder = ::White;
minecraft_palette_on.KnobShadow = ::Black;

// start the "off" palette as a copy of the on
static ImGuiTogglePalette minecraft_palette_off;
minecraft_palette_off = minecraft_palette_on;

// make changes to the off palette
minecraft_palette_off.Frame = toggle_frame_off;
minecraft_palette_off.FrameHover = toggle_frame_off;
minecraft_palette_off.FrameBorder = frame_border_off;

// setup config
ImGuiToggleConfig config;
config.Flags |= ImGuiToggleFlags_A11y | ImGuiToggleFlags_Bordered | ImGuiToggleFlags_Shadowed;
config.Size = minecraft_size;
config.FrameRounding = minecraft_rounding;
config.KnobRounding = minecraft_rounding;
config.A11yStyle = ImGuiToggleA11yStyle_Glyph;

// set up the "on" state configuration
config.On.KnobInset = minecraft_inset;
config.On.KnobOffset = minecraft_offset;
config.On.FrameBorderThickness = minecraft_borders;
config.On.FrameShadowThickness = minecraft_shadows;
config.On.KnobBorderThickness = minecraft_borders;
config.On.KnobShadowThickness = minecraft_shadows;
config.On.Palette = &minecraft_palette_on;

// duplicate the "on" config to the "off", then make changes.
config.Off = config.On;
config.Off.KnobInset = minecraft_inset.MirrorHorizontally();
config.Off.Palette = &minecraft_palette_off;

return config;
}


25 changes: 25 additions & 0 deletions opengl/DearImGuiToggle/imgui_toggle_presets.h
@@ -0,0 +1,25 @@
#pragma once

#include "imgui_toggle.h"

// ImGuiTogglePresets: A few canned configurations for various presets OOTB.
namespace ImGuiTogglePresets
{
// The default, unmodified style.
ImGuiToggleConfig DefaultStyle();

// A style similar to default, but with rectangular knob and frame.
ImGuiToggleConfig RectangleStyle();

// A style that uses a shadow to appear to glow while it's on.
ImGuiToggleConfig GlowingStyle();

// A style that emulates what a toggle on iOS looks like.
ImGuiToggleConfig iOSStyle(float size_scale = 1.0f, bool light_mode = false);

// A style that emulates what a Material Design toggle looks like.
ImGuiToggleConfig MaterialStyle(float size_scale = 1.0f);

// A style that emulates what a toggle close to one from Minecraft.
ImGuiToggleConfig MinecraftStyle(float size_scale = 1.0f);
}
518 changes: 518 additions & 0 deletions opengl/DearImGuiToggle/imgui_toggle_renderer.cpp

Large diffs are not rendered by default.

97 changes: 97 additions & 0 deletions opengl/DearImGuiToggle/imgui_toggle_renderer.h
@@ -0,0 +1,97 @@
#pragma once

#include "DearImGui/imgui.h"
#include "imgui_toggle.h"
#include "imgui_toggle_palette.h"

#ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS
#endif // IMGUI_DEFINE_MATH_OPERATORS
#include "DearImGui/imgui_internal.h"

class ImGuiToggleRenderer
{
public:
ImGuiToggleRenderer();
ImGuiToggleRenderer(const char* label, bool* value, const ImGuiToggleConfig& user_config);
void SetConfig(const char* label, bool* value, const ImGuiToggleConfig& user_config);
bool Render();

private:
// toggle state & context
ImGuiToggleConfig _config;
ImGuiToggleStateConfig _state;
ImGuiTogglePalette _palette;

bool _isMixedValue;
bool _isHovered;
bool _isLastActive;
float _lastActiveTimer;
float _animationPercent;

// imgui specific context
const ImGuiContext* g;
const ImGuiStyle* _style;
ImDrawList* _drawList;
ImGuiID _id;

// raw ui value & label
const char* _label;
bool* _value;

// calculated values
ImRect _boundingBox;
ImVec4 _colorA11yGlyphOff;
ImVec4 _colorA11yGlyphOn;

// inline accessors
inline float GetWidth() const { return _boundingBox.GetWidth(); }
inline float GetHeight() const { return _boundingBox.GetHeight(); }
inline ImVec2 GetPosition() const { return _boundingBox.Min; }
inline ImVec2 GetToggleSize() const { return _boundingBox.GetSize(); }
inline bool IsAnimated() const { return (_config.Flags & ImGuiToggleFlags_Animated) != 0 && _config.AnimationDuration > 0; }
inline bool HasBorderedFrame() const { return (_config.Flags & ImGuiToggleFlags_BorderedFrame) != 0 && _state.FrameBorderThickness > 0; }
inline bool HasShadowedFrame() const { return (_config.Flags & ImGuiToggleFlags_ShadowedKnob) != 0 && _state.FrameShadowThickness > 0; }
inline bool HasBorderedKnob() const { return (_config.Flags & ImGuiToggleFlags_BorderedKnob) != 0 && _state.KnobBorderThickness > 0; }
inline bool HasShadowedKnob() const { return (_config.Flags & ImGuiToggleFlags_ShadowedKnob) != 0 && _state.KnobShadowThickness > 0; }
inline bool HasA11yGlyphs() const { return (_config.Flags & ImGuiToggleFlags_A11y) != 0; }
inline bool HasCircleKnob() const { return _config.KnobRounding >= 1.0f; }
inline bool HasRectangleKnob() const { return _config.KnobRounding < 1.0f; }

// behavior
void ValidateConfig();
bool ToggleBehavior(const ImRect& interaction_bounding_box);

// drawing - general
void DrawToggle();

// drawing - frame
void DrawFrame(ImU32 color_frame);

// drawing a11y
void DrawA11yDot(const ImVec2& pos, ImU32 color);
void DrawA11yGlyph(ImVec2 pos, ImU32 color, bool state, float radius, float thickness);
void DrawA11yLabel(ImVec2 pos, ImU32 color, const char* label);
void DrawA11yFrameOverlay(float knob_radius, bool state);
void DrawA11yFrameOverlays(float knob_radius);

// drawing - knob
void DrawCircleKnob(float radius, ImU32 color_knob);
void DrawRectangleKnob(float radius, ImU32 color_knob);

// drawing - label
void DrawLabel(float x_offset);

// state updating
void UpdateAnimationPercent();
void UpdateStateConfig();
void UpdatePalette();

// helpers
ImVec2 CalculateKnobCenter(float radius, float animation_percent, const ImVec2& offset = ImVec2()) const;
ImRect CalculateKnobBounds(float radius, float animation_percent, const ImVec2& offset = ImVec2()) const;
void DrawRectBorder(ImRect bounds, ImU32 color_border, float rounding, float thickness);
void DrawCircleBorder(const ImVec2& center, float radius, ImU32 color_border, float thickness);
void DrawRectShadow(ImRect bounds, ImU32 color_shadow, float rounding, float thickness);
void DrawCircleShadow(const ImVec2& center, float radius, ImU32 color_shadow, float thickness);
};