Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Smooth Scrolling #7348

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
59 changes: 47 additions & 12 deletions imgui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1243,6 +1243,9 @@ ImGuiStyle::ImGuiStyle()

// Default theme
ImGui::StyleColorsDark(this);

// Smooth Scrolling
ScrollSmooth = 1.00f; // Disabled by default. It's just inmediate jump from ScrollExpected to the visual Scroll
}

// To scale your entire UI (e.g. if you want your app to use High DPI or generally be DPI aware) you may use this helper function. Scaling the fonts is done separately and is up to you.
Expand Down Expand Up @@ -3140,6 +3143,7 @@ static const ImGuiDataVarInfo GStyleVarInfo[] =
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, SeparatorTextBorderSize)},// ImGuiStyleVar_SeparatorTextBorderSize
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SeparatorTextAlign) }, // ImGuiStyleVar_SeparatorTextAlign
{ ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SeparatorTextPadding) }, // ImGuiStyleVar_SeparatorTextPadding
{ ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ScrollSmooth) }, // ImGuiStyleVar_ScrollSmooth
};

const ImGuiDataVarInfo* ImGui::GetStyleVarInfo(ImGuiStyleVar idx)
Expand Down Expand Up @@ -3761,6 +3765,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* ctx, const char* name) : DrawListInst(NUL
IDStack.push_back(ID);
MoveId = GetID("#MOVE");
ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);
ScrollExpected = ImVec2(0,0);
ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);
AutoFitFramesX = AutoFitFramesY = -1;
AutoPosLastDirection = ImGuiDir_None;
Expand Down Expand Up @@ -9211,15 +9216,15 @@ void ImGui::UpdateMouseWheel()
LockWheelingWindow(window, wheel.x);
float max_step = window->InnerRect.GetWidth() * 0.67f;
float scroll_step = ImTrunc(ImMin(2 * window->CalcFontSize(), max_step));
SetScrollX(window, window->Scroll.x - wheel.x * scroll_step);
SetScrollX(window, window->ScrollExpected.x - wheel.x * scroll_step);
g.WheelingWindowScrolledFrame = g.FrameCount;
}
if (do_scroll[ImGuiAxis_Y])
{
LockWheelingWindow(window, wheel.y);
float max_step = window->InnerRect.GetHeight() * 0.67f;
float scroll_step = ImTrunc(ImMin(5 * window->CalcFontSize(), max_step));
SetScrollY(window, window->Scroll.y - wheel.y * scroll_step);
SetScrollY(window, window->ScrollExpected.y - wheel.y * scroll_step);
g.WheelingWindowScrolledFrame = g.FrameCount;
}
}
Expand Down Expand Up @@ -10409,21 +10414,51 @@ static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)
ImVec2 decoration_size(window->DecoOuterSizeX1 + window->DecoInnerSizeX1 + window->DecoOuterSizeX2, window->DecoOuterSizeY1 + window->DecoInnerSizeY1 + window->DecoOuterSizeY2);
for (int axis = 0; axis < 2; axis++)
{
if (window->ScrollTarget[axis] < FLT_MAX)
{
if (window->ScrollTarget[axis] < FLT_MAX) {
float center_ratio = window->ScrollTargetCenterRatio[axis];
float scroll_target = window->ScrollTarget[axis];
if (window->ScrollTargetEdgeSnapDist[axis] > 0.0f)
{
if (window->ScrollTargetEdgeSnapDist[axis] > 0.0f) {
float snap_min = 0.0f;
float snap_max = window->ScrollMax[axis] + window->SizeFull[axis] - decoration_size[axis];
scroll_target = CalcScrollEdgeSnap(scroll_target, snap_min, snap_max, window->ScrollTargetEdgeSnapDist[axis], center_ratio);
}
scroll[axis] = scroll_target - center_ratio * (window->SizeFull[axis] - decoration_size[axis]);
window->ScrollExpected[axis] = scroll_target - center_ratio * (window->SizeFull[axis] - decoration_size[axis]);
}
//
// Smooth scroll.
// Instead use "Scroll" value in the window, all setters that sets the scroll absolutely now points to "ScrollExpected"
// Here, we take from ScrollTarget (from some functions like ScrollHere + mouse wheel) to set the ScrollExpected value
// Also, Scroll var in window is processed to meet ScrollExpected Value
//
// The formula is pretty simple to generate a smooth scrolling that can be tweaked just by one float value.
//
// The Float is "ImGuiStyleVar_ScrollSmooth". Can be set on the style or via PushStyleVar.
// A Value of 1.0f is just inmediate (transported from ScrollExpected to Scroll).
// A Value higher of 1.0f will make the scrolling smoother.
//
// The ScrollExpected is also clamped (as previously the "Scroll" value) from 0 to sScrollMax
//
// The approach is frame bounded and not time bounded.
// It should be prefereable use a time bounded approach but this is pretty simple so we don't need to add extra vars
// to save a scrolling "start" time to have a delta / deal with posible increments during the scrolling itself (restar timer)
// Anyway it should not be complicated to add but this approach is small, simple, can be user or not and works pretty well
//
window->ScrollExpected[axis] = IM_ROUND(ImMax(window->ScrollExpected[axis], 0.0f));
if (!window->Collapsed && !window->SkipItems) {
window->ScrollExpected[axis] = ImMin(window->ScrollExpected[axis], window->ScrollMax[axis]);
}
ImGuiContext& g = *GImGui;
ImGuiStyle& style = g.Style;
{
if (scroll[axis] != window->ScrollExpected[axis]) {
float diff = window->ScrollExpected[axis] - scroll[axis];
if (diff > 0) {
scroll[axis] += ImMin(diff, (diff / style.ScrollSmooth));
} else {
scroll[axis] -= ImMin(-diff, (-diff / style.ScrollSmooth));
}
}
}
scroll[axis] = IM_ROUND(ImMax(scroll[axis], 0.0f));
if (!window->Collapsed && !window->SkipItems)
scroll[axis] = ImMin(scroll[axis], window->ScrollMax[axis]);
}
return scroll;
}
Expand Down Expand Up @@ -10516,13 +10551,13 @@ ImVec2 ImGui::ScrollToRectEx(ImGuiWindow* window, const ImRect& item_rect, ImGui
float ImGui::GetScrollX()
{
ImGuiWindow* window = GImGui->CurrentWindow;
return window->Scroll.x;
return window->ScrollExpected.x;
}

float ImGui::GetScrollY()
{
ImGuiWindow* window = GImGui->CurrentWindow;
return window->Scroll.y;
return window->ScrollExpected.y;
}

float ImGui::GetScrollMaxX()
Expand Down
3 changes: 3 additions & 0 deletions imgui.h
Original file line number Diff line number Diff line change
Expand Up @@ -1588,6 +1588,7 @@ enum ImGuiStyleVar_
ImGuiStyleVar_SeparatorTextBorderSize,// float SeparatorTextBorderSize
ImGuiStyleVar_SeparatorTextAlign, // ImVec2 SeparatorTextAlign
ImGuiStyleVar_SeparatorTextPadding,// ImVec2 SeparatorTextPadding
ImGuiStyleVar_ScrollSmooth, // float ScrollSmooth
ImGuiStyleVar_COUNT
};

Expand Down Expand Up @@ -2040,6 +2041,8 @@ struct ImGuiStyle
ImGuiHoveredFlags HoverFlagsForTooltipMouse;// Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using mouse.
ImGuiHoveredFlags HoverFlagsForTooltipNav; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using keyboard/gamepad.

float ScrollSmooth; // Smooth scrolling amount: 1.0f no smoothing. Anything above 1.0f will make the scroll delta more smooth.

IMGUI_API ImGuiStyle();
IMGUI_API void ScaleAllSizes(float scale_factor);
};
Expand Down
44 changes: 44 additions & 0 deletions imgui_demo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3281,6 +3281,50 @@ static void ShowDemoWindowLayout()
}
ImGui::PopID();

// Smooth scroll functions
IMGUI_DEMO_MARKER("Smooth Scrolling");
HelpMarker("Use ImGuiStyleVar_ScrollSmooth to enable and define the amount of smooth scrolling. Move the scrolls with the same controls as the previous example to check the difference.");

ImGui::PushStyleVar(ImGuiStyleVar_ScrollSmooth,20.0f);
ImGui::PushID("##SmoothScrolling");
for (int i = 0; i < 5; i++) {
if (i > 0) ImGui::SameLine();
ImGui::BeginGroup();
const char* names[] = { "Top", "25%", "Center", "75%", "Bottom" };
ImGui::TextUnformatted(names[i]);

const ImGuiWindowFlags child_flags = enable_extra_decorations ? ImGuiWindowFlags_MenuBar : 0;
const ImGuiID child_id = ImGui::GetID((void*)(intptr_t)i);
const bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(child_w, 200.0f), ImGuiChildFlags_Border, child_flags);
if (ImGui::BeginMenuBar()) {
ImGui::TextUnformatted("abc");
ImGui::EndMenuBar();
}
if (scroll_to_off)
ImGui::SetScrollY(scroll_to_off_px);
if (scroll_to_pos)
ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y + scroll_to_pos_px, i * 0.25f);
if (child_is_visible) // Avoid calling SetScrollHereY when running with culled items
{
for (int item = 0; item < 100; item++) {
if (enable_track && item == track_item) {
ImGui::TextColored(ImVec4(1, 1, 0, 1), "Item %d", item);
ImGui::SetScrollHereY(i * 0.25f); // 0.0f:top, 0.5f:center, 1.0f:bottom
} else {
ImGui::Text("Item %d", item);
}
}
}
float scroll_y = ImGui::GetScrollY();
float scroll_max_y = ImGui::GetScrollMaxY();
ImGui::EndChild();
ImGui::Text("%.0f/%.0f", scroll_y, scroll_max_y);
ImGui::EndGroup();
}
ImGui::PopID();
ImGui::PopStyleVar();


// Horizontal scroll functions
IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal");
ImGui::Spacing();
Expand Down
3 changes: 2 additions & 1 deletion imgui_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -2519,7 +2519,8 @@ struct IMGUI_API ImGuiWindow
int NameBufLen; // Size of buffer storing Name. May be larger than strlen(Name)!
ImGuiID MoveId; // == window->GetID("#MOVE")
ImGuiID ChildId; // ID of corresponding item in parent window (for navigation to return from child window to parent window)
ImVec2 Scroll;
ImVec2 Scroll; // Current Visible Scroll position
ImVec2 ScrollExpected; // Current Expected Scroll position
ImVec2 ScrollMax;
ImVec2 ScrollTarget; // target scroll position. stored as cursor position with scrolling canceled out, so the highest point is always 0.0f. (FLT_MAX for no change)
ImVec2 ScrollTargetCenterRatio; // 0.0f = scroll so that target position is at top, 0.5f = scroll so that target position is centered
Expand Down
4 changes: 2 additions & 2 deletions imgui_widgets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -916,9 +916,9 @@ void ImGui::Scrollbar(ImGuiAxis axis)
}
float size_avail = window->InnerRect.Max[axis] - window->InnerRect.Min[axis];
float size_contents = window->ContentSize[axis] + window->WindowPadding[axis] * 2.0f;
ImS64 scroll = (ImS64)window->Scroll[axis];
ImS64 scroll = (ImS64)window->ScrollExpected[axis];
ScrollbarEx(bb, id, axis, &scroll, (ImS64)size_avail, (ImS64)size_contents, rounding_corners);
window->Scroll[axis] = (float)scroll;
window->ScrollExpected[axis] = (float)scroll;
}

// Vertical/Horizontal scrollbar
Expand Down