2022-06-20 10:14:43 +02:00
|
|
|
#include <algorithm>
|
|
|
|
#include <chrono>
|
|
|
|
#include <string>
|
|
|
|
#include <regex>
|
|
|
|
#include <cmath>
|
|
|
|
|
2023-03-29 00:19:19 +02:00
|
|
|
#include "imgui_logger.h"
|
|
|
|
#include "imgui.h"
|
|
|
|
#include "imgui_internal.h"
|
2022-06-20 10:14:43 +02:00
|
|
|
|
|
|
|
template<class InputIt1, class InputIt2, class BinaryPredicate>
|
2024-02-28 19:16:31 +01:00
|
|
|
static bool equals(InputIt1 first1, InputIt1 last1,
|
2022-06-20 10:14:43 +02:00
|
|
|
InputIt2 first2, InputIt2 last2, BinaryPredicate p)
|
|
|
|
{
|
|
|
|
for (; first1 != last1 && first2 != last2; ++first1, ++first2)
|
|
|
|
{
|
|
|
|
if (!p(*first1, *first2))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return first1 == last1 && first2 == last2;
|
|
|
|
}
|
|
|
|
|
|
|
|
CTextLogger::CTextLogger()
|
2022-06-26 16:47:00 +02:00
|
|
|
: m_bAutoScroll(true)
|
2022-06-20 10:14:43 +02:00
|
|
|
, m_bScrollToCursor(false)
|
2024-02-29 01:32:45 +01:00
|
|
|
, m_bScrollToStart(false)
|
|
|
|
, m_bScrolledToStart(false)
|
2023-04-09 22:35:55 +02:00
|
|
|
, m_bScrollToBottom(true)
|
|
|
|
, m_bScrolledToBottom(false)
|
2023-04-09 19:53:33 +02:00
|
|
|
, m_bHandleUserInputs(true)
|
2022-06-26 16:47:00 +02:00
|
|
|
, m_bWithinLoggingRect(false)
|
2022-08-30 22:54:34 +02:00
|
|
|
, m_bLinesOffsetForward(false)
|
|
|
|
, m_nLinesOffsetAmount(0)
|
2022-06-26 16:47:00 +02:00
|
|
|
, m_nTabSize(4)
|
2022-06-20 10:17:11 +02:00
|
|
|
, m_nLeftMargin(0)
|
2022-08-30 22:54:34 +02:00
|
|
|
, m_flLineSpacing(1.0f)
|
2023-04-09 22:35:55 +02:00
|
|
|
, m_SelectionMode(SelectionMode::Normal)
|
2022-08-30 22:54:34 +02:00
|
|
|
, m_flLastClick(-1.0)
|
2022-06-20 10:14:43 +02:00
|
|
|
, m_nStartTime(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count())
|
|
|
|
{
|
|
|
|
m_Lines.push_back(Line());
|
|
|
|
}
|
|
|
|
|
|
|
|
CTextLogger::~CTextLogger()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
std::string CTextLogger::GetText(const Coordinates& aStart, const Coordinates& aEnd) const
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
|
|
|
std::string result;
|
|
|
|
|
2022-06-21 11:13:30 +02:00
|
|
|
int lstart = aStart.m_nLine;
|
|
|
|
int lend = aEnd.m_nLine;
|
|
|
|
int istart = GetCharacterIndex(aStart);
|
|
|
|
int iend = GetCharacterIndex(aEnd);
|
2022-06-20 10:14:43 +02:00
|
|
|
size_t s = 0;
|
|
|
|
|
2022-06-30 11:29:36 +02:00
|
|
|
for (int i = lstart; i < lend; i++)
|
2024-02-28 19:16:31 +01:00
|
|
|
s += m_Lines[i].Length();
|
2022-06-20 10:14:43 +02:00
|
|
|
|
|
|
|
result.reserve(s + s / 8);
|
|
|
|
|
|
|
|
while (istart < iend || lstart < lend)
|
|
|
|
{
|
2022-06-21 11:13:30 +02:00
|
|
|
if (lstart >= static_cast<int>(m_Lines.size()))
|
2022-06-20 10:14:43 +02:00
|
|
|
break;
|
|
|
|
|
2022-06-21 11:13:30 +02:00
|
|
|
const Line& line = m_Lines[lstart];
|
2024-02-28 19:16:31 +01:00
|
|
|
if (istart < line.Length())
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
2024-02-28 19:16:31 +01:00
|
|
|
result += line.buffer[istart];
|
2022-06-20 10:14:43 +02:00
|
|
|
istart++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
istart = 0;
|
|
|
|
++lstart;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-06-20 10:17:11 +02:00
|
|
|
CTextLogger::Coordinates CTextLogger::GetActualLastLineCoordinates() const
|
|
|
|
{
|
|
|
|
return SanitizeCoordinates(Coordinates(GetTotalLines(), 0));
|
|
|
|
}
|
|
|
|
|
2022-06-20 10:14:43 +02:00
|
|
|
CTextLogger::Coordinates CTextLogger::GetActualCursorCoordinates() const
|
|
|
|
{
|
|
|
|
return SanitizeCoordinates(m_State.m_CursorPosition);
|
|
|
|
}
|
|
|
|
|
2022-06-20 10:17:11 +02:00
|
|
|
CTextLogger::Coordinates CTextLogger::SanitizeCoordinates(const Coordinates & aValue) const
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
2022-06-21 11:13:30 +02:00
|
|
|
int line = aValue.m_nLine;
|
|
|
|
int column = aValue.m_nColumn;
|
|
|
|
if (line >= static_cast<int>(m_Lines.size()))
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
|
|
|
if (m_Lines.empty())
|
|
|
|
{
|
|
|
|
line = 0;
|
|
|
|
column = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-06-21 11:13:30 +02:00
|
|
|
line = static_cast<int>(m_Lines.size() - 1);
|
2022-06-20 10:14:43 +02:00
|
|
|
column = GetLineMaxColumn(line);
|
|
|
|
}
|
|
|
|
return Coordinates(line, column);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2024-02-28 19:16:31 +01:00
|
|
|
column = m_Lines.empty() ? 0 : ImMin(column, GetLineMaxColumn(line));
|
2022-06-20 10:14:43 +02:00
|
|
|
return Coordinates(line, column);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// https://en.wikipedia.org/wiki/UTF-8
|
|
|
|
// We assume that the char is a standalone character (<128) or a leading byte of an UTF-8 code sequence (non-10xxxxxx code)
|
|
|
|
static int UTF8CharLength(CTextLogger::Char c)
|
|
|
|
{
|
|
|
|
if ((c & 0xFE) == 0xFC)
|
|
|
|
return 6;
|
|
|
|
if ((c & 0xFC) == 0xF8)
|
|
|
|
return 5;
|
|
|
|
if ((c & 0xF8) == 0xF0)
|
|
|
|
return 4;
|
|
|
|
else if ((c & 0xF0) == 0xE0)
|
|
|
|
return 3;
|
|
|
|
else if ((c & 0xE0) == 0xC0)
|
|
|
|
return 2;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2023-04-09 19:36:30 +02:00
|
|
|
bool UTF8StringValid(const char* pszString)
|
|
|
|
{
|
2023-04-09 22:35:55 +02:00
|
|
|
size_t byteCount = 0;
|
|
|
|
CTextLogger::Char currentByte;
|
2023-04-09 19:36:30 +02:00
|
|
|
|
|
|
|
while (*pszString)
|
|
|
|
{
|
2023-04-09 22:35:55 +02:00
|
|
|
currentByte = static_cast<CTextLogger::Char>(*pszString);
|
2023-04-09 19:36:30 +02:00
|
|
|
if (byteCount)
|
|
|
|
{
|
|
|
|
if ((currentByte & 0xC0) != 0x80)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
byteCount--;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
byteCount = UTF8CharLength(currentByte) - 1;
|
|
|
|
if (byteCount > 0 && (currentByte & (1 << (7 - byteCount))) == 0)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
pszString++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return byteCount == 0;
|
|
|
|
}
|
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
void CTextLogger::Advance(Coordinates& aCoordinates) const
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
2022-06-22 00:33:12 +02:00
|
|
|
if (aCoordinates.m_nLine < static_cast<int>(m_Lines.size()))
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
2022-06-21 11:13:30 +02:00
|
|
|
const Line& line = m_Lines[aCoordinates.m_nLine];
|
|
|
|
int cindex = GetCharacterIndex(aCoordinates);
|
2022-06-20 10:14:43 +02:00
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
if (cindex + 1 < line.Length())
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
2024-02-28 19:16:31 +01:00
|
|
|
int delta = UTF8CharLength(line.buffer[cindex]);
|
|
|
|
cindex = ImMin(cindex + delta, static_cast<int>(line.Length() - 1));
|
2022-06-20 10:14:43 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
++aCoordinates.m_nLine;
|
|
|
|
cindex = 0;
|
|
|
|
}
|
|
|
|
aCoordinates.m_nColumn = GetCharacterColumn(aCoordinates.m_nLine, cindex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
//void CTextLogger::DeleteRange(const Coordinates & aStart, const Coordinates & aEnd)
|
|
|
|
//{
|
|
|
|
// assert(aEnd >= aStart);
|
|
|
|
//
|
|
|
|
// //printf("D(%d.%d)-(%d.%d)\n", aStart.mLine, aStart.mColumn, aEnd.mLine, aEnd.mColumn);
|
|
|
|
//
|
|
|
|
// if (aEnd == aStart)
|
|
|
|
// return;
|
|
|
|
//
|
|
|
|
// int start = GetCharacterIndex(aStart);
|
|
|
|
// int end = GetCharacterIndex(aEnd);
|
|
|
|
//
|
|
|
|
// if (aStart.m_nLine == aEnd.m_nLine)
|
|
|
|
// {
|
|
|
|
// Line& line = m_Lines[aStart.m_nLine];
|
|
|
|
// int n = GetLineMaxColumn(aStart.m_nLine);
|
|
|
|
// if (aEnd.m_nColumn >= n)
|
|
|
|
// line.erase(line.begin() + start, line.end());
|
|
|
|
// else
|
|
|
|
// line.erase(line.begin() + start, line.begin() + end);
|
|
|
|
// }
|
|
|
|
// else
|
|
|
|
// {
|
|
|
|
// Line& firstLine = m_Lines[aStart.m_nLine];
|
|
|
|
// Line& lastLine = m_Lines[aEnd.m_nLine];
|
|
|
|
//
|
|
|
|
// firstLine.erase(firstLine.begin() + start, firstLine.end());
|
|
|
|
// lastLine.erase(lastLine.begin(), lastLine.begin() + end);
|
|
|
|
//
|
|
|
|
// if (aStart.m_nLine < aEnd.m_nLine)
|
|
|
|
// firstLine.insert(firstLine.end(), lastLine.begin(), lastLine.end());
|
|
|
|
//
|
|
|
|
// if (aStart.m_nLine < aEnd.m_nLine)
|
|
|
|
// RemoveLine(aStart.m_nLine + 1, aEnd.m_nLine + 1);
|
|
|
|
// }
|
|
|
|
//}
|
|
|
|
|
|
|
|
void CTextLogger::MarkNewline(Coordinates& aWhere, int aIndex)
|
2022-09-19 20:21:18 +02:00
|
|
|
{
|
2023-04-09 16:15:08 +02:00
|
|
|
Line& newLine = InsertLine(aWhere.m_nLine + 1);
|
|
|
|
Line& line = m_Lines[aWhere.m_nLine];
|
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
if (aIndex < static_cast<int>(m_Lines[aWhere.m_nLine].buffer.size()))
|
2022-09-19 20:21:18 +02:00
|
|
|
{
|
2024-02-28 19:16:31 +01:00
|
|
|
newLine.buffer.insert(newLine.buffer.begin(), line.buffer.begin() + aIndex, line.buffer.end());
|
|
|
|
line.buffer.erase(line.buffer.begin() + aIndex, line.buffer.end());
|
2022-09-19 20:21:18 +02:00
|
|
|
}
|
|
|
|
else
|
2024-02-28 19:16:31 +01:00
|
|
|
line.buffer.push_back('\n');
|
2023-04-09 19:36:30 +02:00
|
|
|
|
|
|
|
++aWhere.m_nLine;
|
|
|
|
aWhere.m_nColumn = 0;
|
2022-09-19 20:21:18 +02:00
|
|
|
}
|
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
int CTextLogger::InsertTextAt(Coordinates& aWhere, const char* aValue, const ImU32 aColor)
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
|
|
|
int cindex = GetCharacterIndex(aWhere);
|
|
|
|
int totalLines = 0;
|
2022-06-20 10:17:11 +02:00
|
|
|
|
2023-04-09 19:36:30 +02:00
|
|
|
if (!UTF8StringValid(aValue))
|
|
|
|
{
|
|
|
|
assert(0);
|
|
|
|
aValue = "Invalid UTF-8 string\n";
|
|
|
|
}
|
|
|
|
|
2022-06-20 10:14:43 +02:00
|
|
|
while (*aValue != '\0')
|
|
|
|
{
|
|
|
|
assert(!m_Lines.empty());
|
|
|
|
|
|
|
|
if (*aValue == '\r')
|
|
|
|
{
|
|
|
|
// skip
|
|
|
|
++aValue;
|
|
|
|
}
|
|
|
|
else if (*aValue == '\n')
|
|
|
|
{
|
2024-02-28 19:16:31 +01:00
|
|
|
MarkNewline(aWhere, cindex);
|
2022-06-20 10:14:43 +02:00
|
|
|
cindex = 0;
|
|
|
|
++totalLines;
|
|
|
|
++aValue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-06-21 11:13:30 +02:00
|
|
|
Line& line = m_Lines[aWhere.m_nLine];
|
2024-02-28 19:16:31 +01:00
|
|
|
|
|
|
|
// if the buffer isn't empty, and the next log isn't from the same context, log it as a new line
|
|
|
|
if (!line.buffer.empty() && aColor != line.color)
|
2022-09-19 20:21:18 +02:00
|
|
|
{
|
2024-02-28 19:16:31 +01:00
|
|
|
MarkNewline(aWhere, cindex);
|
2022-09-19 20:21:18 +02:00
|
|
|
cindex = 0;
|
|
|
|
++totalLines;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2023-04-09 22:35:55 +02:00
|
|
|
size_t d = UTF8CharLength(*aValue);
|
2022-06-20 11:01:53 +02:00
|
|
|
while (d-- > 0 && *aValue != '\0')
|
2023-04-09 16:15:08 +02:00
|
|
|
{
|
2024-02-28 19:16:31 +01:00
|
|
|
if (cindex >= 0 && cindex <= line.Length())
|
|
|
|
line.buffer.insert(line.buffer.begin() + cindex++, *aValue++);
|
2023-04-09 16:15:08 +02:00
|
|
|
else
|
|
|
|
++aValue; // Possibly an invalid character
|
|
|
|
}
|
2024-02-28 19:16:31 +01:00
|
|
|
|
|
|
|
line.color = aColor;
|
2022-06-20 10:14:43 +02:00
|
|
|
++aWhere.m_nColumn;
|
|
|
|
}
|
|
|
|
}
|
2024-02-28 19:16:31 +01:00
|
|
|
|
2022-09-19 20:21:18 +02:00
|
|
|
if (!*aValue)
|
|
|
|
{
|
|
|
|
Line& line = m_Lines[aWhere.m_nLine];
|
2024-02-28 19:16:31 +01:00
|
|
|
if (!line.buffer.empty() && cindex >= 0 && cindex <= line.Length())
|
|
|
|
line.buffer.insert(line.buffer.begin() + cindex, ' ');
|
2022-09-19 20:21:18 +02:00
|
|
|
}
|
2022-06-20 10:14:43 +02:00
|
|
|
|
|
|
|
return totalLines;
|
|
|
|
}
|
|
|
|
|
|
|
|
CTextLogger::Coordinates CTextLogger::ScreenPosToCoordinates(const ImVec2& aPosition) const
|
|
|
|
{
|
|
|
|
ImVec2 origin = ImGui::GetCursorScreenPos();
|
|
|
|
ImVec2 local(aPosition.x - origin.x, aPosition.y - origin.y);
|
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
int lineNo = ImMax(0, static_cast<int>(ImFloor(local.y / m_CharAdvance.y)));
|
2022-06-20 10:14:43 +02:00
|
|
|
int columnCoord = 0;
|
|
|
|
|
2022-06-22 00:33:12 +02:00
|
|
|
if (lineNo >= 0 && lineNo < static_cast<int>(m_Lines.size()))
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
2024-02-28 19:16:31 +01:00
|
|
|
const Line& line = m_Lines[lineNo];
|
2022-06-20 10:14:43 +02:00
|
|
|
|
|
|
|
int columnIndex = 0;
|
|
|
|
float columnX = 0.0f;
|
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
while (columnIndex < line.Length())
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
|
|
|
float columnWidth = 0.0f;
|
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
if (line.buffer[columnIndex] == '\t')
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
|
|
|
float spaceSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ").x;
|
|
|
|
float oldX = columnX;
|
2024-02-28 19:16:31 +01:00
|
|
|
float newColumnX = (1.0f + ImFloor((1.0f + columnX) / (float(m_nTabSize) * spaceSize))) * (float(m_nTabSize) * spaceSize);
|
2022-06-20 10:14:43 +02:00
|
|
|
columnWidth = newColumnX - oldX;
|
2024-02-28 19:16:31 +01:00
|
|
|
|
|
|
|
if (columnX + columnWidth * 0.5f > local.x)
|
2022-06-20 10:14:43 +02:00
|
|
|
break;
|
2024-02-28 19:16:31 +01:00
|
|
|
|
2022-06-20 10:14:43 +02:00
|
|
|
columnX = newColumnX;
|
|
|
|
columnCoord = (columnCoord / m_nTabSize) * m_nTabSize + m_nTabSize;
|
|
|
|
columnIndex++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char buf[7];
|
2024-02-28 19:16:31 +01:00
|
|
|
size_t d = UTF8CharLength(line.buffer[columnIndex]);
|
2023-04-09 22:35:55 +02:00
|
|
|
size_t i = 0;
|
2024-02-28 19:16:31 +01:00
|
|
|
|
|
|
|
while (i < 6 && d-- > 0 && columnIndex < line.Length())
|
|
|
|
buf[i++] = line.buffer[columnIndex++];
|
|
|
|
|
2022-06-20 10:14:43 +02:00
|
|
|
buf[i] = '\0';
|
|
|
|
columnWidth = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf).x;
|
2024-02-28 19:16:31 +01:00
|
|
|
|
|
|
|
if (columnX + columnWidth * 0.5f > local.x)
|
2022-06-20 10:14:43 +02:00
|
|
|
break;
|
2024-02-28 19:16:31 +01:00
|
|
|
|
2022-06-20 10:14:43 +02:00
|
|
|
columnX += columnWidth;
|
|
|
|
columnCoord++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return SanitizeCoordinates(Coordinates(lineNo, columnCoord));
|
|
|
|
}
|
|
|
|
|
2022-06-20 10:17:11 +02:00
|
|
|
CTextLogger::Coordinates CTextLogger::FindWordStart(const Coordinates & aFrom) const
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
|
|
|
Coordinates at = aFrom;
|
2022-06-22 00:33:12 +02:00
|
|
|
if (at.m_nLine >= static_cast<int>(m_Lines.size()))
|
2022-06-20 10:14:43 +02:00
|
|
|
return at;
|
|
|
|
|
2022-06-21 11:13:30 +02:00
|
|
|
const Line& line = m_Lines[at.m_nLine];
|
|
|
|
int cindex = GetCharacterIndex(at);
|
2022-06-20 10:14:43 +02:00
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
if (cindex >= line.Length())
|
2022-06-20 10:14:43 +02:00
|
|
|
return at;
|
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
while (cindex > 0 && isspace(line.buffer[cindex]))
|
2022-06-20 10:14:43 +02:00
|
|
|
--cindex;
|
|
|
|
|
|
|
|
while (cindex > 0)
|
|
|
|
{
|
2024-02-28 19:16:31 +01:00
|
|
|
Char c = line.buffer[cindex];
|
2022-06-20 10:14:43 +02:00
|
|
|
if ((c & 0xC0) != 0x80) // not UTF code sequence 10xxxxxx
|
|
|
|
{
|
|
|
|
if (c <= 32 && isspace(c))
|
|
|
|
{
|
|
|
|
cindex++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
--cindex;
|
|
|
|
}
|
2022-06-20 10:17:11 +02:00
|
|
|
|
2022-06-20 10:14:43 +02:00
|
|
|
return Coordinates(at.m_nLine, GetCharacterColumn(at.m_nLine, cindex));
|
|
|
|
}
|
|
|
|
|
2022-06-20 10:17:11 +02:00
|
|
|
CTextLogger::Coordinates CTextLogger::FindWordEnd(const Coordinates & aFrom) const
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
|
|
|
Coordinates at = aFrom;
|
2022-06-22 00:33:12 +02:00
|
|
|
if (at.m_nLine >= static_cast<int>(m_Lines.size()))
|
2022-06-20 10:14:43 +02:00
|
|
|
return at;
|
|
|
|
|
2022-06-21 11:13:30 +02:00
|
|
|
const Line& line = m_Lines[at.m_nLine];
|
|
|
|
int cindex = GetCharacterIndex(at);
|
2022-06-20 10:14:43 +02:00
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
if (cindex >= line.Length())
|
2022-06-20 10:14:43 +02:00
|
|
|
return at;
|
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
bool prevspace = static_cast<bool>(isspace(line.buffer[cindex]));
|
|
|
|
while (cindex < line.Length())
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
2024-02-28 19:16:31 +01:00
|
|
|
Char c = line.buffer[cindex];
|
2022-06-21 11:13:30 +02:00
|
|
|
int d = UTF8CharLength(c);
|
2022-06-20 10:14:43 +02:00
|
|
|
|
|
|
|
if (prevspace != !!isspace(c))
|
|
|
|
{
|
|
|
|
if (isspace(c))
|
2024-02-28 19:16:31 +01:00
|
|
|
while (cindex < line.Length() && !isspace(line.buffer[cindex]))
|
2022-06-20 10:14:43 +02:00
|
|
|
++cindex;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
cindex += d;
|
|
|
|
}
|
2022-06-20 10:17:11 +02:00
|
|
|
|
2022-06-20 10:14:43 +02:00
|
|
|
return Coordinates(aFrom.m_nLine, GetCharacterColumn(aFrom.m_nLine, cindex));
|
|
|
|
}
|
|
|
|
|
2022-06-20 10:17:11 +02:00
|
|
|
CTextLogger::Coordinates CTextLogger::FindNextWord(const Coordinates & aFrom) const
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
|
|
|
Coordinates at = aFrom;
|
2022-06-22 00:33:12 +02:00
|
|
|
if (at.m_nLine >= static_cast<int>(m_Lines.size()))
|
2022-06-20 10:14:43 +02:00
|
|
|
return at;
|
|
|
|
|
|
|
|
// skip to the next non-word character
|
2022-06-21 11:13:30 +02:00
|
|
|
int cindex = GetCharacterIndex(aFrom);
|
2022-06-20 10:14:43 +02:00
|
|
|
bool isword = false;
|
|
|
|
bool skip = false;
|
2024-02-28 19:16:31 +01:00
|
|
|
|
|
|
|
if (cindex < static_cast<int>(m_Lines[at.m_nLine].buffer.size()))
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
2022-06-21 11:13:30 +02:00
|
|
|
const Line& line = m_Lines[at.m_nLine];
|
2024-02-28 19:16:31 +01:00
|
|
|
isword = isalnum(line.buffer[cindex]);
|
2022-06-20 10:14:43 +02:00
|
|
|
skip = isword;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (!isword || skip)
|
|
|
|
{
|
2022-08-09 12:13:31 +02:00
|
|
|
if (at.m_nLine >= static_cast<int>(m_Lines.size()))
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
2024-02-28 19:16:31 +01:00
|
|
|
int l = ImMax(0, static_cast<int>(m_Lines.size() - 1));
|
2022-06-20 10:14:43 +02:00
|
|
|
return Coordinates(l, GetLineMaxColumn(l));
|
|
|
|
}
|
|
|
|
|
2022-06-21 11:13:30 +02:00
|
|
|
const Line& line = m_Lines[at.m_nLine];
|
2024-02-28 19:16:31 +01:00
|
|
|
if (cindex < line.Length())
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
2024-02-28 19:16:31 +01:00
|
|
|
isword = isalnum(line.buffer[cindex]);
|
2022-06-20 10:14:43 +02:00
|
|
|
|
|
|
|
if (isword && !skip)
|
|
|
|
return Coordinates(at.m_nLine, GetCharacterColumn(at.m_nLine, cindex));
|
|
|
|
|
|
|
|
if (!isword)
|
|
|
|
skip = false;
|
|
|
|
|
|
|
|
cindex++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cindex = 0;
|
|
|
|
++at.m_nLine;
|
|
|
|
skip = false;
|
|
|
|
isword = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return at;
|
|
|
|
}
|
|
|
|
|
|
|
|
int CTextLogger::GetCharacterIndex(const Coordinates& aCoordinates) const
|
|
|
|
{
|
2022-08-09 12:13:31 +02:00
|
|
|
if (aCoordinates.m_nLine >= static_cast<int>(m_Lines.size()))
|
2022-06-20 10:14:43 +02:00
|
|
|
return -1;
|
2022-06-21 11:13:30 +02:00
|
|
|
|
|
|
|
const Line& line = m_Lines[aCoordinates.m_nLine];
|
2022-06-20 10:14:43 +02:00
|
|
|
int c = 0;
|
|
|
|
int i = 0;
|
2022-06-21 11:13:30 +02:00
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
for (; i < line.Length() && c < aCoordinates.m_nColumn;)
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
2024-02-28 19:16:31 +01:00
|
|
|
if (line.buffer[i] == '\t')
|
2022-06-20 10:14:43 +02:00
|
|
|
c = (c / m_nTabSize) * m_nTabSize + m_nTabSize;
|
|
|
|
else
|
|
|
|
++c;
|
2024-02-28 19:16:31 +01:00
|
|
|
i += UTF8CharLength(line.buffer[i]);
|
2022-06-20 10:14:43 +02:00
|
|
|
}
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
int CTextLogger::GetCharacterColumn(int aLine, int aIndex) const
|
|
|
|
{
|
2022-06-21 10:14:20 +02:00
|
|
|
if (aLine >= static_cast<int>(m_Lines.size()))
|
2022-06-20 10:14:43 +02:00
|
|
|
return 0;
|
2022-06-21 10:14:20 +02:00
|
|
|
|
2022-06-21 11:13:30 +02:00
|
|
|
const Line& line = m_Lines[aLine];
|
2022-06-20 10:14:43 +02:00
|
|
|
int col = 0;
|
|
|
|
int i = 0;
|
2022-06-21 11:13:30 +02:00
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
while (i < aIndex && i < line.Length())
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
2024-02-28 19:16:31 +01:00
|
|
|
Char c = line.buffer[i];
|
2022-06-20 10:14:43 +02:00
|
|
|
i += UTF8CharLength(c);
|
|
|
|
if (c == '\t')
|
|
|
|
col = (col / m_nTabSize) * m_nTabSize + m_nTabSize;
|
|
|
|
else
|
|
|
|
col++;
|
|
|
|
}
|
|
|
|
return col;
|
|
|
|
}
|
|
|
|
|
|
|
|
int CTextLogger::GetLineCharacterCount(int aLine) const
|
|
|
|
{
|
2022-06-21 10:14:20 +02:00
|
|
|
if (aLine >= static_cast<int>(m_Lines.size()))
|
2022-06-20 10:14:43 +02:00
|
|
|
return 0;
|
2022-06-21 10:14:20 +02:00
|
|
|
|
2022-06-21 11:13:30 +02:00
|
|
|
const Line& line = m_Lines[aLine];
|
2022-06-20 10:14:43 +02:00
|
|
|
int c = 0;
|
2022-06-21 11:13:30 +02:00
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
for (size_t i = 0; i < line.Length(); c++)
|
|
|
|
i += static_cast<size_t>(UTF8CharLength(m_Lines[aLine].buffer[i]));
|
2022-06-20 10:14:43 +02:00
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
int CTextLogger::GetLineMaxColumn(int aLine) const
|
|
|
|
{
|
2022-06-21 10:14:20 +02:00
|
|
|
if (aLine >= static_cast<int>(m_Lines.size()))
|
2022-06-20 10:14:43 +02:00
|
|
|
return 0;
|
2022-06-21 10:14:20 +02:00
|
|
|
|
2022-06-21 11:13:30 +02:00
|
|
|
const Line& line = m_Lines[aLine];
|
2022-06-20 10:14:43 +02:00
|
|
|
int col = 0;
|
2022-06-21 11:13:30 +02:00
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
for (size_t i = 0; i < line.Length(); )
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
2024-02-28 19:16:31 +01:00
|
|
|
Char c = line.buffer[i];
|
2022-06-20 10:14:43 +02:00
|
|
|
if (c == '\t')
|
|
|
|
col = (col / m_nTabSize) * m_nTabSize + m_nTabSize;
|
|
|
|
else
|
|
|
|
col++;
|
2022-06-21 10:14:20 +02:00
|
|
|
i += static_cast<size_t>(UTF8CharLength(c));
|
2022-06-20 10:14:43 +02:00
|
|
|
}
|
|
|
|
return col;
|
|
|
|
}
|
|
|
|
|
2022-06-20 10:17:11 +02:00
|
|
|
bool CTextLogger::IsOnWordBoundary(const Coordinates & aAt) const
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
2022-06-22 00:33:12 +02:00
|
|
|
if (aAt.m_nLine >= static_cast<int>(m_Lines.size()) || aAt.m_nColumn == 0)
|
2022-06-20 10:14:43 +02:00
|
|
|
return true;
|
|
|
|
|
2022-06-21 11:13:30 +02:00
|
|
|
const Line& line = m_Lines[aAt.m_nLine];
|
2022-06-21 10:14:20 +02:00
|
|
|
size_t cindex = static_cast<size_t>(GetCharacterIndex(aAt));
|
2022-06-21 11:13:30 +02:00
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
if (cindex >= line.Length())
|
2022-06-20 10:14:43 +02:00
|
|
|
return true;
|
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
return isspace(line.buffer[cindex]) != isspace(line.buffer[cindex - 1]);
|
2022-06-20 10:14:43 +02:00
|
|
|
}
|
|
|
|
|
2022-08-20 01:48:42 +02:00
|
|
|
void CTextLogger::RemoveLine(int aStart, int aEnd)
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
|
|
|
assert(aEnd >= aStart);
|
|
|
|
assert(m_Lines.size() > (size_t)(aEnd - aStart));
|
|
|
|
|
|
|
|
m_Lines.erase(m_Lines.begin() + aStart, m_Lines.begin() + aEnd);
|
|
|
|
assert(!m_Lines.empty());
|
|
|
|
}
|
|
|
|
|
2022-08-20 01:48:42 +02:00
|
|
|
void CTextLogger::RemoveLine(int aIndex)
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
|
|
|
assert(m_Lines.size() > 1);
|
|
|
|
|
|
|
|
m_Lines.erase(m_Lines.begin() + aIndex);
|
|
|
|
assert(!m_Lines.empty());
|
|
|
|
}
|
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
// TODO[ AMOS ]: rename to InsertBlankLine ?
|
2022-06-20 10:14:43 +02:00
|
|
|
CTextLogger::Line& CTextLogger::InsertLine(int aIndex)
|
|
|
|
{
|
2022-06-21 11:13:30 +02:00
|
|
|
Line& result = *m_Lines.insert(m_Lines.begin() + aIndex, Line());
|
2022-06-20 10:14:43 +02:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string CTextLogger::GetWordUnderCursor() const
|
|
|
|
{
|
2022-06-21 11:13:30 +02:00
|
|
|
const Coordinates c = GetCursorPosition();
|
2022-06-20 10:14:43 +02:00
|
|
|
return GetWordAt(c);
|
|
|
|
}
|
|
|
|
|
2022-06-20 10:17:11 +02:00
|
|
|
std::string CTextLogger::GetWordAt(const Coordinates & aCoords) const
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
2022-06-21 11:13:30 +02:00
|
|
|
const Coordinates start = FindWordStart(aCoords);
|
|
|
|
const Coordinates end = FindWordEnd(aCoords);
|
2022-06-20 10:14:43 +02:00
|
|
|
|
|
|
|
std::string r;
|
|
|
|
|
2022-06-21 11:13:30 +02:00
|
|
|
int istart = GetCharacterIndex(start);
|
|
|
|
int iend = GetCharacterIndex(end);
|
2022-06-20 10:14:43 +02:00
|
|
|
|
2022-06-21 11:13:30 +02:00
|
|
|
for (int it = istart; it < iend; ++it)
|
2024-02-28 19:16:31 +01:00
|
|
|
r.push_back(m_Lines[aCoords.m_nLine].buffer[it]);
|
2022-06-20 10:14:43 +02:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
//ImU32 CTextLogger::GetGlyphColor(const Glyph & aGlyph) const
|
|
|
|
//{
|
|
|
|
// ImVec4 color = aGlyph.m_Color;
|
|
|
|
// return ImGui::ColorConvertFloat4ToU32(color);
|
|
|
|
//}
|
2022-06-20 10:14:43 +02:00
|
|
|
|
2022-06-24 17:43:50 +02:00
|
|
|
void CTextLogger::HandleKeyboardInputs(bool bHoveredScrollbar, bool bActiveScrollbar)
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
2022-06-21 11:13:30 +02:00
|
|
|
bool shift = io.KeyShift;
|
|
|
|
bool ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl;
|
|
|
|
bool alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt;
|
2022-06-20 10:14:43 +02:00
|
|
|
|
2022-06-24 17:43:50 +02:00
|
|
|
if (!bActiveScrollbar && ImGui::IsWindowFocused())
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
2022-06-24 17:43:50 +02:00
|
|
|
if (!bHoveredScrollbar && ImGui::IsWindowHovered())
|
2022-06-20 10:14:43 +02:00
|
|
|
ImGui::SetMouseCursor(ImGuiMouseCursor_TextInput);
|
|
|
|
|
|
|
|
io.WantCaptureKeyboard = true;
|
|
|
|
io.WantTextInput = true;
|
|
|
|
|
2022-06-20 10:17:11 +02:00
|
|
|
if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow)))
|
2022-06-20 10:14:43 +02:00
|
|
|
MoveUp(1, shift);
|
|
|
|
else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow)))
|
|
|
|
MoveDown(1, shift);
|
|
|
|
else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow)))
|
|
|
|
MoveLeft(1, shift, ctrl);
|
|
|
|
else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow)))
|
|
|
|
MoveRight(1, shift, ctrl);
|
|
|
|
else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageUp)))
|
|
|
|
MoveUp(GetPageSize() - 4, shift);
|
|
|
|
else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageDown)))
|
|
|
|
MoveDown(GetPageSize() - 4, shift);
|
|
|
|
else if (!alt && ctrl && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home)))
|
|
|
|
MoveTop(shift);
|
|
|
|
else if (ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End)))
|
|
|
|
MoveBottom(shift);
|
|
|
|
else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home)))
|
|
|
|
MoveHome(shift);
|
|
|
|
else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End)))
|
|
|
|
MoveEnd(shift);
|
|
|
|
else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Insert)))
|
|
|
|
Copy();
|
|
|
|
else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_C)))
|
|
|
|
Copy();
|
|
|
|
else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_A)))
|
|
|
|
SelectAll();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-24 17:43:50 +02:00
|
|
|
void CTextLogger::HandleMouseInputs(bool bHoveredScrollbar, bool bActiveScrollbar)
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
|
|
|
2022-06-24 17:43:50 +02:00
|
|
|
bool bShift = io.KeyShift;
|
|
|
|
bool bCtrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl;
|
|
|
|
bool bAlt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt;
|
|
|
|
|
2022-06-26 16:47:00 +02:00
|
|
|
|
|
|
|
if (ImGui::IsMouseClicked(0) && ImGui::IsWindowHovered())
|
|
|
|
m_bWithinLoggingRect = true;
|
2022-06-26 17:10:52 +02:00
|
|
|
else if (ImGui::IsMouseReleased(0))
|
2022-06-26 16:47:00 +02:00
|
|
|
m_bWithinLoggingRect = false;
|
|
|
|
|
|
|
|
if (!bHoveredScrollbar && !bActiveScrollbar && m_bWithinLoggingRect)
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
2024-02-28 23:49:48 +01:00
|
|
|
bool click = ImGui::IsMouseClicked(0);
|
|
|
|
|
2022-06-24 17:43:50 +02:00
|
|
|
if (!bShift && !bAlt)
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
2022-06-21 11:13:30 +02:00
|
|
|
bool doubleClick = ImGui::IsMouseDoubleClicked(0);
|
|
|
|
|
|
|
|
double t = ImGui::GetTime();
|
2022-06-22 00:33:12 +02:00
|
|
|
bool tripleClick = click && !doubleClick && (m_flLastClick != -1.0 && (t - m_flLastClick) < io.MouseDoubleClickTime);
|
2022-06-20 10:14:43 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
Left mouse button triple click
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (tripleClick)
|
|
|
|
{
|
2022-06-24 17:43:50 +02:00
|
|
|
if (!bCtrl)
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
|
|
|
m_State.m_CursorPosition = m_InteractiveStart = m_InteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos());
|
|
|
|
m_SelectionMode = SelectionMode::Line;
|
|
|
|
SetSelection(m_InteractiveStart, m_InteractiveEnd, m_SelectionMode);
|
|
|
|
}
|
|
|
|
|
2022-06-22 00:33:12 +02:00
|
|
|
m_flLastClick = -1.0;
|
2022-06-20 10:14:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Left mouse button double click
|
|
|
|
*/
|
|
|
|
|
|
|
|
else if (doubleClick)
|
|
|
|
{
|
2022-06-24 17:43:50 +02:00
|
|
|
if (!bCtrl)
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
|
|
|
m_State.m_CursorPosition = m_InteractiveStart = m_InteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos());
|
2022-06-20 10:17:11 +02:00
|
|
|
|
2024-02-28 23:49:48 +01:00
|
|
|
// Advance cursor to the end of the selection
|
|
|
|
m_InteractiveStart = FindWordStart(m_State.m_CursorPosition);
|
|
|
|
m_State.m_CursorPosition = m_InteractiveEnd = FindWordEnd(m_State.m_CursorPosition);
|
|
|
|
|
2022-06-20 10:14:43 +02:00
|
|
|
if (m_SelectionMode == SelectionMode::Line)
|
|
|
|
m_SelectionMode = SelectionMode::Normal;
|
|
|
|
else
|
|
|
|
m_SelectionMode = SelectionMode::Word;
|
|
|
|
SetSelection(m_InteractiveStart, m_InteractiveEnd, m_SelectionMode);
|
|
|
|
}
|
|
|
|
|
2022-06-22 00:33:12 +02:00
|
|
|
m_flLastClick = ImGui::GetTime();
|
2022-06-20 10:14:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Left mouse button click
|
|
|
|
*/
|
|
|
|
else if (click)
|
|
|
|
{
|
|
|
|
m_State.m_CursorPosition = m_InteractiveStart = m_InteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos());
|
2022-06-24 17:43:50 +02:00
|
|
|
if (bCtrl)
|
2022-06-20 10:14:43 +02:00
|
|
|
m_SelectionMode = SelectionMode::Word;
|
|
|
|
else
|
|
|
|
m_SelectionMode = SelectionMode::Normal;
|
|
|
|
SetSelection(m_InteractiveStart, m_InteractiveEnd, m_SelectionMode);
|
|
|
|
|
2022-06-22 00:33:12 +02:00
|
|
|
m_flLastClick = ImGui::GetTime();
|
2022-06-20 10:14:43 +02:00
|
|
|
}
|
|
|
|
// Mouse left button dragging (=> update selection)
|
|
|
|
else if (ImGui::IsMouseDragging(0) && ImGui::IsMouseDown(0))
|
|
|
|
{
|
|
|
|
io.WantCaptureMouse = true;
|
|
|
|
m_State.m_CursorPosition = m_InteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos());
|
2022-08-30 22:54:34 +02:00
|
|
|
|
2022-06-20 10:14:43 +02:00
|
|
|
SetSelection(m_InteractiveStart, m_InteractiveEnd, m_SelectionMode);
|
2022-06-26 16:47:00 +02:00
|
|
|
EnsureCursorVisible();
|
2022-06-20 10:14:43 +02:00
|
|
|
}
|
2022-08-30 22:54:34 +02:00
|
|
|
// Move start position of the selection when entries have been erased/inserted
|
|
|
|
if (m_nLinesOffsetAmount && ImGui::IsMouseDown(0))
|
|
|
|
{
|
|
|
|
Coordinates newStart;
|
|
|
|
newStart = m_InteractiveStart;
|
|
|
|
|
|
|
|
if (m_bLinesOffsetForward)
|
|
|
|
{
|
|
|
|
newStart.m_nLine += m_nLinesOffsetAmount;
|
|
|
|
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_nLine -= m_nLinesOffsetAmount;
|
|
|
|
if (newStart.m_nLine < 0)
|
|
|
|
{
|
|
|
|
newStart.m_nLine = 0;
|
|
|
|
newStart.m_nColumn = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_nLinesOffsetAmount = 0;
|
|
|
|
m_InteractiveStart = newStart;
|
|
|
|
}
|
2022-06-20 10:14:43 +02:00
|
|
|
}
|
2024-02-28 23:49:48 +01:00
|
|
|
else if (bShift)
|
|
|
|
{
|
|
|
|
if (click) // Shift select range
|
|
|
|
{
|
|
|
|
Coordinates newSelection = ScreenPosToCoordinates(ImGui::GetMousePos());
|
|
|
|
|
|
|
|
if (newSelection > m_State.m_CursorPosition)
|
|
|
|
SetSelectionEnd(newSelection);
|
|
|
|
else
|
|
|
|
SetSelectionStart(newSelection);
|
|
|
|
|
|
|
|
m_InteractiveStart = m_State.m_SelectionStart;
|
|
|
|
m_InteractiveEnd = m_State.m_SelectionEnd;
|
|
|
|
m_State.m_CursorPosition = newSelection;
|
|
|
|
}
|
|
|
|
}
|
2022-06-20 10:14:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTextLogger::Render()
|
|
|
|
{
|
2022-06-20 10:17:11 +02:00
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f));
|
2022-06-24 17:43:50 +02:00
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
ImGuiWindow* const pWindow = ImGui::GetCurrentWindow();
|
|
|
|
|
|
|
|
const ImGuiID activeID = ImGui::GetActiveID();
|
|
|
|
const ImGuiID hoveredID = ImGui::GetHoveredID();
|
|
|
|
|
|
|
|
const bool bHoveredScrollbar = hoveredID && (hoveredID == ImGui::GetWindowScrollbarID(pWindow, ImGuiAxis_X) || hoveredID == ImGui::GetWindowScrollbarID(pWindow, ImGuiAxis_Y));
|
|
|
|
const bool bActiveScrollbar = activeID && (activeID == ImGui::GetWindowScrollbarID(pWindow, ImGuiAxis_X) || activeID == ImGui::GetWindowScrollbarID(pWindow, ImGuiAxis_Y));
|
2022-06-24 17:43:50 +02:00
|
|
|
|
2023-04-09 19:53:33 +02:00
|
|
|
if (m_bHandleUserInputs)
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
2022-06-24 17:43:50 +02:00
|
|
|
HandleKeyboardInputs(bHoveredScrollbar, bActiveScrollbar);
|
2022-06-20 10:17:11 +02:00
|
|
|
ImGui::PushAllowKeyboardFocus(true);
|
2022-06-20 10:14:43 +02:00
|
|
|
|
2022-06-24 17:43:50 +02:00
|
|
|
HandleMouseInputs(bHoveredScrollbar, bActiveScrollbar);
|
2023-04-09 19:53:33 +02:00
|
|
|
}
|
2022-06-20 10:17:11 +02:00
|
|
|
|
|
|
|
/* Compute mCharAdvance regarding to scaled font size (Ctrl + mouse wheel)*/
|
|
|
|
const float fontSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, "#", nullptr, nullptr).x;
|
|
|
|
m_CharAdvance = ImVec2(fontSize, ImGui::GetTextLineHeightWithSpacing() * m_flLineSpacing);
|
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
const ImVec2 contentSize = ImGui::GetWindowContentRegionMax();
|
|
|
|
ImDrawList* const drawList = ImGui::GetWindowDrawList();
|
2022-06-20 10:14:43 +02:00
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
const ImVec2 cursorScreenPos = ImGui::GetCursorScreenPos();
|
2022-06-20 10:14:43 +02:00
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
float longest = 0.0f;
|
|
|
|
const float scrollY = ImGui::GetScrollY();
|
2022-06-20 10:14:43 +02:00
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
int lineNo = static_cast<int>(ImFloor(scrollY / m_CharAdvance.y));
|
|
|
|
const int lineMax = ImMax(0, ImMin(static_cast<int>(m_Lines.size()) - 1, lineNo + static_cast<int>(ImFloor((scrollY + contentSize.y) / m_CharAdvance.y))));
|
2022-06-21 10:14:20 +02:00
|
|
|
|
2022-06-20 10:14:43 +02:00
|
|
|
if (!m_Lines.empty())
|
|
|
|
{
|
|
|
|
while (lineNo <= lineMax)
|
|
|
|
{
|
2024-02-28 19:16:31 +01:00
|
|
|
const ImVec2 lineStartScreenPos = ImVec2(cursorScreenPos.x, cursorScreenPos.y + lineNo * m_CharAdvance.y);
|
|
|
|
const ImVec2 textScreenPos = ImVec2(lineStartScreenPos.x, lineStartScreenPos.y);
|
2022-06-20 10:14:43 +02:00
|
|
|
|
2022-06-21 11:13:30 +02:00
|
|
|
const Line& line = m_Lines[lineNo];
|
2024-02-28 20:28:11 +01:00
|
|
|
longest = ImMax(TextDistanceToLineStart(Coordinates(lineNo, GetLineMaxColumn(lineNo))), longest);
|
2024-02-28 19:16:31 +01:00
|
|
|
|
2022-06-21 10:14:20 +02:00
|
|
|
Coordinates lineStartCoord(lineNo, 0);
|
|
|
|
Coordinates lineEndCoord(lineNo, GetLineMaxColumn(lineNo));
|
2022-06-20 10:14:43 +02:00
|
|
|
|
2022-06-21 10:14:20 +02:00
|
|
|
// Draw selection for the current line
|
|
|
|
float sstart = -1.0f;
|
|
|
|
float ssend = -1.0f;
|
2022-06-20 10:14:43 +02:00
|
|
|
|
2022-06-21 10:14:20 +02:00
|
|
|
assert(m_State.m_SelectionStart <= m_State.m_SelectionEnd);
|
|
|
|
if (m_State.m_SelectionStart <= lineEndCoord)
|
|
|
|
sstart = m_State.m_SelectionStart > lineStartCoord ? TextDistanceToLineStart(m_State.m_SelectionStart) : 0.0f;
|
|
|
|
if (m_State.m_SelectionEnd > lineStartCoord)
|
|
|
|
ssend = TextDistanceToLineStart(m_State.m_SelectionEnd < lineEndCoord ? m_State.m_SelectionEnd : lineEndCoord);
|
2022-06-20 10:14:43 +02:00
|
|
|
|
2022-06-21 10:14:20 +02:00
|
|
|
if (m_State.m_SelectionEnd.m_nLine > lineNo)
|
|
|
|
ssend += m_CharAdvance.x;
|
2022-06-20 10:14:43 +02:00
|
|
|
|
2022-06-21 10:14:20 +02:00
|
|
|
if (sstart != -1 && ssend != -1 && sstart < ssend)
|
|
|
|
{
|
2024-02-28 19:16:31 +01:00
|
|
|
const ImVec2 vstart(lineStartScreenPos.x + sstart, lineStartScreenPos.y);
|
|
|
|
const ImVec2 vend(lineStartScreenPos.x + ssend, lineStartScreenPos.y + m_CharAdvance.y);
|
|
|
|
|
2022-06-21 10:14:20 +02:00
|
|
|
drawList->AddRectFilled(vstart, vend, ImGui::GetColorU32(ImGuiCol_TextSelectedBg));
|
|
|
|
}
|
2022-06-20 10:14:43 +02:00
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
// Render the cursor
|
|
|
|
if (m_State.m_CursorPosition.m_nLine == lineNo && ImGui::IsWindowFocused())
|
2022-06-21 10:14:20 +02:00
|
|
|
{
|
2024-02-28 19:16:31 +01:00
|
|
|
const ImS64 timeEnd = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
|
|
|
const ImU64 elapsed = timeEnd - m_nStartTime;
|
2022-06-21 10:14:20 +02:00
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
if (elapsed > 400)
|
2022-06-20 20:21:52 +02:00
|
|
|
{
|
2024-02-28 19:16:31 +01:00
|
|
|
const float width = 1.0f;
|
|
|
|
const float cx = TextDistanceToLineStart(m_State.m_CursorPosition);
|
2022-06-20 20:21:52 +02:00
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
const ImVec2 cstart(textScreenPos.x + cx, lineStartScreenPos.y);
|
|
|
|
const ImVec2 cend(textScreenPos.x + cx + width, lineStartScreenPos.y + m_CharAdvance.y);
|
2022-06-21 11:13:30 +02:00
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
drawList->AddRectFilled(cstart, cend, 0xffe0e0e0);
|
|
|
|
|
|
|
|
if (elapsed > 800)
|
|
|
|
m_nStartTime = timeEnd;
|
2022-06-20 10:14:43 +02:00
|
|
|
}
|
2022-06-21 10:14:20 +02:00
|
|
|
}
|
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
if (!line.buffer.empty())
|
2022-06-21 10:14:20 +02:00
|
|
|
{
|
2024-02-28 19:16:31 +01:00
|
|
|
ImU32 color = line.color;
|
2022-06-20 10:14:43 +02:00
|
|
|
|
2022-06-21 20:20:12 +02:00
|
|
|
if (m_itFilter.IsActive())
|
|
|
|
{
|
2024-02-28 19:16:31 +01:00
|
|
|
// Make line dark if it isn't found by the filter
|
|
|
|
if (!m_itFilter.PassFilter(line.buffer.c_str()))
|
|
|
|
color = 0xff605040;
|
2022-06-20 10:14:43 +02:00
|
|
|
}
|
2022-06-20 20:21:52 +02:00
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
const ImVec2 newOffset(textScreenPos.x, textScreenPos.y);
|
|
|
|
drawList->AddText(newOffset, color, line.buffer.c_str());
|
2022-06-20 10:14:43 +02:00
|
|
|
}
|
2022-06-21 10:14:20 +02:00
|
|
|
|
|
|
|
++lineNo;
|
2022-06-20 10:14:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-29 00:59:55 +01:00
|
|
|
// This dummy is here to let Dear ImGui know where the last character of
|
|
|
|
// the line had ended, so that it could properly process the horizontal
|
|
|
|
// scrollbar
|
2022-06-20 10:14:43 +02:00
|
|
|
ImGui::Dummy(ImVec2((longest + 2), m_Lines.size() * m_CharAdvance.y));
|
2024-02-29 00:59:55 +01:00
|
|
|
|
|
|
|
m_bScrolledToStart = ImGui::GetScrollX() == 0.0f;
|
|
|
|
|
|
|
|
if (m_bScrollToStart || (m_bAutoScroll && m_bScrolledToStart && !m_bScrollToCursor))
|
|
|
|
{
|
|
|
|
ImGui::SetScrollHereX(0.0f);
|
|
|
|
m_bScrollToStart = false;
|
|
|
|
}
|
|
|
|
|
2023-04-09 22:35:55 +02:00
|
|
|
m_bScrolledToBottom = ImGui::GetScrollY() >= ImGui::GetScrollMaxY();
|
2022-06-20 10:14:43 +02:00
|
|
|
|
2023-04-09 22:35:55 +02:00
|
|
|
if (m_bScrollToBottom || (m_bAutoScroll && m_bScrolledToBottom && !m_bScrollToCursor))
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
2022-06-21 23:25:15 +02:00
|
|
|
ImGui::SetScrollHereY(1.0f);
|
|
|
|
m_bScrollToBottom = false;
|
2022-06-20 10:14:43 +02:00
|
|
|
}
|
2024-02-25 23:51:04 +01:00
|
|
|
|
2022-06-21 23:25:15 +02:00
|
|
|
m_bScrollToCursor = false;
|
2022-06-20 10:14:43 +02:00
|
|
|
|
2023-04-09 19:53:33 +02:00
|
|
|
if (m_bHandleUserInputs)
|
2022-06-20 10:14:43 +02:00
|
|
|
ImGui::PopAllowKeyboardFocus();
|
|
|
|
|
|
|
|
ImGui::PopStyleVar();
|
|
|
|
}
|
|
|
|
|
2022-06-22 00:33:12 +02:00
|
|
|
void CTextLogger::Copy(bool aCopyAll)
|
|
|
|
{
|
|
|
|
if (!aCopyAll && HasSelection())
|
|
|
|
{
|
|
|
|
ImGui::SetClipboardText(GetSelectedText().c_str());
|
|
|
|
}
|
|
|
|
else if (!aCopyAll)
|
|
|
|
{
|
|
|
|
if (!m_Lines.empty())
|
|
|
|
{
|
|
|
|
const Line& line = m_Lines[GetActualCursorCoordinates().m_nLine];
|
2024-02-28 19:16:31 +01:00
|
|
|
ImGui::SetClipboardText(line.buffer.c_str());
|
2022-06-22 00:33:12 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else // Copy all lines to clipboard.
|
|
|
|
{
|
|
|
|
std::string str;
|
|
|
|
for (const Line& line : m_Lines)
|
|
|
|
{
|
2024-02-28 19:16:31 +01:00
|
|
|
str.append(line.buffer);
|
2022-06-22 00:33:12 +02:00
|
|
|
}
|
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
ImGui::SetClipboardText(str.c_str());
|
2022-06-20 10:14:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
//void CTextLogger::SetText(const ConLog_t& aText)
|
|
|
|
//{
|
|
|
|
// m_Lines.clear();
|
|
|
|
// m_Lines.emplace_back(Line());
|
|
|
|
// for (char chr : aText.m_svConLog)
|
|
|
|
// {
|
|
|
|
// if (chr == '\r') // ignore the carriage return character
|
|
|
|
// continue;
|
|
|
|
// else if (chr == '\n')
|
|
|
|
// m_Lines.emplace_back(Line());
|
|
|
|
// else
|
|
|
|
// m_Lines.back().emplace_back(Glyph(chr, aText.m_imColor));
|
|
|
|
// }
|
|
|
|
//}
|
|
|
|
|
|
|
|
//void CTextLogger::SetTextLines(const std::vector<ConLog_t>& aLines)
|
|
|
|
//{
|
|
|
|
// m_Lines.clear();
|
|
|
|
//
|
|
|
|
// if (aLines.empty())
|
|
|
|
// {
|
|
|
|
// m_Lines.emplace_back(Line());
|
|
|
|
// }
|
|
|
|
// else
|
|
|
|
// {
|
|
|
|
// m_Lines.resize(aLines.size());
|
|
|
|
//
|
|
|
|
// for (size_t i = 0; i < aLines.size(); ++i)
|
|
|
|
// {
|
|
|
|
// const std::string & aLine = aLines[i].m_svConLog;
|
|
|
|
//
|
|
|
|
// m_Lines[i].reserve(aLine.size());
|
|
|
|
// for (size_t j = 0; j < aLine.size(); ++j)
|
|
|
|
// {
|
|
|
|
// m_Lines[i].emplace_back(Glyph(aLine[j], aLines[i].m_imColor));
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//}
|
2022-06-20 10:14:43 +02:00
|
|
|
|
2022-06-26 16:47:00 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2022-06-20 10:17:11 +02:00
|
|
|
void CTextLogger::SetCursorPosition(const Coordinates & aPosition)
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
|
|
|
if (m_State.m_CursorPosition != aPosition)
|
|
|
|
{
|
|
|
|
m_State.m_CursorPosition = aPosition;
|
|
|
|
EnsureCursorVisible();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-20 10:17:11 +02:00
|
|
|
void CTextLogger::SetSelectionStart(const Coordinates & aPosition)
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
|
|
|
m_State.m_SelectionStart = SanitizeCoordinates(aPosition);
|
|
|
|
if (m_State.m_SelectionStart > m_State.m_SelectionEnd)
|
2024-02-28 19:16:31 +01:00
|
|
|
ImSwap(m_State.m_SelectionStart, m_State.m_SelectionEnd);
|
2022-06-20 10:14:43 +02:00
|
|
|
}
|
|
|
|
|
2022-06-20 10:17:11 +02:00
|
|
|
void CTextLogger::SetSelectionEnd(const Coordinates & aPosition)
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
|
|
|
m_State.m_SelectionEnd = SanitizeCoordinates(aPosition);
|
|
|
|
if (m_State.m_SelectionStart > m_State.m_SelectionEnd)
|
2024-02-28 19:16:31 +01:00
|
|
|
ImSwap(m_State.m_SelectionStart, m_State.m_SelectionEnd);
|
2022-06-20 10:14:43 +02:00
|
|
|
}
|
|
|
|
|
2022-06-20 10:17:11 +02:00
|
|
|
void CTextLogger::SetSelection(const Coordinates & aStart, const Coordinates & aEnd, SelectionMode aMode)
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
2022-06-21 11:13:30 +02:00
|
|
|
Coordinates oldSelStart = m_State.m_SelectionStart;
|
|
|
|
Coordinates oldSelEnd = m_State.m_SelectionEnd;
|
2022-06-20 10:14:43 +02:00
|
|
|
|
|
|
|
m_State.m_SelectionStart = SanitizeCoordinates(aStart);
|
|
|
|
m_State.m_SelectionEnd = SanitizeCoordinates(aEnd);
|
2024-02-28 19:16:31 +01:00
|
|
|
|
2022-06-20 10:14:43 +02:00
|
|
|
if (m_State.m_SelectionStart > m_State.m_SelectionEnd)
|
2024-02-28 19:16:31 +01:00
|
|
|
ImSwap(m_State.m_SelectionStart, m_State.m_SelectionEnd);
|
2022-06-20 10:14:43 +02:00
|
|
|
|
|
|
|
switch (aMode)
|
|
|
|
{
|
|
|
|
case CTextLogger::SelectionMode::Normal:
|
|
|
|
break;
|
|
|
|
case CTextLogger::SelectionMode::Word:
|
|
|
|
{
|
|
|
|
m_State.m_SelectionStart = FindWordStart(m_State.m_SelectionStart);
|
|
|
|
if (!IsOnWordBoundary(m_State.m_SelectionEnd))
|
|
|
|
m_State.m_SelectionEnd = FindWordEnd(FindWordStart(m_State.m_SelectionEnd));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case CTextLogger::SelectionMode::Line:
|
|
|
|
{
|
2022-06-21 11:13:30 +02:00
|
|
|
const int lineNo = m_State.m_SelectionEnd.m_nLine;
|
2023-04-09 22:35:55 +02:00
|
|
|
//const size_t lineSize = (size_t)lineNo < m_Lines.size() ? m_Lines[lineNo].size() : 0;
|
2022-06-20 10:14:43 +02:00
|
|
|
m_State.m_SelectionStart = Coordinates(m_State.m_SelectionStart.m_nLine, 0);
|
2024-02-28 23:49:48 +01:00
|
|
|
m_State.m_SelectionEnd = m_Lines.size() > lineNo + 1 ? Coordinates(lineNo + 1, 0) : Coordinates(lineNo, GetLineMaxColumn(lineNo));
|
|
|
|
m_State.m_CursorPosition = m_State.m_SelectionEnd;
|
2022-06-20 10:14:43 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTextLogger::SetTabSize(int aValue)
|
|
|
|
{
|
2024-02-28 19:16:31 +01:00
|
|
|
m_nTabSize = ImMax(0, ImMin(32, aValue));
|
2022-06-20 10:14:43 +02:00
|
|
|
}
|
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
void CTextLogger::InsertText(const char* const text, const ImU32 color)
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
2024-02-28 19:16:31 +01:00
|
|
|
IM_ASSERT(text);
|
2022-06-20 10:14:43 +02:00
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
if (!*text)
|
|
|
|
return;
|
2022-06-20 10:17:11 +02:00
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
Coordinates pos = GetActualLastLineCoordinates();
|
|
|
|
|
|
|
|
const Coordinates& start = ImMin(pos, m_State.m_SelectionStart);
|
|
|
|
int totalLines = pos.m_nLine - start.m_nLine;
|
|
|
|
|
|
|
|
totalLines += InsertTextAt(pos, text, color);
|
2022-06-20 10:14:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CTextLogger::MoveUp(int aAmount, bool aSelect)
|
|
|
|
{
|
2022-06-21 11:13:30 +02:00
|
|
|
const Coordinates oldPos = m_State.m_CursorPosition;
|
2024-02-28 19:16:31 +01:00
|
|
|
m_State.m_CursorPosition.m_nLine = ImMax(0, m_State.m_CursorPosition.m_nLine - aAmount);
|
2022-06-20 10:14:43 +02:00
|
|
|
if (oldPos != m_State.m_CursorPosition)
|
|
|
|
{
|
|
|
|
if (aSelect)
|
|
|
|
{
|
|
|
|
if (oldPos == m_InteractiveStart)
|
|
|
|
m_InteractiveStart = m_State.m_CursorPosition;
|
|
|
|
else if (oldPos == m_InteractiveEnd)
|
|
|
|
m_InteractiveEnd = m_State.m_CursorPosition;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_InteractiveStart = m_State.m_CursorPosition;
|
|
|
|
m_InteractiveEnd = oldPos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
m_InteractiveStart = m_InteractiveEnd = m_State.m_CursorPosition;
|
|
|
|
|
2024-02-28 23:49:48 +01:00
|
|
|
SetSelection(m_InteractiveStart, m_InteractiveEnd, SelectionMode::Normal);
|
2022-06-20 10:14:43 +02:00
|
|
|
EnsureCursorVisible();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTextLogger::MoveDown(int aAmount, bool aSelect)
|
|
|
|
{
|
|
|
|
assert(m_State.m_CursorPosition.m_nColumn >= 0);
|
2022-06-21 11:13:30 +02:00
|
|
|
Coordinates oldPos = m_State.m_CursorPosition;
|
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
m_State.m_CursorPosition.m_nLine = ImMax(0, ImMin(static_cast<int>(m_Lines.size() - 1), m_State.m_CursorPosition.m_nLine + aAmount));
|
2022-06-20 10:14:43 +02:00
|
|
|
|
|
|
|
if (m_State.m_CursorPosition != oldPos)
|
|
|
|
{
|
|
|
|
if (aSelect)
|
|
|
|
{
|
|
|
|
if (oldPos == m_InteractiveEnd)
|
|
|
|
m_InteractiveEnd = m_State.m_CursorPosition;
|
|
|
|
else if (oldPos == m_InteractiveStart)
|
|
|
|
m_InteractiveStart = m_State.m_CursorPosition;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_InteractiveStart = oldPos;
|
|
|
|
m_InteractiveEnd = m_State.m_CursorPosition;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
m_InteractiveStart = m_InteractiveEnd = m_State.m_CursorPosition;
|
|
|
|
|
2024-02-28 23:49:48 +01:00
|
|
|
SetSelection(m_InteractiveStart, m_InteractiveEnd, SelectionMode::Normal);
|
2022-06-20 10:14:43 +02:00
|
|
|
EnsureCursorVisible();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool IsUTFSequence(char c)
|
|
|
|
{
|
|
|
|
return (c & 0xC0) == 0x80;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTextLogger::MoveLeft(int aAmount, bool aSelect, bool aWordMode)
|
|
|
|
{
|
|
|
|
if (m_Lines.empty())
|
|
|
|
return;
|
|
|
|
|
2022-06-21 11:13:30 +02:00
|
|
|
const Coordinates oldPos = m_State.m_CursorPosition;
|
2022-06-20 10:14:43 +02:00
|
|
|
m_State.m_CursorPosition = GetActualCursorCoordinates();
|
2022-06-21 11:13:30 +02:00
|
|
|
|
|
|
|
int line = m_State.m_CursorPosition.m_nLine;
|
|
|
|
int cindex = GetCharacterIndex(m_State.m_CursorPosition);
|
2022-06-20 10:14:43 +02:00
|
|
|
|
|
|
|
while (aAmount-- > 0)
|
|
|
|
{
|
|
|
|
if (cindex == 0)
|
|
|
|
{
|
|
|
|
if (line > 0)
|
|
|
|
{
|
|
|
|
--line;
|
2022-06-21 11:13:30 +02:00
|
|
|
if (static_cast<int>(m_Lines.size()) > line)
|
2024-02-28 19:16:31 +01:00
|
|
|
cindex = static_cast<int>(m_Lines[line].buffer.size());
|
2022-06-20 10:14:43 +02:00
|
|
|
else
|
|
|
|
cindex = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
--cindex;
|
|
|
|
if (cindex > 0)
|
|
|
|
{
|
2022-06-21 11:13:30 +02:00
|
|
|
if (static_cast<int>(m_Lines.size()) > line)
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
2023-04-09 23:33:10 +02:00
|
|
|
const Line &lineData = m_Lines[line];
|
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
while (cindex > 0 && IsUTFSequence(lineData.buffer[cindex]))
|
2023-04-09 23:33:10 +02:00
|
|
|
--cindex;
|
|
|
|
|
|
|
|
// Skip the newline character.
|
2024-02-28 19:16:31 +01:00
|
|
|
if (cindex > 0 && lineData.buffer[cindex] == '\n')
|
2022-06-20 10:14:43 +02:00
|
|
|
--cindex;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_State.m_CursorPosition = Coordinates(line, GetCharacterColumn(line, cindex));
|
|
|
|
if (aWordMode)
|
|
|
|
{
|
|
|
|
m_State.m_CursorPosition = FindWordStart(m_State.m_CursorPosition);
|
|
|
|
cindex = GetCharacterIndex(m_State.m_CursorPosition);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_State.m_CursorPosition = Coordinates(line, GetCharacterColumn(line, cindex));
|
|
|
|
|
|
|
|
assert(m_State.m_CursorPosition.m_nColumn >= 0);
|
|
|
|
if (aSelect)
|
|
|
|
{
|
|
|
|
if (oldPos == m_InteractiveStart)
|
|
|
|
m_InteractiveStart = m_State.m_CursorPosition;
|
|
|
|
else if (oldPos == m_InteractiveEnd)
|
|
|
|
m_InteractiveEnd = m_State.m_CursorPosition;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_InteractiveStart = m_State.m_CursorPosition;
|
|
|
|
m_InteractiveEnd = oldPos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2024-02-28 23:49:48 +01:00
|
|
|
{
|
|
|
|
if (HasSelection() && !aWordMode)
|
|
|
|
m_State.m_CursorPosition = m_InteractiveStart;
|
|
|
|
|
2022-06-20 10:14:43 +02:00
|
|
|
m_InteractiveStart = m_InteractiveEnd = m_State.m_CursorPosition;
|
2024-02-28 23:49:48 +01:00
|
|
|
}
|
|
|
|
|
2022-06-20 10:14:43 +02:00
|
|
|
SetSelection(m_InteractiveStart, m_InteractiveEnd, aSelect && aWordMode ? SelectionMode::Word : SelectionMode::Normal);
|
|
|
|
|
|
|
|
EnsureCursorVisible();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTextLogger::MoveRight(int aAmount, bool aSelect, bool aWordMode)
|
|
|
|
{
|
2022-06-21 11:13:30 +02:00
|
|
|
const Coordinates oldPos = m_State.m_CursorPosition;
|
2022-06-20 10:14:43 +02:00
|
|
|
|
2022-08-09 12:13:31 +02:00
|
|
|
if (m_Lines.empty() || oldPos.m_nLine >= static_cast<int>(m_Lines.size()))
|
2022-06-20 10:14:43 +02:00
|
|
|
return;
|
|
|
|
|
2022-06-21 11:13:30 +02:00
|
|
|
int cindex = GetCharacterIndex(m_State.m_CursorPosition);
|
2022-06-20 10:14:43 +02:00
|
|
|
while (aAmount-- > 0)
|
|
|
|
{
|
2022-06-21 11:13:30 +02:00
|
|
|
int lindex = m_State.m_CursorPosition.m_nLine;
|
|
|
|
const Line& line = m_Lines[lindex];
|
2022-06-20 10:14:43 +02:00
|
|
|
|
2023-04-09 23:33:10 +02:00
|
|
|
bool isNewLine = false;
|
2024-02-28 19:16:31 +01:00
|
|
|
const bool isLastChar = (cindex >= line.Length()-1);
|
2023-04-09 23:33:10 +02:00
|
|
|
|
|
|
|
// If the cursor is at the last character before the newline character,
|
|
|
|
// we want to skip the newline character and move to the next line.
|
2024-02-28 19:16:31 +01:00
|
|
|
if (isLastChar && !line.buffer.empty())
|
|
|
|
isNewLine = line.buffer.back() == '\n';
|
2023-04-09 23:33:10 +02:00
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
if (cindex >= line.Length() || isNewLine)
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
2022-08-09 12:13:31 +02:00
|
|
|
if (m_State.m_CursorPosition.m_nLine < static_cast<int>(m_Lines.size()) - 1)
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
2024-02-28 19:16:31 +01:00
|
|
|
m_State.m_CursorPosition.m_nLine = ImMax(0, ImMin(static_cast<int>(m_Lines.size()) - 1, m_State.m_CursorPosition.m_nLine + 1));
|
2022-06-20 10:14:43 +02:00
|
|
|
m_State.m_CursorPosition.m_nColumn = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2024-02-28 19:16:31 +01:00
|
|
|
cindex += UTF8CharLength(line.buffer[cindex]);
|
2022-06-20 10:14:43 +02:00
|
|
|
m_State.m_CursorPosition = Coordinates(lindex, GetCharacterColumn(lindex, cindex));
|
|
|
|
if (aWordMode)
|
2024-02-28 20:48:35 +01:00
|
|
|
m_State.m_CursorPosition = FindWordEnd(m_State.m_CursorPosition);
|
2022-06-20 10:14:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aSelect)
|
|
|
|
{
|
|
|
|
if (oldPos == m_InteractiveEnd)
|
|
|
|
m_InteractiveEnd = SanitizeCoordinates(m_State.m_CursorPosition);
|
|
|
|
else if (oldPos == m_InteractiveStart)
|
|
|
|
m_InteractiveStart = m_State.m_CursorPosition;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_InteractiveStart = oldPos;
|
|
|
|
m_InteractiveEnd = m_State.m_CursorPosition;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2024-02-28 23:49:48 +01:00
|
|
|
{
|
|
|
|
if (HasSelection() && !aWordMode)
|
|
|
|
m_State.m_CursorPosition = m_InteractiveEnd;
|
|
|
|
|
2022-06-20 10:14:43 +02:00
|
|
|
m_InteractiveStart = m_InteractiveEnd = m_State.m_CursorPosition;
|
2024-02-28 23:49:48 +01:00
|
|
|
}
|
2022-06-20 10:14:43 +02:00
|
|
|
SetSelection(m_InteractiveStart, m_InteractiveEnd, aSelect && aWordMode ? SelectionMode::Word : SelectionMode::Normal);
|
|
|
|
|
|
|
|
EnsureCursorVisible();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTextLogger::MoveTop(bool aSelect)
|
|
|
|
{
|
2022-06-21 11:13:30 +02:00
|
|
|
const Coordinates oldPos = m_State.m_CursorPosition;
|
2022-06-20 10:14:43 +02:00
|
|
|
SetCursorPosition(Coordinates(0, 0));
|
|
|
|
|
|
|
|
if (m_State.m_CursorPosition != oldPos)
|
|
|
|
{
|
|
|
|
if (aSelect)
|
|
|
|
{
|
|
|
|
m_InteractiveEnd = oldPos;
|
|
|
|
m_InteractiveStart = m_State.m_CursorPosition;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
m_InteractiveStart = m_InteractiveEnd = m_State.m_CursorPosition;
|
|
|
|
SetSelection(m_InteractiveStart, m_InteractiveEnd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTextLogger::MoveBottom(bool aSelect)
|
|
|
|
{
|
2022-06-21 11:13:30 +02:00
|
|
|
const Coordinates oldPos = GetCursorPosition();
|
|
|
|
const Coordinates newPos = Coordinates(static_cast<int>(m_Lines.size()) - 1, 0);
|
|
|
|
|
2022-06-20 10:14:43 +02:00
|
|
|
SetCursorPosition(newPos);
|
|
|
|
if (aSelect)
|
|
|
|
{
|
|
|
|
m_InteractiveStart = oldPos;
|
|
|
|
m_InteractiveEnd = newPos;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
m_InteractiveStart = m_InteractiveEnd = newPos;
|
|
|
|
SetSelection(m_InteractiveStart, m_InteractiveEnd);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTextLogger::MoveHome(bool aSelect)
|
|
|
|
{
|
2022-06-21 11:13:30 +02:00
|
|
|
const Coordinates oldPos = m_State.m_CursorPosition;
|
2022-06-20 10:14:43 +02:00
|
|
|
SetCursorPosition(Coordinates(m_State.m_CursorPosition.m_nLine, 0));
|
|
|
|
|
|
|
|
if (m_State.m_CursorPosition != oldPos)
|
|
|
|
{
|
|
|
|
if (aSelect)
|
|
|
|
{
|
|
|
|
if (oldPos == m_InteractiveStart)
|
|
|
|
m_InteractiveStart = m_State.m_CursorPosition;
|
|
|
|
else if (oldPos == m_InteractiveEnd)
|
|
|
|
m_InteractiveEnd = m_State.m_CursorPosition;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_InteractiveStart = m_State.m_CursorPosition;
|
|
|
|
m_InteractiveEnd = oldPos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
m_InteractiveStart = m_InteractiveEnd = m_State.m_CursorPosition;
|
|
|
|
SetSelection(m_InteractiveStart, m_InteractiveEnd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTextLogger::MoveEnd(bool aSelect)
|
|
|
|
{
|
2022-06-21 11:13:30 +02:00
|
|
|
const Coordinates oldPos = m_State.m_CursorPosition;
|
2022-06-20 10:14:43 +02:00
|
|
|
SetCursorPosition(Coordinates(m_State.m_CursorPosition.m_nLine, GetLineMaxColumn(oldPos.m_nLine)));
|
|
|
|
|
|
|
|
if (m_State.m_CursorPosition != oldPos)
|
|
|
|
{
|
|
|
|
if (aSelect)
|
|
|
|
{
|
|
|
|
if (oldPos == m_InteractiveEnd)
|
|
|
|
m_InteractiveEnd = m_State.m_CursorPosition;
|
|
|
|
else if (oldPos == m_InteractiveStart)
|
|
|
|
m_InteractiveStart = m_State.m_CursorPosition;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_InteractiveStart = oldPos;
|
|
|
|
m_InteractiveEnd = m_State.m_CursorPosition;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
m_InteractiveStart = m_InteractiveEnd = m_State.m_CursorPosition;
|
|
|
|
SetSelection(m_InteractiveStart, m_InteractiveEnd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTextLogger::SelectWordUnderCursor()
|
|
|
|
{
|
2022-06-21 11:13:30 +02:00
|
|
|
const Coordinates c = GetCursorPosition();
|
2022-06-20 10:14:43 +02:00
|
|
|
SetSelection(FindWordStart(c), FindWordEnd(c));
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTextLogger::SelectAll()
|
|
|
|
{
|
2022-06-22 00:33:12 +02:00
|
|
|
SetSelection(Coordinates(0, 0), Coordinates(static_cast<int>(m_Lines.size()), 0));
|
2022-06-20 10:14:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CTextLogger::HasSelection() const
|
|
|
|
{
|
|
|
|
return m_State.m_SelectionEnd > m_State.m_SelectionStart;
|
|
|
|
}
|
|
|
|
|
2022-06-26 16:47:00 +02:00
|
|
|
void CTextLogger::MoveSelection(int aLines, bool aForward)
|
|
|
|
{
|
|
|
|
assert(aLines > 0);
|
|
|
|
|
|
|
|
if (aLines < 1)
|
|
|
|
return;
|
|
|
|
|
2022-08-30 22:54:34 +02:00
|
|
|
m_bLinesOffsetForward = aForward;
|
|
|
|
m_nLinesOffsetAmount = aLines;
|
|
|
|
|
2022-06-26 16:47:00 +02:00
|
|
|
if (HasSelection())
|
|
|
|
{
|
|
|
|
Coordinates newStart;
|
|
|
|
Coordinates newEnd;
|
|
|
|
|
2022-08-30 22:54:34 +02:00
|
|
|
newStart = m_State.m_SelectionStart;
|
|
|
|
newEnd = m_State.m_SelectionEnd;
|
|
|
|
|
2022-06-26 16:47:00 +02:00
|
|
|
if (aForward)
|
|
|
|
{
|
|
|
|
newStart.m_nLine += aLines;
|
|
|
|
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;
|
2022-08-30 21:28:26 +02:00
|
|
|
newEnd.m_nColumn = GetLineMaxColumn(newEnd.m_nLine);
|
2022-06-26 16:47:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
newStart.m_nLine -= aLines;
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-20 10:17:11 +02:00
|
|
|
std::string CTextLogger::GetText() const
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
2022-06-22 00:33:12 +02:00
|
|
|
return GetText(Coordinates(), Coordinates(static_cast<int>(m_Lines.size()), 0));
|
2022-06-20 10:14:43 +02:00
|
|
|
}
|
|
|
|
|
2022-06-20 10:17:11 +02:00
|
|
|
std::vector<std::string> CTextLogger::GetTextLines() const
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
2022-06-20 10:17:11 +02:00
|
|
|
std::vector<std::string> result;
|
|
|
|
result.reserve(m_Lines.size());
|
2022-06-20 10:14:43 +02:00
|
|
|
|
2022-06-21 11:13:30 +02:00
|
|
|
for (const Line& line : m_Lines)
|
2022-06-20 10:17:11 +02:00
|
|
|
{
|
2024-02-28 19:16:31 +01:00
|
|
|
result.emplace_back(line.buffer);
|
2022-06-20 10:14:43 +02:00
|
|
|
}
|
|
|
|
|
2022-06-20 10:17:11 +02:00
|
|
|
return result;
|
2022-06-20 10:14:43 +02:00
|
|
|
}
|
|
|
|
|
2022-06-20 10:17:11 +02:00
|
|
|
std::string CTextLogger::GetSelectedText() const
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
2022-06-20 10:17:11 +02:00
|
|
|
return GetText(m_State.m_SelectionStart, m_State.m_SelectionEnd);
|
2022-06-20 10:14:43 +02:00
|
|
|
}
|
|
|
|
|
2022-06-20 10:17:11 +02:00
|
|
|
std::string CTextLogger::GetCurrentLineText()const
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
2022-06-21 11:13:30 +02:00
|
|
|
int lineLength = GetLineMaxColumn(m_State.m_CursorPosition.m_nLine);
|
2022-06-20 10:17:11 +02:00
|
|
|
return GetText(
|
|
|
|
Coordinates(m_State.m_CursorPosition.m_nLine, 0),
|
|
|
|
Coordinates(m_State.m_CursorPosition.m_nLine, lineLength));
|
2022-06-20 20:21:52 +02:00
|
|
|
}
|
|
|
|
|
2022-06-20 10:14:43 +02:00
|
|
|
float CTextLogger::TextDistanceToLineStart(const Coordinates& aFrom) const
|
|
|
|
{
|
2022-06-21 11:13:30 +02:00
|
|
|
const Line& line = m_Lines[aFrom.m_nLine];
|
2022-06-20 10:14:43 +02:00
|
|
|
float distance = 0.0f;
|
|
|
|
float spaceSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ", nullptr, nullptr).x;
|
|
|
|
int colIndex = GetCharacterIndex(aFrom);
|
2024-02-28 19:16:31 +01:00
|
|
|
|
|
|
|
for (size_t it = 0u; it < line.Length() && it < colIndex; )
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
2024-02-28 19:16:31 +01:00
|
|
|
if (line.buffer[it] == '\t')
|
2022-06-20 10:14:43 +02:00
|
|
|
{
|
2024-02-28 19:16:31 +01:00
|
|
|
distance = (1.0f + ImFloor((1.0f + distance) / (float(m_nTabSize) * spaceSize))) * (float(m_nTabSize) * spaceSize);
|
2022-06-20 10:14:43 +02:00
|
|
|
++it;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2024-02-28 19:16:31 +01:00
|
|
|
size_t d = UTF8CharLength(line.buffer[it]);
|
2023-04-09 22:35:55 +02:00
|
|
|
size_t i = 0;
|
2022-06-20 10:14:43 +02:00
|
|
|
char tempCString[7];
|
2023-04-09 22:35:55 +02:00
|
|
|
|
2024-02-28 19:16:31 +01:00
|
|
|
for (; i < 6 && d-- > 0 && it < line.Length(); i++, it++)
|
|
|
|
tempCString[i] = line.buffer[it];
|
2022-06-20 10:14:43 +02:00
|
|
|
|
|
|
|
tempCString[i] = '\0';
|
|
|
|
distance += ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, tempCString, nullptr, nullptr).x;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return distance;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTextLogger::EnsureCursorVisible()
|
|
|
|
{
|
2022-06-21 23:25:15 +02:00
|
|
|
m_bScrollToCursor = true;
|
2022-06-26 16:47:00 +02:00
|
|
|
Coordinates pos = GetActualCursorCoordinates();
|
2022-06-20 10:14:43 +02:00
|
|
|
|
|
|
|
float scrollX = ImGui::GetScrollX();
|
|
|
|
float scrollY = ImGui::GetScrollY();
|
|
|
|
|
2022-06-21 11:13:30 +02:00
|
|
|
float width = ImGui::GetWindowWidth();
|
2022-06-26 16:47:00 +02:00
|
|
|
float height = ImGui::GetWindowHeight();
|
2022-06-20 10:14:43 +02:00
|
|
|
|
2024-02-29 01:32:45 +01:00
|
|
|
float top = 1.0f + ImCeil(scrollY / m_CharAdvance.y);
|
|
|
|
float bottom = ImCeil((scrollY + height) / m_CharAdvance.y);
|
2022-06-20 10:14:43 +02:00
|
|
|
|
2024-02-29 01:32:45 +01:00
|
|
|
float left = ImCeil(scrollX / m_CharAdvance.x);
|
|
|
|
float right = ImCeil((scrollX + width) / m_CharAdvance.x);
|
2022-06-20 10:14:43 +02:00
|
|
|
|
2022-06-26 16:47:00 +02:00
|
|
|
if (pos.m_nColumn < left)
|
2024-02-28 19:16:31 +01:00
|
|
|
ImGui::SetScrollX(ImMax(0.0f, (pos.m_nColumn) * m_CharAdvance.x));
|
2022-06-26 16:47:00 +02:00
|
|
|
if (pos.m_nColumn > right - 3)
|
2024-02-28 19:16:31 +01:00
|
|
|
ImGui::SetScrollX(ImMax(0.0f, (pos.m_nColumn + 3) * m_CharAdvance.x - width));
|
2022-06-20 10:14:43 +02:00
|
|
|
if (pos.m_nLine < top)
|
2024-02-28 19:16:31 +01:00
|
|
|
ImGui::SetScrollY(ImMax(0.0f, (pos.m_nLine) * m_CharAdvance.y));
|
2022-06-26 16:47:00 +02:00
|
|
|
if (pos.m_nLine > bottom - 2)
|
2024-02-28 19:16:31 +01:00
|
|
|
ImGui::SetScrollY(ImMax(0.0f, (pos.m_nLine + 2) * m_CharAdvance.y - height));
|
2022-06-20 10:14:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int CTextLogger::GetPageSize() const
|
|
|
|
{
|
2022-06-21 11:13:30 +02:00
|
|
|
float height = ImGui::GetWindowHeight() - 20.0f;
|
2024-02-28 19:16:31 +01:00
|
|
|
return static_cast<int>(ImFloor(height / m_CharAdvance.y));
|
2022-06-20 10:14:43 +02:00
|
|
|
}
|