Skip to content

Instantly share code, notes, and snippets.

@kudaba
Last active April 9, 2020 06:40
Show Gist options
  • Save kudaba/01337c088a0bcef0c2e22a4e846f729c to your computer and use it in GitHub Desktop.
Save kudaba/01337c088a0bcef0c2e22a4e846f729c to your computer and use it in GitHub Desktop.
Improved number editing control for ImGui
// WARNING: Revision 5+ Requires unreleased code from imgui, stick to 3 or 2 for now (don't look at revision 4)
#define IMGUI_DEFINE_MATH_OPERATORS
#include <imgui.h>
#include <imgui_internal.h>
#include "iminputvalue.h"
namespace ImGui
{
//-------------------------------------------------------------------------------------------------
// Customized number box with mouse drag support and condensed arrow buttons
//-------------------------------------------------------------------------------------------------
template <typename T>
bool InputValue(T& aValue, T aStep, ImGuiDataType aType, char const* aFormat, ImGuiInputTextFlags aFlags)
{
float width = CalcItemWidth();
float height = GetFrameHeight();
BeginGroup();
// Only show buttons if there's enough space
bool hasButtons = width > height * 4;
int buttonStep = 0;
if (hasButtons)
{
width -= height;
PushItemWidth(width);
}
bool changed = InputScalar("", aType, &aValue, nullptr, nullptr, aFormat, aFlags);
ImGuiID id = GetItemID();
SetActiveItemUsesMouseWheel(id);
if (hasButtons)
{
PopItemWidth();
PushStyleVar(ImGuiStyleVar_FramePadding, { 0, 0 });
PushStyleVar(ImGuiStyleVar_ItemSpacing, { 0, 0 });
SameLine();
BeginGroup();
if (ButtonEx("###up", { height, height / 2 }, ImGuiButtonFlags_NoNavFocus))
{
buttonStep = 1;
}
SetActiveItemUsesMouseWheel(id);
float offset = height - GetFontSize();
ImVec2 pos = GetItemRectMin();
RenderArrow(GetWindowDrawList(), { pos.x + offset / 2, pos.y + offset / 4 }, GetColorU32(ImGuiCol_Text), ImGuiDir_Up, 0.5f);
if (ButtonEx("###down", { height, height / 2 + 1 }, ImGuiButtonFlags_NoNavFocus))
{
buttonStep = -1;
}
SetActiveItemUsesMouseWheel(id);
pos = ImGui::GetItemRectMin();
RenderArrow(GetWindowDrawList(), { pos.x + offset / 2, pos.y + 1 }, GetColorU32(ImGuiCol_Text), ImGuiDir_Down, 0.5f);
EndGroup();
PopStyleVar(2);
}
EndGroup();
if (buttonStep)
{
if (ApplyStep(aValue, aStep, aType, buttonStep))
{
changed = true;
SetKeyboardFocusHere(-1);
}
}
// Drag support
{
GC_MouseDragHelper& helper = ImGuiHelpers::GetStorageSlot()->GetOrCreate<GC_MouseDragHelper>(true);
if (helper.HasMouse() || IsItemHovered())
{
if (helper.Update(GetIO().KeyShift ? GC_MouseKey::Left : GC_MouseKey::Middle))
SetKeyboardFocusHere(-1);
}
if (helper.HasMouse())
{
GC_Vector2i delta = GC_InputMouse::Instance().Movement();
if (ApplyStep(aValue, aStep, aType, delta.x - delta.y))
{
changed = true;
if (GImGui->InputTextState.ID == id)
GImGui->InputTextState.Reinitialize = true;
}
}
}
// Mouse wheel support
{
if (IsItemHovered() && IsItemActive())
{
if (float scroll = GetIO().InputCurrentFrame->MouseWheel)
{
if (ApplyStep(aValue, aStep, aType, scroll))
{
changed = true;
if (GImGui->InputTextState.ID == id)
GImGui->InputTextState.Reinitialize = true;
}
}
}
}
return changed;
}
//-------------------------------------------------------------------------------------------------
template <typename T>
bool InputValues(char const* label, int components, ImGuiDataType data_type, T* aValue, T const* aStep, ImGuiInputTextFlags aFlags, char const* aFormat)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiContext& g = *GImGui;
bool value_changed = false;
BeginGroup();
PushID(label);
PushMultiItemsWidths(components, CalcItemWidth());
for (int i = 0; i < components; i++)
{
PushID(i);
if (i > 0)
SameLine(0, g.Style.ItemInnerSpacing.x);
value_changed |= InputValue(aValue[i], aStep[i], data_type, aFormat, aFlags);
PopID();
PopItemWidth();
}
PopID();
const char* label_end = FindRenderedTextEnd(label);
if (label != label_end)
{
SameLine(0.0f, g.Style.ItemInnerSpacing.x);
TextEx(label, label_end);
}
EndGroup();
return value_changed;
}
//-------------------------------------------------------------------------------------------------
// All the value hooks into the custom edit box
//-------------------------------------------------------------------------------------------------
bool Input(char const* aName, float& aValue, float aStep, ImGuiInputTextFlags aFlags, char const* aFormat)
{
return InputValues(aName, 1, ImGuiDataType_Float, &aValue, &aStep, aFlags, aFormat);
}
bool Input(char const* aName, double& aValue, double aStep, ImGuiInputTextFlags aFlags, char const* aFormat)
{
return InputValues(aName, 1, ImGuiDataType_Double, &aValue, &aStep, aFlags, aFormat);
}
bool Input(char const* aName, s8& aValue, s8 aStep, ImGuiInputTextFlags aFlags, char const* aFormat)
{
return InputValues(aName, 1, ImGuiDataType_S8, &aValue, &aStep, aFlags, aFormat);
}
bool Input(char const* aName, s16& aValue, s16 aStep, ImGuiInputTextFlags aFlags, char const* aFormat)
{
return InputValues(aName, 1, ImGuiDataType_S16, &aValue, &aStep, aFlags, aFormat);
}
bool Input(char const* aName, s32& aValue, s32 aStep, ImGuiInputTextFlags aFlags, char const* aFormat)
{
return InputValues(aName, 1, ImGuiDataType_S32, &aValue, &aStep, aFlags, aFormat);
}
bool Input(char const* aName, s64& aValue, s64 aStep, ImGuiInputTextFlags aFlags, char const* aFormat)
{
return InputValues(aName, 1, ImGuiDataType_S64, &aValue, &aStep, aFlags, aFormat);
}
bool Input(char const* aName, u8& aValue, u8 aStep, ImGuiInputTextFlags aFlags, char const* aFormat)
{
return InputValues(aName, 1, ImGuiDataType_U8, &aValue, &aStep, aFlags, aFormat);
}
bool Input(char const* aName, u16& aValue, u16 aStep, ImGuiInputTextFlags aFlags, char const* aFormat)
{
return InputValues(aName, 1, ImGuiDataType_U16, &aValue, &aStep, aFlags, aFormat);
}
bool Input(char const* aName, u32& aValue, u32 aStep, ImGuiInputTextFlags aFlags, char const* aFormat)
{
return InputValues(aName, 1, ImGuiDataType_U32, &aValue, &aStep, aFlags, aFormat);
}
bool Input(char const* aName, u64& aValue, u64 aStep, ImGuiInputTextFlags aFlags, char const* aFormat)
{
return InputValues(aName, 1, ImGuiDataType_U64, &aValue, &aStep, aFlags, aFormat);
}
// Example for vector/math types
bool Input(char const* aName, Vector3<float>& aValue, u64 aStep, ImGuiInputTextFlags aFlags, char const* aFormat)
{
return InputValues(aName, 3, ImGuiDataType_Float, &aValue, &aStep, aFlags, aFormat);
}
}
// WARNING: Revision 5+ Requires unreleased code from imgui, stick to 3 or 2 for now (don't look at revision 4)
namespace ImGui
{
//-------------------------------------------------------------------------------------------------
// All the value hooks into the custom edit box
//-------------------------------------------------------------------------------------------------
bool Input(char const* aName, float& aValue, float aStep, ImGuiInputTextFlags aFlags, char const* aFormat);
bool Input(char const* aName, double& aValue, double aStep, ImGuiInputTextFlags aFlags, char const* aFormat);
bool Input(char const* aName, s8& aValue, s8 aStep, ImGuiInputTextFlags aFlags, char const* aFormat);
bool Input(char const* aName, s16& aValue, s16 aStep, ImGuiInputTextFlags aFlags, char const* aFormat);
bool Input(char const* aName, s32& aValue, s32 aStep, ImGuiInputTextFlags aFlags, char const* aFormat);
bool Input(char const* aName, s64& aValue, s64 aStep, ImGuiInputTextFlags aFlags, char const* aFormat);
bool Input(char const* aName, u8& aValue, u8 aStep, ImGuiInputTextFlags aFlags, char const* aFormat);
bool Input(char const* aName, u16& aValue, u16 aStep, ImGuiInputTextFlags aFlags, char const* aFormat);
bool Input(char const* aName, u32& aValue, u32 aStep, ImGuiInputTextFlags aFlags, char const* aFormat);
bool Input(char const* aName, u64& aValue, u64 aStep, ImGuiInputTextFlags aFlags, char const* aFormat);
// Example for vector/math types
bool Input(char const* aName, Vector3<float>& aValue, u64 aStep, ImGuiInputTextFlags aFlags, char const* aFormat);
}
@kudaba
Copy link
Author

kudaba commented Nov 5, 2019

number_control

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment