mirror of
https://github.com/beefytech/Beef.git
synced 2025-07-04 23:36:00 +02:00
Collapsible regions (aka outlining aka code folding)
This commit is contained in:
parent
3dd4212ccd
commit
90735e3bf8
21 changed files with 2518 additions and 277 deletions
|
@ -346,6 +346,7 @@ namespace Beefy
|
|||
return list;
|
||||
}*/
|
||||
|
||||
[DisableChecks]
|
||||
public static int64 DecodeInt64(ref uint8* ptr)
|
||||
{
|
||||
int64 value = 0;
|
||||
|
@ -364,6 +365,7 @@ namespace Beefy
|
|||
return value;
|
||||
}
|
||||
|
||||
[DisableChecks]
|
||||
public static int32 DecodeInt(uint8[] buf, ref int idx)
|
||||
{
|
||||
int32 value = 0;
|
||||
|
@ -383,6 +385,7 @@ namespace Beefy
|
|||
return value;
|
||||
}
|
||||
|
||||
[DisableChecks]
|
||||
public static void EncodeInt(uint8[] buf, ref int idx, int value)
|
||||
{
|
||||
int curValue = value;
|
||||
|
|
|
@ -5,11 +5,49 @@ using System.Text;
|
|||
using Beefy.widgets;
|
||||
using Beefy.gfx;
|
||||
using Beefy.utils;
|
||||
using Beefy.geom;
|
||||
|
||||
namespace Beefy.theme.dark
|
||||
{
|
||||
public class DarkEditWidgetContent : EditWidgetContent
|
||||
{
|
||||
public class Embed
|
||||
{
|
||||
public enum Kind
|
||||
{
|
||||
LineStart,
|
||||
HideLine,
|
||||
LineEnd,
|
||||
}
|
||||
|
||||
public Kind mKind;
|
||||
|
||||
public ~this()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void Draw(Graphics g, Rect rect, bool hideLine)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void MouseDown(float x, float y, int btn, int btnCount)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual float GetWidth(bool hideLine)
|
||||
{
|
||||
return GS!(24);
|
||||
}
|
||||
}
|
||||
|
||||
public class Data : EditWidgetContent.Data
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Font mFont;
|
||||
public uint32[] mTextColors = sDefaultColors;
|
||||
public uint32 mHiliteColor = 0xFF2f5c88;
|
||||
|
@ -23,6 +61,7 @@ namespace Beefy.theme.dark
|
|||
public uint32 mViewWhiteSpaceColor;
|
||||
public bool mScrollToStartOnLostFocus;
|
||||
public bool mHiliteCurrentLine;
|
||||
public Dictionary<int, Embed> mEmbeds = new .() ~ DeleteDictionaryAndValues!(_);
|
||||
|
||||
protected static uint32[] sDefaultColors = new uint32[] ( Color.White ) ~ delete _;
|
||||
|
||||
|
@ -39,6 +78,48 @@ namespace Beefy.theme.dark
|
|||
mFont = DarkTheme.sDarkTheme?.mSmallFont;
|
||||
}
|
||||
|
||||
protected override EditWidgetContent.Data CreateEditData()
|
||||
{
|
||||
return new Data();
|
||||
}
|
||||
|
||||
public virtual void CheckLineCoords()
|
||||
{
|
||||
if (mLineCoordTextVersionId == mData.mCurTextVersionId)
|
||||
return;
|
||||
|
||||
mLineCoordTextVersionId = mData.mCurTextVersionId;
|
||||
|
||||
if (mLineCoords == null)
|
||||
mLineCoords = new .();
|
||||
if (mLineCoordJumpTable == null)
|
||||
mLineCoordJumpTable = new .();
|
||||
|
||||
mLineCoords.Clear();
|
||||
mLineCoords.GrowUnitialized(mData.mLineStarts.Count);
|
||||
mLineCoordJumpTable.Clear();
|
||||
|
||||
float fontHeight = mFont.GetLineSpacing();
|
||||
int prevJumpIdx = -1;
|
||||
float jumpCoordSpacing = GetJumpCoordSpacing();
|
||||
|
||||
double curY = 0;
|
||||
for (int line < mData.mLineStarts.Count)
|
||||
{
|
||||
float lineHeight = fontHeight;
|
||||
mLineCoords[line] = (float)curY;
|
||||
|
||||
int jumpIdx = (.)(curY / jumpCoordSpacing);
|
||||
while (prevJumpIdx < jumpIdx)
|
||||
{
|
||||
mLineCoordJumpTable.Add(((int32)line, (int32)line + 1));
|
||||
prevJumpIdx++;
|
||||
}
|
||||
mLineCoordJumpTable[jumpIdx].max = (.)line + 1;
|
||||
curY += lineHeight;
|
||||
}
|
||||
}
|
||||
|
||||
public override void GetTextData()
|
||||
{
|
||||
// Generate text flags if we need to...
|
||||
|
@ -97,8 +178,56 @@ namespace Beefy.theme.dark
|
|||
}
|
||||
|
||||
base.GetTextData();
|
||||
|
||||
CheckLineCoords();
|
||||
}
|
||||
|
||||
public int32 mLineCoordTextVersionId = -1;
|
||||
public List<float> mLineCoords ~ delete _;
|
||||
public List<(int32 min, int32 max)> mLineCoordJumpTable ~ delete _;
|
||||
|
||||
public bool IsLineCollapsed(int line)
|
||||
{
|
||||
if (mLineCoords == null)
|
||||
return false;
|
||||
if ((line >= 0) && (line < mLineCoords.Count - 1))
|
||||
return (mLineCoords[line + 1] - mLineCoords[line]) < 0.1f;
|
||||
return false;
|
||||
}
|
||||
|
||||
public float GetLineHeight(int line, float defaultVal)
|
||||
{
|
||||
if ((line >= 0) && (line < mLineCoords.Count - 1))
|
||||
return mLineCoords[line + 1] - mLineCoords[line];
|
||||
return defaultVal;
|
||||
}
|
||||
|
||||
public float GetLineY(int line, float defaultVal)
|
||||
{
|
||||
if ((line >= 0) && (line < mLineCoords.Count))
|
||||
return mLineCoords[line];
|
||||
return defaultVal;
|
||||
}
|
||||
|
||||
public int FindUncollapsedLine(int line)
|
||||
{
|
||||
var line;
|
||||
while ((line > 0) && (IsLineCollapsed(line)))
|
||||
line--;
|
||||
return line;
|
||||
}
|
||||
|
||||
public bool IsInCollapseGroup(int anchorLine, int checkLine)
|
||||
{
|
||||
if (checkLine < anchorLine)
|
||||
return false;
|
||||
if (checkLine == anchorLine)
|
||||
return true;
|
||||
if (checkLine == anchorLine + 1)
|
||||
return IsLineCollapsed(checkLine);
|
||||
return mLineCoords[anchorLine + 1] == mLineCoords[checkLine + 1];
|
||||
}
|
||||
|
||||
protected override void AdjustCursorsAfterExternalEdit(int index, int ofs)
|
||||
{
|
||||
base.AdjustCursorsAfterExternalEdit(index, ofs);
|
||||
|
@ -322,6 +451,8 @@ namespace Beefy.theme.dark
|
|||
Utils.RoundScale(ref mTabSize, newScale / oldScale);
|
||||
SetFont(mFont, mCharWidth != -1, mAllowVirtualCursor);
|
||||
mContentChanged = true; // Defer calling of RecalcSize
|
||||
GetTextData();
|
||||
LineStartsChanged();
|
||||
}
|
||||
|
||||
public virtual float DrawText(Graphics g, String str, float x, float y, uint16 typeIdAndFlags)
|
||||
|
@ -335,15 +466,52 @@ namespace Beefy.theme.dark
|
|||
return mEditWidget.mHasFocus ? mHiliteColor : mUnfocusedHiliteColor;
|
||||
}
|
||||
|
||||
protected Rect GetEmbedRect(int lineIdx, DarkEditWidgetContent.Embed embed)
|
||||
{
|
||||
GetLinePosition(lineIdx, var lineStart, var lineEnd);
|
||||
|
||||
bool hideLine = false;
|
||||
if ((embed.mKind == .HideLine) &&
|
||||
((!mEditWidget.mHasFocus) || (!IsInCollapseGroup(lineIdx, CursorLine))))
|
||||
hideLine = true;
|
||||
|
||||
int wsEnd = lineEnd;
|
||||
if ((hideLine) || (embed.mKind == .LineStart))
|
||||
{
|
||||
for (int i in lineStart..<lineEnd)
|
||||
{
|
||||
var checkEnd = ref mData.mText[i];
|
||||
if (!checkEnd.mChar.IsWhiteSpace)
|
||||
{
|
||||
wsEnd = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String str = scope .(256);
|
||||
ExtractString(lineStart, wsEnd - lineStart, str);
|
||||
|
||||
float selStartX = GetTabbedWidth(str, 0);
|
||||
|
||||
if ((embed.mKind == .LineEnd) ||
|
||||
((embed.mKind == .HideLine) && (!hideLine)))
|
||||
selStartX += GS!(4);
|
||||
|
||||
Rect rect = .(selStartX, mLineCoords[lineIdx] - GS!(2), embed.GetWidth(hideLine), mFont.GetLineSpacing() + GS!(4));
|
||||
if (rect.mY < 0)
|
||||
rect.mY = 0;
|
||||
return rect;
|
||||
}
|
||||
|
||||
public override void Draw(Graphics g)
|
||||
{
|
||||
base.Draw(g);
|
||||
|
||||
|
||||
|
||||
#unwarn
|
||||
int lineCount = GetLineCount();
|
||||
float lineSpacing = GetLineHeight(0);
|
||||
|
||||
g.SetFont(mFont);
|
||||
float lineSpacing = mFont.GetLineSpacing();
|
||||
|
||||
float offsetY = mTextInsets.mTop;
|
||||
if (mHeight < lineSpacing)
|
||||
|
@ -376,7 +544,50 @@ namespace Beefy.theme.dark
|
|||
float lastOverflowX;
|
||||
GetLineCharAtCoord(0, -mY + mEditWidget.mScrollContentContainer.mHeight, out lastLine, out lastCharIdx, out lastOverflowX);
|
||||
|
||||
bool drewCursor = false;
|
||||
bool drewCursor = false;
|
||||
|
||||
void DrawCursor(float x, float y)
|
||||
{
|
||||
if (mHiliteCurrentLine && selStartIdx == selEndIdx)
|
||||
{
|
||||
float thickness = 2 * (lineSpacing / 18);
|
||||
// This isn't quite the right value, but I'm not sure how to get this
|
||||
// to properly highlight the whole line without getting cut off - this works well for now.
|
||||
float totalLineWidth = mEditWidget.mScrollContentContainer.mWidth - thickness;
|
||||
|
||||
float hiliteX = (int)mEditWidget.mHorzPos.v; // If we don't round to int we get jitter while scrolling.
|
||||
using (g.PushColor(DarkTheme.COLOR_CURRENT_LINE_HILITE))
|
||||
g.OutlineRect(hiliteX, y, totalLineWidth, lineSpacing + thickness, thickness);
|
||||
}
|
||||
|
||||
float brightness = (float)Math.Cos(Math.Max(0.0f, mCursorBlinkTicks - 20) / 9.0f);
|
||||
brightness = Math.Clamp(brightness * 2.0f + 1.6f, 0, 1);
|
||||
if (mEditWidget.mVertPos.IsMoving)
|
||||
brightness = 0; // When we animate a pgup or pgdn, it's weird seeing the cursor scrolling around
|
||||
|
||||
Color cursorColor = mTextColors[0];
|
||||
|
||||
if (mOverTypeMode)
|
||||
{
|
||||
if (mCharWidth <= 2)
|
||||
{
|
||||
using (g.PushColor(Color.Mult(cursorColor, Color.Get(brightness * 0.75f))))
|
||||
g.FillRect(x, y, GS!(2), lineSpacing);
|
||||
}
|
||||
else
|
||||
{
|
||||
using (g.PushColor(Color.Mult(cursorColor, Color.Get(brightness * 0.30f))))
|
||||
g.FillRect(x, y, mCharWidth, lineSpacing);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using (g.PushColor(Color.Mult(cursorColor, Color.Get(brightness))))
|
||||
g.FillRect(x, y, Math.Max(1.0f, GS!(1)), lineSpacing);
|
||||
}
|
||||
drewCursor = true;
|
||||
}
|
||||
|
||||
String sectionText = scope String(256);
|
||||
for (int lineIdx = firstLine; lineIdx <= lastLine; lineIdx++)
|
||||
{
|
||||
|
@ -387,7 +598,26 @@ namespace Beefy.theme.dark
|
|||
|
||||
int lineDrawStart = lineStart;
|
||||
float curX = 0;
|
||||
float curY = lineIdx * lineSpacing;
|
||||
float curY = mLineCoords[lineIdx];
|
||||
float height = mLineCoords[lineIdx + 1] - curY;
|
||||
if (height <= 1.0f)
|
||||
continue;
|
||||
|
||||
DarkEditWidgetContent.Embed embed = null;
|
||||
if (mEmbeds.GetValue(lineIdx) case .Ok(out embed))
|
||||
{
|
||||
if ((embed.mKind == .HideLine) &&
|
||||
((!IsInCollapseGroup(lineIdx, CursorLine)) || (!mEditWidget.mHasFocus)))
|
||||
{
|
||||
var embedRect = GetEmbedRect(lineIdx, embed);
|
||||
embed.Draw(g, embedRect, true);
|
||||
g.SetFont(mFont);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
g.SetFont(mFont);
|
||||
|
||||
while (true)
|
||||
{
|
||||
int lineDrawEnd = lineDrawStart;
|
||||
|
@ -481,46 +711,7 @@ namespace Beefy.theme.dark
|
|||
}
|
||||
|
||||
if (aX != -1)
|
||||
{
|
||||
if (mHiliteCurrentLine && selStartIdx == selEndIdx)
|
||||
{
|
||||
float thickness = 2 * (lineSpacing / 18);
|
||||
// This isn't quite the right value, but I'm not sure how to get this
|
||||
// to properly highlight the whole line without getting cut off - this works well for now.
|
||||
float totalLineWidth = mEditWidget.mScrollContentContainer.mWidth - thickness;
|
||||
|
||||
float x = (int)mEditWidget.mHorzPos.v; // If we don't round to int we get jitter while scrolling.
|
||||
using (g.PushColor(DarkTheme.COLOR_CURRENT_LINE_HILITE))
|
||||
g.OutlineRect(x, curY, totalLineWidth, lineSpacing + thickness, thickness);
|
||||
}
|
||||
|
||||
float brightness = (float)Math.Cos(Math.Max(0.0f, mCursorBlinkTicks - 20) / 9.0f);
|
||||
brightness = Math.Clamp(brightness * 2.0f + 1.6f, 0, 1);
|
||||
if (mEditWidget.mVertPos.IsMoving)
|
||||
brightness = 0; // When we animate a pgup or pgdn, it's weird seeing the cursor scrolling around
|
||||
|
||||
Color cursorColor = mTextColors[0];
|
||||
|
||||
if (mOverTypeMode)
|
||||
{
|
||||
if (mCharWidth <= 2)
|
||||
{
|
||||
using (g.PushColor(Color.Mult(cursorColor, Color.Get(brightness * 0.75f))))
|
||||
g.FillRect(aX, curY, GS!(2), lineSpacing);
|
||||
}
|
||||
else
|
||||
{
|
||||
using (g.PushColor(Color.Mult(cursorColor, Color.Get(brightness * 0.30f))))
|
||||
g.FillRect(aX, curY, mCharWidth, lineSpacing);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using (g.PushColor(Color.Mult(cursorColor, Color.Get(brightness))))
|
||||
g.FillRect(aX, curY, Math.Max(1.0f, GS!(1)), lineSpacing);
|
||||
}
|
||||
drewCursor = true;
|
||||
}
|
||||
DrawCursor(aX, curY);
|
||||
}
|
||||
|
||||
lineDrawStart = lineDrawEnd;
|
||||
|
@ -528,7 +719,18 @@ namespace Beefy.theme.dark
|
|||
|
||||
if (lineDrawStart >= lineEnd)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (embed != null)
|
||||
{
|
||||
var embedRect = GetEmbedRect(lineIdx, embed);
|
||||
embed.Draw(g, embedRect, false);
|
||||
g.SetFont(mFont);
|
||||
|
||||
if ((!drewCursor) && (IsLineCollapsed(CursorLineAndColumn.mLine)) &&
|
||||
(GetLineY(CursorLineAndColumn.mLine, -1) == GetLineY(lineIdx + 1, -1)))
|
||||
DrawCursor(embedRect.Right + GS!(2), curY);
|
||||
}
|
||||
}
|
||||
|
||||
g.PopMatrix();
|
||||
|
@ -561,6 +763,8 @@ namespace Beefy.theme.dark
|
|||
|
||||
public override void GetTextCoordAtLineChar(int line, int lineChar, out float x, out float y)
|
||||
{
|
||||
GetTextData();
|
||||
|
||||
String lineText = scope String(256);
|
||||
GetLineText(line, lineText);
|
||||
if (lineChar > lineText.Length)
|
||||
|
@ -571,74 +775,95 @@ namespace Beefy.theme.dark
|
|||
subText.Append(lineText, 0, lineChar);
|
||||
x = GetTabbedWidth(subText, 0, true) + mTextInsets.mLeft;
|
||||
}
|
||||
y = mTextInsets.mTop + line * mFont.GetLineSpacing();
|
||||
y = mTextInsets.mTop + mLineCoords[line];
|
||||
}
|
||||
|
||||
public override void GetTextCoordAtLineAndColumn(int line, int column, out float x, out float y)
|
||||
{
|
||||
GetTextData();
|
||||
Debug.Assert((mCharWidth != -1) || (column == 0));
|
||||
String lineText = scope String(256);
|
||||
GetLineText(line, lineText);
|
||||
x = mTextInsets.mLeft + column * mCharWidth;
|
||||
y = mTextInsets.mTop + line * mFont.GetLineSpacing();
|
||||
y = mTextInsets.mTop + mLineCoords[line];
|
||||
}
|
||||
|
||||
public override bool GetLineCharAtCoord(float x, float y, out int line, out int char8Idx, out float overflowX)
|
||||
protected int GetLineAt(float y)
|
||||
{
|
||||
if (y < 0)
|
||||
return 0;
|
||||
|
||||
GetTextData();
|
||||
|
||||
int lineCount = GetLineCount();
|
||||
var checkY = y - mTextInsets.mTop + 0.001f;
|
||||
var jumpEntry = mLineCoordJumpTable[Math.Clamp((int)(y / GetJumpCoordSpacing()), 0, mLineCoordJumpTable.Count - 1)];
|
||||
|
||||
int line = jumpEntry.min - 1;
|
||||
for (int checkLine in jumpEntry.min ..< jumpEntry.max)
|
||||
{
|
||||
if (checkY >= mLineCoords[checkLine])
|
||||
line = checkLine;
|
||||
}
|
||||
|
||||
if (line < 0)
|
||||
line = 0;
|
||||
if (line >= lineCount)
|
||||
line = lineCount - 1;
|
||||
return line;
|
||||
}
|
||||
|
||||
public override bool GetLineCharAtCoord(float x, float y, out int line, out int lineChar, out float overflowX)
|
||||
{
|
||||
line = (int) ((y - mTextInsets.mTop) / mFont.GetLineSpacing() + 0.001f);
|
||||
int lineCount = GetLineCount();
|
||||
line = GetLineAt(y);
|
||||
return GetLineCharAtCoord(line, x, out lineChar, out overflowX);
|
||||
}
|
||||
|
||||
if (line < 0)
|
||||
line = 0;
|
||||
if (line >= lineCount)
|
||||
line = lineCount - 1;
|
||||
public override bool GetLineAndColumnAtCoord(float x, float y, out int line, out int column)
|
||||
{
|
||||
line = GetLineAt(y);
|
||||
column = Math.Max(0, (int32)((x - mTextInsets.mLeft + 1) / mCharWidth + 0.6f));
|
||||
return mCharWidth != -1;
|
||||
}
|
||||
|
||||
String lineText = scope String(256);
|
||||
GetLineText(line, lineText);
|
||||
int32 char8Count = GetTabbedCharCountToLength(lineText, x - mTextInsets.mLeft);
|
||||
char8Idx = char8Count;
|
||||
public override bool GetLineCharAtCoord(int line, float x, out int lineChar, out float overflowX)
|
||||
{
|
||||
String lineText = scope String(256);
|
||||
GetLineText(line, lineText);
|
||||
int32 char8Count = GetTabbedCharCountToLength(lineText, x - mTextInsets.mLeft);
|
||||
lineChar = char8Count;
|
||||
|
||||
if (char8Count < lineText.Length)
|
||||
{
|
||||
if (char8Count < lineText.Length)
|
||||
{
|
||||
String subString = new:ScopedAlloc! String(char8Count);
|
||||
subString.Append(lineText, 0, char8Count);
|
||||
float subWidth = GetTabbedWidth(subString, 0);
|
||||
float subWidth = GetTabbedWidth(subString, 0);
|
||||
|
||||
var utf8enumerator = lineText.DecodedChars(char8Count);
|
||||
if (utf8enumerator.MoveNext())
|
||||
{
|
||||
char32 c = utf8enumerator.Current;
|
||||
float checkCharWidth = 0;
|
||||
if (c == '\t')
|
||||
checkCharWidth = mTabSize * 0.5f;
|
||||
else
|
||||
float checkCharWidth = 0;
|
||||
if (c == '\t')
|
||||
checkCharWidth = mTabSize * 0.5f;
|
||||
else
|
||||
{
|
||||
checkCharWidth = mFont.GetWidth(c) * 0.5f;
|
||||
}
|
||||
|
||||
if (x >= subWidth + mTextInsets.mLeft + checkCharWidth)
|
||||
char8Idx = (int32)utf8enumerator.NextIndex;
|
||||
if (x >= subWidth + mTextInsets.mLeft + checkCharWidth)
|
||||
lineChar = (int32)utf8enumerator.NextIndex;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
overflowX = (x - mTextInsets.mLeft) - (GetTabbedWidth(lineText, 0) + 0.001f);
|
||||
return overflowX <= 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
overflowX = (x - mTextInsets.mLeft) - (GetTabbedWidth(lineText, 0) + 0.001f);
|
||||
return overflowX <= 0;
|
||||
}
|
||||
|
||||
overflowX = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool GetLineAndColumnAtCoord(float x, float y, out int line, out int column)
|
||||
{
|
||||
line = (int32)((y - mTextInsets.mTop) / mFont.GetLineSpacing() + 0.001f);
|
||||
if (line >= GetLineCount())
|
||||
line = GetLineCount() - 1;
|
||||
line = Math.Max(0, line);
|
||||
column = Math.Max(0, (int32)((x - mTextInsets.mLeft + 1) / mCharWidth + 0.6f));
|
||||
return mCharWidth != -1;
|
||||
}
|
||||
overflowX = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void RecalcSize(int32 startLineNum, int32 endLineNum, bool forceAccurate = false)
|
||||
{
|
||||
|
@ -670,6 +895,53 @@ namespace Beefy.theme.dark
|
|||
base.CursorToLineEnd();
|
||||
}
|
||||
|
||||
public override void GetTextCoordAtCursor(out float x, out float y)
|
||||
{
|
||||
int32 line = CursorLine;
|
||||
if (IsLineCollapsed(line))
|
||||
{
|
||||
line = (.)FindUncollapsedLine(line);
|
||||
GetTextCoordAtLineAndColumn(line, CursorLineAndColumn.mColumn, out x, out y);
|
||||
return;
|
||||
}
|
||||
|
||||
base.GetTextCoordAtCursor(out x, out y);
|
||||
}
|
||||
|
||||
public override void MoveCursorToCoord(float x, float y)
|
||||
{
|
||||
base.MoveCursorToCoord(x, y);
|
||||
|
||||
if ((!mEmbeds.IsEmpty) && (mEmbeds.GetValue(CursorLineAndColumn.mLine) case .Ok(let embed)))
|
||||
{
|
||||
var embedRect = GetEmbedRect(CursorLineAndColumn.mLine, embed);
|
||||
if (x > embedRect.Right)
|
||||
{
|
||||
int endLine = CursorLineAndColumn.mLine;
|
||||
while (true)
|
||||
{
|
||||
if (!IsLineCollapsed(endLine + 1))
|
||||
break;
|
||||
endLine++;
|
||||
}
|
||||
|
||||
if (endLine != CursorLineAndColumn.mLine)
|
||||
{
|
||||
GetLinePosition(endLine, var endLineStart, var endLineEnd);
|
||||
GetLineAndColumnAtLineChar(endLine, endLineEnd - endLineStart, var column);
|
||||
CursorLineAndColumn = .(endLine, column);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void CursorToLineStart(bool allowToScreenStart)
|
||||
{
|
||||
while (IsLineCollapsed(CursorLineAndColumn.mLine))
|
||||
CursorLineAndColumn = .(CursorLineAndColumn.mLine - 1, 1);
|
||||
base.CursorToLineStart(allowToScreenStart);
|
||||
}
|
||||
|
||||
public void RecalcSize(bool forceAccurate = false)
|
||||
{
|
||||
mMaximalScrollAddedHeight = 0;
|
||||
|
@ -707,7 +979,9 @@ namespace Beefy.theme.dark
|
|||
Debug.Assert(!mWidth.IsNaN);
|
||||
}
|
||||
|
||||
mHeight = GetLineCount() * mFont.GetLineSpacing() + mTextInsets.mTop + mTextInsets.mBottom;
|
||||
GetTextData();
|
||||
mHeight = mLineCoords.Back + mTextInsets.mTop + mTextInsets.mBottom;
|
||||
Debug.Assert(mHeight > 0);
|
||||
UpdateMaximalScroll();
|
||||
base.RecalcSize();
|
||||
}
|
||||
|
@ -721,6 +995,21 @@ namespace Beefy.theme.dark
|
|||
{
|
||||
base.ContentChanged();
|
||||
mRecalcSizeLineNum = -1;
|
||||
|
||||
mLineCoordTextVersionId = -1;
|
||||
DeleteAndNullify!(mLineCoords);
|
||||
DeleteAndNullify!(mLineCoordJumpTable);
|
||||
}
|
||||
|
||||
public float GetJumpCoordSpacing()
|
||||
{
|
||||
return mFont.GetLineSpacing() * 4;
|
||||
}
|
||||
|
||||
public override void LineStartsChanged()
|
||||
{
|
||||
base.LineStartsChanged();
|
||||
mLineCoordTextVersionId = -1;
|
||||
}
|
||||
|
||||
public override void TextAppended(String str)
|
||||
|
@ -734,7 +1023,7 @@ namespace Beefy.theme.dark
|
|||
base.TextAppended(str);
|
||||
}
|
||||
|
||||
void UpdateMaximalScroll()
|
||||
protected void UpdateMaximalScroll()
|
||||
{
|
||||
if (mAllowMaximalScroll)
|
||||
{
|
||||
|
@ -744,6 +1033,8 @@ namespace Beefy.theme.dark
|
|||
mMaximalScrollAddedHeight = mEditWidget.mScrollContentContainer.mHeight - mFont.GetLineSpacing();
|
||||
mHeight += mMaximalScrollAddedHeight;
|
||||
|
||||
Debug.Assert(mHeight >= 0);
|
||||
|
||||
if (mHeight != prevHeight)
|
||||
mEditWidget.UpdateScrollbars();
|
||||
}
|
||||
|
@ -757,7 +1048,9 @@ namespace Beefy.theme.dark
|
|||
|
||||
public override float GetLineHeight(int line)
|
||||
{
|
||||
return mFont.GetLineSpacing();
|
||||
if (mLineCoords == null)
|
||||
GetTextData();
|
||||
return mLineCoords[line + 1] - mLineCoords[line];
|
||||
}
|
||||
|
||||
public override float GetPageScrollTextHeight()
|
||||
|
@ -843,6 +1136,27 @@ namespace Beefy.theme.dark
|
|||
|
||||
CheckRecordScrollTop();
|
||||
}
|
||||
|
||||
public override void MouseMove(float x, float y)
|
||||
{
|
||||
base.MouseMove(x, y);
|
||||
|
||||
int line = GetLineAt(y);
|
||||
|
||||
bool isOverEmbed = false;
|
||||
|
||||
if (mEmbeds.GetValue(line) case .Ok(let embed))
|
||||
{
|
||||
Rect embedRect = GetEmbedRect(line, embed);
|
||||
if (embedRect.Contains(x, y))
|
||||
isOverEmbed = true;
|
||||
}
|
||||
|
||||
if (isOverEmbed)
|
||||
BFApp.sApp.SetCursor(Cursor.Pointer);
|
||||
else
|
||||
BFApp.sApp.SetCursor(Cursor.Text);
|
||||
}
|
||||
}
|
||||
|
||||
public class DarkEditWidget : EditWidget
|
||||
|
|
|
@ -181,6 +181,8 @@ namespace Beefy.theme.dark
|
|||
PanelHeader,
|
||||
|
||||
ExtMethod,
|
||||
CollapseClosed,
|
||||
CollapseOpened,
|
||||
|
||||
COUNT
|
||||
};
|
||||
|
|
|
@ -6,8 +6,129 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace Beefy.utils
|
||||
{
|
||||
public struct IdSpan
|
||||
public struct IdSpan : IFormattable
|
||||
{
|
||||
public class LookupContext
|
||||
{
|
||||
public enum SortKind
|
||||
{
|
||||
None,
|
||||
Id,
|
||||
Index
|
||||
}
|
||||
|
||||
public struct Entry
|
||||
{
|
||||
public int32 mIdStart;
|
||||
public int32 mIndexStart;
|
||||
public int32 mLength;
|
||||
}
|
||||
|
||||
public List<Entry> mEntries = new .() ~ delete _;
|
||||
public SortKind mSortKind;
|
||||
|
||||
public this(IdSpan idSpan)
|
||||
{
|
||||
int encodeIdx = 0;
|
||||
int charId = 1;
|
||||
int charIdx = 0;
|
||||
while (true)
|
||||
{
|
||||
int cmd = Utils.DecodeInt(idSpan.mData, ref encodeIdx);
|
||||
if (cmd > 0)
|
||||
charId = cmd;
|
||||
else
|
||||
{
|
||||
int spanSize = -cmd;
|
||||
|
||||
Entry entry;
|
||||
entry.mIdStart = (.)charId;
|
||||
entry.mIndexStart = (.)charIdx;
|
||||
entry.mLength = (.)spanSize;
|
||||
mEntries.Add(entry);
|
||||
|
||||
charId += spanSize;
|
||||
charIdx += spanSize;
|
||||
|
||||
if (cmd == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int GetIndexFromId(int32 findCharId)
|
||||
{
|
||||
if (mSortKind != .Id)
|
||||
{
|
||||
mEntries.Sort(scope (lhs, rhs) => lhs.mIdStart <=> rhs.mIdStart);
|
||||
mSortKind = .Id;
|
||||
}
|
||||
|
||||
int lo = 0;
|
||||
int hi = mEntries.Count - 1;
|
||||
|
||||
while (lo <= hi)
|
||||
{
|
||||
int i = (lo + hi) / 2;
|
||||
var midVal = ref mEntries[i];
|
||||
if ((findCharId >= midVal.mIdStart) && (findCharId < midVal.mIdStart + midVal.mLength))
|
||||
return midVal.mIndexStart + (findCharId - midVal.mIdStart);
|
||||
|
||||
if (findCharId > midVal.mIdStart)
|
||||
lo = i + 1;
|
||||
else
|
||||
hi = i - 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int32 GetIdAtIndex(int32 findCharId)
|
||||
{
|
||||
if (mSortKind != .Index)
|
||||
{
|
||||
mEntries.Sort(scope (lhs, rhs) => lhs.mIndexStart <=> rhs.mIndexStart);
|
||||
mSortKind = .Index;
|
||||
}
|
||||
|
||||
int lo = 0;
|
||||
int hi = mEntries.Count - 1;
|
||||
|
||||
while (lo <= hi)
|
||||
{
|
||||
int i = (lo + hi) / 2;
|
||||
var midVal = ref mEntries[i];
|
||||
if ((findCharId >= midVal.mIndexStart) && (findCharId < midVal.mIndexStart + midVal.mLength))
|
||||
return midVal.mIdStart + (findCharId - midVal.mIndexStart);
|
||||
|
||||
if (findCharId > midVal.mIndexStart)
|
||||
lo = i + 1;
|
||||
else
|
||||
hi = i - 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public override void ToString(String strBuffer)
|
||||
{
|
||||
if (mSortKind != .Index)
|
||||
{
|
||||
mEntries.Sort(scope (lhs, rhs) => lhs.mIndexStart <=> rhs.mIndexStart);
|
||||
mSortKind = .Index;
|
||||
}
|
||||
|
||||
strBuffer.AppendF("IdSpan.LookupCtx(");
|
||||
for (var entry in mEntries)
|
||||
{
|
||||
if (@entry.Index > 0)
|
||||
strBuffer.Append(' ');
|
||||
strBuffer.AppendF($"{entry.mIndexStart}:{entry.mLength}={entry.mIdStart}");
|
||||
}
|
||||
strBuffer.AppendF(")");
|
||||
}
|
||||
}
|
||||
|
||||
enum Change
|
||||
{
|
||||
case Insert(int32 index, int32 id, int16 length);
|
||||
|
@ -671,7 +792,6 @@ namespace Beefy.utils
|
|||
|
||||
public IdSpan Duplicate()
|
||||
{
|
||||
Debug.Assert(!HasChangeList);
|
||||
IdSpan idSpan = IdSpan();
|
||||
if (mData != null)
|
||||
{
|
||||
|
@ -679,6 +799,12 @@ namespace Beefy.utils
|
|||
mData.CopyTo(idSpan.mData, 0, 0, mLength);
|
||||
idSpan.mLength = mLength;
|
||||
}
|
||||
if (mChangeList != null)
|
||||
{
|
||||
idSpan.mChangeList = new .();
|
||||
for (var change in mChangeList)
|
||||
idSpan.mChangeList.Add(change);
|
||||
}
|
||||
return idSpan;
|
||||
}
|
||||
|
||||
|
@ -866,6 +992,11 @@ namespace Beefy.utils
|
|||
return idSpan;
|
||||
}
|
||||
|
||||
public override void ToString(String strBuffer)
|
||||
{
|
||||
ToString(strBuffer, "", null);
|
||||
}
|
||||
|
||||
public void Dump() mut
|
||||
{
|
||||
Prepare();
|
||||
|
@ -895,5 +1026,49 @@ namespace Beefy.utils
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ToString(String outString, String format, IFormatProvider formatProvider)
|
||||
{
|
||||
if (HasChangeList)
|
||||
{
|
||||
IdSpan span = Duplicate();
|
||||
span.ToString(outString, format, formatProvider);
|
||||
return;
|
||||
}
|
||||
|
||||
outString.AppendF($"Span(Length:{mLength} ChangeList:{(mChangeList?.Count).GetValueOrDefault()})");
|
||||
|
||||
if (format == "D")
|
||||
{
|
||||
outString.Append("{");
|
||||
int encodeIdx = 0;
|
||||
int charId = 1;
|
||||
int charIdx = 0;
|
||||
while (true)
|
||||
{
|
||||
int32 cmd = Utils.DecodeInt(mData, ref encodeIdx);
|
||||
if (cmd > 0)
|
||||
{
|
||||
charId = cmd;
|
||||
outString.AppendF($" #{charId}");
|
||||
}
|
||||
else
|
||||
{
|
||||
int32 spanSize = -cmd;
|
||||
|
||||
charId += spanSize;
|
||||
charIdx += spanSize;
|
||||
|
||||
if (cmd == 0)
|
||||
{
|
||||
outString.Append("}");
|
||||
return;
|
||||
}
|
||||
|
||||
outString.AppendF($":{spanSize}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -571,16 +571,16 @@ namespace Beefy.widgets
|
|||
get
|
||||
{
|
||||
if (mCursorTextPos == -1)
|
||||
{
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
GetTextCoordAtLineAndColumn(mVirtualCursorPos.Value.mLine, mVirtualCursorPos.Value.mColumn, out x, out y);
|
||||
|
||||
int line;
|
||||
int lineChar;
|
||||
float overflowX;
|
||||
GetLineCharAtCoord(x, y, out line, out lineChar, out overflowX);
|
||||
mCursorTextPos = (int32)GetTextIdx(line, lineChar);
|
||||
GetLineCharAtCoord(mVirtualCursorPos.Value.mLine, x, out lineChar, out overflowX);
|
||||
|
||||
mCursorTextPos = (int32)GetTextIdx(mVirtualCursorPos.Value.mLine, lineChar);
|
||||
}
|
||||
|
||||
return mCursorTextPos;
|
||||
|
@ -606,14 +606,9 @@ namespace Beefy.widgets
|
|||
int lineChar;
|
||||
GetLineCharAtIdx(mCursorTextPos, out line, out lineChar);
|
||||
|
||||
float x;
|
||||
float y;
|
||||
GetTextCoordAtLineChar(line, lineChar, out x, out y);
|
||||
|
||||
int coordLine;
|
||||
int coordLineColumn;
|
||||
GetLineAndColumnAtCoord(x, y, out coordLine, out coordLineColumn);
|
||||
lineAndColumn.mLine = (int32)coordLine;
|
||||
GetLineAndColumnAtLineChar(line, lineChar, out coordLineColumn);
|
||||
lineAndColumn.mLine = (int32)line;
|
||||
lineAndColumn.mColumn = (int32)coordLineColumn;
|
||||
return lineAndColumn;
|
||||
}
|
||||
|
@ -627,6 +622,20 @@ namespace Beefy.widgets
|
|||
}
|
||||
}
|
||||
|
||||
public int32 CursorLine
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mVirtualCursorPos.HasValue)
|
||||
return mVirtualCursorPos.Value.mLine;
|
||||
|
||||
int line;
|
||||
int lineChar;
|
||||
GetLineCharAtIdx(mCursorTextPos, out line, out lineChar);
|
||||
return (.)line;
|
||||
}
|
||||
}
|
||||
|
||||
public bool WantsUndo
|
||||
{
|
||||
get
|
||||
|
@ -644,7 +653,8 @@ namespace Beefy.widgets
|
|||
mData.Ref(this);
|
||||
mContentChanged = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected virtual Data CreateEditData()
|
||||
{
|
||||
return new Data();
|
||||
|
@ -661,13 +671,13 @@ namespace Beefy.widgets
|
|||
{
|
||||
if (mVirtualCursorPos.HasValue)
|
||||
{
|
||||
int32 line = mVirtualCursorPos.Value.mLine;
|
||||
float x;
|
||||
float y;
|
||||
GetTextCoordAtLineAndColumn(mVirtualCursorPos.Value.mLine, mVirtualCursorPos.Value.mColumn, out x, out y);
|
||||
GetTextCoordAtLineAndColumn(line, mVirtualCursorPos.Value.mColumn, out x, out y);
|
||||
|
||||
int line;
|
||||
int lineChar;
|
||||
bool success = GetLineCharAtCoord(x, y, out line, out lineChar, out overflowX);
|
||||
bool success = GetLineCharAtCoord(line, x, out lineChar, out overflowX);
|
||||
|
||||
textPos = GetTextIdx(line, lineChar);
|
||||
return success;
|
||||
|
@ -851,7 +861,7 @@ namespace Beefy.widgets
|
|||
public override void MouseLeave()
|
||||
{
|
||||
base.MouseLeave();
|
||||
BFApp.sApp.SetCursor(Cursor.Pointer);
|
||||
BFApp.sApp.SetCursor(Cursor.Pointer);
|
||||
}
|
||||
|
||||
public virtual void ClearText()
|
||||
|
@ -1344,6 +1354,11 @@ namespace Beefy.widgets
|
|||
TextChanged();
|
||||
}
|
||||
|
||||
public virtual void LineStartsChanged()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void TextAppended(String str)
|
||||
{
|
||||
if ((mWordWrap) || (mData.mLineStarts == null))
|
||||
|
@ -1366,6 +1381,7 @@ namespace Beefy.widgets
|
|||
}
|
||||
}
|
||||
mData.mLineStarts.Add(mData.mTextLength);
|
||||
LineStartsChanged();
|
||||
|
||||
mContentChanged = true;
|
||||
|
||||
|
@ -1385,69 +1401,70 @@ namespace Beefy.widgets
|
|||
{
|
||||
// Generate line starts and text flags if we need to
|
||||
|
||||
if (mData.mLineStarts == null)
|
||||
{
|
||||
scope AutoBeefPerf("EWC.GetTextData");
|
||||
if (mData.mLineStarts != null)
|
||||
return;
|
||||
|
||||
scope AutoBeefPerf("EWC.GetTextData");
|
||||
|
||||
CharData* char8DataPtr = mData.mText.CArray();
|
||||
uint8* textFlagsPtr = null;
|
||||
if (mData.mTextFlags != null)
|
||||
textFlagsPtr = mData.mTextFlags.CArray();
|
||||
CharData* char8DataPtr = mData.mText.CArray();
|
||||
uint8* textFlagsPtr = null;
|
||||
if (mData.mTextFlags != null)
|
||||
textFlagsPtr = mData.mTextFlags.CArray();
|
||||
|
||||
int32 lineIdx = 0;
|
||||
if (textFlagsPtr != null)
|
||||
int32 lineIdx = 0;
|
||||
if (textFlagsPtr != null)
|
||||
{
|
||||
for (int32 i < mData.mTextLength)
|
||||
{
|
||||
for (int32 i < mData.mTextLength)
|
||||
{
|
||||
if ((char8DataPtr[i].mChar == '\n') || ((textFlagsPtr != null) && ((textFlagsPtr[i] & ((int32)TextFlags.Wrap)) != 0)))
|
||||
lineIdx++;
|
||||
}
|
||||
if ((char8DataPtr[i].mChar == '\n') || ((textFlagsPtr != null) && ((textFlagsPtr[i] & ((int32)TextFlags.Wrap)) != 0)))
|
||||
lineIdx++;
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int32 i < mData.mTextLength)
|
||||
{
|
||||
for (int32 i < mData.mTextLength)
|
||||
{
|
||||
if (char8DataPtr[i].mChar == '\n')
|
||||
lineIdx++;
|
||||
}
|
||||
if (char8DataPtr[i].mChar == '\n')
|
||||
lineIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
mData.mLineStarts = new List<int32>();
|
||||
mData.mLineStarts.GrowUnitialized(lineIdx + 2);
|
||||
int32* lineStartsPtr = mData.mLineStarts.Ptr;
|
||||
lineStartsPtr[0] = 0;
|
||||
|
||||
lineIdx = 0;
|
||||
if (textFlagsPtr != null)
|
||||
mData.mLineStarts = new List<int32>();
|
||||
mData.mLineStarts.GrowUnitialized(lineIdx + 2);
|
||||
int32* lineStartsPtr = mData.mLineStarts.Ptr;
|
||||
lineStartsPtr[0] = 0;
|
||||
|
||||
lineIdx = 0;
|
||||
if (textFlagsPtr != null)
|
||||
{
|
||||
for (int32 i < mData.mTextLength)
|
||||
{
|
||||
if ((textFlagsPtr != null) && ((textFlagsPtr[i] & ((int32)TextFlags.Wrap)) != 0))
|
||||
{
|
||||
lineIdx++;
|
||||
lineStartsPtr[lineIdx] = i;
|
||||
}
|
||||
else if ((char8)char8DataPtr[i].mChar == '\n')
|
||||
{
|
||||
lineIdx++;
|
||||
lineStartsPtr[lineIdx] = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int32 i < mData.mTextLength)
|
||||
{
|
||||
for (int32 i < mData.mTextLength)
|
||||
{
|
||||
if ((textFlagsPtr != null) && ((textFlagsPtr[i] & ((int32)TextFlags.Wrap)) != 0))
|
||||
{
|
||||
lineIdx++;
|
||||
lineStartsPtr[lineIdx] = i;
|
||||
}
|
||||
else if ((char8)char8DataPtr[i].mChar == '\n')
|
||||
{
|
||||
lineIdx++;
|
||||
lineStartsPtr[lineIdx] = i + 1;
|
||||
}
|
||||
}
|
||||
if ((char8)char8DataPtr[i].mChar == '\n')
|
||||
{
|
||||
lineIdx++;
|
||||
lineStartsPtr[lineIdx] = i + 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int32 i < mData.mTextLength)
|
||||
{
|
||||
if ((char8)char8DataPtr[i].mChar == '\n')
|
||||
{
|
||||
lineIdx++;
|
||||
lineStartsPtr[lineIdx] = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mData.mLineStarts[lineIdx + 1] = mData.mTextLength;
|
||||
}
|
||||
}
|
||||
|
||||
mData.mLineStarts[lineIdx + 1] = mData.mTextLength;
|
||||
LineStartsChanged();
|
||||
}
|
||||
|
||||
public virtual void Backspace()
|
||||
|
@ -1519,7 +1536,7 @@ namespace Beefy.widgets
|
|||
ContentChanged();
|
||||
if (offset != 0)
|
||||
{
|
||||
MoveCursorToIdx(textPos, false, .FromTyping);
|
||||
MoveCursorToIdx(textPos, false, .FromTyping_Deleting);
|
||||
EnsureCursorVisible();
|
||||
}
|
||||
}
|
||||
|
@ -1648,7 +1665,7 @@ namespace Beefy.widgets
|
|||
}
|
||||
else
|
||||
{
|
||||
int32 char8Count = 1;
|
||||
int32 charCount = 1;
|
||||
int checkIdx = textPos + 1;
|
||||
while (true)
|
||||
{
|
||||
|
@ -1660,12 +1677,12 @@ namespace Beefy.widgets
|
|||
if (!checkChar.IsCombiningMark)
|
||||
break;
|
||||
}
|
||||
char8Count++;
|
||||
charCount++;
|
||||
checkIdx++;
|
||||
}
|
||||
|
||||
mData.mUndoManager.Add(new DeleteCharAction(this, 0, char8Count));
|
||||
PhysDeleteChars(0, char8Count);
|
||||
mData.mUndoManager.Add(new DeleteCharAction(this, 0, charCount));
|
||||
PhysDeleteChars(0, charCount);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1934,7 +1951,7 @@ namespace Beefy.widgets
|
|||
return .Other;
|
||||
}
|
||||
|
||||
public void GetTextCoordAtCursor(out float x, out float y)
|
||||
public virtual void GetTextCoordAtCursor(out float x, out float y)
|
||||
{
|
||||
if (mVirtualCursorPos.HasValue)
|
||||
{
|
||||
|
@ -1963,8 +1980,9 @@ namespace Beefy.widgets
|
|||
float y;
|
||||
GetTextCoordAtLineAndColumn(mVirtualCursorPos.Value.mLine, mVirtualCursorPos.Value.mColumn, out x, out y);
|
||||
|
||||
float overflowX;
|
||||
return GetLineCharAtCoord(x, y, out line, out lineChar, out overflowX);
|
||||
|
||||
float overflowX;
|
||||
return GetLineCharAtCoord(line, x, out lineChar, out overflowX);
|
||||
}
|
||||
|
||||
public virtual bool PrepareForCursorMove(int dir = 0)
|
||||
|
@ -2064,6 +2082,7 @@ namespace Beefy.widgets
|
|||
if (var insertTextAction = mData.mUndoManager.GetLastUndoAction() as InsertTextAction)
|
||||
insertTextAction.mVirtualCursorPos = origPosition;
|
||||
CursorLineAndColumn = lineStartPosition;
|
||||
|
||||
CursorToLineStart(false);
|
||||
|
||||
// Adjust to requested column
|
||||
|
@ -2110,15 +2129,15 @@ namespace Beefy.widgets
|
|||
while (true)
|
||||
{
|
||||
if (lineChar > 0)
|
||||
MoveCursorTo(lineIdx, lineChar - 1);
|
||||
MoveCursorTo(lineIdx, lineChar - 1, false, 0, .SelectLeft);
|
||||
else if (lineIdx > 0)
|
||||
{
|
||||
int cursorIdx = mCursorTextPos;
|
||||
String lineText = scope String();
|
||||
GetLineText(lineIdx - 1, lineText);
|
||||
MoveCursorTo(lineIdx - 1, (int32)lineText.Length);
|
||||
MoveCursorTo(lineIdx - 1, (int32)lineText.Length, false, 0, .SelectLeft);
|
||||
if ((!mAllowVirtualCursor) && (cursorIdx == mCursorTextPos))
|
||||
MoveCursorTo(lineIdx - 1, (int32)lineText.Length - 1);
|
||||
MoveCursorTo(lineIdx - 1, (int32)lineText.Length - 1, false, 0, .SelectLeft);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -2180,9 +2199,9 @@ namespace Beefy.widgets
|
|||
}
|
||||
|
||||
if (isWithinLine)
|
||||
MoveCursorTo(lineIdx, lineChar + 1, false, 1);
|
||||
MoveCursorTo(lineIdx, lineChar + 1, false, 1, .SelectRight);
|
||||
else if (lineIdx < GetLineCount() - 1)
|
||||
MoveCursorTo(lineIdx + 1, 0);
|
||||
MoveCursorTo(lineIdx + 1, 0, false, 0, .SelectRight);
|
||||
|
||||
if (!mWidgetWindow.IsKeyDown(KeyCode.Control))
|
||||
break;
|
||||
|
@ -2274,7 +2293,7 @@ namespace Beefy.widgets
|
|||
mEditWidget.Submit();
|
||||
case KeyCode.Left:
|
||||
{
|
||||
if (!PrepareForCursorMove(-1))
|
||||
if (!PrepareForCursorMove(-1))
|
||||
{
|
||||
PrepareForCursorMove(-1);
|
||||
|
||||
|
@ -2349,7 +2368,7 @@ namespace Beefy.widgets
|
|||
var lineAndColumn = CursorLineAndColumn;
|
||||
CursorLineAndColumn = LineAndColumn(lineAndColumn.mLine, lineAndColumn.mColumn + 1);
|
||||
EnsureCursorVisible(true, false, false);
|
||||
CursorMoved();
|
||||
PhysCursorMoved(.SelectRight);
|
||||
|
||||
ClampCursor();
|
||||
if (lineAndColumn != CursorLineAndColumn)
|
||||
|
@ -2393,22 +2412,40 @@ namespace Beefy.widgets
|
|||
wasMoveKey = true;
|
||||
if ((lineIdx + aDir >= 0) && (lineIdx + aDir < GetLineCount()))
|
||||
{
|
||||
float wantedX = mCursorWantX;
|
||||
float wantY = 0;
|
||||
|
||||
if (mAllowVirtualCursor)
|
||||
{
|
||||
float cursorX;
|
||||
float cursorY;
|
||||
GetTextCoordAtCursor(out cursorX, out cursorY);
|
||||
|
||||
GetLineAndColumnAtCoord(cursorX, cursorY, var virtLine, ?);
|
||||
|
||||
/*GetTextCoordAtLineAndColumn(lineIdx, 0, ?, var lineY);
|
||||
Debug.WriteLine($"Line:{lineIdx} LineY:{lineY} Cursor:{cursorX},{cursorY}");*/
|
||||
|
||||
if (aDir < 0)
|
||||
{
|
||||
wantY = cursorY - 0.1f;
|
||||
}
|
||||
else
|
||||
{
|
||||
wantY = cursorY + GetLineHeight(virtLine) + 0.1f;
|
||||
}
|
||||
//mCursorWantX = cursorX;
|
||||
}
|
||||
else
|
||||
{
|
||||
lineIdx += aDir;
|
||||
|
||||
lineIdx += aDir;
|
||||
|
||||
float wantedX = mCursorWantX;
|
||||
|
||||
float aX;
|
||||
float aY;
|
||||
GetTextCoordAtLineChar(lineIdx, 0, out aX, out aY);
|
||||
MoveCursorToCoord(mCursorWantX, aY);
|
||||
float aX;
|
||||
float aY;
|
||||
GetTextCoordAtLineChar(lineIdx, 0, out aX, out aY);
|
||||
wantY = aY;
|
||||
}
|
||||
MoveCursorToCoord(mCursorWantX, wantY);
|
||||
|
||||
ClampCursor();
|
||||
|
||||
|
@ -2645,6 +2682,12 @@ namespace Beefy.widgets
|
|||
return false;
|
||||
}
|
||||
|
||||
public virtual bool GetLineCharAtCoord(int line, float x, out int theChar, out float overflowX)
|
||||
{
|
||||
GetTextCoordAtLineAndColumn(line, 0, ?, var y);
|
||||
return GetLineCharAtCoord(x, y, ?, out theChar, out overflowX);
|
||||
}
|
||||
|
||||
public virtual bool GetLineAndColumnAtCoord(float x, float y, out int line, out int column)
|
||||
{
|
||||
line = -1;
|
||||
|
@ -2725,7 +2768,7 @@ namespace Beefy.widgets
|
|||
return;
|
||||
}
|
||||
if (c < 0)
|
||||
lo = i + 1;
|
||||
lo = i + 1;
|
||||
else
|
||||
hi = i - 1;
|
||||
}
|
||||
|
@ -2818,11 +2861,11 @@ namespace Beefy.widgets
|
|||
ExtractString(lineStart, lineEnd - lineStart, outStr); // Full line
|
||||
}
|
||||
|
||||
public int GetTextIdx(int line, int char8Idx)
|
||||
public int GetTextIdx(int line, int charIdx)
|
||||
{
|
||||
GetTextData();
|
||||
int useLine = Math.Min(line, mData.mLineStarts.Count - 1);
|
||||
return mData.mLineStarts[useLine] + char8Idx;
|
||||
return mData.mLineStarts[useLine] + charIdx;
|
||||
}
|
||||
|
||||
public int GetCharIdIdx(int32 findCharId)
|
||||
|
@ -2839,7 +2882,7 @@ namespace Beefy.widgets
|
|||
|
||||
int curLine = 0;
|
||||
int curColumn = 0;
|
||||
int char8Idx = 0;
|
||||
int charIdx = 0;
|
||||
mData.mTextIdData.Prepare();
|
||||
while (true)
|
||||
{
|
||||
|
@ -2863,7 +2906,7 @@ namespace Beefy.widgets
|
|||
if ((curLine == line) && (curColumn == column))
|
||||
return char8Id;
|
||||
|
||||
char8 c = (char8)mData.mText[char8Idx++].mChar;
|
||||
char8 c = (char8)mData.mText[charIdx++].mChar;
|
||||
if (c == '\n')
|
||||
{
|
||||
if (curLine == line)
|
||||
|
@ -2878,7 +2921,7 @@ namespace Beefy.widgets
|
|||
}
|
||||
}
|
||||
|
||||
public virtual void GetTextCoordAtLineChar(int line, int char8Idx, out float x, out float y)
|
||||
public virtual void GetTextCoordAtLineChar(int line, int charIdx, out float x, out float y)
|
||||
{
|
||||
x = 0;
|
||||
y = 0;
|
||||
|
@ -2892,8 +2935,13 @@ namespace Beefy.widgets
|
|||
|
||||
public enum CursorMoveKind
|
||||
{
|
||||
FromTyping,
|
||||
Unknown
|
||||
case FromTyping;
|
||||
case FromTyping_Deleting;
|
||||
case Unknown;
|
||||
case SelectRight;
|
||||
case SelectLeft;
|
||||
|
||||
public bool IsFromTyping => (this == FromTyping) || (this == FromTyping_Deleting);
|
||||
}
|
||||
|
||||
// We used to have a split between PhysCursorMoved and CursorMoved. CursorMoved has a "ResetWantX" and was non-virtual... uh-
|
||||
|
@ -2936,7 +2984,7 @@ namespace Beefy.widgets
|
|||
{
|
||||
int lineIdx;
|
||||
int lineChar;
|
||||
GetCursorLineChar(out lineIdx, out lineChar);
|
||||
GetCursorLineChar(out lineIdx, out lineChar);
|
||||
|
||||
String lineText = scope String();
|
||||
GetLineText(lineIdx, lineText);
|
||||
|
@ -3379,14 +3427,22 @@ namespace Beefy.widgets
|
|||
Debug.Assert(mSelection.Value.mEndPos <= mData.mTextLength);
|
||||
}
|
||||
|
||||
//public void MoveCursorTo
|
||||
public virtual void GetLineAndColumnAtLineChar(int line, int lineChar, out int lineColumn)
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
GetTextCoordAtLineChar(line, lineChar, out x, out y);
|
||||
|
||||
public void MoveCursorTo(int line, int char8Idx, bool centerCursor = false, int movingDir = 0, CursorMoveKind cursorMoveKind = .Unknown)
|
||||
int coordLine;
|
||||
GetLineAndColumnAtCoord(x, y, out coordLine, out lineColumn);
|
||||
}
|
||||
|
||||
public virtual void MoveCursorTo(int line, int charIdx, bool centerCursor = false, int movingDir = 0, CursorMoveKind cursorMoveKind = .Unknown)
|
||||
{
|
||||
int useCharIdx = char8Idx;
|
||||
int useCharIdx = charIdx;
|
||||
|
||||
mShowCursorAtLineEnd = false;
|
||||
CursorTextPos = GetTextIdx(line, char8Idx);
|
||||
CursorTextPos = GetTextIdx(line, charIdx);
|
||||
|
||||
// Skip over UTF8 parts AND unicode combining marks (ie: when we have a letter with an accent mark following it)
|
||||
while (true)
|
||||
|
@ -3433,13 +3489,13 @@ namespace Beefy.widgets
|
|||
|
||||
public void MoveCursorToIdx(int index, bool centerCursor = false, CursorMoveKind cursorMoveKind = .Unknown)
|
||||
{
|
||||
int aLine;
|
||||
int aCharIdx;
|
||||
GetLineCharAtIdx(index, out aLine, out aCharIdx);
|
||||
MoveCursorTo(aLine, aCharIdx, centerCursor, 0, cursorMoveKind);
|
||||
int line;
|
||||
int charIdx;
|
||||
GetLineCharAtIdx(index, out line, out charIdx);
|
||||
MoveCursorTo(line, charIdx, centerCursor, 0, cursorMoveKind);
|
||||
}
|
||||
|
||||
public void MoveCursorToCoord(float x, float y)
|
||||
public virtual void MoveCursorToCoord(float x, float y)
|
||||
{
|
||||
bool failed = false;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue