Closed Pagghiu closed 9 years ago
Hacking a copy&paste of ImGui::Button to create ImGui::ButtonIcon yields pleasing results Anyway I can confirm that being able to interleave font icons in "default" font would make things a lot easier in some situations.
#pragma once
enum ImGuiIconEnum
{
ImGui_fa_camera_retro = 0xF083
};
namespace ImGui
{
extern ImFont* GIconFont;
bool ButtonIcon(const char* label, ImGuiIconEnum iconChar, const ImVec2& size = ImVec2(0, 0), bool repeat_when_held = false);
}
#pragma once
#include "imgui.h"
// This is just to help Intellisense for private imgui.cpp symbols on visual studio...
#ifndef STB_INCLUDE_STB_RECT_PACK_H
#include "imgui.cpp"
#endif
ImFont* ImGui::GIconFont = nullptr;
bool ImGui::ButtonIcon(const char* label, ImGuiIconEnum iconChar, const ImVec2& size_arg, bool repeat_when_held)
{
IM_ASSERT(GIconFont);
ImGuiState& g = *GImGui;
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
char labelIcon[8] = { 0 };
ImTextCharToUtf8(labelIcon, 8, (unsigned int)iconChar);
const ImGuiStyle& style = g.Style;
const ImGuiID id = window->GetID(label);
const ImVec2 label_size = CalcTextSize(label, NULL, true);
ImGui::PushFont(GIconFont);
const ImVec2 label_icon_size = CalcTextSize(labelIcon, NULL, true);
ImGui::PopFont();
ImVec2 size(size_arg.x != 0.0f ? size_arg.x : (label_size.x + label_icon_size.x + style.ItemInnerSpacing.x + style.FramePadding.x * 2),
size_arg.y != 0.0f ? size_arg.y : ImMax(label_size.y + style.FramePadding.y * 2, label_icon_size.y + style.FramePadding.y * 2));
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
ItemSize(bb, style.FramePadding.y);
if (!ItemAdd(bb, &id))
return false;
bool hovered, held;
bool pressed = ButtonBehavior(bb, id, &hovered, &held, true, repeat_when_held ? ImGuiButtonFlags_Repeat : 0);
// Render
const ImU32 col = window->Color((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
ImGui::PushFont(GIconFont);
const ImVec2 offIcon = ImVec2(ImMax(0.0f, size.x - label_size.x - label_icon_size.x - style.ItemInnerSpacing.x) * 0.5f, ImMax(0.0f, size.y - label_icon_size.y) * 0.5f); // Center (only applies if we explicitly gave a size bigger than the text size, which isn't the common path)
RenderTextClipped(bb.Min + offIcon, labelIcon, NULL, NULL, bb.Max, NULL);
ImGui::PopFont();
const ImVec2 off = ImVec2(ImMax(0.0f, size.x - label_size.x + label_icon_size.x + style.ItemInnerSpacing.x) * 0.5f, ImMax(0.0f, size.y - label_size.y) * 0.5f); // Center (only applies if we explicitly gave a size bigger than the text size, which isn't the common path)
RenderTextClipped(bb.Min + off, label, NULL, &label_size, bb.Max, NULL); // Render clip (only applies if we explicitly gave a size smaller than the text size, which isn't the commmon path)
return pressed;
}
You use it like this
ImGui::ButtonIcon("Button Line 1\nButton Line 2", ImGui_fa_camera_retro);
Hi,
There's several things here. Yes to the need of making it possible / easy to integrate symbols into fonts.
Everything is possible really..
1) Yes, the icons are coming from a TTF file (just download http://fortawesome.github.io/Font-Awesome/). 2+3) I would invest more time in proper Font scaling first rather then ability to have arbitrary bitmaps. There are already many ready to use ttf icon fonts and tools to build them from vector shapes, svg or similar. User Bitmaps would allow to bake colored icons but in this case I would pre-pack them in a texture using bitmaps or vector graphics (maybe with nanosvg) and stb_rect_pack. ImGui::Image is the way to go then, with a custom layout similar to my example.
My code is just a simple test with a fixed layout that doesn't cover everyone's cases of course. I was wondering if I could quickly get some button icons using .ttf fonts, and I'm sharing in case someone else wants to play or elaborate some more. I think I may be extending the ButtonIcon function trying to cover a few "common" layout cases (icon left/right/center/top/bottom), to be chosen with some flags, but as always, you cannot cover everyone's needs or taste.
Your considerations on the line layout control are making me think that the real world use case for "inline" icon font mixed with regular character are maybe fewer than I was thinking, unless, like you're suggesting, one thinks of a mini markup language, that doesn't absolutely fit the simplicity and style of the ImGui api. Personally it has been taking me 3 minutes to get the layout I was looking for by copying/pasting/modifying the ImGui::Button code, so I really don't feel the need for a mini markup language. My vote so is for building a simpler copy/paste/modify experience for custom widgets like this one.
Will probably do a) loading range from multiple fonts into one + b) font scaling.
My vote so is for building a simpler copy/paste/modify experience for custom widgets like this one.
I will keep working on this with the intent of opening more of imgui.cpp down the line (which means supporting its API) but I have no doubt it will take a long time until we get there. There will be some friction and frustration as those copy/pasted code will break several times (I already made some change now). This is the code for Button()
static bool ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags flags)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiState& g = *GImGui;
const ImGuiStyle& style = g.Style;
const ImGuiID id = window->GetID(label);
const ImVec2 label_size = ImGui::CalcTextSize(label, NULL, true);
const ImVec2 size(size_arg.x != 0.0f ? size_arg.x : (label_size.x + style.FramePadding.x*2), size_arg.y != 0.0f ? size_arg.y : (label_size.y + style.FramePadding.y*2));
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);
ItemSize(bb, style.FramePadding.y);
if (!ItemAdd(bb, &id))
return false;
bool hovered, held;
bool pressed = ButtonBehavior(bb, id, &hovered, &held, true, flags);
// Render
const ImU32 col = window->Color((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);
RenderTextClipped(bb.Min, bb.Max, label, NULL, &label_size, NULL, ImGuiAlign_Center | ImGuiAlign_VCenter);
// Automatically close popups
if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))
ImGui::CloseCurrentPopup();
return pressed;
}
bool ImGui::Button(const char* label, const ImVec2& size_arg, bool repeat_when_held)
{
return ButtonEx(label, size_arg, repeat_when_held ? ImGuiButtonFlags_Repeat : 0);
}
And I intend to remove the "repeat" flag (which is rarely used) and move it to a helper function or stateful thing. Especially before repeat needs timing parameters anyway.
I was expecting to be walking on thin ice when using internal api, so breaking changes are not a big deal ;) I will try to re-align my additions more frequently before they diverge too much. Being able to compose widgets easier is a great mid-long term plan, I will stay tuned!
The AA branch now support this with the new ImFontConfig* settings. All the AddFontxxx() functions now have a ImFontConfig* pointer. If you pass one you can alter settings.
Merge two fonts:
io.Fonts->AddFontDefault();
// Add character ranges and merge into main font
static ImWchar ranges[] = { 0xf000, 0xf3ff, 0 };
ImFontConfig config;
config.MergeMode = true;
io.Fonts->AddFontFromFileTTF("fontawesome-webfont.ttf", 16.0f, &config, ranges);
There's a few catch here. You can specify a different size to some degree but the font ine height will always be based on the first font of a merged list. So here the default font is 13 and 16 for the icon is alright, but if you use 30 icons it won't fit.
By default characters of different sizes are aligned on their baseline. You can see config.MergeGlyphCenterV = true to center the merged character vertically which may make more sense with icons.
It works but it's probably a little limited in the sense that you'll need your icon to be roughly the same size as your text.
You can adjust the config.OversampleH, config.OversampleV parameters, even without using subpixel positioning this will also give you leeway you scale the font display with less noticeable artefacts.
This is now merged in master along with the entire new AA-branch. Note that it is a rather big update and you'll need to update your render function to use indexed vertex rendering.
will demo be showcasing this anytime soon?
BTW, I have try the following code, but the "PushFont" crash !
It seems the font is not loaded, why ?
ImWchar ranges[] = { 0xf000, 0xf3ff, 0 };
ImFontConfig config;
config.MergeMode = true;
ImFont* font = ImGui::GetIO().Fonts->AddFontFromFileTTF("install/fontawesome-webfont.ttf", 16.0f, &config, ranges);
ImGui::PushFont(font);
Where does it crash? Does are two separate blocks of code, right? (you need to call io.Fonts.GetTex* to get and upload the texture atlas. The ranges[] needs to be in scope, perhaps add a static in front of ImWchar ranges[].
No it is one code block !
It crash at "SetCurrentFont": IM_ASSERT(font && font->IsLoaded());
I have not called "GetTexDataAsRGBA32", but what it is used for ?
and how to use it ?
Thx
You cannot use the font before it has been built into a font atlas.
Your existing code is probably calling GetTextData*
somewhere else nothing would work. This create a bitmap texture out of all the font and you can upload the texture on GPU. Check the examples.
Thanks Omar,
I have just put the following code before the GetTexData* :
// Font awesome
io.Fonts->AddFontDefault();
ImWchar ranges[] = { 0xf000, 0xf3ff, 0 };
ImFontConfig config;
config.MergeMode = true;
VisualDesignerApp::_fontAwesome = io.Fonts->AddFontFromFileTTF("install/fontawesome-webfont.ttf", 16.0f, &config); // , ranges);
But when I try to use the font I got the wrong symbok, I got an ASCII one ?
ImGui::PushFont(VisualDesignerApp::_fontAwesome);
ImGui::Button("\uF04B");
ImGui::PopFont();
Well you aren't passing the desired ranges to AddFont. And when you do make sure ranges[] is global/static.
Yes, it is what I have do : 1) ranges are static 2) they are passed to AddFont (Not in the code here, but I have try too)
ImGui::Button("\uF04B"); isn't correct it is 16-bit unicode whereas ImGui takes UTF-8.
According to this it looks like you can utf-8 encode using an u8 (unsigned char) prefix: https://github.com/juliettef/IconFontCppHeaders/blob/master/IconsFontAwesome.h
Actually I am being confused with those fancy modern literals http://en.cppreference.com/w/cpp/language/string_literal Just in case try the UTF-8 representation "\xEF\x81\x8B"
<IconsFontAwesome_c.h>
insteadI use IconsFontAwesome_c.h to build icon font,but crash in imgui_draw.cpp Line 1273 ==>"IM_ASSERT(font_offset >= 0);" Pls help me ,THS!
adjust your path to the .ttf file, or move the executable to a proper folder
Redownload.ttf file ,Compile it sucessfully now,Thanks!
I've tried loading some icon fonts and it works great for button icons etc. Anyway if you want to make a button (or other control) with an Icon followed by some text, you cannot do it easily. The current ImGui::Button() call that assumes a single font for the label, unless you bake your ttf characters into the icon font (or the opposite). My opinion is that for practical use it should be possible to map those icon fonts into some glyph range of current font (not a new one). I've not gone in the details of the source code generating the glyphs so I'm not sure of the implications and/or difficulty. Just as an idea, icon fonts could be used to create the vector graphics for new ui/graphics theme like described in #184 without the need of bitmaps (that will not scale easily on higher DPI displays) or fancy SVG drawings (that requires an SVG parser/renderer etc).
Comments?