diff --git a/imgui.cpp b/imgui.cpp index e9a626ff8..d15f45f9d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -681,8 +681,7 @@ static void AddDrawListToRenderList(ImVector& out_rende static void AddWindowToRenderList(ImVector& out_render_list, ImGuiWindow* window); static void AddWindowToSortedBuffer(ImVector& out_sorted_windows, ImGuiWindow* window); -static ImGuiSettingsWindow* FindWindowSettings(const char* name); -static ImGuiSettingsWindow* AddWindowSettings(const char* name); +static ImGuiWindowSettings* AddWindowSettings(const char* name); static void LoadIniSettingsFromDisk(const char* ini_filename); static void LoadIniSettingsFromMemory(const char* buf); @@ -2089,7 +2088,7 @@ void ImGui::ItemSize(const ImVec2& size, float text_offset_y) window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x + size.x, window->DC.CursorPos.y); window->DC.CursorPos = ImVec2((float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX), (float)(int)(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y)); window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x); - window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y); + window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y); //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG] window->DC.PrevLineHeight = line_height; @@ -3276,9 +3275,9 @@ void ImGui::NewFrame() Begin("Debug##Default"); } -static void* SettingsHandlerWindow_ReadOpenEntry(ImGuiContext&, const char* name) +static void* SettingsHandlerWindow_ReadOpen(ImGuiContext&, const char* name) { - ImGuiSettingsWindow* settings = FindWindowSettings(name); + ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHash(name, 0)); if (!settings) settings = AddWindowSettings(name); return (void*)settings; @@ -3286,7 +3285,7 @@ static void* SettingsHandlerWindow_ReadOpenEntry(ImGuiContext&, const char* name static void SettingsHandlerWindow_ReadLine(ImGuiContext&, void* entry, const char* line) { - ImGuiSettingsWindow* settings = (ImGuiSettingsWindow*)entry; + ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry; float x, y; int i; if (sscanf(line, "Pos=%f,%f", &x, &y) == 2) settings->Pos = ImVec2(x, y); @@ -3302,9 +3301,9 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext& g, ImGuiTextBuffer* buf ImGuiWindow* window = g.Windows[i]; if (window->Flags & ImGuiWindowFlags_NoSavedSettings) continue; - ImGuiSettingsWindow* settings = FindWindowSettings(window->Name); - if (!settings) // This will only return NULL in the rare instance where the window was first created with ImGuiWindowFlags_NoSavedSettings then had the flag disabled later on. We don't bind settings in this case (bug #1000). - continue; + ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID); + if (!settings) + settings = AddWindowSettings(window->Name); settings->Pos = window->Pos; settings->Size = window->SizeFull; settings->Collapsed = window->Collapsed; @@ -3315,7 +3314,7 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext& g, ImGuiTextBuffer* buf buf->reserve(buf->size() + g.SettingsWindows.Size * 96); // ballpark reserve for (int i = 0; i != g.SettingsWindows.Size; i++) { - const ImGuiSettingsWindow* settings = &g.SettingsWindows[i]; + const ImGuiWindowSettings* settings = &g.SettingsWindows[i]; if (settings->Pos.x == FLT_MAX) continue; const char* name = settings->Name; @@ -3339,7 +3338,7 @@ void ImGui::Initialize() ImGuiSettingsHandler ini_handler; ini_handler.TypeName = "Window"; ini_handler.TypeHash = ImHash("Window", 0, 0); - ini_handler.ReadOpenEntryFn = SettingsHandlerWindow_ReadOpenEntry; + ini_handler.ReadOpenFn = SettingsHandlerWindow_ReadOpen; ini_handler.ReadLineFn = SettingsHandlerWindow_ReadLine; ini_handler.WriteAllFn = SettingsHandlerWindow_WriteAll; g.SettingsHandlers.push_front(ini_handler); @@ -3414,29 +3413,22 @@ void ImGui::Shutdown() g.Initialized = false; } -static ImGuiSettingsWindow* FindWindowSettings(const char* name) +ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id) { ImGuiContext& g = *GImGui; - ImGuiID id = ImHash(name, 0); for (int i = 0; i != g.SettingsWindows.Size; i++) - { - ImGuiSettingsWindow* ini = &g.SettingsWindows[i]; - if (ini->Id == id) - return ini; - } + if (g.SettingsWindows[i].Id == id) + return &g.SettingsWindows[i]; return NULL; } -static ImGuiSettingsWindow* AddWindowSettings(const char* name) +static ImGuiWindowSettings* AddWindowSettings(const char* name) { ImGuiContext& g = *GImGui; - g.SettingsWindows.resize(g.SettingsWindows.Size + 1); - ImGuiSettingsWindow* settings = &g.SettingsWindows.back(); + g.SettingsWindows.push_back(ImGuiWindowSettings()); + ImGuiWindowSettings* settings = &g.SettingsWindows.back(); settings->Name = ImStrdup(name); settings->Id = ImHash(name, 0); - settings->Collapsed = false; - settings->Pos = ImVec2(FLT_MAX,FLT_MAX); - settings->Size = ImVec2(0,0); return settings; } @@ -3451,6 +3443,15 @@ static void LoadIniSettingsFromDisk(const char* ini_filename) ImGui::MemFree(file_data); } +ImGuiSettingsHandler* ImGui::FindSettingsHandler(ImGuiID type_hash) +{ + ImGuiContext& g = *GImGui; + for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) + if (g.SettingsHandlers[handler_n].TypeHash == type_hash) + return &g.SettingsHandlers[handler_n]; + return NULL; +} + // Zero-tolerance, no error reporting, cheap .ini parsing static void LoadIniSettingsFromMemory(const char* buf_readonly) { @@ -3492,11 +3493,8 @@ static void LoadIniSettingsFromMemory(const char* buf_readonly) name_start++; // Skip second '[' } const ImGuiID type_hash = ImHash(type_start, 0, 0); - entry_handler = NULL; - for (int handler_n = 0; handler_n < g.SettingsHandlers.Size && entry_handler == NULL; handler_n++) - if (g.SettingsHandlers[handler_n].TypeHash == type_hash) - entry_handler = &g.SettingsHandlers[handler_n]; - entry_data = entry_handler ? entry_handler->ReadOpenEntryFn(g, name_start) : NULL; + entry_handler = ImGui::FindSettingsHandler(type_hash); + entry_data = entry_handler ? entry_handler->ReadOpenFn(g, name_start) : NULL; } else if (entry_handler != NULL && entry_data != NULL) { @@ -4984,21 +4982,15 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFl window->PosFloat = ImVec2(60, 60); window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y); - ImGuiSettingsWindow* settings = FindWindowSettings(name); - if (!settings) - settings = AddWindowSettings(name); - else - SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false); - - if (settings->Pos.x != FLT_MAX) + if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID)) { + SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false); window->PosFloat = settings->Pos; window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y); window->Collapsed = settings->Collapsed; + if (ImLengthSqr(settings->Size) > 0.00001f) + size = settings->Size; } - - if (ImLengthSqr(settings->Size) > 0.00001f) - size = settings->Size; window->Size = window->SizeFull = size; } @@ -5060,22 +5052,31 @@ static ImVec2 CalcSizeAutoFit(ImGuiWindow* window) if ((flags & ImGuiWindowFlags_Tooltip) != 0) { // Tooltip always resize. We keep the spacing symmetric on both axises for aesthetic purpose. - size_auto_fit = window->SizeContents + window->WindowPadding - ImVec2(0.0f, style.ItemSpacing.y); + size_auto_fit = window->SizeContents; } else { // Handling case of auto fit window not fitting on the screen (on either axis): we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than DisplaySize-WindowPadding. - size_auto_fit = ImClamp(window->SizeContents + window->WindowPadding, style.WindowMinSize, ImMax(style.WindowMinSize, g.IO.DisplaySize - g.Style.DisplaySafeAreaPadding)); + size_auto_fit = ImClamp(window->SizeContents, style.WindowMinSize, ImMax(style.WindowMinSize, g.IO.DisplaySize - g.Style.DisplaySafeAreaPadding)); ImVec2 size_auto_fit_after_constraint = CalcSizeFullWithConstraint(window, size_auto_fit); if (size_auto_fit_after_constraint.x < window->SizeContents.x && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar)) size_auto_fit.y += style.ScrollbarSize; if (size_auto_fit_after_constraint.y < window->SizeContents.y && !(flags & ImGuiWindowFlags_NoScrollbar)) size_auto_fit.x += style.ScrollbarSize; - size_auto_fit.y = ImMax(size_auto_fit.y - style.ItemSpacing.y, 0.0f); } return size_auto_fit; } +static float GetScrollMaxX(ImGuiWindow* window) +{ + return ImMax(0.0f, window->SizeContents.x - (window->SizeFull.x - window->ScrollbarSizes.x)); +} + +static float GetScrollMaxY(ImGuiWindow* window) +{ + return ImMax(0.0f, window->SizeContents.y - (window->SizeFull.y - window->ScrollbarSizes.y)); +} + static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window) { ImVec2 scroll = window->Scroll; @@ -5088,8 +5089,8 @@ static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window) scroll = ImMax(scroll, ImVec2(0.0f, 0.0f)); if (!window->Collapsed && !window->SkipItems) { - scroll.x = ImMin(scroll.x, ImMax(0.0f, window->SizeContents.x - (window->SizeFull.x - window->ScrollbarSizes.x))); // == GetScrollMaxX for that window - scroll.y = ImMin(scroll.y, ImMax(0.0f, window->SizeContents.y - (window->SizeFull.y - window->ScrollbarSizes.y))); // == GetScrollMaxY for that window + scroll.x = ImMin(scroll.x, GetScrollMaxX(window)); + scroll.y = ImMin(scroll.y, GetScrollMaxY(window)); } return scroll; } @@ -5274,6 +5275,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Update contents size from last frame for auto-fitting (unless explicitly specified) window->SizeContents.x = (float)(int)((window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : ((window_is_new ? 0.0f : window->DC.CursorMaxPos.x - window->Pos.x) + window->Scroll.x)); window->SizeContents.y = (float)(int)((window->SizeContentsExplicit.y != 0.0f) ? window->SizeContentsExplicit.y : ((window_is_new ? 0.0f : window->DC.CursorMaxPos.y - window->Pos.y) + window->Scroll.y)); + window->SizeContents += window->WindowPadding; // Hide popup/tooltip window when first appearing while we measure size (because we recycle them) if (window->HiddenFrames > 0) @@ -5340,10 +5342,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Update scrollbar status (based on the Size that was effective during last frame or the auto-resized Size). We need to do this before manual resize (below) is effective. if (!window->Collapsed) { - window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((window->SizeContents.y > window->SizeFull.y + style.ItemSpacing.y) && !(flags & ImGuiWindowFlags_NoScrollbar)); + window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((window->SizeContents.y > window->SizeFull.y) && !(flags & ImGuiWindowFlags_NoScrollbar)); window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((window->SizeContents.x > window->SizeFull.x - (window->ScrollbarY ? style.ScrollbarSize : 0.0f) - window->WindowPadding.x) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar)); if (window->ScrollbarX && !window->ScrollbarY) - window->ScrollbarY = (window->SizeContents.y > window->SizeFull.y + style.ItemSpacing.y - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar); + window->ScrollbarY = (window->SizeContents.y > window->SizeFull.y + style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar); window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f); } @@ -6660,14 +6662,12 @@ float ImGui::GetScrollY() float ImGui::GetScrollMaxX() { - ImGuiWindow* window = GetCurrentWindowRead(); - return ImMax(0.0f, window->SizeContents.x - (window->SizeFull.x - window->ScrollbarSizes.x)); + return GetScrollMaxX(GImGui->CurrentWindow); } float ImGui::GetScrollMaxY() { - ImGuiWindow* window = GetCurrentWindowRead(); - return ImMax(0.0f, window->SizeContents.y - (window->SizeFull.y - window->ScrollbarSizes.y)); + return GetScrollMaxY(GImGui->CurrentWindow); } void ImGui::SetScrollX(float scroll_x) @@ -6690,9 +6690,13 @@ void ImGui::SetScrollFromPosY(float pos_y, float center_y_ratio) ImGuiWindow* window = GetCurrentWindow(); IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f); window->ScrollTarget.y = (float)(int)(pos_y + window->Scroll.y); - if (center_y_ratio <= 0.0f && window->ScrollTarget.y <= window->WindowPadding.y) // Minor hack to make "scroll to top" take account of WindowPadding, else it would scroll to (WindowPadding.y - ItemSpacing.y) - window->ScrollTarget.y = 0.0f; window->ScrollTargetCenterRatio.y = center_y_ratio; + + // Minor hack to to make scrolling to top/bottom of window take account of WindowPadding, it looks more right to the user this way + if (center_y_ratio <= 0.0f && window->ScrollTarget.y <= window->WindowPadding.y) + window->ScrollTarget.y = 0.0f; + else if (center_y_ratio >= 1.0f && window->ScrollTarget.y >= window->SizeContents.y - window->WindowPadding.y + GImGui->Style.ItemSpacing.y) + window->ScrollTarget.y = window->SizeContents.y; } // center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item. @@ -11594,7 +11598,6 @@ void ImGui::EndGroup() ImGuiGroupData& group_data = window->DC.GroupStack.back(); ImRect group_bb(group_data.BackupCursorPos, window->DC.CursorMaxPos); - group_bb.Max.y -= g.Style.ItemSpacing.y; // Cancel out last vertical spacing because we are adding one ourselves. group_bb.Max = ImMax(group_bb.Min, group_bb.Max); window->DC.CursorPos = group_data.BackupCursorPos; @@ -12233,7 +12236,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), SizeContents (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->SizeContents.x, window->SizeContents.y); if (ImGui::IsItemHovered()) GImGui->OverlayDrawList.AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255,255,0,255)); - ImGui::BulletText("Scroll: (%.2f,%.2f)", window->Scroll.x, window->Scroll.y); + ImGui::BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f)", window->Scroll.x, GetScrollMaxX(window), window->Scroll.y, GetScrollMaxY(window)); ImGui::BulletText("Active: %d, WriteAccessed: %d", window->Active, window->WriteAccessed); ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask); ImGui::BulletText("NavRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRectRel[0].Min.x, window->NavRectRel[0].Min.y, window->NavRectRel[0].Max.x, window->NavRectRel[0].Max.y); diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 1aa318dd4..53f27c601 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2518,7 +2518,7 @@ struct ExampleAppConsole ImGui::PopStyleVar(); ImGui::Separator(); - ImGui::BeginChild("ScrollingRegion", ImVec2(0,-ImGui::GetItemsLineHeightWithSpacing()), false, ImGuiWindowFlags_HorizontalScrollbar); + ImGui::BeginChild("ScrollingRegion", ImVec2(0, -ImGui::GetStyle().ItemSpacing.y - ImGui::GetItemsLineHeightWithSpacing()), false, ImGuiWindowFlags_HorizontalScrollbar); // Leave room for 1 separator + 1 InputText if (ImGui::BeginPopupContextWindow()) { if (ImGui::Selectable("Clear")) ClearLog(); @@ -2848,11 +2848,9 @@ static void ShowExampleAppLayout(bool* p_open) ImGui::Separator(); ImGui::TextWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. "); ImGui::EndChild(); - ImGui::BeginChild("buttons"); - if (ImGui::Button("Revert")) {} - ImGui::SameLine(); - if (ImGui::Button("Save")) {} - ImGui::EndChild(); + if (ImGui::Button("Revert")) {} + ImGui::SameLine(); + if (ImGui::Button("Save")) {} ImGui::EndGroup(); } ImGui::End(); diff --git a/imgui_internal.h b/imgui_internal.h index c4e075374..12b26c287 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -39,10 +39,10 @@ struct ImGuiGroupData; struct ImGuiSimpleColumns; struct ImGuiDrawContext; struct ImGuiTextEditState; -struct ImGuiSettingsWindow; struct ImGuiMouseCursorData; struct ImGuiPopupRef; struct ImGuiWindow; +struct ImGuiWindowSettings; typedef int ImGuiLayoutType; // enum: horizontal or vertical // enum ImGuiLayoutType_ typedef int ImGuiButtonFlags; // flags: for ButtonEx(), ButtonBehavior() // enum ImGuiButtonFlags_ @@ -390,7 +390,7 @@ struct IMGUI_API ImGuiTextEditState }; // Data saved in imgui.ini file -struct ImGuiSettingsWindow +struct ImGuiWindowSettings { char* Name; ImGuiID Id; @@ -398,14 +398,14 @@ struct ImGuiSettingsWindow ImVec2 Size; bool Collapsed; - ImGuiSettingsWindow() { Name = NULL; Id = 0; Pos = Size = ImVec2(0,0); Collapsed = false; } + ImGuiWindowSettings() { Name = NULL; Id = 0; Pos = Size = ImVec2(0,0); Collapsed = false; } }; struct ImGuiSettingsHandler { const char* TypeName; // Short description stored in .ini file. Disallowed characters: '[' ']' ImGuiID TypeHash; // == ImHash(TypeName, 0, 0) - void* (*ReadOpenEntryFn)(ImGuiContext& ctx, const char* name); + void* (*ReadOpenFn)(ImGuiContext& ctx, const char* name); void (*ReadLineFn)(ImGuiContext& ctx, void* entry, const char* line); void (*WriteAllFn)(ImGuiContext& ctx, ImGuiTextBuffer* out_buf); }; @@ -557,7 +557,7 @@ struct ImGuiContext // Settings float SettingsDirtyTimer; // Save .ini Settings on disk when time reaches zero - ImVector SettingsWindows; // .ini settings for ImGuiWindow + ImVector SettingsWindows; // .ini settings for ImGuiWindow ImVector SettingsHandlers; // List of .ini settings handlers // Logging @@ -903,7 +903,9 @@ namespace ImGui IMGUI_API void Initialize(); - IMGUI_API void MarkIniSettingsDirty(); + IMGUI_API void MarkIniSettingsDirty(); + IMGUI_API ImGuiSettingsHandler* FindSettingsHandler(ImGuiID type_id); + IMGUI_API ImGuiWindowSettings* FindWindowSettings(ImGuiID id); IMGUI_API void SetActiveID(ImGuiID id, ImGuiWindow* window); IMGUI_API ImGuiID GetActiveID();