Merge branch 'master' into navigation

docking
omar 7 years ago
commit bf0d9c123e

@ -23,19 +23,23 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i
- window/tooltip: allow to set the width of a tooltip to allow TextWrapped() etc. while keeping the height automatic. - window/tooltip: allow to set the width of a tooltip to allow TextWrapped() etc. while keeping the height automatic.
- window: increase minimum size of a window with menus or fix the menu rendering so that it doesn't look odd. - window: increase minimum size of a window with menus or fix the menu rendering so that it doesn't look odd.
- window: double-clicking on title bar to minimize isn't consistent, perhaps move to single-click on left-most collapse icon? - window: double-clicking on title bar to minimize isn't consistent, perhaps move to single-click on left-most collapse icon?
- window: expose contents size. (#1045)
- window: GetWindowSize() returns (0,0) when not calculated? (#1045)
!- scrolling: allow immediately effective change of scroll after Begin() if we haven't appended items yet. !- scrolling: allow immediately effective change of scroll after Begin() if we haven't appended items yet.
- scrolling/clipping: separator on the initial position of a window is not visible (cursorpos.y <= clippos.y) - scrolling/clipping: separator on the initial position of a window is not visible (cursorpos.y <= clippos.y)
- drawlist: move Font, FontSize, FontTexUvWhitePixel inside ImDrawList and make it self-contained (apart from drawing settings?) - drawlist: move Font, FontSize, FontTexUvWhitePixel inside ImDrawList and make it self-contained (apart from drawing settings?)
- drawlist: make it easier to toggle AA per primitive, so we can use e.g. non-AA fill + AA borders more naturally
- drawlist: end-user probably can't call Clear() directly because we expect a texture to be pushed in the stack. - drawlist: end-user probably can't call Clear() directly because we expect a texture to be pushed in the stack.
- drawlist: maintaining bounding box per command would allow to merge draw command when clipping isn't relied on (typical non-scrolling window or non-overflowing column would merge with previous command). - drawlist: maintaining bounding box per command would allow to merge draw command when clipping isn't relied on (typical non-scrolling window or non-overflowing column would merge with previous command).
- drawlist: avoid passing null (-9999,+9999) rectangle to end-user, instead perhaps pass rectangle based on io.DisplaySize? - drawlist: avoid passing null (-9999,+9999) rectangle to end-user, instead perhaps pass rectangle based on io.DisplaySize?
- drawlist: primtiives/helpers to manipulate vertices post submission, so e.g. a quad/rect can be resized to fit later submitted content, _without_ using the ChannelSplit api
- main: considering adding an Init() function? some constructs are awkward in the implementation because of the lack of them. - main: considering adding an Init() function? some constructs are awkward in the implementation because of the lack of them.
- main: find a way to preserve relative orders of multiple reappearing windows (so an app toggling between "modes" e.g. fullscreen vs all tools) won't lose relative ordering. - main: find a way to preserve relative orders of multiple reappearing windows (so an app toggling between "modes" e.g. fullscreen vs all tools) won't lose relative ordering.
- main: IsItemHovered() make it more consistent for various type of widgets, widgets with multiple components, etc. also effectively IsHovered() region sometimes differs from hot region, e.g tree nodes - main: IsItemHovered() make it more consistent for various type of widgets, widgets with multiple components, etc. also effectively IsHovered() region sometimes differs from hot region, e.g tree nodes
- main: IsItemHovered() info stored in a stack? so that 'if TreeNode() { Text; TreePop; } if IsHovered' return the hover state of the TreeNode? - main: IsItemHovered() info stored in a stack? so that 'if TreeNode() { Text; TreePop; } if IsHovered' return the hover state of the TreeNode?
- main: rename the main "Debug" window to avoid ID collision with user who may want to use "Debug" with specific flags - main: rename the main "Debug" window to avoid ID collision with user who may want to use "Debug" with specific flags.
- widgets: display mode: widget-label, label-widget (aligned on column or using fixed size), label-newline-tab-widget etc. (#395) - widgets: display mode: widget-label, label-widget (aligned on column or using fixed size), label-newline-tab-widget etc. (#395)
- widgets: clean up widgets internal toward exposing everything and stabilizing imgui_internals.h. - widgets: clean up widgets internal toward exposing everything and stabilizing imgui_internals.h.
@ -64,11 +68,14 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i
- input number: use mouse wheel to step up/down - input number: use mouse wheel to step up/down
- input number: applying arithmetics ops (+,-,*,/) messes up with text edit undo stack. - input number: applying arithmetics ops (+,-,*,/) messes up with text edit undo stack.
- layout: helper or a way to express ImGui::SameLine(ImGui::GetCursorStartPos().x + ImGui::CalcItemWidth() + ImGui::GetStyle().ItemInnerSpacing.x); in a simpler manner.
- layout: generalization of the above: a concept equivalent to word processor ruler tab stop ~ mini columns (position in X, no clipping implied) (vaguely relate to #267, #395, also what is used internally for menu items)
- layout: horizontal layout helper (#97) - layout: horizontal layout helper (#97)
- layout: horizontal flow until no space left (#404) - layout: horizontal flow until no space left (#404)
- layout: more generic alignment state (left/right/centered) for single items? - layout: more generic alignment state (left/right/centered) for single items?
- layout: clean up the InputFloatN/SliderFloatN/ColorEdit4 layout code. item width should include frame padding. - layout: clean up the InputFloatN/SliderFloatN/ColorEdit4 layout code. item width should include frame padding.
- layout: BeginGroup() needs a border option. - layout: BeginGroup() needs a border option.
- layout: vertical alignement of mixed height items (e.g. buttons) within a same line (#1284)
- columns: sizing policy (e.g. for each column: fixed size, %, fill, distribute default size among fills) (#513, #125) - columns: sizing policy (e.g. for each column: fixed size, %, fill, distribute default size among fills) (#513, #125)
- columns: add a conditional parameter to SetColumnOffset() (#513, #125) - columns: add a conditional parameter to SetColumnOffset() (#513, #125)
@ -78,8 +85,9 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i
- columns: flag to add horizontal separator above/below? - columns: flag to add horizontal separator above/below?
- columns/layout: setup minimum line height (equivalent of automatically calling AlignFirstTextHeightToWidgets) - columns/layout: setup minimum line height (equivalent of automatically calling AlignFirstTextHeightToWidgets)
!- color: the color helpers/types are a mess and needs sorting out. !- color: the color conversion helpers/types are a mess and needs sorting out.
- color: (api breaking) ImGui::ColorConvertXXX functions should be loose ImColorConvertXX to match imgui_internals.h - color: (api breaking) ImGui::ColorConvertXXX functions should be loose ImColorConvertXX to match imgui_internals.h
- coloredit: it is still somehow awkward to copy colors around (unless going through Hex mode).
- plot: full featured plot/graph api w/ scrolling, zooming etc. all bell & whistle. why not! - plot: full featured plot/graph api w/ scrolling, zooming etc. all bell & whistle. why not!
- plot: PlotLines() should use the polygon-stroke facilities, less verticles (currently issues with averaging normals) - plot: PlotLines() should use the polygon-stroke facilities, less verticles (currently issues with averaging normals)
@ -158,11 +166,12 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i
!- settings: expose enough to save/load .ini from RAM instead of fopen !- settings: expose enough to save/load .ini from RAM instead of fopen
- settings: write more decent code to allow saving/loading new fields - settings: write more decent code to allow saving/loading new fields
- settings: api for per-tool simple persistent data (bool,int,float,columns sizes,etc.) in .ini file - settings: api for per-tool simple persistent data (bool,int,float,columns sizes,etc.) in .ini file (#437)
- stb: add defines to disable stb implementations - stb: add defines to disable stb implementations
!- style: better default styles. !- style: better default styles.
!- style: move border to style structure, remove _ShowBorder flag. !- style: move border to style structure, remove _ShowBorder flag.
- style: border types: out-screen, in-screen, etc.
- style/optimization: store rounded corners in texture to use 1 quad per corner (filled and wireframe) to lower the cost of rounding. - style/optimization: store rounded corners in texture to use 1 quad per corner (filled and wireframe) to lower the cost of rounding.
- style: add window shadow (fading away from the window. Paint-style calculation of vertices alpha after drawlist would be easier) - style: add window shadow (fading away from the window. Paint-style calculation of vertices alpha after drawlist would be easier)
- style: color-box not always square? - style: color-box not always square?
@ -171,6 +180,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i
- style: global scale setting. - style: global scale setting.
- style: WindowPadding needs to be EVEN as the 0.5 multiplier used on this value probably have a subtle effect on clip rectangle - style: WindowPadding needs to be EVEN as the 0.5 multiplier used on this value probably have a subtle effect on clip rectangle
- style: have a more global HSV setter (e.g. alter hue on all elements). consider replacing active/hovered by offset in HSV space? (#438, #707, #1223) - style: have a more global HSV setter (e.g. alter hue on all elements). consider replacing active/hovered by offset in HSV space? (#438, #707, #1223)
- style: gradients fill (#1223) ~ 2 bg colors for each fill? tricky with rounded shapes and using textures for corners.
- style editor: color child window height expressed in multiple of line height. - style editor: color child window height expressed in multiple of line height.
- log: LogButtons() options for specifying depth and/or hiding depth slider - log: LogButtons() options for specifying depth and/or hiding depth slider
@ -205,12 +215,13 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i
- keyboard: full keyboard navigation and focus. (#323) - keyboard: full keyboard navigation and focus. (#323)
- focus: preserve ActiveId/focus stack state, e.g. when opening a menu and close it, previously selected InputText() focus gets restored (#622) - focus: preserve ActiveId/focus stack state, e.g. when opening a menu and close it, previously selected InputText() focus gets restored (#622)
- focus: SetKeyboardFocusHere() on with >= 0 offset could be done on same frame (else latch and modulate on beginning of next frame) - focus: SetKeyboardFocusHere() on with >= 0 offset could be done on same frame (else latch and modulate on beginning of next frame)
- input: rework IO system to be able to pass actual ordered/timestamped events. (~#335, #71) - inputs: rework IO system to be able to pass actual ordered/timestamped events. use an event queue? (~#335, #71)
- input: allow to decide and pass explicit double-clicks (e.g. for windows by the CS_DBLCLKS style). - inputs: allow to decide and pass explicit double-clicks (e.g. for windows by the CS_DBLCLKS style).
- input: support track pad style scrolling & slider edit. - inputs: support track pad style scrolling & slider edit.
- misc: provide a way to compile out the entire implementation while providing a dummy API (e.g. #define IMGUI_DUMMY_IMPL) - misc: provide a way to compile out the entire implementation while providing a dummy API (e.g. #define IMGUI_DUMMY_IMPL)
- misc: provide HoveredTime and ActivatedTime to ease the creation of animations. - misc: provide HoveredTime and ActivatedTime to ease the creation of animations.
- misc: fix for compilation settings where stdcall isn't the default (e.g. vectorcall) (#1230)
- remote: make a system like RemoteImGui first-class citizen/project (#75) - remote: make a system like RemoteImGui first-class citizen/project (#75)
- demo: demo: add a virtual scrolling example? - demo: demo: add a virtual scrolling example?

@ -240,6 +240,8 @@
Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code. Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code.
Also read releases logs https://github.com/ocornut/imgui/releases for more details. Also read releases logs https://github.com/ocornut/imgui/releases for more details.
- 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame.
- 2017/08/15 (1.51) - changed parameter order for BeginPopupContextWindow(), note that most uses relied on default parameters completely.
- 2017/08/13 (1.51) - renamed ImGuiCol_Columns_*** to ImGuiCol_Separator_*** - 2017/08/13 (1.51) - renamed ImGuiCol_Columns_*** to ImGuiCol_Separator_***
- 2017/08/11 (1.51) - renamed ImGuiSetCond_*** types and flags to ImGuiCond_***. Kept redirection enums (will obsolete). - 2017/08/11 (1.51) - renamed ImGuiSetCond_*** types and flags to ImGuiCond_***. Kept redirection enums (will obsolete).
- 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton(). - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().
@ -654,11 +656,10 @@ static void MarkIniSettingsDirty(ImGuiWindow* window);
static void PushColumnClipRect(int column_index = -1); static void PushColumnClipRect(int column_index = -1);
static ImRect GetVisibleRect(); static ImRect GetVisibleRect();
static bool BeginPopupEx(const char* str_id, ImGuiWindowFlags extra_flags); static bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags);
static void CloseInactivePopups(); static void CloseInactivePopups();
static void ClosePopupToLevel(int remaining); static void ClosePopupToLevel(int remaining);
static void ClosePopup(ImGuiID id); static void ClosePopup(ImGuiID id);
static bool IsPopupOpen(ImGuiID id);
static ImGuiWindow* GetFrontMostModalRootWindow(); static ImGuiWindow* GetFrontMostModalRootWindow();
static ImVec2 FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size, int* last_dir, const ImRect& rect_to_avoid); static ImVec2 FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size, int* last_dir, const ImRect& rect_to_avoid);
@ -4133,21 +4134,14 @@ void ImGui::EndTooltip()
ImGui::End(); ImGui::End();
} }
static bool IsPopupOpen(ImGuiID id)
{
ImGuiContext& g = *GImGui;
return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == id;
}
// Mark popup as open (toggle toward open state). // Mark popup as open (toggle toward open state).
// Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. // Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.
// Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). // Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
// One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL) // One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)
void ImGui::OpenPopupEx(const char* str_id, bool reopen_existing) void ImGui::OpenPopupEx(ImGuiID id, bool reopen_existing)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow; ImGuiWindow* window = g.CurrentWindow;
ImGuiID id = window->GetID(str_id);
int current_stack_size = g.CurrentPopupStack.Size; int current_stack_size = g.CurrentPopupStack.Size;
ImVec2 mouse_pos = g.IO.MousePos; ImVec2 mouse_pos = g.IO.MousePos;
ImVec2 popup_pos = (!g.NavDisableHighlight && g.NavDisableMouseHover) ? NavCalcPreferredMousePos() : mouse_pos; ImVec2 popup_pos = (!g.NavDisableHighlight && g.NavDisableMouseHover) ? NavCalcPreferredMousePos() : mouse_pos;
@ -4163,7 +4157,8 @@ void ImGui::OpenPopupEx(const char* str_id, bool reopen_existing)
void ImGui::OpenPopup(const char* str_id) void ImGui::OpenPopup(const char* str_id)
{ {
ImGui::OpenPopupEx(str_id, false); ImGuiContext& g = *GImGui;
OpenPopupEx(g.CurrentWindow->GetID(str_id), false);
} }
static void CloseInactivePopups() static void CloseInactivePopups()
@ -4219,7 +4214,7 @@ static void ClosePopupToLevel(int remaining)
static void ClosePopup(ImGuiID id) static void ClosePopup(ImGuiID id)
{ {
if (!IsPopupOpen(id)) if (!ImGui::IsPopupOpen(id))
return; return;
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ClosePopupToLevel(g.OpenPopupStack.Size - 1); ClosePopupToLevel(g.OpenPopupStack.Size - 1);
@ -4239,17 +4234,17 @@ void ImGui::CloseCurrentPopup()
static inline void ClearSetNextWindowData() static inline void ClearSetNextWindowData()
{ {
// FIXME-OPT
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
g.SetNextWindowPosCond = g.SetNextWindowSizeCond = g.SetNextWindowContentSizeCond = g.SetNextWindowCollapsedCond = 0; g.SetNextWindowPosCond = g.SetNextWindowSizeCond = g.SetNextWindowContentSizeCond = g.SetNextWindowCollapsedCond = 0;
g.SetNextWindowSizeConstraint = g.SetNextWindowFocus = false; g.SetNextWindowSizeConstraint = g.SetNextWindowFocus = false;
} }
static bool BeginPopupEx(const char* str_id, ImGuiWindowFlags extra_flags) static bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow; ImGuiWindow* window = g.CurrentWindow;
const ImGuiID id = window->GetID(str_id); if (!ImGui::IsPopupOpen(id))
if (!IsPopupOpen(id))
{ {
ClearSetNextWindowData(); // We behave like Begin() and need to consume those values ClearSetNextWindowData(); // We behave like Begin() and need to consume those values
return false; return false;
@ -4275,12 +4270,26 @@ static bool BeginPopupEx(const char* str_id, ImGuiWindowFlags extra_flags)
bool ImGui::BeginPopup(const char* str_id) bool ImGui::BeginPopup(const char* str_id)
{ {
if (GImGui->OpenPopupStack.Size <= GImGui->CurrentPopupStack.Size) // Early out for performance ImGuiContext& g = *GImGui;
if (g.OpenPopupStack.Size <= g.CurrentPopupStack.Size) // Early out for performance
{ {
ClearSetNextWindowData(); // We behave like Begin() and need to consume those values ClearSetNextWindowData(); // We behave like Begin() and need to consume those values
return false; return false;
} }
return BeginPopupEx(str_id, ImGuiWindowFlags_ShowBorders); return BeginPopupEx(g.CurrentWindow->GetID(str_id), ImGuiWindowFlags_ShowBorders);
}
// FIXME
bool ImGui::IsPopupOpen(ImGuiID id)
{
ImGuiContext& g = *GImGui;
return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == id;
}
bool ImGui::IsPopupOpen(const char* str_id)
{
ImGuiContext& g = *GImGui;
return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == g.CurrentWindow->GetID(str_id);
} }
bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags extra_flags) bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags extra_flags)
@ -4323,29 +4332,31 @@ void ImGui::EndPopup()
// 2. If you want right-clicking on the same item to reopen the popup at new location, use the same code replacing IsItemHovered() with IsItemHoveredRect() // 2. If you want right-clicking on the same item to reopen the popup at new location, use the same code replacing IsItemHovered() with IsItemHoveredRect()
// and passing true to the OpenPopupEx(). // and passing true to the OpenPopupEx().
// Because: hovering an item in a window below the popup won't normally trigger is hovering behavior/coloring. The pattern of ignoring the fact that // Because: hovering an item in a window below the popup won't normally trigger is hovering behavior/coloring. The pattern of ignoring the fact that
// the item isn't interactable (because it is blocked by the active popup) may useful in some situation when e.g. large canvas as one item, content of menu // the item can be interacted with (because it is blocked by the active popup) may useful in some situation when e.g. large canvas as one item, content of menu
// driven by click position. // driven by click position.
bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button) bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button)
{ {
if (IsItemHovered() && IsMouseClicked(mouse_button)) if (IsItemHovered() && IsMouseClicked(mouse_button))
OpenPopupEx(str_id, false); OpenPopupEx(GImGui->CurrentWindow->GetID(str_id), false);
return BeginPopup(str_id); return BeginPopup(str_id);
} }
bool ImGui::BeginPopupContextWindow(bool also_over_items, const char* str_id, int mouse_button) bool ImGui::BeginPopupContextWindow(const char* str_id, int mouse_button, bool also_over_items)
{ {
if (!str_id) str_id = "window_context_menu"; if (!str_id)
str_id = "window_context";
if (IsWindowHoveredRect() && IsMouseClicked(mouse_button)) if (IsWindowHoveredRect() && IsMouseClicked(mouse_button))
if (also_over_items || !IsAnyItemHovered()) if (also_over_items || !IsAnyItemHovered())
OpenPopupEx(str_id, true); OpenPopupEx(GImGui->CurrentWindow->GetID(str_id), true);
return BeginPopup(str_id); return BeginPopup(str_id);
} }
bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button) bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button)
{ {
if (!str_id) str_id = "void_context_menu"; if (!str_id)
str_id = "void_context";
if (!IsAnyWindowHovered() && IsMouseClicked(mouse_button)) if (!IsAnyWindowHovered() && IsMouseClicked(mouse_button))
OpenPopupEx(str_id, true); OpenPopupEx(GImGui->CurrentWindow->GetID(str_id), true);
return BeginPopup(str_id); return BeginPopup(str_id);
} }
@ -7063,32 +7074,32 @@ void ImGui::SetNextTreeNodeOpen(bool is_open, ImGuiCond cond)
void ImGui::PushID(const char* str_id) void ImGui::PushID(const char* str_id)
{ {
ImGuiWindow* window = GetCurrentWindow(); ImGuiWindow* window = GetCurrentWindowRead();
window->IDStack.push_back(window->GetID(str_id)); window->IDStack.push_back(window->GetID(str_id));
} }
void ImGui::PushID(const char* str_id_begin, const char* str_id_end) void ImGui::PushID(const char* str_id_begin, const char* str_id_end)
{ {
ImGuiWindow* window = GetCurrentWindow(); ImGuiWindow* window = GetCurrentWindowRead();
window->IDStack.push_back(window->GetID(str_id_begin, str_id_end)); window->IDStack.push_back(window->GetID(str_id_begin, str_id_end));
} }
void ImGui::PushID(const void* ptr_id) void ImGui::PushID(const void* ptr_id)
{ {
ImGuiWindow* window = GetCurrentWindow(); ImGuiWindow* window = GetCurrentWindowRead();
window->IDStack.push_back(window->GetID(ptr_id)); window->IDStack.push_back(window->GetID(ptr_id));
} }
void ImGui::PushID(int int_id) void ImGui::PushID(int int_id)
{ {
const void* ptr_id = (void*)(intptr_t)int_id; const void* ptr_id = (void*)(intptr_t)int_id;
ImGuiWindow* window = GetCurrentWindow(); ImGuiWindow* window = GetCurrentWindowRead();
window->IDStack.push_back(window->GetID(ptr_id)); window->IDStack.push_back(window->GetID(ptr_id));
} }
void ImGui::PopID() void ImGui::PopID()
{ {
ImGuiWindow* window = GetCurrentWindow(); ImGuiWindow* window = GetCurrentWindowRead();
window->IDStack.pop_back(); window->IDStack.pop_back();
} }
@ -9488,7 +9499,7 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi
PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding); PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding);
const ImGuiWindowFlags flags = ImGuiWindowFlags_ComboBox | ((window->Flags & ImGuiWindowFlags_ShowBorders) ? ImGuiWindowFlags_ShowBorders : 0); const ImGuiWindowFlags flags = ImGuiWindowFlags_ComboBox | ((window->Flags & ImGuiWindowFlags_ShowBorders) ? ImGuiWindowFlags_ShowBorders : 0);
if (BeginPopupEx(label, flags)) if (BeginPopupEx(id, flags))
{ {
// Display items // Display items
// FIXME-OPT: Use clipper // FIXME-OPT: Use clipper
@ -9933,7 +9944,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled)
{ {
SetNextWindowPos(popup_pos, ImGuiCond_Always); SetNextWindowPos(popup_pos, ImGuiCond_Always);
ImGuiWindowFlags flags = ImGuiWindowFlags_ShowBorders | ((window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) ? ImGuiWindowFlags_ChildMenu|ImGuiWindowFlags_ChildWindow : ImGuiWindowFlags_ChildMenu); ImGuiWindowFlags flags = ImGuiWindowFlags_ShowBorders | ((window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) ? ImGuiWindowFlags_ChildMenu|ImGuiWindowFlags_ChildWindow : ImGuiWindowFlags_ChildMenu);
menu_is_open = BeginPopupEx(label, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)
} }
return menu_is_open; return menu_is_open;

@ -383,13 +383,14 @@ namespace ImGui
IMGUI_API bool MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled = true); // return true when activated + toggle (*p_selected) if p_selected != NULL IMGUI_API bool MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled = true); // return true when activated + toggle (*p_selected) if p_selected != NULL
// Popups // Popups
IMGUI_API void OpenPopup(const char* str_id); // mark popup as open. popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). IMGUI_API void OpenPopup(const char* str_id); // call to mark popup as open (don't call every frame!). popups are closed when user click outside, or if CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. By default, Selectable()/MenuItem() are calling CloseCurrentPopup(). Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).
IMGUI_API bool BeginPopup(const char* str_id); // return true if the popup is open, and you can start outputting to it. only call EndPopup() if BeginPopup() returned true! IMGUI_API bool BeginPopup(const char* str_id); // return true if the popup is open, and you can start outputting to it. only call EndPopup() if BeginPopup() returned true!
IMGUI_API bool BeginPopupModal(const char* name, bool* p_open = NULL, ImGuiWindowFlags extra_flags = 0); // modal dialog (block interactions behind the modal window, can't close the modal window by clicking outside) IMGUI_API bool BeginPopupModal(const char* name, bool* p_open = NULL, ImGuiWindowFlags extra_flags = 0); // modal dialog (block interactions behind the modal window, can't close the modal window by clicking outside)
IMGUI_API bool BeginPopupContextItem(const char* str_id, int mouse_button = 1); // helper to open and begin popup when clicked on last item. read comments in .cpp! IMGUI_API bool BeginPopupContextItem(const char* str_id, int mouse_button = 1); // helper to open and begin popup when clicked on last item. read comments in .cpp!
IMGUI_API bool BeginPopupContextWindow(bool also_over_items = true, const char* str_id = NULL, int mouse_button = 1); // helper to open and begin popup when clicked on current window. IMGUI_API bool BeginPopupContextWindow(const char* str_id = NULL, int mouse_button = 1, bool also_over_items = true); // helper to open and begin popup when clicked on current window.
IMGUI_API bool BeginPopupContextVoid(const char* str_id = NULL, int mouse_button = 1); // helper to open and begin popup when clicked in void (no window). IMGUI_API bool BeginPopupContextVoid(const char* str_id = NULL, int mouse_button = 1); // helper to open and begin popup when clicked in void (no window).
IMGUI_API void EndPopup(); IMGUI_API void EndPopup();
IMGUI_API bool IsPopupOpen(const char* str_id); // return true if the popup is open
IMGUI_API void CloseCurrentPopup(); // close the popup we have begin-ed into. clicking on a MenuItem or Selectable automatically close the current popup. IMGUI_API void CloseCurrentPopup(); // close the popup we have begin-ed into. clicking on a MenuItem or Selectable automatically close the current popup.
// Logging: all text output from interface is redirected to tty/file/clipboard. By default, tree nodes are automatically opened during logging. // Logging: all text output from interface is redirected to tty/file/clipboard. By default, tree nodes are automatically opened during logging.
@ -947,7 +948,7 @@ public:
inline const value_type& back() const { IM_ASSERT(Size > 0); return Data[Size-1]; } inline const value_type& back() const { IM_ASSERT(Size > 0); return Data[Size-1]; }
inline void swap(ImVector<T>& rhs) { int rhs_size = rhs.Size; rhs.Size = Size; Size = rhs_size; int rhs_cap = rhs.Capacity; rhs.Capacity = Capacity; Capacity = rhs_cap; value_type* rhs_data = rhs.Data; rhs.Data = Data; Data = rhs_data; } inline void swap(ImVector<T>& rhs) { int rhs_size = rhs.Size; rhs.Size = Size; Size = rhs_size; int rhs_cap = rhs.Capacity; rhs.Capacity = Capacity; Capacity = rhs_cap; value_type* rhs_data = rhs.Data; rhs.Data = Data; Data = rhs_data; }
inline int _grow_capacity(int new_size) { int new_capacity = Capacity ? (Capacity + Capacity/2) : 8; return new_capacity > new_size ? new_capacity : new_size; } inline int _grow_capacity(int size) const { int new_capacity = Capacity ? (Capacity + Capacity/2) : 8; return new_capacity > size ? new_capacity : size; }
inline void resize(int new_size) { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; } inline void resize(int new_size) { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }
inline void reserve(int new_capacity) inline void reserve(int new_capacity)
@ -968,15 +969,11 @@ public:
inline iterator insert(const_iterator it, const value_type& v) { IM_ASSERT(it >= Data && it <= Data+Size); const ptrdiff_t off = it - Data; if (Size == Capacity) reserve(Capacity ? Capacity * 2 : 4); if (off < (int)Size) memmove(Data + off + 1, Data + off, ((size_t)Size - (size_t)off) * sizeof(value_type)); Data[off] = v; Size++; return Data + off; } inline iterator insert(const_iterator it, const value_type& v) { IM_ASSERT(it >= Data && it <= Data+Size); const ptrdiff_t off = it - Data; if (Size == Capacity) reserve(Capacity ? Capacity * 2 : 4); if (off < (int)Size) memmove(Data + off + 1, Data + off, ((size_t)Size - (size_t)off) * sizeof(value_type)); Data[off] = v; Size++; return Data + off; }
}; };
// Helper: execute a block of code at maximum once a frame // Helper: execute a block of code at maximum once a frame. Convenient if you want to quickly create an UI within deep-nested code that runs multiple times every frame.
// Convenient if you want to quickly create an UI within deep-nested code that runs multiple times every frame.
// Usage: // Usage:
// IMGUI_ONCE_UPON_A_FRAME // static ImGuiOnceUponAFrame oaf;
// { // if (oaf)
// // code block will be executed one per frame // ImGui::Text("This will be called only once per frame");
// }
// Attention! the macro expands into 2 statement so make sure you don't use it within e.g. an if() statement without curly braces.
#define IMGUI_ONCE_UPON_A_FRAME static ImGuiOnceUponAFrame imgui_oaf##__LINE__; if (imgui_oaf##__LINE__)
struct ImGuiOnceUponAFrame struct ImGuiOnceUponAFrame
{ {
ImGuiOnceUponAFrame() { RefFrame = -1; } ImGuiOnceUponAFrame() { RefFrame = -1; }
@ -984,6 +981,11 @@ struct ImGuiOnceUponAFrame
operator bool() const { int current_frame = ImGui::GetFrameCount(); if (RefFrame == current_frame) return false; RefFrame = current_frame; return true; } operator bool() const { int current_frame = ImGui::GetFrameCount(); if (RefFrame == current_frame) return false; RefFrame = current_frame; return true; }
}; };
// Helper macro for ImGuiOnceUponAFrame. Attention: The macro expands into 2 statement so make sure you don't use it within e.g. an if() statement without curly braces.
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // Will obsolete
#define IMGUI_ONCE_UPON_A_FRAME static ImGuiOnceUponAFrame imgui_oaf; if (imgui_oaf)
#endif
// Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]" // Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]"
struct ImGuiTextFilter struct ImGuiTextFilter
{ {
@ -1007,11 +1009,11 @@ struct ImGuiTextFilter
ImVector<TextRange> Filters; ImVector<TextRange> Filters;
int CountGrep; int CountGrep;
ImGuiTextFilter(const char* default_filter = ""); IMGUI_API ImGuiTextFilter(const char* default_filter = "");
~ImGuiTextFilter() {} ~ImGuiTextFilter() {}
void Clear() { InputBuf[0] = 0; Build(); } void Clear() { InputBuf[0] = 0; Build(); }
bool Draw(const char* label = "Filter (inc,-exc)", float width = 0.0f); // Helper calling InputText+Build IMGUI_API bool Draw(const char* label = "Filter (inc,-exc)", float width = 0.0f); // Helper calling InputText+Build
bool PassFilter(const char* text, const char* text_end = NULL) const; IMGUI_API bool PassFilter(const char* text, const char* text_end = NULL) const;
bool IsActive() const { return !Filters.empty(); } bool IsActive() const { return !Filters.empty(); }
IMGUI_API void Build(); IMGUI_API void Build();
}; };
@ -1427,13 +1429,26 @@ struct ImFontAtlas
int TexWidth; // Texture width calculated during Build(). int TexWidth; // Texture width calculated during Build().
int TexHeight; // Texture height calculated during Build(). int TexHeight; // Texture height calculated during Build().
int TexDesiredWidth; // Texture width desired by user before Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height. int TexDesiredWidth; // Texture width desired by user before Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height.
int TexGlyphPadding; // Padding between glyphs within texture in pixels. Defaults to 1.
ImVec2 TexUvWhitePixel; // Texture coordinates to a white pixel ImVec2 TexUvWhitePixel; // Texture coordinates to a white pixel
ImVector<ImFont*> Fonts; // Hold all the fonts returned by AddFont*. Fonts[0] is the default font upon calling ImGui::NewFrame(), use ImGui::PushFont()/PopFont() to change the current font. ImVector<ImFont*> Fonts; // Hold all the fonts returned by AddFont*. Fonts[0] is the default font upon calling ImGui::NewFrame(), use ImGui::PushFont()/PopFont() to change the current font.
// Private // [Private] User rectangle for packing custom texture data into the atlas.
struct CustomRect
{
unsigned int ID; // Input // User ID. <0x10000 for font mapped data (WIP/UNSUPPORTED), >=0x10000 for other texture data
unsigned short Width, Height; // Input // Desired rectangle dimension
unsigned short X, Y; // Output // Packed position in Atlas
CustomRect() { ID = 0xFFFFFFFF; Width = Height = 0; X = Y = 0xFFFF; }
bool IsPacked() const { return X != 0xFFFF; }
};
// [Private] Members
ImVector<CustomRect> CustomRects; // Rectangles for packing custom texture data into the atlas.
ImVector<ImFontConfig> ConfigData; // Internal data ImVector<ImFontConfig> ConfigData; // Internal data
IMGUI_API bool Build(); // Build pixels data. This is automatically for you by the GetTexData*** functions. IMGUI_API bool Build(); // Build pixels data. This is automatically for you by the GetTexData*** functions.
IMGUI_API void RenderCustomTexData(int pass, void* rects); IMGUI_API int CustomRectRegister(unsigned int id, int width, int height);
IMGUI_API void CustomRectCalcUV(const CustomRect* rect, ImVec2* out_uv_min, ImVec2* out_uv_max);
}; };
// Font runtime data and rendering // Font runtime data and rendering

@ -311,14 +311,11 @@ void ImGui::ShowTestWindow(bool* p_open)
ImGui::EndTooltip(); ImGui::EndTooltip();
} }
// Testing IMGUI_ONCE_UPON_A_FRAME macro // Testing ImGuiOnceUponAFrame helper.
//static ImGuiOnceUponAFrame once;
//for (int i = 0; i < 5; i++) //for (int i = 0; i < 5; i++)
//{ // if (once)
// IMGUI_ONCE_UPON_A_FRAME // ImGui::Text("This will be displayed only once.");
// {
// ImGui::Text("This will be displayed only once.");
// }
//}
ImGui::Separator(); ImGui::Separator();
@ -1888,9 +1885,10 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
ImGui::TreePop(); ImGui::TreePop();
} }
if (ImGui::TreeNode("Fonts", "Fonts (%d)", ImGui::GetIO().Fonts->Fonts.Size)) bool fonts_opened = ImGui::TreeNode("Fonts", "Fonts (%d)", ImGui::GetIO().Fonts->Fonts.Size);
ImGui::SameLine(); ShowHelpMarker("Tip: Load fonts with io.Fonts->AddFontFromFileTTF()\nbefore calling io.Fonts->GetTex* functions.");
if (fonts_opened)
{ {
ImGui::SameLine(); ShowHelpMarker("Tip: Load fonts with io.Fonts->AddFontFromFileTTF()\nbefore calling io.Fonts->GetTex* functions.");
ImFontAtlas* atlas = ImGui::GetIO().Fonts; ImFontAtlas* atlas = ImGui::GetIO().Fonts;
if (ImGui::TreeNode("Atlas texture", "Atlas texture (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight)) if (ImGui::TreeNode("Atlas texture", "Atlas texture (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight))
{ {
@ -1901,14 +1899,13 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
for (int i = 0; i < atlas->Fonts.Size; i++) for (int i = 0; i < atlas->Fonts.Size; i++)
{ {
ImFont* font = atlas->Fonts[i]; ImFont* font = atlas->Fonts[i];
ImGui::BulletText("Font %d: \'%s\', %.2f px, %d glyphs", i, font->ConfigData ? font->ConfigData[0].Name : "", font->FontSize, font->Glyphs.Size); bool font_details_opened = ImGui::TreeNode(font, "Font %d: \'%s\', %.2f px, %d glyphs", i, font->ConfigData ? font->ConfigData[0].Name : "", font->FontSize, font->Glyphs.Size);
ImGui::TreePush((void*)(intptr_t)i);
ImGui::SameLine(); if (ImGui::SmallButton("Set as default")) ImGui::GetIO().FontDefault = font; ImGui::SameLine(); if (ImGui::SmallButton("Set as default")) ImGui::GetIO().FontDefault = font;
ImGui::PushFont(font); if (font_details_opened)
ImGui::Text("The quick brown fox jumps over the lazy dog");
ImGui::PopFont();
if (ImGui::TreeNode("Details"))
{ {
ImGui::PushFont(font);
ImGui::Text("The quick brown fox jumps over the lazy dog");
ImGui::PopFont();
ImGui::DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f"); // Scale only this font ImGui::DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f"); // Scale only this font
ImGui::SameLine(); ShowHelpMarker("Note than the default embedded font is NOT meant to be scaled.\n\nFont are currently rendered into bitmaps at a given size at the time of building the atlas. You may oversample them to get some flexibility with scaling. You can also render at multiple sizes and select which one to use at runtime.\n\n(Glimmer of hope: the atlas system should hopefully be rewritten in the future to make scaling more natural and automatic.)"); ImGui::SameLine(); ShowHelpMarker("Note than the default embedded font is NOT meant to be scaled.\n\nFont are currently rendered into bitmaps at a given size at the time of building the atlas. You may oversample them to get some flexibility with scaling. You can also render at multiple sizes and select which one to use at runtime.\n\n(Glimmer of hope: the atlas system should hopefully be rewritten in the future to make scaling more natural and automatic.)");
ImGui::Text("Ascent: %f, Descent: %f, Height: %f", font->Ascent, font->Descent, font->Ascent - font->Descent); ImGui::Text("Ascent: %f, Descent: %f, Height: %f", font->Ascent, font->Descent, font->Ascent - font->Descent);
@ -1962,7 +1959,6 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
} }
ImGui::TreePop(); ImGui::TreePop();
} }
ImGui::TreePop();
} }
static float window_scale = 1.0f; static float window_scale = 1.0f;
ImGui::DragFloat("this window scale", &window_scale, 0.005f, 0.3f, 2.0f, "%.1f"); // scale only this window ImGui::DragFloat("this window scale", &window_scale, 0.005f, 0.3f, 2.0f, "%.1f"); // scale only this window

@ -1034,7 +1034,7 @@ void ImDrawData::ScaleClipRects(const ImVec2& scale)
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// ImFontAtlas // ImFontConfig
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
ImFontConfig::ImFontConfig() ImFontConfig::ImFontConfig()
@ -1055,12 +1055,53 @@ ImFontConfig::ImFontConfig()
memset(Name, 0, sizeof(Name)); memset(Name, 0, sizeof(Name));
} }
//-----------------------------------------------------------------------------
// ImFontAtlas
//-----------------------------------------------------------------------------
// A work of art lies ahead! (. = white layer, X = black layer, others are blank)
// The white texels on the top left are the ones we'll use everywhere in ImGui to render filled shapes.
const int FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF = 90;
const int FONT_ATLAS_DEFAULT_TEX_DATA_H = 27;
const int FONT_ATLAS_DEFAULT_TEX_DATA_ID = 0xF0000;
const char FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF * FONT_ATLAS_DEFAULT_TEX_DATA_H + 1] =
{
"..- -XXXXXXX- X - X -XXXXXXX - XXXXXXX"
"..- -X.....X- X.X - X.X -X.....X - X.....X"
"--- -XXX.XXX- X...X - X...X -X....X - X....X"
"X - X.X - X.....X - X.....X -X...X - X...X"
"XX - X.X -X.......X- X.......X -X..X.X - X.X..X"
"X.X - X.X -XXXX.XXXX- XXXX.XXXX -X.X X.X - X.X X.X"
"X..X - X.X - X.X - X.X -XX X.X - X.X XX"
"X...X - X.X - X.X - XX X.X XX - X.X - X.X "
"X....X - X.X - X.X - X.X X.X X.X - X.X - X.X "
"X.....X - X.X - X.X - X..X X.X X..X - X.X - X.X "
"X......X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X XX-XX X.X "
"X.......X - X.X - X.X -X.....................X- X.X X.X-X.X X.X "
"X........X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X..X-X..X.X "
"X.........X -XXX.XXX- X.X - X..X X.X X..X - X...X-X...X "
"X..........X-X.....X- X.X - X.X X.X X.X - X....X-X....X "
"X......XXXXX-XXXXXXX- X.X - XX X.X XX - X.....X-X.....X "
"X...X..X --------- X.X - X.X - XXXXXXX-XXXXXXX "
"X..X X..X - -XXXX.XXXX- XXXX.XXXX ------------------------------------"
"X.X X..X - -X.......X- X.......X - XX XX - "
"XX X..X - - X.....X - X.....X - X.X X.X - "
" X..X - X...X - X...X - X..X X..X - "
" XX - X.X - X.X - X...XXXXXXXXXXXXX...X - "
"------------ - X - X -X.....................X- "
" ----------------------------------- X...XXXXXXXXXXXXX...X - "
" - X..X X..X - "
" - X.X X.X - "
" - XX XX - "
};
ImFontAtlas::ImFontAtlas() ImFontAtlas::ImFontAtlas()
{ {
TexID = NULL; TexID = NULL;
TexPixelsAlpha8 = NULL; TexPixelsAlpha8 = NULL;
TexPixelsRGBA32 = NULL; TexPixelsRGBA32 = NULL;
TexWidth = TexHeight = TexDesiredWidth = 0; TexWidth = TexHeight = TexDesiredWidth = 0;
TexGlyphPadding = 1;
TexUvWhitePixel = ImVec2(0, 0); TexUvWhitePixel = ImVec2(0, 0);
} }
@ -1086,6 +1127,7 @@ void ImFontAtlas::ClearInputData()
Fonts[i]->ConfigDataCount = 0; Fonts[i]->ConfigDataCount = 0;
} }
ConfigData.clear(); ConfigData.clear();
CustomRects.clear();
} }
void ImFontAtlas::ClearTexData() void ImFontAtlas::ClearTexData()
@ -1272,15 +1314,69 @@ ImFont* ImFontAtlas::AddFontFromMemoryCompressedBase85TTF(const char* compressed
return font; return font;
} }
int ImFontAtlas::CustomRectRegister(unsigned int id, int width, int height)
{
IM_ASSERT(width > 0 && width <= 0xFFFF);
IM_ASSERT(height > 0 && height <= 0xFFFF);
CustomRect r;
r.ID = id;
r.Width = (unsigned short)width;
r.Height = (unsigned short)height;
CustomRects.push_back(r);
return CustomRects.Size - 1; // Return index
}
void ImFontAtlas::CustomRectCalcUV(const CustomRect* rect, ImVec2* out_uv_min, ImVec2* out_uv_max)
{
IM_ASSERT(TexWidth > 0 && TexHeight > 0); // Font atlas needs to be built before we can calculate UV coordinates
IM_ASSERT(rect->IsPacked()); // Make sure the rectangle has been packed
*out_uv_min = ImVec2((float)rect->X / TexWidth, (float)rect->Y / TexHeight);
*out_uv_max = ImVec2((float)(rect->X + rect->Width) / TexWidth, (float)(rect->Y + rect->Height) / TexHeight);
}
bool ImFontAtlas::Build() bool ImFontAtlas::Build()
{ {
IM_ASSERT(ConfigData.Size > 0); return ImFontAtlasBuildWithStbTruetype(this);
}
TexID = NULL; bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
TexWidth = TexHeight = 0; {
TexUvWhitePixel = ImVec2(0, 0); IM_ASSERT(atlas->ConfigData.Size > 0);
ClearTexData();
ImFontAtlasBuildRegisterDefaultCustomRects(atlas);
atlas->TexID = NULL;
atlas->TexWidth = atlas->TexHeight = 0;
atlas->TexUvWhitePixel = ImVec2(0, 0);
atlas->ClearTexData();
// Count glyphs/ranges
int total_glyphs_count = 0;
int total_ranges_count = 0;
for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++)
{
ImFontConfig& cfg = atlas->ConfigData[input_i];
if (!cfg.GlyphRanges)
cfg.GlyphRanges = atlas->GetGlyphRangesDefault();
for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2, total_ranges_count++)
total_glyphs_count += (in_range[1] - in_range[0]) + 1;
}
// We need a width for the skyline algorithm. Using a dumb heuristic here to decide of width. User can override TexDesiredWidth and TexGlyphPadding if they wish.
// Width doesn't really matter much, but some API/GPU have texture size limitations and increasing width can decrease height.
atlas->TexWidth = (atlas->TexDesiredWidth > 0) ? atlas->TexDesiredWidth : (total_glyphs_count > 4000) ? 4096 : (total_glyphs_count > 2000) ? 2048 : (total_glyphs_count > 1000) ? 1024 : 512;
atlas->TexHeight = 0;
// Start packing
const int max_tex_height = 1024*32;
stbtt_pack_context spc;
stbtt_PackBegin(&spc, NULL, atlas->TexWidth, max_tex_height, 0, atlas->TexGlyphPadding, NULL);
stbtt_PackSetOversampling(&spc, 1, 1);
// Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values).
ImFontAtlasBuildPackCustomRects(atlas, spc.pack_info);
// Initialize font information (so we can error without any cleanup)
struct ImFontTempBuildData struct ImFontTempBuildData
{ {
stbtt_fontinfo FontInfo; stbtt_fontinfo FontInfo;
@ -1288,76 +1384,43 @@ bool ImFontAtlas::Build()
stbtt_pack_range* Ranges; stbtt_pack_range* Ranges;
int RangesCount; int RangesCount;
}; };
ImFontTempBuildData* tmp_array = (ImFontTempBuildData*)ImGui::MemAlloc((size_t)ConfigData.Size * sizeof(ImFontTempBuildData)); ImFontTempBuildData* tmp_array = (ImFontTempBuildData*)ImGui::MemAlloc((size_t)atlas->ConfigData.Size * sizeof(ImFontTempBuildData));
for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++)
// Initialize font information early (so we can error without any cleanup) + count glyphs
int total_glyph_count = 0;
int total_glyph_range_count = 0;
for (int input_i = 0; input_i < ConfigData.Size; input_i++)
{ {
ImFontConfig& cfg = ConfigData[input_i]; ImFontConfig& cfg = atlas->ConfigData[input_i];
ImFontTempBuildData& tmp = tmp_array[input_i]; ImFontTempBuildData& tmp = tmp_array[input_i];
IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == atlas));
IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == this));
const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)cfg.FontData, cfg.FontNo); const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)cfg.FontData, cfg.FontNo);
IM_ASSERT(font_offset >= 0); IM_ASSERT(font_offset >= 0);
if (!stbtt_InitFont(&tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset)) if (!stbtt_InitFont(&tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset))
return false; return false;
// Count glyphs
if (!cfg.GlyphRanges)
cfg.GlyphRanges = GetGlyphRangesDefault();
for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2)
{
total_glyph_count += (in_range[1] - in_range[0]) + 1;
total_glyph_range_count++;
}
} }
// Start packing. We need a known width for the skyline algorithm. Using a cheap heuristic here to decide of width. User can override TexDesiredWidth if they wish.
// After packing is done, width shouldn't matter much, but some API/GPU have texture size limitations and increasing width can decrease height.
TexWidth = (TexDesiredWidth > 0) ? TexDesiredWidth : (total_glyph_count > 4000) ? 4096 : (total_glyph_count > 2000) ? 2048 : (total_glyph_count > 1000) ? 1024 : 512;
TexHeight = 0;
const int max_tex_height = 1024*32;
stbtt_pack_context spc;
stbtt_PackBegin(&spc, NULL, TexWidth, max_tex_height, 0, 1, NULL);
// Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values).
ImVector<stbrp_rect> extra_rects;
RenderCustomTexData(0, &extra_rects);
stbtt_PackSetOversampling(&spc, 1, 1);
stbrp_pack_rects((stbrp_context*)spc.pack_info, &extra_rects[0], extra_rects.Size);
for (int i = 0; i < extra_rects.Size; i++)
if (extra_rects[i].was_packed)
TexHeight = ImMax(TexHeight, extra_rects[i].y + extra_rects[i].h);
// Allocate packing character data and flag packed characters buffer as non-packed (x0=y0=x1=y1=0) // Allocate packing character data and flag packed characters buffer as non-packed (x0=y0=x1=y1=0)
int buf_packedchars_n = 0, buf_rects_n = 0, buf_ranges_n = 0; int buf_packedchars_n = 0, buf_rects_n = 0, buf_ranges_n = 0;
stbtt_packedchar* buf_packedchars = (stbtt_packedchar*)ImGui::MemAlloc(total_glyph_count * sizeof(stbtt_packedchar)); stbtt_packedchar* buf_packedchars = (stbtt_packedchar*)ImGui::MemAlloc(total_glyphs_count * sizeof(stbtt_packedchar));
stbrp_rect* buf_rects = (stbrp_rect*)ImGui::MemAlloc(total_glyph_count * sizeof(stbrp_rect)); stbrp_rect* buf_rects = (stbrp_rect*)ImGui::MemAlloc(total_glyphs_count * sizeof(stbrp_rect));
stbtt_pack_range* buf_ranges = (stbtt_pack_range*)ImGui::MemAlloc(total_glyph_range_count * sizeof(stbtt_pack_range)); stbtt_pack_range* buf_ranges = (stbtt_pack_range*)ImGui::MemAlloc(total_ranges_count * sizeof(stbtt_pack_range));
memset(buf_packedchars, 0, total_glyph_count * sizeof(stbtt_packedchar)); memset(buf_packedchars, 0, total_glyphs_count * sizeof(stbtt_packedchar));
memset(buf_rects, 0, total_glyph_count * sizeof(stbrp_rect)); // Unnecessary but let's clear this for the sake of sanity. memset(buf_rects, 0, total_glyphs_count * sizeof(stbrp_rect)); // Unnecessary but let's clear this for the sake of sanity.
memset(buf_ranges, 0, total_glyph_range_count * sizeof(stbtt_pack_range)); memset(buf_ranges, 0, total_ranges_count * sizeof(stbtt_pack_range));
// First font pass: pack all glyphs (no rendering at this point, we are working with rectangles in an infinitely tall texture at this point) // First font pass: pack all glyphs (no rendering at this point, we are working with rectangles in an infinitely tall texture at this point)
for (int input_i = 0; input_i < ConfigData.Size; input_i++) for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++)
{ {
ImFontConfig& cfg = ConfigData[input_i]; ImFontConfig& cfg = atlas->ConfigData[input_i];
ImFontTempBuildData& tmp = tmp_array[input_i]; ImFontTempBuildData& tmp = tmp_array[input_i];
// Setup ranges // Setup ranges
int glyph_count = 0; int font_glyphs_count = 0;
int glyph_ranges_count = 0; int font_ranges_count = 0;
for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2) for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2, font_ranges_count++)
{ font_glyphs_count += (in_range[1] - in_range[0]) + 1;
glyph_count += (in_range[1] - in_range[0]) + 1;
glyph_ranges_count++;
}
tmp.Ranges = buf_ranges + buf_ranges_n; tmp.Ranges = buf_ranges + buf_ranges_n;
tmp.RangesCount = glyph_ranges_count; tmp.RangesCount = font_ranges_count;
buf_ranges_n += glyph_ranges_count; buf_ranges_n += font_ranges_count;
for (int i = 0; i < glyph_ranges_count; i++) for (int i = 0; i < font_ranges_count; i++)
{ {
const ImWchar* in_range = &cfg.GlyphRanges[i * 2]; const ImWchar* in_range = &cfg.GlyphRanges[i * 2];
stbtt_pack_range& range = tmp.Ranges[i]; stbtt_pack_range& range = tmp.Ranges[i];
@ -1370,31 +1433,32 @@ bool ImFontAtlas::Build()
// Pack // Pack
tmp.Rects = buf_rects + buf_rects_n; tmp.Rects = buf_rects + buf_rects_n;
buf_rects_n += glyph_count; buf_rects_n += font_glyphs_count;
stbtt_PackSetOversampling(&spc, cfg.OversampleH, cfg.OversampleV); stbtt_PackSetOversampling(&spc, cfg.OversampleH, cfg.OversampleV);
int n = stbtt_PackFontRangesGatherRects(&spc, &tmp.FontInfo, tmp.Ranges, tmp.RangesCount, tmp.Rects); int n = stbtt_PackFontRangesGatherRects(&spc, &tmp.FontInfo, tmp.Ranges, tmp.RangesCount, tmp.Rects);
IM_ASSERT(n == font_glyphs_count);
stbrp_pack_rects((stbrp_context*)spc.pack_info, tmp.Rects, n); stbrp_pack_rects((stbrp_context*)spc.pack_info, tmp.Rects, n);
// Extend texture height // Extend texture height
for (int i = 0; i < n; i++) for (int i = 0; i < n; i++)
if (tmp.Rects[i].was_packed) if (tmp.Rects[i].was_packed)
TexHeight = ImMax(TexHeight, tmp.Rects[i].y + tmp.Rects[i].h); atlas->TexHeight = ImMax(atlas->TexHeight, tmp.Rects[i].y + tmp.Rects[i].h);
} }
IM_ASSERT(buf_rects_n == total_glyph_count); IM_ASSERT(buf_rects_n == total_glyphs_count);
IM_ASSERT(buf_packedchars_n == total_glyph_count); IM_ASSERT(buf_packedchars_n == total_glyphs_count);
IM_ASSERT(buf_ranges_n == total_glyph_range_count); IM_ASSERT(buf_ranges_n == total_ranges_count);
// Create texture // Create texture
TexHeight = ImUpperPowerOfTwo(TexHeight); atlas->TexHeight = ImUpperPowerOfTwo(atlas->TexHeight);
TexPixelsAlpha8 = (unsigned char*)ImGui::MemAlloc(TexWidth * TexHeight); atlas->TexPixelsAlpha8 = (unsigned char*)ImGui::MemAlloc(atlas->TexWidth * atlas->TexHeight);
memset(TexPixelsAlpha8, 0, TexWidth * TexHeight); memset(atlas->TexPixelsAlpha8, 0, atlas->TexWidth * atlas->TexHeight);
spc.pixels = TexPixelsAlpha8; spc.pixels = atlas->TexPixelsAlpha8;
spc.height = TexHeight; spc.height = atlas->TexHeight;
// Second pass: render characters // Second pass: render font characters
for (int input_i = 0; input_i < ConfigData.Size; input_i++) for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++)
{ {
ImFontConfig& cfg = ConfigData[input_i]; ImFontConfig& cfg = atlas->ConfigData[input_i];
ImFontTempBuildData& tmp = tmp_array[input_i]; ImFontTempBuildData& tmp = tmp_array[input_i];
stbtt_PackSetOversampling(&spc, cfg.OversampleH, cfg.OversampleV); stbtt_PackSetOversampling(&spc, cfg.OversampleH, cfg.OversampleV);
stbtt_PackFontRangesRenderIntoRects(&spc, &tmp.FontInfo, tmp.Ranges, tmp.RangesCount, tmp.Rects); stbtt_PackFontRangesRenderIntoRects(&spc, &tmp.FontInfo, tmp.Ranges, tmp.RangesCount, tmp.Rects);
@ -1407,9 +1471,9 @@ bool ImFontAtlas::Build()
buf_rects = NULL; buf_rects = NULL;
// Third pass: setup ImFont and glyphs for runtime // Third pass: setup ImFont and glyphs for runtime
for (int input_i = 0; input_i < ConfigData.Size; input_i++) for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++)
{ {
ImFontConfig& cfg = ConfigData[input_i]; ImFontConfig& cfg = atlas->ConfigData[input_i];
ImFontTempBuildData& tmp = tmp_array[input_i]; ImFontTempBuildData& tmp = tmp_array[input_i];
ImFont* dst_font = cfg.DstFont; // We can have multiple input fonts writing into a same destination font (when using MergeMode=true) ImFont* dst_font = cfg.DstFont; // We can have multiple input fonts writing into a same destination font (when using MergeMode=true)
@ -1419,20 +1483,9 @@ bool ImFontAtlas::Build()
float ascent = unscaled_ascent * font_scale; float ascent = unscaled_ascent * font_scale;
float descent = unscaled_descent * font_scale; float descent = unscaled_descent * font_scale;
if (!cfg.MergeMode) ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent);
{
dst_font->ContainerAtlas = this;
dst_font->ConfigData = &cfg;
dst_font->ConfigDataCount = 0;
dst_font->FontSize = cfg.SizePixels;
dst_font->Ascent = ascent;
dst_font->Descent = descent;
dst_font->Glyphs.resize(0);
dst_font->MetricsTotalSurface = 0;
}
dst_font->ConfigDataCount++;
float off_x = cfg.GlyphOffset.x; float off_x = cfg.GlyphOffset.x;
float off_y = cfg.GlyphOffset.y; float off_y = cfg.GlyphOffset.y + (float)(int)(dst_font->Ascent + 0.5f);
dst_font->FallbackGlyph = NULL; // Always clear fallback so FindGlyph can return NULL. It will be set again in BuildLookupTable() dst_font->FallbackGlyph = NULL; // Always clear fallback so FindGlyph can return NULL. It will be set again in BuildLookupTable()
for (int i = 0; i < tmp.RangesCount; i++) for (int i = 0; i < tmp.RangesCount; i++)
@ -1450,19 +1503,24 @@ bool ImFontAtlas::Build()
stbtt_aligned_quad q; stbtt_aligned_quad q;
float dummy_x = 0.0f, dummy_y = 0.0f; float dummy_x = 0.0f, dummy_y = 0.0f;
stbtt_GetPackedQuad(range.chardata_for_range, TexWidth, TexHeight, char_idx, &dummy_x, &dummy_y, &q, 0); stbtt_GetPackedQuad(range.chardata_for_range, atlas->TexWidth, atlas->TexHeight, char_idx, &dummy_x, &dummy_y, &q, 0);
dst_font->Glyphs.resize(dst_font->Glyphs.Size + 1); dst_font->Glyphs.resize(dst_font->Glyphs.Size + 1);
ImFont::Glyph& glyph = dst_font->Glyphs.back(); ImFont::Glyph& glyph = dst_font->Glyphs.back();
glyph.Codepoint = (ImWchar)codepoint; glyph.Codepoint = (ImWchar)codepoint;
glyph.X0 = q.x0 + off_x; glyph.Y0 = q.y0 + off_y; glyph.X1 = q.x1 + off_x; glyph.Y1 = q.y1 + off_y; glyph.X0 = q.x0 + off_x;
glyph.U0 = q.s0; glyph.V0 = q.t0; glyph.U1 = q.s1; glyph.V1 = q.t1; glyph.Y0 = q.y0 + off_y;
glyph.Y0 += (float)(int)(dst_font->Ascent + 0.5f); glyph.X1 = q.x1 + off_x;
glyph.Y1 += (float)(int)(dst_font->Ascent + 0.5f); glyph.Y1 = q.y1 + off_y;
glyph.U0 = q.s0;
glyph.V0 = q.t0;
glyph.U1 = q.s1;
glyph.V1 = q.t1;
glyph.XAdvance = (pc.xadvance + cfg.GlyphExtraSpacing.x); // Bake spacing into XAdvance glyph.XAdvance = (pc.xadvance + cfg.GlyphExtraSpacing.x); // Bake spacing into XAdvance
if (cfg.PixelSnapH) if (cfg.PixelSnapH)
glyph.XAdvance = (float)(int)(glyph.XAdvance + 0.5f); glyph.XAdvance = (float)(int)(glyph.XAdvance + 0.5f);
dst_font->MetricsTotalSurface += (int)((glyph.U1 - glyph.U0) * TexWidth + 1.99f) * (int)((glyph.V1 - glyph.V0) * TexHeight + 1.99f); // +1 to account for average padding, +0.99 to round dst_font->MetricsTotalSurface += (int)((glyph.U1 - glyph.U0) * atlas->TexWidth + 1.99f) * (int)((glyph.V1 - glyph.V0) * atlas->TexHeight + 1.99f); // +1 to account for average padding, +0.99 to round
} }
} }
cfg.DstFont->BuildLookupTable(); cfg.DstFont->BuildLookupTable();
@ -1474,100 +1532,105 @@ bool ImFontAtlas::Build()
ImGui::MemFree(tmp_array); ImGui::MemFree(tmp_array);
// Render into our custom data block // Render into our custom data block
RenderCustomTexData(1, &extra_rects); ImFontAtlasBuildRenderDefaultTexData(atlas);
return true; return true;
} }
void ImFontAtlas::RenderCustomTexData(int pass, void* p_rects) void ImFontAtlasBuildRegisterDefaultCustomRects(ImFontAtlas* atlas)
{ {
// A work of art lies ahead! (. = white layer, X = black layer, others are blank) // FIXME-WIP: We should register in the constructor (but cannot because our static instances may not have allocator ready by the time they initialize). This needs to be fixed because we can expose CustomRects.
// The white texels on the top left are the ones we'll use everywhere in ImGui to render filled shapes. if (atlas->CustomRects.empty())
const int TEX_DATA_W = 90; atlas->CustomRectRegister(FONT_ATLAS_DEFAULT_TEX_DATA_ID, FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF*2+1, FONT_ATLAS_DEFAULT_TEX_DATA_H);
const int TEX_DATA_H = 27; }
const char texture_data[TEX_DATA_W*TEX_DATA_H+1] =
{
"..- -XXXXXXX- X - X -XXXXXXX - XXXXXXX"
"..- -X.....X- X.X - X.X -X.....X - X.....X"
"--- -XXX.XXX- X...X - X...X -X....X - X....X"
"X - X.X - X.....X - X.....X -X...X - X...X"
"XX - X.X -X.......X- X.......X -X..X.X - X.X..X"
"X.X - X.X -XXXX.XXXX- XXXX.XXXX -X.X X.X - X.X X.X"
"X..X - X.X - X.X - X.X -XX X.X - X.X XX"
"X...X - X.X - X.X - XX X.X XX - X.X - X.X "
"X....X - X.X - X.X - X.X X.X X.X - X.X - X.X "
"X.....X - X.X - X.X - X..X X.X X..X - X.X - X.X "
"X......X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X XX-XX X.X "
"X.......X - X.X - X.X -X.....................X- X.X X.X-X.X X.X "
"X........X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X..X-X..X.X "
"X.........X -XXX.XXX- X.X - X..X X.X X..X - X...X-X...X "
"X..........X-X.....X- X.X - X.X X.X X.X - X....X-X....X "
"X......XXXXX-XXXXXXX- X.X - XX X.X XX - X.....X-X.....X "
"X...X..X --------- X.X - X.X - XXXXXXX-XXXXXXX "
"X..X X..X - -XXXX.XXXX- XXXX.XXXX ------------------------------------"
"X.X X..X - -X.......X- X.......X - XX XX - "
"XX X..X - - X.....X - X.....X - X.X X.X - "
" X..X - X...X - X...X - X..X X..X - "
" XX - X.X - X.X - X...XXXXXXXXXXXXX...X - "
"------------ - X - X -X.....................X- "
" ----------------------------------- X...XXXXXXXXXXXXX...X - "
" - X..X X..X - "
" - X.X X.X - "
" - XX XX - "
};
ImVector<stbrp_rect>& rects = *(ImVector<stbrp_rect>*)p_rects; void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent)
if (pass == 0) {
if (!font_config->MergeMode)
{ {
// Request rectangles font->ContainerAtlas = atlas;
stbrp_rect r; font->ConfigData = font_config;
memset(&r, 0, sizeof(r)); font->ConfigDataCount = 0;
r.w = (TEX_DATA_W*2)+1; font->FontSize = font_config->SizePixels;
r.h = TEX_DATA_H+1; font->Ascent = ascent;
rects.push_back(r); font->Descent = descent;
font->Glyphs.resize(0);
font->MetricsTotalSurface = 0;
} }
else if (pass == 1) font->ConfigDataCount++;
{ }
// Render/copy pixels
const stbrp_rect& r = rects[0]; void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* pack_context_opaque)
for (int y = 0, n = 0; y < TEX_DATA_H; y++) {
for (int x = 0; x < TEX_DATA_W; x++, n++) stbrp_context* pack_context = (stbrp_context*)pack_context_opaque;
{
const int offset0 = (int)(r.x + x) + (int)(r.y + y) * TexWidth;
const int offset1 = offset0 + 1 + TEX_DATA_W;
TexPixelsAlpha8[offset0] = texture_data[n] == '.' ? 0xFF : 0x00;
TexPixelsAlpha8[offset1] = texture_data[n] == 'X' ? 0xFF : 0x00;
}
const ImVec2 tex_uv_scale(1.0f / TexWidth, 1.0f / TexHeight);
TexUvWhitePixel = ImVec2((r.x + 0.5f) * tex_uv_scale.x, (r.y + 0.5f) * tex_uv_scale.y);
// Setup mouse cursors ImVector<ImFontAtlas::CustomRect>& user_rects = atlas->CustomRects;
const ImVec2 cursor_datas[ImGuiMouseCursor_Count_][3] = ImVector<stbrp_rect> pack_rects;
pack_rects.resize(user_rects.Size);
memset(pack_rects.Data, 0, sizeof(stbrp_rect) * user_rects.Size);
for (int i = 0; i < user_rects.Size; i++)
{
pack_rects[i].w = user_rects[i].Width;
pack_rects[i].h = user_rects[i].Height;
}
stbrp_pack_rects(pack_context, &pack_rects[0], pack_rects.Size);
for (int i = 0; i < pack_rects.Size; i++)
if (pack_rects[i].was_packed)
{ {
// Pos ........ Size ......... Offset ...... user_rects[i].X = pack_rects[i].x;
{ ImVec2(0,3), ImVec2(12,19), ImVec2( 0, 0) }, // ImGuiMouseCursor_Arrow user_rects[i].Y = pack_rects[i].y;
{ ImVec2(13,0), ImVec2(7,16), ImVec2( 4, 8) }, // ImGuiMouseCursor_TextInput IM_ASSERT(pack_rects[i].w == user_rects[i].Width && pack_rects[i].h == user_rects[i].Height);
{ ImVec2(31,0), ImVec2(23,23), ImVec2(11,11) }, // ImGuiMouseCursor_Move atlas->TexHeight = ImMax(atlas->TexHeight, pack_rects[i].y + pack_rects[i].h);
{ ImVec2(21,0), ImVec2( 9,23), ImVec2( 5,11) }, // ImGuiMouseCursor_ResizeNS }
{ ImVec2(55,18),ImVec2(23, 9), ImVec2(11, 5) }, // ImGuiMouseCursor_ResizeEW }
{ ImVec2(73,0), ImVec2(17,17), ImVec2( 9, 9) }, // ImGuiMouseCursor_ResizeNESW
{ ImVec2(55,0), ImVec2(17,17), ImVec2( 9, 9) }, // ImGuiMouseCursor_ResizeNWSE void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas)
}; {
ImFontAtlas::CustomRect& r = atlas->CustomRects[0];
for (int type = 0; type < ImGuiMouseCursor_Count_; type++) IM_ASSERT(r.ID == FONT_ATLAS_DEFAULT_TEX_DATA_ID);
IM_ASSERT(r.Width == FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF*2+1);
IM_ASSERT(r.Height == FONT_ATLAS_DEFAULT_TEX_DATA_H);
IM_ASSERT(r.IsPacked());
IM_ASSERT(atlas->TexPixelsAlpha8 != NULL);
// Render/copy pixels
for (int y = 0, n = 0; y < FONT_ATLAS_DEFAULT_TEX_DATA_H; y++)
for (int x = 0; x < FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF; x++, n++)
{ {
ImGuiMouseCursorData& cursor_data = GImGui->MouseCursorData[type]; const int offset0 = (int)(r.X + x) + (int)(r.Y + y) * atlas->TexWidth;
ImVec2 pos = cursor_datas[type][0] + ImVec2((float)r.x, (float)r.y); const int offset1 = offset0 + FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF + 1;
const ImVec2 size = cursor_datas[type][1]; atlas->TexPixelsAlpha8[offset0] = FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[n] == '.' ? 0xFF : 0x00;
cursor_data.Type = type; atlas->TexPixelsAlpha8[offset1] = FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[n] == 'X' ? 0xFF : 0x00;
cursor_data.Size = size;
cursor_data.HotOffset = cursor_datas[type][2];
cursor_data.TexUvMin[0] = (pos) * tex_uv_scale;
cursor_data.TexUvMax[0] = (pos + size) * tex_uv_scale;
pos.x += TEX_DATA_W+1;
cursor_data.TexUvMin[1] = (pos) * tex_uv_scale;
cursor_data.TexUvMax[1] = (pos + size) * tex_uv_scale;
} }
const ImVec2 tex_uv_scale(1.0f / atlas->TexWidth, 1.0f / atlas->TexHeight);
atlas->TexUvWhitePixel = ImVec2((r.X + 0.5f) * tex_uv_scale.x, (r.Y + 0.5f) * tex_uv_scale.y);
// Setup mouse cursors
const ImVec2 cursor_datas[ImGuiMouseCursor_Count_][3] =
{
// Pos ........ Size ......... Offset ......
{ ImVec2(0,3), ImVec2(12,19), ImVec2( 0, 0) }, // ImGuiMouseCursor_Arrow
{ ImVec2(13,0), ImVec2(7,16), ImVec2( 4, 8) }, // ImGuiMouseCursor_TextInput
{ ImVec2(31,0), ImVec2(23,23), ImVec2(11,11) }, // ImGuiMouseCursor_Move
{ ImVec2(21,0), ImVec2( 9,23), ImVec2( 5,11) }, // ImGuiMouseCursor_ResizeNS
{ ImVec2(55,18),ImVec2(23, 9), ImVec2(11, 5) }, // ImGuiMouseCursor_ResizeEW
{ ImVec2(73,0), ImVec2(17,17), ImVec2( 9, 9) }, // ImGuiMouseCursor_ResizeNESW
{ ImVec2(55,0), ImVec2(17,17), ImVec2( 9, 9) }, // ImGuiMouseCursor_ResizeNWSE
};
for (int type = 0; type < ImGuiMouseCursor_Count_; type++)
{
ImGuiMouseCursorData& cursor_data = GImGui->MouseCursorData[type];
ImVec2 pos = cursor_datas[type][0] + ImVec2((float)r.X, (float)r.Y);
const ImVec2 size = cursor_datas[type][1];
cursor_data.Type = type;
cursor_data.Size = size;
cursor_data.HotOffset = cursor_datas[type][2];
cursor_data.TexUvMin[0] = (pos) * tex_uv_scale;
cursor_data.TexUvMax[0] = (pos + size) * tex_uv_scale;
pos.x += FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF + 1;
cursor_data.TexUvMin[1] = (pos) * tex_uv_scale;
cursor_data.TexUvMax[1] = (pos + size) * tex_uv_scale;
} }
} }

@ -4,6 +4,8 @@
// You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility! // You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility!
// Implement maths operators for ImVec2 (disabled by default to not collide with using IM_VEC2_CLASS_EXTRA along with your own math types+operators) // Implement maths operators for ImVec2 (disabled by default to not collide with using IM_VEC2_CLASS_EXTRA along with your own math types+operators)
// #define IMGUI_DEFINE_MATH_OPERATORS // #define IMGUI_DEFINE_MATH_OPERATORS
// Define IM_PLACEMENT_NEW() macro helper.
// #define IMGUI_DEFINE_PLACEMENT_NEW
#pragma once #pragma once
@ -822,10 +824,12 @@ namespace ImGui
IMGUI_API void FocusableItemUnregister(ImGuiWindow* window); IMGUI_API void FocusableItemUnregister(ImGuiWindow* window);
IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_x, float default_y); IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_x, float default_y);
IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x); IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x);
IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled); IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled);
IMGUI_API void PopItemFlag(); IMGUI_API void PopItemFlag();
IMGUI_API void OpenPopupEx(const char* str_id, bool reopen_existing);
IMGUI_API void OpenPopupEx(ImGuiID id, bool reopen_existing);
IMGUI_API bool IsPopupOpen(ImGuiID id);
IMGUI_API int CalcTypematicPressedRepeatAmount(float t, float t_prev, float repeat_delay, float repeat_rate); IMGUI_API int CalcTypematicPressedRepeatAmount(float t, float t_prev, float repeat_delay, float repeat_rate);
// NB: All position are in absolute pixels coordinates (never using window coordinates internally) // NB: All position are in absolute pixels coordinates (never using window coordinates internally)
@ -873,6 +877,13 @@ namespace ImGui
} // namespace ImGui } // namespace ImGui
// ImFontAtlas internals
IMGUI_API bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildRegisterDefaultCustomRects(ImFontAtlas* atlas);
IMGUI_API void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent);
IMGUI_API void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* spc);
IMGUI_API void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas);
#ifdef __clang__ #ifdef __clang__
#pragma clang diagnostic pop #pragma clang diagnostic pop
#endif #endif

Loading…
Cancel
Save