Scrolling: Made it possible for mouse wheel and navigation-triggered scrolling to override a call to SetScrollX()/SetScrollY(), making it possible to use a simpler stateless pattern for auto-scrolling.

Demo: Log, Console: Using a simpler stateless pattern for auto-scrolling.
This commit is contained in:
omar 2019-07-22 18:24:23 -07:00
parent 34cf00566f
commit dcd03f62a7
5 changed files with 40 additions and 42 deletions

View File

@ -67,6 +67,11 @@ Other Changes:
- InputTextMultiline: Fixed vertical scrolling tracking glitch. - InputTextMultiline: Fixed vertical scrolling tracking glitch.
- Word-wrapping: Fixed overzealous word-wrapping when glyph edge lands exactly on the limit. Because - Word-wrapping: Fixed overzealous word-wrapping when glyph edge lands exactly on the limit. Because
of this, auto-fitting exactly unwrapped text would make it wrap. (fixes initial 1.15 commit, 78645a7d). of this, auto-fitting exactly unwrapped text would make it wrap. (fixes initial 1.15 commit, 78645a7d).
- Scrolling: Made it possible for mouse wheel and navigation-triggered scrolling to override a call to
SetScrollX()/SetScrollY(), making it possible to use a simpler stateless pattern for auto-scrolling:
// (Submit items..)
if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) // Keep scrolling at the bottom if already
ImGui::SetScrollHereY(1.0f);
- Scrolling: Added SetScrollHereX(), SetScrollFromPosX() for completeness. (#1580) [@kevreco] - Scrolling: Added SetScrollHereX(), SetScrollFromPosX() for completeness. (#1580) [@kevreco]
- Style: Attenuated default opacity of ImGuiCol_Separator in Classic and Light styles. - Style: Attenuated default opacity of ImGuiCol_Separator in Classic and Light styles.
- Style: Added style.ColorButtonPosition (left/right, defaults to ImGuiDir_Right) to move the color button - Style: Added style.ColorButtonPosition (left/right, defaults to ImGuiDir_Right) to move the color button
@ -86,6 +91,7 @@ Other Changes:
- ImDrawListSplitter: Fixed memory leak when using low-level split api (was not affecting ImDrawList api, - ImDrawListSplitter: Fixed memory leak when using low-level split api (was not affecting ImDrawList api,
also this type was added in 1.71 and not advertised as a public-facing feature). also this type was added in 1.71 and not advertised as a public-facing feature).
- Fonts: binary_to_compressed_c.cpp: Display an error message if failing to open/read the input font file. - Fonts: binary_to_compressed_c.cpp: Display an error message if failing to open/read the input font file.
- Demo: Log, Console: Using a simpler stateless pattern for auto-scrolling.
- Backends: DX10/DX11: Backup, clear and restore Geometry Shader is any is bound when calling renderer. - Backends: DX10/DX11: Backup, clear and restore Geometry Shader is any is bound when calling renderer.
- Backends: DX11: Clear Hull Shader, Domain Shader, Compute Shader before rendering. Not backing/restoring them. - Backends: DX11: Clear Hull Shader, Domain Shader, Compute Shader before rendering. Not backing/restoring them.
- Backends: OSX: Disabled default native Mac clipboard copy/paste implementation in core library (added in 1.71), - Backends: OSX: Disabled default native Mac clipboard copy/paste implementation in core library (added in 1.71),

View File

@ -3506,7 +3506,7 @@ void ImGui::UpdateMouseWheel()
{ {
float max_step = window->InnerRect.GetHeight() * 0.67f; float max_step = window->InnerRect.GetHeight() * 0.67f;
float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step)); float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step));
SetWindowScrollY(window, window->Scroll.y - wheel_y * scroll_step); SetScrollY(window, window->Scroll.y - wheel_y * scroll_step);
} }
} }
@ -3521,7 +3521,7 @@ void ImGui::UpdateMouseWheel()
{ {
float max_step = window->InnerRect.GetWidth() * 0.67f; float max_step = window->InnerRect.GetWidth() * 0.67f;
float scroll_step = ImFloor(ImMin(2 * window->CalcFontSize(), max_step)); float scroll_step = ImFloor(ImMin(2 * window->CalcFontSize(), max_step));
SetWindowScrollX(window, window->Scroll.x - wheel_x * scroll_step); SetScrollX(window, window->Scroll.x - wheel_x * scroll_step);
} }
} }
} }
@ -6524,16 +6524,6 @@ ImVec2 ImGui::GetWindowPos()
return window->Pos; return window->Pos;
} }
void ImGui::SetWindowScrollX(ImGuiWindow* window, float new_scroll_x)
{
window->Scroll.x = new_scroll_x;
}
void ImGui::SetWindowScrollY(ImGuiWindow* window, float new_scroll_y)
{
window->Scroll.y = new_scroll_y;
}
void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond) void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)
{ {
// Test condition (NB: bit 0 is always true) and clear flags for next time // Test condition (NB: bit 0 is always true) and clear flags for next time
@ -6921,6 +6911,18 @@ void ImGui::SetScrollY(float scroll_y)
window->ScrollTargetCenterRatio.y = 0.0f; window->ScrollTargetCenterRatio.y = 0.0f;
} }
void ImGui::SetScrollX(ImGuiWindow* window, float new_scroll_x)
{
window->ScrollTarget.x = new_scroll_x;
window->ScrollTargetCenterRatio.x = 0.0f;
}
void ImGui::SetScrollY(ImGuiWindow* window, float new_scroll_y)
{
window->ScrollTarget.y = new_scroll_y;
window->ScrollTargetCenterRatio.y = 0.0f;
}
void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio) void ImGui::SetScrollFromPosX(float local_x, float center_x_ratio)
{ {
// We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size
@ -8333,9 +8335,9 @@ static void ImGui::NavUpdate()
if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest) if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest)
{ {
if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right)
SetWindowScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed)); SetScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));
if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down)
SetWindowScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); SetScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));
} }
// *Normal* Manual scroll with NavScrollXXX keys // *Normal* Manual scroll with NavScrollXXX keys
@ -8343,12 +8345,12 @@ static void ImGui::NavUpdate()
ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f/10.0f, 10.0f); ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f/10.0f, 10.0f);
if (scroll_dir.x != 0.0f && window->ScrollbarX) if (scroll_dir.x != 0.0f && window->ScrollbarX)
{ {
SetWindowScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed)); SetScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed));
g.NavMoveFromClampedRefRect = true; g.NavMoveFromClampedRefRect = true;
} }
if (scroll_dir.y != 0.0f) if (scroll_dir.y != 0.0f)
{ {
SetWindowScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed)); SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed));
g.NavMoveFromClampedRefRect = true; g.NavMoveFromClampedRefRect = true;
} }
} }
@ -8466,9 +8468,9 @@ static float ImGui::NavUpdatePageUpPageDown(int allowed_dir_flags)
{ {
// Fallback manual-scroll when window has no navigable item // Fallback manual-scroll when window has no navigable item
if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true)) if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true))
SetWindowScrollY(window, window->Scroll.y - window->InnerRect.GetHeight()); SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());
else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true)) else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true))
SetWindowScrollY(window, window->Scroll.y + window->InnerRect.GetHeight()); SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());
} }
else else
{ {

View File

@ -3449,7 +3449,7 @@ struct ExampleAppConsole
Commands.push_back("CLEAR"); Commands.push_back("CLEAR");
Commands.push_back("CLASSIFY"); // "classify" is only here to provide an example of "C"+[tab] completing to "CL" and displaying matches. Commands.push_back("CLASSIFY"); // "classify" is only here to provide an example of "C"+[tab] completing to "CL" and displaying matches.
AutoScroll = true; AutoScroll = true;
ScrollToBottom = true; ScrollToBottom = false;
AddLog("Welcome to Dear ImGui!"); AddLog("Welcome to Dear ImGui!");
} }
~ExampleAppConsole() ~ExampleAppConsole()
@ -3470,7 +3470,6 @@ struct ExampleAppConsole
for (int i = 0; i < Items.Size; i++) for (int i = 0; i < Items.Size; i++)
free(Items[i]); free(Items[i]);
Items.clear(); Items.clear();
ScrollToBottom = true;
} }
void AddLog(const char* fmt, ...) IM_FMTARGS(2) void AddLog(const char* fmt, ...) IM_FMTARGS(2)
@ -3483,8 +3482,6 @@ struct ExampleAppConsole
buf[IM_ARRAYSIZE(buf)-1] = 0; buf[IM_ARRAYSIZE(buf)-1] = 0;
va_end(args); va_end(args);
Items.push_back(Strdup(buf)); Items.push_back(Strdup(buf));
if (AutoScroll)
ScrollToBottom = true;
} }
void Draw(const char* title, bool* p_open) void Draw(const char* title, bool* p_open)
@ -3513,8 +3510,7 @@ struct ExampleAppConsole
if (ImGui::SmallButton("Add Dummy Text")) { AddLog("%d some text", Items.Size); AddLog("some more text"); AddLog("display very important message here!"); } ImGui::SameLine(); if (ImGui::SmallButton("Add Dummy Text")) { AddLog("%d some text", Items.Size); AddLog("some more text"); AddLog("display very important message here!"); } ImGui::SameLine();
if (ImGui::SmallButton("Add Dummy Error")) { AddLog("[error] something went wrong"); } ImGui::SameLine(); if (ImGui::SmallButton("Add Dummy Error")) { AddLog("[error] something went wrong"); } ImGui::SameLine();
if (ImGui::SmallButton("Clear")) { ClearLog(); } ImGui::SameLine(); if (ImGui::SmallButton("Clear")) { ClearLog(); } ImGui::SameLine();
bool copy_to_clipboard = ImGui::SmallButton("Copy"); ImGui::SameLine(); bool copy_to_clipboard = ImGui::SmallButton("Copy");
if (ImGui::SmallButton("Scroll to bottom")) ScrollToBottom = true;
//static float t = 0.0f; if (ImGui::GetTime() - t > 0.02f) { t = ImGui::GetTime(); AddLog("Spam %f", t); } //static float t = 0.0f; if (ImGui::GetTime() - t > 0.02f) { t = ImGui::GetTime(); AddLog("Spam %f", t); }
ImGui::Separator(); ImGui::Separator();
@ -3522,9 +3518,7 @@ struct ExampleAppConsole
// Options menu // Options menu
if (ImGui::BeginPopup("Options")) if (ImGui::BeginPopup("Options"))
{ {
if (ImGui::Checkbox("Auto-scroll", &AutoScroll)) ImGui::Checkbox("Auto-scroll", &AutoScroll);
if (AutoScroll)
ScrollToBottom = true;
ImGui::EndPopup(); ImGui::EndPopup();
} }
@ -3573,9 +3567,11 @@ struct ExampleAppConsole
} }
if (copy_to_clipboard) if (copy_to_clipboard)
ImGui::LogFinish(); ImGui::LogFinish();
if (ScrollToBottom)
if (ScrollToBottom || (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY()))
ImGui::SetScrollHereY(1.0f); ImGui::SetScrollHereY(1.0f);
ScrollToBottom = false; ScrollToBottom = false;
ImGui::PopStyleVar(); ImGui::PopStyleVar();
ImGui::EndChild(); ImGui::EndChild();
ImGui::Separator(); ImGui::Separator();
@ -3767,13 +3763,11 @@ struct ExampleAppLog
ImGuiTextBuffer Buf; ImGuiTextBuffer Buf;
ImGuiTextFilter Filter; ImGuiTextFilter Filter;
ImVector<int> LineOffsets; // Index to lines offset. We maintain this with AddLog() calls, allowing us to have a random access on lines ImVector<int> LineOffsets; // Index to lines offset. We maintain this with AddLog() calls, allowing us to have a random access on lines
bool AutoScroll; bool AutoScroll; // Keep scrolling if already at the bottom
bool ScrollToBottom;
ExampleAppLog() ExampleAppLog()
{ {
AutoScroll = true; AutoScroll = true;
ScrollToBottom = false;
Clear(); Clear();
} }
@ -3794,8 +3788,6 @@ struct ExampleAppLog
for (int new_size = Buf.size(); old_size < new_size; old_size++) for (int new_size = Buf.size(); old_size < new_size; old_size++)
if (Buf[old_size] == '\n') if (Buf[old_size] == '\n')
LineOffsets.push_back(old_size + 1); LineOffsets.push_back(old_size + 1);
if (AutoScroll)
ScrollToBottom = true;
} }
void Draw(const char* title, bool* p_open = NULL) void Draw(const char* title, bool* p_open = NULL)
@ -3809,9 +3801,7 @@ struct ExampleAppLog
// Options menu // Options menu
if (ImGui::BeginPopup("Options")) if (ImGui::BeginPopup("Options"))
{ {
if (ImGui::Checkbox("Auto-scroll", &AutoScroll)) ImGui::Checkbox("Auto-scroll", &AutoScroll);
if (AutoScroll)
ScrollToBottom = true;
ImGui::EndPopup(); ImGui::EndPopup();
} }
@ -3876,9 +3866,9 @@ struct ExampleAppLog
} }
ImGui::PopStyleVar(); ImGui::PopStyleVar();
if (ScrollToBottom) if (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
ImGui::SetScrollHereY(1.0f); ImGui::SetScrollHereY(1.0f);
ScrollToBottom = false;
ImGui::EndChild(); ImGui::EndChild();
ImGui::End(); ImGui::End();
} }

View File

@ -1460,8 +1460,8 @@ namespace ImGui
IMGUI_API ImVec2 CalcWindowExpectedSize(ImGuiWindow* window); IMGUI_API ImVec2 CalcWindowExpectedSize(ImGuiWindow* window);
IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent); IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent);
IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window); IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window);
IMGUI_API void SetWindowScrollX(ImGuiWindow* window, float new_scroll_x); IMGUI_API void SetScrollX(ImGuiWindow* window, float new_scroll_x);
IMGUI_API void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y); IMGUI_API void SetScrollY(ImGuiWindow* window, float new_scroll_y);
IMGUI_API ImRect GetWindowAllowedExtentRect(ImGuiWindow* window); IMGUI_API ImRect GetWindowAllowedExtentRect(ImGuiWindow* window);
IMGUI_API void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond = 0); IMGUI_API void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond = 0);
IMGUI_API void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond = 0); IMGUI_API void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond = 0);

View File

@ -3637,8 +3637,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_
if (IsKeyPressedMap(ImGuiKey_LeftArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); } if (IsKeyPressedMap(ImGuiKey_LeftArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); }
else if (IsKeyPressedMap(ImGuiKey_RightArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); } else if (IsKeyPressedMap(ImGuiKey_RightArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); }
else if (IsKeyPressedMap(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); } else if (IsKeyPressedMap(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); }
else if (IsKeyPressedMap(ImGuiKey_DownArrow) && is_multiline) { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); } else if (IsKeyPressedMap(ImGuiKey_DownArrow) && is_multiline) { if (io.KeyCtrl) SetScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); }
else if (IsKeyPressedMap(ImGuiKey_Home)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); } else if (IsKeyPressedMap(ImGuiKey_Home)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); }
else if (IsKeyPressedMap(ImGuiKey_End)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); } else if (IsKeyPressedMap(ImGuiKey_End)) { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); }
else if (IsKeyPressedMap(ImGuiKey_Delete) && !is_readonly) { state->OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); } else if (IsKeyPressedMap(ImGuiKey_Delete) && !is_readonly) { state->OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); }