ImGui console UX improvements

* Fixed click-through parent window.
* Keep selection position when lines get cleared.
* Keep cursor position when lines get cleared.
* Ensure cursor visibility when dragging cursor outside rect.
* Falter free scrolling in autocomplete window.
* Falter free scrolling in logging window (window no longer stutters when lines get cleared).
This commit is contained in:
Kawe Mazidjatari 2022-06-26 16:47:00 +02:00
parent 8867b3bd7b
commit efe71fbe24
6 changed files with 178 additions and 52 deletions

View File

@ -32,13 +32,14 @@ CConsole::CConsole(void)
m_nHistoryPos = -1;
m_bInitialized = false;
m_pszConsoleTitle = "Console";
m_pszConsoleLabel = "Console";
m_pszLoggingLabel = "LoggingRegion";
m_vCommands.push_back("CLEAR");
m_vCommands.push_back("HELP");
m_vCommands.push_back("HISTORY");
snprintf(m_szSummary, 256, "%zu history items", m_vHistory.size());
snprintf(m_szSummary, sizeof(m_szSummary), "%zu history items", m_vHistory.size());
std::thread think(&CConsole::Think, this);
think.detach();
@ -119,6 +120,11 @@ void CConsole::Draw(void)
ImGui::SetNextWindowPos(m_ivSuggestWindowPos);
ImGui::SetNextWindowSize(m_ivSuggestWindowSize);
if (m_bSuggestUpdate)
{
ImGui::SetNextWindowScroll(ImVec2(0.f, 0.f));
m_bSuggestUpdate = false;
}
ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(500, 37)); nVars++;
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f); nVars++;
@ -140,11 +146,15 @@ void CConsole::Think(void)
{
if (m_Logger.GetTotalLines() > con_max_size_logvector->GetInt())
{
while (m_Logger.GetTotalLines() > con_max_size_logvector->GetInt() / 4 * 3)
while (m_Logger.GetTotalLines() > con_max_size_logvector->GetInt())
{
m_Logger.RemoveLine(0);
m_nScrollBack++;
m_nSelectBack++;
}
m_Logger.MoveSelection(m_nSelectBack, false);
m_Logger.MoveCursor(m_nSelectBack, false);
m_nSelectBack = 0;
}
while (m_vHistory.size() > 512)
@ -174,7 +184,7 @@ void CConsole::Think(void)
//-----------------------------------------------------------------------------
void CConsole::BasePanel(void)
{
if (!ImGui::Begin(m_pszConsoleTitle, &m_bActivate))
if (!ImGui::Begin(m_pszConsoleLabel, &m_bActivate))
{
ImGui::End();
return;
@ -205,7 +215,18 @@ void CConsole::BasePanel(void)
ImGui::Separator();
///////////////////////////////////////////////////////////////////////
ImGui::BeginChild("ScrollingRegion", ImVec2(0, -flFooterHeightReserve), true, m_nLoggingFlags);
if (!m_Logger.m_bScrolledToMax && m_nScrollBack > 0)
{
ImGuiWindow* pWindow = ImGui::GetCurrentWindow();
ImGuiID nID = pWindow->GetID(m_pszLoggingLabel);
snprintf(m_szWindowLabel, sizeof(m_szWindowLabel), "%s/%s_%08X", m_pszConsoleLabel, m_pszLoggingLabel, nID);
ImGui::SetWindowScrollY(m_szWindowLabel, m_flScrollY - m_nScrollBack * fontSize.y);
}
m_nScrollBack = 0;
///////////////////////////////////////////////////////////////////////
ImGui::BeginChild(m_pszLoggingLabel, ImVec2(0, -flFooterHeightReserve), true, m_nLoggingFlags);
m_Logger.Render();
if (m_bCopyToClipBoard)
@ -214,12 +235,8 @@ void CConsole::BasePanel(void)
m_bCopyToClipBoard = false;
}
if (!m_Logger.m_bScrolledToMax && m_nScrollBack > 0)
{
ImGui::SetScrollY(ImGui::GetScrollY() - m_nScrollBack * fontSize.y);
m_nScrollBack = 0;
}
m_nScrollBack = 0;
m_flScrollX = ImGui::GetScrollX();
m_flScrollY = ImGui::GetScrollY();
///////////////////////////////////////////////////////////////////////
ImGui::EndChild();
@ -373,12 +390,6 @@ void CConsole::SuggestPanel(void)
ImGui::ScrollToRect(pWindow, imRect);
m_bSuggestMoved = false;
}
if (m_bSuggestUpdate)
{
ImGui::SetScrollHereY(0.f);
m_bSuggestUpdate = false;
}
}
ImGui::PopAllowKeyboardFocus();

View File

@ -28,13 +28,18 @@ class CConsole
private:
///////////////////////////////////////////////////////////////////////////
char m_szInputBuf[512] = { '\0' };
char m_szSummary[256] = { '\0' };
const char* m_pszConsoleTitle = nullptr;
char m_szSummary[512] = { '\0' };
char m_szWindowLabel[512] = { '\0' };
const char* m_pszConsoleLabel = nullptr;
const char* m_pszLoggingLabel = nullptr;
vector<string> m_vCommands;
vector<string> m_vHistory;
ssize_t m_nHistoryPos = -1;
int m_nScrollBack = 0;
int m_nSelectBack = 0;
float m_flScrollX = 0.f;
float m_flScrollY = 0.f;
float m_flFadeAlpha = 0.f;
bool m_bInitialized = false;
bool m_bModernTheme = false;

View File

@ -365,6 +365,8 @@ namespace ImGui
IMGUI_API void SetWindowFontScale(float scale); // [OBSOLETE] set font scale. Adjust IO.FontGlobalScale if you want to scale all windows. This is an old API! For correct scaling, prefer to reload font + rebuild ImFontAtlas + call style.ScaleAllSizes().
IMGUI_API void SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond = 0); // set named window position.
IMGUI_API void SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond = 0); // set named window size. set axis to 0.0f to force an auto-fit on this axis.
IMGUI_API void SetWindowScrollX(const char* name, float scroll_x); // set named window scroll x position.
IMGUI_API void SetWindowScrollY(const char* name, float scroll_y); // set named window scroll y position.
IMGUI_API void SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond = 0); // set named window collapsed state
IMGUI_API void SetWindowFocus(const char* name); // set named window to be focused / top-most. use NULL to remove focus.

View File

@ -131,8 +131,8 @@ public:
Coordinates GetCursorPosition() const { return GetActualCursorCoordinates(); }
void MoveCursor(int aLines, bool aForward = true);
void SetCursorPosition(const Coordinates& aPosition);
bool IsCursorPositionChanged() const { return m_bCursorPositionChanged; }
inline void SetHandleMouseInputs (bool aValue){ m_bHandleMouseInputs = aValue;}
inline bool IsHandleMouseInputsEnabled() const { return m_bHandleKeyboardInputs; }
@ -163,12 +163,13 @@ public:
void SelectWordUnderCursor();
void SelectAll();
bool HasSelection() const;
void MoveSelection(int aLines, bool aForward = true);
void RemoveLine(int aStart, int aEnd, bool aInternal = false);
void RemoveLine(int aIndex, bool aInternal = false);
private:
struct EditorState
struct LoggerState_t
{
Coordinates m_SelectionStart;
Coordinates m_SelectionEnd;
@ -205,13 +206,13 @@ private:
public:
bool m_bAutoScroll;
bool m_bScrollToBottom;
bool m_bScrollToCursor;
bool m_bScrolledToMax;
private:
bool m_bHandleKeyboardInputs;
bool m_bHandleMouseInputs;
bool m_bWithinLoggingRect;
bool m_bShowWhiteSpaces;
bool m_bScrollToCursor;
bool m_bCursorPositionChanged;
float m_flTextStart; // position (in pixels) where a code line starts relative to the left of the TextLogger.
float m_flLineSpacing;
double m_flLastClick;
@ -227,7 +228,7 @@ private:
ImVec2 m_CharAdvance;
Lines m_Lines;
EditorState m_State;
LoggerState_t m_State;
std::mutex m_Mutex;
std::string m_svLineBuffer;
public:

View File

@ -7148,6 +7148,26 @@ void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)
SetWindowSize(window, size, cond);
}
void ImGui::SetWindowScrollX(const char* name, float scroll_x)
{
if (ImGuiWindow* window = FindWindowByName(name))
{
window->DC.CursorMaxPos.x += window->Scroll.x;
window->Scroll.x = scroll_x;
window->DC.CursorMaxPos.x -= window->Scroll.x;
}
}
void ImGui::SetWindowScrollY(const char* name, float scroll_y)
{
if (ImGuiWindow* window = FindWindowByName(name))
{
window->DC.CursorMaxPos.y += window->Scroll.y;
window->Scroll.y = scroll_y;
window->DC.CursorMaxPos.y -= window->Scroll.y;
}
}
void ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)
{
// Test condition (NB: bit 0 is always true) and clear flags for next time

View File

@ -23,23 +23,23 @@ bool equals(InputIt1 first1, InputIt1 last1,
}
CTextLogger::CTextLogger()
: m_flLineSpacing(1.0f)
, m_nTabSize(4)
, m_bAutoScroll(true)
: m_bAutoScroll(true)
, m_bScrollToBottom(true)
, m_bScrollToCursor(false)
, m_bScrolledToMax(false)
, m_flTextStart(0.0f)
, m_nLeftMargin(0)
, m_bCursorPositionChanged(false)
, m_nColorRangeMin(0)
, m_nColorRangeMax(0)
, m_SelectionMode(SelectionMode::Normal)
, m_flLastClick(-1.0)
, m_bHandleKeyboardInputs(true)
, m_bHandleMouseInputs(true)
, m_bWithinLoggingRect(false)
, m_bShowWhiteSpaces(false)
, m_flTextStart(0.0f)
, m_flLineSpacing(1.0f)
, m_flLastClick(-1.0)
, m_nTabSize(4)
, m_nLeftMargin(0)
, m_nColorRangeMin(0)
, m_nColorRangeMax(0)
, m_nStartTime(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count())
, m_SelectionMode(SelectionMode::Normal)
{
m_Lines.push_back(Line());
}
@ -609,7 +609,17 @@ void CTextLogger::HandleMouseInputs(bool bHoveredScrollbar, bool bActiveScrollba
bool bCtrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl;
bool bAlt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt;
if (!bHoveredScrollbar && !bActiveScrollbar && ImGui::IsWindowFocused())
if (ImGui::IsMouseClicked(0) && ImGui::IsWindowHovered())
{
m_bWithinLoggingRect = true;
}
if (ImGui::IsMouseReleased(0))
{
m_bWithinLoggingRect = false;
}
if (!bHoveredScrollbar && !bActiveScrollbar && m_bWithinLoggingRect)
{
if (!bShift && !bAlt)
{
@ -675,6 +685,7 @@ void CTextLogger::HandleMouseInputs(bool bHoveredScrollbar, bool bActiveScrollba
io.WantCaptureMouse = true;
m_State.m_CursorPosition = m_InteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos());
SetSelection(m_InteractiveStart, m_InteractiveEnd, m_SelectionMode);
EnsureCursorVisible();
}
}
}
@ -683,7 +694,6 @@ void CTextLogger::HandleMouseInputs(bool bHoveredScrollbar, bool bActiveScrollba
void CTextLogger::Render()
{
m_Mutex.lock();
m_bCursorPositionChanged = false;
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f));
@ -947,12 +957,41 @@ void CTextLogger::SetTextLines(const std::vector<CConLog>& aLines)
}
}
void CTextLogger::MoveCursor(int aLines, bool aForward)
{
Coordinates newStart;
if (aForward)
{
newStart = m_State.m_CursorPosition;
newStart.m_nLine += aLines;
if (newStart.m_nLine >= static_cast<int>(m_Lines.size()))
{
newStart.m_nLine = static_cast<int>(m_Lines.size()) - 1;
newStart.m_nColumn = GetLineMaxColumn(newStart.m_nLine);
}
}
else
{
newStart = m_State.m_CursorPosition;
newStart.m_nLine -= aLines;
if (newStart.m_nLine < 0)
{
newStart.m_nLine = 0;
newStart.m_nColumn = 0;
}
}
m_State.m_CursorPosition = newStart;
}
void CTextLogger::SetCursorPosition(const Coordinates & aPosition)
{
if (m_State.m_CursorPosition != aPosition)
{
m_State.m_CursorPosition = aPosition;
m_bCursorPositionChanged = true;
EnsureCursorVisible();
}
}
@ -1003,10 +1042,6 @@ void CTextLogger::SetSelection(const Coordinates & aStart, const Coordinates & a
default:
break;
}
if (m_State.m_SelectionStart != oldSelStart ||
m_State.m_SelectionEnd != oldSelEnd)
m_bCursorPositionChanged = true;
}
void CTextLogger::SetTabSize(int aValue)
@ -1309,6 +1344,60 @@ bool CTextLogger::HasSelection() const
return m_State.m_SelectionEnd > m_State.m_SelectionStart;
}
void CTextLogger::MoveSelection(int aLines, bool aForward)
{
assert(aLines > 0);
if (aLines < 1)
return;
if (HasSelection())
{
Coordinates newStart;
Coordinates newEnd;
if (aForward)
{
newStart = m_State.m_SelectionStart;
newStart.m_nLine += aLines;
newEnd = m_State.m_SelectionEnd;
newEnd.m_nLine += aLines;
if (newStart.m_nLine >= static_cast<int>(m_Lines.size()))
{
newStart.m_nLine = static_cast<int>(m_Lines.size()) - 1;
newStart.m_nColumn = GetLineMaxColumn(newStart.m_nLine);
}
if (newEnd.m_nLine >= static_cast<int>(m_Lines.size()))
{
newEnd.m_nLine = static_cast<int>(m_Lines.size()) - 1;
newEnd.m_nColumn = GetLineMaxColumn(newStart.m_nLine);
}
}
else
{
newStart = m_State.m_SelectionStart;
newStart.m_nLine -= aLines;
newEnd = m_State.m_SelectionEnd;
newEnd.m_nLine -= aLines;
if (newStart.m_nLine < 0)
{
newStart.m_nLine = 0;
newStart.m_nColumn = 0;
}
if (newEnd.m_nLine < 0)
{
newEnd.m_nLine = 0;
newEnd.m_nColumn = 0;
}
}
SetSelectionStart(newStart);
SetSelectionEnd(newEnd);
}
}
std::string CTextLogger::GetText() const
{
return GetText(Coordinates(), Coordinates(static_cast<int>(m_Lines.size()), 0));
@ -1408,12 +1497,13 @@ float CTextLogger::TextDistanceToLineStart(const Coordinates& aFrom) const
void CTextLogger::EnsureCursorVisible()
{
m_bScrollToCursor = true;
Coordinates pos = GetActualCursorCoordinates();
float scrollX = ImGui::GetScrollX();
float scrollY = ImGui::GetScrollY();
float height = ImGui::GetWindowHeight();
float width = ImGui::GetWindowWidth();
float height = ImGui::GetWindowHeight();
int top = 1 + static_cast<int>(ceil(scrollY / m_CharAdvance.y));
int bottom = static_cast<int>(ceil((scrollY + height) / m_CharAdvance.y));
@ -1421,17 +1511,14 @@ void CTextLogger::EnsureCursorVisible()
int left = static_cast<int>(ceil(scrollX / m_CharAdvance.x));
int right = static_cast<int>(ceil((scrollX + width) / m_CharAdvance.x));
Coordinates pos = GetActualCursorCoordinates();
float len = TextDistanceToLineStart(pos);
if (pos.m_nColumn < left)
ImGui::SetScrollX(std::max(0.0f, (pos.m_nColumn) * m_CharAdvance.x));
if (pos.m_nColumn > right - 3)
ImGui::SetScrollX(std::max(0.0f, (pos.m_nColumn + 3) * m_CharAdvance.x - width));
if (pos.m_nLine < top)
ImGui::SetScrollY(std::max(0.0f, (pos.m_nLine - 1) * m_CharAdvance.y));
if (pos.m_nLine > bottom - 4)
ImGui::SetScrollY(std::max(0.0f, (pos.m_nLine + 4) * m_CharAdvance.y - height));
if (len + m_flTextStart < left + 4)
ImGui::SetScrollX(std::max(0.0f, len + m_flTextStart - 4));
if (len + m_flTextStart > right - 4)
ImGui::SetScrollX(std::max(0.0f, len + m_flTextStart + 4 - width));
ImGui::SetScrollY(std::max(0.0f, (pos.m_nLine) * m_CharAdvance.y));
if (pos.m_nLine > bottom - 2)
ImGui::SetScrollY(std::max(0.0f, (pos.m_nLine + 2) * m_CharAdvance.y - height));
}
int CTextLogger::GetPageSize() const