Skip to content

Generic Box() component #5944

@ypujante

Description

@ypujante

I have created a generic Box component (inspired by jetpack compose) for the purpose of:

  • having padding (top/right/bottom/left)
  • background color
  • border

It works great as can be seen in this image

imgui-box

Here is the code I have:

struct Modifier
{
  ImVec4 fPadding{};
  ImU32 fBackgroundColor{};
  ImU32 fBorderColor{};

  constexpr Modifier &padding(float iTop, float iRight, float iBottom, float iLeft) { fPadding = {iTop, iRight, iBottom, iLeft}; return *this; } // x/y/z/w
  constexpr Modifier &padding(float iPadding) { return padding(iPadding, iPadding, iPadding, iPadding); }
  constexpr Modifier &padding(float iHorizontalPadding, float iVerticalPadding) { return padding(iVerticalPadding, iHorizontalPadding, iVerticalPadding, iHorizontalPadding); }

  constexpr Modifier &backgroundColor(ImU32 iColor) { fBackgroundColor = iColor; return *this; }
  constexpr Modifier &borderColor(ImU32 iColor) { fBorderColor = iColor; return *this; }
};

// note that I use ImVec2 math in this code
void Box(Modifier const &iModifier, std::function<void()> const &iBoxContent)
{
  // split draw list in 2
  ImDrawListSplitter splitter{};
  splitter.Split(ImGui::GetWindowDrawList(), 2);

  // first we draw in channel 1 to render iBoxContent (will be on top)
  splitter.SetCurrentChannel(ImGui::GetWindowDrawList(), 1);

  ImGui::BeginGroup();
  {
    auto position = ImGui::GetCursorPos();
    // account for padding left/top
    ImGui::SetCursorPos(position + ImVec2{iModifier.fPadding.w, iModifier.fPadding.x});
    iBoxContent();
    ImGui::EndGroup();
  }

  auto min = ImGui::GetItemRectMin();
  // account for padding right/bottom
  auto max = ImGui::GetItemRectMax() + ImVec2{iModifier.fPadding.y, iModifier.fPadding.z};

  // second we draw the rectangle and border in channel 0 (will be below)
  splitter.SetCurrentChannel(ImGui::GetWindowDrawList(), 0);

  // draw the background
  if(!ColorIsTransparent(iModifier.fBackgroundColor))
    ImGui::GetWindowDrawList()->AddRectFilled(min, max, iModifier.fBackgroundColor);

  // draw the border
  if(!ColorIsTransparent(iModifier.fBorderColor))
    ImGui::GetWindowDrawList()->AddRect(min, max, iModifier.fBorderColor);

  // merge the 2 draw lists
  splitter.Merge(ImGui::GetWindowDrawList());

  // reposition the cursor (top left) and render a "dummy" box of the correct size so that it occupies
  // the proper amount of space
  ImGui::SetCursorScreenPos(min);
  ImGui::Dummy(max - min);
}

It works great, but of course it is a very different api style from the rest of ImGui. I should have instead a BeginBox(Modifier) and EndBox() api instead. The reason why I was not able to follow the same API is that in the EndBox() call I need information that is provided in the BeginBox call (like the colors and padding). Is there any recommended way on how I could do that (I guess I would need some form of "generic" PushMyVar api)?

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions