diff --git a/.gitignore b/.gitignore
index 3afa4ac6..9c070a9e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@ SDL2.dll
*.vcxproj.user
*/llvm-*
*/llvm_*
+*/_llvm*
*/x64/*
**/Debug/*
**/Debug */*
diff --git a/BeefBoot/BeefBoot.vcxproj b/BeefBoot/BeefBoot.vcxproj
index cab7e38c..d317bbad 100644
--- a/BeefBoot/BeefBoot.vcxproj
+++ b/BeefBoot/BeefBoot.vcxproj
@@ -108,7 +108,7 @@
Level3
Disabled
_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
- ../;../IDEHelper;../BeefySysLib/platform/win;../BeefySysLib/third_party;..\extern\llvm-project_18_1_4\llvm\include;..\extern\llvm_win64_18_1_4\include;..\extern\llvm-project_18_1_4\llvm\lib\Target;..\extern\llvm_win64_18_1_4\lib\Target\X86;..\extern\llvm-project_18_1_4\llvm\tools\clang\include
+ ../;../IDEHelper;../BeefySysLib/platform/win;../BeefySysLib/third_party;..\extern\llvm-project_19_1_7\llvm\include;..\extern\llvm_win64_19_1_7\include;..\extern\llvm-project_19_1_7\llvm\lib\Target;..\extern\llvm_win64_19_1_7\lib\Target\X86;..\extern\llvm-project_19_1_7\llvm\tools\clang\include
true
false
MultiThreadedDebug
@@ -152,7 +152,7 @@
true
NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
- ../;../IDEHelper;../BeefySysLib/platform/win;../BeefySysLib/third_party;..\extern\llvm-project_18_1_4\llvm\include;..\extern\llvm_win64_18_1_4\include;..\extern\llvm-project_18_1_4\llvm\lib\Target;..\extern\llvm_win64_18_1_4\lib\Target\X86;..\extern\llvm\tools\clang\include
+ ../;../IDEHelper;../BeefySysLib/platform/win;../BeefySysLib/third_party;..\extern\llvm-project_19_1_7\llvm\include;..\extern\llvm_win64_19_1_7\include;..\extern\llvm-project_19_1_7\llvm\lib\Target;..\extern\llvm_win64_19_1_7\lib\Target\X86;..\extern\llvm\tools\clang\include
MultiThreaded
false
true
diff --git a/BeefBoot/CMakeLists.txt b/BeefBoot/CMakeLists.txt
index 365fa336..ad29063b 100644
--- a/BeefBoot/CMakeLists.txt
+++ b/BeefBoot/CMakeLists.txt
@@ -119,7 +119,7 @@ add_executable(${PROJECT_NAME}
${SRC_FILES}
)
-find_package(LLVM 18.1 CONFIG COMPONENTS)
+find_package(LLVM 19.1 CONFIG COMPONENTS)
if (LLVM_FOUND)
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
@@ -128,7 +128,7 @@ if (LLVM_FOUND)
include_directories(${LLVM_INCLUDE_DIRS})
add_definitions(${LLVM_DEFINITIONS})
- set(TARGET_LIBS_OS "-lLLVM-18")
+ set(TARGET_LIBS_OS "-lLLVM-19")
else()
message(FATAL_ERROR "LLVM not found")
endif()
diff --git a/BeefBuild/BeefProj.toml b/BeefBuild/BeefProj.toml
index 38960488..0fc316c1 100644
--- a/BeefBuild/BeefProj.toml
+++ b/BeefBuild/BeefProj.toml
@@ -7,7 +7,7 @@ StartupObject = "BeefBuild.Program"
[Platform.Windows]
Description = "BeefBuild"
-FileVersion = "0.43.5"
+FileVersion = "0.43.6"
[Configs.Debug.Win32]
TargetName = ""
@@ -19,8 +19,8 @@ TargetName = "$(ProjectName)_d"
OtherLinkFlags = "$(LinkFlags) Comdlg32.lib kernel32.lib user32.lib advapi32.lib shell32.lib IDEHelper64_d.lib Rpcrt4.lib Ole32.lib"
CLibType = "Dynamic"
BeefLibType = "DynamicDebug"
-DebugCommandArguments = "-proddir=c:\\proj\\BeefTest update BeefProj1"
-DebugWorkingDirectory = "c:\\beef\\ide"
+DebugCommandArguments = "-run"
+DebugWorkingDirectory = "c:\\proj\\BeefTest"
EnvironmentVars = ["_NO_DEBUG_HEAP=1"]
PreprocessorMacros = ["DEBUG", "CLI"]
diff --git a/BeefBuild/src/BuildApp.bf b/BeefBuild/src/BuildApp.bf
index 3aaacb3e..e9ba6891 100644
--- a/BeefBuild/src/BuildApp.bf
+++ b/BeefBuild/src/BuildApp.bf
@@ -25,6 +25,7 @@ namespace BeefBuild
public bool mWantsGenerate = false;
public bool mHandledVerb;
public String mRunArgs ~ delete _;
+ public String mStartingDirectory = new .() ~ delete _;
MainVerbState mMainVerbState;
/*void Test()
@@ -57,6 +58,8 @@ namespace BeefBuild
public override void Init()
{
+ Directory.GetCurrentDirectory(mStartingDirectory);
+
GetVersionInfo(var exeTime);
if (mVerbosity == .Default)
@@ -388,9 +391,9 @@ namespace BeefBuild
mFailed = true;
}
- protected override void CompileFailed()
+ protected override void CompileFailed(Stopwatch stopwatch)
{
- base.CompileFailed();
+ base.CompileFailed(stopwatch);
mFailed = true;
}
@@ -434,9 +437,6 @@ namespace BeefBuild
{
if ((mVerb == .Run) && (!mDidRun) && (!mFailed))
{
- let curPath = scope String();
- Directory.GetCurrentDirectory(curPath);
-
let workspaceOptions = gApp.GetCurWorkspaceOptions();
let options = gApp.GetCurProjectOptions(mWorkspace.mStartupProject);
let targetPaths = scope List();
@@ -445,7 +445,7 @@ namespace BeefBuild
if (targetPaths.IsEmpty)
return;
- ExecutionQueueCmd executionCmd = QueueRun(targetPaths[0], mRunArgs ?? "", curPath);
+ ExecutionQueueCmd executionCmd = QueueRun(targetPaths[0], mRunArgs ?? "", mStartingDirectory);
executionCmd.mRunFlags |= .NoRedirect;
executionCmd.mIsTargetRun = true;
mDidRun = true;
diff --git a/BeefBuild/src/Program.bf b/BeefBuild/src/Program.bf
index 610dac9d..5164047f 100644
--- a/BeefBuild/src/Program.bf
+++ b/BeefBuild/src/Program.bf
@@ -31,7 +31,7 @@ namespace BeefBuild
//TestZip2();
String commandLine = scope String();
- commandLine.Join(" ", params args);
+ commandLine.Join(" ", args);
BuildApp mApp = new BuildApp();
mApp.ParseCommandLine(commandLine);
diff --git a/BeefLibs/Beefy2D/BeefProj.toml b/BeefLibs/Beefy2D/BeefProj.toml
index 6a90b2be..f7bbe704 100644
--- a/BeefLibs/Beefy2D/BeefProj.toml
+++ b/BeefLibs/Beefy2D/BeefProj.toml
@@ -1,4 +1,5 @@
FileVersion = 1
+Dependencies = {corlib = "*", MiniZ = "*"}
[Project]
Name = "Beefy2D"
@@ -50,3 +51,7 @@ OtherLinkFlags = "$(LinkFlags) \"$(ProjectDir)/dist/BeefySysLib64.lib\""
CLibType = "Static"
BeefLibType = "Static"
PostBuildCmds = ["CopyToDependents(\"$(ProjectDir)/dist/BeefySysLib64.dll\")", "CopyToDependents(\"$(ProjectDir)/dist/BeefySysLib64.pdb\")"]
+
+[Configs.Release_NoLink.Win32]
+OtherLinkFlags = ""
+PreprocessorMacros = ["RELEASE", "BF32"]
diff --git a/BeefLibs/Beefy2D/src/BFApp.bf b/BeefLibs/Beefy2D/src/BFApp.bf
index 6c341123..d1518b0a 100644
--- a/BeefLibs/Beefy2D/src/BFApp.bf
+++ b/BeefLibs/Beefy2D/src/BFApp.bf
@@ -332,6 +332,22 @@ namespace Beefy
}
}
+ public Point? CursorScreenPos
+ {
+ get
+ {
+ for (var window in mWindows)
+ {
+ if (var widgetWindow = window as WidgetWindow)
+ {
+ if (widgetWindow.mHasMouseInside)
+ return .(widgetWindow.mClientMouseX + widgetWindow.mClientX, widgetWindow.mClientMouseY + widgetWindow.mClientY);
+ }
+ }
+ return null;
+ }
+ }
+
public static void Startup(String[] args, Action startupCallback)
{
/*string[] newArgs = new string[args.Length + 1];
diff --git a/BeefLibs/Beefy2D/src/BFWindow.bf b/BeefLibs/Beefy2D/src/BFWindow.bf
index a4cdf220..f5cbe2a5 100644
--- a/BeefLibs/Beefy2D/src/BFWindow.bf
+++ b/BeefLibs/Beefy2D/src/BFWindow.bf
@@ -5,6 +5,7 @@ using System.Diagnostics;
using Beefy.gfx;
using Beefy.sys;
using System.IO;
+using Beefy.geom;
#if MONOTOUCH
using MonoTouch;
@@ -109,7 +110,7 @@ namespace Beefy
public virtual void PreDraw(Graphics g)
{
- g.PushDrawLayer(mDefaultDrawLayer);
+ g.PushDrawLayer(mDefaultDrawLayer);
}
public virtual void PostDraw(Graphics g)
@@ -327,6 +328,9 @@ namespace Beefy
static void Static_NativeDragDropFileDelegate(void* window, char8* filePath) { GetBFWindow(window).DragDropFile(StringView(filePath)); }
#endif
+ public Rect ClientRect => .(mClientX, mClientY, mClientWidth, mClientHeight);
+ public Rect WindowRect => .(mX, mY, mWindowWidth, mWindowHeight);
+
public this()
{
}
@@ -480,6 +484,11 @@ namespace Beefy
BFWindow_Resize(mNativeWindow, (int32)x, (int32)y, (int32)width, (int32)height, (int32)showKind);
}
+ public virtual void ResizeClient(int width, int height)
+ {
+ Resize(mX, mY, mWindowWidth + (width - mClientWidth), mWindowHeight + (height - mClientHeight));
+ }
+
public void SetForeground()
{
BFWindow_SetForeground(mNativeWindow);
diff --git a/BeefLibs/Beefy2D/src/Utils.bf b/BeefLibs/Beefy2D/src/Utils.bf
index 8fd91952..21195eb6 100644
--- a/BeefLibs/Beefy2D/src/Utils.bf
+++ b/BeefLibs/Beefy2D/src/Utils.bf
@@ -6,6 +6,7 @@ using System.Threading;
using System.IO;
using System.Diagnostics;
using System.Security.Cryptography;
+using Beefy.gfx;
namespace Beefy
{
@@ -21,8 +22,6 @@ namespace Beefy
public static float Deg2Rad = Math.PI_f / 180.0f;
-
-
public static int32 Rand()
{
return mRandom.NextI32();
@@ -33,6 +32,12 @@ namespace Beefy
return (Rand() & 0xFFFFFF) / (float)0xFFFFFF;
}
+ public static float RandFloatS()
+ {
+#unwarn
+ return ((Rand() & 0x1FFFFFF) / (float)0xFFFFFF) - 1.0f;
+ }
+
public static float Interpolate(float left, float right, float pct)
{
return left + (right - left) * pct;
@@ -472,5 +477,12 @@ namespace Beefy
{
val = (float)Math.Round(val * scale);
}
+
+ public static Image GetAnimFrame(Image[] images, float pct)
+ {
+ int frameNum = (int)(images.Count * (pct - 0.000001f));
+ frameNum %= images.Count;
+ return images[frameNum];
+ }
}
}
diff --git a/BeefLibs/Beefy2D/src/geom/Plane.bf b/BeefLibs/Beefy2D/src/geom/Plane.bf
index 8e52b3d7..70ab9a9b 100644
--- a/BeefLibs/Beefy2D/src/geom/Plane.bf
+++ b/BeefLibs/Beefy2D/src/geom/Plane.bf
@@ -283,7 +283,7 @@ namespace Beefy.geom
public override void ToString(String str)
{
- str.AppendF($"{Normal:{Normal} D:{D}}");
+ str.AppendF($"{{Normal:{Normal} D:{D}}}");
}
#endregion
diff --git a/BeefLibs/Beefy2D/src/geom/Rect.bf b/BeefLibs/Beefy2D/src/geom/Rect.bf
index 32beb6ed..b14504f1 100644
--- a/BeefLibs/Beefy2D/src/geom/Rect.bf
+++ b/BeefLibs/Beefy2D/src/geom/Rect.bf
@@ -97,6 +97,14 @@ namespace Beefy.geom
public T CenterX => mX + mWidth / 2;
public T CenterY => mY + mHeight / 2;
+ public static Rect operator implicit (Rect value)
+ where T : operator implicit TOther
+ where TOther : operator TOther + TOther, operator TOther - TOther, operator TOther * TOther, operator TOther / TOther, operator -TOther, operator TOther / int8, IIsNaN, operator implicit int8
+ where int : operator TOther <=> TOther
+ {
+ return .(value.mX, value.mY, value.mWidth, value.mHeight);
+ }
+
public this(T x = default, T y = default, T width = default, T height = default)
{
mX = x;
@@ -236,6 +244,11 @@ namespace Beefy.geom
mHeight *= scaleY;
}
+ public void Scale(T scale) mut
+ {
+ Scale(scale, scale);
+ }
+
public void ScaleFrom(T scaleX, T scaleY, T centerX, T centerY) mut
{
Offset(-centerX, -centerY);
@@ -249,6 +262,7 @@ namespace Beefy.geom
}
}
+ typealias RectI32 = Rect;
typealias Rect = Rect;
typealias RectD = Rect;
}
diff --git a/BeefLibs/Beefy2D/src/geom/Vector2.bf b/BeefLibs/Beefy2D/src/geom/Vector2.bf
index 7db097fc..9f1c06da 100644
--- a/BeefLibs/Beefy2D/src/geom/Vector2.bf
+++ b/BeefLibs/Beefy2D/src/geom/Vector2.bf
@@ -46,6 +46,11 @@ namespace Beefy.geom
public Vector2 Normalized => Vector2(mX, mY)..Normalize();
+ public bool IsLengthLess(float check)
+ {
+ return mX * mX + mY * mY < check * check;
+ }
+
public static Vector2 Normalize(Vector2 vector)
{
Vector2 newVec;
@@ -104,6 +109,11 @@ namespace Beefy.geom
return Vector2((float)Math.Cos(angle) * length, (float)Math.Sin(angle) * length);
}
+ public static Vector2 operator -(Vector2 vec1)
+ {
+ return Vector2(-vec1.mX, -vec1.mY);
+ }
+
public static Vector2 operator +(Vector2 vec1, Vector2 vec2)
{
return Vector2(vec1.mX + vec2.mX, vec1.mY + vec2.mY);
@@ -123,5 +133,10 @@ namespace Beefy.geom
{
return Vector2(vec1.mX / factor, vec1.mY / factor);
}
+
+ public override void ToString(String strBuffer)
+ {
+ strBuffer.AppendF("{0:0.0#}, {1:0.0#}", mX, mY);
+ }
}
}
diff --git a/BeefLibs/Beefy2D/src/geom/Vector3.bf b/BeefLibs/Beefy2D/src/geom/Vector3.bf
index 4ee85123..0adfc7e7 100644
--- a/BeefLibs/Beefy2D/src/geom/Vector3.bf
+++ b/BeefLibs/Beefy2D/src/geom/Vector3.bf
@@ -101,6 +101,8 @@ namespace Beefy.geom
}
}
+ public Vector2 XY => .(mX, mY);
+
public this(float x, float y, float z)
{
mX = x;
@@ -306,5 +308,7 @@ namespace Beefy.geom
{
str.AppendF("{0:0.0#}, {1:0.0#}, {2:0.0#}", mX, mY, mZ);
}
+
+ public static Vector3 operator implicit(Vector2 vec) => .(vec.mX, vec.mY, 0);
}
}
diff --git a/BeefLibs/Beefy2D/src/gfx/DrawLayer.bf b/BeefLibs/Beefy2D/src/gfx/DrawLayer.bf
index ea9d6448..c4de0423 100644
--- a/BeefLibs/Beefy2D/src/gfx/DrawLayer.bf
+++ b/BeefLibs/Beefy2D/src/gfx/DrawLayer.bf
@@ -13,6 +13,8 @@ namespace Beefy.gfx
#if !STUDIO_CLIENT
public class DrawLayer
{
+ public static List sDrawLayers = new .() ~ delete _;
+
[CallingConvention(.Stdcall), CLink]
static extern void* DrawLayer_Create(void* window);
@@ -33,10 +35,12 @@ namespace Beefy.gfx
public this(BFWindow window)
{
mNativeDrawLayer = DrawLayer_Create((window != null) ? (window.mNativeWindow) : null);
+ sDrawLayers.Add(this);
}
public ~this()
{
+ sDrawLayers.Remove(this);
DrawLayer_Delete(mNativeDrawLayer);
}
diff --git a/BeefLibs/Beefy2D/src/gfx/Font.bf b/BeefLibs/Beefy2D/src/gfx/Font.bf
index d5fe081c..005c00b2 100644
--- a/BeefLibs/Beefy2D/src/gfx/Font.bf
+++ b/BeefLibs/Beefy2D/src/gfx/Font.bf
@@ -6,6 +6,7 @@ using Beefy.geom;
using Beefy.utils;
using System.Diagnostics;
using System.Threading;
+using res;
namespace Beefy.gfx
{
@@ -36,6 +37,104 @@ namespace Beefy.gfx
public float mMaxWidth;
}
+ public class FontEffect
+ {
+ public enum Kind
+ {
+ case None;
+ case Outline(float thickness, uint32 color);
+ }
+
+ public class EffectCharData
+ {
+ public Font.CharData mSrcCharData;
+ public Font.CharData mEffectCharData ~ delete _;
+ }
+
+ public class FontEntry
+ {
+ public Font mFont;
+ public ImageAtlas mAtlas = new ImageAtlas() ~ delete _;
+ public List mCharData = new .() ~ delete _;
+ }
+
+ public Kind mKind;
+ public String mEffectOptions = new .() ~ delete _;
+ public Dictionary mFontEntries = new .() ~ DeleteDictionaryAndValues!(_);
+ public Dictionary mCharData = new .() ~ DeleteDictionaryAndValues!(_);
+
+ public this(Kind kind)
+ {
+ mKind = kind;
+ switch (mKind)
+ {
+ case .Outline(let thickness, let color):
+ mEffectOptions..Clear().AppendF(
+ $"""
+ Effect=Stroke
+ Size={thickness}
+ Color=#{color:X}
+ """);
+ default:
+ }
+ }
+
+ public ~this()
+ {
+
+ }
+
+ public Font.CharData Apply(Font font, Font.CharData charData)
+ {
+ EffectCharData effectCharData = null;
+ if (mCharData.TryAdd(charData, ?, var effectCharDataPtr))
+ {
+ effectCharData = new EffectCharData();
+ *effectCharDataPtr = effectCharData;
+
+ effectCharData.mSrcCharData = charData;
+ effectCharData.mEffectCharData = new .(charData);
+ }
+ else
+ {
+ return (*effectCharDataPtr).mEffectCharData;
+ }
+
+ FontEntry fontEntry = null;
+ if (mFontEntries.TryAdd(font, ?, var fontEntryPtr))
+ {
+ fontEntry = new .();
+ *fontEntryPtr = fontEntry;
+ }
+ else
+ fontEntry = *fontEntryPtr;
+
+ int32 ofsX = 0;
+ int32 ofsY = 0;
+ if (mKind case .Outline(let thickness, ?))
+ {
+ ofsX += (.)Math.Ceiling(thickness) + 2;
+ ofsY += (.)Math.Ceiling(thickness) + 2;
+ }
+
+ effectCharData.mEffectCharData.mXOffset -= ofsX;
+ effectCharData.mEffectCharData.mYOffset -= ofsY;
+ var effectImage = fontEntry.mAtlas.Alloc(charData.mImageSegment.mSrcWidth + ofsX * 2, charData.mImageSegment.mSrcHeight + ofsY * 2);
+ effectCharData.mEffectCharData.mImageSegment = effectImage;
+ effectCharData.mSrcCharData.mImageSegment.ApplyEffect(effectImage, mEffectOptions);
+
+ /*uint32 color = 0xFFFFFFFF;
+ effectImage.SetBits(0, 0, 1, 1, 1, &color);*/
+
+ return effectCharData.mEffectCharData;
+ }
+
+ public void Prepare(Font font, StringView str)
+ {
+
+ }
+ }
+
public class Font
{
[CallingConvention(.Stdcall), CLink]
@@ -89,6 +188,24 @@ namespace Beefy.gfx
public int32 mYOffset;
public int32 mXAdvance;
public bool mIsCombiningMark;
+
+ public this()
+ {
+
+ }
+
+ public this(CharData copyFrom)
+ {
+ mImageSegment = copyFrom.mImageSegment;
+ mX = copyFrom.mX;
+ mY = copyFrom.mY;
+ mWidth = copyFrom.mWidth;
+ mHeight = copyFrom.mHeight;
+ mXOffset = copyFrom.mXOffset;
+ mYOffset = copyFrom.mYOffset;
+ mXAdvance = copyFrom.mXAdvance;
+ mIsCombiningMark = copyFrom.mIsCombiningMark;
+ }
}
public class Page
@@ -139,6 +256,9 @@ namespace Beefy.gfx
//BitmapFont mBMFont ~ delete _;
public StringView mEllipsis = "...";
+ List mFontEffectStack ~ delete _;
+ DisposeProxy mFontEffectDisposeProxy = new .(new () => { PopFontEffect(); }) ~ delete _;
+
public this()
{
}
@@ -153,6 +273,19 @@ namespace Beefy.gfx
FTFont_ClearCache();
}
+ public DisposeProxy PushFontEffect(FontEffect fontEffect)
+ {
+ if (mFontEffectStack == null)
+ mFontEffectStack = new .();
+ mFontEffectStack.Add(fontEffect);
+ return mFontEffectDisposeProxy;
+ }
+
+ public void PopFontEffect()
+ {
+ mFontEffectStack.PopBack();
+ }
+
static void BuildFontNameCache()
{
#if BF_PLATFORM_WINDOWS
@@ -383,6 +516,14 @@ namespace Beefy.gfx
void GetFontPath(StringView fontName, String path)
{
+ if (fontName.StartsWith('['))
+ {
+ path.Set(fontName);
+ if (FilePackManager.TryMakeMemoryString(path))
+ return;
+ path.Clear();
+ }
+
if (fontName.Contains('.'))
{
Path.GetAbsolutePath(fontName, BFApp.sApp.mInstallDir, path);
@@ -666,13 +807,23 @@ namespace Beefy.gfx
mLowCharData[(int)checkChar] = charData;
else
mCharData[checkChar] = charData;
- return charData;
+ break;
}
-
+ }
+
+ if (charData == null)
+ {
if (checkChar == (char32)'?')
return null;
return GetCharData((char32)'?');
}
+
+ if (mFontEffectStack?.IsEmpty == false)
+ {
+ var fontEffect = mFontEffectStack.Back;
+ charData = fontEffect.Apply(this, charData);
+ }
+
return charData;
}
@@ -828,10 +979,10 @@ namespace Beefy.gfx
}
}
- public void Draw(Graphics g, StringView theString, FontMetrics* fontMetrics = null)
+ public void Draw(Graphics g, float x, float y, StringView theString, FontMetrics* fontMetrics = null)
{
- float curX = 0;
- float curY = 0;
+ float curX = x;
+ float curY = y;
Matrix newMatrix = Matrix();
bool hasClipRect = g.mClipRect.HasValue;
@@ -839,7 +990,10 @@ namespace Beefy.gfx
uint32 color = g.mColor;
- g.PushTextRenderState();
+ bool usingTextRenderState = mFontEffectStack?.IsEmpty != false;
+
+ if (usingTextRenderState)
+ g.PushTextRenderState();
float markTopOfs = 0;
float markBotOfs = 0;
@@ -869,6 +1023,8 @@ namespace Beefy.gfx
}
CharData charData = GetCharData(c);
+ if (charData == null)
+ continue;
float drawX = curX + charData.mXOffset;
float drawY = curY + charData.mYOffset;
@@ -943,7 +1099,11 @@ namespace Beefy.gfx
prevChar = c;
}
- g.PopRenderState();
+ if (usingTextRenderState)
+ g.PopRenderState();
+
+ if (fontMetrics != null)
+ fontMetrics.mMaxX = Math.Max(fontMetrics.mMaxX, curX);
}
public float Draw(Graphics g, StringView theString, float x, float y, int32 justification = -1, float width = 0, FontOverflowMode stringEndMode = FontOverflowMode.Overflow, FontMetrics* fontMetrics = null)
@@ -1060,14 +1220,11 @@ namespace Beefy.gfx
if (g != null)
{
- using (g.PushTranslate(useX, useY))
- Draw(g, workingStr, fontMetrics);
- }
- else
- {
- if (fontMetrics != null)
- fontMetrics.mMaxWidth = Math.Max(fontMetrics.mMaxWidth, GetWidth(workingStr));
+ Draw(g, useX, useY, workingStr, fontMetrics);
}
+
+ if (fontMetrics != null)
+ fontMetrics.mMaxWidth = Math.Max(fontMetrics.mMaxWidth, GetWidth(workingStr));
drawHeight += GetLineSpacing();
if (fontMetrics != null)
diff --git a/BeefLibs/Beefy2D/src/gfx/Graphics.bf b/BeefLibs/Beefy2D/src/gfx/Graphics.bf
index 45293fb9..00d33739 100644
--- a/BeefLibs/Beefy2D/src/gfx/Graphics.bf
+++ b/BeefLibs/Beefy2D/src/gfx/Graphics.bf
@@ -547,6 +547,11 @@ namespace Beefy.gfx
(int)mPixelSnapping, mSmoothing ? 1 : 0, mAdditive ? 1 : 0, (int)g.mColor);*/
}
+ public void DrawCentered(Image image, float x = 0, float y = 0)
+ {
+ Draw(image, x - image.mWidth / 2, y - image.mHeight / 2);
+ }
+
public void Draw(IDrawable drawable, Vector2 vec) => Draw(drawable, vec.mX, vec.mY);
public void DrawButton(Image image, float x, float y, float width)
@@ -924,6 +929,11 @@ namespace Beefy.gfx
FillRect(x, y + height - thickness, width, thickness);
}
+ public void OutlineRect(Rect rect)
+ {
+ OutlineRect(rect.mX, rect.mY, rect.mWidth, rect.mHeight);
+ }
+
public void FillRectGradient(float x, float y, float width, float height,
Color colorTopLeft, Color colorTopRight, Color colorBotLeft, Color colorBotRight)
{
@@ -943,6 +953,39 @@ namespace Beefy.gfx
Gfx_SetDrawVertex(5, m.tx + (m.a + m.c), m.ty + (m.b + m.d), 0, 0, 0, Color.Mult(mColor, colorBotRight));
}
+ public void FillRectGradientEx(float x, float y, float width, float height,
+ Color colorTopLeft, Color colorTopRight, Color colorBotLeft, Color colorBotRight)
+ {
+ Matrix m = Matrix.IdentityMatrix;
+ m.SetMultiplied(x, y, width, height, ref mMatrix);
+
+ int32 r = ((int32)colorTopLeft.R + colorTopRight.R + colorBotLeft.R + colorBotRight.R) / 4;
+ int32 g = ((int32)colorTopLeft.G + colorTopRight.G + colorBotLeft.G + colorBotRight.G) / 4;
+ int32 b = ((int32)colorTopLeft.B + colorTopRight.B + colorBotLeft.B + colorBotRight.B) / 4;
+ int32 a = ((int32)colorTopLeft.A + colorTopRight.A + colorBotLeft.A + colorBotRight.A) / 4;
+ Color centerColor = .(r, g, b, a);
+
+ //TODO: Multiply color
+
+ Gfx_AllocTris(mWhiteDot.mNativeTextureSegment, 12);
+
+ Gfx_SetDrawVertex(0, m.tx, m.ty, 0, 0, 0, Color.Mult(mColor, colorTopLeft));
+ Gfx_SetDrawVertex(1, m.tx + m.a, m.ty + m.b, 0, 0, 0, Color.Mult(mColor, colorTopRight));
+ Gfx_SetDrawVertex(2, m.tx + (m.a + m.c) * 0.5f, m.ty + (m.b + m.d) * 0.5f, 0, 0, 0, Color.Mult(mColor, centerColor));
+
+ Gfx_CopyDrawVertex(3, 1); // Top Right
+ Gfx_SetDrawVertex(4, m.tx + (m.a + m.c), m.ty + (m.b + m.d), 0, 0, 0, Color.Mult(mColor, colorBotRight));
+ Gfx_CopyDrawVertex(5, 2); // Center
+
+ Gfx_CopyDrawVertex(6, 2); // Center
+ Gfx_CopyDrawVertex(7, 4); // Bottom Right
+ Gfx_SetDrawVertex(8, m.tx + m.c, m.ty + m.d, 0, 0, 0, Color.Mult(mColor, colorBotLeft));
+
+ Gfx_CopyDrawVertex(9, 8); // Bottom Left
+ Gfx_CopyDrawVertex(10, 0); // Top Left
+ Gfx_CopyDrawVertex(11, 2); // Center
+ }
+
void DoDrawLine(float x0, float y0, float x1, float y1, float width)
{
Image img = GetBorderedWhiteSquare(Math.Max((int)width, 1));
diff --git a/BeefLibs/Beefy2D/src/gfx/Image.bf b/BeefLibs/Beefy2D/src/gfx/Image.bf
index 5f737827..f5db7e87 100644
--- a/BeefLibs/Beefy2D/src/gfx/Image.bf
+++ b/BeefLibs/Beefy2D/src/gfx/Image.bf
@@ -3,6 +3,7 @@ using System.Collections;
using System.Text;
using Beefy.utils;
using System.Diagnostics;
+using res;
#if STUDIO_CLIENT
using Beefy.ipc;
@@ -70,6 +71,9 @@ namespace Beefy.gfx
[CallingConvention(.Stdcall), CLink]
static extern int32 Gfx_Texture_GetHeight(void* textureSegment);
+ [CallingConvention(.Stdcall), CLink]
+ static extern void Gfx_ApplyEffect(void* destTextureSegment, void* srcTextureSegment, char8* options);
+
public this()
{
}
@@ -94,11 +98,13 @@ namespace Beefy.gfx
return CreateFromNativeTextureSegment(aNativeTextureSegment);
}
- public static Image LoadFromFile(String fileName, LoadFlags flags = .None)
+ public static Image LoadFromFile(StringView fileName, LoadFlags flags = .None)
{
scope AutoBeefPerf("Image.LoadFromFile");
- void* aNativeTextureSegment = Gfx_LoadTexture(fileName, (int32)flags);
+ var useFileName = scope String()..Append(fileName);
+ FilePackManager.TryMakeMemoryString(useFileName);
+ void* aNativeTextureSegment = Gfx_LoadTexture(useFileName, (int32)flags);
if (aNativeTextureSegment == null)
{
if (flags.HasFlag(.FatalError))
@@ -186,6 +192,11 @@ namespace Beefy.gfx
Gfx_ModifyTextureSegment(mNativeTextureSegment, srcImage.mNativeTextureSegment, (int32)srcX, (int32)srcY, (int32)srcWidth, (int32)srcHeight);
}
+ public void ApplyEffect(Image destImage, StringView options)
+ {
+ Gfx_ApplyEffect(destImage.mNativeTextureSegment, mNativeTextureSegment, options.ToScopeCStr!());
+ }
+
public void SetBits(int destX, int destY, int destWidth, int destHeight, int srcPitch, uint32* bits)
{
Gfx_Texture_SetBits(mNativeTextureSegment, (.)destX, (.)destY, (.)destWidth, (.)destHeight, (.)srcPitch, bits);
diff --git a/BeefLibs/Beefy2D/src/gfx/ImageAtlas.bf b/BeefLibs/Beefy2D/src/gfx/ImageAtlas.bf
new file mode 100644
index 00000000..f600f752
--- /dev/null
+++ b/BeefLibs/Beefy2D/src/gfx/ImageAtlas.bf
@@ -0,0 +1,66 @@
+using System.Collections;
+using System;
+namespace Beefy.gfx;
+
+class ImageAtlas
+{
+ public class Page
+ {
+ public Image mImage ~ delete _;
+ public int32 mCurX;
+ public int32 mCurY;
+ public int32 mMaxRowHeight;
+ }
+
+ public List mPages = new .() ~ DeleteContainerAndItems!(_);
+ public bool mAllowMultiplePages = true;
+
+ public int32 mImageWidth = 1024;
+ public int32 mImageHeight = 1024;
+
+ public this()
+ {
+
+ }
+
+ public Image Alloc(int32 width, int32 height)
+ {
+ Page page = null;
+ if (!mPages.IsEmpty)
+ {
+ page = mPages.Back;
+ if (page.mCurX + (int)width > page.mImage.mSrcWidth)
+ {
+ // Move down to next row
+ page.mCurX = 0;
+ page.mCurY += page.mMaxRowHeight;
+ page.mMaxRowHeight = 0;
+ }
+
+ if (page.mCurY + height > page.mImage.mSrcHeight)
+ {
+ // Doesn't fit
+ page = null;
+ }
+ }
+
+ if (page == null)
+ {
+ page = new .();
+ page.mImage = Image.CreateDynamic(mImageWidth, mImageHeight);
+
+ uint32* colors = new uint32[mImageWidth*mImageHeight]*;
+ defer delete colors;
+ for (int i < mImageWidth*mImageHeight)
+ colors[i] = 0xFF000000 | (.)i;
+
+ page.mImage.SetBits(0, 0, mImageWidth, mImageHeight, mImageWidth, colors);
+ mPages.Add(page);
+ }
+
+ Image image = page.mImage.CreateImageSegment(page.mCurX, page.mCurY, width, height);
+ page.mCurX += width;
+ page.mMaxRowHeight = Math.Max(page.mMaxRowHeight, height);
+ return image;
+ }
+}
\ No newline at end of file
diff --git a/BeefLibs/Beefy2D/src/gfx/Matrix.bf b/BeefLibs/Beefy2D/src/gfx/Matrix.bf
index d48dfed6..a03e2d25 100644
--- a/BeefLibs/Beefy2D/src/gfx/Matrix.bf
+++ b/BeefLibs/Beefy2D/src/gfx/Matrix.bf
@@ -168,6 +168,11 @@ namespace Beefy.gfx
return Point(tx + a * point.x + c * point.y, ty + b * point.x + d * point.y);
}
+ public Vector2 Multiply(Vector2 point)
+ {
+ return Vector2(tx + a * point.mX + c * point.mY, ty + b * point.mX + d * point.mY);
+ }
+
public void Invert() mut
{
float _a = a;
diff --git a/BeefLibs/Beefy2D/src/gfx/Shader.bf b/BeefLibs/Beefy2D/src/gfx/Shader.bf
index 25e0de6a..334888b5 100644
--- a/BeefLibs/Beefy2D/src/gfx/Shader.bf
+++ b/BeefLibs/Beefy2D/src/gfx/Shader.bf
@@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Text;
+using res;
namespace Beefy.gfx
{
@@ -28,9 +29,22 @@ namespace Beefy.gfx
[CallingConvention(.Stdcall), CLink]
static extern void* Gfx_GetShaderParam(void* shader, String paramName);
- public static Shader CreateFromFile(String fileName, VertexDefinition vertexDefinition)
+ public static Shader CreateFromFile(StringView fileName, VertexDefinition vertexDefinition)
{
- void* aNativeShader = Gfx_LoadShader(fileName, vertexDefinition.mNativeVertexDefinition);
+ var useFileName = scope String(fileName);
+ if (FilePackManager.TryMakeMemoryString(useFileName, ".fx_VS_vs_4_0"))
+ {
+ var useFileName2 = scope String(fileName);
+ if (FilePackManager.TryMakeMemoryString(useFileName2, ".fx_PS_ps_4_0"))
+ {
+ useFileName.Append("\n");
+ useFileName.Append(useFileName2);
+ }
+ }
+
+ FilePackManager.TryMakeMemoryString(useFileName, ".fx");
+
+ void* aNativeShader = Gfx_LoadShader(useFileName, vertexDefinition.mNativeVertexDefinition);
if (aNativeShader == null)
return null;
diff --git a/BeefLibs/Beefy2D/src/res/FilePack.bf b/BeefLibs/Beefy2D/src/res/FilePack.bf
new file mode 100644
index 00000000..1e48fcb7
--- /dev/null
+++ b/BeefLibs/Beefy2D/src/res/FilePack.bf
@@ -0,0 +1,592 @@
+#pragma warning disable 168
+
+using System;
+using System.IO;
+using System.Collections;
+using utils;
+
+namespace res;
+
+class FilePack
+{
+ public class FindFileData
+ {
+ public enum Flags
+ {
+ None = 0,
+ Files = 1,
+ Directories = 2,
+ }
+
+ public virtual bool MoveNext() => false;
+ public virtual void GetFileName(String outStr) {}
+ public virtual void GetFilePath(String outStr) {}
+
+ public virtual Platform.BfpTimeStamp GetWriteTime() => 0;
+ public virtual Platform.BfpTimeStamp GetCreatedTime() => 0;
+ public virtual Platform.BfpTimeStamp GetAccessTime() => 0;
+ public virtual Platform.BfpFileAttributes GetFileAttributes() => 0;
+ public virtual int64 GetFileSize() => 0;
+ }
+
+ public class FileEntry
+ {
+ public virtual void GetFilePath(String outFileName) {}
+ public virtual void GetFileName(String outFileName) {}
+ public virtual Result> GetFileData() => .Err;
+ public virtual FindFileData CreateFindFileData(StringView wildcard, FindFileData.Flags flags) => null;
+ public virtual Result ExtractTo(StringView dest) => .Err;
+ }
+
+ public virtual StringView FilePath => default;
+
+ public virtual bool PathPackMatches(StringView path)
+ {
+ return false;
+ }
+
+ public virtual Stream OpenFile(StringView file)
+ {
+ return null;
+ }
+
+ public virtual FileEntry GetFileEntry(StringView path)
+ {
+ return null;
+ }
+}
+
+#if BF_DEPENDS_MINIZ
+class ZipFilePack : FilePack
+{
+ public class ZipFindFileData : FindFileData
+ {
+ public ZipFileEntry mZipFileEntry;
+ public int32 mIdx = -1;
+ public String mWildcard ~ delete _;
+ public Flags mFlags;
+
+ public override bool MoveNext()
+ {
+ while (true)
+ {
+ mIdx++;
+ if ((mZipFileEntry.mFiles == null) || (mIdx >= mZipFileEntry.mFiles.Count))
+ return false;
+
+ var fileName = GetFileName(.. scope .());
+ if (Path.WildcareCompare(fileName, mWildcard))
+ break;
+ }
+ return true;
+ }
+
+ public override void GetFilePath(String outFileName)
+ {
+ var fileEntry = mZipFileEntry.mFiles[mIdx];
+ fileEntry.mZipEntry.GetFileName(outFileName);
+ }
+
+ public override void GetFileName(String outFileName)
+ {
+ var fileEntry = mZipFileEntry.mFiles[mIdx];
+ var filePath = fileEntry.mZipEntry.GetFileName(.. scope .());
+ Path.GetFileName(filePath, outFileName);
+ }
+
+ public override Platform.BfpTimeStamp GetWriteTime()
+ {
+ return (.)mZipFileEntry.mFiles[mIdx].mZipEntry.GetTimestamp();
+ }
+
+ public override Platform.BfpTimeStamp GetCreatedTime()
+ {
+ return GetWriteTime();
+ }
+
+ public override Platform.BfpTimeStamp GetAccessTime()
+ {
+ return GetWriteTime();
+ }
+
+ public override Platform.BfpFileAttributes GetFileAttributes()
+ {
+ return .Normal;
+ }
+
+ public override int64 GetFileSize()
+ {
+ return mZipFileEntry.mFiles[mIdx].mZipEntry.GetUncompressedSize();
+ }
+ }
+
+ public class ZipFileEntry : FileEntry
+ {
+ public MiniZ.ZipFile.Entry mZipEntry ~ delete _;
+ public List mFiles ~ delete _;
+
+ public override Result> GetFileData()
+ {
+ return mZipEntry.ExtractToMemory();
+ }
+
+ public override FindFileData CreateFindFileData(StringView wildcard, FindFileData.Flags flags)
+ {
+ var findFileData = new ZipFindFileData();
+ findFileData.mZipFileEntry = this;
+ findFileData.mWildcard = new .(wildcard);
+ findFileData.mFlags = flags;
+ return findFileData;
+ }
+
+ public override Result ExtractTo(StringView dest)
+ {
+ return mZipEntry.ExtractToFile(dest);
+ }
+
+ public override void GetFilePath(String outFileName)
+ {
+ mZipEntry.GetFileName(outFileName);
+ }
+
+ public override void GetFileName(String outFileName)
+ {
+ var filePath = mZipEntry.GetFileName(.. scope .());
+ Path.GetFileName(filePath, outFileName);
+ }
+ }
+
+ String mPath ~ delete _;
+ MiniZ.ZipFile mZipFile ~ delete _;
+ Dictionary mFileMap ~ DeleteDictionaryAndKeysAndValues!(_);
+ ZipFileEntry mRootFileEntry;
+
+ public List RootFiles
+ {
+ get
+ {
+ FillFileMap();
+ return mRootFileEntry.mFiles;
+ }
+ }
+
+ public this()
+ {
+
+ }
+
+ public override StringView FilePath => mPath;
+
+ public Result Init(StringView path)
+ {
+ mPath = new .(path);
+ mZipFile = new MiniZ.ZipFile();
+ Try!(mZipFile.OpenMapped(path));
+ return .Ok;
+ }
+
+ public Result Init(StringView path, Range range)
+ {
+ mPath = new .(path);
+ mZipFile = new MiniZ.ZipFile();
+ Try!(mZipFile.OpenMapped(path, range));
+ return .Ok;
+ }
+
+ public Result Init(StringView path, Span data)
+ {
+ mPath = new .(path);
+ mZipFile = new MiniZ.ZipFile();
+ Try!(mZipFile.Open(data));
+ return .Ok;
+ }
+
+ void FillFileMap()
+ {
+ if (mFileMap != null)
+ return;
+ mRootFileEntry = new .();
+ mRootFileEntry.mFiles = new .();
+ mFileMap = new .();
+ mFileMap[new .("/")] = mRootFileEntry;
+
+ for (int i < mZipFile.GetNumFiles())
+ {
+ var entry = new MiniZ.ZipFile.Entry();
+ mZipFile.SelectEntry(i, entry);
+ var fileName = entry.GetFileName(.. new .());
+
+ ZipFileEntry zipFileEntry = new .();
+ zipFileEntry.mZipEntry = entry;
+ mFileMap[fileName] = zipFileEntry;
+
+ var checkFileName = StringView(fileName);
+ if (checkFileName.EndsWith('/'))
+ checkFileName.RemoveFromEnd(1);
+ var fileDir = Path.GetDirectoryPath(checkFileName, .. scope .());
+ fileDir.Append("/");
+ if (mFileMap.TryGet(fileDir, ?, var dirEntry))
+ {
+ if (dirEntry.mFiles == null)
+ dirEntry.mFiles = new .();
+ dirEntry.mFiles.Add(zipFileEntry);
+ }
+ }
+ }
+
+ public override FileEntry GetFileEntry(StringView path)
+ {
+ var path;
+ FillFileMap();
+ if (path.StartsWith('/'))
+ path.RemoveFromStart(1);
+ if (path.Contains('\\'))
+ {
+ var pathStr = scope:: String(path);
+ pathStr.Replace('\\', '/');
+ path = pathStr;
+ }
+ if (mFileMap.TryGetAlt(path, ?, var entry))
+ return entry;
+ return null;
+ }
+
+ public override bool PathPackMatches(StringView path)
+ {
+ return Path.Equals(path, mPath);
+ }
+
+ public override Stream OpenFile(StringView file)
+ {
+ return base.OpenFile(file);
+ }
+}
+#endif
+
+static class FilePackManager
+{
+ public static List sFilePacks = new .() ~ DeleteContainerAndItems!(_);
+
+ public static void AddFilePack(FilePack filePack)
+ {
+ sFilePacks.Add(filePack);
+ }
+
+ public static void RemoveFilePack(FilePack filePack)
+ {
+ sFilePacks.Remove(filePack);
+ }
+
+ [StaticHook(typeof(Platform.Hook), typeof(FilePackManager.HookOverrides)), AlwaysInclude]
+ public static struct Hook
+ {
+ }
+
+ static StableIndexedList sStreams = new .() ~ { DeleteContainerAndItems!(_); _ = null; }
+ static StableIndexedList sFindFileData = new .() ~ { DeleteContainerAndItems!(_); _ = null; }
+
+ static Stream TryGetStream(Platform.BfpFile* file, out int idx)
+ {
+ idx = (int)(void*)file;
+ return sStreams?.SafeGet(idx);
+ }
+
+ static FilePack.FindFileData TryGetFindFileData(Platform.BfpFindFileData* findFileData, out int idx)
+ {
+ idx = (int)(void*)findFileData;
+ return sFindFileData?.SafeGet(idx);
+ }
+
+ public static struct HookOverrides
+ {
+ [StaticHook]
+ public static Platform.BfpFile* BfpFile_Create(char8* name, Platform.BfpFileCreateKind createKind, Platform.BfpFileCreateFlags createFlags, Platform.BfpFileAttributes createdFileAttrs, Platform.BfpFileResult* outResult)
+ {
+ if ((createKind == .OpenExisting) && (!createFlags.HasFlag(.Write)))
+ {
+ if (var memory = TryGetMemory(.(name)))
+ {
+ var memoryStream = new FixedMemoryStream(memory);
+ return (.)(void*)sStreams.Add(memoryStream);
+ }
+ }
+
+ return Hook.sBfpFile_Create(name, createKind, createFlags, createdFileAttrs, outResult);
+ }
+
+ [StaticHook]
+ public static int BfpFile_GetSystemHandle(Platform.BfpFile* file)
+ {
+ return Hook.sBfpFile_GetSystemHandle(file);
+ }
+
+ [StaticHook]
+ public static void BfpFile_Release(Platform.BfpFile* file)
+ {
+ if (var stream = TryGetStream(file, var idx))
+ {
+ sStreams.RemoveAt(idx);
+ delete stream;
+ return;
+ }
+
+ Hook.sBfpFile_Release(file);
+ }
+
+ [StaticHook]
+ public static int BfpFile_Write(Platform.BfpFile* file, void* buffer, int size, int timeoutMS, Platform.BfpFileResult* outResult)
+ {
+ return Hook.sBfpFile_Write(file, buffer, size, timeoutMS, outResult);
+ }
+
+ [StaticHook]
+ public static int BfpFile_Read(Platform.BfpFile* file, void* buffer, int size, int timeoutMS, Platform.BfpFileResult* outResult)
+ {
+ if (var stream = TryGetStream(file, ?))
+ {
+ switch (stream.TryRead(.((.)buffer, size)))
+ {
+ case .Ok(let val):
+ if (val != size)
+ {
+ if (outResult != null)
+ *outResult = .PartialData;
+ }
+ else if (outResult != null)
+ *outResult = .Ok;
+ return val;
+ case .Err(let err):
+ if (outResult != null)
+ *outResult = .UnknownError;
+ return 0;
+ }
+ }
+
+ return Hook.sBfpFile_Read(file, buffer, size, timeoutMS, outResult);
+ }
+
+ [StaticHook]
+ public static void BfpFile_Flush(Platform.BfpFile* file)
+ {
+ if (var stream = TryGetStream(file, ?))
+ {
+ return;
+ }
+
+ Hook.sBfpFile_Flush(file);
+ }
+
+ [StaticHook]
+ public static int64 BfpFile_GetFileSize(Platform.BfpFile* file)
+ {
+ if (var stream = TryGetStream(file, ?))
+ {
+ return stream.Length;
+ }
+
+ return Hook.sBfpFile_GetFileSize(file);
+ }
+
+ [StaticHook]
+ public static int64 BfpFile_Seek(Platform.BfpFile* file, int64 offset, Platform.BfpFileSeekKind seekKind)
+ {
+ if (var stream = TryGetStream(file, ?))
+ {
+ stream.Seek(offset, (.)seekKind);
+ return stream.Position;
+ }
+
+ return Hook.sBfpFile_Seek(file, offset, seekKind);
+
+ }
+
+ [StaticHook]
+ public static void BfpFile_Truncate(Platform.BfpFile* file, Platform.BfpFileResult* outResult)
+ {
+ Hook.sBfpFile_Truncate(file, outResult);
+ }
+
+ [StaticHook]
+ public static Platform.BfpFindFileData* BfpFindFileData_FindFirstFile(char8* path, Platform.BfpFindFileFlags flags, Platform.BfpFileResult* outResult)
+ {
+ StringView pathStr = .(path);
+
+ String findDir = scope .();
+ Path.GetDirectoryPath(pathStr, findDir).IgnoreError();
+ findDir.Append("/");
+ String wildcard = scope .();
+ Path.GetFileName(pathStr, wildcard);
+
+ if (var packFile = FindFilePackEntry(findDir))
+ {
+ var packFindFile = packFile.CreateFindFileData(wildcard, (.)flags);
+ if (!packFindFile.MoveNext())
+ {
+ delete packFile;
+ if (outResult != null)
+ *outResult = .NoResults;
+ return null;
+ }
+ return (.)(void*)sFindFileData.Add(packFindFile);
+ }
+
+ return Hook.sBfpFindFileData_FindFirstFile(path, flags, outResult);
+ }
+
+ [StaticHook]
+ public static bool BfpFindFileData_FindNextFile(Platform.BfpFindFileData* findData)
+ {
+ if (var packFindFile = TryGetFindFileData(findData, ?))
+ {
+ return packFindFile.MoveNext();
+ }
+
+ return Hook.sBfpFindFileData_FindNextFile(findData);
+ }
+
+ [StaticHook]
+ public static void BfpFindFileData_GetFileName(Platform.BfpFindFileData* findData, char8* outName, int32* inOutNameSize, Platform.BfpFileResult* outResult)
+ {
+ if (var packFindFile = TryGetFindFileData(findData, ?))
+ {
+ var str = packFindFile.GetFileName(.. scope .());
+ Platform.SetStrHelper(str, outName, inOutNameSize, (.)outResult);
+ return;
+ }
+
+ Hook.sBfpFindFileData_GetFileName(findData, outName, inOutNameSize, outResult);
+ }
+
+ [StaticHook]
+ public static Platform.BfpTimeStamp BfpFindFileData_GetTime_LastWrite(Platform.BfpFindFileData* findData)
+ {
+ if (var packFindFile = TryGetFindFileData(findData, ?))
+ {
+ return packFindFile.GetWriteTime();
+ }
+
+ return Hook.sBfpFindFileData_GetTime_LastWrite(findData);
+ }
+
+ [StaticHook]
+ public static Platform.BfpTimeStamp BfpFindFileData_GetTime_Created(Platform.BfpFindFileData* findData)
+ {
+ if (var packFindFile = TryGetFindFileData(findData, ?))
+ {
+ return packFindFile.GetCreatedTime();
+ }
+
+ return Hook.sBfpFindFileData_GetTime_Created(findData);
+ }
+
+ [StaticHook]
+ public static Platform.BfpTimeStamp BfpFindFileData_GetTime_Access(Platform.BfpFindFileData* findData)
+ {
+ if (var packFindFile = TryGetFindFileData(findData, ?))
+ {
+ return packFindFile.GetAccessTime();
+ }
+
+ return Hook.sBfpFindFileData_GetTime_Access(findData);
+ }
+
+ [StaticHook]
+ public static Platform.BfpFileAttributes BfpFindFileData_GetFileAttributes(Platform.BfpFindFileData* findData)
+ {
+ if (var packFindFile = TryGetFindFileData(findData, ?))
+ {
+ return packFindFile.GetFileAttributes();
+ }
+
+ return Hook.sBfpFindFileData_GetFileAttributes(findData);
+ }
+
+ [StaticHook]
+ public static int64 BfpFindFileData_GetFileSize(Platform.BfpFindFileData* findData)
+ {
+ if (var packFindFile = TryGetFindFileData(findData, ?))
+ {
+ return packFindFile.GetFileSize();
+ }
+
+ return Hook.sBfpFindFileData_GetFileSize(findData);
+ }
+
+ [StaticHook]
+ public static void BfpFindFileData_Release(Platform.BfpFindFileData* findData)
+ {
+ if (var packFindFile = TryGetFindFileData(findData, var idx))
+ {
+ sFindFileData.RemoveAt(idx);
+ delete packFindFile;
+ return;
+ }
+
+ Hook.sBfpFindFileData_Release(findData);
+ }
+ }
+
+ public static FilePack.FileEntry FindFilePackEntry(StringView path)
+ {
+ if (path.StartsWith('['))
+ {
+ int packEndIdx = path.IndexOf(']');
+
+ if (packEndIdx != -1)
+ {
+ var packPath = path.Substring(1, packEndIdx - 1);
+ var filePath = path.Substring(packEndIdx + 1);
+ for (var filePack in sFilePacks)
+ {
+ if (filePack.PathPackMatches(packPath))
+ {
+ return filePack.GetFileEntry(filePath);
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ public static Result> TryGetMemory(StringView path)
+ {
+ var fileEntry = FindFilePackEntry(path);
+ if (fileEntry == null)
+ return .Err;
+
+ return fileEntry.GetFileData();
+ }
+
+ public static bool TryMakeMemoryString(String path)
+ {
+ if (TryGetMemory(path) case .Ok(let val))
+ {
+ String prevPath = scope .()..Append(path);
+
+ var ext = Path.GetExtension(path, .. scope .());
+ path.Set(scope $"@{(int)(void*)val.Ptr:X}:{val.Length}:{prevPath}");
+ return true;
+ }
+ return false;
+ }
+
+ public static bool TryMakeMemoryString(String path, params Span checkExts)
+ {
+ var ext = Path.GetExtension(path, .. scope .());
+ if (!ext.IsEmpty)
+ return TryMakeMemoryString(path);
+
+ for (var checkExt in checkExts)
+ {
+ var testPath = scope String();
+ testPath.Append(path);
+ testPath.Append(checkExt);
+ if (TryMakeMemoryString(testPath))
+ {
+ path.Set(testPath);
+ return true;
+ }
+ }
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/BeefLibs/Beefy2D/src/res/PEFile.bf b/BeefLibs/Beefy2D/src/res/PEFile.bf
new file mode 100644
index 00000000..e09b0531
--- /dev/null
+++ b/BeefLibs/Beefy2D/src/res/PEFile.bf
@@ -0,0 +1,275 @@
+#pragma warning disable 168
+
+using System;
+using System.IO;
+
+namespace Beefy.Res;
+
+
+static class PEFile
+{
+ public const uint16 PE_MACHINE_X86 = 0x14c;
+ public const uint16 PE_MACHINE_X64 = 0x8664;
+
+ [CRepr]
+ public struct PEHeader
+ {
+ public uint16 e_magic; // Magic number
+ public uint16 e_cblp; // uint8s on last page of file
+ public uint16 e_cp; // Pages in file
+ public uint16 e_crlc; // Relocations
+ public uint16 e_cparhdr; // Size of header in paragraphs
+ public uint16 e_minalloc; // Minimum extra paragraphs needed
+ public uint16 e_maxalloc; // Maximum extra paragraphs needed
+ public uint16 e_ss; // Initial (relative) SS value
+ public uint16 e_sp; // Initial SP value
+ public uint16 e_csum; // Checksum
+ public uint16 e_ip; // Initial IP value
+ public uint16 e_cs; // Initial (relative) CS value
+ public uint16 e_lfarlc; // File address of relocation table
+ public uint16 e_ovno; // Overlay number
+ public uint16[4] e_res; // Reserved uint16s
+ public uint16 e_oemid; // OEM identifier (for e_oeminfo)
+ public uint16 e_oeminfo; // OEM information; e_oemid specific
+ public uint16[10] e_res2; // Reserved uint16s
+ public int32 e_lfanew; // File address of new exe header
+ };
+
+
+ [CRepr]
+ public struct PEFileHeader
+ {
+ public uint16 mMachine;
+ public uint16 mNumberOfSections;
+ public uint32 mTimeDateStamp;
+ public uint32 mPointerToSymbolTable;
+ public uint32 mNumberOfSymbols;
+ public uint16 mSizeOfOptionalHeader;
+ public uint16 mCharacteristics;
+ };
+
+ [CRepr]
+ public struct PEImportObjectHeader
+ {
+ public uint16 mSig1;
+ public uint16 mSig2;
+ public uint16 mVersion;
+ public uint16 mMachine;
+ public uint32 mTimeDateStamp;
+ public uint32 mDataSize;
+ public uint16 mHint;
+ public uint16 mType;
+ };
+
+ [CRepr]
+ public struct PEDataDirectory
+ {
+ public uint32 mVirtualAddress;
+ public uint32 mSize;
+ };
+
+ [CRepr]
+ public struct PEOptionalHeader32
+ {
+ //
+ // Standard fields.
+ //
+
+ public uint16 mMagic;
+ public uint8 mMajorLinkerVersion;
+ public uint8 mMinorLinkerVersion;
+ public uint32 mSizeOfCode;
+ public uint32 mSizeOfInitializedData;
+ public uint32 mSizeOfUninitializedData;
+ public uint32 mAddressOfEntryPoint;
+ public uint32 mBaseOfCode;
+ public uint32 mBaseOfData;
+
+ //
+ // NT additional fields.
+ //
+
+ public uint32 mImageBase;
+ public uint32 mSectionAlignment;
+ public uint32 mFileAlignment;
+ public uint16 mMajorOperatingSystemVersion;
+ public uint16 mMinorOperatingSystemVersion;
+ public uint16 mMajorImageVersion;
+ public uint16 mMinorImageVersion;
+ public uint16 mMajorSubsystemVersion;
+ public uint16 mMinorSubsystemVersion;
+ public uint32 mReserved1;
+ public uint32 mSizeOfImage;
+ public uint32 mSizeOfHeaders;
+ public uint32 mCheckSum;
+ public uint16 mSubsystem;
+ public uint16 mDllCharacteristics;
+ public uint32 mSizeOfStackReserve;
+ public uint32 mSizeOfStackCommit;
+ public uint32 mSizeOfHeapReserve;
+ public uint32 mSizeOfHeapCommit;
+ public uint32 mLoaderFlags;
+ public uint32 mNumberOfRvaAndSizes;
+ public PEDataDirectory[16] mDataDirectory;
+ };
+
+ [CRepr]
+ public struct PEOptionalHeader64
+ {
+ //
+ // Standard fields.
+ //
+
+ public uint16 mMagic;
+ public uint8 mMajorLinkerVersion;
+ public uint8 mMinorLinkerVersion;
+ public uint32 mSizeOfCode;
+ public uint32 mSizeOfInitializedData;
+ public uint32 mSizeOfUninitializedData;
+ public uint32 mAddressOfEntryPoint;
+ public uint32 mBaseOfCode;
+
+ //
+ // NT additional fields.
+ //
+
+ public uint64 mImageBase;
+ public uint32 mSectionAlignment;
+ public uint32 mFileAlignment;
+ public uint16 mMajorOperatingSystemVersion;
+ public uint16 mMinorOperatingSystemVersion;
+ public uint16 mMajorImageVersion;
+ public uint16 mMinorImageVersion;
+ public uint16 mMajorSubsystemVersion;
+ public uint16 mMinorSubsystemVersion;
+ public uint32 mReserved1;
+ public uint32 mSizeOfImage;
+ public uint32 mSizeOfHeaders;
+ public uint32 mCheckSum;
+ public uint16 mSubsystem;
+ public uint16 mDllCharacteristics;
+ public uint64 mSizeOfStackReserve;
+ public uint64 mSizeOfStackCommit;
+ public uint64 mSizeOfHeapReserve;
+ public uint64 mSizeOfHeapCommit;
+ public uint32 mLoaderFlags;
+ public uint32 mNumberOfRvaAndSizes;
+ public PEDataDirectory[16] mDataDirectory;
+ };
+
+ [CRepr]
+ struct PE_NTHeaders32
+ {
+ uint32 mSignature;
+ PEFileHeader mFileHeader;
+ PEOptionalHeader32 mOptionalHeader;
+ };
+
+ [CRepr]
+ public struct PE_NTHeaders64
+ {
+ public uint32 mSignature;
+ public PEFileHeader mFileHeader;
+ public PEOptionalHeader64 mOptionalHeader;
+ };
+
+ const int IMAGE_SIZEOF_SHORT_NAME = 8;
+
+ [CRepr]
+ public struct PESectionHeader
+ {
+ public char8[IMAGE_SIZEOF_SHORT_NAME] mName;
+ public uint32 mVirtualSize;
+ public uint32 mVirtualAddress;
+ public uint32 mSizeOfRawData;
+ public uint32 mPointerToRawData;
+ public uint32 mPointerToRelocations;
+ public uint32 mPointerToLineNumbers;
+ public uint16 mNumberOfRelocations;
+ public uint16 mNumberOfLineNumbers;
+ public uint32 mCharacteristics;
+ };
+
+ [CRepr]
+ struct COFFRelocation
+ {
+ uint32 mVirtualAddress;
+ uint32 mSymbolTableIndex;
+ uint16 mType;
+ };
+
+ [CRepr]
+ struct PE_SymInfo
+ {
+ [Union]
+ public struct Name
+ {
+ public char8[8] mName;
+ public int32[2] mNameOfs;
+ }
+
+ public Name mName;
+
+ /*union
+ {
+ char mName[8];
+ int32 mNameOfs[2];
+ };*/
+
+ public int32 mValue;
+ public uint16 mSectionNum;
+ public uint16 mType;
+ public int8 mStorageClass;
+ public int8 mNumOfAuxSymbols;
+ };
+
+ [CRepr]
+ struct PE_SymInfoAux
+ {
+ public uint32 mLength;
+ public uint16 mNumberOfRelocations;
+ public uint16 mNumberOfLinenumbers;
+ public uint32 mCheckSum;
+ public uint16 mNumber;
+ public uint8 mSelection;
+ public char8 mUnused;
+ public char8 mUnused2;
+ public char8 mUnused3;
+ };
+
+ public static Result LocateExtraFileInExecutable()
+ {
+#if BF_PLATFORM_WINDOWS
+ var exePath = Environment.GetExecutableFilePath(.. scope .());
+ var exeFileInfo = scope FileInfo(exePath);
+
+ int fileLength = exeFileInfo.GetFileSize();
+
+ var moduleHandle = Windows.GetModuleHandleA(null);
+ uint8* modulePtr = (.)(void*)(int)moduleHandle;
+ PEFile.PEHeader* header = (.)modulePtr;
+ PEFile.PE_NTHeaders64* hdr64 = (.)(modulePtr + header.e_lfanew);
+ if (hdr64.mFileHeader.mMachine != PEFile.PE_MACHINE_X64)
+ return .Err;
+
+ int exeEnd = 0;
+ for (int sectIdx < hdr64.mFileHeader.mNumberOfSections)
+ {
+ PEFile.PESectionHeader* sectHdrHead = (.)(modulePtr + header.e_lfanew + sizeof(PEFile.PE_NTHeaders64) + sectIdx * sizeof(PEFile.PESectionHeader));
+ exeEnd = Math.Max(exeEnd, sectHdrHead.mPointerToRawData + sectHdrHead.mSizeOfRawData);
+ }
+
+ if (hdr64.mOptionalHeader.mDataDirectory[4].mVirtualAddress != 0)
+ {
+ // This handles the case where our installer is signed
+ fileLength = hdr64.mOptionalHeader.mDataDirectory[4].mVirtualAddress;
+ }
+
+ Range span = .(exeEnd, fileLength);
+ if (span.Length > 0)
+ return span;
+#endif
+ return .Err;
+ }
+}
+
diff --git a/BeefLibs/Beefy2D/src/theme/dark/DarkButton.bf b/BeefLibs/Beefy2D/src/theme/dark/DarkButton.bf
index 46e6e5ed..3f7b7e03 100644
--- a/BeefLibs/Beefy2D/src/theme/dark/DarkButton.bf
+++ b/BeefLibs/Beefy2D/src/theme/dark/DarkButton.bf
@@ -14,7 +14,7 @@ namespace Beefy.theme.dark
public float mLabelYOfs;
[DesignEditable(DefaultEditString=true)]
- public String Label
+ public StringView Label
{
get
{
diff --git a/BeefLibs/Beefy2D/src/theme/dark/DarkCheckBox.bf b/BeefLibs/Beefy2D/src/theme/dark/DarkCheckBox.bf
index bff67f10..e32d4856 100644
--- a/BeefLibs/Beefy2D/src/theme/dark/DarkCheckBox.bf
+++ b/BeefLibs/Beefy2D/src/theme/dark/DarkCheckBox.bf
@@ -121,7 +121,8 @@ namespace Beefy.theme.dark
{
g.SetFont(mFont);
- DarkTheme.DrawUnderlined(g, mLabel, GS!(22), GS!(-1));
+ using(g.PushColor(DarkTheme.COLOR_TEXT))
+ DarkTheme.DrawUnderlined(g, mLabel, GS!(22), GS!(-1));
/*int underlinePos = mLabel.IndexOf('&');
if ((underlinePos != -1) && (underlinePos < mLabel.Length - 1))
diff --git a/BeefLibs/Beefy2D/src/theme/dark/DarkDialog.bf b/BeefLibs/Beefy2D/src/theme/dark/DarkDialog.bf
index ade3ea14..9d2a0e84 100644
--- a/BeefLibs/Beefy2D/src/theme/dark/DarkDialog.bf
+++ b/BeefLibs/Beefy2D/src/theme/dark/DarkDialog.bf
@@ -119,7 +119,8 @@ namespace Beefy.theme.dark
g.SetFont(mFont);
if (mText != null)
- g.DrawString(mText, mTextInsets.mLeft, mTextInsets.mTop, FontAlign.Left, mWidth - mTextInsets.Horz, FontOverflowMode.Wrap);
+ using (g.PushColor(DarkTheme.COLOR_TEXT))
+ g.DrawString(mText, mTextInsets.mLeft, mTextInsets.mTop, FontAlign.Left, mWidth - mTextInsets.Horz, FontOverflowMode.Wrap);
}
}
}
diff --git a/BeefLibs/Beefy2D/src/theme/dark/DarkEditWidget.bf b/BeefLibs/Beefy2D/src/theme/dark/DarkEditWidget.bf
index b2d32e3e..f334ece6 100644
--- a/BeefLibs/Beefy2D/src/theme/dark/DarkEditWidget.bf
+++ b/BeefLibs/Beefy2D/src/theme/dark/DarkEditWidget.bf
@@ -50,6 +50,7 @@ namespace Beefy.theme.dark
}
public Font mFont;
+ public float mLineHeightScale = 1.0f;
public uint32[] mTextColors = sDefaultColors;
public uint32 mHiliteColor = 0xFF2f5c88;
public uint32 mUnfocusedHiliteColor = 0x00000000;
@@ -68,6 +69,8 @@ namespace Beefy.theme.dark
protected static uint32[] sDefaultColors = new uint32[] ( Color.White ) ~ delete _;
+ public float LineHeight => Math.Max(Math.Round(mFont.GetLineSpacing() * mLineHeightScale), 1);
+
public this(EditWidgetContent refContent = null) : base(refContent)
{
//mTextInsets.Set(-3, 2, 0, 2);
@@ -79,6 +82,8 @@ namespace Beefy.theme.dark
mHeight = GS!(24);
mHorzJumpSize = GS!(40);
mFont = DarkTheme.sDarkTheme?.mSmallFont;
+ mTextColors[0] = DarkTheme.COLOR_TEXT;
+ mHiliteColor = DarkTheme.COLOR_TEXT_SELECTED;
}
protected override EditWidgetContent.Data CreateEditData()
@@ -102,7 +107,7 @@ namespace Beefy.theme.dark
mLineCoords.GrowUninitialized(mData.mLineStarts.Count);
mLineCoordJumpTable.Clear();
- float fontHeight = mFont.GetLineSpacing();
+ float fontHeight = LineHeight;
int prevJumpIdx = -1;
float jumpCoordSpacing = GetJumpCoordSpacing();
@@ -212,6 +217,13 @@ namespace Beefy.theme.dark
return defaultVal;
}
+ public float GetTextOffset()
+ {
+ float baseLineSpacing = mFont.GetLineSpacing();
+ float lineSpacing = LineHeight;
+ return lineSpacing / 2.0f - baseLineSpacing / 2.0f;
+ }
+
public int FindUncollapsedLine(int line)
{
var line;
@@ -507,7 +519,7 @@ namespace Beefy.theme.dark
((embed.mKind == .HideLine) && (!hideLine)))
selStartX += GS!(4);
- Rect rect = .(selStartX, mLineCoords[lineIdx] - GS!(1), embed.GetWidth(hideLine), mFont.GetLineSpacing() + GS!(3));
+ Rect rect = .(selStartX, mLineCoords[lineIdx] - GS!(1) + GetTextOffset(), embed.GetWidth(hideLine), mFont.GetLineSpacing() + GS!(3));
if (rect.mY < 0)
rect.mY = 0;
return rect;
@@ -524,7 +536,9 @@ namespace Beefy.theme.dark
#unwarn
int lineCount = GetLineCount();
- float lineSpacing = mFont.GetLineSpacing();
+ float lineSpacing = LineHeight;
+ float fontLineSpacing = mFont.GetLineSpacing();
+ float textYOffset = GetTextOffset();
float offsetY = mTextInsets.mTop;
if (mHeight < lineSpacing)
@@ -563,7 +577,7 @@ namespace Beefy.theme.dark
{
if (mHiliteCurrentLine && selStartIdx == selEndIdx)
{
- float thickness = 2 * (lineSpacing / 18);
+ float thickness = Math.Ceiling(2 * (Math.Floor(fontLineSpacing) / 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;
@@ -594,18 +608,18 @@ namespace Beefy.theme.dark
if (mCharWidth <= 2)
{
using (g.PushColor(Color.Mult(cursorColor, Color.Get(brightness * 0.75f))))
- g.FillRect(x, y, GS!(2), lineSpacing);
+ g.FillRect(x, y + textYOffset, GS!(2), fontLineSpacing);
}
else
{
using (g.PushColor(Color.Mult(cursorColor, Color.Get(brightness * 0.30f))))
- g.FillRect(x, y, mCharWidth, lineSpacing);
+ g.FillRect(x, y + textYOffset, mCharWidth, fontLineSpacing);
}
}
else
{
using (g.PushColor(Color.Mult(cursorColor, Color.Get(brightness))))
- g.FillRect(x, y, Math.Max(1.0f, GS!(1)), lineSpacing);
+ g.FillRect(x, y + textYOffset, Math.Max(1.0f, GS!(1)), fontLineSpacing);
}
drewCursor = true;
}
@@ -699,7 +713,7 @@ namespace Beefy.theme.dark
}
float nextX = curX;
- nextX = DrawText(g, sectionText, curX, curY, curTypeIdAndFlags);
+ nextX = DrawText(g, sectionText, curX, curY + textYOffset, curTypeIdAndFlags);
DrawSectionFlagsOver(g, curX, curY, nextX - curX, flags);
//int32 lineDrawStartColumn = lineDrawStart - lineStart;
diff --git a/BeefLibs/Beefy2D/src/theme/dark/DarkListView.bf b/BeefLibs/Beefy2D/src/theme/dark/DarkListView.bf
index 556b611c..767c405d 100644
--- a/BeefLibs/Beefy2D/src/theme/dark/DarkListView.bf
+++ b/BeefLibs/Beefy2D/src/theme/dark/DarkListView.bf
@@ -978,7 +978,7 @@ namespace Beefy.theme.dark
public float mDragOffset = 0;
public bool mShowColumnGrid;
public bool mShowGridLines;
- public Color mGridLinesColor = 0x0CFFFFFF;
+ public Color mGridLinesColor;
public bool mShowHeader = true;
public bool mEndInEllipsis;
public bool mWordWrap;
@@ -1002,6 +1002,7 @@ namespace Beefy.theme.dark
{
mFont = DarkTheme.sDarkTheme.mSmallFont;
mBoldFont = DarkTheme.sDarkTheme.mSmallBoldFont;
+ mGridLinesColor = DarkTheme.COLOR_GRID;
SetShowHeader(true);
}
@@ -1028,6 +1029,12 @@ namespace Beefy.theme.dark
mScrollContentInsets.mBottom += GS!(2);
base.InitScrollbars(wantHorz, wantVert);
+
+ float scrollIncrement = this.mFont.GetLineSpacing();
+ if (mHorzScrollbar != null)
+ mHorzScrollbar.mScrollIncrement = scrollIncrement;
+ if (mVertScrollbar != null)
+ mVertScrollbar.mScrollIncrement = scrollIncrement;
}
protected override ListViewItem CreateListViewItem()
@@ -1087,7 +1094,7 @@ namespace Beefy.theme.dark
if ((mShowColumnGrid) && (columnIdx < mColumns.Count - 1))
{
- DrawColumnGridColumn(g, column.mWidth, DarkTheme.sUnitSize, mHeight - DarkTheme.sUnitSize - 1, 0xFF707070);
+ DrawColumnGridColumn(g, column.mWidth, DarkTheme.sUnitSize, mHeight - DarkTheme.sUnitSize - 1, DarkTheme.COLOR_GRID);
}
float sortArrowX = g.mFont.GetWidth(column.mLabel) + DarkTheme.sUnitSize/2;
@@ -1146,7 +1153,7 @@ namespace Beefy.theme.dark
if ((mShowColumnGrid) && (columnIdx < mColumns.Count - 1))
{
- DrawColumnGridColumn(g, curX, GS!(4), mHeight - GS!(8), 0xFF888888);
+ DrawColumnGridColumn(g, curX, GS!(4), mHeight - GS!(8), DarkTheme.COLOR_GRID);
}
}
}
diff --git a/BeefLibs/Beefy2D/src/theme/dark/DarkMenu.bf b/BeefLibs/Beefy2D/src/theme/dark/DarkMenu.bf
index dea78e05..d0b826eb 100644
--- a/BeefLibs/Beefy2D/src/theme/dark/DarkMenu.bf
+++ b/BeefLibs/Beefy2D/src/theme/dark/DarkMenu.bf
@@ -32,7 +32,7 @@ namespace Beefy.theme.dark
let darkMenuWidget = (DarkMenuWidget)mMenuWidget;
g.SetFont(mMenuItem.mBold ? darkMenuWidget.mBoldFont : darkMenuWidget.mFont);
- using (g.PushColor(mMenuItem.mDisabled ? 0xFFA8A8A8 : 0xFFFFFFFF))
+ using (g.PushColor(mMenuItem.mDisabled ? DarkTheme.COLOR_TEXT_DISABLED : DarkTheme.COLOR_TEXT))
{
StringView leftStr = mMenuItem.mLabel;
StringView rightStr = default;
@@ -43,12 +43,9 @@ namespace Beefy.theme.dark
leftStr.RemoveToEnd(barIdx);
}
- using (g.PushColor(DarkTheme.COLOR_TEXT))
- {
- g.DrawString(leftStr, GS!(36), 0);
- if (!rightStr.IsEmpty)
- g.DrawString(rightStr, mWidth - GS!(8), 0, .Right);
- }
+ g.DrawString(leftStr, GS!(36), 0);
+ if (!rightStr.IsEmpty)
+ g.DrawString(rightStr, mWidth - GS!(8), 0, .Right);
}
if (mMenuItem.mIconImage != null)
@@ -57,7 +54,7 @@ namespace Beefy.theme.dark
if (mMenuItem.IsParent)
{
- using (g.PushColor(mMenuItem.mDisabled ? 0xFFA8A8A8 : 0xFFFFFFFF))
+ using (g.PushColor(mMenuItem.mDisabled ? DarkTheme.COLOR_TEXT_DISABLED : DarkTheme.COLOR_TEXT))
g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.RightArrow), mWidth - GS!(16), 0);
}
}
@@ -239,6 +236,20 @@ namespace Beefy.theme.dark
MarkDirty();
}
}
+
+ public override void InitScrollbars(bool wantHorz, bool wantVert)
+ {
+ base.InitScrollbars(wantHorz, wantVert);
+
+ float scrollIncrement = 0;
+ if (var darkMenuWidget = mScrollContent as DarkMenuWidget)
+ scrollIncrement = darkMenuWidget.mFont.GetLineSpacing();
+
+ if (mHorzScrollbar != null)
+ mHorzScrollbar.mScrollIncrement = scrollIncrement;
+ if (mVertScrollbar != null)
+ mVertScrollbar.mScrollIncrement = scrollIncrement;
+ }
}
public class DarkMenuWidget : MenuWidget
diff --git a/BeefLibs/Beefy2D/src/theme/dark/DarkTheme.bf b/BeefLibs/Beefy2D/src/theme/dark/DarkTheme.bf
index 97225b72..77e83968 100644
--- a/BeefLibs/Beefy2D/src/theme/dark/DarkTheme.bf
+++ b/BeefLibs/Beefy2D/src/theme/dark/DarkTheme.bf
@@ -197,7 +197,12 @@ namespace Beefy.theme.dark
};
public static uint32 COLOR_TEXT = 0xFFFFFFFF;
+ public static uint32 COLOR_TEXT_DISABLED = 0xFFA8A8A8;
+ public static uint32 COLOR_TEXT_SELECTED = 0xFF2f5c88;
public static uint32 COLOR_WINDOW = 0xFF595962;
+ public static uint32 COLOR_DIALOG_OUTLINE_IN = 0xFF404040;
+ public static uint32 COLOR_DIALOG_OUTLINE_OUT = 0xFF202020;
+ public static uint32 COLOR_GRID = 0x0CFFFFFF;
public static uint32 COLOR_BKG = 0xFF26262A;
public static uint32 COLOR_SELECTED_OUTLINE = 0xFFCFAE11;
public static uint32 COLOR_MENU_FOCUSED = 0xFFE5A910;
diff --git a/BeefLibs/Beefy2D/src/theme/dark/DarkTooltip.bf b/BeefLibs/Beefy2D/src/theme/dark/DarkTooltip.bf
index 66c68f20..3fe64668 100644
--- a/BeefLibs/Beefy2D/src/theme/dark/DarkTooltip.bf
+++ b/BeefLibs/Beefy2D/src/theme/dark/DarkTooltip.bf
@@ -33,7 +33,7 @@ namespace Beefy.theme.dark
public bool mAllowResize;
public bool mHasClosed;
public Insets mRelWidgetMouseInsets ~ delete _;
- public bool mAllowMouseInsideSelf;
+ public Insets mAllowMouseInsideInsets ~ delete _;
public bool mAllowMouseOutside;
public int mAutoCloseDelay;
public bool mIsClipped;
@@ -96,6 +96,21 @@ namespace Beefy.theme.dark
Detach();
}
+ public static Point CalcSize(StringView text, float x = 0, float y = 0)
+ {
+ var font = DarkTheme.sDarkTheme.mSmallFont;
+ float minWidth = 0;
+ float minHeight = 0;
+
+ BFApp.sApp.GetWorkspaceRectFrom((.)x, (.)y, 0, 0, var workspaceX, var workspaceY, var workspaceWidth, var workspaceHeight);
+
+ FontMetrics fontMetrics = .();
+ float height = font.Draw(null, text, x, y, 0, 0, FontOverflowMode.Overflow, &fontMetrics);
+ float tooltipWidth = Math.Max(fontMetrics.mMaxWidth + GS!(32), minWidth);
+ float tooltipHeight = Math.Clamp(height + GS!(16), minHeight, workspaceHeight);
+ return .(tooltipWidth, tooltipHeight);
+ }
+
void Attach(Widget widget)
{
if (mRelWidget != null)
@@ -162,7 +177,7 @@ namespace Beefy.theme.dark
int32 relX = x - mWidgetWindow.mX;
int32 relY = y - mWidgetWindow.mY;
- if ((relX >= mWidgetWindow.mWindowWidth - GS!(18)) && (relY >= mWidgetWindow.mWindowHeight - GS!(18)))
+ if ((relX >= mWidgetWindow.mWindowWidth - GS!(22)) && (relY >= mWidgetWindow.mWindowHeight - GS!(22)))
return BFWindowBase.HitTestResult.BottomRight;
return BFWindowBase.HitTestResult.Client;
}
@@ -178,10 +193,13 @@ namespace Beefy.theme.dark
g.DrawBox(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.Menu), 0, 0, mWidth, mHeight);
g.SetFont(mFont);
- if (mIsClipped)
- g.DrawString(mText, GS!(8), GS!(5), .Left, mWidth - GS!(16), .Ellipsis);
- else
- g.DrawString(mText, 0, GS!(5), .Centered, mWidth);
+ using (g.PushColor(DarkTheme.COLOR_TEXT))
+ {
+ if (mIsClipped)
+ g.DrawString(mText, GS!(8), GS!(5), .Left, mWidth - GS!(16), .Ellipsis);
+ else
+ g.DrawString(mText, 0, GS!(5), .Centered, mWidth);
+ }
if (mAllowResize)
g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.ResizeGrabber), mWidth - GS!(22), mHeight - GS!(22));
@@ -248,21 +266,42 @@ namespace Beefy.theme.dark
if (mAllowMouseOutside)
return;
+ Rect widgetRect = .(0, 0, mRelWidget.mWidth, mRelWidget.mHeight);
+ mRelWidgetMouseInsets?.ApplyTo(ref widgetRect);
+
float rootX;
float rootY;
- mRelWidget.SelfToRootTranslate(0, 0, out rootX, out rootY);
+ mRelWidget.SelfToRootTranslate(widgetRect.mX, widgetRect.mY, out rootX, out rootY);
- Rect checkRect = Rect(rootX, rootY, mRelWidget.mWidth, mRelWidget.mHeight);
- mRelWidgetMouseInsets?.ApplyTo(ref checkRect);
+ Rect checkRect = Rect(rootX, rootY, widgetRect.mWidth, widgetRect.mHeight);
+
if ((mRelWidget.mWidgetWindow != null) && (mRelWidget.mWidgetWindow.mHasMouseInside))
{
- //checkRect.Inflate(8, 8);
if (checkRect.Contains(mRelWidget.mWidgetWindow.mClientMouseX, mRelWidget.mWidgetWindow.mClientMouseY))
return;
}
+ if (mAllowMouseInsideInsets != null)
+ {
+ if (mWidgetWindow.mHasMouseInside)
+ return;
- if ((mWidgetWindow.mHasMouseInside) && (mAllowMouseInsideSelf))
- return;
+ Rect clientRect = mWidgetWindow.ClientRect;
+ mAllowMouseInsideInsets.ApplyTo(ref clientRect);
+
+ var cursorScreenPos = BFApp.sApp.CursorScreenPos;
+ if (cursorScreenPos != null)
+ {
+ if (clientRect.Contains((.)cursorScreenPos.Value.x, (.)cursorScreenPos.Value.y))
+ return;
+ }
+
+ NOP!();
+
+ /*var screenX = (int32)rootX + mWidgetWindow.mClientX;
+ var screenY = (int32)rootY + mWidgetWindow.mClientY;
+ if (clientRect.Contains(screenX, screenY))
+ return;*/
+ }
var checkWindow = BFApp.sApp.FocusedWindow;
if ((checkWindow != null) && (checkWindow.HasParent(mWidgetWindow)))
@@ -423,15 +462,6 @@ namespace Beefy.theme.dark
SetLastMouseWidget(null);
sMouseStillTicks = -1;
}
-
- if (numOverWidgets > 1)
- {
- //int a = 0;
- Debug.WriteLine("numOverWidgets > 1");
- }
-
-
- //Debug.Assert(numOverWidgets <= 1);
}
}
}
diff --git a/BeefLibs/Beefy2D/src/utils/StableIndexedList.bf b/BeefLibs/Beefy2D/src/utils/StableIndexedList.bf
new file mode 100644
index 00000000..6bcb182d
--- /dev/null
+++ b/BeefLibs/Beefy2D/src/utils/StableIndexedList.bf
@@ -0,0 +1,60 @@
+using System.Collections;
+namespace utils;
+
+class StableIndexedList
+{
+ public List mList = new .() ~ delete _;
+ public List mFreeIndices = new .() ~ delete _;
+
+ public this()
+ {
+ mList.Add(default); // Reserve idx 0
+ }
+
+ public ref T this[int idx]
+ {
+ get
+ {
+ return ref mList[idx];
+ }
+
+ set
+ {
+ mList[idx] = value;
+ }
+ }
+
+ public int Add(T val)
+ {
+ if (!mFreeIndices.IsEmpty)
+ {
+ int32 idx = mFreeIndices.PopBack();
+ mList[idx] = val;
+ return idx;
+ }
+ mList.Add(val);
+ return mList.Count - 1;
+ }
+
+ public void RemoveAt(int idx)
+ {
+ if (idx == mList.Count - 1)
+ {
+ mList.PopBack();
+ }
+ else
+ {
+ mList[idx] = default;
+ mFreeIndices.Add((.)idx);
+ }
+ }
+
+ public List.Enumerator GetEnumerator() => mList.GetEnumerator();
+
+ public T SafeGet(int idx)
+ {
+ if ((idx < 0) || (idx > mList.Count))
+ return default;
+ return mList[idx];
+ }
+}
\ No newline at end of file
diff --git a/BeefLibs/Beefy2D/src/widgets/Dialog.bf b/BeefLibs/Beefy2D/src/widgets/Dialog.bf
index 01723f6b..7ece807c 100644
--- a/BeefLibs/Beefy2D/src/widgets/Dialog.bf
+++ b/BeefLibs/Beefy2D/src/widgets/Dialog.bf
@@ -322,6 +322,11 @@ namespace Beefy.widgets
mDefaultButton.MouseClicked(0, 0, 0, 0, 3);
}
+ public virtual void GenerateClipboardText(String outText)
+ {
+ outText.AppendF("{}\n{}", mTitle, mText);
+ }
+
void WindowKeyDown(KeyDownEvent evt)
{
if ((evt.mKeyCode != .Alt) && (mWidgetWindow.IsKeyDown(.Alt)) && (!mWidgetWindow.IsKeyDown(.Control)))
@@ -387,6 +392,29 @@ namespace Beefy.widgets
evt.mHandled = true;
}
}
+
+ if (evt.mKeyFlags.HasFlag(.Ctrl) && (evt.mKeyCode == (KeyCode)'C'))
+ {
+ ClipboardBlock: do
+ {
+ if (mChildWidgets != null)
+ {
+ for (var child in mChildWidgets)
+ {
+ if (child is ButtonWidget)
+ continue;
+ break ClipboardBlock;
+ }
+ }
+
+ var clipboardText = GenerateClipboardText(.. scope String());
+ if (!clipboardText.IsEmpty)
+ {
+ BFApp.sApp.SetClipboardText(clipboardText, "");
+ evt.mHandled = true;
+ }
+ }
+ }
}
}
}
diff --git a/BeefLibs/Beefy2D/src/widgets/EditWidget.bf b/BeefLibs/Beefy2D/src/widgets/EditWidget.bf
index b7c37bf9..4209b4b6 100644
--- a/BeefLibs/Beefy2D/src/widgets/EditWidget.bf
+++ b/BeefLibs/Beefy2D/src/widgets/EditWidget.bf
@@ -2836,7 +2836,7 @@ namespace Beefy.widgets
hi = i - 1;
}
- if (hi < GetLineCount())
+ if ((hi >= 0) && (hi < GetLineCount()))
{
line = (int32)hi;
theChar = idx - mData.mLineStarts[hi];
diff --git a/BeefLibs/Beefy2D/src/widgets/ListView.bf b/BeefLibs/Beefy2D/src/widgets/ListView.bf
index ee75eb89..199d5617 100644
--- a/BeefLibs/Beefy2D/src/widgets/ListView.bf
+++ b/BeefLibs/Beefy2D/src/widgets/ListView.bf
@@ -158,7 +158,7 @@ namespace Beefy.widgets
}
}
- public void WithSelectedItems(delegate void(ListViewItem) func, bool skipSelectedChildrenOnSelectedItems = false, bool skipClosed = false)
+ public void WithSelectedItems(delegate void(ListViewItem item) func, bool skipSelectedChildrenOnSelectedItems = false, bool skipClosed = false)
{
bool selfSelected = Selected;
if (selfSelected)
@@ -907,6 +907,9 @@ namespace Beefy.widgets
{
base.KeyDown(keyCode, isRepeat);
+ if (mWidgetWindow == null)
+ return;
+
switch (keyCode)
{
case (KeyCode)'A':
diff --git a/BeefLibs/Beefy2D/src/widgets/Widget.bf b/BeefLibs/Beefy2D/src/widgets/Widget.bf
index 46bb27d1..4ecee709 100644
--- a/BeefLibs/Beefy2D/src/widgets/Widget.bf
+++ b/BeefLibs/Beefy2D/src/widgets/Widget.bf
@@ -633,7 +633,7 @@ namespace Beefy.widgets
if (targetWidget.mParent == mParent)
{
- mParent.ParentToSelfTranslate(transX, transY, out transX, out transY);
+ targetWidget.ParentToSelfTranslate(transX, transY, out transX, out transY);
return;
}
diff --git a/BeefLibs/Beefy2D/src/widgets/WidgetWindow.bf b/BeefLibs/Beefy2D/src/widgets/WidgetWindow.bf
index 60e3e82a..f0f40ee3 100644
--- a/BeefLibs/Beefy2D/src/widgets/WidgetWindow.bf
+++ b/BeefLibs/Beefy2D/src/widgets/WidgetWindow.bf
@@ -69,6 +69,7 @@ namespace Beefy.widgets
public bool mWantsUpdateF;
public bool mTempWantsUpdateF;
+ public Image mContentRenderTarget ~ delete _;
public int32 mContentClientWidth;
public int32 mContentClientHeight;
@@ -229,11 +230,31 @@ namespace Beefy.widgets
public override void PreDraw(Graphics g)
{
base.PreDraw(g);
+
+ if (mContentRenderTarget != null)
+ {
+ g.mMatrix.Set(Matrix.IdentityMatrix);
+ }
+ else
+ {
+ g.mMatrix.Set(mScaleMatrix);
+ }
- g.mMatrix.Set(mScaleMatrix);
g.mMatrixStack[g.mMatrixStackIdx] = g.mMatrix;
}
+ public override void PostDraw(Graphics g)
+ {
+ base.PostDraw(g);
+
+ if (mContentRenderTarget != null)
+ {
+ for (var drawLayer in DrawLayer.sDrawLayers)
+ drawLayer.DrawToRenderTarget(mContentRenderTarget);
+ g.DrawQuad(mContentRenderTarget, 0, 0, 0, 0, mClientWidth, mClientHeight, 1, 1);
+ }
+ }
+
public override void RehupSize()
{
base.RehupSize();
@@ -880,8 +901,14 @@ namespace Beefy.widgets
mMouseFlags = default;
}
- public void SetContentSize(int width, int height)
+ public void SetContentSize(int width, int height, bool createRenderTarget = false)
{
+ DeleteAndNullify!(mContentRenderTarget);
+ if (createRenderTarget)
+ {
+ mContentRenderTarget = Image.CreateRenderTarget((.)width, (.)height);
+ }
+
mContentClientWidth = (.)width;
mContentClientHeight = (.)height;
RehupSize();
diff --git a/BeefLibs/FMOD/src/FMod.bf b/BeefLibs/FMOD/src/FMod.bf
index 7ebd85bc..6b3482ef 100644
--- a/BeefLibs/FMOD/src/FMod.bf
+++ b/BeefLibs/FMOD/src/FMod.bf
@@ -2012,14 +2012,14 @@ namespace FMOD
return result;
}
- public RESULT createSound (uint8[] data, MODE mode, ref CREATESOUNDEXINFO exinfo, out Sound sound)
+ public RESULT createSound (Span data, MODE mode, ref CREATESOUNDEXINFO exinfo, out Sound sound)
{
sound = null;
exinfo.cbsize = (int32)sizeof(CREATESOUNDEXINFO);
int soundraw;
- RESULT result = FMOD_System_CreateSound(rawPtr, (char8*)&data[0], mode, ref exinfo, out soundraw);
+ RESULT result = FMOD_System_CreateSound(rawPtr, (.)data.Ptr, mode, ref exinfo, out soundraw);
sound = new Sound(soundraw);
return result;
diff --git a/BeefLibs/MiniZ/src/Zip.bf b/BeefLibs/MiniZ/src/Zip.bf
index d9ff58b4..6a9ac391 100644
--- a/BeefLibs/MiniZ/src/Zip.bf
+++ b/BeefLibs/MiniZ/src/Zip.bf
@@ -34,21 +34,91 @@ Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://ww
using System;
using System.IO;
using System.Diagnostics;
+using System.Collections;
#define TINFL_USE_64BIT_BITBUF
#define MINIZ_HAS_64BIT_REGISTERS
#define MINIZ_LITTLE_ENDIAN
#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES
+#if BF_PLATFORM_WINDOWS
+#define ALLOW_FILE_MAPPING
+#endif
+
namespace MiniZ
{
class ZipFile
{
+ class RangeStream : Stream
+ {
+ public bool mOwnsStream;
+ public Stream mStream ~ { if (mOwnsStream) delete _; };
+ public Range mRange;
+
+ public override bool CanRead => mStream.CanRead;
+ public override bool CanWrite => mStream.CanWrite;
+ public override int64 Length => mRange.Length;
+
+ public override int64 Position
+ {
+ get
+ {
+ return mStream.Position - mRange.Start;
+ }
+
+ set
+ {
+ mStream.Position = value + mRange.Start;
+ }
+ }
+
+ public override Result Seek(int64 pos, SeekKind seekKind = .Absolute)
+ {
+ //return mStream.Seek(pos, seekKind);
+
+ switch (seekKind)
+ {
+ case .Absolute:
+ return mStream.Seek(pos + mRange.Start, .Absolute);
+ case .Relative:
+ return mStream.Seek(pos, .Relative);
+ case .FromEnd:
+ return mStream.Seek(mRange.End - pos);
+ }
+ }
+
+ public override Result TryRead(Span data)
+ {
+ return mStream.TryRead(data);
+ }
+
+ public override Result TryRead(Span data, int timeoutMS)
+ {
+ return mStream.TryRead(data, timeoutMS);
+ }
+
+ public override Result TryWrite(Span data)
+ {
+ return mStream.TryWrite(data);
+ }
+
+ public override Result Close()
+ {
+ return mStream.Close();
+ }
+
+ public override Result Flush()
+ {
+ return mStream.Flush();
+ }
+ }
+
public class Entry
{
ZipFile mZipFile;
MiniZ.ZipArchiveFileStat mFileStat;
int32 mFileIdx;
+ Span mAllocatedData ~ delete mAllocatedData.Ptr;
public bool IsDirectory
{
@@ -65,6 +135,17 @@ namespace MiniZ
return .Ok;
}
+ public Result> ExtractToMemory()
+ {
+ if (mAllocatedData.IsEmpty)
+ {
+ mAllocatedData = .(new uint8[mFileStat.mUncompSize]*, mFileStat.mUncompSize);
+ if (!MiniZ.ZipReaderExtractToMem(&mZipFile.[Friend]mFile, mFileIdx, mAllocatedData.Ptr, mAllocatedData.Length, .None))
+ return .Err;
+ }
+ return .Ok(mAllocatedData);
+ }
+
int GetStrLen(char8* ptr, int max)
{
int i = 0;
@@ -97,10 +178,27 @@ namespace MiniZ
outComment.Append(&mFileStat.mComment, GetStrLen(&mFileStat.mComment, mFileStat.mComment.Count));
return .Ok;
}
+
+ public DateTime GetDateTime()
+ {
+ int64 time = (int64)mFileStat.mTime * 10000000L + 116444736000000000L;
+ return DateTime(time);
+ }
+
+ public Platform.BfpTimeStamp GetTimestamp()
+ {
+ return (.)(mFileStat.mTime * 10000000L + 116444736000000000L);
+ }
}
MiniZ.ZipArchive mFile;
bool mInitialized;
+#if ALLOW_FILE_MAPPING
+ Windows.FileHandle mShareFileHandle ~ _.Close();
+ Windows.Handle mShareFileMapping ~ _.Close();
+ Span mMappedData;
+#endif
+ Stream mOwnedStream ~ delete _;
public ~this()
{
@@ -117,6 +215,74 @@ namespace MiniZ
return .Ok;
}
+ public Result Open(StringView fileName, Range range)
+ {
+ Debug.Assert(!mInitialized);
+
+ FileStream fileStream = new FileStream();
+ if (fileStream.Open(fileName, .Read, .Read) case .Err)
+ {
+ delete fileStream;
+ return .Err;
+ }
+
+ RangeStream rangeStream = new RangeStream() { mStream = fileStream, mOwnsStream = true, mRange = range };
+ mOwnedStream = rangeStream;
+ if (!MiniZ.ZipReaderInitStream(&mFile, rangeStream, .None))
+ return .Err;
+ mInitialized = true;
+ return .Ok;
+ }
+
+ public Result Open(Span data)
+ {
+ Debug.Assert(!mInitialized);
+ if (!MiniZ.ZipReaderInitMem(&mFile, data.Ptr, data.Length, .None))
+ return .Err;
+ mInitialized = true;
+ return .Ok;
+ }
+
+ public Result OpenMapped(StringView fileName)
+ {
+#if ALLOW_FILE_MAPPING
+ mShareFileHandle = Windows.CreateFileA(fileName.ToScopeCStr!(), Windows.GENERIC_READ, .Read, null, .Open, 0, default);
+ uint32 sizeHigh = 0;
+ uint32 sizeLow = (.)Windows.GetFileSize(mShareFileHandle, (.)&sizeHigh);
+ mShareFileMapping = Windows.CreateFileMappingA(mShareFileHandle, null, Windows.PAGE_READONLY, sizeHigh, sizeLow, null);
+ mMappedData = .((.)Windows.MapViewOfFile(mShareFileMapping, Windows.FILE_MAP_READ, 0, 0, sizeLow), sizeLow);
+
+ Debug.Assert(!mInitialized);
+ if (!MiniZ.ZipReaderInitMem(&mFile, mMappedData.Ptr, mMappedData.Length, .None))
+ return .Err;
+ mInitialized = true;
+ return .Ok;
+#else
+ return Open(fileName);
+#endif
+ }
+
+ public Result OpenMapped(StringView fileName, Range range)
+ {
+#if ALLOW_FILE_MAPPING
+ mShareFileHandle = Windows.CreateFileA(fileName.ToScopeCStr!(), Windows.GENERIC_READ, .Read, null, .Open, 0, default);
+ uint32 sizeHigh = 0;
+ uint32 sizeLow = (.)range.End;
+
+ mShareFileMapping = Windows.CreateFileMappingA(mShareFileHandle, null, Windows.PAGE_READONLY, sizeHigh, sizeLow, null);
+ uint8* ptr = (.)Windows.MapViewOfFile(mShareFileMapping, Windows.FILE_MAP_READ, 0, 0, sizeLow);
+ mMappedData = .(ptr + range.Start, range.Length);
+
+ Debug.Assert(!mInitialized);
+ if (!MiniZ.ZipReaderInitMem(&mFile, mMappedData.Ptr, mMappedData.Length, .None))
+ return .Err;
+ mInitialized = true;
+ return .Ok;
+#else
+ return Open(fileName, range);
+#endif
+ }
+
public Result Init(Stream stream)
{
Debug.Assert(!mInitialized);
@@ -3115,7 +3281,7 @@ namespace MiniZ
return s;
}
- static bool zip_reader_init_mem(ZipArchive* pZip, void* pMem, int size, ZipFlags flags)
+ public static bool ZipReaderInitMem(ZipArchive* pZip, void* pMem, int size, ZipFlags flags)
{
if (!zip_reader_init_internal(pZip, flags))
return false;
diff --git a/BeefLibs/corlib/src/Allocator.bf b/BeefLibs/corlib/src/Allocator.bf
index 20fe04a7..90f2dfda 100644
--- a/BeefLibs/corlib/src/Allocator.bf
+++ b/BeefLibs/corlib/src/Allocator.bf
@@ -97,7 +97,7 @@ namespace System
}
}
- struct AllocWrapper where T : new, delete
+ struct AllocWrapper where T : new where alloctype(T) : delete
{
alloctype(T) mVal;
public alloctype(T) Val
diff --git a/BeefLibs/corlib/src/Attribute.bf b/BeefLibs/corlib/src/Attribute.bf
index ad27c2c2..83765a96 100644
--- a/BeefLibs/corlib/src/Attribute.bf
+++ b/BeefLibs/corlib/src/Attribute.bf
@@ -372,7 +372,7 @@ namespace System
[AttributeUsage(.Constructor)]
public struct AllowAppendAttribute : Attribute
{
-
+ public bool ZeroGap;
}
[AttributeUsage(.Class | .Struct)]
diff --git a/BeefLibs/corlib/src/Collections/HashSet.bf b/BeefLibs/corlib/src/Collections/HashSet.bf
index cd478a2d..e38d50f5 100644
--- a/BeefLibs/corlib/src/Collections/HashSet.bf
+++ b/BeefLibs/corlib/src/Collections/HashSet.bf
@@ -1245,7 +1245,7 @@ namespace System.Collections
public int32 GetHashKey(int hashCode)
{
if (sizeof(int) == 4)
- return (int32)hashCode;
+ return (int32)hashCode & 0x7FFFFFFF;
#unwarn
return (int32)(hashCode ^ ((hashCode >> 31) * 1171)) & 0x7FFFFFFF;
}
diff --git a/BeefLibs/corlib/src/Compiler.bf b/BeefLibs/corlib/src/Compiler.bf
index b0785400..66705c77 100644
--- a/BeefLibs/corlib/src/Compiler.bf
+++ b/BeefLibs/corlib/src/Compiler.bf
@@ -63,7 +63,7 @@ namespace System
mCmdInfo.Append("\n");
}
- public void AddEdit(StringView dataName, StringView label, StringView defaultValue)
+ public void AddEdit(StringView dataName, StringView label, StringView defaultValue, bool focus = false)
{
mCmdInfo.AppendF($"addEdit\t");
dataName.QuoteString(mCmdInfo);
@@ -71,7 +71,7 @@ namespace System
label.QuoteString(mCmdInfo);
mCmdInfo.Append("\t");
defaultValue.QuoteString(mCmdInfo);
- mCmdInfo.Append("\n");
+ mCmdInfo.AppendF($"\t{focus}\n");
}
public void AddFilePath(StringView dataName, StringView label, StringView defaultValue)
@@ -216,7 +216,7 @@ namespace System
public override void InitUI()
{
- AddEdit("name", "Class Name", "");
+ AddEdit("name", "Class Name", "", true);
}
public override void Generate(String outFileName, String outText, ref Flags generateFlags)
@@ -225,10 +225,12 @@ namespace System
if (name.EndsWith(".bf", .OrdinalIgnoreCase))
name.RemoveFromEnd(3);
outFileName.Append(name);
+
+ var ns = Namespace..Trim();
+ if (!ns.IsEmpty)
+ outText.AppendF($"namespace {Namespace};\n\n");
outText.AppendF(
$"""
- namespace {Namespace};
-
class {name}
{{
}}
@@ -334,6 +336,7 @@ namespace System
static extern void Comptime_EmitMethodEntry(int64 methodHandle, StringView text);
static extern void Comptime_EmitMethodExit(int64 methodHandle, StringView text);
static extern void Comptime_EmitMixin(StringView text);
+ static extern String Comptime_GetStringById(int32 id);
[Comptime(OnlyFromComptime=true)]
public static MethodBuilder CreateMethod(Type owner, StringView methodName, Type returnType, MethodFlags methodFlags)
diff --git a/BeefLibs/corlib/src/Console.bf b/BeefLibs/corlib/src/Console.bf
index e3399176..f5f8fd3c 100644
--- a/BeefLibs/corlib/src/Console.bf
+++ b/BeefLibs/corlib/src/Console.bf
@@ -396,6 +396,7 @@ namespace System
{
sForegroundColor = sOriginalForegroundColor;
sBackgroundColor = sOriginalBackgroundColor;
+ SetColors();
#if !BF_PLATFORM_WINDOWS
Write("\x1B[0m");
diff --git a/BeefLibs/corlib/src/Diagnostics/Debug.bf b/BeefLibs/corlib/src/Diagnostics/Debug.bf
index dd4bb851..81e86c18 100644
--- a/BeefLibs/corlib/src/Diagnostics/Debug.bf
+++ b/BeefLibs/corlib/src/Diagnostics/Debug.bf
@@ -25,7 +25,7 @@ namespace System.Diagnostics
#if !DEBUG
[SkipCall]
#endif
- public static void FatalError(String msg = "Fatal error encountered", String filePath = Compiler.CallerFilePath, int line = Compiler.CallerLineNum)
+ public static void FatalError(StringView msg = "Fatal error encountered", String filePath = Compiler.CallerFilePath, int line = Compiler.CallerLineNum)
{
String failStr = scope .()..Append(msg, " at line ");
line.ToString(failStr);
@@ -44,10 +44,19 @@ namespace System.Diagnostics
#endif
}
+#if !BF_RUNTIME_DISABLE
[CallingConvention(.Cdecl)]
static extern void Write(char8* str, int strLen);
[CallingConvention(.Cdecl)]
static extern void Write(int val);
+#else
+ static void Write(char8* str, int strLen)
+ {
+ }
+ static void Write(int val)
+ {
+ }
+#endif
public static void Write(String line)
{
@@ -59,7 +68,7 @@ namespace System.Diagnostics
Write(sv.[Friend]mPtr, sv.[Friend]mLength);
}
- public static void Write(String fmt, params Span