diff --git a/imgui.cpp b/imgui.cpp index 76d80ef5..ad0faa39 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -140,6 +140,9 @@ 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. + - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with position: + GetCursorPos()/SetCursorPos() functions now include the scrolled amount. It shouldn't affect the majority of users, but take note that SetCursorPosX(100.0f) puts you at +100 from the starting x position which may include scrolling, not at +100 from the window left side. + GetContentRegionMax()/GetWindowContentRegionMin()/GetWindowContentRegionMax() functions allow include the scrolled amount. Typically those were used in cases where no scrolling would happen so it may not be a problem, but watch out! - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize - 2015/08/05 (1.44) - split imgui.cpp into extra files: imgui_demo.cpp imgui_draw.cpp imgui_internal.h that you need to add to your project. - 2015/07/18 (1.44) - fixed angles in ImDrawList::PathArcTo(), PathArcToFast() (introduced in 1.43) being off by an extra PI for no justifiable reason @@ -537,7 +540,7 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWind static inline bool IsWindowContentHoverable(ImGuiWindow* window); static void ClearSetNextWindowData(); static void CheckStacksSize(ImGuiWindow* window, bool write); -static void Scrollbar(ImGuiWindow* window); +static void Scrollbar(ImGuiWindow* window, bool horizontal); static bool CloseWindowButton(bool* p_opened); static void AddDrawListToRenderList(ImVector& out_render_list, ImDrawList* draw_list); @@ -1470,12 +1473,13 @@ ImGuiWindow::ImGuiWindow(const char* name) Flags = 0; PosFloat = Pos = ImVec2(0.0f, 0.0f); Size = SizeFull = ImVec2(0.0f, 0.0f); - SizeContents = ImVec2(0.0f, 0.0f); + SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f); WindowPadding = ImVec2(0.0f, 0.0f); Scroll = ImVec2(0.0f, 0.0f); ScrollTargetRelY = FLT_MAX; ScrollTargetCenterRatioY = 0.5f; - ScrollbarY = false; + ScrollbarX = ScrollbarY = false; + ScrollbarSizes = ImVec2(0.0f, 0.0f); Active = WasActive = false; Accessed = false; Collapsed = false; @@ -1576,7 +1580,7 @@ void ImGui::ItemSize(const ImVec2& size, float text_offset_y) const float line_height = ImMax(window->DC.CurrentLineHeight, size.y); const float text_base_offset = ImMax(window->DC.CurrentLineTextBaseOffset, 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.ColumnsStartX + window->DC.ColumnsOffsetX), (float)(int)(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y)); + window->DC.CursorPos = ImVec2((float)(int)(window->Pos.x - window->Scroll.x + window->DC.ColumnsStartX + 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); @@ -1708,9 +1712,9 @@ float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x) ImGuiWindow* window = GetCurrentWindowRead(); if (wrap_pos_x == 0.0f) - wrap_pos_x = ImGui::GetContentRegionMax().x; - if (wrap_pos_x > 0.0f) - wrap_pos_x += window->Pos.x; // wrap_pos_x is provided is window local space + wrap_pos_x = ImGui::GetContentRegionMax().x + window->Pos.x; + else if (wrap_pos_x > 0.0f) + wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space const float wrap_width = wrap_pos_x > 0.0f ? ImMax(wrap_pos_x - pos.x, 0.00001f) : 0.0f; return wrap_width; @@ -3083,7 +3087,7 @@ void ImGui::CloseCurrentPopup() static void ClearSetNextWindowData() { ImGuiState& g = *GImGui; - g.SetNextWindowPosCond = g.SetNextWindowSizeCond = g.SetNextWindowCollapsedCond = g.SetNextWindowFocus = 0; + g.SetNextWindowPosCond = g.SetNextWindowSizeCond = g.SetNextWindowContentSizeCond = g.SetNextWindowCollapsedCond = g.SetNextWindowFocus = 0; } static bool BeginPopupEx(const char* str_id, ImGuiWindowFlags extra_flags) @@ -3429,6 +3433,7 @@ bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size_on_first_ IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow)); const int current_frame = ImGui::GetFrameCount(); + const bool first_begin_of_the_frame = (window->LastFrameDrawn != current_frame); bool window_was_visible = (window->LastFrameDrawn == current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on if (flags & ImGuiWindowFlags_Popup) { @@ -3466,6 +3471,15 @@ bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size_on_first_ ImGui::SetWindowSize(g.SetNextWindowSizeVal, g.SetNextWindowSizeCond); g.SetNextWindowSizeCond = 0; } + if (g.SetNextWindowContentSizeCond) + { + window->SizeContentsExplicit = g.SetNextWindowContentSizeVal; + g.SetNextWindowContentSizeCond = 0; + } + else if (first_begin_of_the_frame) + { + window->SizeContentsExplicit = ImVec2(0.0f, 0.0f); + } if (g.SetNextWindowCollapsedCond) { if (!window_was_visible) window->SetWindowCollapsedAllowFlags |= ImGuiSetCond_Appearing; @@ -3494,7 +3508,6 @@ bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size_on_first_ bg_alpha = style.WindowFillAlphaDefault; // When reusing window again multiple times a frame, just append content (don't need to setup again) - const bool first_begin_of_the_frame = (window->LastFrameDrawn != current_frame); if (first_begin_of_the_frame) { window->Active = true; @@ -3548,9 +3561,9 @@ bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size_on_first_ // SIZE - // Save contents size from last frame for auto-fitting - window->SizeContents = window_is_new ? ImVec2(0.0f, 0.0f) : window->DC.CursorMaxPos - window->Pos; - window->SizeContents.y += window->Scroll.y; + // Save contents size from last frame for auto-fitting (unless explicitly specified) + window->SizeContents.x = (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 = (window->SizeContentsExplicit.y != 0.0f) ? window->SizeContentsExplicit.y : ((window_is_new ? 0.0f : window->DC.CursorMaxPos.y - window->Pos.y) + window->Scroll.y); // Hide popup/tooltip window when first appearing while we measure size (because we recycle them) if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0 && !window_was_visible) @@ -3564,7 +3577,7 @@ bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size_on_first_ } } - // Lock window padding so that altering the ShowBorders flag for childs doesn't have side-effects. + // Lock window padding so that altering the ShowBorders flag for children doesn't have side-effects. window->WindowPadding = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_ShowBorders | ImGuiWindowFlags_ComboBox | ImGuiWindowFlags_Popup))) ? ImVec2(0,0) : style.WindowPadding; // Calculate auto-fit size @@ -3577,6 +3590,10 @@ bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size_on_first_ else { size_auto_fit = ImClamp(window->SizeContents + window->WindowPadding, style.WindowMinSize, ImMax(style.WindowMinSize, g.IO.DisplaySize - window->WindowPadding)); + + // Handling case of auto fit window not fitting in screen on one axis, we are growing auto fit size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than DisplaySize-WindowPadding. + if (size_auto_fit.x < window->SizeContents.x && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar)) + size_auto_fit.y += style.ScrollbarSize; if (size_auto_fit.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); @@ -3647,7 +3664,7 @@ bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size_on_first_ if (parent_window->DC.MenuBarAppending) rect_to_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight()); else - rect_to_avoid = ImRect(parent_window->Pos.x + style.ItemSpacing.x, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - style.ItemSpacing.x - parent_window->ScrollbarWidth(), FLT_MAX); // We want some overlap to convey the relative depth of each popup (here hard-coded to 4) + rect_to_avoid = ImRect(parent_window->Pos.x + style.ItemSpacing.x, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - style.ItemSpacing.x - parent_window->ScrollbarSizes.x, FLT_MAX); // We want some overlap to convey the relative depth of each popup (here hard-coded to 4) window->PosFloat = FindBestPopupWindowPos(window->PosFloat, window->Size, flags, &window->AutoPosLastDirection, rect_to_avoid); } else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_appearing_after_being_hidden) @@ -3716,9 +3733,9 @@ bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size_on_first_ window->Scroll.y = window->ScrollTargetRelY - ((1.0f - center_ratio_y) * window->TitleBarHeight()) - (center_ratio_y * window->SizeFull.y); window->ScrollTargetRelY = FLT_MAX; } - window->Scroll.y = ImMax(window->Scroll.y, 0.0f); + window->Scroll = ImMax(window->Scroll, ImVec2(0.0f, 0.0f)); if (!window->Collapsed && !window->SkipItems) - window->Scroll.y = ImMin(window->Scroll.y, ImMax(0.0f, window->SizeContents.y - window->SizeFull.y)); + window->Scroll = ImMin(window->Scroll, ImMax(ImVec2(0.0f, 0.0f), window->SizeContents - window->SizeFull + window->ScrollbarSizes)); // Modal window darkens what is behind them if ((flags & ImGuiWindowFlags_Modal) != 0 && window == GetFrontMostModalRootWindow()) @@ -3776,8 +3793,10 @@ bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size_on_first_ title_bar_rect = window->TitleBarRect(); } - // Scrollbar + // Scrollbars + window->ScrollbarX = (window->SizeContents.x > window->Size.x - style.ScrollbarSize - window->WindowPadding.x) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar); window->ScrollbarY = (window->SizeContents.y > window->Size.y + style.ItemSpacing.y) && !(flags & ImGuiWindowFlags_NoScrollbar); + window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f); // Window background if (bg_alpha > 0.0f) @@ -3789,7 +3808,7 @@ bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size_on_first_ else if ((flags & ImGuiWindowFlags_Popup) != 0) window->DrawList->AddRectFilled(window->Pos, window->Pos+window->Size, window->Color(ImGuiCol_WindowBg, bg_alpha), window_rounding); else if ((flags & ImGuiWindowFlags_ChildWindow) != 0) - window->DrawList->AddRectFilled(window->Pos, window->Pos+window->Size-ImVec2(window->ScrollbarWidth(),0.0f), window->Color(ImGuiCol_ChildWindowBg, bg_alpha), window_rounding, window->ScrollbarY ? (1|8) : (0xF)); + window->DrawList->AddRectFilled(window->Pos, window->Pos+window->Size - window->ScrollbarSizes, window->Color(ImGuiCol_ChildWindowBg, bg_alpha), window_rounding, window->ScrollbarY ? (1|8) : (0xF)); else window->DrawList->AddRectFilled(window->Pos, window->Pos+window->Size, window->Color(ImGuiCol_WindowBg, bg_alpha), window_rounding); } @@ -3814,9 +3833,11 @@ bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size_on_first_ window->DrawList->AddLine(title_bar_rect.GetBL(), title_bar_rect.GetBR(), window->Color(ImGuiCol_Border)); } - // Scrollbar + // Scrollbars + if (window->ScrollbarX) + Scrollbar(window, true); if (window->ScrollbarY) - Scrollbar(window); + Scrollbar(window, false); // Render resize grip // (after the input handling so we don't have a frame of latency) @@ -3833,7 +3854,7 @@ bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size_on_first_ // Setup drawing context window->DC.ColumnsStartX = window->WindowPadding.x; window->DC.ColumnsOffsetX = 0.0f; - window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.ColumnsStartX + window->DC.ColumnsOffsetX, window->TitleBarHeight() + window->MenuBarHeight() + window->WindowPadding.y) - ImVec2(0.0f, window->Scroll.y); + window->DC.CursorStartPos = window->Pos - window->Scroll + ImVec2(window->DC.ColumnsStartX + window->DC.ColumnsOffsetX, window->TitleBarHeight() + window->MenuBarHeight() + window->WindowPadding.y); window->DC.CursorPos = window->DC.CursorStartPos; window->DC.CursorPosPrevLine = window->DC.CursorPos; window->DC.CursorMaxPos = window->DC.CursorStartPos; @@ -3909,8 +3930,8 @@ bool ImGui::Begin(const char* name, bool* p_opened, const ImVec2& size_on_first_ ImRect clip_rect(title_bar_rect.Min.x+0.5f+window->WindowPadding.x*0.5f, title_bar_rect.Max.y+window->MenuBarHeight()+0.5f, window->Pos.x+window->Size.x+0.5f-window->WindowPadding.x*0.5f, window->Pos.y+window->Size.y+0.5f); if ((flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_ShowBorders)) clip_rect.Min += ImVec2(1.0f,1.0f); - if (window->ScrollbarY) - clip_rect.Max.x -= style.ScrollbarSize; + clip_rect.Max.x -= window->ScrollbarY ? style.ScrollbarSize : 0.0f; + clip_rect.Max.y -= window->ScrollbarX ? style.ScrollbarSize : 0.0f; PushClipRect(clip_rect); // Clear 'accessed' flag last thing @@ -3965,23 +3986,34 @@ void ImGui::End() // Vertical scrollbar // The entire piece of code below is rather confusing because: // - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab) -// - We store values as ratio and in a form that allows the window content to change while we are holding on a scrollbar -static void Scrollbar(ImGuiWindow* window) +// - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar +// - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal. +static void Scrollbar(ImGuiWindow* window, bool horizontal) { ImGuiState& g = *GImGui; const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID("#SCROLLY"); + const ImGuiID id = window->GetID(horizontal ? "#SCROLLX" : "#SCROLLY"); // Render background - ImRect bb(window->Rect().Max.x - style.ScrollbarSize, window->Pos.y + window->TitleBarHeight()+1, window->Rect().Max.x, window->Rect().Max.y-1); + bool other_scrollbar = (horizontal ? window->ScrollbarY : window->ScrollbarX); + float other_scrollbar_size_w = other_scrollbar ? style.ScrollbarSize : 0.0f; + const ImRect window_rect = window->Rect(); + ImRect bb = horizontal + ? ImRect(window->Pos.x + 1, window_rect.Max.y - style.ScrollbarSize, window_rect.Max.x - 1 - other_scrollbar_size_w, window_rect.Max.y) + : ImRect(window_rect.Max.x - style.ScrollbarSize, window->Pos.y + window->TitleBarHeight()+1, window_rect.Max.x, window_rect.Max.y - 1 - other_scrollbar_size_w); window->DrawList->AddRectFilled(bb.Min, bb.Max, window->Color(ImGuiCol_ScrollbarBg)); bb.Expand(-3.0f); - const float scrollbar_height = bb.GetHeight(); + + // V denote the main axis of the scrollbar + float scrollbar_size_v = horizontal ? bb.GetWidth() : bb.GetHeight(); + float scroll_v = horizontal ? window->Scroll.x : window->Scroll.y; + float win_size_avail_v = (horizontal ? window->Size.x : window->Size.y) - other_scrollbar_size_w; + float win_size_contents_v = horizontal ? window->SizeContents.x : window->SizeContents.y; // The grabable box size generally represent the amount visible (vs the total scrollable amount) // But we maintain a minimum size in pixel to allow for the user to still aim inside. - const float grab_h_pixels = ImMin(ImMax(scrollbar_height * ImSaturate(window->Size.y / ImMax(window->SizeContents.y, window->Size.y)), style.GrabMinSize), scrollbar_height); - const float grab_h_norm = grab_h_pixels / scrollbar_height; + const float grab_h_pixels = ImMin(ImMax(scrollbar_size_v * ImSaturate(win_size_avail_v / ImMax(win_size_contents_v, win_size_avail_v)), style.GrabMinSize), scrollbar_size_v); + const float grab_h_norm = grab_h_pixels / scrollbar_size_v; // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar(). bool held = false; @@ -3989,46 +4021,58 @@ static void Scrollbar(ImGuiWindow* window) const bool previously_held = (g.ActiveId == id); ImGui::ButtonBehavior(bb, id, &hovered, &held, true); - const float scroll_max = ImMax(1.0f, window->SizeContents.y - window->Size.y); - float scroll_ratio = ImSaturate(window->Scroll.y / scroll_max); - float grab_y_norm = scroll_ratio * (scrollbar_height - grab_h_pixels) / scrollbar_height; - if (held) + float scroll_max = ImMax(1.0f, win_size_contents_v - win_size_avail_v); + float scroll_ratio = ImSaturate(scroll_v / scroll_max); + float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; + if (held && grab_h_norm < 1.0f) { - const float clicked_y_norm = ImSaturate((g.IO.MousePos.y - bb.Min.y) / scrollbar_height); // Click position in scrollbar space (0.0f->1.0f) + float scrollbar_pos_v = horizontal ? bb.Min.x : bb.Min.y; + float mouse_pos_v = horizontal ? g.IO.MousePos.x : g.IO.MousePos.y; + float* click_delta_to_grab_center_v = horizontal ? &g.ScrollbarClickDeltaToGrabCenter.x : &g.ScrollbarClickDeltaToGrabCenter.y; + + // Click position in scrollbar normalized space (0.0f->1.0f) + const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v); g.HoveredId = id; bool seek_absolute = false; if (!previously_held) { // On initial click calculate the distance between mouse and the center of the grab - if (clicked_y_norm >= grab_y_norm && clicked_y_norm <= grab_y_norm + grab_h_norm) + if (clicked_v_norm >= grab_v_norm && clicked_v_norm <= grab_v_norm + grab_h_norm) { - g.ScrollbarClickDeltaToGrabCenter = clicked_y_norm - grab_y_norm - grab_h_norm*0.5f; + *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f; } else { seek_absolute = true; - g.ScrollbarClickDeltaToGrabCenter = 0; + *click_delta_to_grab_center_v = 0.0f; } } // Apply scroll - // It is ok to modify ScrollY here because we are being called in Begin() after the calculation of SizeContents and before setting up our starting position - const float scroll_y_norm = ImSaturate((clicked_y_norm - g.ScrollbarClickDeltaToGrabCenter - grab_h_norm*0.5f) / (1.0f - grab_h_norm)); - window->Scroll.y = (float)(int)(0.5f + scroll_y_norm * (window->SizeContents.y - window->Size.y)); + // It is ok to modify Scroll here because we are being called in Begin() after the calculation of SizeContents and before setting up our starting position + const float scroll_v_norm = ImSaturate((clicked_v_norm - *click_delta_to_grab_center_v - grab_h_norm*0.5f) / (1.0f - grab_h_norm)); + scroll_v = (float)(int)(0.5f + scroll_v_norm * scroll_max);//(win_size_contents_v - win_size_v)); + if (horizontal) + window->Scroll.x = scroll_v; + else + window->Scroll.y = scroll_v; // Update values for rendering - scroll_ratio = ImSaturate(window->Scroll.y / scroll_max); - grab_y_norm = scroll_ratio * (scrollbar_height - grab_h_pixels) / scrollbar_height; + scroll_ratio = ImSaturate(scroll_v / scroll_max); + grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; // Update distance to grab now that we have seeked and saturated if (seek_absolute) - g.ScrollbarClickDeltaToGrabCenter = clicked_y_norm - grab_y_norm - grab_h_norm*0.5f; + *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f; } // Render const ImU32 grab_col = window->Color(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab); - window->DrawList->AddRectFilled(ImVec2(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_y_norm)), ImVec2(bb.Max.x, ImLerp(bb.Min.y, bb.Max.y, grab_y_norm) + grab_h_pixels), grab_col, style.ScrollbarRounding); + if (horizontal) + window->DrawList->AddRectFilled(ImVec2(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y), ImVec2(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, bb.Max.y), grab_col, style.ScrollbarRounding); + else + window->DrawList->AddRectFilled(ImVec2(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm)), ImVec2(bb.Max.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm) + grab_h_pixels), grab_col, style.ScrollbarRounding); } // Moving window to front of display (which happens to be back of our sorted list) @@ -4519,6 +4563,20 @@ void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiSetCond cond) g.SetNextWindowSizeCond = cond ? cond : ImGuiSetCond_Always; } +void ImGui::SetNextWindowContentSize(const ImVec2& size) +{ + ImGuiState& g = *GImGui; + g.SetNextWindowContentSizeVal = size; + g.SetNextWindowContentSizeCond = ImGuiSetCond_Always; +} + +void ImGui::SetNextWindowContentWidth(float width) +{ + ImGuiState& g = *GImGui; + g.SetNextWindowContentSizeVal = ImVec2(width, g.SetNextWindowContentSizeCond ? g.SetNextWindowContentSizeVal.y : 0.0f); + g.SetNextWindowContentSizeCond = ImGuiSetCond_Always; +} + void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiSetCond cond) { ImGuiState& g = *GImGui; @@ -4536,11 +4594,13 @@ void ImGui::SetNextWindowFocus() ImVec2 ImGui::GetContentRegionMax() { ImGuiWindow* window = GetCurrentWindowRead(); - ImVec2 mx = window->Size - window->WindowPadding; + ImVec2 content_region_size = ImVec2(window->SizeContentsExplicit.x ? window->SizeContentsExplicit.x : window->Size.x, window->SizeContentsExplicit.y ? window->SizeContentsExplicit.y : window->Size.y); + ImVec2 mx = content_region_size - window->Scroll - window->WindowPadding; if (window->DC.ColumnsCount != 1) mx.x = ImGui::GetColumnOffset(window->DC.ColumnsCurrent + 1) - window->WindowPadding.x; else - mx.x -= window->ScrollbarWidth(); + mx.x -= window->ScrollbarSizes.x; + mx.y -= window->ScrollbarSizes.y; return mx; } @@ -4559,17 +4619,22 @@ float ImGui::GetContentRegionAvailWidth() ImVec2 ImGui::GetWindowContentRegionMin() { ImGuiWindow* window = GetCurrentWindowRead(); - return ImVec2(0, window->TitleBarHeight() + window->MenuBarHeight()) + window->WindowPadding; + return ImVec2(-window->Scroll.x, -window->Scroll.y + window->TitleBarHeight() + window->MenuBarHeight()) + window->WindowPadding; } ImVec2 ImGui::GetWindowContentRegionMax() { ImGuiWindow* window = GetCurrentWindowRead(); - ImVec2 m = window->Size - window->WindowPadding; - m.x -= window->ScrollbarWidth(); + ImVec2 content_region_size = ImVec2(window->SizeContentsExplicit.x ? window->SizeContentsExplicit.x : window->Size.x, window->SizeContentsExplicit.y ? window->SizeContentsExplicit.y : window->Size.y); + ImVec2 m = content_region_size - window->Scroll - window->WindowPadding - window->ScrollbarSizes; return m; } +float ImGui::GetWindowContentRegionWidth() +{ + return GetWindowContentRegionMax().x - GetWindowContentRegionMin().x; +} + float ImGui::GetTextLineHeight() { ImGuiState& g = *GImGui; @@ -4619,37 +4684,39 @@ void ImGui::SetWindowFontScale(float scale) ImVec2 ImGui::GetCursorPos() { ImGuiWindow* window = GetCurrentWindowRead(); - return window->DC.CursorPos - window->Pos; + return window->DC.CursorPos - window->Pos + window->Scroll; } float ImGui::GetCursorPosX() { - return ImGui::GetCursorPos().x; + ImGuiWindow* window = GetCurrentWindow(); + return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x; } float ImGui::GetCursorPosY() { - return ImGui::GetCursorPos().y; + ImGuiWindow* window = GetCurrentWindow(); + return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y; } void ImGui::SetCursorPos(const ImVec2& local_pos) { ImGuiWindow* window = GetCurrentWindow(); - window->DC.CursorPos = window->Pos + local_pos; + window->DC.CursorPos = window->Pos - window->Scroll + local_pos; window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos); } void ImGui::SetCursorPosX(float x) { ImGuiWindow* window = GetCurrentWindow(); - window->DC.CursorPos.x = window->Pos.x + x; + window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x; window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x); } void ImGui::SetCursorPosY(float y) { ImGuiWindow* window = GetCurrentWindow(); - window->DC.CursorPos.y = window->Pos.y + y; + window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y; window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y); } @@ -6944,7 +7011,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 } draw_window = GetCurrentWindow(); draw_window->DC.CursorPos += style.FramePadding; - size.x -= draw_window->ScrollbarWidth(); + size.x -= draw_window->ScrollbarSizes.x; } else { @@ -8388,7 +8455,7 @@ void ImGui::BeginGroup() group_data.BackupLogLinePosY = window->DC.LogLinePosY; group_data.AdvanceCursor = true; - window->DC.ColumnsStartX = window->DC.CursorPos.x - window->Pos.x; + window->DC.ColumnsStartX = window->DC.CursorPos.x - window->Pos.x + window->Scroll.x; window->DC.CursorMaxPos = window->DC.CursorPos; window->DC.CurrentLineHeight = 0.0f; window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; @@ -8442,7 +8509,7 @@ void ImGui::SameLine(float local_pos_x, float spacing_w) if (local_pos_x != 0.0f) { if (spacing_w < 0.0f) spacing_w = 0.0f; - x = window->Pos.x + local_pos_x + spacing_w; + x = window->Pos.x - window->Scroll.x + local_pos_x + spacing_w; y = window->DC.CursorPosPrevLine.y; } else @@ -8471,7 +8538,7 @@ void ImGui::NextColumn() window->DC.ColumnsCellMaxY = ImMax(window->DC.ColumnsCellMaxY, window->DC.CursorPos.y); if (++window->DC.ColumnsCurrent < window->DC.ColumnsCount) { - window->DC.ColumnsOffsetX = ImGui::GetColumnOffset(window->DC.ColumnsCurrent) - window->DC.ColumnsStartX + g.Style.ItemSpacing.x; + window->DC.ColumnsOffsetX = ImGui::GetColumnOffset(window->DC.ColumnsCurrent) - window->DC.ColumnsStartX + window->Scroll.x + g.Style.ItemSpacing.x; window->DrawList->ChannelsSetCurrent(window->DC.ColumnsCurrent); } else @@ -8481,7 +8548,7 @@ void ImGui::NextColumn() window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY; window->DrawList->ChannelsSetCurrent(0); } - window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.ColumnsStartX + window->DC.ColumnsOffsetX); + window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.ColumnsStartX - window->Scroll.x + window->DC.ColumnsOffsetX); window->DC.CursorPos.y = window->DC.ColumnsCellMinY; window->DC.CurrentLineHeight = 0.0f; window->DC.CurrentLineTextBaseOffset = 0.0f; @@ -8536,8 +8603,9 @@ float ImGui::GetColumnOffset(int column_index) IM_ASSERT(column_index < window->DC.ColumnsOffsetsT.Size); const float t = window->DC.ColumnsOffsetsT[column_index]; - const float min_x = window->DC.ColumnsStartX; - const float max_x = window->Size.x - ((window->Flags & ImGuiWindowFlags_NoScrollbar) ? 0 : g.Style.ScrollbarSize);// - window->WindowPadding().x; + const float content_region_width = window->SizeContentsExplicit.x ? window->SizeContentsExplicit.x : window->Size.x; + const float min_x = window->DC.ColumnsStartX - window->Scroll.x; + const float max_x = content_region_width - window->Scroll.x - ((window->Flags & ImGuiWindowFlags_NoScrollbar) ? 0 : g.Style.ScrollbarSize);// - window->WindowPadding().x; const float x = min_x + t * (max_x - min_x); return (float)(int)x; } @@ -8552,8 +8620,9 @@ void ImGui::SetColumnOffset(int column_index, float offset) IM_ASSERT(column_index < window->DC.ColumnsOffsetsT.Size); const ImGuiID column_id = window->DC.ColumnsSetID + ImGuiID(column_index); - const float min_x = window->DC.ColumnsStartX; - const float max_x = window->Size.x - ((window->Flags & ImGuiWindowFlags_NoScrollbar) ? 0 : g.Style.ScrollbarSize);// - window->WindowPadding().x; + const float content_region_width = window->SizeContentsExplicit.x ? window->SizeContentsExplicit.x : window->Size.x; + const float min_x = window->DC.ColumnsStartX - window->Scroll.x; + const float max_x = content_region_width - window->Scroll.x - ((window->Flags & ImGuiWindowFlags_NoScrollbar) ? 0 : g.Style.ScrollbarSize);// - window->WindowPadding().x; const float t = (offset - min_x) / (max_x - min_x); window->DC.StateStorage->SetFloat(column_id, t); window->DC.ColumnsOffsetsT[column_index] = t; @@ -8641,7 +8710,7 @@ void ImGui::Columns(int columns_count, const char* id, bool border) window->DC.ColumnsStartPos = window->DC.CursorPos; window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY = window->DC.CursorPos.y; window->DC.ColumnsOffsetX = 0.0f; - window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.ColumnsStartX + window->DC.ColumnsOffsetX); + window->DC.CursorPos.x = (float)(int)(window->Pos.x - window->Scroll.x + window->DC.ColumnsStartX + window->DC.ColumnsOffsetX); if (window->DC.ColumnsCount != 1) { @@ -8672,7 +8741,7 @@ void ImGui::Indent() ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); window->DC.ColumnsStartX += g.Style.IndentSpacing; - window->DC.CursorPos.x = window->Pos.x + window->DC.ColumnsStartX + window->DC.ColumnsOffsetX; + window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + window->DC.ColumnsStartX + window->DC.ColumnsOffsetX; } void ImGui::Unindent() @@ -8680,7 +8749,7 @@ void ImGui::Unindent() ImGuiState& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); window->DC.ColumnsStartX -= g.Style.IndentSpacing; - window->DC.CursorPos.x = window->Pos.x + window->DC.ColumnsStartX + window->DC.ColumnsOffsetX; + window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + window->DC.ColumnsStartX + window->DC.ColumnsOffsetX; } void ImGui::TreePush(const char* str_id) diff --git a/imgui.h b/imgui.h index 8a6fc016..d1aad6d4 100644 --- a/imgui.h +++ b/imgui.h @@ -120,11 +120,12 @@ namespace ImGui IMGUI_API bool BeginChild(const char* str_id, const ImVec2& size = ImVec2(0,0), bool border = false, ImGuiWindowFlags extra_flags = 0); // begin a scrolling region. size==0.0f: use remaining window size, size<0.0f: use remaining window size minus abs(size). size>0.0f: fixed size. each axis can use a different mode, e.g. ImVec2(0,400). IMGUI_API bool BeginChild(ImGuiID id, const ImVec2& size = ImVec2(0,0), bool border = false, ImGuiWindowFlags extra_flags = 0); // " IMGUI_API void EndChild(); - IMGUI_API ImVec2 GetContentRegionMax(); // window or current column boundaries, in windows coordinates + IMGUI_API ImVec2 GetContentRegionMax(); // current content boundaries (typically window boundaries including scrolling, or current column boundaries), in windows coordinates IMGUI_API ImVec2 GetContentRegionAvail(); // == GetContentRegionMax() - GetCursorPos() - IMGUI_API ImVec2 GetWindowContentRegionMin(); // window boundaries, in windows coordinates - IMGUI_API ImVec2 GetWindowContentRegionMax(); IMGUI_API float GetContentRegionAvailWidth(); // + IMGUI_API ImVec2 GetWindowContentRegionMin(); // content boundaries min (roughly (0,0)-Scroll), in window coordinates + IMGUI_API ImVec2 GetWindowContentRegionMax(); // content boundaries max (roughly (0,0)+Size-Scroll) where Size can be override with SetNextWindowContentSize(), in window coordinates + IMGUI_API float GetWindowContentRegionWidth(); // IMGUI_API ImDrawList* GetWindowDrawList(); // get rendering command-list if you want to append your own draw primitives IMGUI_API ImFont* GetWindowFont(); IMGUI_API float GetWindowFontSize(); // size (also height in pixels) of current font with current scale applied @@ -138,6 +139,8 @@ namespace ImGui IMGUI_API void SetNextWindowPos(const ImVec2& pos, ImGuiSetCond cond = 0); // set next window position. call before Begin() IMGUI_API void SetNextWindowPosCenter(ImGuiSetCond cond = 0); // set next window position to be centered on screen. call before Begin() IMGUI_API void SetNextWindowSize(const ImVec2& size, ImGuiSetCond cond = 0); // set next window size. set axis to 0.0f to force an auto-fit on this axis. call before Begin() + IMGUI_API void SetNextWindowContentSize(const ImVec2& size); // set next window content size (enforce the range of scrollbars). set axis to 0.0f to leave it automatic. call before Begin() + IMGUI_API void SetNextWindowContentWidth(float width); // set next window content width (enforce the range of horizontal scrollbar). call before Begin() IMGUI_API void SetNextWindowCollapsed(bool collapsed, ImGuiSetCond cond = 0); // set next window collapsed state. call before Begin() IMGUI_API void SetNextWindowFocus(); // set next window to be focused / front-most. call before Begin() IMGUI_API void SetWindowPos(const ImVec2& pos, ImGuiSetCond cond = 0); // set current window position - call within Begin()/End(). may incur tearing @@ -449,6 +452,7 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_NoSavedSettings = 1 << 8, // Never load/save settings in .ini file ImGuiWindowFlags_NoInputs = 1 << 9, // Disable catching mouse or keyboard inputs ImGuiWindowFlags_MenuBar = 1 << 10, // Has a menu-bar + ImGuiWindowFlags_HorizontalScrollbar = 1 << 11, // Enable horizontal scrollbar (off by default). You need to use SetNextWindowContentSize(ImVec2(width,0.0f)); prior to calling Begin() to specify width. Read code in imgui_demo in the "Horizontal Scrolling" section. // [Internal] ImGuiWindowFlags_ChildWindow = 1 << 20, // Don't use! For internal use by BeginChild() ImGuiWindowFlags_ChildWindowAutoFitX = 1 << 21, // Don't use! For internal use by BeginChild() diff --git a/imgui_internal.h b/imgui_internal.h index 2e8634ed..274b0bc6 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -359,9 +359,11 @@ struct ImGuiState // Storage for SetNexWindow** and SetNextTreeNode*** functions ImVec2 SetNextWindowPosVal; ImVec2 SetNextWindowSizeVal; + ImVec2 SetNextWindowContentSizeVal; bool SetNextWindowCollapsedVal; ImGuiSetCond SetNextWindowPosCond; ImGuiSetCond SetNextWindowSizeCond; + ImGuiSetCond SetNextWindowContentSizeCond; ImGuiSetCond SetNextWindowCollapsedCond; bool SetNextWindowFocus; bool SetNextTreeNodeOpenedVal; @@ -384,7 +386,7 @@ struct ImGuiState float DragSpeedDefaultRatio; // If speed == 0.0f, uses (max-min) * DragSpeedDefaultRatio float DragSpeedScaleSlow; float DragSpeedScaleFast; - float ScrollbarClickDeltaToGrabCenter; // Distance between mouse and center of grab box, normalized in parent space + ImVec2 ScrollbarClickDeltaToGrabCenter; // Distance between mouse and center of grab box, normalized in parent space. Use storage? char Tooltip[1024]; char* PrivateClipboard; // If no custom clipboard handler is defined @@ -446,7 +448,7 @@ struct ImGuiState DragSpeedDefaultRatio = 0.01f; DragSpeedScaleSlow = 0.01f; DragSpeedScaleFast = 10.0f; - ScrollbarClickDeltaToGrabCenter = 0.0f; + ScrollbarClickDeltaToGrabCenter = ImVec2(0.0f, 0.0f); memset(Tooltip, 0, sizeof(Tooltip)); PrivateClipboard = NULL; @@ -558,12 +560,14 @@ struct ImGuiWindow ImVec2 Size; // Current size (==SizeFull or collapsed title bar size) ImVec2 SizeFull; // Size when non collapsed ImVec2 SizeContents; // Size of contents (== extents reach of the drawing cursor) from previous frame + ImVec2 SizeContentsExplicit; // Size of contents explicitly set by the user via SetNextWindowContentSize() ImVec2 WindowPadding; // Window padding at the time of begin. We need to lock it, in particular manipulation of the ShowBorder would have an effect ImGuiID MoveID; // == window->GetID("#MOVE") ImVec2 Scroll; float ScrollTargetRelY; // target scroll position. stored as cursor position with scrolling canceled out, so the highest point is always 0.0f. (FLT_MAX for no change) float ScrollTargetCenterRatioY; // 0.0f = scroll so that target position is at top, 0.5f = scroll so that target position is centered - bool ScrollbarY; + bool ScrollbarX, ScrollbarY; + ImVec2 ScrollbarSizes; // bool Active; // Set to true on Begin() bool WasActive; bool Accessed; // Set to true when any widget access the current window @@ -614,7 +618,6 @@ public: ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight())); } float MenuBarHeight() const { return (Flags & ImGuiWindowFlags_MenuBar) ? CalcFontSize() + GImGui->Style.FramePadding.y * 2.0f : 0.0f; } ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight(); return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight()); } - float ScrollbarWidth() const { return ScrollbarY ? GImGui->Style.ScrollbarSize : 0.0f; } ImU32 Color(ImGuiCol idx, float a=1.f) const { ImVec4 c = GImGui->Style.Colors[idx]; c.w *= GImGui->Style.Alpha * a; return ImGui::ColorConvertFloat4ToU32(c); } ImU32 Color(const ImVec4& col) const { ImVec4 c = col; c.w *= GImGui->Style.Alpha; return ImGui::ColorConvertFloat4ToU32(c); } };