1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-07-05 07:45:59 +02:00

Initial checkin

This commit is contained in:
Brian Fiete 2019-08-23 11:56:54 -07:00
parent c74712dad9
commit 078564ac9e
3242 changed files with 1616395 additions and 0 deletions

View file

@ -0,0 +1,787 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.IO;
using Beefy.gfx;
using Beefy.widgets;
using Beefy.theme;
using Beefy.theme.dark;
using Beefy.utils;
using Beefy.res;
using Beefy.geom;
using System.Threading;
#if MONOTOUCH
using MonoTouch;
#endif
#if STUDIO_CLIENT || STUDIO_HOST
using Beefy.ipc;
#endif
namespace Beefy
{
public enum Cursor
{
Pointer,
Hand,
Dragging,
Text,
CircleSlash,
Sizeall,
SizeNESW,
SizeNS,
SizeNWSE,
SizeWE,
Wait,
None,
COUNT
}
public class BFApp
#if STUDIO_CLIENT
: IStudioClient
#endif
{
public delegate void UpdateDelegate(bool batchStart);
public delegate void DrawDelegate();
public static BFApp sApp;
public int32 mUpdateCnt;
public bool mIsUpdateBatchStart;
public bool mAutoDirty = true;
public Graphics mGraphics ~ delete _;
int32 mRefreshRate = 60;
public String mInstallDir ~ delete _;
public String mUserDataDir ~ delete _;
public List<BFWindow> mWindows = new List<BFWindow>() ~ delete _;
List<Object> mDeferredDeletes = new List<Object>() ~ delete _;
public bool mShuttingDown = false;
public bool mStopping;
public bool mStarted;
public ResourceManager mResourceManager ~ delete _;
public int32 mFPSDrawCount;
public int32 mFPSUpdateCount;
public int32 mLastFPS;
public uint32 mLastFPSUpdateCnt;
public Matrix4? mColorMatrix;
public ConstantDataDefinition mColorMatrixDataDef = new ConstantDataDefinition(16, new ConstantDataDefinition.DataType[] { ConstantDataDefinition.DataType.Matrix | ConstantDataDefinition.DataType.PixelShaderUsage }) ~ delete _;
[StdCall, CLink]
static extern void Lib_Startup(int32 argc, char8** argv, void* startupCallback);
[StdCall, CLink]
static extern void BFApp_GetDesktopResolution(out int32 width, out int32 height);
[StdCall, CLink]
static extern void BFApp_GetWorkspaceRect(out int32 x, out int32 y, out int32 width, out int32 height);
[StdCall, CLink]
static extern void BFApp_Create();
[StdCall, CLink]
static extern void BFApp_Delete();
[StdCall, CLink]
static extern void BFApp_SetRefreshRate(int32 rate);
[StdCall, CLink]
public static extern void BFApp_SetDrawEnabled(int32 enabled);
[StdCall, CLink]
static extern void BFApp_Init();
[StdCall, CLink]
static extern void BFApp_Run();
[StdCall, CLink]
static extern void BFApp_Shutdown();
[StdCall, CLink]
static extern void BFApp_SetCallbacks(void* updateDelegate, void* drawDelegate);
[StdCall, CLink]
static extern char8* BFApp_GetInstallDir();
[StdCall, CLink]
static extern void BFApp_SetCursor(int32 cursor);
[StdCall, CLink]
static extern void* BFApp_GetClipboardData(char8* format, out int32 size);
[StdCall, CLink]
static extern void BFApp_ReleaseClipboardData(void* ptr);
[StdCall, CLink]
static extern void BFApp_SetClipboardData(char8* format, void* ptr, int32 size, int32 resetClipboard);
[StdCall, CLink]
public static extern void BFApp_CheckMemory();
[StdCall, CLink]
public static extern void BFApp_RehupMouse();
UpdateDelegate mUpdateDelegate ~ delete _;
DrawDelegate mDrawDelegate ~ delete _;
#if STUDIO_CLIENT
public bool mTrackingDraw = false;
public IPCClientManager mIPCClientManager;
public IPCProxy<IStudioHost> mStudioHost;
public static IStudioHost StudioHostProxy
{
get
{
return sApp.mStudioHost.Proxy;
}
}
#endif
#if STUDIO_CLIENT
public IPCEndpoint<IStudioClient> mStudioClient;
#endif
#if MONOTOUCH
[MonoPInvokeCallback(typeof(DrawDelegate))]
static void Static_Draw()
{
sApp.Draw();
}
[MonoPInvokeCallback(typeof(UpdateDelegate))]
static void Static_Update()
{
sApp.Update();
}
#endif
static void Static_Draw()
{
sApp.Draw();
}
static void Static_Update(bool batchStart)
{
sApp.Update(batchStart);
}
float mLastUpdateDelta; // In seconds
public this()
{
Utils.GetTickCountMicro();
//mColorMatrix = Matrix4.Identity;
/*mColorMatrix = Matrix4(
1, 1, 1, 1,
1, 1, 1, 1,
1, 1, 1, 1,
1, 1, 1, 1);*/
sApp = this;
BFApp_Create();
#if STUDIO_CLIENT
BFApp_SetRefreshRate(0); // Let studio dictate updating/drawing
mUpdateDelegate = LocalUpdate;
mDrawDelegate = LocalDraw;
#else
BFApp_SetRefreshRate(mRefreshRate);
mUpdateDelegate = new => Static_Update;
mDrawDelegate = new => Static_Draw;
#endif
BFApp_SetCallbacks(mUpdateDelegate.GetFuncPtr(), mDrawDelegate.GetFuncPtr());
}
#if STUDIO_CLIENT
int mStudioUpdateCnt = 0;
int mCount = 0;
uint mLocalUpdateTick;
public virtual void LocalUpdate()
{
mLocalUpdateTick = Utils.GetTickCount();
if (mHostIsPerfRecording)
{
PerfTimer.Log("HostPerfRecording Done at: {0}", Utils.GetTickCount());
PerfTimer.StopRecording();
PerfTimer.DbgPrint();
mHostIsPerfRecording = false;
}
IPCClientManager.sIPCClientManager.Update();
if (mHostIsPerfRecording)
PerfTimer.Log("Update Done at: {0}", Utils.GetTickCount());
IPCClientManager.sIPCClientManager.RemoteSyncToRead();
if (mHostIsPerfRecording)
PerfTimer.Log("RemoteSyncToRead Done at: {0}", Utils.GetTickCount());
mStudioUpdateCnt++;
mStudioHost.Proxy.ClientUpdated(mStudioUpdateCnt);
if (mHostIsPerfRecording)
PerfTimer.Log("ClientUpdated Done at: {0}", Utils.GetTickCount());
mCount++;
}
public virtual void LocalDraw()
{
}
public void TrackDraw()
{
if (!mTrackingDraw)
return;
StackTrace st = new StackTrace(true);
StringBuilder sb = new StringBuilder();
var frames = st.GetFrames();
for (int frameNum = 0; frameNum < frames.Length; frameNum++)
{
var frame = frames[frameNum];
sb.Append(frame.GetMethod().DeclaringType.ToString());
sb.Append(".");
sb.Append(frame.GetMethod().Name);
sb.Append("(");
int paramIdx = 0;
for (var param in frame.GetMethod().GetParameters())
{
if (paramIdx > 0)
sb.Append(", ");
sb.Append(param.ParameterType.ToString());
sb.Append(" ");
sb.Append(param.Name);
paramIdx++;
}
sb.Append(")");
sb.Append("|");
sb.Append(frame.GetFileName());
sb.Append("|");
sb.Append(frame.GetFileLineNumber());
sb.AppendLine();
}
}
#endif
public int32 RefreshRate
{
get
{
return mRefreshRate;
}
set
{
mRefreshRate = value;
BFApp_SetRefreshRate(mRefreshRate);
}
}
// Simulation seconds since last update
public float UpdateDelta
{
get
{
if (mRefreshRate != 0)
return 1.0f / mRefreshRate;
return mLastUpdateDelta;
}
}
public static void Startup(String[] args, Action startupCallback)
{
/*string[] newArgs = new string[args.Length + 1];
Array.Copy(args, 0, newArgs, 1, args.Length);
newArgs[0] = System.Reflection.Assembly.GetEntryAssembly().Location;
Lib_Startup(newArgs.Length, newArgs, startupCallback);*/
char8*[] char8PtrArr = scope char8*[args.Count];
for (int32 i = 0; i < args.Count; i++)
char8PtrArr[i] = args[i];
Lib_Startup((int32)args.Count, char8PtrArr.CArray(), startupCallback.GetFuncPtr());
}
public virtual bool HandleCommandLineParam(String key, String value)
{
return false;
}
public virtual void UnhandledCommandLine(String key, String value)
{
}
public virtual void ParseCommandLine(String[] args)
{
for (var str in args)
{
int eqPos = str.IndexOf('=');
if (eqPos == -1)
{
if (!HandleCommandLineParam(str, null))
UnhandledCommandLine(str, null);
}
else
{
var cmd = scope String(str, 0, eqPos);
var param = scope String(str, eqPos + 1);
if (!HandleCommandLineParam(cmd, param))
UnhandledCommandLine(cmd, param);
}
}
}
public virtual void ParseCommandLine(String theString)
{
List<String> stringList = scope List<String>();
String curString = null;
bool hadSpace = false;
bool inQuote = false;
for (int32 i = 0; i < theString.Length; i++)
{
char8 c = theString[i];
if ((theString[i] == ' ') && (!inQuote))
{
hadSpace = true;
}
else
{
if (hadSpace)
{
if (!inQuote)
{
if (c == '=')
{
}
else
{
if (curString != null)
{
stringList.Add(curString);
curString = null;
}
}
}
hadSpace = false;
}
if (curString == null)
curString = scope:: String();
if (c == '"')
{
inQuote = !inQuote;
}
else
{
curString.Append(theString[i]);
}
}
}
if (curString != null)
stringList.Add(curString);
for (String param in stringList)
{
int32 eqPos = (int32)param.IndexOf('=');
if (eqPos != -1)
{
String key = scope String(param, 0, eqPos);
String value = scope String(param, eqPos + 1);
if (!HandleCommandLineParam(key, value))
UnhandledCommandLine(key, value);
}
else
if (!HandleCommandLineParam(param, null))
UnhandledCommandLine(param, null);
}
}
public void GetDesktopResolution(out int width, out int height)
{
int32 widthOut;
int32 heightOut;
BFApp_GetDesktopResolution(out widthOut, out heightOut);
width = widthOut;
height = heightOut;
}
public void GetWorkspaceRect(out int x, out int y, out int width, out int height)
{
int32 xOut;
int32 yOut;
int32 widthOut;
int32 heightOut;
BFApp_GetWorkspaceRect(out xOut, out yOut, out widthOut, out heightOut);
x = xOut;
y = yOut;
width = widthOut;
height = heightOut;
}
public bool HasModalDialogs()
{
for (var window in mWindows)
{
if (window.mWindowFlags.HasFlag(BFWindowBase.Flags.Modal))
return true;
}
return false;
}
public bool HasPopupMenus()
{
for (var window in mWindows)
{
var widgetWindow = window as WidgetWindow;
if ((widgetWindow != null) && (widgetWindow.mRootWidget is MenuContainer))
return true;
}
return false;
}
public virtual void Init()
{
scope AutoBeefPerf("BFApp.Init");
#if STUDIO_CLIENT
mIPCClientManager = IPCClientManager.sIPCClientManager = new IPCClientManager();
mIPCClientManager.Init();
mStudioClient = IPCEndpoint<IStudioClient>.CreateNamed(this);
mStudioHost = IPCProxy<IStudioHost>.CreateNamed();
mIPCClientManager.mRemoteProcessId = mStudioHost.Proxy.ClientConnected(mStudioClient.ObjId, Process.GetCurrentProcess().Id);
mIPCClientManager.mConnecting = false;
#endif
BFApp_Init();
Interlocked.Fence();
mInstallDir = new String(BFApp_GetInstallDir());
mUserDataDir = new String(mInstallDir);
mResourceManager = new ResourceManager();
String resFileName = scope String();
resFileName.Append(mInstallDir, "Resources.json");
if (File.Exists(resFileName))
{
StructuredData structuredData = scope StructuredData();
structuredData.Load(resFileName);
mResourceManager.ParseConfigData(structuredData);
}
}
public void InitGraphics()
{
DefaultVertex.Init();
ModelDef.VertexDef.Init();
mGraphics = new Graphics();
String filePath = scope String();
filePath.Append(mInstallDir, "shaders/Std");
if (mColorMatrix != null)
filePath.Append("_hue");
Shader shader = Shader.CreateFromFile(filePath, DefaultVertex.sVertexDefinition);
//shader.SetTechnique("Standard");
mGraphics.mDefaultShader = shader;
mGraphics.mDefaultRenderState = RenderState.Create();
mGraphics.mDefaultRenderState.mIsFromDefaultRenderState = true;
mGraphics.mDefaultRenderState.Shader = shader;
filePath.Append("_font");
Shader textShader = Shader.CreateFromFile(filePath, DefaultVertex.sVertexDefinition);
mGraphics.mTextShader = textShader;
//shader.SetTechnique("Standard");
//mGraphics.mDefaultShader = textShader;
//mGraphics.mDefaultRenderState = RenderState.Create();
//mGraphics.mDefaultRenderState.mIsFromDefaultRenderState = true;
//mGraphics.mDefaultRenderState.Shader = shader;
//mGraphics.mDefaultShader = textShader;
//mGraphics.mDefaultRenderState.Shader = textShader;
}
public ~this()
{
#if STUDIO_HOST || STUDIO_CLIENT
if (IPCManager.sIPCManager != null)
IPCManager.sIPCManager.Dispose();
#endif
Debug.Assert(mShuttingDown, "Shutdown must be called before deleting the app");
ProcessDeferredDeletes();
BFApp_Delete();
ShutdownCompleted();
}
public void DeferDelete(Object obj)
{
//Slow, paranoid check.
//Debug.Assert(!mDeferredDeletes.Contains(obj));
mDeferredDeletes.Add(obj);
}
public void ProcessDeferredDeletes()
{
for (int32 i = 0; i < mDeferredDeletes.Count; i++)
delete mDeferredDeletes[i];
mDeferredDeletes.Clear();
}
public virtual void ShutdownCompleted()
{
}
public virtual void Shutdown()
{
Debug.Assert(!mShuttingDown, "Shutdown can only be called once");
/*if (!mStarted)
Debug.Assert(mStopping, "Shutdown can only be called after the app is stopped");*/
mShuttingDown = true;
//Dispose();
}
public virtual void Run()
{
if (mStopping)
return;
BFApp_Run();
}
public virtual void Stop()
{
mStopping = true;
while (mWindows.Count > 0)
mWindows[0].Close();
BFApp_Shutdown();
}
uint32 mPrevUpdateTickCount;
uint32 mUpdateTickCount;
uint32 mLastEndedTickCount;
public virtual void Update(bool batchStart)
{
//Utils.BFRT_CPP("gBFGC.MutatorSectionEntry()");
mIsUpdateBatchStart = batchStart;
mUpdateCnt++;
mPrevUpdateTickCount = mUpdateTickCount;
mUpdateTickCount = Utils.GetTickCount();
if (mPrevUpdateTickCount != 0)
mLastUpdateDelta = (mUpdateTickCount - mPrevUpdateTickCount) / 1000.0f;
using (PerfTimer.ZoneStart("BFApp.Update"))
{
mFPSUpdateCount++;
if (mRefreshRate == 0)
{
int32 elapsed = (int32)(mUpdateTickCount - mLastFPSUpdateCnt);
if (elapsed > 1000)
{
mLastFPS = mFPSDrawCount;
mLastFPSUpdateCnt = mUpdateTickCount;
mFPSUpdateCount = 0;
mFPSDrawCount = 0;
}
}
else
{
if (mFPSUpdateCount == mRefreshRate)
{
mLastFPS = mFPSDrawCount;
//Debug.WriteLine("FPS: " + mLastFPS);
mFPSUpdateCount = 0;
mFPSDrawCount = 0;
}
}
//GC.Collect();
using (PerfTimer.ZoneStart("BFApp.Update mWindows updates"))
{
for (int32 windowIdx = 0; windowIdx < mWindows.Count; windowIdx++)
{
mWindows[windowIdx].Update();
}
}
}
mLastEndedTickCount = Utils.GetTickCount();
ProcessDeferredDeletes();
//Utils.BFRT_CPP("gBFGC.MutatorSectionExit()");
}
public virtual void DoDraw()
{
}
#if STUDIO_CLIENT
bool mHostIsPerfRecording;
public virtual void HostIsPerfRecording(bool isPerfRecording, int drawCount)
{
mHostIsPerfRecording = isPerfRecording;
if (mHostIsPerfRecording)
{
PerfTimer.StartRecording();
PerfTimer.Log("HostIsPerfRecording {0}", drawCount);
PerfTimer.Log("PrevUpdateTickCount: {0}", mPrevUpdateTickCount);
PerfTimer.Log("UpdateTickCount: {0}", mUpdateTickCount);
PerfTimer.Log("LocalUpdateTick: {0}", mLocalUpdateTick);
//using (PerfTimer.ZoneStart("HostIsPerfRecording"))
//PerfTimer.Log("Got HostIsPerfRecording");
}
}
#endif
public virtual void Draw()
{
#if STUDIO_CLIENT
#endif
PerfTimer.ZoneStart("BFApp.Draw");
PerfTimer.Message("Client Draw Start");
mFPSDrawCount++;
#if STUDIO_CLIENT
mStudioHost.Proxy.FrameStarted(mUpdateCnt);
#endif
if (mGraphics == null)
InitGraphics();
mGraphics.StartDraw();
for (BFWindow window in mWindows)
{
if ((window.mVisible) && ((window.mIsDirty) || (mAutoDirty)))
{
window.PreDraw(mGraphics);
if (mColorMatrix != null)
{
#if DEBUG
mColorMatrix = Matrix4(
0.90f, 0.10f, 0.00f, 0,
0.00f, 0.90f, 0.10f, 0,
0.10f, 0.00f, 1.05f, 0,
0, 0, 0, 1);
#else
mColorMatrix = Matrix4(
0.90f, 0.10f, 0.00f, 0,
0.00f, 1.05f, 0.10f, 0,
0.10f, 0.00f, 0.90f, 0,
0, 0, 0, 1);
#endif
mGraphics.SetShaderConstantData(0, &mColorMatrix.ValueRef, mColorMatrixDataDef);
}
window.Draw(mGraphics);
window.PostDraw(mGraphics);
window.mIsDirty = false;
}
}
mGraphics.EndDraw();
#if STUDIO_CLIENT
PerfTimer.Log("Sending FrameDone");
mStudioHost.Proxy.FrameDone();
//mFrameDonesSent++;
#endif
PerfTimer.ZoneEnd();
}
public void MarkDirty()
{
for (var window in mWindows)
{
if (var widgetWindow = window as WidgetWindow)
{
widgetWindow.mIsDirty = true;
}
}
}
Cursor curCursor;
public virtual void SetCursor(Cursor cursor)
{
if (curCursor == cursor)
return;
curCursor = cursor;
BFApp_SetCursor((int32)cursor);
}
public virtual void* GetClipboardData(String format, out int32 size)
{
return BFApp_GetClipboardData(format, out size);
}
public virtual void ReleaseClipboardData(void* ptr)
{
BFApp_ReleaseClipboardData(ptr);
}
public virtual bool GetClipboardText(String outStr)
{
int32 aSize;
void* clipboardData = GetClipboardData("text", out aSize);
if (clipboardData == null)
return false;
outStr.Append((char8*)clipboardData);
ReleaseClipboardData(clipboardData);
return true;
}
public virtual void SetClipboardData(String format, void* ptr, int32 size, bool resetClipboard)
{
BFApp_SetClipboardData(format, ptr, size, resetClipboard ? 1 : 0);
}
public virtual void SetClipboardText(String text)
{
//IntPtr aPtr = Marshal.StringToCoTaskMemUni(text);
SetClipboardData("text", text.CStr(), (int32)text.Length + 1, true);
}
#if STUDIO_CLIENT
public int GetProcessId()
{
return Process.GetCurrentProcess().Id;
}
#endif
public void RehupMouse()
{
BFApp_RehupMouse();
}
}
}

View file

@ -0,0 +1,772 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
using Beefy.gfx;
using Beefy.sys;
using System.IO;
#if MONOTOUCH
using MonoTouch;
#endif
#if STUDIO_CLIENT
using Beefy.ipc;
#endif
namespace Beefy
{
public class BFWindowBase
{
public enum Flags
{
Border = 0x000001,
ThickFrame = 0x000002,
Resizable = 0x000004,
SysMenu = 0x000008,
Caption = 0x000010,
Minimize = 0x000020,
Maximize = 0x000040,
ClientSized = 0x000080,
QuitOnClose = 0x000100,
VSync = 0x000200,
PopupPosition = 0x000400,
DestAlpha = 0x000800,
AlphaMask = 0x001000,
Child = 0x002000,
TopMost = 0x004000,
ToolWindow = 0x008000,
NoActivate = 0x01'0000,
NoMouseActivate = 0x02'0000,
Menu = 0x04'0000,
Modal = 0x08'0000,
ScaleContent = 0x10'0000,
UseParentMenu = 0x20'0000,
CaptureMediaKeys = 0x40'0000,
Fullscreen = 0x80'0000,
FakeFocus = 0x0100'0000
};
public enum HitTestResult
{
NotHandled = -3,
Border = 18,
Bottom = 15,
BottomLeft = 16,
BottomRight = 17,
Caption = 2,
Client = 1,
Close = 20,
Error = -2,
GrowBox = 4,
Help = 21,
HScroll = 6,
Left = 10,
Menu = 5,
MaxButton = 9,
MinButton = 8,
NoWhere = 0,
Reduce = 8,
Right = 11,
Size = 4,
SysMenu = 3,
Top = 12,
TopLeft = 13,
TopRight = 14,
Transparent = -1,
VScroll = 7,
Zoom = 9
}
public SysMenu mSysMenu ~ delete _;
public Dictionary<int, SysMenu> mSysMenuMap = new Dictionary<int, SysMenu>() ~ delete _;
public DrawLayer mDefaultDrawLayer ~ delete _;
public virtual void Draw(Graphics g)
{
}
public virtual void PreDraw(Graphics g)
{
g.PushDrawLayer(mDefaultDrawLayer);
}
public virtual void PostDraw(Graphics g)
{
g.PopDrawLayer();
}
}
#if !STUDIO_CLIENT
public class BFWindow : BFWindowBase, INativeWindow
{
delegate void NativeMovedDelegate(void* window);
delegate int32 NativeCloseQueryDelegate(void* window);
delegate void NativeClosedDelegate(void* window);
delegate void NativeGotFocusDelegate(void* window);
delegate void NativeLostFocusDelegate(void* window);
delegate void NativeKeyCharDelegate(void* window, char32 theChar);
delegate bool NativeKeyDownDelegate(void* window, int32 keyCode, int32 isRepeat);
delegate void NativeKeyUpDelegate(void* window, int32 keyCode);
delegate int32 NativeHitTestDelegate(void* window, int32 x, int32 y);
delegate void NativeMouseMoveDelegate(void* window, int32 x, int32 y);
delegate void NativeMouseProxyMoveDelegate(void* window, int32 x, int32 y);
delegate void NativeMouseDownDelegate(void* window, int32 x, int32 y, int32 btn, int32 btnCount);
delegate void NativeMouseUpDelegate(void* window, int32 x, int32 y, int32 btn);
delegate void NativeMouseWheelDelegate(void* window, int32 x, int32 y, int32 delta);
delegate void NativeMouseLeaveDelegate(void* window);
delegate void NativeMenuItemSelectedDelegate(void* window, void* menu);
public void* mNativeWindow;
public bool mNativeWindowClosed;
static int32 sId;
protected int32 mId = ++sId;
public String mName ~ delete _;
public String mTitle ~ delete _;
public int32 mX;
public int32 mY;
public int32 mWindowWidth;
public int32 mWindowHeight;
public int32 mClientX;
public int32 mClientY;
public int32 mClientWidth;
public int32 mClientHeight;
public float mAlpha = 1.0f;
public Flags mWindowFlags;
public bool mVisible = true;
private bool mMouseVisible;
public bool mHasFocus = false;
public bool mHasClosed;
public bool mIsDirty = true;
public BFWindow mParent;
public List<BFWindow> mChildWindows = new List<BFWindow>() ~ delete _;
static protected Dictionary<int, BFWindow> sWindowDictionary = new Dictionary<int, BFWindow>() ~ delete _;
static NativeMovedDelegate sNativeMovedDelegate ~ delete _;
static NativeCloseQueryDelegate sNativeCloseQueryDelegate ~ delete _;
static NativeClosedDelegate sNativeClosedDelegate ~ delete _;
static NativeGotFocusDelegate sNativeGotFocusDelegate ~ delete _;
static NativeLostFocusDelegate sNativeLostFocusDelegate ~ delete _;
static NativeKeyCharDelegate sNativeKeyCharDelegate ~ delete _;
static NativeKeyDownDelegate sNativeKeyDownDelegate ~ delete _;
static NativeKeyUpDelegate sNativeKeyUpDelegate ~ delete _;
static NativeHitTestDelegate sNativeHitTestDelegate ~ delete _;
static NativeMouseMoveDelegate sNativeMouseMoveDelegate ~ delete _;
static NativeMouseProxyMoveDelegate sNativeMouseProxyMoveDelegate ~ delete _;
static NativeMouseDownDelegate sNativeMouseDownDelegate ~ delete _;
static NativeMouseUpDelegate sNativeMouseUpDelegate ~ delete _;
static NativeMouseWheelDelegate sNativeMouseWheelDelegate ~ delete _;
static NativeMouseLeaveDelegate sNativeMouseLeaveDelegate ~ delete _;
static NativeMenuItemSelectedDelegate sNativeMenuItemSelectedDelegate ~ delete _;
[StdCall, CLink]
static extern void* BFApp_CreateWindow(void* parent, char8* title, int32 x, int32 y, int32 width, int32 height, int32 windowFlags);
[StdCall, CLink]
static extern void* BFWindow_GetNativeUnderlying(void* window);
[StdCall, CLink]
static extern void BFWindow_SetCallbacks(void* window, void* movedDelegate, void* closeQueryDelegate, void* closedDelegate,
void* gotFocusDelegate, void* lostFocusDelegate,
void* keyCharDelegate, void* keyDownDelegate, void* keyUpDelegate, void* hitTestDelegate,
void* mouseMoveDelegate, void* mouseProxyMoveDelegate, void* mouseDownDelegate, void* mouseUpDelegate, void* mouseWheelDelegate, void* mouseLeaveDelegate,
void* menuItemSelectedDelegate);
[StdCall, CLink]
static extern void BFWindow_SetTitle(void* window, char8* title);
[StdCall, CLink]
static extern void BFWindow_SetMinimumSize(void* window, int32 minWidth, int32 minHeight, bool clientSized);
[StdCall, CLink]
static extern void BFWindow_GetPosition(void* window, out int32 x, out int32 y, out int32 width, out int32 height, out int32 clientX, out int32 clientY, out int32 clientWidth, out int32 clientHeight);
[StdCall, CLink]
static extern void BFWindow_Resize(void* window, int32 x, int32 y, int32 width, int32 height);
[StdCall, CLink]
static extern void BFWindow_Close(void* window, int32 force);
[StdCall, CLink]
static extern void BFWindow_SetForeground(void* window);
[StdCall, CLink]
static extern void BFWindow_LostFocus(void* window, void* newFocus);
[StdCall, CLink]
static extern void BFWindow_SetNonExclusiveMouseCapture(void* window);
[StdCall, CLink]
static extern void BFWindow_SetClientPosition(void* window, int32 x, int32 y);
[StdCall, CLink]
static extern void BFWindow_SetAlpha(void* window, float alpha, uint32 destAlphaSrcMask, int32 mouseVisible);
[StdCall, CLink]
static extern void BFWindow_SetMouseVisible(void* window, bool mouseVisible);
[StdCall, CLink]
static extern void BFWindow_CaptureMouse(void* window);
[StdCall, CLink]
static extern bool BFWindow_IsMouseCaptured(void* window);
[StdCall, CLink]
static extern void* BFWindow_AddMenuItem(void* window, void* parent, int32 insertIdx, char8* text, char8* hotKey, void* bitmap, int32 enabled, int32 checkState, int32 radioCheck);
[StdCall, CLink]
static extern void* BFWindow_ModifyMenuItem(void* window, void* item, char8* text, char8* hotKey, void* bitmap, int32 enabled, int32 checkState, int32 radioCheck);
[StdCall, CLink]
static extern void* BFWindow_DeleteMenuItem(void* window, void* item);
public static BFWindow GetBFWindow(void* window)
{
return sWindowDictionary[(int)window];
}
#if MONOTOUCH
[MonoPInvokeCallback(typeof(NativeMovedDelegate))]
static void Static_NativeMovedDelegate(void* window) { GetBFWindow(window).Moved(); }
[MonoPInvokeCallback(typeof(NativeClosedDelegate))]
static void Static_NativeClosedDelegate(void* window) { GetBFWindow(window).Closed(); }
[MonoPInvokeCallback(typeof(NativeCloseQueryDelegate))]
static int Static_NativeCloseQueryDelegate(void* window) { return GetBFWindow(window).CloseQuery(); }
[MonoPInvokeCallback(typeof(NativeGotFocusDelegate))]
static void Static_NativeGotFocusDelegate(void* window) { GetBFWindow(window).GotFocus(); }
[MonoPInvokeCallback(typeof(NativeLostFocusDelegate))]
static void Static_NativeLostFocusDelegate(void* window) { GetBFWindow(window).LostFocus(); }
[MonoPInvokeCallback(typeof(NativeKeyCharDelegate))]
static void Static_NativeKeyCharDelegate(void* window, char8 c) { GetBFWindow(window).KeyChar(c); }
[MonoPInvokeCallback(typeof(NativeKeyDownDelegate))]
static bool Static_NativeKeyDownDelegate(void* window, int key, int isRepeat) { return GetBFWindow(window).KeyDown(key, isRepeat); }
[MonoPInvokeCallback(typeof(NativeKeyUpDelegate))]
static void Static_NativeKeyUpDelegate(void* window, int key) { GetBFWindow(window).KeyUp(key); }
[MonoPInvokeCallback(typeof(NativeMouseMoveDelegate))]
static void Static_NativeMouseMoveDelegate(void* window, int mouseX, int mouseY) { GetBFWindow(window).MouseMove(mouseX, mouseY); }
[MonoPInvokeCallback(typeof(NativeMouseProxyMoveDelegate))]
static void Static_NativeMouseProxyMoveDelegate(void* window, int mouseX, int mouseY) { GetBFWindow(window).MouseProxyMove(mouseX, mouseY); }
[MonoPInvokeCallback(typeof(NativeMouseDownDelegate))]
static void Static_NativeMouseDownDelegate(void* window, int mouseX, int mouseY, int btnNum, int btnCount) { GetBFWindow(window).MouseDown(mouseX, mouseY, btnNum, btnCount); }
[MonoPInvokeCallback(typeof(NativeMouseUpDelegate))]
static void Static_NativeMouseUpDelegate(void* window, int mouseX, int mouseY, int btnNum) { GetBFWindow(window).MouseUp(mouseX, mouseY, btnNum); }
[MonoPInvokeCallback(typeof(NativeMouseWheelDelegate))]
static void Static_NativeMouseWheelDelegate(void* window, int mouseX, int mouseY, int delta) { GetBFWindow(window).MouseWheel(mouseX, mouseY, delta); }
[MonoPInvokeCallback(typeof(NativeMouseLeaveDelegate))]
static void Static_NativeMouseLeaveDelegate(void* window) { GetBFWindow(window).MouseLeave(); }
[MonoPInvokeCallback(typeof(NativeMenuItemSelectedDelegate))]
static void Static_NativeMenuItemSelectedDelegate(void* window, void* item) { GetBFWindow(window).NativeMenuItemSelected(item); }
#else
static void Static_NativeMovedDelegate(void* window) { GetBFWindow(window).Moved(); }
static void Static_NativeClosedDelegate(void* window) { GetBFWindow(window).Closed(); }
static int32 Static_NativeCloseQueryDelegate(void* window) { return GetBFWindow(window).CloseQuery(); }
static void Static_NativeGotFocusDelegate(void* window) { GetBFWindow(window).GotFocus(); }
static void Static_NativeLostFocusDelegate(void* window) { GetBFWindow(window).LostFocus(null); }
static void Static_NativeKeyCharDelegate(void* window, char32 c) { GetBFWindow(window).KeyChar(c); }
static bool Static_NativeKeyDownDelegate(void* window, int32 key, int32 isRepeat) { return GetBFWindow(window).KeyDown(key, isRepeat); }
static void Static_NativeKeyUpDelegate(void* window, int32 key) { GetBFWindow(window).KeyUp(key); }
static int32 Static_NativeHitTestDelegate(void* window, int32 x, int32 y) { return (int32)GetBFWindow(window).HitTest(x, y); }
static void Static_NativeMouseMoveDelegate(void* window, int32 mouseX, int32 mouseY) { GetBFWindow(window).MouseMove(mouseX, mouseY); }
static void Static_NativeMouseProxyMoveDelegate(void* window, int32 mouseX, int32 mouseY) { GetBFWindow(window).MouseProxyMove(mouseX, mouseY); }
static void Static_NativeMouseDownDelegate(void* window, int32 mouseX, int32 mouseY, int32 btnNum, int32 btnCount) { GetBFWindow(window).MouseDown(mouseX, mouseY, btnNum, btnCount); }
static void Static_NativeMouseUpDelegate(void* window, int32 mouseX, int32 mouseY, int32 btnNum) { GetBFWindow(window).MouseUp(mouseX, mouseY, btnNum); }
static void Static_NativeMouseWheelDelegate(void* window, int32 mouseX, int32 mouseY, int32 delta) { GetBFWindow(window).MouseWheel(mouseX, mouseY, delta); }
static void Static_NativeMouseLeaveDelegate(void* window) { GetBFWindow(window).MouseLeave(); }
static void Static_NativeMenuItemSelectedDelegate(void* window, void* item) { GetBFWindow(window).NativeMenuItemSelected(item); }
#endif
public this()
{
}
public this(BFWindow parent, String title, int x, int y, int width, int height, BFWindow.Flags windowFlags)
{
Init(parent, title, x, y, width, height, windowFlags);
}
public ~this()
{
bool worked = sWindowDictionary.Remove((int)mNativeWindow);
Debug.Assert(worked);
}
void Init(BFWindow parent, String title, int x, int y, int width, int height, BFWindow.Flags windowFlags)
{
mTitle = new String(title);
mParent = parent;
if (mParent != null)
mParent.mChildWindows.Add(this);
var useFlags = windowFlags;
/*if (useFlags.HasFlag(.FakeFocus))
{
useFlags |= .NoActivate;
useFlags |= .NoMouseActivate;
}*/
mNativeWindow = BFApp_CreateWindow((parent != null) ? (parent.mNativeWindow) : null, title, (int32)x, (int32)y, (int32)width, (int32)height, (int32)useFlags);
sWindowDictionary[(int)mNativeWindow] = this;
if (sNativeMovedDelegate == null)
{
sNativeMovedDelegate = new => Static_NativeMovedDelegate;
sNativeClosedDelegate = new => Static_NativeClosedDelegate;
sNativeCloseQueryDelegate = new => Static_NativeCloseQueryDelegate;
sNativeGotFocusDelegate = new => Static_NativeGotFocusDelegate;
sNativeLostFocusDelegate = new => Static_NativeLostFocusDelegate;
sNativeKeyCharDelegate = new => Static_NativeKeyCharDelegate;
sNativeKeyDownDelegate = new => Static_NativeKeyDownDelegate;
sNativeKeyUpDelegate = new => Static_NativeKeyUpDelegate;
sNativeHitTestDelegate = new => Static_NativeHitTestDelegate;
sNativeMouseMoveDelegate = new => Static_NativeMouseMoveDelegate;
sNativeMouseProxyMoveDelegate = new => Static_NativeMouseProxyMoveDelegate;
sNativeMouseDownDelegate = new => Static_NativeMouseDownDelegate;
sNativeMouseUpDelegate = new => Static_NativeMouseUpDelegate;
sNativeMouseWheelDelegate = new => Static_NativeMouseWheelDelegate;
sNativeMouseLeaveDelegate = new => Static_NativeMouseLeaveDelegate;
sNativeMenuItemSelectedDelegate = new => Static_NativeMenuItemSelectedDelegate;
}
BFWindow_SetCallbacks(mNativeWindow, sNativeMovedDelegate.GetFuncPtr(), sNativeCloseQueryDelegate.GetFuncPtr(), sNativeClosedDelegate.GetFuncPtr(), sNativeGotFocusDelegate.GetFuncPtr(), sNativeLostFocusDelegate.GetFuncPtr(),
sNativeKeyCharDelegate.GetFuncPtr(), sNativeKeyDownDelegate.GetFuncPtr(), sNativeKeyUpDelegate.GetFuncPtr(), sNativeHitTestDelegate.GetFuncPtr(),
sNativeMouseMoveDelegate.GetFuncPtr(), sNativeMouseProxyMoveDelegate.GetFuncPtr(), sNativeMouseDownDelegate.GetFuncPtr(), sNativeMouseUpDelegate.GetFuncPtr(), sNativeMouseWheelDelegate.GetFuncPtr(), sNativeMouseLeaveDelegate.GetFuncPtr(),
sNativeMenuItemSelectedDelegate.GetFuncPtr());
BFApp.sApp.mWindows.Add(this);
mDefaultDrawLayer = new DrawLayer(this);
if (windowFlags.HasFlag(Flags.Menu))
{
mSysMenu = new SysMenu();
mSysMenu.mWindow = this;
}
mWindowFlags = windowFlags;
if ((parent != null) && (!mWindowFlags.HasFlag(Flags.NoActivate)))
parent.LostFocus(this);
if ((parent != null) && (mWindowFlags.HasFlag(Flags.Modal)))
parent.PreModalChild();
if ((mWindowFlags.HasFlag(.FakeFocus)) && (!mWindowFlags.HasFlag(.NoActivate)))
GotFocus();
BFApp.sApp.RehupMouse();
}
public void SetTitle(String title)
{
mTitle.Set(title);
BFWindow_SetTitle(mNativeWindow, mTitle);
}
public int Handle
{
get
{
return (int)BFWindow_GetNativeUnderlying(mNativeWindow);
}
}
#if BF_PLATFORM_WINDOWS
public Windows.HWnd HWND
{
get
{
return (.)(int)BFWindow_GetNativeUnderlying(mNativeWindow);
}
}
#endif
public void PreModalChild()
{
//MouseLeave();
}
public virtual void Dispose()
{
Close(true);
}
public virtual int32 CloseQuery()
{
return 1;
}
public virtual void Close(bool force = false)
{
// This doesn't play properly with CloseQuery. We may do a force close in CloseQuery, but that fails
// if we do this following logic:
/*if (mNativeWindowClosed)
return;
mNativeWindowClosed = true;*/
while (mChildWindows.Count > 0)
mChildWindows[mChildWindows.Count - 1].Close(force);
//for (var childWindow in mChildWindows)
//childWindow.Close(force);
//mChildWindows[mChildWindows.Count - 1].Close(force);
if (mNativeWindow != null)
{
BFWindow_Close(mNativeWindow, force ? 1 : 0);
}
else
{
Closed();
}
}
public virtual void Resize(int x, int y, int width, int height)
{
Debug.Assert(mNativeWindow != null);
BFWindow_Resize(mNativeWindow, (int32)x, (int32)y, (int32)width, (int32)height);
}
public void SetForeground()
{
BFWindow_SetForeground(mNativeWindow);
GotFocus();
}
public void SetNonExclusiveMouseCapture()
{
// Does checking of mouse coords against all window even when this window has mouse capture,
// helps some dragging scenarios. Gets turned off automatically on mouse up.
BFWindow_SetNonExclusiveMouseCapture(mNativeWindow);
}
public virtual void Closed()
{
if (mHasClosed)
return;
mHasClosed = true;
bool hadFocus = mHasFocus;
BFApp.sApp.mWindows.Remove(this);
if (mWindowFlags.HasFlag(Flags.QuitOnClose))
BFApp.sApp.Stop();
if (mParent != null)
{
mParent.mChildWindows.Remove(this);
if ((hadFocus) && (mWindowFlags.HasFlag(Flags.Modal)))
mParent.GotFocus();
}
DeleteAndNullify!(mDefaultDrawLayer);
BFApp.sApp.DeferDelete(this);
}
public void SetMinimumSize(int32 minWidth, int32 minHeight, bool clientSized = false)
{
BFWindow_SetMinimumSize(mNativeWindow, minWidth, minHeight, clientSized);
}
public virtual void Moved()
{
BFWindow_GetPosition(mNativeWindow, out mX, out mY, out mWindowWidth, out mWindowHeight, out mClientX, out mClientY, out mClientWidth, out mClientHeight);
mIsDirty = true;
}
public virtual void SetClientPosition(float x, float y)
{
mClientX = (int32)x;
mClientY = (int32)y;
BFWindow_SetClientPosition(mNativeWindow, (int32)x, (int32)y);
}
public virtual void SetAlpha(float alpha, uint32 destAlphaSrcMask, bool mouseVisible)
{
mAlpha = alpha;
BFWindow_SetAlpha(mNativeWindow, alpha, destAlphaSrcMask, mouseVisible ? 1 : 0);
}
public virtual void SetMouseVisible(bool mouseVisible)
{
mMouseVisible = mouseVisible;
BFWindow_SetMouseVisible(mNativeWindow, mouseVisible);
}
public virtual void CaptureMouse()
{
BFWindow_CaptureMouse(mNativeWindow);
}
public bool IsMouseCaptured()
{
return BFWindow_IsMouseCaptured(mNativeWindow);
}
public virtual void* AddMenuItem(void* parent, int insertIdx, String text, String hotKey, void* bitmap, bool enabled, int checkState, bool radioCheck)
{
return BFWindow_AddMenuItem(mNativeWindow, parent, (int32)insertIdx, text, hotKey, bitmap, enabled ? 1 : 0, (int32)checkState, radioCheck ? 1 : 0);
}
public virtual void ModifyMenuItem(void* item, String text, String hotKey, void* bitmap, bool enabled, int checkState, bool radioCheck)
{
BFWindow_ModifyMenuItem(mNativeWindow, item, text, hotKey, bitmap, enabled ? 1 : 0, (int32)checkState, radioCheck ? 1 : 0);
}
public virtual void DeleteMenuItem(void* menuItem)
{
BFWindow_DeleteMenuItem(mNativeWindow, menuItem);
}
public virtual void MenuItemSelected(SysMenu sysMenu)
{
sysMenu.Selected();
}
public virtual void NativeMenuItemSelected(void* menu)
{
SysMenu aSysMenu = mSysMenuMap[(int)menu];
MenuItemSelected(aSysMenu);
}
public virtual SysBitmap LoadSysBitmap(String path)
{
return null;
}
public virtual void GotFocus()
{
if (mHasFocus)
return;
mHasFocus = true;
//Console.WriteLine("GotFocus {0}", mTitle);
}
public virtual void LostFocus(BFWindow newFocus)
{
if (!mHasFocus)
return;
mHasFocus = false;
if (mNativeWindow != null)
BFWindow_LostFocus(mNativeWindow, (newFocus != null) ? newFocus.mNativeWindow : null);
//TODO: REMOVE
//Debug.WriteLine("LostFocus {0}", mTitle);
}
public virtual void KeyChar(char32 theChar)
{
}
public virtual bool KeyDown(int32 keyCode, int32 isRepeat)
{
return false;
}
public virtual void KeyUp(int32 keyCode)
{
}
public virtual HitTestResult HitTest(int32 x, int32 y)
{
return HitTestResult.NotHandled;
}
public virtual void MouseMove(int32 x, int32 y)
{
}
public virtual void MouseProxyMove(int32 x, int32 y)
{
}
public virtual void MouseDown(int32 x, int32 y, int32 btn, int32 btnCount)
{
}
public virtual void MouseUp(int32 x, int32 y, int32 btn)
{
}
public virtual void MouseWheel(int32 x, int32 y, int32 delta)
{
}
public virtual void MouseLeave()
{
}
public override void Draw(Graphics g)
{
}
public virtual void Update()
{
}
}
#else
public class BFWindow : BFWindowBase, IStudioClientWindow
{
//IStudioWidgetWindow mProxy;
public int mClientWidth;
public int mClientHeight;
public int mClientX;
public int mClientY;
public float mAlpha = 1.0f;
public bool mVisible = true;
public Flags mWindowFlags;
public IPCProxy<IStudioHostWindow> mRemoteWindow;
public IPCEndpoint<IStudioClientWindow> mStudioClientWindow;
public BFWindow(BFWindow parent, string title, int x, int y, int width, int height, BFWindow.Flags windowFlags)
{
mStudioClientWindow = IPCEndpoint<IStudioClientWindow>.Create(this);
IStudioHost studioInstance = BFApp.sApp.mStudioHost.Proxy;
IPCObjectId remoteWindowId = studioInstance.CreateWindow(mStudioClientWindow.ObjId, 0, title, x, y, width, height, (int)windowFlags);
mRemoteWindow = IPCProxy<IStudioHostWindow>.Create(remoteWindowId);
mDefaultDrawLayer = new DrawLayer(this);
BFApp.sApp.mWindows.Add(this);
mClientX = 0;
mClientY = 0;
mClientWidth = width;
mClientHeight = height;
mWindowFlags = windowFlags;
}
public void Dispose()
{
Close();
}
public virtual int CloseQuery()
{
return 1;
}
public virtual void Close(bool force = false)
{
//if (mNativeWindow != null)
//BFWindow_Close(mNativeWindow, force ? 1 : 0);
}
public virtual void Closed()
{
//mNativeWindow = null;
BFApp.sApp.mWindows.Remove(this);
}
public virtual void Moved()
{
//BFWindow_GetPosition(mNativeWindow, out mX, out mY, out mWindowWidth, out mWindowHeight, out mClientX, out mClientY, out mClientWidth, out mClientHeight);
}
public virtual void SetClientPosition(float x, float y)
{
//BFWindow_SetClientPosition(mNativeWindow, (int)x, (int)y);
}
public virtual void SetAlpha(float alpha, uint destAlphaSrcMask, bool mouseVisible)
{
//mAlpha = alpha;
//BFWindow_SetAlpha(mNativeWindow, alpha, destAlphaSrcMask, mouseVisible ? 1 : 0);
}
public virtual void* AddMenuItem(void* parent, string text, string hotKey, void* bitmap, bool enabled, int checkState, bool radioCheck)
{
//return BFWindow_AddMenuItem(mNativeWindow, parent, text, hotKey, bitmap, enabled ? 1 : 0, checkState, radioCheck ? 1 : 0);
return null;
}
public virtual void MenuItemSelected(SysMenu sysMenu)
{
sysMenu.Selected();
}
public virtual void NativeMenuItemSelected(void* menu)
{
//SysMenu aSysMenu = mSysMenuMap[menu];
//MenuItemSelected(aSysMenu);
}
public virtual SysBitmap LoadSysBitmap(string path)
{
return null;
}
public virtual void GotFocus()
{
}
public virtual void LostFocus()
{
}
public virtual void KeyChar(char32 theChar)
{
}
public virtual void KeyDown(int keyCode, int isRepeat)
{
}
public virtual void KeyUp(int keyCode)
{
}
public virtual void MouseMove(int x, int y)
{
}
public virtual void MouseProxyMove(int x, int y)
{
}
public virtual void MouseDown(int x, int y, int btn, int btnCount)
{
}
public virtual void MouseUp(int x, int y, int btn)
{
}
public virtual void MouseWheel(int x, int y, int delta)
{
}
public virtual void MouseLeave()
{
}
public virtual void Update()
{
}
public override void PreDraw(Graphics g)
{
base.PreDraw(g);
mRemoteWindow.Proxy.RemoteDrawingStarted();
}
public override void PostDraw(Graphics g)
{
base.PostDraw(g);
mRemoteWindow.Proxy.RemoteDrawingDone();
}
public void SetIsVisible(bool visible)
{
mVisible = visible;
}
}
#endif
}

View file

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace Beefy
{
public class Rand
{
static Random sRand = new Random() ~ delete _;
public static int32 Int()
{
return sRand.NextI32();
}
public static int32 SInt()
{
return sRand.Next(int32.MinValue, int32.MaxValue);
}
public static float Float()
{
return (float)sRand.NextDouble();
}
public static float SFloat()
{
return (float)(sRand.NextDouble() * 2) - 1.0f;
}
}
}

View file

@ -0,0 +1,447 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Threading;
using System.IO;
using System.Diagnostics;
using System.Security.Cryptography;
namespace Beefy
{
public static class Utils
{
static Random mRandom = new Random() ~ delete _;
[StdCall, CLink]
static extern int32 BF_TickCount();
[StdCall, CLink]
static extern int32 BF_TickCountMicroFast();
public static float Deg2Rad = Math.PI_f / 180.0f;
public static int32 Rand()
{
return mRandom.NextI32();
}
public static float RandFloat()
{
return (Rand() & 0xFFFFFF) / (float)0xFFFFFF;
}
public static float Interpolate(float left, float right, float pct)
{
return left + (right - left) * pct;
}
public static float Clamp(float val, float min, float max)
{
return Math.Max(min, Math.Min(max, val));
}
public static float Lerp(float val1, float val2, float pct)
{
return val1 + (val2 - val1) * pct;
}
public static float EaseInAndOut(float pct)
{
return ((-Math.Cos(pct * Math.PI_f) + 1.0f) / 2.0f);
}
public static float Distance(float dX, float dY)
{
return Math.Sqrt(dX * dX + dY * dY);
}
public static char8 CtrlKeyChar(char32 theChar)
{
char8 aChar = (char8)(theChar + (int)'A' - (char8)1);
if (aChar < (char8)0)
return (char8)0;
return aChar;
}
public static uint32 GetTickCount()
{
return (uint32)BF_TickCount();
}
public static uint64 GetTickCountMicro()
{
return (uint32)BF_TickCountMicroFast();
}
public static Object DefaultConstruct(Type theType)
{
ThrowUnimplemented();
/*ConstructorInfo constructor = theType.GetConstructors()[0];
ParameterInfo[] paramInfos = constructor.GetParameters();
object[] aParams = new object[paramInfos.Length];
for (int paramIdx = 0; paramIdx < aParams.Length; paramIdx++)
aParams[paramIdx] = paramInfos[paramIdx].DefaultValue;
object newObject = constructor.Invoke(aParams);
return newObject;*/
}
/*public static int StrToInt(string theString)
{
if (theString.StartsWith("0X", StringComparison.OrdinalIgnoreCase))
return Convert.ToInt32(theString.Substring(2), 16);
return Convert.ToInt32(theString);
}
// WaitForEvent differs from theEvent.WaitOne in that it doesn't pump the Windows
// message loop under STAThread
public static bool WaitForEvent(EventWaitHandle theEvent, int timeMS)
{
return WaitForSingleObject(theEvent.SafeWaitHandle, timeMS) == 0;
}*/
public static void GetDirWithSlash(String dir)
{
if (dir.IsEmpty)
return;
char8 endChar = dir[dir.Length - 1];
if ((endChar != Path.DirectorySeparatorChar) && (endChar != Path.AltDirectorySeparatorChar))
dir.Append(Path.DirectorySeparatorChar);
}
public static Result<void> DelTree(String path, Predicate<String> fileFilter = null)
{
if (path.Length <= 2)
return .Err;
if ((path[0] != '/') && (path[0] != '\\'))
{
if (path[1] == ':')
{
if (path.Length < 3)
return .Err;
}
else
return .Err;
}
for (var fileEntry in Directory.EnumerateDirectories(path))
{
let fileName = scope String();
fileEntry.GetFilePath(fileName);
Try!(DelTree(fileName, fileFilter));
}
for (var fileEntry in Directory.EnumerateFiles(path))
{
let fileName = scope String();
fileEntry.GetFilePath(fileName);
if (fileFilter != null)
if (!fileFilter(fileName))
continue;
Try!(File.SetAttributes(fileName, FileAttributes.Archive));
Try!(File.Delete(fileName));
}
// Allow failure for the directory, this can often be locked for various reasons
// but we only consider a file failure to be an "actual" failure
Directory.Delete(path).IgnoreError();
return .Ok;
}
public static Result<void, FileError> LoadTextFile(String fileName, String outBuffer, bool autoRetry = true, delegate void() onPreFilter = null)
{
// Retry for a while if the other side is still writing out the file
for (int i = 0; i < 100; i++)
{
if (File.ReadAllText(fileName, outBuffer, true) case .Err(let err))
{
bool retry = false;
if ((autoRetry) && (err case .FileOpenError(let fileOpenErr)))
{
if (fileOpenErr == .SharingViolation)
retry = true;
}
if (!retry)
return .Err(err);
}
else
break;
Thread.Sleep(20);
}
if (onPreFilter != null)
onPreFilter();
/*if (hashPtr != null)
*hashPtr = MD5.Hash(Span<uint8>((uint8*)outBuffer.Ptr, outBuffer.Length));*/
bool isAscii = false;
int outIdx = 0;
for (int32 i = 0; i < outBuffer.Length; i++)
{
char8 c = outBuffer[i];
if (c >= '\x80')
{
switch (UTF8.TryDecode(outBuffer.Ptr + i, outBuffer.Length - i))
{
case .Ok((?, let len)):
if (len > 1)
{
for (int cnt < len)
{
char8 cPart = outBuffer[i++];
outBuffer[outIdx++] = cPart;
}
i--;
continue;
}
case .Err: isAscii = true;
}
}
if (c != '\r')
{
if (outIdx == i)
{
outIdx++;
continue;
}
outBuffer[outIdx++] = c;
}
}
outBuffer.RemoveToEnd(outIdx);
if (isAscii)
{
String prevBuffer = scope String();
outBuffer.MoveTo(prevBuffer);
for (var c in prevBuffer.RawChars)
{
outBuffer.Append((char32)c, 1);
}
}
return .Ok;
}
public static bool FileTextEquals(String textA, String textB)
{
int32 posA = 0;
int32 posB = 0;
while (true)
{
char8 char8A = (char8)0;
char8 char8B = (char8)0;
while (posA < textA.Length)
{
char8A = textA[posA++];
if (char8A != '\r')
break;
char8A = (char8)0;
}
while (posB < textB.Length)
{
char8B = textB[posB++];
if (char8B != '\r')
break;
char8B = (char8)0;
}
if ((char8A == (char8)0) && (char8B == (char8)0))
return true;
if (char8A != char8B)
return false;
}
}
public static Result<void> WriteTextFile(StringView path, StringView text)
{
var stream = scope FileStream();
if (stream.Create(path) case .Err)
{
return .Err;
}
if (stream.WriteStrUnsized(text) case .Err)
return .Err;
return .Ok;
}
public static int LevenshteinDistance(String s, String t)
{
int n = s.Length;
int m = t.Length;
int32[,] d = new int32[n + 1, m + 1];
defer delete d;
if (n == 0)
{
return m;
}
if (m == 0)
{
return n;
}
for (int32 i = 0; i <= n; d[i, 0] = i++)
{}
for (int32 j = 0; j <= m; d[0, j] = j++)
{}
for (int32 i = 1; i <= n; i++)
{
for (int32 j = 1; j <= m; j++)
{
int32 cost = (t[j - 1] == s[i - 1]) ? 0 : 1;
d[i, j] = Math.Min(
Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1),
d[i - 1, j - 1] + cost);
}
}
return d[n, m];
}
/*public static List<TSource> ToSortedList<TSource>(this IEnumerable<TSource> source)
{
var list = source.ToList();
list.Sort();
return list;
}*/
public static int64 DecodeInt64(ref uint8* ptr)
{
int64 value = 0;
int32 shift = 0;
int64 curByte;
repeat
{
curByte = *(ptr++);
value |= ((curByte & 0x7f) << shift);
shift += 7;
} while (curByte >= 128);
// Sign extend negative numbers.
if (((curByte & 0x40) != 0) && (shift < 64))
value |= -1 << shift;
return value;
}
public static int32 DecodeInt(uint8[] buf, ref int idx)
{
int32 value = 0;
int32 Shift = 0;
int32 curByte;
repeat
{
curByte = buf[idx++];
value |= ((curByte & 0x7f) << Shift);
Shift += 7;
} while (curByte >= 128);
// Sign extend negative numbers.
if ((curByte & 0x40) != 0)
value |= -1 << Shift;
return value;
}
public static void EncodeInt(uint8[] buf, ref int idx, int value)
{
int curValue = value;
bool hasMore;
repeat
{
uint8 curByte = (uint8)(curValue & 0x7f);
curValue >>= 7;
hasMore = !((((curValue == 0) && ((curByte & 0x40) == 0)) ||
((curValue == -1) && ((curByte & 0x40) != 0))));
if (hasMore)
curByte |= 0x80;
buf[idx++] = curByte;
}
while (hasMore);
}
public static bool Contains<T>(IEnumerator<T> itr, T value)
{
for (var check in itr)
if (check == value)
return true;
return false;
}
public static void QuoteString(String str, String strOut)
{
strOut.Append('"');
for (int i < str.Length)
{
char8 c = str[i];
strOut.Append(c);
}
strOut.Append('"');
}
public static void ParseSpaceSep(String str, ref int idx, String subStr)
{
while (idx < str.Length)
{
char8 c = str[idx];
if (c != ' ')
break;
idx++;
}
if (idx >= str.Length)
return;
// Quoted
if (str[idx] == '"')
{
idx++;
while (idx < str.Length)
{
char8 c = str[idx++];
if (c == '"')
break;
subStr.Append(c);
}
return;
}
// Unquoted
while (idx < str.Length)
{
char8 c = str[idx++];
if (c == ' ')
break;
subStr.Append(c);
}
}
public static void SnapScale(ref float val, float scale)
{
val *= scale;
float frac = val - (int)val;
if ((frac <= 0.001f) || (frac >= 0.999f))
val = (float)Math.Round(val);
}
public static void RoundScale(ref float val, float scale)
{
val = (float)Math.Round(val * scale);
}
}
}

View file

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Text;
using Beefy.widgets;
namespace Beefy.events
{
public class DialogEvent : Event
{
public bool mCloseDialog;
public ButtonWidget mButton;
public String mResult;
}
}

View file

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Beefy.events
{
public class DragEvent : Event
{
public float mX;
public float mY;
public Object mDragTarget;
public int32 mDragTargetDir;
public bool mDragAllowed = true;
}
}

View file

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Beefy.events
{
public class EditEvent : Event
{
//string mText;
}
}

View file

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Beefy.events
{
public class Event
{
public Object mSender;
public bool mHandled;
}
}

View file

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Text;
using Beefy.widgets;
namespace Beefy.widgets
{
public enum KeyFlags
{
None = 0,
Alt = 1,
Ctrl = 2,
Shift = 4
}
}
namespace Beefy.events
{
/*[Flags]
public enum KeyFlags
{
Alt = 1,
Ctrl = 2,
Shift = 4
}*/
public class KeyDownEvent : Event
{
public KeyFlags mKeyFlags;
public KeyCode mKeyCode;
public bool mIsRepeat;
}
public class KeyCharEvent : Event
{
public char32 mChar;
}
}

View file

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Text;
using Beefy.widgets;
namespace Beefy.events
{
public class MouseEvent : Event
{
public float mX;
public float mY;
public int32 mBtn;
public int32 mBtnCount;
public int32 mWheelDelta;
public void GetRootCoords(out float x, out float y)
{
Widget widget = (Widget)mSender;
widget.SelfToRootTranslate(mX, mY, out x, out y);
}
}
}

View file

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Beefy.events
{
public class ScrollEvent : Event
{
public double mOldPos;
public double mNewPos;
public bool mIsFromUser;
}
}

View file

@ -0,0 +1,9 @@
using System;
using System.Collections.Generic;
using System.Text;
using Beefy.widgets;
namespace Beefy.events
{
}

View file

@ -0,0 +1,493 @@
using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Beefy.geom;
namespace Beefy.geom
{
public struct Matrix4
{
public float m00;
public float m01;
public float m02;
public float m03;
public float m10;
public float m11;
public float m12;
public float m13;
public float m20;
public float m21;
public float m22;
public float m23;
public float m30;
public float m31;
public float m32;
public float m33;
public static readonly Matrix4 Identity = Matrix4(1f, 0f, 0f, 0f,
0f, 1f, 0f, 0f,
0f, 0f, 1f, 0f,
0f, 0f, 0f, 1f);
public this(
float m00, float m01, float m02, float m03,
float m10, float m11, float m12, float m13,
float m20, float m21, float m22, float m23,
float m30, float m31, float m32, float m33)
{
this.m00 = m00;
this.m01 = m01;
this.m02 = m02;
this.m03 = m03;
this.m10 = m10;
this.m11 = m11;
this.m12 = m12;
this.m13 = m13;
this.m20 = m20;
this.m21 = m21;
this.m22 = m22;
this.m23 = m23;
this.m30 = m30;
this.m31 = m31;
this.m32 = m32;
this.m33 = m33;
}
public static Matrix4 CreatePerspective(float width, float height, float nearPlaneDistance, float farPlaneDistance)
{
Matrix4 matrix;
if (nearPlaneDistance <= 0f)
{
Runtime.FatalError("nearPlaneDistance <= 0");
}
if (farPlaneDistance <= 0f)
{
Runtime.FatalError("farPlaneDistance <= 0");
}
if (nearPlaneDistance >= farPlaneDistance)
{
Runtime.FatalError("nearPlaneDistance >= farPlaneDistance");
}
/*matrix.M11 = (2f * nearPlaneDistance) / width;
matrix.M12 = matrix.M13 = matrix.M14 = 0f;
matrix.M22 = (2f * nearPlaneDistance) / height;
matrix.M21 = matrix.M23 = matrix.M24 = 0f;
matrix.M33 = farPlaneDistance / (nearPlaneDistance - farPlaneDistance);
matrix.M31 = matrix.M32 = 0f;
matrix.M34 = -1f;
matrix.M41 = matrix.M42 = matrix.M44 = 0f;
matrix.M43 = (nearPlaneDistance * farPlaneDistance) / (nearPlaneDistance - farPlaneDistance);*/
/*matrix.m00 = (2f * nearPlaneDistance) / width;
matrix.m01 = 0f;
matrix.m02 = 0f;
matrix.m03 = 0f;
matrix.m10 = 0f;
matrix.m11 = (2f * nearPlaneDistance) / height;
matrix.m12 = 0f;
matrix.m13 = 0f;
matrix.m20 = 0f;
matrix.m21 = 0f;
matrix.m22 = farPlaneDistance / (nearPlaneDistance - farPlaneDistance);
matrix.m23 = (nearPlaneDistance * farPlaneDistance) / (nearPlaneDistance - farPlaneDistance);
matrix.m30 = 0f;
matrix.m31 = 0f;
matrix.m32 = -1f;
matrix.m33 = 0f; */
matrix.m00 = (2f * nearPlaneDistance) / width;
matrix.m10 = matrix.m20 = matrix.m30 = 0f;
matrix.m11 = (2f * nearPlaneDistance) / height;
matrix.m01 = matrix.m21 = matrix.m31 = 0f;
matrix.m22 = farPlaneDistance / (nearPlaneDistance - farPlaneDistance);
matrix.m02 = matrix.m12 = 0f;
matrix.m32 = -1f;
matrix.m03 = matrix.m13 = matrix.m33 = 0f;
matrix.m23 = (nearPlaneDistance * farPlaneDistance) / (nearPlaneDistance - farPlaneDistance);
return matrix;
}
public static Matrix4 CreatePerspectiveFieldOfView(float fieldOfView, float aspectRatio, float nearPlaneDistance, float farPlaneDistance)
{
Matrix4 result;
CreatePerspectiveFieldOfView(fieldOfView, aspectRatio, nearPlaneDistance, farPlaneDistance, out result);
return result;
}
public static void CreatePerspectiveFieldOfView(float fieldOfView, float aspectRatio, float nearPlaneDistance, float farPlaneDistance, out Matrix4 result)
{
if ((fieldOfView <= 0f) || (fieldOfView >= 3.141593f))
{
Runtime.FatalError("fieldOfView <= 0 or >= PI");
}
if (nearPlaneDistance <= 0f)
{
Runtime.FatalError("nearPlaneDistance <= 0");
}
if (farPlaneDistance <= 0f)
{
Runtime.FatalError("farPlaneDistance <= 0");
}
if (nearPlaneDistance >= farPlaneDistance)
{
Runtime.FatalError("nearPlaneDistance >= farPlaneDistance");
}
float num = 1f / ((float)Math.Tan((double)(fieldOfView * 0.5f)));
float num9 = num / aspectRatio;
result.m00 = num9;
result.m10 = result.m20 = result.m30 = 0;
result.m11 = num;
result.m01 = result.m21 = result.m31 = 0;
result.m02 = result.m12 = 0f;
result.m22 = farPlaneDistance / (nearPlaneDistance - farPlaneDistance);
result.m32 = -1;
result.m03 = result.m13 = result.m33 = 0;
result.m23 = (nearPlaneDistance * farPlaneDistance) / (nearPlaneDistance - farPlaneDistance);
}
public static Matrix4 CreatePerspectiveOffCenter(float left, float right, float bottom, float top, float nearPlaneDistance, float farPlaneDistance)
{
Matrix4 result;
CreatePerspectiveOffCenter(left, right, bottom, top, nearPlaneDistance, farPlaneDistance, out result);
return result;
}
public static void CreatePerspectiveOffCenter(float left, float right, float bottom, float top, float nearPlaneDistance, float farPlaneDistance, out Matrix4 result)
{
if (nearPlaneDistance <= 0f)
{
Runtime.FatalError("nearPlaneDistance <= 0");
}
if (farPlaneDistance <= 0f)
{
Runtime.FatalError("farPlaneDistance <= 0");
}
if (nearPlaneDistance >= farPlaneDistance)
{
Runtime.FatalError("nearPlaneDistance >= farPlaneDistance");
}
result.m00 = (2f * nearPlaneDistance) / (right - left);
result.m10 = result.m20 = result.m30 = 0;
result.m11 = (2f * nearPlaneDistance) / (top - bottom);
result.m01 = result.m21 = result.m31 = 0;
result.m02 = (left + right) / (right - left);
result.m12 = (top + bottom) / (top - bottom);
result.m22 = farPlaneDistance / (nearPlaneDistance - farPlaneDistance);
result.m32 = -1;
result.m23 = (nearPlaneDistance * farPlaneDistance) / (nearPlaneDistance - farPlaneDistance);
result.m03 = result.m13 = result.m33 = 0;
}
public static Matrix4 Multiply(Matrix4 m1, Matrix4 m2)
{
Matrix4 r;
r.m00 = m1.m00 * m2.m00 + m1.m01 * m2.m10 + m1.m02 * m2.m20 + m1.m03 * m2.m30;
r.m01 = m1.m00 * m2.m01 + m1.m01 * m2.m11 + m1.m02 * m2.m21 + m1.m03 * m2.m31;
r.m02 = m1.m00 * m2.m02 + m1.m01 * m2.m12 + m1.m02 * m2.m22 + m1.m03 * m2.m32;
r.m03 = m1.m00 * m2.m03 + m1.m01 * m2.m13 + m1.m02 * m2.m23 + m1.m03 * m2.m33;
r.m10 = m1.m10 * m2.m00 + m1.m11 * m2.m10 + m1.m12 * m2.m20 + m1.m13 * m2.m30;
r.m11 = m1.m10 * m2.m01 + m1.m11 * m2.m11 + m1.m12 * m2.m21 + m1.m13 * m2.m31;
r.m12 = m1.m10 * m2.m02 + m1.m11 * m2.m12 + m1.m12 * m2.m22 + m1.m13 * m2.m32;
r.m13 = m1.m10 * m2.m03 + m1.m11 * m2.m13 + m1.m12 * m2.m23 + m1.m13 * m2.m33;
r.m20 = m1.m20 * m2.m00 + m1.m21 * m2.m10 + m1.m22 * m2.m20 + m1.m23 * m2.m30;
r.m21 = m1.m20 * m2.m01 + m1.m21 * m2.m11 + m1.m22 * m2.m21 + m1.m23 * m2.m31;
r.m22 = m1.m20 * m2.m02 + m1.m21 * m2.m12 + m1.m22 * m2.m22 + m1.m23 * m2.m32;
r.m23 = m1.m20 * m2.m03 + m1.m21 * m2.m13 + m1.m22 * m2.m23 + m1.m23 * m2.m33;
r.m30 = m1.m30 * m2.m00 + m1.m31 * m2.m10 + m1.m32 * m2.m20 + m1.m33 * m2.m30;
r.m31 = m1.m30 * m2.m01 + m1.m31 * m2.m11 + m1.m32 * m2.m21 + m1.m33 * m2.m31;
r.m32 = m1.m30 * m2.m02 + m1.m31 * m2.m12 + m1.m32 * m2.m22 + m1.m33 * m2.m32;
r.m33 = m1.m30 * m2.m03 + m1.m31 * m2.m13 + m1.m32 * m2.m23 + m1.m33 * m2.m33;
return r;
}
public static Matrix4 Transpose(Matrix4 m)
{
return Matrix4(
m.m00, m.m10, m.m20, m.m30,
m.m01, m.m11, m.m21, m.m31,
m.m02, m.m12, m.m22, m.m32,
m.m03, m.m13, m.m23, m.m33);
}
public static Matrix4 CreateTranslation(float x, float y, float z)
{
return Matrix4(
1, 0, 0, x,
0, 1, 0, y,
0, 0, 1, z,
0, 0, 0, 1);
}
public static Matrix4 CreateTransform(Vector3 position, Vector3 scale, Quaternion orientation)
{
// Ordering:
// 1. Scale
// 2. Rotate
// 3. Translate
Matrix4 rot = orientation.ToMatrix();
return Matrix4(
scale.mX * rot.m00, scale.mY * rot.m01, scale.mZ * rot.m02, position.mX,
scale.mX * rot.m10, scale.mY * rot.m11, scale.mZ * rot.m12, position.mY,
scale.mX * rot.m20, scale.mY * rot.m21, scale.mZ * rot.m22, position.mZ,
0, 0, 0, 1);
}
public static Matrix4 CreateRotationX(float radians)
{
Matrix4 result = Matrix4.Identity;
var val1 = (float)Math.Cos(radians);
var val2 = (float)Math.Sin(radians);
result.m11 = val1;
result.m21 = val2;
result.m12 = -val2;
result.m22 = val1;
return result;
}
public static Matrix4 CreateRotationY(float radians)
{
Matrix4 returnMatrix = Matrix4.Identity;
var val1 = (float)Math.Cos(radians);
var val2 = (float)Math.Sin(radians);
returnMatrix.m00 = val1;
returnMatrix.m20 = -val2;
returnMatrix.m02 = val2;
returnMatrix.m22 = val1;
return returnMatrix;
}
public static Matrix4 CreateRotationZ(float radians)
{
Matrix4 returnMatrix = Matrix4.Identity;
var val1 = (float)Math.Cos(radians);
var val2 = (float)Math.Sin(radians);
returnMatrix.m00 = val1;
returnMatrix.m10 = val2;
returnMatrix.m01 = -val2;
returnMatrix.m11 = val1;
return returnMatrix;
}
public static Matrix4 CreateScale(float scale)
{
Matrix4 result;
result.m00 = scale;
result.m10 = 0;
result.m20 = 0;
result.m30 = 0;
result.m01 = 0;
result.m11 = scale;
result.m21 = 0;
result.m31 = 0;
result.m02 = 0;
result.m12 = 0;
result.m22 = scale;
result.m32 = 0;
result.m03 = 0;
result.m13 = 0;
result.m23 = 0;
result.m33 = 1;
return result;
}
public static Matrix4 CreateScale(float xScale, float yScale, float zScale)
{
Matrix4 result;
result.m00 = xScale;
result.m10 = 0;
result.m20 = 0;
result.m30 = 0;
result.m01 = 0;
result.m11 = yScale;
result.m21 = 0;
result.m31 = 0;
result.m02 = 0;
result.m12 = 0;
result.m22 = zScale;
result.m32 = 0;
result.m03 = 0;
result.m13 = 0;
result.m23 = 0;
result.m33 = 1;
return result;
}
public static Matrix4 CreateScale(Vector3 scales)
{
Matrix4 result;
result.m00 = scales.mX;
result.m10 = 0;
result.m20 = 0;
result.m30 = 0;
result.m01 = 0;
result.m11 = scales.mY;
result.m21 = 0;
result.m31 = 0;
result.m02 = 0;
result.m12 = 0;
result.m22 = scales.mZ;
result.m32 = 0;
result.m03 = 0;
result.m13 = 0;
result.m23 = 0;
result.m33 = 1;
return result;
}
public static Matrix4 CreateTranslation(Vector3 position)
{
Matrix4 result;
result.m00 = 1;
result.m10 = 0;
result.m20 = 0;
result.m30 = 0;
result.m01 = 0;
result.m11 = 1;
result.m21 = 0;
result.m31 = 0;
result.m02 = 0;
result.m12 = 0;
result.m22 = 1;
result.m32 = 0;
result.m03 = position.mX;
result.m13 = position.mY;
result.m23 = position.mZ;
result.m33 = 1;
return result;
}
/*public static Matrix4 Inverse()
{
Real m00 = m[0][0], m01 = m[0][1], m02 = m[0][2], m03 = m[0][3];
Real m10 = m[1][0], m11 = m[1][1], m12 = m[1][2], m13 = m[1][3];
Real m20 = m[2][0], m21 = m[2][1], m22 = m[2][2], m23 = m[2][3];
Real m30 = m[3][0], m31 = m[3][1], m32 = m[3][2], m33 = m[3][3];
Real v0 = m20 * m31 - m21 * m30;
Real v1 = m20 * m32 - m22 * m30;
Real v2 = m20 * m33 - m23 * m30;
Real v3 = m21 * m32 - m22 * m31;
Real v4 = m21 * m33 - m23 * m31;
Real v5 = m22 * m33 - m23 * m32;
Real t00 = + (v5 * m11 - v4 * m12 + v3 * m13);
Real t10 = - (v5 * m10 - v2 * m12 + v1 * m13);
Real t20 = + (v4 * m10 - v2 * m11 + v0 * m13);
Real t30 = - (v3 * m10 - v1 * m11 + v0 * m12);
Real invDet = 1 / (t00 * m00 + t10 * m01 + t20 * m02 + t30 * m03);
Real d00 = t00 * invDet;
Real d10 = t10 * invDet;
Real d20 = t20 * invDet;
Real d30 = t30 * invDet;
Real d01 = - (v5 * m01 - v4 * m02 + v3 * m03) * invDet;
Real d11 = + (v5 * m00 - v2 * m02 + v1 * m03) * invDet;
Real d21 = - (v4 * m00 - v2 * m01 + v0 * m03) * invDet;
Real d31 = + (v3 * m00 - v1 * m01 + v0 * m02) * invDet;
v0 = m10 * m31 - m11 * m30;
v1 = m10 * m32 - m12 * m30;
v2 = m10 * m33 - m13 * m30;
v3 = m11 * m32 - m12 * m31;
v4 = m11 * m33 - m13 * m31;
v5 = m12 * m33 - m13 * m32;
Real d02 = + (v5 * m01 - v4 * m02 + v3 * m03) * invDet;
Real d12 = - (v5 * m00 - v2 * m02 + v1 * m03) * invDet;
Real d22 = + (v4 * m00 - v2 * m01 + v0 * m03) * invDet;
Real d32 = - (v3 * m00 - v1 * m01 + v0 * m02) * invDet;
v0 = m21 * m10 - m20 * m11;
v1 = m22 * m10 - m20 * m12;
v2 = m23 * m10 - m20 * m13;
v3 = m22 * m11 - m21 * m12;
v4 = m23 * m11 - m21 * m13;
v5 = m23 * m12 - m22 * m13;
Real d03 = - (v5 * m01 - v4 * m02 + v3 * m03) * invDet;
Real d13 = + (v5 * m00 - v2 * m02 + v1 * m03) * invDet;
Real d23 = - (v4 * m00 - v2 * m01 + v0 * m03) * invDet;
Real d33 = + (v3 * m00 - v1 * m01 + v0 * m02) * invDet;
return Matrix4(
d00, d01, d02, d03,
d10, d11, d12, d13,
d20, d21, d22, d23,
d30, d31, d32, d33);
}*/
bool IsAffine()
{
return m30 == 0 && m31 == 0 && m32 == 0 && m33 == 1;
}
public static Matrix4 InverseAffine(Matrix4 mtx)
{
Debug.Assert(mtx.IsAffine());
float m10 = mtx.m10, m11 = mtx.m11, m12 = mtx.m12;
float m20 = mtx.m20, m21 = mtx.m21, m22 = mtx.m22;
float t00 = m22 * m11 - m21 * m12;
float t10 = m20 * m12 - m22 * m10;
float t20 = m21 * m10 - m20 * m11;
float m00 = mtx.m00, m01 = mtx.m01, m02 = mtx.m02;
float invDet = 1 / (m00 * t00 + m01 * t10 + m02 * t20);
t00 *= invDet; t10 *= invDet; t20 *= invDet;
m00 *= invDet; m01 *= invDet; m02 *= invDet;
float r00 = t00;
float r01 = m02 * m21 - m01 * m22;
float r02 = m01 * m12 - m02 * m11;
float r10 = t10;
float r11 = m00 * m22 - m02 * m20;
float r12 = m02 * m10 - m00 * m12;
float r20 = t20;
float r21 = m01 * m20 - m00 * m21;
float r22 = m00 * m11 - m01 * m10;
float m03 = mtx.m03, m13 = mtx.m13, m23 = mtx.m23;
float r03 = -(r00 * m03 + r01 * m13 + r02 * m23);
float r13 = -(r10 * m03 + r11 * m13 + r12 * m23);
float r23 = -(r20 * m03 + r21 * m13 + r22 * m23);
return Matrix4(
r00, r01, r02, r03,
r10, r11, r12, r13,
r20, r21, r22, r23,
0, 0, 0, 1);
}
}
}

View file

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Beefy.geom
{
public struct Point
{
public float x;
public float y;
public this(float x, float y)
{
this.x = x;
this.y = y;
}
}
}

View file

@ -0,0 +1,775 @@
using System;
using Beefy.gfx;
namespace Beefy.geom
{
public struct Quaternion : IHashable, IEquatable<Quaternion>
{
public float mX;
public float mY;
public float mZ;
public float mW;
public static readonly Quaternion Identity = Quaternion(0, 0, 0, 1);
public this(float x, float y, float z, float w)
{
mX = x;
mY = y;
mZ = z;
mW = w;
}
public this(Vector3 vectorPart, float scalarPart)
{
mX = vectorPart.mX;
mY = vectorPart.mY;
mZ = vectorPart.mZ;
mW = scalarPart;
}
public static Quaternion Add(Quaternion quaternion1, Quaternion quaternion2)
{
Quaternion quaternion;
quaternion.mX = quaternion1.mX + quaternion2.mX;
quaternion.mY = quaternion1.mY + quaternion2.mY;
quaternion.mZ = quaternion1.mZ + quaternion2.mZ;
quaternion.mW = quaternion1.mW + quaternion2.mW;
return quaternion;
}
public static void Add(ref Quaternion quaternion1, ref Quaternion quaternion2, out Quaternion result)
{
result.mX = quaternion1.mX + quaternion2.mX;
result.mY = quaternion1.mY + quaternion2.mY;
result.mZ = quaternion1.mZ + quaternion2.mZ;
result.mW = quaternion1.mW + quaternion2.mW;
}
public static Quaternion Concatenate(Quaternion value1, Quaternion value2)
{
Quaternion quaternion;
float x = value2.mX;
float y = value2.mY;
float z = value2.mZ;
float w = value2.mW;
float num4 = value1.mX;
float num3 = value1.mY;
float num2 = value1.mZ;
float num = value1.mW;
float num12 = (y * num2) - (z * num3);
float num11 = (z * num4) - (x * num2);
float num10 = (x * num3) - (y * num4);
float num9 = ((x * num4) + (y * num3)) + (z * num2);
quaternion.mX = ((x * num) + (num4 * w)) + num12;
quaternion.mY = ((y * num) + (num3 * w)) + num11;
quaternion.mZ = ((z * num) + (num2 * w)) + num10;
quaternion.mW = (w * num) - num9;
return quaternion;
}
public static void Concatenate(ref Quaternion value1, ref Quaternion value2, out Quaternion result)
{
float x = value2.mX;
float y = value2.mY;
float z = value2.mZ;
float w = value2.mW;
float num4 = value1.mX;
float num3 = value1.mY;
float num2 = value1.mZ;
float num = value1.mW;
float num12 = (y * num2) - (z * num3);
float num11 = (z * num4) - (x * num2);
float num10 = (x * num3) - (y * num4);
float num9 = ((x * num4) + (y * num3)) + (z * num2);
result.mX = ((x * num) + (num4 * w)) + num12;
result.mY = ((y * num) + (num3 * w)) + num11;
result.mZ = ((z * num) + (num2 * w)) + num10;
result.mW = (w * num) - num9;
}
public void Conjugate() mut
{
mX = -mX;
mY = -mY;
mZ = -mZ;
}
public static Quaternion Conjugate(Quaternion value)
{
Quaternion quaternion;
quaternion.mX = -value.mX;
quaternion.mY = -value.mY;
quaternion.mZ = -value.mZ;
quaternion.mW = value.mW;
return quaternion;
}
public static void Conjugate(ref Quaternion value, out Quaternion result)
{
result.mX = -value.mX;
result.mY = -value.mY;
result.mZ = -value.mZ;
result.mW = value.mW;
}
public static Quaternion CreateFromAxisAngle(Vector3 axis, float angle)
{
Quaternion quaternion;
float num2 = angle * 0.5f;
float num = (float)Math.Sin((double)num2);
float num3 = (float)Math.Cos((double)num2);
quaternion.mX = axis.mX * num;
quaternion.mY = axis.mY * num;
quaternion.mZ = axis.mZ * num;
quaternion.mW = num3;
return quaternion;
}
public static void CreateFromAxisAngle(ref Vector3 axis, float angle, out Quaternion result)
{
float num2 = angle * 0.5f;
float num = (float)Math.Sin((double)num2);
float num3 = (float)Math.Cos((double)num2);
result.mX = axis.mX * num;
result.mY = axis.mY * num;
result.mZ = axis.mZ * num;
result.mW = num3;
}
public static Quaternion CreateFromRotationMatrix(Matrix4 matrix)
{
float num8 = (matrix.m11 + matrix.m22) + matrix.m33;
Quaternion quaternion = Quaternion();
if (num8 > 0f)
{
float num = (float)Math.Sqrt((double)(num8 + 1f));
quaternion.mW = num * 0.5f;
num = 0.5f / num;
quaternion.mX = (matrix.m23 - matrix.m32) * num;
quaternion.mY = (matrix.m31 - matrix.m13) * num;
quaternion.mZ = (matrix.m12 - matrix.m21) * num;
return quaternion;
}
if ((matrix.m11 >= matrix.m22) && (matrix.m11 >= matrix.m33))
{
float num7 = (float)Math.Sqrt((double)(((1f + matrix.m11) - matrix.m22) - matrix.m33));
float num4 = 0.5f / num7;
quaternion.mX = 0.5f * num7;
quaternion.mY = (matrix.m12 + matrix.m21) * num4;
quaternion.mZ = (matrix.m13 + matrix.m31) * num4;
quaternion.mW = (matrix.m23 - matrix.m32) * num4;
return quaternion;
}
if (matrix.m22 > matrix.m33)
{
float num6 = (float)Math.Sqrt((double)(((1f + matrix.m22) - matrix.m11) - matrix.m33));
float num3 = 0.5f / num6;
quaternion.mX = (matrix.m21 + matrix.m12) * num3;
quaternion.mY = 0.5f * num6;
quaternion.mZ = (matrix.m32 + matrix.m23) * num3;
quaternion.mW = (matrix.m31 - matrix.m13) * num3;
return quaternion;
}
float num5 = (float)Math.Sqrt((double)(((1f + matrix.m33) - matrix.m11) - matrix.m22));
float num2 = 0.5f / num5;
quaternion.mX = (matrix.m31 + matrix.m13) * num2;
quaternion.mY = (matrix.m32 + matrix.m23) * num2;
quaternion.mZ = 0.5f * num5;
quaternion.mW = (matrix.m12 - matrix.m21) * num2;
return quaternion;
}
public static void CreateFromRotationMatrix(ref Matrix4 matrix, out Quaternion result)
{
float num8 = (matrix.m11 + matrix.m22) + matrix.m33;
if (num8 > 0f)
{
float num = (float)Math.Sqrt((double)(num8 + 1f));
result.mW = num * 0.5f;
num = 0.5f / num;
result.mX = (matrix.m23 - matrix.m32) * num;
result.mY = (matrix.m31 - matrix.m13) * num;
result.mZ = (matrix.m12 - matrix.m21) * num;
}
else if ((matrix.m11 >= matrix.m22) && (matrix.m11 >= matrix.m33))
{
float num7 = (float)Math.Sqrt((double)(((1f + matrix.m11) - matrix.m22) - matrix.m33));
float num4 = 0.5f / num7;
result.mX = 0.5f * num7;
result.mY = (matrix.m12 + matrix.m21) * num4;
result.mZ = (matrix.m13 + matrix.m31) * num4;
result.mW = (matrix.m23 - matrix.m32) * num4;
}
else if (matrix.m22 > matrix.m33)
{
float num6 = (float)Math.Sqrt((double)(((1f + matrix.m22) - matrix.m11) - matrix.m33));
float num3 = 0.5f / num6;
result.mX = (matrix.m21 + matrix.m12) * num3;
result.mY = 0.5f * num6;
result.mZ = (matrix.m32 + matrix.m23) * num3;
result.mW = (matrix.m31 - matrix.m13) * num3;
}
else
{
float num5 = (float)Math.Sqrt((double)(((1f + matrix.m33) - matrix.m11) - matrix.m22));
float num2 = 0.5f / num5;
result.mX = (matrix.m31 + matrix.m13) * num2;
result.mY = (matrix.m32 + matrix.m23) * num2;
result.mZ = 0.5f * num5;
result.mW = (matrix.m12 - matrix.m21) * num2;
}
}
public static Quaternion CreateFromYawPitchRoll(float yaw, float pitch, float roll)
{
Quaternion quaternion;
float num9 = roll * 0.5f;
float num6 = (float)Math.Sin((double)num9);
float num5 = (float)Math.Cos((double)num9);
float num8 = pitch * 0.5f;
float num4 = (float)Math.Sin((double)num8);
float num3 = (float)Math.Cos((double)num8);
float num7 = yaw * 0.5f;
float num2 = (float)Math.Sin((double)num7);
float num = (float)Math.Cos((double)num7);
quaternion.mX = ((num * num4) * num5) + ((num2 * num3) * num6);
quaternion.mY = ((num2 * num3) * num5) - ((num * num4) * num6);
quaternion.mZ = ((num * num3) * num6) - ((num2 * num4) * num5);
quaternion.mW = ((num * num3) * num5) + ((num2 * num4) * num6);
return quaternion;
}
public static void CreateFromYawPitchRoll(float yaw, float pitch, float roll, out Quaternion result)
{
float num9 = roll * 0.5f;
float num6 = (float)Math.Sin((double)num9);
float num5 = (float)Math.Cos((double)num9);
float num8 = pitch * 0.5f;
float num4 = (float)Math.Sin((double)num8);
float num3 = (float)Math.Cos((double)num8);
float num7 = yaw * 0.5f;
float num2 = (float)Math.Sin((double)num7);
float num = (float)Math.Cos((double)num7);
result.mX = ((num * num4) * num5) + ((num2 * num3) * num6);
result.mY = ((num2 * num3) * num5) - ((num * num4) * num6);
result.mZ = ((num * num3) * num6) - ((num2 * num4) * num5);
result.mW = ((num * num3) * num5) + ((num2 * num4) * num6);
}
public static Quaternion Divide(Quaternion quaternion1, Quaternion quaternion2)
{
Quaternion quaternion;
float x = quaternion1.mX;
float y = quaternion1.mY;
float z = quaternion1.mZ;
float w = quaternion1.mW;
float num14 = (((quaternion2.mX * quaternion2.mX) + (quaternion2.mY * quaternion2.mY)) + (quaternion2.mZ * quaternion2.mZ)) + (quaternion2.mW * quaternion2.mW);
float num5 = 1f / num14;
float num4 = -quaternion2.mX * num5;
float num3 = -quaternion2.mY * num5;
float num2 = -quaternion2.mZ * num5;
float num = quaternion2.mW * num5;
float num13 = (y * num2) - (z * num3);
float num12 = (z * num4) - (x * num2);
float num11 = (x * num3) - (y * num4);
float num10 = ((x * num4) + (y * num3)) + (z * num2);
quaternion.mX = ((x * num) + (num4 * w)) + num13;
quaternion.mY = ((y * num) + (num3 * w)) + num12;
quaternion.mZ = ((z * num) + (num2 * w)) + num11;
quaternion.mW = (w * num) - num10;
return quaternion;
}
public static void Divide(ref Quaternion quaternion1, ref Quaternion quaternion2, out Quaternion result)
{
float x = quaternion1.mX;
float y = quaternion1.mY;
float z = quaternion1.mZ;
float w = quaternion1.mW;
float num14 = (((quaternion2.mX * quaternion2.mX) + (quaternion2.mY * quaternion2.mY)) + (quaternion2.mZ * quaternion2.mZ)) + (quaternion2.mW * quaternion2.mW);
float num5 = 1f / num14;
float num4 = -quaternion2.mX * num5;
float num3 = -quaternion2.mY * num5;
float num2 = -quaternion2.mZ * num5;
float num = quaternion2.mW * num5;
float num13 = (y * num2) - (z * num3);
float num12 = (z * num4) - (x * num2);
float num11 = (x * num3) - (y * num4);
float num10 = ((x * num4) + (y * num3)) + (z * num2);
result.mX = ((x * num) + (num4 * w)) + num13;
result.mY = ((y * num) + (num3 * w)) + num12;
result.mZ = ((z * num) + (num2 * w)) + num11;
result.mW = (w * num) - num10;
}
public static float Dot(Quaternion quaternion1, Quaternion quaternion2)
{
return ((((quaternion1.mX * quaternion2.mX) + (quaternion1.mY * quaternion2.mY)) + (quaternion1.mZ * quaternion2.mZ)) + (quaternion1.mW * quaternion2.mW));
}
public static void Dot(ref Quaternion quaternion1, ref Quaternion quaternion2, out float result)
{
result = (((quaternion1.mX * quaternion2.mX) + (quaternion1.mY * quaternion2.mY)) + (quaternion1.mZ * quaternion2.mZ)) + (quaternion1.mW * quaternion2.mW);
}
public bool Equals(Quaternion other)
{
return (mX == other.mX) && (mY == other.mY) && (mZ == other.mZ) && (mW == other.mW);
}
public int GetHashCode()
{
//return ((mX.GetHashCode() + mY.GetHashCode()) + mZ.GetHashCode()) + mW.GetHashCode();
ThrowUnimplemented();
}
public static Quaternion Inverse(Quaternion quaternion)
{
Quaternion quaternion2;
float num2 = (((quaternion.mX * quaternion.mX) + (quaternion.mY * quaternion.mY)) + (quaternion.mZ * quaternion.mZ)) + (quaternion.mW * quaternion.mW);
float num = 1f / num2;
quaternion2.mX = -quaternion.mX * num;
quaternion2.mY = -quaternion.mY * num;
quaternion2.mZ = -quaternion.mZ * num;
quaternion2.mW = quaternion.mW * num;
return quaternion2;
}
public static void Inverse(ref Quaternion quaternion, out Quaternion result)
{
float num2 = (((quaternion.mX * quaternion.mX) + (quaternion.mY * quaternion.mY)) + (quaternion.mZ * quaternion.mZ)) + (quaternion.mW * quaternion.mW);
float num = 1f / num2;
result.mX = -quaternion.mX * num;
result.mY = -quaternion.mY * num;
result.mZ = -quaternion.mZ * num;
result.mW = quaternion.mW * num;
}
public float Length()
{
float num = (((mX * mX) + (mY * mY)) + (mZ * mZ)) + (mW * mW);
return (float)Math.Sqrt((double)num);
}
public float LengthSquared()
{
return ((((mX * mX) + (mY * mY)) + (mZ * mZ)) + (mW * mW));
}
public static Quaternion Lerp(Quaternion quaternion1, Quaternion quaternion2, float amount)
{
float num = amount;
float num2 = 1f - num;
Quaternion quaternion = Quaternion();
float num5 = (((quaternion1.mX * quaternion2.mX) + (quaternion1.mY * quaternion2.mY)) + (quaternion1.mZ * quaternion2.mZ)) + (quaternion1.mW * quaternion2.mW);
if (num5 >= 0f)
{
quaternion.mX = (num2 * quaternion1.mX) + (num * quaternion2.mX);
quaternion.mY = (num2 * quaternion1.mY) + (num * quaternion2.mY);
quaternion.mZ = (num2 * quaternion1.mZ) + (num * quaternion2.mZ);
quaternion.mW = (num2 * quaternion1.mW) + (num * quaternion2.mW);
}
else
{
quaternion.mX = (num2 * quaternion1.mX) - (num * quaternion2.mX);
quaternion.mY = (num2 * quaternion1.mY) - (num * quaternion2.mY);
quaternion.mZ = (num2 * quaternion1.mZ) - (num * quaternion2.mZ);
quaternion.mW = (num2 * quaternion1.mW) - (num * quaternion2.mW);
}
float num4 = (((quaternion.mX * quaternion.mX) + (quaternion.mY * quaternion.mY)) + (quaternion.mZ * quaternion.mZ)) + (quaternion.mW * quaternion.mW);
float num3 = 1f / ((float)Math.Sqrt((double)num4));
quaternion.mX *= num3;
quaternion.mY *= num3;
quaternion.mZ *= num3;
quaternion.mW *= num3;
return quaternion;
}
public static void Lerp(ref Quaternion quaternion1, ref Quaternion quaternion2, float amount, out Quaternion result)
{
float num = amount;
float num2 = 1f - num;
float num5 = (((quaternion1.mX * quaternion2.mX) + (quaternion1.mY * quaternion2.mY)) + (quaternion1.mZ * quaternion2.mZ)) + (quaternion1.mW * quaternion2.mW);
if (num5 >= 0f)
{
result.mX = (num2 * quaternion1.mX) + (num * quaternion2.mX);
result.mY = (num2 * quaternion1.mY) + (num * quaternion2.mY);
result.mZ = (num2 * quaternion1.mZ) + (num * quaternion2.mZ);
result.mW = (num2 * quaternion1.mW) + (num * quaternion2.mW);
}
else
{
result.mX = (num2 * quaternion1.mX) - (num * quaternion2.mX);
result.mY = (num2 * quaternion1.mY) - (num * quaternion2.mY);
result.mZ = (num2 * quaternion1.mZ) - (num * quaternion2.mZ);
result.mW = (num2 * quaternion1.mW) - (num * quaternion2.mW);
}
float num4 = (((result.mX * result.mX) + (result.mY * result.mY)) + (result.mZ * result.mZ)) + (result.mW * result.mW);
float num3 = 1f / ((float)Math.Sqrt((double)num4));
result.mX *= num3;
result.mY *= num3;
result.mZ *= num3;
result.mW *= num3;
}
public static Quaternion Slerp(Quaternion quaternion1, Quaternion quaternion2, float amount)
{
float num2;
float num3;
Quaternion quaternion;
float num = amount;
float num4 = (((quaternion1.mX * quaternion2.mX) + (quaternion1.mY * quaternion2.mY)) + (quaternion1.mZ * quaternion2.mZ)) + (quaternion1.mW * quaternion2.mW);
bool flag = false;
if (num4 < 0f)
{
flag = true;
num4 = -num4;
}
if (num4 > 0.999999f)
{
num3 = 1f - num;
num2 = flag ? -num : num;
}
else
{
float num5 = (float)Math.Acos((double)num4);
float num6 = (float)(1.0 / Math.Sin((double)num5));
num3 = ((float)Math.Sin((double)((1f - num) * num5))) * num6;
num2 = flag ? (((float)(-Math.Sin((double)(num * num5))) * num6)) : (((float)Math.Sin((double)(num * num5))) * num6);
}
quaternion.mX = (num3 * quaternion1.mX) + (num2 * quaternion2.mX);
quaternion.mY = (num3 * quaternion1.mY) + (num2 * quaternion2.mY);
quaternion.mZ = (num3 * quaternion1.mZ) + (num2 * quaternion2.mZ);
quaternion.mW = (num3 * quaternion1.mW) + (num2 * quaternion2.mW);
return quaternion;
}
public static void Slerp(ref Quaternion quaternion1, ref Quaternion quaternion2, float amount, out Quaternion result)
{
float num2;
float num3;
float num = amount;
float num4 = (((quaternion1.mX * quaternion2.mX) + (quaternion1.mY * quaternion2.mY)) + (quaternion1.mZ * quaternion2.mZ)) + (quaternion1.mW * quaternion2.mW);
bool flag = false;
if (num4 < 0f)
{
flag = true;
num4 = -num4;
}
if (num4 > 0.999999f)
{
num3 = 1f - num;
num2 = flag ? -num : num;
}
else
{
float num5 = (float)Math.Acos((double)num4);
float num6 = (float)(1.0 / Math.Sin((double)num5));
num3 = ((float)Math.Sin((double)((1f - num) * num5))) * num6;
num2 = flag ? (((float)(-Math.Sin((double)(num * num5))) * num6)) : (((float)Math.Sin((double)(num * num5))) * num6);
}
result.mX = (num3 * quaternion1.mX) + (num2 * quaternion2.mX);
result.mY = (num3 * quaternion1.mY) + (num2 * quaternion2.mY);
result.mZ = (num3 * quaternion1.mZ) + (num2 * quaternion2.mZ);
result.mW = (num3 * quaternion1.mW) + (num2 * quaternion2.mW);
}
public static Quaternion Subtract(Quaternion quaternion1, Quaternion quaternion2)
{
Quaternion quaternion;
quaternion.mX = quaternion1.mX - quaternion2.mX;
quaternion.mY = quaternion1.mY - quaternion2.mY;
quaternion.mZ = quaternion1.mZ - quaternion2.mZ;
quaternion.mW = quaternion1.mW - quaternion2.mW;
return quaternion;
}
public static void Subtract(ref Quaternion quaternion1, ref Quaternion quaternion2, out Quaternion result)
{
result.mX = quaternion1.mX - quaternion2.mX;
result.mY = quaternion1.mY - quaternion2.mY;
result.mZ = quaternion1.mZ - quaternion2.mZ;
result.mW = quaternion1.mW - quaternion2.mW;
}
public static Quaternion Multiply(Quaternion quaternion1, Quaternion quaternion2)
{
Quaternion quaternion;
float x = quaternion1.mX;
float y = quaternion1.mY;
float z = quaternion1.mZ;
float w = quaternion1.mW;
float num4 = quaternion2.mX;
float num3 = quaternion2.mY;
float num2 = quaternion2.mZ;
float num = quaternion2.mW;
float num12 = (y * num2) - (z * num3);
float num11 = (z * num4) - (x * num2);
float num10 = (x * num3) - (y * num4);
float num9 = ((x * num4) + (y * num3)) + (z * num2);
quaternion.mX = ((x * num) + (num4 * w)) + num12;
quaternion.mY = ((y * num) + (num3 * w)) + num11;
quaternion.mZ = ((z * num) + (num2 * w)) + num10;
quaternion.mW = (w * num) - num9;
return quaternion;
}
public static Quaternion Multiply(Quaternion quaternion1, float scaleFactor)
{
Quaternion quaternion;
quaternion.mX = quaternion1.mX * scaleFactor;
quaternion.mY = quaternion1.mY * scaleFactor;
quaternion.mZ = quaternion1.mZ * scaleFactor;
quaternion.mW = quaternion1.mW * scaleFactor;
return quaternion;
}
public static void Multiply(ref Quaternion quaternion1, float scaleFactor, out Quaternion result)
{
result.mX = quaternion1.mX * scaleFactor;
result.mY = quaternion1.mY * scaleFactor;
result.mZ = quaternion1.mZ * scaleFactor;
result.mW = quaternion1.mW * scaleFactor;
}
public static void Multiply(ref Quaternion quaternion1, ref Quaternion quaternion2, out Quaternion result)
{
float x = quaternion1.mX;
float y = quaternion1.mY;
float z = quaternion1.mZ;
float w = quaternion1.mW;
float num4 = quaternion2.mX;
float num3 = quaternion2.mY;
float num2 = quaternion2.mZ;
float num = quaternion2.mW;
float num12 = (y * num2) - (z * num3);
float num11 = (z * num4) - (x * num2);
float num10 = (x * num3) - (y * num4);
float num9 = ((x * num4) + (y * num3)) + (z * num2);
result.mX = ((x * num) + (num4 * w)) + num12;
result.mY = ((y * num) + (num3 * w)) + num11;
result.mZ = ((z * num) + (num2 * w)) + num10;
result.mW = (w * num) - num9;
}
public static Quaternion Negate(Quaternion quaternion)
{
Quaternion quaternion2;
quaternion2.mX = -quaternion.mX;
quaternion2.mY = -quaternion.mY;
quaternion2.mZ = -quaternion.mZ;
quaternion2.mW = -quaternion.mW;
return quaternion2;
}
public static void Negate(ref Quaternion quaternion, out Quaternion result)
{
result.mX = -quaternion.mX;
result.mY = -quaternion.mY;
result.mZ = -quaternion.mZ;
result.mW = -quaternion.mW;
}
public void Normalize() mut
{
float num2 = (((mX * mX) + (mY * mY)) + (mZ * mZ)) + (mW * mW);
float num = 1f / ((float)Math.Sqrt((double)num2));
mX *= num;
mY *= num;
mZ *= num;
mW *= num;
}
public static Quaternion Normalize(Quaternion quaternion)
{
Quaternion quaternion2;
float num2 = (((quaternion.mX * quaternion.mX) + (quaternion.mY * quaternion.mY)) + (quaternion.mZ * quaternion.mZ)) + (quaternion.mW * quaternion.mW);
float num = 1f / ((float)Math.Sqrt((double)num2));
quaternion2.mX = quaternion.mX * num;
quaternion2.mY = quaternion.mY * num;
quaternion2.mZ = quaternion.mZ * num;
quaternion2.mW = quaternion.mW * num;
return quaternion2;
}
public static void Normalize(ref Quaternion quaternion, out Quaternion result)
{
float num2 = (((quaternion.mX * quaternion.mX) + (quaternion.mY * quaternion.mY)) + (quaternion.mZ * quaternion.mZ)) + (quaternion.mW * quaternion.mW);
float num = 1f / ((float)Math.Sqrt((double)num2));
result.mX = quaternion.mX * num;
result.mY = quaternion.mY * num;
result.mZ = quaternion.mZ * num;
result.mW = quaternion.mW * num;
}
public static Quaternion operator +(Quaternion quaternion1, Quaternion quaternion2)
{
Quaternion quaternion;
quaternion.mX = quaternion1.mX + quaternion2.mX;
quaternion.mY = quaternion1.mY + quaternion2.mY;
quaternion.mZ = quaternion1.mZ + quaternion2.mZ;
quaternion.mW = quaternion1.mW + quaternion2.mW;
return quaternion;
}
public static Quaternion operator /(Quaternion quaternion1, Quaternion quaternion2)
{
Quaternion quaternion;
float x = quaternion1.mX;
float y = quaternion1.mY;
float z = quaternion1.mZ;
float w = quaternion1.mW;
float num14 = (((quaternion2.mX * quaternion2.mX) + (quaternion2.mY * quaternion2.mY)) + (quaternion2.mZ * quaternion2.mZ)) + (quaternion2.mW * quaternion2.mW);
float num5 = 1f / num14;
float num4 = -quaternion2.mX * num5;
float num3 = -quaternion2.mY * num5;
float num2 = -quaternion2.mZ * num5;
float num = quaternion2.mW * num5;
float num13 = (y * num2) - (z * num3);
float num12 = (z * num4) - (x * num2);
float num11 = (x * num3) - (y * num4);
float num10 = ((x * num4) + (y * num3)) + (z * num2);
quaternion.mX = ((x * num) + (num4 * w)) + num13;
quaternion.mY = ((y * num) + (num3 * w)) + num12;
quaternion.mZ = ((z * num) + (num2 * w)) + num11;
quaternion.mW = (w * num) - num10;
return quaternion;
}
public static bool operator ==(Quaternion quaternion1, Quaternion quaternion2)
{
return ((((quaternion1.mX == quaternion2.mX) && (quaternion1.mY == quaternion2.mY)) && (quaternion1.mZ == quaternion2.mZ)) && (quaternion1.mW == quaternion2.mW));
}
public static bool operator !=(Quaternion quaternion1, Quaternion quaternion2)
{
if (((quaternion1.mX == quaternion2.mX) && (quaternion1.mY == quaternion2.mY)) && (quaternion1.mZ == quaternion2.mZ))
return (quaternion1.mW != quaternion2.mW);
return true;
}
public static Quaternion operator *(Quaternion quaternion1, Quaternion quaternion2)
{
Quaternion quaternion;
float x = quaternion1.mX;
float y = quaternion1.mY;
float z = quaternion1.mZ;
float w = quaternion1.mW;
float num4 = quaternion2.mX;
float num3 = quaternion2.mY;
float num2 = quaternion2.mZ;
float num = quaternion2.mW;
float num12 = (y * num2) - (z * num3);
float num11 = (z * num4) - (x * num2);
float num10 = (x * num3) - (y * num4);
float num9 = ((x * num4) + (y * num3)) + (z * num2);
quaternion.mX = ((x * num) + (num4 * w)) + num12;
quaternion.mY = ((y * num) + (num3 * w)) + num11;
quaternion.mZ = ((z * num) + (num2 * w)) + num10;
quaternion.mW = (w * num) - num9;
return quaternion;
}
public static Quaternion operator *(Quaternion quaternion1, float scaleFactor)
{
Quaternion quaternion;
quaternion.mX = quaternion1.mX * scaleFactor;
quaternion.mY = quaternion1.mY * scaleFactor;
quaternion.mZ = quaternion1.mZ * scaleFactor;
quaternion.mW = quaternion1.mW * scaleFactor;
return quaternion;
}
public static Quaternion operator -(Quaternion quaternion1, Quaternion quaternion2)
{
Quaternion quaternion;
quaternion.mX = quaternion1.mX - quaternion2.mX;
quaternion.mY = quaternion1.mY - quaternion2.mY;
quaternion.mZ = quaternion1.mZ - quaternion2.mZ;
quaternion.mW = quaternion1.mW - quaternion2.mW;
return quaternion;
}
public static Quaternion operator -(Quaternion quaternion)
{
Quaternion quaternion2;
quaternion2.mX = -quaternion.mX;
quaternion2.mY = -quaternion.mY;
quaternion2.mZ = -quaternion.mZ;
quaternion2.mW = -quaternion.mW;
return quaternion2;
}
public override void ToString(String outStr)
{
ThrowUnimplemented();
}
internal Matrix4 ToMatrix()
{
Matrix4 matrix = Matrix4.Identity;
ToMatrix(out matrix);
return matrix;
}
/*internal void ToMatrix(out Matrix4 matrix)
{
Quaternion.ToMatrix(this, out matrix);
}*/
public void ToMatrix(out Matrix4 matrix)
{
float fTx = mX + mX;
float fTy = mY + mY;
float fTz = mZ + mZ;
float fTwx = fTx * mW;
float fTwy = fTy * mW;
float fTwz = fTz * mW;
float fTxx = fTx * mX;
float fTxy = fTy * mX;
float fTxz = fTz * mX;
float fTyy = fTy * mY;
float fTyz = fTz * mY;
float fTzz = fTz * mZ;
matrix.m00 = 1.0f - (fTyy + fTzz);
matrix.m01 = fTxy - fTwz;
matrix.m02 = fTxz + fTwy;
matrix.m03 = 0;
matrix.m10 = fTxy + fTwz;
matrix.m11 = 1.0f - (fTxx + fTzz);
matrix.m12 = fTyz - fTwx;
matrix.m13 = 0;
matrix.m20 = fTxz - fTwy;
matrix.m21 = fTyz + fTwx;
matrix.m22 = 1.0f - (fTxx + fTyy);
matrix.m23 = 0;
matrix.m30 = 0;
matrix.m31 = 0;
matrix.m32 = 0;
matrix.m33 = 1.0f;
}
internal Vector3 XYZ
{
get
{
return Vector3(mX, mY, mZ);
}
set mut
{
mX = value.mX;
mY = value.mY;
mZ = value.mZ;
}
}
}
}

View file

@ -0,0 +1,197 @@
using System;
using System.Collections.Generic;
using System.Text;
using Beefy.gfx;
namespace Beefy.geom
{
public struct Rect
{
public float mX;
public float mY;
public float mWidth;
public float mHeight;
public float Left
{
get
{
return mX;
}
}
public float Top
{
get
{
return mY;
}
}
public float Right
{
get
{
return mX + mWidth;
}
}
public float Bottom
{
get
{
return mY + mHeight;
}
}
public float Width
{
get
{
return mWidth;
}
}
public float Height
{
get
{
return mHeight;
}
}
public this(float x = 0, float y = 0, float width = 0, float height = 0)
{
mX = x;
mY = y;
mWidth = width;
mHeight = height;
}
public void Set(float x = 0, float y = 0, float width = 0, float height = 0) mut
{
mX = x;
mY = y;
mWidth = width;
mHeight = height;
}
public bool Intersects(Rect rect)
{
return !((rect.mX + rect.mWidth <= mX) ||
(rect.mY + rect.mHeight <= mY) ||
(rect.mX >= mX + mWidth) ||
(rect.mY >= mY + mHeight));
}
public void SetIntersectionOf(Rect rect1, Rect rect2) mut
{
float x1 = Math.Max(rect1.mX, rect2.mX);
float x2 = Math.Min(rect1.mX + rect1.mWidth, rect2.mX + rect2.mWidth);
float y1 = Math.Max(rect1.mY, rect2.mY);
float y2 = Math.Min(rect1.mY + rect1.mHeight, rect2.mY + rect2.mHeight);
if (((x2 - x1) < 0) || ((y2 - y1) < 0))
{
mX = 0;
mY = 0;
mWidth = 0;
mHeight = 0;
}
else
{
mX = x1;
mY = y1;
mWidth = x2 - x1;
mHeight = y2 - y1;
}
}
public void SetIntersectionOf(Rect rect1, float x, float y, float width, float height) mut
{
float x1 = Math.Max(rect1.mX, x);
float x2 = Math.Min(rect1.mX + rect1.mWidth, x + width);
float y1 = Math.Max(rect1.mY, y);
float y2 = Math.Min(rect1.mY + rect1.mHeight, y + height);
if (((x2 - x1) < 0) || ((y2 - y1) < 0))
{
mX = 0;
mY = 0;
mWidth = 0;
mHeight = 0;
}
else
{
mX = x1;
mY = y1;
mWidth = x2 - x1;
mHeight = y2 - y1;
}
}
public Rect Intersection(Rect rect)
{
float x1 = Math.Max(mX, rect.mX);
float x2 = Math.Min(mX + mWidth, rect.mX + rect.mWidth);
float y1 = Math.Max(mY, rect.mY);
float y2 = Math.Min(mY + mHeight, rect.mY + rect.mHeight);
if (((x2 - x1) < 0) || ((y2 - y1) < 0))
return Rect(0, 0, 0, 0);
else
return Rect(x1, y1, x2 - x1, y2 - y1);
}
public Rect Union(Rect rect)
{
float x1 = Math.Min(mX, rect.mX);
float x2 = Math.Max(mX + mWidth, rect.mX + rect.mWidth);
float y1 = Math.Min(mY, rect.mY);
float y2 = Math.Max(mY + mHeight, rect.mY + rect.mHeight);
return Rect(x1, y1, x2 - x1, y2 - y1);
}
public bool Contains(float x, float y)
{
return ((x >= mX) && (x < mX + mWidth) &&
(y >= mY) && (y < mY + mHeight));
}
public bool Contains(Point pt)
{
return Contains(pt.x, pt.y);
}
public bool Contains(Rect rect)
{
return Contains(rect.mX, rect.mY) && Contains(rect.mX + rect.mWidth, rect.mY + rect.mHeight);
}
public void Offset(float x, float y) mut
{
mX += x;
mY += y;
}
public void Inflate(float x, float y) mut
{
mX -= x;
mWidth += x * 2;
mY -= y;
mHeight += y * 2;
}
public void Scale(float scaleX, float scaleY) mut
{
mX *= scaleX;
mY *= scaleY;
mWidth *= scaleX;
mHeight *= scaleY;
}
public void ScaleFrom(float scaleX, float scaleY, float centerX, float centerY) mut
{
Offset(-centerX, -centerY);
Scale(scaleX, scaleY);
Offset(centerX, centerY);
}
}
}

View file

@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Beefy.geom
{
public struct Vector2
{
public float mX;
public float mY;
public float Length
{
get
{
return (float)Math.Sqrt(mX * mX + mY * mY);
}
}
public float LengthSquared
{
get
{
return mX * mX + mY * mY;
}
}
public this(float x, float y)
{
mX = x;
mY = y;
}
public static void DistanceSquared(Vector2 value1, Vector2 value2, out float result)
{
result = (value1.mX - value2.mX) * (value1.mX - value2.mX) +
(value1.mY - value2.mY) * (value1.mY - value2.mY);
}
public static float Distance(Vector2 vector1, Vector2 vector2)
{
float result;
DistanceSquared(vector1, vector2, out result);
return (float)Math.Sqrt(result);
}
public static Vector2 Add(Vector2 vec1, Vector2 vec2)
{
return Vector2(vec1.mX + vec2.mX, vec1.mY + vec2.mY);
}
public static Vector2 Subtract(Vector2 vec1, Vector2 vec2)
{
return Vector2(vec1.mX - vec2.mX, vec1.mY - vec2.mY);
}
public static float Dot(Vector2 vec1, Vector2 vec2)
{
return vec1.mX * vec2.mX + vec1.mY * vec2.mY;
}
public static Vector2 FromAngle(float angle, float length = 1.0f)
{
return Vector2((float)Math.Cos(angle) * length, (float)Math.Sin(angle) * length);
}
public static Vector2 operator +(Vector2 vec1, Vector2 vec2)
{
return Vector2(vec1.mX + vec2.mX, vec1.mY + vec2.mY);
}
public static Vector2 operator -(Vector2 vec1, Vector2 vec2)
{
return Vector2(vec1.mX - vec2.mX, vec1.mY - vec2.mY);
}
public static Vector2 operator *(Vector2 vec1, float factor)
{
return Vector2(vec1.mX * factor, vec1.mY * factor);
}
public static Vector2 operator /(Vector2 vec1, float factor)
{
return Vector2(vec1.mX / factor, vec1.mY / factor);
}
}
}

View file

@ -0,0 +1,247 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Beefy.gfx;
namespace Beefy.geom
{
public struct Vector3 : IHashable, IEquatable<Vector3>
{
[Reflect]
public float mX;
[Reflect]
public float mY;
[Reflect]
public float mZ;
private static Vector3 sZero = Vector3(0f, 0f, 0f);
private static Vector3 sOne = Vector3(1f, 1f, 1f);
private static Vector3 sUnitX = Vector3(1f, 0f, 0f);
private static Vector3 sUnitY = Vector3(0f, 1f, 0f);
private static Vector3 sUnitZ = Vector3(0f, 0f, 1f);
private static Vector3 sUp = Vector3(0f, 1f, 0f);
private static Vector3 sDown = Vector3(0f, -1f, 0f);
private static Vector3 sRight = Vector3(1f, 0f, 0f);
private static Vector3 sLeft = Vector3(-1f, 0f, 0f);
private static Vector3 sForward = Vector3(0f, 0f, -1f);
private static Vector3 sBackward = Vector3(0f, 0f, 1f);
public static Vector3 Zero
{
get { return sZero; }
}
public static Vector3 One
{
get { return sOne; }
}
public static Vector3 UnitX
{
get { return sUnitX; }
}
public static Vector3 UnitY
{
get { return sUnitY; }
}
public static Vector3 UnitZ
{
get { return sUnitZ; }
}
public static Vector3 Up
{
get { return sUp; }
}
public static Vector3 Down
{
get { return sDown; }
}
public static Vector3 Right
{
get { return sRight; }
}
public static Vector3 Left
{
get { return sLeft; }
}
public static Vector3 Forward
{
get { return sForward; }
}
public static Vector3 Backward
{
get { return sBackward; }
}
public float Length
{
get
{
return (float)Math.Sqrt(mX * mX + mY * mY + mZ * mZ);
}
}
public float LengthSquared
{
get
{
return mX * mX + mY * mY + mZ * mZ;
}
}
public this(float x, float y, float z)
{
mX = x;
mY = y;
mZ = z;
}
public bool Equals(Vector3 other)
{
return this == other;
}
public int GetHashCode()
{
return (int)(this.mX + this.mY + this.mZ);
}
/*public static Vector2D Add(Vector2D vec1, Vector2D vec2)
{
return new Vector2D(vec1.mX + vec2.mX, vec1.mY + vec2.mY);
}
public static Vector2D Subtract(Vector2D vec1, Vector2D vec2)
{
return new Vector2D(vec1.mX - vec2.mX, vec1.mY - vec2.mY);
}*/
public static Vector3 Normalize(Vector3 vector)
{
Vector3 newVec;
Normalize(vector, out newVec);
return vector;
}
public static void Normalize(Vector3 value, out Vector3 result)
{
float factor= Distance(value, sZero);
factor = 1f / factor;
result.mX = value.mX * factor;
result.mY = value.mY * factor;
result.mZ = value.mZ * factor;
}
public static float Dot(Vector3 vec1, Vector3 vec2)
{
return vec1.mX * vec2.mX + vec1.mY * vec2.mY + vec1.mZ * vec2.mZ;
}
public static Vector3 Cross(Vector3 vector1, Vector3 vector2)
{
return Vector3(vector1.mY * vector2.mZ - vector2.mY * vector1.mZ,
-(vector1.mX * vector2.mZ - vector2.mX * vector1.mZ),
vector1.mX * vector2.mY - vector2.mX * vector1.mY);
}
public static float DistanceSquared(Vector3 value1, Vector3 value2)
{
return (value1.mX - value2.mX) * (value1.mX - value2.mX) +
(value1.mY - value2.mY) * (value1.mY - value2.mY) +
(value1.mZ - value2.mZ) * (value1.mZ - value2.mZ);
}
public static float Distance(Vector3 vector1, Vector3 vector2)
{
float result = DistanceSquared(vector1, vector2);
return (float)Math.Sqrt(result);
}
/*public static Vector2D FromAngle(float angle, float length = 1.0f)
{
return new Vector2D((float)Math.Cos(angle) * length, (float)Math.Sin(angle) * length);
}*/
public static Vector3 Transform(Vector3 vec, Matrix4 matrix)
{
Vector3 result;
float fInvW = 1.0f / (matrix.m30 * vec.mX + matrix.m31 * vec.mY + matrix.m32 * vec.mZ + matrix.m33);
result.mX = (matrix.m00 * vec.mX + matrix.m01 * vec.mY + matrix.m02 * vec.mZ + matrix.m03) * fInvW;
result.mY = (matrix.m10 * vec.mX + matrix.m11 * vec.mY + matrix.m12 * vec.mZ + matrix.m13) * fInvW;
result.mZ = (matrix.m20 * vec.mX + matrix.m21 * vec.mY + matrix.m22 * vec.mZ + matrix.m23) * fInvW;
return result;
}
/*public static void Transform(Vector3[] sourceArray, ref Matrix4 matrix, Vector3[] destinationArray)
{
//Debug.Assert(destinationArray.Length >= sourceArray.Length, "The destination array is smaller than the source array.");
for (var i = 0; i < sourceArray.Length; i++)
{
var position = sourceArray[i];
destinationArray[i] =
new Vector3(
(position.mX * matrix.m11) + (position.mY * matrix.m21) + (position.mZ * matrix.m31) + matrix.m41,
(position.mX * matrix.m12) + (position.mY * matrix.m22) + (position.mZ * matrix.m32) + matrix.m42,
(position.mX * matrix.m13) + (position.mY * matrix.m23) + (position.mZ * matrix.m33) + matrix.m43);
}
}*/
public static Vector3 Transform(Vector3 vec, Quaternion quat)
{
Matrix4 matrix = quat.ToMatrix();
return Transform(vec, matrix);
}
public static Vector3 TransformNormal(Vector3 normal, Matrix4 matrix)
{
return Vector3((normal.mX * matrix.m11) + (normal.mY * matrix.m21) + (normal.mZ * matrix.m31),
(normal.mX * matrix.m12) + (normal.mY * matrix.m22) + (normal.mZ * matrix.m32),
(normal.mX * matrix.m13) + (normal.mY * matrix.m23) + (normal.mZ * matrix.m33));
}
public static bool operator ==(Vector3 value1, Vector3 value2)
{
return (value1.mX == value2.mX) &&
(value1.mY == value2.mY) &&
(value1.mZ == value2.mZ);
}
public static bool operator !=(Vector3 value1, Vector3 value2)
{
return !(value1 == value2);
}
public static Vector3 operator +(Vector3 vec1, Vector3 vec2)
{
return Vector3(vec1.mX + vec2.mX, vec1.mY + vec2.mY, vec1.mZ + vec2.mZ);
}
public static Vector3 operator -(Vector3 vec1, Vector3 vec2)
{
return Vector3(vec1.mX - vec2.mX, vec1.mY - vec2.mY, vec1.mZ - vec2.mZ);
}
public static Vector3 operator *(Vector3 vec, float scale)
{
return Vector3(vec.mX * scale, vec.mY * scale, vec.mZ * scale);
}
public override void ToString(String str)
{
str.AppendF("{0:0.0#}, {1:0.0#}, {2:0.0#}", mX, mY, mZ);
}
}
}

View file

@ -0,0 +1,158 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Beefy.gfx
{
public struct Color : uint32
{
public const Color White = 0xFFFFFFFF;
public const Color Black = 0xFF000000;
public const Color Red = 0xFFFF0000;
public const Color Green = 0xFF00FF00;
public const Color Blue = 0xFF0000FF;
public const Color Yellow = 0xFFFFFF00;
public static implicit operator uint32(Color color);
public static implicit operator Color(uint32 color);
public this(int32 r, int32 g, int32 b)
{
this = 0xFF000000 | (uint32)((r << 16) | (g << 8) | (b));
}
public this(int32 r, int32 g, int32 b, int32 a)
{
this = (uint32)((a << 24) | (r << 16) | (g << 8) | (b));
}
public static Color Get(float a)
{
return 0x00FFFFFF | (((uint32)(255.0f * a)) << 24);
}
public static Color Get(uint32 rgb, float a)
{
return (uint32)(((uint32)(a * 255) << 24) | (rgb & 0xffffff));
}
public static Color Get(int32 r, int32 g, int32 b)
{
return 0xFF000000 | (uint32)((r << 16) | (g << 8) | (b));
}
public static Color Get(int32 r, int32 g, int32 b, int32 a)
{
return (uint32)((a << 24) | (r << 16) | (g << 8) | (b));
}
public static float GetAlpha(uint32 color)
{
return ((float)(0xff & (color >> 24))) / 255.0f;
}
public static Color Mult(Color color, Color colorMult)
{
if (color == 0xFFFFFFFF)
return colorMult;
if (colorMult == 0xFFFFFFFF)
return color;
uint32 result =
(((((color >> 24) & 0xFF) * ((colorMult >> 24) & 0xFF)) / 255) << 24) |
(((((color >> 16) & 0xFF) * ((colorMult >> 16) & 0xFF)) / 255) << 16) |
(((((color >> 8) & 0xFF) * ((colorMult >> 8) & 0xFF)) / 255) << 8) |
(((color & 0xFF) * (colorMult & 0xFF)) / 255);
return result;
}
public static Color Lerp(Color color1, Color color2, float pct)
{
if (color1 == color2)
return color1;
uint32 a = (uint32)(pct * 256.0f);
uint32 oma = 256 - a;
uint32 aColor =
(((((color1 & 0x000000FF) * oma) + ((color2 & 0x000000FF) * a)) >> 8) & 0x000000FF) |
(((((color1 & 0x0000FF00) * oma) + ((color2 & 0x0000FF00) * a)) >> 8) & 0x0000FF00) |
(((((color1 & 0x00FF0000) * oma) + ((color2 & 0x00FF0000) * a)) >> 8) & 0x00FF0000) |
(((((color1 >> 24) & 0xFF) * oma) + (((color2 >> 24) & 0xFF) * a) & 0x0000FF00) << 16);
return aColor;
}
public static void ToHSV(uint32 color, out float h, out float s, out float v)
{
float r = ((color >> 16) & 0xFF) / 255.0f;
float g = ((color >> 8) & 0xFF) / 255.0f;
float b = ((color >> 0) & 0xFF) / 255.0f;
float min, max, delta;
min = Math.Min(r, Math.Min(g, b));
max = Math.Max(r, Math.Max(g, b));
v = max; // v
delta = max - min;
if (max != 0)
s = delta / max; // s
else
{
// r = g = b = 0 // s = 0, v is undefined
s = 0;
h = -1;
return;
}
if (r == max)
h = (g - b) / delta; // between yellow & magenta
else if (g == max)
h = 2 + (b - r) / delta; // between cyan & yellow
else
h = 4 + (r - g) / delta; // between magenta & cyan
h /= 6; // degrees
if (h < 0)
h += 1.0f;
}
public static Color FromHSV(float h, float s, float v, int32 a)
{
float r, g, b;
if (s == 0.0f)
{
r = g = b = v;
}
else
{
float useH = h * 6.0f;
int32 i = (int32)useH;
float f = useH - i;
if ((i & 1) == 0)
f = 1 - f;
float m = v * (1 - s);
float n = v * (1 - s*f);
switch(i)
{
case 0: fallthrough;
case 6: r = v; g = n; b = m; break;
case 1: r = n; g = v; b = m; break;
case 2: r = m; g = v; b = n; break;
case 3: r = m; g = n; b = v; break;
case 4: r = n; g = m; b = v; break;
case 5: r = v; g = m; b = n; break;
default: r = 0; g = 0; b = 0; break;
}
}
return Get((int32)(r * 255.0f), (int32)(g * 255.0f), (int32)(b * 255.0f), a);
}
public static Color FromHSV(float h, float s, float v)
{
return FromHSV(h, s, v, 0xFF);
}
public static uint32 ToNative(Color color)
{
return (color & 0xFF00FF00) | ((color & 0x00FF0000) >> 16) | ((color & 0x000000FF) << 16);
}
}
}

View file

@ -0,0 +1,103 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Diagnostics;
namespace Beefy.gfx
{
public class ConstantDataDefinition
{
public enum DataType
{
Float,
Vector2,
Vector3,
Vector4,
Matrix,
VertexShaderUsage = 0x100,
PixelShaderUsage = 0x200
}
public int32 mDataSize;
public DataType[] mDataTypes ~ delete _;
public this(int32 dataSize, DataType[] dataTypes)
{
mDataSize = dataSize;
mDataTypes = dataTypes;
}
public this(Type type)
{
ThrowUnimplemented();
/*var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
int memberCount = fields.Length;
mDataTypes = new int[memberCount];
mDataSize = Marshal.SizeOf(type);
List<Type> primitives = new List<Type>();
int fieldIdx = 0;
foreach (var field in fields)
{
var memberAttribute = field.GetCustomAttribute<VertexMemberAttribute>();
primitives.Clear();
VertexDefinition.FindPrimitives(field.FieldType, primitives);
int floats = 0;
int shorts = 0;
int colors = 0;
foreach (var prim in primitives)
{
if (prim == typeof(float))
floats++;
else if (prim == typeof(ushort))
shorts++;
else if (prim == typeof(uint))
colors++;
}
DataType dataType = DataType.Single;
int usageType = 1;
if (floats != 0)
{
Debug.Assert(floats == primitives.Count);
if (floats == 16)
dataType = DataType.Matrix;
else
{
Debug.Assert(floats <= 4);
dataType = DataType.Single + floats - 1;
}
}
else if (shorts != 0)
{
/*if (shorts == 2)
dataType = DataType.Short2;
else if (shorts == 4)
dataType = DataType.Short4;
else*/
Debug.Fail("Invalid short count");
}
else if (colors != 0)
{
/*if (colors == 1)
vertexDefData.mFormat = VertexElementFormat.Color;
else*/
Debug.Fail("Invalid color count");
}
mDataTypes[fieldIdx++] = (int)dataType | (usageType << 8);
} */
}
}
}

View file

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace Beefy.gfx
{
public struct ConstantDataMemberAttribute : Attribute
{
public bool mVertexShaderWants;
public bool mPixelShaderWants;
public this(bool vertexShaderWants, bool pixelShaderWants)
{
mVertexShaderWants = vertexShaderWants;
mPixelShaderWants = pixelShaderWants;
}
}
}

View file

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Beefy.geom;
namespace Beefy.gfx
{
public struct DefaultVertex
{
[VertexMember(VertexElementUsage.Position2D)]
public Vector3 mPos;
[VertexMember(VertexElementUsage.TextureCoordinate)]
public TexCoords mTexCoords;
[VertexMember(VertexElementUsage.Color)]
public uint32 mColor;
public static VertexDefinition sVertexDefinition ~ delete _;
public static void Init()
{
sVertexDefinition = new VertexDefinition(typeof(DefaultVertex));
}
}
}

View file

@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using Beefy.widgets;
using Beefy;
#if STUDIO_CLIENT
using Beefy.ipc;
#endif
namespace Beefy.gfx
{
#if !STUDIO_CLIENT
public class DrawLayer
{
[StdCall, CLink]
static extern void* DrawLayer_Create(void* window);
[StdCall, CLink]
static extern void DrawLayer_Delete(void* drawLayer);
[StdCall, CLink]
static extern void DrawLayer_Clear(void* drawLayer);
[StdCall, CLink]
static extern void DrawLayer_Activate(void* drawLayer);
[StdCall, CLink]
static extern void DrawLayer_DrawToRenderTarget(void* drawLayer, void* texture);
public void* mNativeDrawLayer;
public this(BFWindow window)
{
mNativeDrawLayer = DrawLayer_Create((window != null) ? (window.mNativeWindow) : null);
}
public ~this()
{
DrawLayer_Delete(mNativeDrawLayer);
}
public void Activate()
{
DrawLayer_Activate(mNativeDrawLayer);
}
public void Clear()
{
DrawLayer_Clear(mNativeDrawLayer);
}
public void DrawToRenderTarget(Image texture)
{
DrawLayer_DrawToRenderTarget(mNativeDrawLayer, texture.mNativeTextureSegment);
}
}
#else
public class DrawLayer
{
IPCProxy<IStudioDrawLayer> mDrawLayer;
public DrawLayer(BFWindow window)
{
IPCObjectId drawLayerObjId = BFApp.sApp.mStudioHost.Proxy.CreateDrawLayer((window != null) ? window.mRemoteWindow.ObjId : IPCObjectId.Null);
mDrawLayer = IPCProxy<IStudioDrawLayer>.Create(drawLayerObjId);
}
public void Dispose()
{
}
public void Activate()
{
mDrawLayer.Proxy.Activate();
}
public void Clear()
{
}
public void DrawToRenderTarget(Image texture)
{
}
}
#endif
}

View file

@ -0,0 +1,881 @@
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Beefy.geom;
using Beefy.utils;
using System.Diagnostics;
using System.Threading;
namespace Beefy.gfx
{
public enum FontOverflowMode
{
Overflow,
Clip,
Truncate,
Wrap,
Ellipsis
}
public enum FontAlign
{
Left = -1,
Centered,
Right
}
public struct FontMetrics
{
public int32 mLineCount;
public float mMinX;
public float mMinY;
public float mMaxX;
public float mMaxY;
public float mMaxWidth;
}
public class Font
{
[StdCall, CLink]
static extern FTFont* FTFont_Load(char8* fileName, float pointSize);
[StdCall, CLink]
static extern void FTFont_Delete(FTFont* ftFont, bool cacheRetain);
[StdCall, CLink]
static extern void FTFont_ClearCache();
[StdCall, CLink]
static extern FTGlyph* FTFont_AllocGlyph(FTFont* ftFont, int32 char8Code, bool allowDefault);
[StdCall, CLink]
static extern int32 FTFont_GetKerning(FTFont* font, int32 char8CodeA, int32 char8CodeB);
static Dictionary<String, String> sFontNameMap ~ DeleteDictionyAndKeysAndItems!(_);
static Monitor sMonitor = new .() ~ delete _;
struct FTFont
{
public int32 mHeight;
public int32 mAscent;
public int32 mDescent;
public int32 mMaxAdvance;
}
struct FTGlyph
{
public void* mPage;
public void* mTextureSegment;
public int32 mX;
public int32 mY;
public int32 mWidth;
public int32 mHeight;
public int32 mXOffset;
public int32 mYOffset;
public int32 mXAdvance;
}
public class CharData
{
public Image mImageSegment ~ delete _;
public int32 mX;
public int32 mY;
public int32 mWidth;
public int32 mHeight;
public int32 mXOffset;
public int32 mYOffset;
public int32 mXAdvance;
public bool mIsCombiningMark;
}
public class Page
{
public Image mImage ~ delete _;
public bool mIsCorrectedImage = true;
}
public struct AltFont
{
public Font mFont;
public bool mOwned;
}
class MarkRefData
{
public float mTop;
public float mBottom;
}
enum MarkPosition
{
AboveC, // Center
AboveR, // Left edge of mark aligned on center of char8
AboveE, // Center of mark aligned on right edge of char8
BelowC,
BelowR,
OverC,
OverE,
TopR, // Center of edge aligned to top of char8
}
const int32 LOW_CHAR_COUNT = 128;
Dictionary<char32, CharData> mCharData;
CharData[] mLowCharData;
FTFont* mFTFont;
String mPath;
List<AltFont> mAlternates;
MarkRefData mMarkRefData ~ delete _;
float[] mLoKerningTable;
public this()
{
}
public ~this()
{
Dispose();
}
public static ~this()
{
FTFont_ClearCache();
}
static void BuildFontNameCache()
{
#if BF_PLATFORM_WINDOWS
using (sMonitor.Enter())
{
sFontNameMap = new .();
Windows.HKey hkey;
if (Windows.RegOpenKeyExA(Windows.HKEY_LOCAL_MACHINE, @"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts", 0,
Windows.KEY_QUERY_VALUE | Windows.KEY_WOW64_32KEY | Windows.KEY_ENUMERATE_SUB_KEYS, out hkey) == Windows.S_OK)
{
defer Windows.RegCloseKey(hkey);
for (int32 i = 0; true; i++)
{
char16[256] fontNameArr;
uint32 nameLen = 255;
uint32 valType = 0;
char16[256] data;
uint32 dataLen = 256 * 2;
int32 result = Windows.RegEnumValueW(hkey, i, &fontNameArr, &nameLen, null, &valType, &data, &dataLen);
if (result == 0)
{
if (valType == 1)
{
String fontName = new String(&fontNameArr);
int parenPos = fontName.IndexOf(" (");
if (parenPos != -1)
fontName.RemoveToEnd(parenPos);
fontName.ToUpper();
String fontPath = new String(&data);
if ((!fontPath.EndsWith(".TTF", .OrdinalIgnoreCase)) || (!sFontNameMap.TryAdd(fontName, fontPath)))
{
delete fontName;
delete fontPath;
}
}
}
else
{
if (result == Windows.ERROR_MORE_DATA)
continue;
break;
}
}
}
}
#endif
}
public static void ClearFontNameCache()
{
using (sMonitor.Enter())
{
DeleteDictionyAndKeysAndItems!(sFontNameMap);
sFontNameMap = null;
}
}
public void Dispose(bool cacheRetain)
{
if (mFTFont != null)
{
FTFont_Delete(mFTFont, cacheRetain);
mFTFont = null;
}
if (mLowCharData != null)
{
for (var charData in mLowCharData)
delete charData;
DeleteAndNullify!(mLowCharData);
}
if (mCharData != null)
{
for (var charData in mCharData.Values)
delete charData;
DeleteAndNullify!(mCharData);
}
if (mAlternates != null)
{
for (var altFont in mAlternates)
if (altFont.mOwned)
delete altFont.mFont;
DeleteAndNullify!(mAlternates);
}
DeleteAndNullify!(mPath);
DeleteAndNullify!(mLoKerningTable);
}
public void Dispose()
{
Dispose(false);
}
public static void ClearCache()
{
FTFont_ClearCache();
}
public void AddAlternate(Font altFont)
{
AltFont altFontEntry;
altFontEntry.mFont = altFont;
altFontEntry.mOwned = false;
mAlternates.Add(altFontEntry);
}
public Result<void> AddAlternate(String path, float pointSize = -1)
{
Font altFont = Try!(LoadFromFile(path, pointSize));
AltFont altFontEntry;
altFontEntry.mFont = altFont;
altFontEntry.mOwned = true;
mAlternates.Add(altFontEntry);
return .Ok;
}
public bool GetVal(String outVal, List<String> cmds, String find)
{
for (String cmd in cmds)
{
if (cmd.StartsWith(find) && (cmd[find.Length] == '='))
{
outVal.Append(cmd, find.Length + 1);
return true;
}
}
return false;
}
public int32 GetValInt(List<String> cmds, String find)
{
String strVal = scope String();
if (!GetVal(strVal, cmds, find))
return 0;
return int32.Parse(strVal);
}
public void CalcKerning()
{
for (char8 c0 = ' '; c0 < '\x80'; c0++)
{
for (char8 c1 = ' '; c1 < '\x80'; c1++)
{
float kernVal = FTFont_GetKerning(mFTFont, (int32)c0, (int32)c1);
if (kernVal != 0)
{
if (mLoKerningTable == null)
mLoKerningTable = new float[128*128];
mLoKerningTable[(int32)c0 + ((int32)c1)*128] = kernVal;
}
}
}
}
public float GetKerning(char32 c0, char32 c1)
{
if (mLoKerningTable == null)
return 0;
if ((c0 < '\x80') && (c1 < '\x80'))
{
return mLoKerningTable[(int32)c0 + ((int32)c1)*128];
}
return FTFont_GetKerning(mFTFont, (int32)c0, (int32)c1);
}
void GetFontPath(StringView fontName, String path)
{
if (fontName.Contains('.'))
{
Path.GetAbsolutePath(fontName, BFApp.sApp.mInstallDir, path);
}
else
{
using (sMonitor.Enter())
{
if (sFontNameMap == null)
BuildFontNameCache();
#if BF_PLATFORM_WINDOWS
let lookupStr = scope String(fontName)..ToUpper();
String pathStr;
if (sFontNameMap.TryGetValue(lookupStr, out pathStr))
{
char8[256] windowsDir;
Windows.GetWindowsDirectoryA(&windowsDir, 256);
path.Append(&windowsDir);
path.Append(@"\Fonts\");
path.Append(pathStr);
}
#endif
}
}
}
internal bool Load(StringView fontName, float pointSize = -1)
{
Dispose();
mCharData = new Dictionary<char32, CharData>();
mLowCharData = new CharData[LOW_CHAR_COUNT];
mAlternates = new List<AltFont>();
float usePointSize = pointSize;
mPath = new String();
GetFontPath(fontName, mPath);
String fontPath = scope String(mPath);
if (pointSize == -1)
{
fontPath.Set(BFApp.sApp.mInstallDir);
fontPath.Append("fonts/SourceCodePro-Regular.ttf");
usePointSize = 9;
}
mFTFont = FTFont_Load(fontPath, usePointSize);
if (mFTFont == null)
return false;
CalcKerning();
return true;
}
public static Result<Font> LoadFromFile(String path, float pointSize = -1)
{
scope AutoBeefPerf("Font.LoadFromFile");
Font font = new Font();
if (!font.Load(path, pointSize))
return .Err; //TODO: Make proper error
return font;
}
public bool HasChar(char32 theChar)
{
return true;
/*if ((theChar >= (char8)0) && (theChar < (char8)LOW_CHAR_COUNT))
return mLowCharData[(int)theChar] != null;
return mCharData.ContainsKey(theChar);*/
}
public float GetWidth(StringView theString)
{
float curX = 0;
int len = theString.Length;
if (len == 0)
{
return 0;
}
char32 prevChar = (char8)0;
for (var c in theString.DecodedChars)
{
int idx = @c.NextIndex;
if (c == (char32)'\x01')
{
@c.NextIndex = idx + 4;
continue;
}
else if (c == (char32)'\x02')
{
continue;
}
CharData charData = GetCharData((char32)c);
if ((charData != null) && (!charData.mIsCombiningMark))
{
curX += charData.mXAdvance;
if (prevChar != (char8)0)
{
float kernAmount = GetKerning(prevChar, c);
curX += kernAmount;
}
}
prevChar = c;
if (idx >= len)
break;
}
return curX;
}
static MarkPosition[] sMarkPositionsLow = new MarkPosition[0x70]
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
/*0*/{.AboveC, .AboveC, .AboveC, .AboveC, /**/ .AboveC, .AboveC, .AboveC, .AboveC, /**/ .AboveC, .AboveC, .AboveC, .AboveC, /**/ .AboveC, .AboveC, .AboveC, .AboveC,
/*1*/ .AboveC, .AboveC, .AboveC, .AboveC, /**/ .AboveC, .AboveE, .AboveC, .AboveC, /**/ .BelowC, .BelowC, .AboveE, .TopR, /**/ .BelowC, .BelowC, .BelowC, .BelowC,
/*2*/ .BelowC, .BelowC, .BelowC, .BelowC, /**/ .BelowC, .BelowC, .BelowC, .BelowC, /**/ .BelowC, .BelowC, .BelowC, .BelowC, /**/ .BelowC, .BelowC, .BelowC, .BelowC,
/*3*/ .BelowC, .BelowC, .BelowC, .BelowC, /**/ .OverC , .OverC , .OverC , .OverC , /**/ .OverC , .BelowC, .BelowC, .BelowC, /**/ .AboveC, .AboveC, .AboveC, .AboveC,
/*4*/ .AboveC, .AboveC, .AboveC, .AboveC, /**/ .AboveC, .BelowC, .AboveC, .BelowC, /**/ .BelowC, .BelowC, .AboveC, .AboveC, /**/ .AboveC, .BelowC, .BelowC, .OverC,
/*5*/ .AboveC, .AboveC, .AboveC, .BelowC, /**/ .BelowC, .BelowC, .BelowC, .AboveC, /**/ .AboveE, .BelowC, .AboveC, .AboveC, /**/ .BelowR, .AboveR, .AboveR, .BelowR,
/*6*/ .AboveR, .AboveR, .BelowR, .AboveC, /**/ .AboveC, .AboveC, .AboveC, .AboveC, /**/ .AboveC, .AboveC, .AboveC, .AboveC, /**/ .AboveC, .AboveC, .AboveC, .AboveC,
} ~ delete _;
MarkPosition GetMarkPosition(char32 checkChar)
{
if ((checkChar >= '\u{0300}') && (checkChar <= '\u{036F}'))
{
return sMarkPositionsLow[(int)(checkChar - '\u{0300}')];
}
return .OverC;
}
CharData GetCharData(char32 checkChar)
{
CharData charData;
if ((checkChar >= (char8)0) && (checkChar < (char32)LOW_CHAR_COUNT))
charData = mLowCharData[(int)checkChar];
else
mCharData.TryGetValue(checkChar, out charData);
if (charData == null)
{
for (int fontIdx = -1; fontIdx < mAlternates.Count; fontIdx++)
{
FTFont* ftFont;
if (fontIdx == -1)
ftFont = mFTFont;
else
ftFont = mAlternates[fontIdx].mFont.mFTFont;
if (ftFont == null)
continue;
var ftGlyph = FTFont_AllocGlyph(ftFont, (int32)checkChar, fontIdx == mAlternates.Count - 1);
if (ftGlyph == null)
continue;
charData = new CharData();
charData.mX = ftGlyph.mX;
charData.mY = ftGlyph.mY;
charData.mWidth = ftGlyph.mWidth;
charData.mHeight = ftGlyph.mHeight;
charData.mXAdvance = ftGlyph.mXAdvance;
charData.mXOffset = ftGlyph.mXOffset;
charData.mYOffset = ftGlyph.mYOffset;
charData.mImageSegment = new Image();
charData.mImageSegment.mNativeTextureSegment = ftGlyph.mTextureSegment;
charData.mImageSegment.mX = ftGlyph.mX;
charData.mImageSegment.mY = ftGlyph.mY;
charData.mImageSegment.mWidth = ftGlyph.mWidth;
charData.mImageSegment.mHeight = ftGlyph.mHeight;
charData.mImageSegment.mSrcWidth = ftGlyph.mWidth;
charData.mImageSegment.mSrcHeight = ftGlyph.mHeight;
charData.mIsCombiningMark = ((checkChar >= '\u{0300}') && (checkChar <= '\u{036F}')) || ((checkChar >= '\u{1DC0}') && (checkChar <= '\u{1DFF}'));
if (charData.mIsCombiningMark)
charData.mXAdvance = 0;
if ((checkChar >= (char32)0) && (checkChar < (char32)LOW_CHAR_COUNT))
mLowCharData[(int)checkChar] = charData;
else
mCharData[checkChar] = charData;
return charData;
}
if (checkChar == (char32)'?')
return null;
return GetCharData((char32)'?');
}
return charData;
}
MarkRefData GetMarkRefData()
{
if (mMarkRefData == null)
{
mMarkRefData = new MarkRefData();
var charData = GetCharData('o');
mMarkRefData.mTop = charData.mYOffset;
mMarkRefData.mBottom = charData.mYOffset + charData.mHeight;
}
return mMarkRefData;
}
public float GetWidth(char32 theChar)
{
CharData charData = GetCharData(theChar);
return charData.mXAdvance;
}
public int GetCharCountToLength(StringView theString, float maxLength, bool* hitTabStop = null)
{
float curX = 0;
int startIdx = 0;
char32 prevChar = (char8)0;
for (var c in theString.DecodedChars)
{
int idx = @c.NextIndex;
if (c == (char32)'\x01')
{
@c.NextIndex = idx + 4;
//startIdx = idx;
continue;
}
else if (c == (char32)'\x02')
{
//startIdx = idx;
continue;
}
else if (c == (char32)'\t')
{
if (hitTabStop != null)
{
*hitTabStop = true;
return startIdx;
}
}
CharData charData = GetCharData(c);
curX += charData.mXAdvance;
if (prevChar != (char32)0)
{
float kernAmount = GetKerning(prevChar, c);
curX += kernAmount;
}
if (curX > maxLength)
return startIdx;
prevChar = c;
startIdx = idx;
}
return (int32)theString.Length;
}
public float GetLineSpacing()
{
if (mFTFont == null)
return 0;
return mFTFont.mHeight;
}
public float GetHeight()
{
if (mFTFont == null)
return 0;
return mFTFont.mHeight;
}
public float GetAscent()
{
if (mFTFont == null)
return 0;
return mFTFont.mAscent;
}
public float GetDescent()
{
if (mFTFont == null)
return 0;
return mFTFont.mDescent;
}
public float GetWrapHeight(StringView theString, float width)
{
return Draw(null, theString, 0, 0, -1, width, FontOverflowMode.Wrap);
}
public static void StrEncodeColor(uint32 color, String outString)
{
uint32 colorVal = (color >> 1) & 0x7F7F7F7F;
outString.Append('\x01');
outString.Append((char8*)&colorVal, 4);
}
public static void StrEncodePopColor(String outString)
{
outString.Append('\x02');
}
public static char8[5] EncodeColor(uint32 color)
{
char8[5] val;
val[0] = '\x01';
*((uint32*)&val[1]) = (color >> 1) & 0x7F7F7F7F;
return val;
}
public static char8 EncodePopColor()
{
return '\x02';
}
public void Draw(Graphics g, StringView theString, FontMetrics* fontMetrics = null)
{
if (mFTFont == null)
return;
float curX = 0;
float curY = 0;
Matrix newMatrix = Matrix();
bool hasClipRect = g.mClipRect.HasValue;
Rect clipRect = g.mClipRect.GetValueOrDefault();
uint32 color = g.mColor;
g.PushTextRenderState();
#unwarn
float markScale = mFTFont.mHeight / 8.0f;
float markTopOfs = 0;
float markBotOfs = 0;
CharData lastCharData = null;
float lastCharX = 0;
char32 prevChar = (char32)0;
for (var c in theString.DecodedChars)
{
int idx = @c.NextIndex;
if (c == (char32)'\x01') // Set new color
{
if (idx <= theString.Length - 4)
{
uint32 newColor = *(uint32*)(theString.Ptr + idx) << 1;
color = Color.Mult(newColor, g.mColor);
@c.NextIndex = idx + 4;
continue;
}
}
else if (c == (char32)'\x02') // Restore color
{
color = g.mColor;
continue;
}
CharData charData = GetCharData(c);
float drawX = curX + charData.mXOffset;
float drawY = curY + charData.mYOffset;
if ((charData.mIsCombiningMark) && (lastCharData != null))
{
var markRefData = GetMarkRefData();
var markPos = GetMarkPosition(c);
if (markPos == .TopR)
{
drawX = lastCharX + lastCharData.mXOffset + lastCharData.mWidth - charData.mWidth / 2;
drawY = curY + lastCharData.mYOffset - charData.mHeight / 2;
}
else if ((markPos == .AboveE) || (markPos == .OverE))
{
drawX = lastCharX + lastCharData.mXOffset + lastCharData.mWidth - charData.mWidth / 2;
}
else
{
drawX = lastCharX + lastCharData.mXOffset + (lastCharData.mWidth / 2);
if ((markPos == .AboveC) || (markPos == .BelowC) || (markPos == .OverC))
drawX -= charData.mWidth / 2;
}
if ((markPos == .AboveC) || (markPos == .AboveR))
{
drawY += lastCharData.mYOffset - markRefData.mTop - markTopOfs;
markTopOfs += charData.mHeight;
}
else if ((markPos == .BelowC) || (markPos == .BelowR))
{
drawY += (lastCharData.mYOffset + lastCharData.mHeight) - markRefData.mBottom + markBotOfs;
markBotOfs += charData.mHeight;
}
}
else
{
lastCharX = curX;
markTopOfs = 0;
markBotOfs = 0;
if (prevChar != (char8)0)
{
float kernAmount = GetKerning(prevChar, c);
curX += kernAmount;
drawX += kernAmount;
}
lastCharData = charData;
}
newMatrix.SetMultiplied(drawX, drawY, ref g.mMatrix);
bool isFullyClipped = false;
if (hasClipRect)
{
if (newMatrix.tx < clipRect.mX -charData.mImageSegment.mWidth)
{
isFullyClipped = true;
}
else if (newMatrix.tx > clipRect.mX + clipRect.mWidth)
{
isFullyClipped = true;
if ((newMatrix.a > 0) && (fontMetrics == null)) // Forward? If so, all future char8s will clip
break;
}
}
if (!isFullyClipped)
charData.mImageSegment.Draw(newMatrix, g.ZDepth, color);
curX += charData.mXAdvance;
prevChar = c;
}
g.PopRenderState();
}
public float Draw(Graphics g, StringView theString, float x, float y, int32 justification = -1, float width = 0, FontOverflowMode stringEndMode = FontOverflowMode.Overflow, FontMetrics* fontMetrics = null)
{
float drawHeight = 0;
float useX = x;
float useY = y;
StringView workingStr = theString;
String tempStr = null;
void PopulateTempStr()
{
if (tempStr.Length != workingStr.Length)
{
tempStr.Clear();
tempStr.Append(workingStr);
}
}
while (true)
{
int32 crPos = (int32)workingStr.IndexOf('\n');
if (crPos == -1)
break;
float sectionHeight = Draw(g, StringView(workingStr, 0, crPos), useX, useY, justification, width, stringEndMode, fontMetrics);
drawHeight += sectionHeight;
useY += sectionHeight;
workingStr = .(workingStr, crPos + 1, workingStr.Length - (crPos + 1));
}
if ((justification != -1) || (stringEndMode != FontOverflowMode.Overflow))
{
float aWidth;
while (true)
{
aWidth = GetWidth(workingStr);
if (aWidth <= width)
break;
if (stringEndMode == FontOverflowMode.Ellipsis)
{
float ellipsisLen = GetWidth("...");
if (width < ellipsisLen)
return 0; // Don't draw at all if we don't even have enough space for the ellipsis
int strLen = GetCharCountToLength(workingStr, width - ellipsisLen);
tempStr = scope:: String(Math.Min(strLen, 128));
tempStr.Append(theString, 0, strLen);
tempStr.Append("...");
workingStr = tempStr;
aWidth = GetWidth(workingStr);
break;
}
else if (stringEndMode == FontOverflowMode.Truncate)
{
int strLen = GetCharCountToLength(workingStr, width);
tempStr = scope:: String(Math.Min(strLen, 128));
tempStr.Append(theString, 0, strLen);
workingStr = tempStr;
aWidth = GetWidth(workingStr);
break;
}
else if (stringEndMode == FontOverflowMode.Wrap)
{
int32 maxChars = (int32)GetCharCountToLength(workingStr, width);
int32 checkIdx = maxChars;
if (checkIdx < workingStr.Length)
{
while ((checkIdx > 0) && (!workingStr[checkIdx].IsWhiteSpace))
checkIdx--;
}
if (checkIdx == 0)
{
// Can't break on whitespace
checkIdx = maxChars;
}
if (checkIdx > 0)
{
if (fontMetrics != null)
fontMetrics.mLineCount++;
drawHeight += Draw(g, StringView(workingStr, 0, checkIdx), useX, useY, justification, width, stringEndMode, fontMetrics);
useY += GetLineSpacing();
workingStr.Adjust(checkIdx);
}
}
else
break;
}
if (fontMetrics != null)
fontMetrics.mMaxWidth = Math.Max(fontMetrics.mMaxWidth, aWidth);
if (justification == 0)
useX += (width - aWidth) / 2;
else if (justification == 1)
useX += width - aWidth;
}
if (g != null)
{
using (g.PushTranslate(useX, useY))
Draw(g, workingStr, fontMetrics);
}
else
{
if (fontMetrics != null)
fontMetrics.mMaxWidth = Math.Max(fontMetrics.mMaxWidth, GetWidth(workingStr));
}
drawHeight += GetLineSpacing();
if (fontMetrics != null)
fontMetrics.mLineCount++;
return drawHeight;
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Beefy.gfx
{
public interface IDrawable
{
void Draw(Matrix matrix, float z, uint32 color);
}
}

View file

@ -0,0 +1,283 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using Beefy.utils;
using System.Diagnostics;
#if STUDIO_CLIENT
using Beefy.ipc;
#endif
namespace Beefy.gfx
{
#if !STUDIO_CLIENT
public class Image : IDrawable
{
public Image mSrcTexture;
public int32 mSrcX;
public int32 mSrcY;
public int32 mSrcWidth;
public int32 mSrcHeight;
public float mWidth;
public float mHeight;
public float mX;
public float mY;
public void* mNativeTextureSegment;
public PixelSnapping mPixelSnapping = PixelSnapping.Auto;
[StdCall, CLink]
public static extern void Gfx_DrawTextureSegment(void* textureSegment, float a, float b, float c, float d, float tx, float ty, float z, uint32 color, int32 pixelSnapping);
[StdCall, CLink]
static extern void* Gfx_LoadTexture(char8* fileName, int32 additive);
[StdCall, CLink]
static extern void* Gfx_CreateDynTexture(int32 width, int32 height);
[StdCall, CLink]
static extern void* Gfx_CreateRenderTarget(int32 width, int32 height, int32 destAlpha);
[StdCall, CLink]
static extern void* Gfx_ModifyTextureSegment(void* destSegment, void* srcTextureSegment, int32 srcX, int32 srcY, int32 srcWidth, int32 srcHeight);
[StdCall, CLink]
static extern void* Gfx_CreateTextureSegment(void* textureSegment, int32 srcX, int32 srcY, int32 srcWidth, int32 srcHeight);
[StdCall, CLink]
public static extern void Gfx_SetDrawSize(void* textureSegment, int32 width, int32 height);
[StdCall, CLink]
static extern void Gfx_Texture_SetBits(void* textureSegment, int32 destX, int32 destY, int32 destWidth, int32 destHeight, int32 srcPitch, uint32* bits);
[StdCall, CLink]
static extern void Gfx_Texture_Delete(void* textureSegment);
[StdCall, CLink]
static extern int32 Gfx_Texture_GetWidth(void* textureSegment);
[StdCall, CLink]
static extern int32 Gfx_Texture_GetHeight(void* textureSegment);
internal this()
{
}
public ~this()
{
Gfx_Texture_Delete(mNativeTextureSegment);
}
public void Draw(Matrix matrix, float z, uint32 color)
{
Image.Gfx_DrawTextureSegment(mNativeTextureSegment, matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty, z,
color, (int32)mPixelSnapping);
}
public static Image CreateRenderTarget(int32 width, int32 height, bool destAlpha = false)
{
void* aNativeTextureSegment = Gfx_CreateRenderTarget(width, height, destAlpha ? 1 : 0);
if (aNativeTextureSegment == null)
return null;
return CreateFromNativeTextureSegment(aNativeTextureSegment);
}
public static Image LoadFromFile(String fileName, bool additive = false)
{
scope AutoBeefPerf("Image.LoadFromFile");
void* aNativeTextureSegment = Gfx_LoadTexture(fileName, additive ? 1 : 0);
if (aNativeTextureSegment == null)
return null;
return CreateFromNativeTextureSegment(aNativeTextureSegment);
}
public static Image CreateDynamic(int width, int height)
{
void* nativeTextureSegment = Gfx_CreateDynTexture((.)width, (.)height);
if (nativeTextureSegment == null)
return null;
return CreateFromNativeTextureSegment(nativeTextureSegment);
}
public static Image CreateFromNativeTextureSegment(void* nativeTextureSegment)
{
Image texture = new Image();
texture.mNativeTextureSegment = nativeTextureSegment;
texture.mSrcWidth = Gfx_Texture_GetWidth(nativeTextureSegment);
texture.mSrcHeight = Gfx_Texture_GetHeight(nativeTextureSegment);
texture.mWidth = texture.mSrcWidth;
texture.mHeight = texture.mSrcHeight;
return texture;
}
public Image CreateImageSegment(int srcX, int srcY, int srcWidth, int srcHeight)
{
Image textureSegment = new Image();
textureSegment.mSrcTexture = this;
textureSegment.mSrcX = (int32)srcX;
textureSegment.mSrcY = (int32)srcY;
textureSegment.mSrcWidth = (int32)srcWidth;
textureSegment.mSrcHeight = (int32)srcHeight;
textureSegment.mWidth = Math.Abs(srcWidth);
textureSegment.mHeight = Math.Abs(srcHeight);
textureSegment.mNativeTextureSegment = Gfx_CreateTextureSegment(mNativeTextureSegment, (int32)srcX, (int32)srcY, (int32)srcWidth, (int32)srcHeight);
return textureSegment;
}
public void CreateImageSegment(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight)
{
if (mNativeTextureSegment != null)
{
Gfx_Texture_Delete(mNativeTextureSegment);
}
mSrcTexture = srcImage;
mSrcX = (int32)srcX;
mSrcY = (int32)srcY;
mSrcWidth = (int32)srcWidth;
mSrcHeight = (int32)srcHeight;
mWidth = Math.Abs(srcWidth);
mHeight = Math.Abs(srcHeight);
mNativeTextureSegment = Gfx_CreateTextureSegment(srcImage.mNativeTextureSegment, (int32)srcX, (int32)srcY, (int32)srcWidth, (int32)srcHeight);
}
public void SetDrawSize(int width, int height)
{
mWidth = width;
mHeight = height;
Gfx_SetDrawSize(mNativeTextureSegment, (int32)width, (int32)height);
}
public void Scale(float scale)
{
mWidth = (int32)(mSrcWidth * scale);
mHeight = (int32)(mSrcHeight * scale);
Gfx_SetDrawSize(mNativeTextureSegment, (int32)mWidth, (int32)mHeight);
}
public void Modify(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight)
{
mSrcTexture = srcImage;
mSrcX = (int32)srcX;
mSrcY = (int32)srcY;
mSrcWidth = (int32)srcWidth;
mSrcHeight = (int32)srcHeight;
mWidth = Math.Abs(srcWidth);
mHeight = Math.Abs(srcHeight);
Gfx_ModifyTextureSegment(mNativeTextureSegment, srcImage.mNativeTextureSegment, (int32)srcX, (int32)srcY, (int32)srcWidth, (int32)srcHeight);
}
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);
}
public void CreateImageCels(Image[,] celImages)
{
int32 rows = (int32)celImages.GetLength(0);
int32 cols = (int32)celImages.GetLength(1);
int32 celW = mSrcWidth / cols;
int32 celH = mSrcHeight / rows;
Debug.Assert(celW * cols == mSrcWidth);
Debug.Assert(celH * rows == mSrcHeight);
for (int32 row = 0; row < rows; row++)
{
for (int32 col = 0; col < cols; col++)
{
celImages[row, col] = CreateImageSegment(col * celW, row * celH, celW, celH);
}
}
}
}
#else
public class Image : IDrawable
{
public Image mSrcTexture;
public int mSrcX;
public int mSrcY;
public int mSrcWidth;
public int mSrcHeight;
public float mWidth;
public float mHeight;
public float mX;
public float mY;
public IPCProxy<IStudioImage> mStudioImage;
public Image[] mCelImages;
PixelSnapping mPixelSnapping = PixelSnapping.Auto;
public void Draw(Matrix matrix, float z, uint color, int cel)
{
//Image.Gfx_DrawTextureSegment(mNativeTextureSegment, matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty,
//color, mAdditive ? 1 : 0, (int)mPixelSnapping);
mStudioImage.Proxy.DrawTextureSegment(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty, z,
color, (int)mPixelSnapping);
}
public static Image LoadFromFile(string fileName, bool additive = false)
{
//void* aNativeTextureSegment = Gfx_LoadTexture(fileName);
IPCObjectId aNativeTextureSegmentId = BFApp.sApp.mStudioHost.Proxy.LoadImage(fileName, additive);
if (aNativeTextureSegmentId.IsNull())
return null;
return CreateFromNativeTextureSegment(aNativeTextureSegmentId);
}
public static Image CreateFromNativeTextureSegment(IPCObjectId nativeTextureSegmentId)
{
Image texture = new Image();
texture.mStudioImage = IPCProxy<IStudioImage>.Create(nativeTextureSegmentId);
texture.mSrcWidth = texture.mStudioImage.Proxy.GetSrcWidth();
texture.mSrcHeight = texture.mStudioImage.Proxy.GetSrcHeight();
texture.mWidth = texture.mSrcWidth;
texture.mHeight = texture.mSrcHeight;
return texture;
}
internal Image()
{
}
public Image CreateImageSegment(int srcX, int srcY, int srcWidth, int srcHeight)
{
Image aTextureSegment = new Image();
aTextureSegment.mStudioImage = IPCProxy<IStudioImage>.Create(mStudioImage.Proxy.CreateImageSegment(srcX, srcY, srcWidth, srcHeight));
aTextureSegment.mSrcTexture = this;
aTextureSegment.mSrcX = srcX;
aTextureSegment.mSrcY = srcY;
aTextureSegment.mSrcWidth = srcWidth;
aTextureSegment.mSrcHeight = srcHeight;
aTextureSegment.mWidth = Math.Abs(srcWidth);
aTextureSegment.mHeight = Math.Abs(srcHeight);
return aTextureSegment;
}
public void CreateImageCels(int cols, int rows)
{
int celW = mSrcWidth / cols;
int celH = mSrcHeight / rows;
Debug.Assert(celW * cols == mSrcWidth);
Debug.Assert(celH * rows == mSrcHeight);
mCelImages = new Image[cols * rows];
for (int row = 0; row < rows; row++)
{
for (int col = 0; col < cols; col++)
{
mCelImages[col + row * cols] = CreateImageSegment(col * celW, row * celH, celW, celH);
}
}
}
}
#endif
}

View file

@ -0,0 +1,205 @@
using System;
using System.Collections.Generic;
using System.Text;
using Beefy.geom;
namespace Beefy.gfx
{
public struct Matrix
{
public float a;
public float b;
public float c;
public float d;
public float tx;
public float ty;
public this()
{
a = 1;
b = 0;
c = 0;
d = 1;
tx = 0;
ty = 0;
}
public this(float _a, float _b, float _c, float _d, float _tx, float _ty)
{
a = _a;
b = _b;
c = _c;
d = _d;
tx = _tx;
ty = _ty;
}
public static Matrix IdentityMatrix = Matrix(1, 0, 0, 1, 0, 0);
public Matrix Duplicate()
{
return Matrix(a, b, c, d, tx, ty);
}
public void Identity() mut
{
tx = 0;
ty = 0;
a = 1;
b = 0;
c = 0;
d = 1;
}
public void Translate(float x, float y) mut
{
tx += x;
ty += y;
}
public void Scale(float scaleX, float scaleY) mut
{
a *= scaleX;
b *= scaleY;
c *= scaleX;
d *= scaleY;
tx *= scaleX;
ty *= scaleY;
}
public void Rotate(float angle) mut
{
float _a = a;
float _b = b;
float _c = c;
float _d = d;
float _tx = tx;
float _ty = ty;
float sin = (float)Math.Sin(angle);
float cos = (float)Math.Cos(angle);
a = _a * cos - _b * sin;
b = _a * sin + _b * cos;
c = _c * cos - _d * sin;
d = _c * sin + _d * cos;
tx = _tx * cos - _ty * sin;
ty = _tx * sin + _ty * cos;
}
public void Multiply(Matrix mat2) mut
{
float _a = a;
float _b = b;
float _c = c;
float _d = d;
float _tx = tx;
float _ty = ty;
a = _a * mat2.a + _b * mat2.c;
b = _a * mat2.b + _b * mat2.d;
c = _c * mat2.a + _d * mat2.c;
d = _c * mat2.b + _d * mat2.d;
tx = _tx * mat2.a + _ty * mat2.c + mat2.tx;
ty = _tx * mat2.b + _ty * mat2.d + mat2.ty;
}
public void Set(Matrix mat2) mut
{
a = mat2.a;
b = mat2.b;
c = mat2.c;
d = mat2.d;
tx = mat2.tx;
ty = mat2.ty;
}
public void SetMultiplied(float x, float y, ref Matrix mat2) mut
{
a = mat2.a;
b = mat2.b;
c = mat2.c;
d = mat2.d;
tx = x * mat2.a + y * mat2.c + mat2.tx;
ty = x * mat2.b + y * mat2.d + mat2.ty;
}
public void SetMultiplied(float x, float y, float width, float height, ref Matrix mat2) mut
{
a = mat2.a * width;
b = mat2.b * width;
c = mat2.c * height;
d = mat2.d * height;
tx = x * mat2.a + y * mat2.c + mat2.tx;
ty = x * mat2.b + y * mat2.d + mat2.ty;
}
public void SetMultiplied(Matrix mat1, Matrix mat2) mut
{
float a1 = mat1.a;
float b1 = mat1.b;
float c1 = mat1.c;
float d1 = mat1.d;
float tx1 = mat1.tx;
float ty1 = mat1.ty;
float a2 = mat2.a;
float b2 = mat2.b;
float c2 = mat2.c;
float d2 = mat2.d;
float tx2 = mat2.tx;
float ty2 = mat2.ty;
a = a1 * a2 + b1 * c2;
b = a1 * b2 + b1 * d2;
c = c1 * a2 + d1 * c2;
d = c1 * b2 + d1 * d2;
tx = tx1 * a2 + ty1 * c2 + tx2;
ty = tx1 * b2 + ty1 * d2 + ty2;
}
public Point Multiply(Point point)
{
return Point(tx + a * point.x + c * point.y, ty + b * point.x + d * point.y);
}
public void Invert() mut
{
float _a = a;
float _b = b;
float _c = c;
float _d = d;
float _tx = tx;
float _ty = ty;
float den = a * d - b * c;
a = _d / den;
b = -_b / den;
c = -_c / den;
d = _a / den;
tx = (_c * _ty - _d * _tx) / den;
ty = -(_a * _ty - _b * _tx) / den;
}
public static Matrix Lerp(Matrix mat1, Matrix mat2, float pct)
{
float omp = 1.0f - pct;
Matrix matrix = Matrix();
matrix.a = (mat1.a * omp) + (mat2.a * pct);
matrix.b = (mat1.b * omp) + (mat2.b * pct);
matrix.c = (mat1.c * omp) + (mat2.c * pct);
matrix.d = (mat1.d * omp) + (mat2.d * pct);
matrix.tx = (mat1.tx * omp) + (mat2.tx * pct);
matrix.ty = (mat1.ty * omp) + (mat2.ty * pct);
return matrix;
}
}
}

View file

@ -0,0 +1,391 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Beefy;
using Beefy.utils;
using Beefy.geom;
#if STUDIO_CLIENT
using Beefy.ipc;
#endif
namespace Beefy.gfx
{
public class ModelDef
{
public struct JointTranslation
{
public Quaternion mQuat;
public Vector3 mScale;
public Vector3 mTrans;
}
//[StructLayout(LayoutKind.Sequential)]
public struct VertexDef
{
[VertexMember(VertexElementUsage.Position3D)]
public Vector3 mPosition;
[VertexMember(VertexElementUsage.Color)]
public uint32 mColor;
[VertexMember(VertexElementUsage.TextureCoordinate)]
public TexCoords mTexCoords;
[VertexMember(VertexElementUsage.Normal)]
public Vector3 mNormal;
[VertexMember(VertexElementUsage.TextureCoordinate, 1)]
public TexCoords mBumpTexCoords;
[VertexMember(VertexElementUsage.Tangent)]
public Vector3 mTangent;
public static VertexDefinition sVertexDefinition ~ delete _;
public static void Init()
{
sVertexDefinition = new VertexDefinition(typeof(VertexDef));
}
}
}
#if !STUDIO_CLIENT
extension ModelDef
{
public class Animation
{
public void* mNativeModelDefAnimation;
public int32 mFrameCount;
public String mName;
public int32 mAnimIdx;
[StdCall, CLink]
extern static void ModelDefAnimation_GetJointTranslation(void* nativeAnimation, int32 jointIdx, float frame, out JointTranslation jointTranslation);
[StdCall, CLink]
extern static int32 ModelDefAnimation_GetFrameCount(void* nativeAnimation);
[StdCall, CLink]
extern static char8* ModelDefAnimation_GetName(void* nativeAnimation);
[StdCall, CLink]
extern static void ModelDefAnimation_Clip(void* nativeAnimation, int32 startFrame, int32 numFrames);
internal this(void* nativeModelDefAnimation)
{
mNativeModelDefAnimation = nativeModelDefAnimation;
mFrameCount = ModelDefAnimation_GetFrameCount(mNativeModelDefAnimation);
mName = new String(ModelDefAnimation_GetName(mNativeModelDefAnimation));
}
public void GetJointTranslation(int32 jointIdx, float frame, out JointTranslation jointTranslation)
{
ModelDefAnimation_GetJointTranslation(mNativeModelDefAnimation, jointIdx, frame, out jointTranslation);
}
public void Clip(int32 startFrame, int32 numFrames)
{
ModelDefAnimation_Clip(mNativeModelDefAnimation, startFrame, numFrames);
mFrameCount = ModelDefAnimation_GetFrameCount(mNativeModelDefAnimation);
}
}
public void* mNativeModelDef;
public float mFrameRate;
public int32 mJointCount;
public Animation[] mAnims;
public Dictionary<String, Animation> mAnimMap = new Dictionary<String, Animation>();
[StdCall, CLink]
extern static void* Res_OpenFBX(String fileName, void* nativeVertexDef);
[StdCall, CLink]
extern static void* ModelDef_CreateModelInstance(void* nativeModel);
[StdCall, CLink]
extern static float ModelDef_GetFrameRate(void* nativeModel);
[StdCall, CLink]
extern static int32 ModelDef_GetJointCount(void* nativeModel);
[StdCall, CLink]
extern static int32 ModelDef_GetAnimCount(void* nativeModel);
[StdCall, CLink]
extern static void* ModelDef_GetAnimation(void* nativeModel, int32 animIdx);
this(void* nativeModelDef)
{
mNativeModelDef = nativeModelDef;
mFrameRate = ModelDef_GetFrameRate(mNativeModelDef);
mJointCount = ModelDef_GetJointCount(mNativeModelDef);
int32 animCount = ModelDef_GetAnimCount(mNativeModelDef);
mAnims = new Animation[animCount];
for (int32 animIdx = 0; animIdx < animCount; animIdx++)
{
var anim = new Animation(ModelDef_GetAnimation(mNativeModelDef, animIdx));
anim.mAnimIdx = animIdx;
mAnims[animIdx] = anim;
mAnimMap[anim.mName] = anim;
}
}
public static ModelDef LoadModel(String fileName)
{
void* nativeModelDef = Res_OpenFBX(fileName, VertexDef.sVertexDefinition.mNativeVertexDefinition);
if (nativeModelDef == null)
return null;
return new ModelDef(nativeModelDef);
}
public ModelInstance CreateInstance()
{
void* nativeModelInstance = ModelDef_CreateModelInstance(mNativeModelDef);
if (nativeModelInstance == null)
return null;
var modelInstance = new ModelInstance(nativeModelInstance, this);
return modelInstance;
}
public Animation GetAnimation(String name)
{
return mAnimMap[name];
}
}
public class ModelInstance : RenderCmd
{
[StdCall, CLink]
extern static void ModelInstance_SetJointTranslation(void* nativeModelInstance, int32 jointIdx, ref ModelDef.JointTranslation jointTranslation);
[StdCall, CLink]
extern static void ModelInstance_SetMeshVisibility(void* nativeModelInstance, int32 jointIdx, int32 visibility);
public ModelDef mModelDef;
public ModelDef.Animation mAnim;
public float mFrame;
public float mAnimSpeed = 1.0f;
public bool mLoop;
internal this(void* nativeModelInstance, ModelDef modelDef)
{
mNativeRenderCmd = nativeModelInstance;
mModelDef = modelDef;
}
public void RehupAnimState()
{
for (int32 jointIdx = 0; jointIdx < mModelDef.mJointCount; jointIdx++)
{
ModelDef.JointTranslation jointTranslation;
mAnim.GetJointTranslation(jointIdx, mFrame, out jointTranslation);
SetJointTranslation(jointIdx, ref jointTranslation);
}
}
public void Update()
{
if (mAnim == null)
return;
mFrame += mModelDef.mFrameRate * BFApp.sApp.UpdateDelta * mAnimSpeed;
/*if ((mFrame >= 35.0f) || (mFrame < 1.0f))
mFrame = 34.0f;*/
if (mAnim.mFrameCount > 1)
{
float endFrameNum = mAnim.mFrameCount - 1.0f;
while (mFrame >= endFrameNum)
{
if (mLoop)
mFrame -= endFrameNum;
else
mFrame = endFrameNum - 0.00001f;
}
while (mFrame < 0)
mFrame += endFrameNum;
}
RehupAnimState();
}
public void Play(ModelDef.Animation anim, bool loop = false)
{
mLoop = loop;
mAnim = anim;
mFrame = 0;
RehupAnimState();
}
public void Play(String name, bool loop = false)
{
Play(mModelDef.GetAnimation(name), loop);
}
public void Play(bool loop = false)
{
Play(mModelDef.mAnims[0], loop);
}
public void SetJointTranslation(int32 jointIdx, ref ModelDef.JointTranslation jointTranslation)
{
ModelInstance_SetJointTranslation(mNativeRenderCmd, jointIdx, ref jointTranslation);
}
public void SetMeshVisibility(int32 meshIdx, bool visible)
{
ModelInstance_SetMeshVisibility(mNativeRenderCmd, meshIdx, visible ? 1 : 0);
}
}
#else
extension ModelDef
{
public class Animation
{
public void* mNativeModelDefAnimation;
public int mFrameCount;
public string mName;
public int mAnimIdx;
public IPCProxy<IStudioModelDefAnimation> mStudioModelDefAnimation;
internal Animation(IPCProxy<IStudioModelDefAnimation> studioModelDefAnimation)
{
mStudioModelDefAnimation = studioModelDefAnimation;
mFrameCount = mStudioModelDefAnimation.Proxy.GetFrameCount();
mName = mStudioModelDefAnimation.Proxy.GetName();
}
public void Clip(int startFrame, int numFrames)
{
//ModelDefAnimation_Clip(mNativeModelDefAnimation, startFrame, numFrames);
//mFrameCount = ModelDefAnimation_GetFrameCount(mNativeModelDefAnimation);
mStudioModelDefAnimation.Proxy.Clip(startFrame, numFrames);
mFrameCount = mStudioModelDefAnimation.Proxy.GetFrameCount();
}
}
public IPCProxy<IStudioModelDef> mStudioModelDef;
public float mFrameRate;
public int mJointCount;
public Animation[] mAnims;
public Dictionary<string, Animation> mAnimMap = new Dictionary<string, Animation>();
ModelDef(IPCProxy<IStudioModelDef> studioModelDef)
{
mStudioModelDef = studioModelDef;
mFrameRate = mStudioModelDef.Proxy.GetFrameRate();// ModelDef_GetFrameRate(mNativeModelDef);
mJointCount = mStudioModelDef.Proxy.GetJointCount();
int animCount = mStudioModelDef.Proxy.GetAnimCount();
mAnims = new Animation[animCount];
for (int animIdx = 0; animIdx < animCount; animIdx++)
{
var objId = mStudioModelDef.Proxy.GetAnimation(animIdx);
var anim = new Animation(IPCProxy<IStudioModelDefAnimation>.Create(objId));
anim.mAnimIdx = animIdx;
mAnims[animIdx] = anim;
mAnimMap[anim.mName] = anim;
}
}
public static ModelDef LoadModel(string fileName)
{
var objId = BFApp.StudioHostProxy.Res_OpenFBX(fileName, VertexDef.sVertexDefinition.mStudioVertexDefinition);
IPCProxy<IStudioModelDef> studioModelDef = IPCProxy<IStudioModelDef>.Create(objId);
if (studioModelDef == null)
return null;
return new ModelDef(studioModelDef);
}
public ModelInstance CreateInstance()
{
var objId = mStudioModelDef.Proxy.CreateModelInstance();
IPCProxy<IStudioModelInstance> studioModelInstance = IPCProxy<IStudioModelInstance>.Create(objId);
if (studioModelInstance == null)
return null;
var modelInstance = new ModelInstance(studioModelInstance, this);
return modelInstance;
}
public Animation GetAnimation(string name)
{
return mAnimMap[name];
}
}
public class ModelInstance : RenderCmd
{
public ModelDef mModelDef;
public ModelDef.Animation mAnim;
public float mFrame;
public float mAnimSpeed = 1.0f;
public bool mLoop;
public IPCProxy<IStudioModelInstance> mStudioModelInstance;
internal ModelInstance(IPCProxy<IStudioModelInstance> studioModelInstance, ModelDef modelDef)
{
mStudioModelInstance = studioModelInstance;
var objId = studioModelInstance.Proxy.GetAsRenderCmd();
mStudioRenderCmd = IPCProxy<IStudioRenderCmd>.Create(objId);
mModelDef = modelDef;
}
void RehupAnimState()
{
mStudioModelInstance.Proxy.RehupAnimState(mAnim.mAnimIdx, mFrame);
}
public void Update()
{
if (mAnim == null)
return;
mFrame += mModelDef.mFrameRate * BFApp.sApp.UpdateDelta * mAnimSpeed;
/*if ((mFrame >= 35.0f) || (mFrame < 1.0f))
mFrame = 34.0f;*/
if (mAnim.mFrameCount > 1)
{
float endFrameNum = mAnim.mFrameCount - 1.0f;
while (mFrame >= endFrameNum)
{
if (mLoop)
mFrame -= endFrameNum;
else
mFrame = endFrameNum - 0.00001f;
}
while (mFrame < 0)
mFrame += endFrameNum;
}
RehupAnimState();
}
public void Play(ModelDef.Animation anim, bool loop = false)
{
mLoop = loop;
mAnim = anim;
mFrame = 0;
RehupAnimState();
}
public void Play(string name, bool loop = false)
{
Play(mModelDef.GetAnimation(name), loop);
}
public void Play(bool loop = false)
{
Play(mModelDef.mAnims[0], loop);
}
public void SetMeshVisibility(int meshIdx, bool visible)
{
mStudioModelInstance.Proxy.SetMeshVisibility(meshIdx, visible);
}
}
#endif
}

View file

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Beefy.gfx
{
public enum PixelSnapping
{
Never,
Always,
Auto,
Default
}
}

View file

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
#if STUDIO_CLIENT
using Beefy.ipc;
#endif
namespace Beefy.gfx
{
#if !STUDIO_CLIENT
public class RenderCmd
{
public void* mNativeRenderCmd;
}
#else
public class RenderCmd
{
public IPCProxy<IStudioRenderCmd> mStudioRenderCmd;
}
#endif
}

View file

@ -0,0 +1,175 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Beefy;
using Beefy.geom;
namespace Beefy.gfx
{
public enum DepthFunc
{
Never,
Less,
LessEqual,
Equal,
Greater,
NotEqual,
GreaterEqual,
Always
}
#if !STUDIO_CLIENT
public class RenderState
{
[StdCall, CLink]
static extern void* Gfx_CreateRenderState(void* srcNativeRenderState);
[StdCall, CLink]
static extern void RenderState_Delete(void* renderState);
[StdCall, CLink]
static extern void RenderState_SetClip(void* renderState, float x, float y, float width, float height);
[StdCall, CLink]
static extern void RenderState_DisableClip(void* renderState);
[StdCall, CLink]
static extern void RenderState_SetShader(void* nativeRenderState, void* nativeShader);
[StdCall, CLink]
static extern void RenderState_SetDepthFunc(void* nativeRenderState, int32 depthFunc);
[StdCall, CLink]
static extern void RenderState_SetDepthWrite(void* nativeRenderState, int32 depthWrite);
public void* mNativeRenderState;
public bool mIsFromDefaultRenderState;
internal this()
{
}
internal ~this()
{
RenderState_Delete(mNativeRenderState);
}
public static RenderState Create(RenderState srcRenderState = null)
{
void* nativeRenderState = Gfx_CreateRenderState((srcRenderState != null) ? srcRenderState.mNativeRenderState : null);
if (nativeRenderState == null)
return null;
RenderState renderState = new RenderState();
renderState.mNativeRenderState = nativeRenderState;
return renderState;
}
public Shader Shader
{
set
{
RenderState_SetShader(mNativeRenderState, value.mNativeShader);
}
}
public DepthFunc DepthFunc
{
set
{
RenderState_SetDepthFunc(mNativeRenderState, (int32)value);
}
}
public bool DepthWrite
{
set
{
RenderState_SetDepthWrite(mNativeRenderState, value ? 1 : 0);
}
}
public Rect? ClipRect
{
set
{
if (value.HasValue)
{
Rect rect = value.Value;
RenderState_SetClip(mNativeRenderState, rect.mX, rect.mY, rect.mWidth, rect.mHeight);
}
else
RenderState_DisableClip(mNativeRenderState);
}
}
}
#else
public class RenderState
{
public IPCProxy<IStudioRenderState> mStudioRenderState;
public bool mIsFromDefaultRenderState;
internal RenderState()
{
}
public static RenderState Create(RenderState srcRenderState = null)
{
/*if (nativeRenderState == IntPtr.Zero)
return null;*/
RenderState renderState = new RenderState();
var renderStateRef = new IPCStudioObjectRef<IStudioRenderState>();
if (srcRenderState != null)
renderStateRef = srcRenderState.mStudioRenderState;
var objId = BFApp.sApp.mStudioHost.Proxy.CreateRenderState(renderStateRef);
renderState.mStudioRenderState = IPCProxy<IStudioRenderState>.Create(objId);
return renderState;
}
public Shader Shader
{
set
{
mStudioRenderState.Proxy.SetShader(value.mStudioShader);
}
}
public DepthFunc DepthFunc
{
set
{
mStudioRenderState.Proxy.SetDepthFunc((int)value); ;
}
}
public bool DepthWrite
{
set
{
mStudioRenderState.Proxy.SetDepthWrite(value);
}
}
public Rect? ClipRect
{
set
{
if (value.HasValue)
{
Rect rect = value.Value;
mStudioRenderState.Proxy.SetClipRect(rect.mX, rect.mY, rect.mWidth, rect.mHeight);
}
else
{
mStudioRenderState.Proxy.DisableClipRect();
}
}
}
}
#endif
}

View file

@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace Beefy.gfx
{
public class ShaderParam
{
public void* mNativeShaderParam;
internal this(void* shaderParam)
{
}
}
#if !STUDIO_CLIENT
public class Shader
{
public void* mNativeShader;
public Dictionary<String, ShaderParam> mShaderParamMap;
[StdCall, CLink]
static extern void* Gfx_LoadShader(char8* fileName, void* vertexDefinition);
[StdCall, CLink]
static extern void* Gfx_Shader_Delete(void* shader);
[StdCall, CLink]
static extern void* Gfx_GetShaderParam(void* shader, String paramName);
public static Shader CreateFromFile(String fileName, VertexDefinition vertexDefinition)
{
void* aNativeShader = Gfx_LoadShader(fileName, vertexDefinition.mNativeVertexDefinition);
if (aNativeShader == null)
return null;
Shader aShader = new Shader(aNativeShader);
return aShader;
}
internal this(void* nativeShader)
{
mNativeShader = nativeShader;
}
public ~this()
{
Gfx_Shader_Delete(mNativeShader);
}
ShaderParam GetParam(String paramName)
{
ShaderParam aShaderParam = null;
if (!mShaderParamMap.TryGetValue(paramName, out aShaderParam))
{
void* nativeShaderParam = Gfx_GetShaderParam(mNativeShader, paramName);
if (nativeShaderParam != null)
aShaderParam = new ShaderParam(nativeShaderParam);
mShaderParamMap[paramName] = aShaderParam;
}
return aShaderParam;
}
}
#else
public class Shader : IStudioShader
{
public IPCProxy<IStudioShader> mStudioShader;
public static Shader CreateFromFile(string fileName, VertexDefinition vertexDefinition)
{
Shader shader = new Shader();
IPCObjectId objId = BFApp.StudioHostProxy.CreateShaderFromFile(fileName, vertexDefinition.mStudioVertexDefinition);
shader.mStudioShader = IPCProxy<IStudioShader>.Create(objId);
return shader;
}
internal Shader()
{
}
}
#endif
}

View file

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace Beefy.gfx
{
public struct TexCoords
{
[Reflect]
public float mU;
[Reflect]
public float mV;
public this(float u, float v)
{
mU = u;
mV = v;
}
}
}

View file

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
namespace Beefy.gfx
{
//[StructLayout(LayoutKind.Sequential)]
public struct Vertex3D
{
public float mX;
public float mY;
public float mZ;
public float mU;
public float mV;
public uint32 mColor;
public this(float x, float y, float z, float u, float v, uint32 color)
{
mX = x;
mY = y;
mZ = z;
mU = u;
mV = v;
mColor = color;
}
}
}

View file

@ -0,0 +1,166 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Diagnostics;
namespace Beefy.gfx
{
public class VertexDefinition
{
enum VertexElementFormat
{
Float,
Vector2,
Vector3,
Vector4,
Color,
Byte4,
Short2,
Short4,
NormalizedShort2,
NormalizedShort4,
HalfVector2,
HalfVector4
}
[CRepr]
struct VertexDefData
{
public VertexElementUsage mUsage;
public int32 mUsageIndex;
public VertexElementFormat mFormat;
}
#if !STUDIO_CLIENT
[StdCall, CLink]
static extern void* Gfx_CreateVertexDefinition(VertexDefData* elementData, int32 numElements);
[StdCall, CLink]
static extern void Gfx_VertexDefinition_Delete(void* nativeVertexDefinition);
public void* mNativeVertexDefinition;
#else
public IPCProxy<IStudioVertexDefinition> mStudioVertexDefinition;
#endif
public int32 mVertexSize;
public int32 mPosition2DOffset = -1;
public static void FindPrimitives(Type type, List<Type> primitives)
{
if ((type.IsPrimitive) /*|| (field.FieldType.IsArray)*/)
{
primitives.Add(type);
return;
}
var typeInst = type as TypeInstance;
if (typeInst == null)
return;
for (var field in typeInst.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
{
FindPrimitives(field.FieldType, primitives);
}
}
#if !STUDIO_CLIENT
public static VertexDefinition CreateFromData(void* elementData, int32 numElements, int32 vertexSize)
{
var vertexDefinition = new VertexDefinition();
vertexDefinition.mNativeVertexDefinition = Gfx_CreateVertexDefinition((VertexDefData*)elementData, numElements);
vertexDefinition.mVertexSize = vertexSize;
return vertexDefinition;
}
#endif
internal this()
{
}
public ~this()
{
Gfx_VertexDefinition_Delete(mNativeVertexDefinition);
}
public this(Type type)
{
var typeInst = type as TypeInstance;
var vertexDefDataArray = scope VertexDefData[typeInst.FieldCount];
mVertexSize = typeInst.InstanceSize;
List<Type> primitives = scope List<Type>(3);
int32 fieldIdx = 0;
for (var field in typeInst.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
{
var memberAttribute = field.GetCustomAttribute<VertexMemberAttribute>().Get();
var vertexDefData = VertexDefData();
vertexDefData.mUsage = memberAttribute.mElementUsage;
vertexDefData.mUsageIndex = memberAttribute.mUsageIndex;
primitives.Clear();
FindPrimitives(field.FieldType, primitives);
int32 floats = 0;
int32 shorts = 0;
int32 colors = 0;
for (var prim in primitives)
{
if (prim == typeof(float))
floats++;
else if (prim == typeof(uint16))
shorts++;
else if (prim == typeof(uint32))
colors++;
}
if (memberAttribute.mElementUsage == VertexElementUsage.Position2D)
mPosition2DOffset = field.MemberOffset;
if (floats != 0)
{
Debug.Assert(floats == primitives.Count);
Debug.Assert(floats <= 4);
vertexDefData.mFormat = VertexElementFormat.Float + floats - 1;
}
else if (shorts != 0)
{
if (shorts == 2)
vertexDefData.mFormat = VertexElementFormat.Short2;
else if (shorts == 4)
vertexDefData.mFormat = VertexElementFormat.Short4;
else
Runtime.FatalError("Invalid short count");
}
else if (colors != 0)
{
if (colors == 1)
vertexDefData.mFormat = VertexElementFormat.Color;
else
Runtime.FatalError("Invalid color count");
}
vertexDefDataArray[fieldIdx++] = vertexDefData;
}
#if !STUDIO_CLIENT
mNativeVertexDefinition = Gfx_CreateVertexDefinition(vertexDefDataArray.CArray(), fieldIdx);
#else
fixed (VertexDefData* vertexDefData = vertexDefDataArray)
{
IPCObjectId objId = BFApp.sApp.mStudioHost.Proxy.CreateVertexDefinition(Marshal.SizeOf(typeof(VertexDefData)) * vertexDefDataArray.Length, vertexDefData, vertexDefDataArray.Length, mVertexSize);
mStudioVertexDefinition = IPCProxy<IStudioVertexDefinition>.Create(objId);
}
#endif
}
}
}

View file

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace Beefy.gfx
{
public enum VertexElementUsage
{
Position2D,
Position3D,
Color,
TextureCoordinate,
Normal,
Binormal,
Tangent,
BlendIndices,
BlendWeight,
Depth,
Fog,
PointSize,
Sample,
TessellateFactor
}
}

View file

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace Beefy.gfx
{
public class VertexLayout
{
}
}

View file

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace Beefy.gfx
{
[AttributeUsage(.Field, .ReflectAttribute, ReflectUser=.All)]
public struct VertexMemberAttribute : Attribute
{
public VertexElementUsage mElementUsage;
public int32 mUsageIndex;
public this(VertexElementUsage elementUsage, int32 usageIndex = 0)
{
mElementUsage = elementUsage;
mUsageIndex = usageIndex;
}
}
}

View file

@ -0,0 +1,206 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using Beefy.gfx;
namespace Beefy.res
{
#if !STUDIO_CLIENT
public class PSDLayer
{
[StdCall, CLink]
extern static void Res_PSDLayer_GetSize(void* layerInfo, out int32 x, out int32 y, out int32 width, out int32 height);
[StdCall, CLink]
extern static int32 Res_PSDLayer_GetLayerId(void* layerInfo);
[StdCall, CLink]
extern static char8* Res_PSDLayer_GetName(void* layerInfo);
[StdCall, CLink]
extern static int32 Res_PSDLayer_IsVisible(void* layerInfo);
public void* mNativeLayerInfo;
public int32 mIdx;
public this(void* nativeLayerInfo, int32 idx)
{
mNativeLayerInfo = nativeLayerInfo;
mIdx = idx;
}
public void GetName(String str)
{
str.Append(Res_PSDLayer_GetName(mNativeLayerInfo));
}
public int32 GetLayerId()
{
return Res_PSDLayer_GetLayerId(mNativeLayerInfo);
}
public void GetSize(out int32 x, out int32 y, out int32 width, out int32 height)
{
Res_PSDLayer_GetSize(mNativeLayerInfo, out x, out y, out width, out height);
}
public bool IsVisible()
{
return Res_PSDLayer_IsVisible(mNativeLayerInfo) != 0;
}
}
public class PSDReader
{
[StdCall, CLink]
extern static void* Res_OpenPSD(String fileName);
[StdCall, CLink]
extern static void Res_DeletePSDReader(void* pSDReader);
[StdCall, CLink]
extern static int32 Res_PSD_GetLayerCount(void* pSDReader);
[StdCall, CLink]
extern static void* Res_PSD_GetLayerTexture(void* pSDReader, int32 layerIdx, out int32 xOfs, out int32 yOfs);
[StdCall, CLink]
extern static void* Res_PSD_GetMergedLayerTexture(void* pSDReader, void* layerIndices, int32 count, out int32 xOfs, out int32 yOfs);
[StdCall, CLink]
extern static void* Res_PSD_GetLayerInfo(void* pSDReader, int32 layerIdx);
void* mNativePSDReader;
protected this()
{
}
public int32 GetLayerCount()
{
return Res_PSD_GetLayerCount(mNativePSDReader);
}
public PSDLayer GetLayer(int32 layerIdx)
{
return new PSDLayer(Res_PSD_GetLayerInfo(mNativePSDReader, layerIdx), layerIdx);
}
public static PSDReader OpenFile(String fileName)
{
void* nativePSDReader = Res_OpenPSD(fileName);
if (nativePSDReader == null)
return null;
PSDReader aPSDReader = new PSDReader();
aPSDReader.mNativePSDReader = nativePSDReader;
return aPSDReader;
}
public Image LoadLayerImage(int32 layerIdx)
{
int32 aXOfs = 0;
int32 aYOfs = 0;
void* texture = Res_PSD_GetLayerTexture(mNativePSDReader, layerIdx, out aXOfs, out aYOfs);
if (texture == null)
return null;
Image image = Image.CreateFromNativeTextureSegment(texture);
image.mX = aXOfs;
image.mY = aYOfs;
return image;
}
public Image LoadMergedLayerImage(int32 [] layerIndices)
{
int32 aXOfs = 0;
int32 aYOfs = 0;
void* texture = Res_PSD_GetMergedLayerTexture(mNativePSDReader, layerIndices.CArray(), (int32)layerIndices.Count, out aXOfs, out aYOfs);
if (texture == null)
return null;
Image image = Image.CreateFromNativeTextureSegment(texture);
image.mX = aXOfs;
image.mY = aYOfs;
return image;
}
public void Dipose()
{
if (mNativePSDReader != null)
{
Res_DeletePSDReader(mNativePSDReader);
mNativePSDReader = null;
}
}
}
#else
public class PSDLayer
{
public int mIdx;
public PSDLayer(int nativeLayerInfo, int idx)
{
//mNativeLayerInfo = nativeLayerInfo;
mIdx = idx;
}
public string GetName()
{
return null;
}
public int GetLayerId()
{
return 0;
}
public void GetSize(out int x, out int y, out int width, out int height)
{
x = 0;
y = 0;
width = 0;
height = 0;
return;
}
public bool IsVisible()
{
return false;
}
}
public class PSDReader
{
protected PSDReader()
{
}
public int GetLayerCount()
{
return 0;
}
public PSDLayer GetLayer(int layerIdx)
{
return null;
}
public static PSDReader OpenFile(string fileName)
{
return null;
}
public Image LoadLayerImage(int layerIdx)
{
return null;
}
public Image LoadMergedLayerImage(int [] layerIndices)
{
return null;
}
public void Dipose()
{
//TODO: Dispose
}
}
#endif
}

View file

@ -0,0 +1,184 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using Beefy.utils;
using Beefy.gfx;
using System.Runtime.InteropServices;
namespace Beefy.res
{
public class ResInfo
{
public String mName ~ delete _;
public String mFilePath ~ delete _;
public Guid mResId;
public Object mLoadedObject;
}
public class ResImageInfo : ResInfo
{
}
public class ResGroup
{
public List<ResInfo> mResInfoList = new List<ResInfo>() ~ DeleteContainerAndItems!(_);
}
public class ResourceManager
{
/*[StdCall, CLink]
static extern void Wwise_Shutdown();*/
public Dictionary<String, ResGroup> mResGroupMap = new Dictionary<String, ResGroup>() ~ delete _;
public Dictionary<Guid, ResInfo> mIdToResInfoMap = new Dictionary<Guid, ResInfo>() ~ delete _;
public ~this()
{
//Wwise_Shutdown();
}
public void ParseConfigData(StructuredData data)
{
ThrowUnimplemented();
/*int fileResVer = data.GetInt("ResVer");
using (data.Open("Groups"))
{
for (int groupIdx = 0; groupIdx < data.Count; groupIdx++)
{
using (data.Open(groupIdx))
{
ResGroup resGroup = new ResGroup();
mResGroupMap[data.GetString("Name")] = resGroup;
using (data.Open("Resources"))
{
for (int resIdx = 0; resIdx < data.Count; resIdx++)
{
using (data.Open(resIdx))
{
ResInfo resInfo = null;
string aType = data.GetString("Type");
if (aType == "Image")
{
resInfo = new ResImageInfo();
}
resInfo.mName = data.GetString("Name");
resInfo.mResId = Guid.Parse(data.GetString("Id"));
resInfo.mFilePath = data.GetString("Path");
resGroup.mResInfoList.Add(resInfo);
}
}
}
}
}
}
Type appType = BFApp.sApp.GetType();
Type resourcesType = appType.Assembly.GetType(appType.Namespace + ".Res");
if (resourcesType == null)
return;
Type soundBanksTypes = resourcesType.GetNestedType("SoundBanks");
if (soundBanksTypes != null)
{
using (data.Open("SoundBanks"))
{
for (int soundBanksIdx = 0; soundBanksIdx < data.Count; soundBanksIdx++)
{
string name = data.Keys[soundBanksIdx];
uint wwiseId = data.GetUInt(soundBanksIdx);
SoundBank soundBank = new SoundBank();
soundBank.mName = name;
soundBank.mWwiseSoundId = wwiseId;
FieldInfo fieldInfo = soundBanksTypes.GetField(name);
fieldInfo.SetValue(null, soundBank);
}
}
}
Type soundEventsTypes = resourcesType.GetNestedType("SoundEvents");
if (soundEventsTypes != null)
{
using (data.Open("SoundEvents"))
{
for (int soundBanksIdx = 0; soundBanksIdx < data.Count; soundBanksIdx++)
{
string name = data.Keys[soundBanksIdx];
uint wwiseId = data.GetUInt(soundBanksIdx);
SoundEvent soundRes = new SoundEvent();
soundRes.mWwiseEventId = wwiseId;
FieldInfo fieldInfo = soundEventsTypes.GetField(name);
fieldInfo.SetValue(null, soundRes);
}
}
}
Type soundParametersTypes = resourcesType.GetNestedType("SoundParameters");
if (soundParametersTypes != null)
{
using (data.Open("SoundParameters"))
{
for (int soundBanksIdx = 0; soundBanksIdx < data.Count; soundBanksIdx++)
{
string name = data.Keys[soundBanksIdx];
uint wwiseId = data.GetUInt(soundBanksIdx);
SoundParameter soundParameter = new SoundParameter();
soundParameter.mWwiseParamId = wwiseId;
FieldInfo fieldInfo = soundParametersTypes.GetField(name);
fieldInfo.SetValue(null, soundParameter);
}
}
}*/
}
public bool LoadResGroup(String groupName)
{
ThrowUnimplemented();
/*Type appType = BFApp.sApp.GetType();
Type resourcesType = appType.Assembly.GetType(appType.Namespace + ".Res");
Type imagesType = resourcesType.GetNestedType("Images");
ResGroup resGroup = mResGroupMap[groupName];
foreach (ResInfo resInfo in resGroup.mResInfoList)
{
if (resInfo is ResImageInfo)
{
string fileName = resInfo.mFilePath;
if (!fileName.Contains(':'))
fileName = BFApp.sApp.mInstallDir + resInfo.mFilePath;
Image image = Image.LoadFromFile(fileName, false);
FieldInfo fieldInfo = imagesType.GetField(resInfo.mName);
fieldInfo.SetValue(null, image);
}
}
return true;*/
}
public Object GetResourceById(Guid id, bool allowLoad = false)
{
ThrowUnimplemented();
/*ResInfo resInfo = mIdToResInfoMap[id];
if ((resInfo.mLoadedObject == null) && (allowLoad))
{
if (resInfo is ResImageInfo)
resInfo.mLoadedObject = Image.LoadFromFile(resInfo.mFilePath, false);
}
return resInfo.mLoadedObject;*/
}
}
}

View file

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using Beefy;
namespace Beefy.res
{
/*public class SoundBank
{
[StdCall, CLink]
static extern void Wwise_LoadBankByName(String name);
public String mName;
public uint32 mWwiseSoundId;
public void Load()
{
Wwise_LoadBankByName(mName);
}
}*/
}

View file

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using Beefy;
namespace Beefy.res
{
/*public class SoundEvent
{
[StdCall, CLink]
static extern void Wwise_SendEvent(uint32 eventId, void* objectId);
public uint32 mWwiseEventId;
public void Post(SoundGameObject soundGameObject = null)
{
if (soundGameObject == null)
Wwise_SendEvent(mWwiseEventId, null);
else if (soundGameObject == null)
Wwise_SendEvent(mWwiseEventId, soundGameObject.mWwiseObject);
}
}*/
}

View file

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Beefy.res
{
public class SoundGameObject
{
public void* mWwiseObject;
}
}

View file

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using Beefy;
namespace Beefy.res
{
/*public class SoundParameter
{
[StdCall, CLink]
static extern int32 Wwise_SetRTPCValue(uint32 paramId, float value, void* soundGameObject);
public uint32 mWwiseParamId;
public bool Set(float value, SoundGameObject soundGameObject = null)
{
return Wwise_SetRTPCValue(mWwiseParamId, value, (soundGameObject == null) ? null : soundGameObject.mWwiseObject) != 0;
}
}*/
}

View file

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Beefy.sys
{
public class SysBitmap
{
public void* mNativeBFBitmap;
}
}

View file

@ -0,0 +1,150 @@
using System;
using System.Collections.Generic;
using System.Text;
using Beefy.widgets;
using Beefy;
using System.Runtime.InteropServices;
namespace Beefy.sys
{
public class SysMenu : IMenu
{
public String mText ~ delete _;
public String mHotKey ~ delete _;
public SysBitmap mBitmap;
public bool mEnabled;
public int32 mCheckState;
public bool mRadioCheck;
public SysMenu mParent;
public BFWindow mWindow;
public void* mNativeBFMenu;
public Event<MenuItemSelectedHandler> mOnMenuItemSelected ~ _.Dispose();
public Event<MenuItemUpdateHandler> mOnMenuItemUpdate ~ _.Dispose();
public List<SysMenu> mChildren ~ DeleteContainerAndItems!(_);
internal this()
{
}
public int ChildCount
{
get
{
return (mChildren != null) ? mChildren.Count : 0;
}
}
public virtual SysMenu AddMenuItem(String text, String hotKey = null, MenuItemSelectedHandler menuItemSelectedHandler = null, MenuItemUpdateHandler menuItemUpdateHandler = null,
SysBitmap bitmap = null, bool enabled = true, int32 checkState = -1, bool radioCheck = false)
{
if (mChildren == null)
mChildren = new List<SysMenu>();
SysMenu sysMenu = new SysMenu();
if (text != null)
sysMenu.mText = new String(text);
if (hotKey != null)
sysMenu.mHotKey = new String(hotKey);
sysMenu.mBitmap = bitmap;
sysMenu.mEnabled = enabled;
sysMenu.mCheckState = checkState;
sysMenu.mRadioCheck = radioCheck;
if (menuItemSelectedHandler != null)
sysMenu.mOnMenuItemSelected.Add(menuItemSelectedHandler);
if (menuItemUpdateHandler != null)
sysMenu.mOnMenuItemUpdate.Add(menuItemUpdateHandler);
sysMenu.mNativeBFMenu = mWindow.AddMenuItem(mNativeBFMenu, mChildren.Count, text, hotKey, (bitmap != null) ? bitmap.mNativeBFBitmap : null, enabled, checkState, radioCheck);
sysMenu.mParent = this;
sysMenu.mWindow = mWindow;
mWindow.mSysMenuMap[(int)sysMenu.mNativeBFMenu ] = sysMenu;
mChildren.Add(sysMenu);
return sysMenu;
}
public void SetDisabled(bool disabled)
{
mEnabled = !disabled;
Modify(mText, mHotKey, mBitmap, mEnabled, mCheckState, mRadioCheck);
}
public void SetHotKey(StringView hotKey)
{
if (hotKey.IsNull)
{
DeleteAndNullify!(mHotKey);
}
else
{
if (mHotKey == null)
mHotKey = new String(hotKey);
else
mHotKey.Set(hotKey);
}
UpdateChanges();
}
public void UpdateChanges()
{
mWindow.ModifyMenuItem(mNativeBFMenu, mText, mHotKey, (mBitmap != null) ? mBitmap.mNativeBFBitmap : null, mEnabled, mCheckState, mRadioCheck);
}
public virtual void Modify(String text, String hotKey = null, SysBitmap bitmap = null, bool enabled = true, int32 checkState = -1, bool radioCheck = false)
{
if ((Object)mText != text)
{
if (text != null)
{
if (mText == null)
mText = new String(text);
else
mText.Set(text);
}
else
DeleteAndNullify!(mText);
}
if ((Object)mHotKey != hotKey)
{
if (hotKey != null)
{
if (mHotKey == null)
mHotKey = new String(hotKey);
else
mHotKey.Set(hotKey);
}
else
DeleteAndNullify!(mHotKey);
}
mBitmap = bitmap;
mEnabled = enabled;
mCheckState = checkState;
mRadioCheck = radioCheck;
UpdateChanges();
}
public virtual void Dispose()
{
mWindow.mSysMenuMap.Remove((int)mNativeBFMenu);
mWindow.DeleteMenuItem(mNativeBFMenu);
}
public void UpdateChildItems()
{
if (mChildren != null)
{
for (SysMenu child in mChildren)
child.mOnMenuItemUpdate(child);
}
}
public virtual void Selected()
{
mOnMenuItemSelected(this);
UpdateChildItems();
}
}
}

View file

@ -0,0 +1,99 @@
using System;
using System.Collections.Generic;
using System.Text;
using Beefy.widgets;
using Beefy.gfx;
namespace Beefy.theme
{
public class DesignToolboxEntry
{
public String mGroupName;
public String mName;
public String mSortName;
public Image mIcon;
public Type mType;
public this(String name, Type theType, Image icon = null)
{
int32 index = (int32)name.IndexOf('@');
if (index != -1)
{
mName = new String(index);
mName.Append(name, 0, index);
mSortName = new String();
mSortName.Append(name, index + 1);
}
else
{
mName = new String(name);
mSortName = new String(name);
}
mType = theType;
mIcon = icon;
}
}
public class ThemeFactory
{
public static ThemeFactory mDefault;
public virtual void Init()
{
}
public virtual void Update()
{
}
public virtual ButtonWidget CreateButton(Widget parent, String caption, float x, float y, float width, float height)
{
return null;
}
public virtual CheckBox CreateCheckbox(Widget parent, float x = 0, float y = 0, float width = 0, float height = 0)
{
return null;
}
public virtual EditWidget CreateEditWidget(Widget parent, float x = 0, float y = 0, float width = 0, float height = 0)
{
return null;
}
public virtual TabbedView CreateTabbedView(TabbedView.SharedData sharedData, Widget parent = null, float x = 0, float y = 0, float width = 0, float height = 0)
{
return null;
}
public virtual DockingFrame CreateDockingFrame(DockingFrame parent = null)
{
return null;
}
public virtual ListView CreateListView()
{
return null;
}
public virtual Scrollbar CreateScrollbar(Scrollbar.Orientation orientation)
{
return null;
}
public virtual InfiniteScrollbar CreateInfiniteScrollbar()
{
return null;
}
public virtual MenuWidget CreateMenuWidget(Menu menu)
{
return null;
}
public virtual Dialog CreateDialog(String title = null, String text = null, Image icon = null)
{
return null;
}
}
}

View file

@ -0,0 +1,114 @@
using System;
using System.Collections.Generic;
using System.Text;
using Beefy.widgets;
using Beefy.gfx;
using Beefy.utils;
namespace Beefy.theme.dark
{
public class DarkButton : ButtonWidget, IHotKeyHandler
{
public String mLabel ~ delete _;
public float mDrawDownPct;
public float mLabelYOfs;
[DesignEditable(DefaultEditString=true)]
public String Label
{
get
{
return mLabel;
}
set
{
String.NewOrSet!(mLabel, value);
}
}
bool IHotKeyHandler.Handle(KeyCode keyCode)
{
if (mDisabled)
return false;
if (DarkTheme.CheckUnderlineKeyCode(mLabel, keyCode))
{
mDrawDownPct = 1.0f;
MouseClicked(0, 0, 3);
return true;
}
return false;
}
public override void DefaultDesignInit()
{
base.DefaultDesignInit();
mIdStr = "Button";
Label = "Button";
mWidth = GS!(80);
mHeight = GS!(20);
}
public override void Draw(Graphics g)
{
base.Draw(g);
bool drawDown = ((mMouseDown && mMouseOver) || (mMouseFlags.HasFlag(MouseFlag.Kbd)));
if (mDrawDownPct > 0)
drawDown = true;
Image texture = drawDown ? DarkTheme.sDarkTheme.mImages[(int32)DarkTheme.ImageIdx.BtnDown] :
mMouseOver ? DarkTheme.sDarkTheme.mImages[(int32)DarkTheme.ImageIdx.BtnOver] :
DarkTheme.sDarkTheme.mImages[(int32)DarkTheme.ImageIdx.BtnUp];
if (mDisabled)
texture = DarkTheme.sDarkTheme.mImages[(int32)DarkTheme.ImageIdx.BtnUp];
g.DrawBox(texture, 0, 0, mWidth, mHeight);
if ((mHasFocus) && (!mDisabled))
{
using (g.PushColor(DarkTheme.COLOR_SELECTED_OUTLINE))
g.DrawBox(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.Outline), 0, 0, mWidth, mHeight);
}
g.SetFont(DarkTheme.sDarkTheme.mSmallFont);
if (mLabel != null)
{
using (g.PushColor(mDisabled ? 0x80FFFFFF : Color.White))
{
DarkTheme.DrawUnderlined(g, mLabel, GS!(2), (mHeight - GS!(20)) / 2 + mLabelYOfs, .Centered, mWidth - GS!(4), .Truncate);
}
}
}
public override void Update()
{
base.Update();
if (mDrawDownPct > 0)
{
mDrawDownPct = Math.Max(mDrawDownPct - 0.25f, 0);
MarkDirty();
}
}
public override void Serialize(StructuredData data)
{
base.Serialize(data);
data.Add("Label", mLabel);
}
public override bool Deserialize(StructuredData data)
{
base.Deserialize(data);
data.GetString(mLabel, "Label");
return true;
}
public override void GotFocus()
{
base.GotFocus();
}
}
}

View file

@ -0,0 +1,176 @@
using System;
using System.Collections.Generic;
using System.Text;
using Beefy.widgets;
using Beefy.gfx;
namespace Beefy.theme.dark
{
public class DarkCheckBox : CheckBox, IHotKeyHandler
{
public bool mLargeFormat;
public Image mCheckIcon;
public Image mIndeterminateIcon;
public String mLabel ~ delete _;
public Font mFont;
public bool mDisabled;
public State mState;
public override bool Checked
{
get
{
return mState != .Unchecked;
}
set
{
mState = value ? .Checked : .Unchecked;
}
}
public override State State
{
get
{
return mState;
}
set
{
mState = value;
}
}
public String Label
{
get
{
return mLabel;
}
set
{
String.NewOrSet!(mLabel, value);
}
}
public this()
{
mMouseInsets = new Insets(2, 2, 3, 3);
mCheckIcon = DarkTheme.sDarkTheme.GetImage(.Check);
mIndeterminateIcon = DarkTheme.sDarkTheme.GetImage(.CheckIndeterminate);
mFont = DarkTheme.sDarkTheme.mSmallFont;
}
bool IHotKeyHandler.Handle(KeyCode keyCode)
{
if (mDisabled)
return false;
if (DarkTheme.CheckUnderlineKeyCode(mLabel, keyCode))
{
//SetFocus();
Checked = !Checked;
return true;
}
return false;
}
public override void DefaultDesignInit()
{
base.DefaultDesignInit();
mIdStr = "CheckBox";
mWidth = 20;
mHeight = 20;
}
public override void Draw(Graphics g)
{
base.Draw(g);
if (mLargeFormat)
{
g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.CheckboxLarge));
}
else
{
if (mMouseOver)
{
if (mMouseDown)
g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.CheckboxDown));
else
g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.CheckboxOver));
}
else
g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.Checkbox));
}
if (mState == .Checked)
g.Draw(mCheckIcon);
else if (mState == .Indeterminate)
g.Draw(mIndeterminateIcon);
if (mHasFocus)
{
using (g.PushColor(DarkTheme.COLOR_SELECTED_OUTLINE))
g.DrawButton(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.Outline), 0, 0, mWidth);
}
if (mLabel != null)
{
g.SetFont(mFont);
DarkTheme.DrawUnderlined(g, mLabel, GS!(22), 0);
/*int underlinePos = mLabel.IndexOf('&');
if ((underlinePos != -1) && (underlinePos < mLabel.Length - 1))
{
String label = scope String();
label.Append(mLabel, 0, underlinePos);
float underlineX = mFont.GetWidth(label);
char32 underlineC = mLabel.GetChar32(underlinePos + 1).0;
float underlineWidth = mFont.GetWidth(underlineC);
label.Append(mLabel, underlinePos + 1);
g.DrawString(label, GS!(22), 0);
g.FillRect(GS!(22) + underlineX, mFont.GetAscent() + GS!(1), underlineWidth, (int)GS!(1.2f));
}
else
{
g.DrawString(mLabel, GS!(22), 0);
}*/
}
}
public override void DrawAll(Graphics g)
{
if (mDisabled)
{
using (g.PushColor(0x80FFFFFF))
base.DrawAll(g);
}
else
base.DrawAll(g);
}
public float CalcWidth()
{
if (mLabel == null)
return 20;
return mFont.GetWidth(mLabel) + GS!(22);
}
public override void KeyChar(char32 theChar)
{
if (mDisabled)
return;
base.KeyChar(theChar);
if (theChar == ' ')
Checked = !Checked;
}
}
}

View file

@ -0,0 +1,375 @@
using System;
using System.Collections.Generic;
using System.Text;
using Beefy.widgets;
using Beefy.gfx;
using Beefy.events;
namespace Beefy.theme.dark
{
public class DarkComboBox : ComboBox
{
public class CBMenuWidget : DarkMenuWidget
{
public this(Menu menu) :
base(menu)
{
}
public override float GetReverseAdjust()
{
return GS!(-10);
}
}
/*public string Label
{
get
{
if (mEditWidget == null)
return mLabel;
else
return mEditWidget.Text;
}
set
{
if (mEditWidget == null)
mLabel = value;
else
mEditWidget.Text = value;
}
}*/
String mLabel ~ delete _;
public float mLabelX = GS!(8);
public FontAlign mLabelAlign = FontAlign.Centered;
public bool mFrameless = false;
public Event<Action<Menu>> mPopulateMenuAction ~ _.Dispose();
public CBMenuWidget mCurMenuWidget;
bool mJustClosed;
public uint32 mBkgColor;
public DarkEditWidget mEditWidget;
bool mAllowReverseDropdown; // Allow popdown to "popup" if there isn't enough space
public Widget mPrevFocusWidget;
public bool mFocusDropdown = true;
virtual public StringView Label
{
get
{
if (mEditWidget != null)
{
if (mLabel == null)
mLabel = new String();
mLabel.Clear();
mEditWidget.GetText(mLabel);
}
return mLabel;
}
set
{
String.NewOrSet!(mLabel, value);
if (mEditWidget != null)
mEditWidget.SetText(mLabel);
}
}
public override void Draw(Graphics g)
{
base.Draw(g);
float yOfs = 0;
if (mEditWidget != null)
{
g.DrawBox(DarkTheme.sDarkTheme.GetImage(.EditBox), 0, 0, mWidth, mHeight);
g.Draw(DarkTheme.sDarkTheme.mImages[(int32)DarkTheme.ImageIdx.ComboEnd], mWidth - GS!(25), (mHeight - GS!(22)) / 2 + yOfs);
if ((mHasFocus) || (mEditWidget.mHasFocus))
{
using (g.PushColor(DarkTheme.COLOR_SELECTED_OUTLINE))
g.DrawBox(DarkTheme.sDarkTheme.GetImage(.Outline), 0, 0, mWidth, mHeight);
}
return;
}
if (!mFrameless)
{
Image texture = DarkTheme.sDarkTheme.mImages[(int32)DarkTheme.ImageIdx.ComboBox];
g.DrawBox(texture, 0, -2, mWidth, mHeight);
}
else
{
if (mBkgColor != 0)
{
using (g.PushColor(mBkgColor))
g.FillRect(0, 0, mWidth, mHeight);
}
yOfs = 2.0f;
}
if (mEditWidget == null)
{
using (g.PushColor(mDisabled ? 0x80FFFFFF : Color.White))
{
g.Draw(DarkTheme.sDarkTheme.mImages[(int32)DarkTheme.ImageIdx.ComboEnd], mWidth - GS!(25), (mHeight - GS!(24)) / 2 + yOfs);
g.SetFont(DarkTheme.sDarkTheme.mSmallFont);
String label = scope String();
GetLabel(label);
if (label != null)
{
float fontHeight = g.mFont.GetHeight();
//g.DrawString(label, mLabelX, (mHeight - GS!(24)) / 2, mLabelAlign, mWidth - mLabelX - GS!(24), FontOverflowMode.Ellipsis);
g.DrawString(label, mLabelX, (mHeight - fontHeight) / 2 - GS!(2) - 1, mLabelAlign, mWidth - mLabelX - GS!(24), FontOverflowMode.Ellipsis);
}
}
if (mHasFocus)
{
using (g.PushColor(DarkTheme.COLOR_SELECTED_OUTLINE))
g.DrawBox(DarkTheme.sDarkTheme.GetImage(.Outline), GS!(2), 0, mWidth - GS!(4), mHeight - GS!(4));
}
}
}
public void GetLabel(String label)
{
if (mEditWidget == null)
{
if (mLabel != null)
label.Append(mLabel);
}
else
mEditWidget.GetText(label);
}
void HandleKeyDown(KeyDownEvent evt)
{
if (evt.mKeyCode == KeyCode.Escape)
{
evt.mHandled = true;
mCurMenuWidget.Close();
}
}
public virtual void MenuClosed()
{
if (mPrevFocusWidget != null)
{
mPrevFocusWidget.SetFocus();
}
}
void HandleClose(Menu menu, Menu selectedItem)
{
mCurMenuWidget = null;
mJustClosed = true;
//mWidgetWindow.mWindowKeyDownDelegate -= HandleKeyDown;
MenuClosed();
}
public virtual MenuWidget ShowDropdown()
{
mPrevFocusWidget = mWidgetWindow.mFocusWidget;
float popupXOfs = GS!(5);
float popupYOfs = GS!(-2);
if (mEditWidget != null)
{
popupXOfs = GS!(2);
popupYOfs = GS!(2);
}
else if (mFrameless)
{
popupXOfs = GS!(2);
popupYOfs = GS!(2);
}
//if (mCurMenuWidget != null)
//mCurMenuWidget.Close();
mAutoFocus = false;
Menu aMenu = new Menu();
mPopulateMenuAction(aMenu);
WidgetWindow menuWindow = null;
if (mCurMenuWidget != null)
menuWindow = mCurMenuWidget.mWidgetWindow;
let menuWidget = new CBMenuWidget(aMenu);
//menuWidget.SetShowPct(0.0f);
//menuWidget.mWindowFlags &= ~(BFWindow.Flags.PopupPosition);
mCurMenuWidget = menuWidget;
aMenu.mOnMenuClosed.Add(new => HandleClose);
menuWidget.mMinContainerWidth = mWidth + GS!(8);
menuWidget.mMaxContainerWidth = Math.Max(menuWidget.mMinContainerWidth, 1536);
menuWidget.mWindowFlags = .ClientSized | .DestAlpha | .FakeFocus;
if (!mFocusDropdown)
menuWidget.mWindowFlags |= .NoActivate;
menuWidget.CalcSize();
float popupY = mHeight + popupYOfs;
//TODO: Autocomplete didn't work on this: BFApp.sApp.GetW...
menuWidget.Init(this, popupXOfs, popupY, true, menuWindow);
// Why were we capturing?
mWidgetWindow.TransferMouse(menuWidget.mWidgetWindow);
if (menuWindow == null)
menuWidget.SetShowPct(0.0f);
return menuWidget;
}
public override void MouseDown(float x, float y, int32 btn, int32 btnCount)
{
base.MouseDown(x, y, btn, btnCount);
if (mDisabled)
return;
if ((mCurMenuWidget == null) && (!mJustClosed))
{
if (mEditWidget != null)
SetFocus();
ShowDropdown();
//mCurMenuWidget.SetFocus();
//mWidgetWindow.mWindowKeyDownDelegate += HandleKeyDown;
}
}
public override void SetFocus()
{
if (mEditWidget != null)
{
mEditWidget.SetFocus();
return;
}
base.SetFocus();
}
public override void Update()
{
base.Update();
mJustClosed = false;
bool hasFocus = mHasFocus;
if ((mEditWidget != null) && (mEditWidget.mHasFocus))
hasFocus = true;
if ((!hasFocus) && (mCurMenuWidget != null))
if (mCurMenuWidget.mWidgetWindow.mHasFocus)
hasFocus = true;
if ((!hasFocus) && (mCurMenuWidget != null))
{
mCurMenuWidget.SubmitSelection();
if (mCurMenuWidget != null)
mCurMenuWidget.Close();
}
}
public override void Resize(float x, float y, float width, float height)
{
base.Resize(x, y, width, height);
ResizeComponents();
}
private void ResizeComponents()
{
if (mEditWidget != null)
{
mEditWidget.Resize(0, 0, Math.Max(mWidth - GS!(24), 0), mHeight);
}
}
public virtual bool WantsKeyHandling()
{
return true;
}
void EditKeyDownHandler(KeyDownEvent evt)
{
if (!WantsKeyHandling())
return;
if ((evt.mKeyCode == .Up) || (evt.mKeyCode == .Down) ||
(evt.mKeyCode == .PageUp) || (evt.mKeyCode == .PageDown) ||
(evt.mKeyCode == .Home) || (evt.mKeyCode == .End))
{
if (mCurMenuWidget != null)
mCurMenuWidget.KeyDown(evt.mKeyCode, false);
}
if ((evt.mKeyCode == .Down) && (mCurMenuWidget == null))
{
ShowDropdown();
var label = Label;
for (let itemWidget in mCurMenuWidget.mItemWidgets)
{
if (itemWidget.mMenuItem.mLabel == label)
mCurMenuWidget.SetSelection(@itemWidget.Index);
}
}
if ((evt.mKeyCode == .Escape) && (mCurMenuWidget != null) && (mEditWidget != null))
{
mCurMenuWidget.Close();
mEditWidget.SetFocus();
evt.mHandled = true;
}
if ((evt.mKeyCode == .Return) && (mCurMenuWidget != null))
{
mCurMenuWidget.SubmitSelection();
evt.mHandled = true;
}
}
public override void KeyDown(KeyCode keyCode, bool isRepeat)
{
if (mCurMenuWidget != null)
mCurMenuWidget.KeyDown(keyCode, isRepeat);
}
public void MakeEditable(DarkEditWidget editWidget = null)
{
if (mEditWidget == null)
{
mEditWidget = editWidget;
if (mEditWidget == null)
mEditWidget = new DarkEditWidget();
mEditWidget.mDrawBox = false;
mEditWidget.mOnSubmit.Add(new => EditSubmit);
mEditWidget.mOnKeyDown.Add(new => EditKeyDownHandler);
var ewc = (DarkEditWidgetContent)mEditWidget.mEditWidgetContent;
ewc.mScrollToStartOnLostFocus = true;
AddWidget(mEditWidget);
ResizeComponents();
}
}
private void EditSubmit(EditEvent theEvent)
{
if (mCurMenuWidget != null)
{
if (mCurMenuWidget.mSelectIdx != -1)
{
mCurMenuWidget.SubmitSelection();
if (mCurMenuWidget != null)
mCurMenuWidget.Close();
}
}
}
public override void KeyDown(KeyDownEvent keyEvent)
{
base.KeyDown(keyEvent);
EditKeyDownHandler(keyEvent);
}
}
}

View file

@ -0,0 +1,125 @@
using System;
using System.Collections.Generic;
using System.Text;
using Beefy.gfx;
using Beefy.widgets;
namespace Beefy.theme.dark
{
public class DarkDialog : Dialog
{
public Font mFont;
public Insets mTextInsets = new Insets() ~ delete _;
public float mMinWidth;
public float mButtonBottomMargin = GS!(12);
public float mButtonRightMargin = GS!(16);
public this(String title = null, String text = null, Image icon = null) :
base(title, text, icon)
{
mFont = DarkTheme.sDarkTheme.mSmallFont;
mTextInsets.Set(GS!(20), GS!(20), GS!(20), GS!(20));
if (icon != null)
mTextInsets.mLeft += icon.mWidth + GS!(20);
}
public override ButtonWidget CreateButton(String label)
{
DarkButton button = new DarkButton();
button.Label = label;
button.mAutoFocus = true;
return button;
}
public override EditWidget CreateEditWidget()
{
return new DarkEditWidget();
}
public override void CalcSize()
{
base.CalcSize();
// Size buttons with a zero width first so we can determine a minimium size this dialog needs to be
mWidth = mMinWidth;
mHeight = 0;
ResizeComponents();
String str = scope String();
for (var strView in mText.Split('\n'))
{
str.Clear();
strView.ToString(str);
mWidth = Math.Max(mWidth, mFont.GetWidth(str) + mTextInsets.Horz);
}
mWidth = Math.Max(mWidth, GS!(240));
float maxWidth = GS!(900);
if (mWidth >= maxWidth)
{
// We don't want to barely size the dialog large enough, otherwise the wrapping would leave just a short chunk
if (mWidth < maxWidth * 1.4f)
mWidth = maxWidth * 0.7f;
else
mWidth = maxWidth;
}
mWidth = Math.Max(-mButtons[0].mX + mTextInsets.mLeft, mWidth);
mHeight = GS!(80);
for (var strView in mText.Split('\n'))
{
str.Clear();
strView.ToString(str);
mHeight += mFont.GetWrapHeight(str, mWidth - mTextInsets.Horz);
}
if (mDialogEditWidget != null)
mHeight += GS!(16);
}
public override void ResizeComponents()
{
base.ResizeComponents();
float maxTextLen = 0;
for (DarkButton button in mButtons)
maxTextLen = Math.Max(maxTextLen, mFont.GetWidth(button.mLabel));
float buttonBaseSize = GS!(30) + maxTextLen;
float spacing = GS!(8);
float curY = mHeight - GS!(20) - mButtonBottomMargin;
float curX = mWidth - (mButtons.Count * buttonBaseSize) - ((mButtons.Count - 1) * spacing) - mButtonRightMargin;
if (mDialogEditWidget != null)
{
mDialogEditWidget.Resize(GS!(16), curY - GS!(36), Math.Max(mWidth - GS!(16) * 2, 0), GS!(24));
}
for (DarkButton button in mButtons)
{
float aSize = buttonBaseSize;
button.Resize(curX, curY, aSize, GS!(22));
curX += aSize + spacing;
}
}
public override void Draw(Graphics g)
{
base.Draw(g);
using (g.PushColor(DarkTheme.COLOR_WINDOW))
g.FillRect(0, 0, mWidth, mHeight);
if (mIcon != null)
g.Draw(mIcon, GS!(20), (mHeight - GS!(8) - mIcon.mHeight) / 2);
g.SetFont(mFont);
if (mText != null)
g.DrawString(mText, mTextInsets.mLeft, mTextInsets.mTop, FontAlign.Left, mWidth - mTextInsets.Horz, FontOverflowMode.Wrap);
}
}
}

View file

@ -0,0 +1,137 @@
using System;
using System.Collections.Generic;
using System.Text;
using Beefy.widgets;
using Beefy.gfx;
namespace Beefy.theme.dark
{
public class DarkDockingFrame : DockingFrame
{
IDockable mCurDragTarget;
public bool mDrawBkg = true;
public this()
{
mMinWindowSize = GS!(100);
mMinWindowSize = GS!(32);
mDragMarginSize = GS!(64);
mDragWindowMarginSize = GS!(10);
mWindowMargin = 0;
mSplitterSize = GS!(6.0f);
mWindowSpacing = GS!(2.0f);
}
public ~this()
{
}
public override void RemovedFromParent(Widget previousParent, WidgetWindow window)
{
base.RemovedFromParent(previousParent, window);
}
public override void Draw(Graphics g)
{
if (mDrawBkg)
{
using (g.PushColor(DarkTheme.COLOR_BKG))
g.FillRect(0, 0, mWidth, mHeight);
}
base.Draw(g);
}
public override void DrawAll(Graphics g)
{
base.DrawAll(g);
if ((mCurDragTarget != null) && (mWidgetWindow.mAlpha == 1.0f))
{
using (g.PushTranslate(mWidgetWindow.mMouseX, mWidgetWindow.mMouseY))
{
mCurDragTarget.DrawDockPreview(g);
}
}
}
public override void DrawDraggingDock(Graphics g)
{
if (mDraggingCustomDock != null)
{
mDraggingCustomDock.Draw(g);
}
else
{
if (mDraggingAlign == WidgetAlign.Inside)
{
using (g.PushColor(0x30FFFFFF))
g.DrawBox(DarkTheme.sDarkTheme.mImages[(int32)DarkTheme.ImageIdx.WhiteCircle], mDraggingRef.mX, mDraggingRef.mY, mDraggingRef.mWidth, mDraggingRef.mHeight);
}
else
{
DockedWidget widgetRef = mDraggingRef ?? this;
Matrix matrix = Matrix.IdentityMatrix;
float dist = 0;
if (mDraggingAlign == WidgetAlign.Left)
{
matrix.Rotate(Math.PI_f);
matrix.Translate(widgetRef.mX, widgetRef.mY + widgetRef.mHeight);
dist = widgetRef.mHeight;
}
else if (mDraggingAlign == WidgetAlign.Right)
{
matrix.Rotate(0);
matrix.Translate(widgetRef.mX + widgetRef.mWidth, widgetRef.mY);
dist = widgetRef.mHeight;
}
else if (mDraggingAlign == WidgetAlign.Top)
{
matrix.Rotate(-Math.PI_f / 2);
matrix.Translate(widgetRef.mX, widgetRef.mY);
dist = widgetRef.mWidth;
}
else if (mDraggingAlign == WidgetAlign.Bottom)
{
matrix.Rotate(Math.PI_f / 2);
matrix.Translate(widgetRef.mX + widgetRef.mWidth, widgetRef.mY + widgetRef.mHeight);
dist = widgetRef.mWidth;
}
using (g.PushMatrix(matrix))
{
if (mDraggingRef != null)
g.DrawBox(DarkTheme.sDarkTheme.mImages[(int32)DarkTheme.ImageIdx.GlowDot], GS!(-9), GS!(-8), GS!(20), dist + GS!(8) * 2);
else
g.DrawBox(DarkTheme.sDarkTheme.mImages[(int32)DarkTheme.ImageIdx.WhiteCircle], GS!(-13), GS!(-8), GS!(20), dist + GS!(8) * 2);
int32 arrowCount = 3;
if (dist < 80)
arrowCount = 1;
else if (dist < 120)
arrowCount = 2;
float arrowSep = dist / (arrowCount + 1);
for (int32 arrowNum = 0; arrowNum < arrowCount; arrowNum++)
g.Draw(DarkTheme.sDarkTheme.mImages[(int32)DarkTheme.ImageIdx.ArrowRight], GS!(-22), GS!(-9) + (arrowNum + 1) * arrowSep);
}
}
}
}
public override void ShowDragTarget(IDockable draggingItem)
{
base.ShowDragTarget(draggingItem);
mCurDragTarget = draggingItem;
}
public override void HideDragTarget(IDockable draggingItem, bool executeDrag = false)
{
base.HideDragTarget(draggingItem, executeDrag);
mCurDragTarget = null;
}
}
}

View file

@ -0,0 +1,922 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using Beefy.widgets;
using Beefy.gfx;
using Beefy.utils;
namespace Beefy.theme.dark
{
public class DarkEditWidgetContent : EditWidgetContent
{
public Font mFont;
public uint32[] mTextColors = sDefaultColors;
public uint32 mHiliteColor = 0xFF2f5c88;
public uint32 mUnfocusedHiliteColor = 0x00000000;
public int32 mRecalcSizeLineNum = -1;
public float mRecalcSizeCurMaxWidth = 0;
public bool mHasQueuedRecalcSize;
public int32 mTopCharId = -1;
public double mTopCharIdVertPos = -1;
public bool mWantsCheckScrollPosition;
public uint32 mViewWhiteSpaceColor;
public bool mScrollToStartOnLostFocus;
protected static uint32[] sDefaultColors = new uint32[] { Color.White } ~ delete _;
public this(EditWidgetContent refContent = null) : base(refContent)
{
//mTextInsets.Set(-3, 2, 0, 2);
//mTextInsets.Set(GS!(-3), GS!(2), 0, GS!(2));
mTextInsets.Set(GS!(0), GS!(2), 0, GS!(2));
mWidth = GS!(100);
mHeight = GS!(24);
mHorzJumpSize = GS!(40);
mFont = DarkTheme.sDarkTheme.mSmallFont;
}
public override void GetTextData()
{
// Generate text flags if we need to...
if ((mData.mTextFlags == null) && (mWordWrap))
{
scope AutoBeefPerf("DEWC.GetTextData");
mData.mTextFlags = new uint8[mData.mTextLength + 1];
int32 lineIdx = 0;
int32 lineStartIdx = 0;
String lineCheck = scope String();
for (int32 i = 0; i < mData.mTextLength; i++)
{
char8 c = (char8)mData.mText[i].mChar;
lineCheck.Clear();
if (c == '\n')
ExtractString(lineStartIdx, i - lineStartIdx, lineCheck);
else if (i == mData.mTextLength - 1)
ExtractString(lineStartIdx, i - lineStartIdx + 1, lineCheck);
if (lineCheck.Length > 0)
{
String lineCheckLeft = scope String();
lineCheckLeft.Reference(lineCheck);
while (true)
{
int32 maxChars = GetTabbedCharCountToLength(lineCheckLeft, mEditWidget.mScrollContentContainer.mWidth - mTextInsets.mLeft - mTextInsets.mRight);
if (maxChars == 0)
maxChars = 1;
if (maxChars >= lineCheckLeft.Length)
break;
int32 checkIdx = maxChars;
while ((checkIdx > 0) && (!lineCheckLeft[checkIdx].IsWhiteSpace))
checkIdx--;
if (checkIdx == 0)
checkIdx = maxChars - 1;
mData.mTextFlags[lineStartIdx + checkIdx + 1] |= (int32)TextFlags.Wrap;
lineStartIdx += checkIdx + 1;
//lineCheck.Remove(0, checkIdx + 1);
lineCheckLeft.AdjustPtr(checkIdx + 1);
}
}
if (c == '\n')
{
lineStartIdx = i + 1;
lineIdx++;
}
}
}
base.GetTextData();
}
protected override void AdjustCursorsAfterExternalEdit(int index, int ofs)
{
base.AdjustCursorsAfterExternalEdit(index, ofs);
mWantsCheckScrollPosition = true;
}
public float GetTabbedPos(float startX)
{
float spaceWidth = mFont.GetWidth((char32)' ');
if (mTabSize == 0)
return startX + spaceWidth;
return (float)Math.Truncate((startX + spaceWidth) / mTabSize + 0.999f) * mTabSize;
}
static mixin GetTabSection(var origString, var stringLeft, var subStr)
{
int32 tabIdx = (int32)stringLeft.IndexOf('\t');
if (tabIdx == -1)
break;
if (subStr == null)
{
subStr = scope:: String(stringLeft, 0, tabIdx);
stringLeft = scope:: String(origString, tabIdx + 1);
}
else
{
subStr.Clear();
subStr.Append(stringLeft, 0, tabIdx);
stringLeft.Remove(0, tabIdx + 1);
}
tabIdx
}
public float DoDrawText(Graphics g, String origString, float x, float y)
{
String stringLeft = origString;
float aX = x;
float aY = y;
void DrawString(String str, float x, float y)
{
if (str.Length == 0)
return;
g.DrawString(str, x, y);
if (mViewWhiteSpaceColor != 0)
{
let prevColor = g.mColor;
g.PopColor();
g.PushColor(mViewWhiteSpaceColor);
float curX = x;
int lastNonSpace = 0;
for (int i < str.Length)
{
char8 c = str[i];
if (c == ' ')
{
// Flush length
if (lastNonSpace < i)
{
var contentStr = scope String();
contentStr.Reference(str.Ptr + lastNonSpace, i - lastNonSpace);
curX += mFont.GetWidth(contentStr);
}
g.DrawString("·", curX, y);
curX += mFont.GetWidth(' ');
lastNonSpace = i + 1;
}
}
g.PopColor();
g.PushColorOverride(prevColor);
}
}
String subStr = null;
while (true)
{
GetTabSection!(origString, stringLeft, subStr);
if (g != null)
DrawString(subStr, aX, aY);
aX += mFont.GetWidth(subStr);
if ((mViewWhiteSpaceColor != 0) && (g != null))
{
let prevColor = g.mColor;
g.PopColor();
g.PushColor(mViewWhiteSpaceColor);
g.DrawString("→", aX, y);
g.PopColor();
g.PushColorOverride(prevColor);
}
aX = GetTabbedPos(aX);
}
if (g != null)
DrawString(stringLeft, aX, aY);
//TODO: This is just an "emergency dropout", remove when we optimize more?
/*if ((mX + x >= 0) && (stringLeft.Length > 1000))
{
return aX + 10000;
}*/
aX += mFont.GetWidth(stringLeft);
return aX;
}
/*public int GetTabbedCharCountToLength(String origString, float len)
{
String stringLeft = origString;
float aX = 0;
int idx = 0;
String subStr = null;
while (true)
{
int tabIdx = GetTabSection!(origString, stringLeft, subStr);
int char8Count = mFont.GetCharCountToLength(subStr, len - aX);
if (char8Count < subStr.Length)
return idx + char8Count;
idx += tabIdx + 1;
aX += mFont.GetWidth(subStr);
float prevX = aX;
aX = GetTabbedPos(aX);
if (len < aX)
return idx - 1;
}
return idx + mFont.GetCharCountToLength(stringLeft, len - aX);
}*/
public int32 GetTabbedCharCountToLength(String origString, float len)
{
float aX = 0;
int32 idx = 0;
String subStr = scope String();
subStr.Reference(origString);
while (true)
{
bool hitTabStop = false;
int32 char8Count = (int32)mFont.GetCharCountToLength(subStr, len - aX, &hitTabStop);
if (!hitTabStop)
return idx + char8Count;
aX += mFont.GetWidth(StringView(subStr, 0, char8Count));
aX = GetTabbedPos(aX);
if (aX > len + 0.001f)
return idx + char8Count;
idx += char8Count + 1;
subStr.AdjustPtr(char8Count + 1);
}
}
public virtual void DrawSectionFlagsOver(Graphics g, float x, float y, float width, uint8 flags)
{
}
public float GetTabbedWidth(String origString, float x, bool forceAccurate = false)
{
String stringLeft = origString;
float aX = x;
String subStr = null;
while (true)
{
#unwarn
int32 tabIdx = GetTabSection!(origString, stringLeft, subStr);
aX += mFont.GetWidth(subStr);
aX = GetTabbedPos(aX);
}
//TODO: This is just an "emergency dropout", remove when we optimize more?
/*if ((!forceAccurate) && (mX + x >= 0) && (stringLeft.Length > 1000))
{
return aX + 10000;
}*/
return aX + mFont.GetWidth(stringLeft);
}
public void SetFont(Font font, bool isMonospace, bool virtualCursor)
{
mFont = font;
if (isMonospace)
{
mCharWidth = mFont.GetWidth((char32)' ');
//Debug.Assert(mFont.GetWidth((char32)'W') == mCharWidth);
if (mTabSize == 0)
mTabSize = mTabLength * mCharWidth;
else
mTabSize = (float)Math.Round(mTabSize / mCharWidth) * mCharWidth;
}
else
mCharWidth = -1;
if (virtualCursor)
Debug.Assert(isMonospace);
mAllowVirtualCursor = virtualCursor;
}
public override void RehupScale(float oldScale, float newScale)
{
base.RehupScale(oldScale, newScale);
Utils.RoundScale(ref mTabSize, newScale / oldScale);
SetFont(mFont, mCharWidth != -1, mAllowVirtualCursor);
mContentChanged = true; // Defer calling of RecalcSize
}
public virtual float DrawText(Graphics g, String str, float x, float y, uint16 typeIdAndFlags)
{
using (g.PushColor(mTextColors[typeIdAndFlags & 0xFF]))
return DoDrawText(g, str, x, y);
}
public virtual uint32 GetSelectionColor(uint8 flags)
{
return mEditWidget.mHasFocus ? mHiliteColor : mUnfocusedHiliteColor;
}
public override void Draw(Graphics g)
{
base.Draw(g);
#unwarn
int lineCount = GetLineCount();
float lineSpacing = GetLineHeight(0);
g.SetFont(mFont);
float offsetY = mTextInsets.mTop;
if (mHeight < lineSpacing)
offsetY = (mHeight - lineSpacing) * 0.75f;
g.PushTranslate(mTextInsets.mLeft, offsetY);
int selStartLine = -1;
int selStartCharIdx = -1;
int selEndLine = -1;
int selEndCharIdx = -1;
int selStartIdx = -1;
int selEndIdx = -1;
if (mSelection != null)
{
mSelection.Value.GetAsForwardSelect(out selStartIdx, out selEndIdx);
GetLineCharAtIdx(selStartIdx, out selStartLine, out selStartCharIdx);
GetLineCharAtIdx(selEndIdx, out selEndLine, out selEndCharIdx);
}
int firstLine;
int firstCharIdx;
float overflowX;
GetLineCharAtCoord(0, -mY, out firstLine, out firstCharIdx, out overflowX);
int lastLine;
int lastCharIdx;
float lastOverflowX;
GetLineCharAtCoord(0, -mY + mEditWidget.mScrollContentContainer.mHeight, out lastLine, out lastCharIdx, out lastOverflowX);
bool drewCursor = false;
String sectionText = scope String(256);
for (int lineIdx = firstLine; lineIdx <= lastLine; lineIdx++)
{
//string lineText = GetLineText(lineIdx);
int lineStart;
int lineEnd;
GetLinePosition(lineIdx, out lineStart, out lineEnd);
int lineDrawStart = lineStart;
float curX = 0;
float curY = lineIdx * lineSpacing;
while (true)
{
int lineDrawEnd = lineDrawStart;
uint16 curTypeIdAndFlags = *(uint16*)&mData.mText[lineDrawStart].mDisplayTypeId;
// Check for transition of curTypeIdAndFlags - colors ignore whitespace, but if flags are set then we need
// to be exact
/*while ((lineDrawEnd < lineEnd) && ((*(uint16*)&mData.mText[lineDrawEnd].mDisplayTypeId == curTypeIdAndFlags) ||
((curTypeIdAndFlags < 0x100) && (((char8)mData.mText[lineDrawEnd].mChar).IsWhiteSpace))))
lineDrawEnd++;*/
while (true)
{
var checkEnd = ref mData.mText[lineDrawEnd];
if ((lineDrawEnd < lineEnd) && ((*(uint16*)&checkEnd.mDisplayTypeId == curTypeIdAndFlags) ||
((curTypeIdAndFlags < 0x100) && (checkEnd.mChar.IsWhiteSpace) && (checkEnd.mDisplayFlags == 0))))
lineDrawEnd++;
else
break;
}
sectionText.Clear();
ExtractString(lineDrawStart, lineDrawEnd - lineDrawStart, sectionText);
int selStart = Math.Max(0, selStartIdx - lineDrawStart);
int selEnd = Math.Min(lineDrawEnd - lineDrawStart, selEndIdx - lineDrawStart);
uint8 flags = (uint8)(curTypeIdAndFlags >> 8);
if ((lineDrawStart >= selStartIdx) && (lineDrawEnd < selEndIdx) && (lineDrawEnd == lineDrawStart))
{
// Blank line selected
using (g.PushColor(GetSelectionColor(flags)))
g.FillRect(curX, curY, 4, lineSpacing);
}
if (selEnd > selStart)
{
String selPrevString = scope String(selStart);
selPrevString.Append(sectionText, 0, selStart);
String selIncludeString = scope String(selEnd);
selIncludeString.Append(sectionText, 0, selEnd);
float selStartX = GetTabbedWidth(selPrevString, curX);
float selEndX = GetTabbedWidth(selIncludeString, curX);
if (lineIdx != selEndLine)
selEndX += mFont.GetWidth((char32)' ');
using (g.PushColor(GetSelectionColor(flags)))
g.FillRect(selStartX, curY, selEndX - selStartX, lineSpacing);
}
float nextX = curX;
nextX = DrawText(g, sectionText, curX, curY, curTypeIdAndFlags);
DrawSectionFlagsOver(g, curX, curY, nextX - curX, flags);
//int32 lineDrawStartColumn = lineDrawStart - lineStart;
//int32 lineDrawEndColumn = lineDrawEnd - lineStart;
if ((mEditWidget.mHasFocus) && (!drewCursor))
{
float aX = -1;
if (mVirtualCursorPos != null)
{
if ((lineIdx == mVirtualCursorPos.Value.mLine) && (lineDrawEnd == lineEnd))
{
aX = mVirtualCursorPos.Value.mColumn * mCharWidth;
}
}
else if (mCursorTextPos >= lineDrawStart)
{
bool isInside = mCursorTextPos < lineDrawEnd;
if ((mCursorTextPos == lineDrawEnd) && (lineDrawEnd == lineEnd))
{
if (lineDrawEnd == mData.mTextLength)
isInside = true;
if (mWordWrap)
{
if ((mShowCursorAtLineEnd) || (lineEnd >= mData.mTextFlags.Count) || (mData.mTextFlags[lineEnd] & (int32)TextFlags.Wrap) == 0)
isInside = true;
}
else
isInside = true;
}
if (isInside)
{
String subText = scope String(mCursorTextPos - lineDrawStart);
subText.Append(sectionText, 0, mCursorTextPos - lineDrawStart);
aX = GetTabbedWidth(subText, curX);
}
}
if (aX != -1)
{
float brightness = (float)Math.Cos(Math.Max(0.0f, mCursorBlinkTicks - 20) / 9.0f);
brightness = Math.Max(0, Math.Min(1.0f, brightness * 2.0f + 1.6f));
if (mEditWidget.mVertPos.IsMoving)
brightness = 0; // When we animate a pgup or pgdn, it's weird seeing the cursor scrolling around
if (mOverTypeMode)
{
if (mCharWidth <= 2)
{
using (g.PushColor(Color.Get(brightness * 0.75f)))
g.FillRect(aX, curY, GS!(2), lineSpacing);
}
else
{
using (g.PushColor(Color.Get(brightness * 0.30f)))
g.FillRect(aX, curY, mCharWidth, lineSpacing);
}
}
else
{
using (g.PushColor(Color.Get(brightness)))
g.FillRect(aX, curY, Math.Max(1.0f, GS!(1)), lineSpacing);
}
drewCursor = true;
}
}
lineDrawStart = lineDrawEnd;
curX = nextX;
if (lineDrawStart >= lineEnd)
break;
}
}
g.PopMatrix();
/*using (g.PushColor(0x4000FF00))
g.FillRect(-8, -8, mWidth + 16, mHeight + 16);*/
/*if (mDbgX != -1)
g.FillRect(mDbgX - 1, mDbgY - 1, 3, 3);*/
}
public override void AddWidget(Widget widget)
{
base.AddWidget(widget);
}
public override bool AllowChar(char32 theChar)
{
if ((int)theChar < 32)
return (theChar == '\n') || (mIsMultiline && (theChar == '\t'));
return mFont.HasChar(theChar);
}
public override void InsertAtCursor(String theString, InsertFlags insertFlags)
{
scope AutoBeefPerf("DarkEditWidgetContent.InsertAtCursor");
base.InsertAtCursor(theString, insertFlags);
}
public override void GetTextCoordAtLineChar(int line, int lineChar, out float x, out float y)
{
String lineText = scope String(256);
GetLineText(line, lineText);
if (lineChar > lineText.Length)
x = GetTabbedWidth(lineText, 0) + (mFont.GetWidth((char32)' ') * (lineChar - (int32)lineText.Length)) + mTextInsets.mLeft;
else
{
String subText = scope String(Math.Min(lineChar, 256));
subText.Append(lineText, 0, lineChar);
x = GetTabbedWidth(subText, 0, true) + mTextInsets.mLeft;
}
y = mTextInsets.mTop + line * mFont.GetLineSpacing();
}
public override void GetTextCoordAtLineAndColumn(int line, int column, out float x, out float y)
{
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();
}
public override bool GetLineCharAtCoord(float x, float y, out int line, out int char8Idx, out float overflowX)
{
line = (int) ((y - mTextInsets.mTop) / mFont.GetLineSpacing() + 0.001f);
int lineCount = GetLineCount();
if (line < 0)
line = 0;
if (line >= lineCount)
line = lineCount - 1;
String lineText = scope String(256);
GetLineText(line, lineText);
int32 char8Count = GetTabbedCharCountToLength(lineText, x - mTextInsets.mLeft);
char8Idx = char8Count;
if (char8Count < lineText.Length)
{
String subString = scope String(char8Count);
subString.Append(lineText, 0, char8Count);
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
{
checkCharWidth = mFont.GetWidth(c) * 0.5f;
}
if (x >= subWidth + mTextInsets.mLeft + checkCharWidth)
char8Idx = (int32)utf8enumerator.NextIndex;
}
}
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;
}
void RecalcSize(int32 startLineNum, int32 endLineNum, bool forceAccurate = false)
{
scope AutoBeefPerf("DEWC.RecalcSize");
String line = scope String();
for (int32 lineIdx = startLineNum; lineIdx < endLineNum; lineIdx++)
{
line.Clear();
GetLineText(lineIdx, line);
mRecalcSizeCurMaxWidth = Math.Max(mRecalcSizeCurMaxWidth, GetTabbedWidth(line, 0, forceAccurate) + mHorzJumpSize);
Debug.Assert(!mRecalcSizeCurMaxWidth.IsNaN);
}
}
public override void CursorToLineEnd()
{
//int32 line;
//int32 lineChar;
//GetCursorLineChar(out line, out lineChar);
/*RecalcSize(line, line + 1, true);
if (mRecalcSizeCurMaxWidth > mWidth)
{
mRecalcSizeLineNum = -1;
}*/
mRecalcSizeLineNum = -1;
RecalcSize(true);
base.CursorToLineEnd();
}
public void RecalcSize(bool forceAccurate = false)
{
mMaximalScrollAddedHeight = 0;
if (mRecalcSizeLineNum == -1)
{
mRecalcSizeCurMaxWidth = 0;
mHasQueuedRecalcSize = false;
}
else // We need to recalc again after our current pass
mHasQueuedRecalcSize = true;
if (!mIsReadOnly)
{
float cursorX;
float cursorY;
GetTextCoordAtCursor(out cursorX, out cursorY);
mRecalcSizeCurMaxWidth = Math.Max(mRecalcSizeCurMaxWidth, cursorX + mHorzJumpSize);
}
if (mUpdateCnt == 0)
{
RecalcSize(0, GetLineCount());
mWidth = mRecalcSizeCurMaxWidth + mTextInsets.mLeft + mTextInsets.mRight;
Debug.Assert(!mWidth.IsNaN);
}
else if (mRecalcSizeLineNum == -1)
{
mRecalcSizeLineNum = 0;
// The actual recalculation will take 16 ticks so just make sure we have enough width for
// the current line for now
var lineAndCol = CursorLineAndColumn;
RecalcSize(lineAndCol.mLine, lineAndCol.mLine + 1, forceAccurate);
mWidth = Math.Max(mWidth, mRecalcSizeCurMaxWidth + mTextInsets.mLeft + mTextInsets.mRight);
Debug.Assert(!mWidth.IsNaN);
}
mHeight = GetLineCount() * mFont.GetLineSpacing() + mTextInsets.mTop + mTextInsets.mBottom;
UpdateMaximalScroll();
base.RecalcSize();
}
public override void RecalcSize()
{
RecalcSize(false);
}
public override void ContentChanged()
{
base.ContentChanged();
mRecalcSizeLineNum = -1;
}
public override void TextAppended(String str)
{
if ((mData.mLineStarts != null) && (mIsReadOnly))
{
int32 recalcSizeLineNum = Math.Max((int32)mData.mLineStarts.Count - 2, 0);
if ((mRecalcSizeLineNum == -1) || (recalcSizeLineNum < mRecalcSizeLineNum))
mRecalcSizeLineNum = recalcSizeLineNum;
}
base.TextAppended(str);
}
void UpdateMaximalScroll()
{
if (mAllowMaximalScroll)
{
let prevHeight = mHeight;
mHeight -= mMaximalScrollAddedHeight;
mMaximalScrollAddedHeight = mEditWidget.mScrollContentContainer.mHeight - mFont.GetLineSpacing();
mHeight += mMaximalScrollAddedHeight;
if (mHeight != prevHeight)
mEditWidget.UpdateScrollbars();
}
}
public override void Resize(float x, float y, float width, float height)
{
base.Resize(x, y, width, height);
UpdateMaximalScroll();
}
public override float GetLineHeight(int line)
{
return mFont.GetLineSpacing();
}
public override float GetPageScrollTextHeight()
{
float numLinesVisible = mEditWidget.mScrollContentContainer.mHeight / mFont.GetLineSpacing();
if (numLinesVisible - (int32)numLinesVisible < 0.90f)
numLinesVisible = (int32) numLinesVisible;
float val = numLinesVisible * mFont.GetLineSpacing();
if (val <= 0)
return base.GetPageScrollTextHeight();
return val;
}
public void CheckRecordScrollTop()
{
if (mWantsCheckScrollPosition)
{
if (mTopCharId != -1)
{
int textIdx = mData.mTextIdData.GetPrepared().GetIndexFromId(mTopCharId);
if (textIdx != -1)
{
int line;
int lineChar;
GetLineCharAtIdx(textIdx, out line, out lineChar);
var vertPos = mEditWidget.mVertPos.mDest;
var offset = vertPos % mFont.GetLineSpacing();
mEditWidget.mVertScrollbar.ScrollTo(line * mFont.GetLineSpacing() + offset);
}
else
{
mTopCharId = -1;
}
}
mWantsCheckScrollPosition = false;
}
if (mEditWidget.mHasFocus)
{
mTopCharId = -1;
}
else
{
var vertPos = mEditWidget.mVertPos.mDest;
if ((mTopCharId == -1) || (mTopCharIdVertPos != vertPos))
{
float lineNum = (float)(vertPos / mFont.GetLineSpacing());
int lineStart;
int lineEnd;
GetLinePosition((int32)lineNum, out lineStart, out lineEnd);
int idAtStart = mData.mTextIdData.GetIdAtIndex((int32)lineStart);
if (idAtStart == -1)
idAtStart = 0;
mTopCharId = (int32)idAtStart;
mTopCharIdVertPos = vertPos;
}
}
}
public override void Update()
{
base.Update();
if ((mRecalcSizeLineNum != -1) && (BFApp.sApp.mIsUpdateBatchStart))
{
int32 lineCount = GetLineCount();
int32 toLine = Math.Min(lineCount, mRecalcSizeLineNum + Math.Max(1, lineCount / 16) + 80);
RecalcSize(mRecalcSizeLineNum, toLine);
if (toLine == lineCount)
{
mRecalcSizeLineNum = -1;
mWidth = mRecalcSizeCurMaxWidth + mTextInsets.mLeft + mTextInsets.mRight;
base.RecalcSize();
}
else
mRecalcSizeLineNum = toLine;
}
if ((mRecalcSizeLineNum == -1) && (mHasQueuedRecalcSize))
RecalcSize();
CheckRecordScrollTop();
}
}
public class DarkEditWidget : EditWidget
{
public bool mDrawBox = true;
public this(DarkEditWidgetContent content = null)
{
mEditWidgetContent = content;
if (mEditWidgetContent == null)
mEditWidgetContent = new DarkEditWidgetContent();
mEditWidgetContent.mEditWidget = this;
mScrollContent = mEditWidgetContent;
mScrollContentContainer.AddWidget(mEditWidgetContent);
SetupInsets();
//mScrollbarInsets.Set(18, 1, 0, 0);
mHorzPos.mSpeed = 0.2f;
mVertPos.mSpeed = 0.2f;
mScrollbarBaseContentSizeOffset = GS!(3);
}
protected virtual void SetupInsets()
{
mScrollContentInsets.Set(GS!(3), GS!(3), GS!(3), GS!(3));
}
protected override void HandleWindowMouseDown(Beefy.events.MouseEvent event)
{
base.HandleWindowMouseDown(event);
// If we got closed as part of this click, don't propagate the click through
if (mParent == null)
{
event.mHandled = true;
}
}
public override void RehupScale(float oldScale, float newScale)
{
base.RehupScale(oldScale, newScale);
SetupInsets();
}
public override void DefaultDesignInit()
{
base.DefaultDesignInit();
mWidth = GS!(80);
mHeight = GS!(20);
SetText("Edit Text");
}
public override void InitScrollbars(bool wantHorz, bool wantVert)
{
SetupInsets();
base.InitScrollbars(wantHorz, wantVert);
float scrollIncrement = ((DarkEditWidgetContent) mEditWidgetContent).mFont.GetLineSpacing() * GS!(3);
if (mHorzScrollbar != null)
mHorzScrollbar.mScrollIncrement = scrollIncrement;
if (mVertScrollbar != null)
mVertScrollbar.mScrollIncrement = scrollIncrement;
}
public override void Draw(Graphics g)
{
base.Draw(g);
if (mDrawBox)
{
g.DrawBox(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.EditBox), 0, 0, mWidth, mHeight);
if (mHasFocus)
{
using (g.PushColor(DarkTheme.COLOR_SELECTED_OUTLINE))
g.DrawBox(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.Outline), 0, 0, mWidth, mHeight);
}
}
/*using (g.PushColor(0x40FF0000))
g.FillRect(0, 0, mWidth, mHeight);*/
}
public override void LostFocus()
{
base.LostFocus();
var darkEditWidgetContent = (DarkEditWidgetContent)mEditWidgetContent;
darkEditWidgetContent.CheckRecordScrollTop();
if (darkEditWidgetContent.mScrollToStartOnLostFocus)
HorzScrollTo(0);
}
}
}

View file

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Text;
using Beefy.widgets;
using Beefy.gfx;
namespace Beefy.theme.dark
{
public class DarkIconButton : ButtonWidget
{
public Image mIcon;
public float mIconOfsX;
public float mIconOfsY;
public override void Draw(Graphics g)
{
base.Draw(g);
g.Draw(mIcon, mIconOfsX, mIconOfsY);
}
}
}

View file

@ -0,0 +1,160 @@
using System;
using System.Collections.Generic;
using System.Text;
using Beefy.gfx;
using Beefy.widgets;
using Beefy.geom;
namespace Beefy.theme.dark
{
public class DarkInfiniteScrollbar : InfiniteScrollbar
{
/*public class DarkThumb : InfiniteScrollbar.Thumb
{
public override void Draw(Graphics g)
{
base.Draw(g);
DarkInfiniteScrollbar scrollbar = (DarkInfiniteScrollbar)mScrollbar;
g.DrawButtonVert(scrollbar.GetImage((mMouseOver || mMouseDown) ? DarkTheme.ImageIdx.ScrollbarThumb : DarkTheme.ImageIdx.ScrollbarThumbOver), 0, 0, mHeight);
}
}*/
public class DarkArrow : InfiniteScrollbar.Arrow
{
public this(bool isBack)
{
mBack = isBack;
}
public override void Draw(Graphics g)
{
base.Draw(g);
DarkInfiniteScrollbar scrollbar = (DarkInfiniteScrollbar)mScrollbar;
if (mMouseOver && mMouseDown)
g.Draw(scrollbar.GetImage(DarkTheme.ImageIdx.ShortButtonDown));
else if (mMouseOver)
g.Draw(scrollbar.GetImage(DarkTheme.ImageIdx.ShortButton));
if (mBack)
{
g.PushScale(1, -1);
g.Draw(scrollbar.GetImage(DarkTheme.ImageIdx.ScrollbarArrow), 0, -DarkTheme.sUnitSize);
g.PopMatrix();
}
else
{
g.Draw(scrollbar.GetImage(DarkTheme.ImageIdx.ScrollbarArrow));
}
}
}
public override void RehupScale(float oldScale, float newScale)
{
mBaseSize = DarkTheme.sUnitSize - 1;
base.RehupScale(oldScale, newScale);
}
public this()
{
mBaseSize = DarkTheme.sUnitSize - 1;
mDualBarSizeOffset = -2;
/*mThumb = new DarkThumb();
mThumb.mScrollbar = this;
AddWidget(mThumb);*/
mStartArrow = new DarkArrow(true);
mStartArrow.mScrollbar = this;
AddWidget(mStartArrow);
mEndArrow = new DarkArrow(false);
mEndArrow.mScrollbar = this;
AddWidget(mEndArrow);
}
public Image GetImage(DarkTheme.ImageIdx image)
{
return DarkTheme.sDarkTheme.GetImage((DarkTheme.ImageIdx)((int32)image + (int32)DarkTheme.ImageIdx.VertScrollbar - (int32)DarkTheme.ImageIdx.Scrollbar));
}
public override Beefy.geom.Rect GetThumbPos()
{
float btnMargin = GS!(18);
float sizeLeft = (mHeight - btnMargin * 2);
float pagePct = 0.125f;
float thumbSize = Math.Min(sizeLeft, Math.Max(GS!(16), sizeLeft * pagePct));
bool wasNeg = mScrollThumbFrac < 0;
float trackPct = (float)Math.Pow(Math.Abs(mScrollThumbFrac) * 1000, 0.5f) / 200;
if (wasNeg)
trackPct = 0.5f - trackPct;
else
trackPct = 0.5f + trackPct;
float thumbPos = Math.Clamp(btnMargin + trackPct*sizeLeft - thumbSize/2, btnMargin, mHeight - btnMargin - thumbSize);
return Rect(0, thumbPos, mWidth, thumbSize);
}
public override void Draw(Graphics g)
{
base.Draw(g);
g.DrawButtonVert(GetImage(DarkTheme.ImageIdx.Scrollbar), 0, 0, mHeight);
let thumbPos = GetThumbPos();
bool isOver = mMouseOver && thumbPos.Contains(mLastMouseX, mLastMouseY);
using (g.PushColor(0x40000000))
{
float y0 = mHeight / 2;
float y1 = thumbPos.mY + thumbPos.mHeight / 2;
g.FillRect(GS!(6), Math.Min(y0, y1), mWidth - GS!(12), Math.Abs(y0 - y1));
}
g.DrawButtonVert(GetImage(isOver ? DarkTheme.ImageIdx.ScrollbarThumb : DarkTheme.ImageIdx.ScrollbarThumbOver), 0, thumbPos.mY, thumbPos.mHeight);
//mThumb.mVisible = true;
//mThumb.Resize(0, btnMargin + thumbPos - (int)(thumbSize*0.5f), mBaseSize, thumbSize);
}
public override float GetAccelFracAt(float dx, float dy)
{
float btnMargin = GS!(18);
float sizeLeft = (mHeight - btnMargin * 2);
float trackSize = sizeLeft - GS!(20);
return dy / trackSize * 2.0f;
}
public override void ResizeContent()
{
mStartArrow.Resize(0, 0, mBaseSize, mBaseSize);
mEndArrow.Resize(0, mHeight - mBaseSize, mBaseSize, mBaseSize);
//float btnMargin = GS!(18);
//float sizeLeft = (mHeight - btnMargin * 2);
/*float pagePct = 0.125f;
float thumbSize = Math.Min(sizeLeft, Math.Max(GS!(12), sizeLeft * pagePct));
float trackSize = sizeLeft + 1; // sizeLeft - ((thumbSize) - (sizeLeft * pagePct)) + 1;
float trackPct = ((float)mScrollThumbFrac + 1.0f) * 0.5f;*/
//float thumbPos = Utils.Lerp(thumbSize * 0.5f, trackSize - (thumbSize * 0.5f), trackPct);
//mThumb.mVisible = true;
//mThumb.Resize(0, btnMargin + thumbPos - (int)(thumbSize*0.5f), mBaseSize, thumbSize);
}
public override void Resize(float x, float y, float width, float height)
{
base.Resize(x, y, width, height);
ResizeContent();
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,408 @@
using System;
using System.Collections.Generic;
using System.Text;
using Beefy.widgets;
using Beefy.gfx;
using System.Diagnostics;
namespace Beefy.theme.dark
{
public class DarkMenuItem : MenuItemWidget
{
int32 mSelectedTicks;
int32 mDeselectedTicks;
public this(Menu menuItem) : base(menuItem)
{
if (mMenuItem.mDisabled)
mMouseVisible = false;
}
public override void Draw(Graphics g)
{
base.Draw(g);
if (mMenuItem.mLabel == null)
g.DrawButton(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.MenuSepHorz), GS!(28), 0, mWidth - GS!(32));
else
{
let darkMenuWidget = (DarkMenuWidget)mMenuWidget;
g.SetFont(mMenuItem.mBold ? darkMenuWidget.mBoldFont : darkMenuWidget.mFont);
if (mMenuItem.mDisabled)
{
using (g.PushColor(0xFFA8A8A8))
g.DrawString(mMenuItem.mLabel, GS!(36), 0);
}
else
g.DrawString(mMenuItem.mLabel, GS!(36), 0);
if (mMenuItem.mIconImage != null)
g.Draw(mMenuItem.mIconImage, GS!(4), 0);
}
if (mMenuItem.mItems.Count > 0)
{
g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.RightArrow), mWidth - GS!(16), 0);
}
}
public void CloseSubMenu()
{
mSubMenu.Close();
}
void SubMenuClosed(Menu menu, Menu selectedItem)
{
mSubMenu.mMenu.mOnMenuClosed.Remove(scope => SubMenuClosed, true);
mSubMenu = null;
if ((!mMouseOver) && (mMenuWidget.mSelectIdx == mIndex))
mMenuWidget.SetSelection(-1);
if (selectedItem != null)
{
mMenuWidget.mItemSelected = (Menu)selectedItem;
mMenuWidget.Close();
}
}
public void OpenSubMenu(bool setFocus)
{
if (mWidgetWindow.mHasClosed)
return;
mMenuWidget.mOpeningSubMenu = true;
mSubMenu = new DarkMenuWidget(mMenuItem);
if (setFocus)
mSubMenu.mSelectIdx = 0;
else
mSubMenu.mWindowFlags = .ClientSized | .NoActivate | .DestAlpha | .FakeFocus;
mSubMenu.Init(mWidgetWindow.mRootWidget, mX + mWidth + GS!(10), mY);
mSubMenu.mMenu.mOnMenuClosed.Add(new => SubMenuClosed);
mSubMenu.mParentMenuItemWidget = this;
mMenuWidget.mOpeningSubMenu = false;
}
public override void MouseDown(float x, float y, int32 btn, int32 btnCount)
{
base.MouseDown(x, y, btn, btnCount);
if ((mMenuItem.mItems.Count > 0) && (mSubMenu == null))
{
OpenSubMenu(true);
}
}
public override void Update()
{
base.Update();
if (mMenuItem.mItems.Count > 0)
{
if (mIndex == mMenuWidget.mSelectIdx)
{
mSelectedTicks++;
mDeselectedTicks = 0;
if ((mSelectedTicks == 10) && (mSubMenu == null))
{
for (DarkMenuItem item in mMenuWidget.mItemWidgets)
{
if ((item != this) && (item.mSubMenu != null))
item.CloseSubMenu();
}
OpenSubMenu(false);
}
}
else
{
mDeselectedTicks++;
mSelectedTicks = 0;
if ((mDeselectedTicks > 20) && (mSubMenu != null))
CloseSubMenu();
}
}
}
public override void Submit()
{
mMenuWidget.mItemSelected = mMenuItem;
mMenuWidget.Close();
if (mMenuItem.mOnMenuItemSelected.HasListeners)
mMenuItem.mOnMenuItemSelected(mMenuItem);
}
public override void MouseUp(float x, float y, int32 btn)
{
if (mMenuItem.mItems.Count > 0)
return;
if ((btn == 0) && (mMouseOver))
{
Submit();
}
base.MouseUp(x, y, btn);
}
}
public class DarkMenuContainer : MenuContainer
{
public float mShowPct = 1.0f;
public float mDrawHeight;
public float mDrawY;
public void DrawSelf(Graphics g)
{
base.Draw(g);
using (g.PushColor(0x80000000))
g.DrawBox(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.DropShadow), GS!(2), mDrawY + GS!(2), mWidth - GS!(2), mDrawHeight - GS!(2));
base.Draw(g);
using (g.PushColor(0xFFFFFFFF))
g.DrawBox(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.Menu), 0, mDrawY, mWidth - GS!(8), mDrawHeight - GS!(8));
}
public override void DrawAll(Graphics g)
{
float toDeg = Math.PI_f * 0.4f;
float toVal = Math.Sin(toDeg);
float curvedOpenPct = Math.Min(1.0f, (float)Math.Sin(mShowPct * toDeg) / toVal + 0.01f);
mDrawHeight = Math.Min(curvedOpenPct * mHeight + GS!(20), mHeight);
if (mReverse)
{
mDrawY = mHeight - mDrawHeight;
}
else
{
mDrawY = 0;
}
using (g.PushClip(0, mDrawY, mWidth, mDrawHeight))
{
DrawSelf(g);
}
using (g.PushClip(0, mDrawY, mWidth, mDrawHeight - GS!(10)))
{
//using (g.PushColor(Color.Get(Math.Min(1.0f, curvedOpenPct * 1.5f))))
base.DrawAll(g);
}
}
public override void Update()
{
base.Update();
var darkMenuWidget = (DarkMenuWidget)mScrollContent;
if (mShowPct < 1.0f)
{
float openSpeed = 0.08f + (0.15f / (darkMenuWidget.mItemWidgets.Count + 1));
mShowPct = Math.Min(1.0f, mShowPct + openSpeed);
MarkDirty();
}
}
}
public class DarkMenuWidget : MenuWidget
{
public float mItemSpacing;
public Font mFont;
public Font mBoldFont;
public bool mHasDrawn;
public this(Menu menu) :
base(menu)
{
mFont = DarkTheme.sDarkTheme.mSmallFont;
mBoldFont = DarkTheme.sDarkTheme.mSmallBoldFont;
mItemSpacing = mFont.GetLineSpacing();
mWindowFlags |= BFWindow.Flags.DestAlpha;
mPopupInsets.Set(GS!(2), GS!(2), GS!(10), GS!(10));
}
public override MenuContainer CreateMenuContainer()
{
DarkMenuContainer menuContainer = new DarkMenuContainer();
menuContainer.mScrollbarInsets.Set(GS!(2), 0, GS!(10), GS!(10));
//menuContainer.mScrollContentInsets = new Insets(0, 0, 0, 0);
return menuContainer;
}
public override MenuItemWidget CreateMenuItemWidget(Menu menuItem)
{
return new DarkMenuItem(menuItem);
}
public override float GetReverseAdjust()
{
return GS!(10);
}
ScrollableWidget GetScrollableParent()
{
ScrollableWidget scrollableWidget = mParent as ScrollableWidget;
if (scrollableWidget != null)
return scrollableWidget;
if (mParent != null)
{
scrollableWidget = mParent.mParent as ScrollableWidget;
if (scrollableWidget != null)
return scrollableWidget;
}
return null;
}
public override void EnsureItemVisible(int itemIdx)
{
base.EnsureItemVisible(itemIdx);
if (itemIdx == -1)
return;
var item = mItemWidgets[itemIdx];
float aX;
float aY;
item.SelfToOtherTranslate(this, 0, 0, out aX, out aY);
float topInsets = 0;
float lineHeight = item.mHeight;
ScrollableWidget scrollableWidget = GetScrollableParent();
if (scrollableWidget == null)
return;
float bottomInset = GS!(8);
if (aY < scrollableWidget.mVertPos.mDest + topInsets)
{
float scrollPos = aY - topInsets;
scrollableWidget.VertScrollTo(scrollPos);
}
else if (aY + lineHeight + bottomInset >= scrollableWidget.mVertPos.mDest + scrollableWidget.mScrollContentContainer.mHeight)
{
float scrollPos = aY + lineHeight + bottomInset - scrollableWidget.mScrollContentContainer.mHeight;
scrollableWidget.VertScrollTo(scrollPos);
}
}
public override void Draw(Graphics g)
{
/*using (g.PushColor(0xFFFF0000))
g.FillRect(0, 0, mWidth, mHeight);*/
float mDrawHeight = mHeight;
g.DrawButtonVert(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.MenuSepVert), GS!(18), GS!(2), mDrawHeight - GS!(4));
g.SetFont(mFont);
for (int32 itemIdx = 0; itemIdx < mItemWidgets.Count; itemIdx++)
{
#unwarn
MenuItemWidget item = (MenuItemWidget)mItemWidgets[itemIdx];
float curY = GS!(2) + mItemSpacing * itemIdx;
if (itemIdx == mSelectIdx)
{
using (g.PushColor(DarkTheme.COLOR_MENU_FOCUSED))
g.DrawButton(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.MenuSelect), GS!(4), curY, mWidth - GS!(6));
}
//if (item.mMenu.mLabel != null)
//g.Draw(DarkTheme.mDarkTheme.GetImage(DarkTheme.ImageIdx.Check), 6, curY + 1);
}
}
public void SetShowPct(float showPct)
{
DarkMenuContainer darkMenuContainer = (DarkMenuContainer)mParent.mParent;
darkMenuContainer.mShowPct = showPct;
}
public override void MouseLeave()
{
base.MouseLeave();
mSelectIdx = -1;
}
public override void ResizeComponents()
{
float maxWidth = 0;
float curY = GS!(2);
for (MenuItemWidget item in mItemWidgets)
{
if (item.mMenuItem.mLabel != null)
maxWidth = Math.Max(maxWidth, mFont.GetWidth(item.mMenuItem.mLabel));
item.Resize(0, curY, mWidth - GS!(8), mItemSpacing);
item.mMouseInsets = new Insets(0, GS!(6), 0, 0);
curY += mItemSpacing;
}
}
public override void CalcSize()
{
float maxWidth = 0;
for (MenuItemWidget item in mItemWidgets)
{
if (item.mMenuItem.mLabel != null)
maxWidth = Math.Max(maxWidth, mFont.GetWidth(item.mMenuItem.mLabel));
}
mWidth = Math.Max(mWidth, GS!(25) + maxWidth + GS!(40));
mHeight = mMenu.mItems.Count * mItemSpacing + GS!(6);
}
public override void Update()
{
base.Update();
}
public override void KeyDown(KeyCode keyCode, bool isRepeat)
{
base.KeyDown(keyCode, isRepeat);
switch (keyCode)
{
case .Right:
if (mSelectIdx != -1)
{
var darkMenuItem = (DarkMenuItem)mItemWidgets[mSelectIdx];
if (darkMenuItem.mSubMenu == null)
{
if (darkMenuItem.mMenuItem.mItems.Count > 0)
darkMenuItem.OpenSubMenu(true);
}
else
{
mOpeningSubMenu = true;
darkMenuItem.mSubMenu.SetFocus();
if (darkMenuItem.mSubMenu.mSelectIdx == -1)
{
darkMenuItem.mSubMenu.mSelectIdx = 0;
darkMenuItem.mSubMenu.MarkDirty();
}
darkMenuItem.mSubMenu.mWidgetWindow.SetForeground();
mOpeningSubMenu = false;
}
}
case .Left:
if (mParentMenuItemWidget != null)
{
var darkMenuItem = (DarkMenuItem)mParentMenuItemWidget;
int32 selectIdx = darkMenuItem.mMenuWidget.mSelectIdx;
darkMenuItem.CloseSubMenu();
darkMenuItem.mMenuWidget.mSelectIdx = selectIdx;
}
default:
}
}
}
}

View file

@ -0,0 +1,192 @@
using System;
using System.Collections.Generic;
using System.Text;
using Beefy.gfx;
using Beefy.widgets;
namespace Beefy.theme.dark
{
public class DarkScrollbar : Scrollbar
{
public class DarkThumb : Scrollbar.Thumb
{
public override void Draw(Graphics g)
{
base.Draw(g);
DarkScrollbar scrollbar = (DarkScrollbar)mScrollbar;
if (mScrollbar.mOrientation == Scrollbar.Orientation.Horz)
g.DrawButton(scrollbar.GetImage((mMouseOver || mMouseDown) ? DarkTheme.ImageIdx.ScrollbarThumb : DarkTheme.ImageIdx.ScrollbarThumbOver), 0, 0, mWidth);
else
g.DrawButtonVert(scrollbar.GetImage((mMouseOver || mMouseDown) ? DarkTheme.ImageIdx.ScrollbarThumb : DarkTheme.ImageIdx.ScrollbarThumbOver), 0, 0, mHeight);
}
}
public class DarkArrow : Scrollbar.Arrow
{
public this(bool isBack)
{
mBack = isBack;
}
public override void Draw(Graphics g)
{
base.Draw(g);
DarkScrollbar scrollbar = (DarkScrollbar)mScrollbar;
if (mMouseOver && mMouseDown)
g.Draw(scrollbar.GetImage(DarkTheme.ImageIdx.ShortButtonDown));
else if (mMouseOver)
g.Draw(scrollbar.GetImage(DarkTheme.ImageIdx.ShortButton));
if (mBack)
{
if (mScrollbar.mOrientation == Scrollbar.Orientation.Horz)
{
g.PushScale(-1.0f, 1);
g.Draw(scrollbar.GetImage(DarkTheme.ImageIdx.ScrollbarArrow), -DarkTheme.sUnitSize, 0);
g.PopMatrix();
}
else
{
g.PushScale(1, -1);
g.Draw(scrollbar.GetImage(DarkTheme.ImageIdx.ScrollbarArrow), 0, -DarkTheme.sUnitSize);
g.PopMatrix();
}
}
else
{
g.Draw(scrollbar.GetImage(DarkTheme.ImageIdx.ScrollbarArrow));
}
}
}
public this()
{
mBaseSize = DarkTheme.sUnitSize - 1;
mDualBarSizeOffset = -2;
mThumb = new DarkThumb();
mThumb.mScrollbar = this;
AddWidget(mThumb);
mStartArrow = new DarkArrow(true);
mStartArrow.mScrollbar = this;
AddWidget(mStartArrow);
mEndArrow = new DarkArrow(false);
mEndArrow.mScrollbar = this;
AddWidget(mEndArrow);
}
public override void RehupScale(float oldScale, float newScale)
{
mBaseSize = DarkTheme.sUnitSize - 1;
base.RehupScale(oldScale, newScale);
}
public Image GetImage(DarkTheme.ImageIdx image)
{
if (mOrientation == Orientation.Horz)
return DarkTheme.sDarkTheme.GetImage(image);
else
return DarkTheme.sDarkTheme.GetImage((DarkTheme.ImageIdx)((int32)image + (int32)DarkTheme.ImageIdx.VertScrollbar - (int32)DarkTheme.ImageIdx.Scrollbar));
}
public override void Draw(Graphics g)
{
base.Draw(g);
if (mOrientation == Orientation.Horz)
{
g.DrawButton(GetImage(DarkTheme.ImageIdx.Scrollbar), 0, 0, mWidth);
}
else
{
g.DrawButtonVert(GetImage(DarkTheme.ImageIdx.Scrollbar), 0, 0, mHeight);
}
}
public override void DrawAll(Graphics g)
{
if ((mWidth <= 0) || (mHeight <= 0))
return;
base.DrawAll(g);
}
public override double GetContentPosAt(float x, float y)
{
float btnMargin = GS!(18);
float sizeLeft = (mOrientation == Orientation.Horz) ? (mWidth - btnMargin * 2) : (mHeight - btnMargin * 2);
if (mOrientation == Orientation.Horz)
{
float trackSize = sizeLeft - mThumb.mWidth;
float trackPct = (x - btnMargin) / trackSize;
double contentPos = (mContentSize - mPageSize) * trackPct;
return contentPos;
}
else
{
float trackSize = sizeLeft - mThumb.mHeight;
float trackPct = (y - btnMargin) / trackSize;
double contentPos = (mContentSize - mPageSize) * trackPct;
return contentPos;
}
}
public override void ResizeContent()
{
mStartArrow.Resize(0, 0, mBaseSize, mBaseSize);
if (mOrientation == Orientation.Horz)
mEndArrow.Resize(mWidth - mBaseSize, 0, mBaseSize, mBaseSize);
else
mEndArrow.Resize(0, mHeight - mBaseSize, mBaseSize, mBaseSize);
float btnMargin = GS!(18);
float sizeLeft = (mOrientation == Orientation.Horz) ? (mWidth - btnMargin * 2) : (mHeight - btnMargin * 2);
if ((mPageSize < mContentSize) && (mContentSize > 0))
{
double pagePct = mPageSize / mContentSize;
mThumb.mVisible = true;
if (sizeLeft >= GS!(12))
{
mThumb.mVisible = true;
mStartArrow.mVisible = true;
mEndArrow.mVisible = true;
}
else
{
mThumb.mVisible = false;
mStartArrow.mVisible = false;
mEndArrow.mVisible = false;
/*sizeLeft = 0;
sizeLeft = (mOrientation == Orientation.Horz) ? (mWidth) : (mHeight);
btnMargin = 0;*/
}
double thumbSize = Math.Max(Math.Round(Math.Min(sizeLeft, Math.Max(GS!(12), sizeLeft * pagePct))), 0);
double trackSize = Math.Max(sizeLeft - ((thumbSize) - (sizeLeft * pagePct)) + 1, 0);
if (mOrientation == Orientation.Horz)
mThumb.Resize((float)(btnMargin + (float)Math.Round(mContentPos / mContentSize * trackSize)), 0, (float)thumbSize, mBaseSize);
else
mThumb.Resize(0, (float)(btnMargin + (float)Math.Round(mContentPos / mContentSize * trackSize)), mBaseSize, (float)thumbSize);
}
else
{
mThumb.mVisible = false;
}
}
public override void Resize(float x, float y, float width, float height)
{
base.Resize(x, y, width, height);
ResizeContent();
}
}
}

View file

@ -0,0 +1,194 @@
using System;
using System.Collections.Generic;
using System.Text;
using Beefy;
using Beefy.widgets;
using Beefy.events;
using Beefy.gfx;
namespace Beefy.theme.dark
{
public class DarkSmartEdit : Widget
{
public String mText;
public bool mAllowEdit = true;
public Object mValue;
public List<Widget> mMoveWidgets;
public Action<DarkSmartEdit> mValueChangedAction;
bool mCancelingEdit;
float mMouseDownX;
float mMouseDownY;
Object mMouseDownValue;
bool mDidDragEdit;
EditWidget mCurEditWidget;
public void SetValue(float value)
{
mValue = value;
mText.AppendF("{0:0.0}", value);
UpdateWidth();
}
public void SetValue(String value)
{
mValue = value;
mText = value;
UpdateWidth();
}
public void SetDisplay(String value)
{
mText = value;
UpdateWidth();
}
public void NudgeMoveWidgets(float offset)
{
if (mMoveWidgets != null)
{
for (Widget moveWidget in mMoveWidgets)
moveWidget.mX += offset;
}
}
void UpdateWidth()
{
float newWidth;
if (mCurEditWidget != null)
newWidth = mCurEditWidget.mWidth + 2;
else
newWidth = DarkTheme.sDarkTheme.mSmallFont.GetWidth(mText);
float adjust = newWidth - mWidth;
NudgeMoveWidgets(adjust);
mWidth = newWidth;
}
public override void Draw(Graphics g)
{
base.Draw(g);
if (mText == null)
return;
g.SetFont(DarkTheme.sDarkTheme.mSmallFont);
if ((mMouseDown) && (mMouseDownValue is float))
{
g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.LeftArrowDisabled), -mX - 11, 0);
float endX = mWidth;
if (mMoveWidgets != null)
endX = mMoveWidgets[mMoveWidgets.Count - 1].mX + mMoveWidgets[mMoveWidgets.Count - 1].mWidth - mX;
g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.RightArrowDisabled), endX - 5, 0);
}
using (g.PushColor(0xFFCC9600))
g.DrawString(mText, 0, 0);
}
void HandleEditLostFocus(Widget widget)
{
EditWidget editWidget = (EditWidget)widget;
editWidget.mOnLostFocus.Remove(scope => HandleEditLostFocus, true);
editWidget.mOnSubmit.Remove(scope => HandleEditSubmit, true);
editWidget.mOnCancel.Remove(scope => HandleEditCancel, true);
if (!mCancelingEdit)
{
mText.Clear();
editWidget.GetText(mText);
if (mValue is float)
{
float aValue = 0;
aValue = float.Parse(mText);
SetValue(aValue);
if (mValueChangedAction != null)
mValueChangedAction(this);
}
}
editWidget.RemoveSelf();
mCurEditWidget = null;
UpdateWidth();
}
void HandleEditCancel(EditEvent theEvent)
{
mCancelingEdit = true;
HandleEditLostFocus((EditWidget)theEvent.mSender);
}
void HandleEditSubmit(EditEvent theEvent)
{
HandleEditLostFocus((EditWidget)theEvent.mSender);
}
public override void MouseDown(float x, float y, int32 btn, int32 btnCount)
{
base.MouseDown(x, y, btn, btnCount);
mMouseDownX = x;
mMouseDownY = y;
mMouseDownValue = mValue;
mDidDragEdit = false;
}
public override void MouseMove(float x, float y)
{
base.MouseMove(x, y);
if (mMouseDown)
{
if (mMouseDownValue is float)
{
float delta = (float)Math.Round(x - mMouseDownX + y - mMouseDownY);
// First drag must be at least 5 pixels to avoid accidentally dragging when trying to click
if ((mDidDragEdit) || (Math.Abs(delta) >= 4))
{
mDidDragEdit = true;
float aValue = (float)mMouseDownValue + delta;
SetValue(aValue);
if (mValueChangedAction != null)
mValueChangedAction(this);
}
}
}
}
public override void MouseUp(float x, float y, int32 btn)
{
base.MouseUp(x, y, btn);
if ((mMouseOver) && (!mDidDragEdit) && (mValue != null))
{
mCancelingEdit = false;
EditWidget editWidget = new DarkEditWidget();
String numString = scope String();
numString.AppendF("{0}", mValue);
editWidget.SetText(numString);
editWidget.Content.SelectAll();
float aX;
float aY;
SelfToParentTranslate(0, 0, out aX, out aY);
float width = mWidth + 8;
editWidget.Resize(aX, aY, width, 20);
mParent.AddWidget(editWidget);
editWidget.mOnLostFocus.Add(new => HandleEditLostFocus);
editWidget.mOnSubmit.Add(new => HandleEditSubmit);
editWidget.mOnCancel.Add(new => HandleEditCancel);
editWidget.SetFocus();
mCurEditWidget = editWidget;
UpdateWidth();
}
}
}
}

View file

@ -0,0 +1,755 @@
using System;
using System.Collections.Generic;
using System.Text;
using Beefy.widgets;
using Beefy.gfx;
using Beefy.events;
using System.Diagnostics;
namespace Beefy.theme.dark
{
public class DarkTabbedView : TabbedView
{
public DarkTabEnd mTabEnd;
public DarkTabButton mRightTab; // Shows between tabs and tabEnd
public float mLeftObscure;
float mAllowRightSpace;
float cMinTabSize = 5.0f;
public class DarkTabButtonClose : ButtonWidget
{
public override void Draw(Graphics g)
{
base.Draw(g);
if (mMouseOver)
{
using (g.PushColor(mMouseDown ? 0xFFFF0000 : Color.White))
g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.CloseOver), GS!(-4), GS!(-4));
}
else
g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.Close), GS!(-4), GS!(-4));
}
public override void MouseClicked(float x, float y, int32 btn)
{
base.MouseClicked(x, y, btn);
var tabButton = (DarkTabButton)mParent;
if (tabButton.mCloseClickedEvent.HasListeners)
tabButton.mCloseClickedEvent();
}
}
public class DarkTabButton : TabbedView.TabButton
{
public Action mForceCloseEvent;
public DarkTabButtonClose mCloseButton;
public bool mIsEndTab;
public bool mIsRightTab;
public float mObscuredDir; // <0 = left, >0 = right
public uint32 mTextColor = Color.White;
public float mTabWidthOffset = 30;
public this(bool isEnd = false)
{
mContentInsets.Set(0, GS!(1), GS!(1), GS!(1));
mIsEndTab = isEnd;
if (!mIsEndTab)
{
mCloseButton = new DarkTabButtonClose();
AddWidget(mCloseButton);
}
mDragHelper.mMinDownTicks = 15;
mDragHelper.mTriggerDist = GS!(2);
}
public override void RehupScale(float oldScale, float newScale)
{
base.RehupScale(oldScale, newScale);
mContentInsets.Set(0, GS!(1), GS!(1), GS!(1));
mDragHelper.mTriggerDist = GS!(2);
if (mLabel != null)
mWantWidth = DarkTheme.sDarkTheme.mSmallFont.GetWidth(mLabel) + GS!(mTabWidthOffset);
//mHeight = DarkTheme.sUnitSize;
}
public override void MouseDown(float x, float y, int32 btn, int32 btnCount)
{
base.MouseDown(x, y, btn, btnCount);
mDragHelper.mAllowDrag = mObscuredDir == 0;
}
public override void Resize(float x, float y, float width, float height)
{
base.Resize(x, y, width, height);
if (mCloseButton != null)
mCloseButton.Resize(mWidth - GS!(13), GS!(4), GS!(12), GS!(12));
}
public override void Draw(Graphics g)
{
base.Draw(g);
if (mIsEndTab)
return;
/*float drawWidth = mDrawWidth;
if (drawWidth == 0)
drawWidth = mWidth;*/
float drawWidth = mWidth;
Image image = null;
if (mIsActive)
image = (mMouseOver || mMouseDown) ? DarkTheme.sDarkTheme.mImages[(int32)DarkTheme.ImageIdx.TabActiveOver] : DarkTheme.sDarkTheme.mImages[(int32)DarkTheme.ImageIdx.TabActive];
else
image = ((!mIsEndTab) && (mMouseOver || mMouseDown)) ? DarkTheme.sDarkTheme.mImages[(int32)DarkTheme.ImageIdx.TabInactiveOver] : DarkTheme.sDarkTheme.mImages[(int32)DarkTheme.ImageIdx.TabInactive];
g.DrawButton(image, 0, 0, drawWidth + 1);
if ((mIsActive) && (DarkTheme.sScale != 1.0f))
{
// When scaling, we can end up with a subpixel we don't want
//using (g.PushColor(0xFFFF0000))
g.DrawButton(DarkTheme.sDarkTheme.mWindowTopImage, GS!(2), (float)Math.Ceiling(DarkTheme.sScale * (20)) - 1, drawWidth - GS!(4));
}
g.SetFont(DarkTheme.sDarkTheme.mSmallFont);
if ((mLabel != null) && (drawWidth > GS!(16)))
{
//using (g.PushColor(((DarkTabbedView)mParent).mTextColor))
using (g.PushColor(mTextColor))
{
float textWidth = g.mFont.GetWidth(mLabel);
float useWidth = mWidth - GS!(12)*2;
if (textWidth < useWidth)
g.DrawString(mLabel, GS!(9) + (useWidth - textWidth)/2, 0, .Left, useWidth, .Truncate);
else
g.DrawString(mLabel, GS!(12), 0, .Left, useWidth, .Truncate);
}
}
}
public override void DrawDockPreview(Graphics g)
{
using (g.PushTranslate(-mX - mDragHelper.mMouseDownX, -mY - mDragHelper.mMouseDownY))
{
if (IsTotalWindowContent())
((DarkTabbedView)mTabbedView).DrawDockPreview(g);
else
((DarkTabbedView)mTabbedView).DrawDockPreview(g, this);
}
}
}
public class DarkTabDock : ICustomDock
{
public TabbedView mTabbedView;
public bool mAlreadyContains;
public this(TabbedView tabbedView, bool alreadyContains)
{
mTabbedView = tabbedView;
mAlreadyContains = alreadyContains;
}
public void Draw(Graphics g)
{
if (!mAlreadyContains)
{
using (g.PushColor(0x60FFFFFF))
g.DrawBox(DarkTheme.sDarkTheme.mImages[(int32)DarkTheme.ImageIdx.WhiteCircle], mTabbedView.mX - GS!(6), mTabbedView.mY - GS!(6), mTabbedView.mWidth + GS!(6) * 2, GS!(32));
}
}
public void Dock(IDockable dockable)
{
dockable.Dock(mTabbedView.mParentDockingFrame, mTabbedView, DockingFrame.WidgetAlign.Inside);
}
}
public class DarkTabMenuButton : ButtonWidget
{
public override void Draw(Graphics g)
{
base.Draw(g);
if (mMouseOver || mMouseDown)
{
using (g.PushColor(0xFFF7A900))
g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.DropMenuButton), GS!(-4), GS!(-4));
}
else
g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.DropMenuButton), GS!(-4), GS!(-4));
}
}
public class DarkTabEnd : DarkTabButton
{
public DarkTabMenuButton mMenuButton;
public int32 mMenuClosedTick;
public this()
: base(true)
{
mMenuButton = new DarkTabMenuButton();
AddWidget(mMenuButton);
mMenuButton.mOnMouseDown.Add(new => MenuClicked);
}
void ShowMenu(float x, float y)
{
Menu menu = new Menu();
/*menu.AddItem("Item 1");
menu.AddItem("Item 2");
menu.AddItem();
menu.AddItem("Item 3");*/
var menuItem = menu.AddItem("Frame Type");
var subItem = menuItem.AddItem("Static");
subItem.mOnMenuItemSelected.Add(new (evt) =>
{
mTabbedView.mIsFillWidget = false;
mTabbedView.mSizePriority = 0;
mTabbedView.mRequestedWidth = mTabbedView.mWidth;
mTabbedView.GetRootDockingFrame().Rehup();
mTabbedView.GetRootDockingFrame().ResizeContent();
});
if (!mTabbedView.mIsFillWidget)
subItem.mIconImage = DarkTheme.sDarkTheme.GetImage(.Check);
subItem = menuItem.AddItem("Documents");
subItem.mOnMenuItemSelected.Add(new (evt) =>
{
/*for (var tabSibling in mTabbedView.mParentDockingFrame.mDockedWidgets)
{
tabSibling.mSizePriority = 0;
if (mTabbedView.mParentDockingFrame.mSplitType == .Horz)
tabSibling.mRequestedWidth = tabSibling.mWidth;
else
tabSibling.mRequestedHeight = tabSibling.mHeight;
}*/
mTabbedView.mIsFillWidget = true;
mTabbedView.GetRootDockingFrame().Rehup();
mTabbedView.GetRootDockingFrame().ResizeContent();
});
if (mTabbedView.mIsFillWidget)
subItem.mIconImage = DarkTheme.sDarkTheme.GetImage(.Check);
menuItem = menu.AddItem("Permanent");
if (!mTabbedView.mAutoClose)
menuItem.mIconImage = DarkTheme.sDarkTheme.GetImage(.Check);
menuItem.mOnMenuItemSelected.Add(new (evt) =>
{
mTabbedView.mAutoClose = !mTabbedView.mAutoClose;
});
menuItem = menu.AddItem("Close");
menuItem.mOnMenuItemSelected.Add(new (evt) =>
{
let prevAutoClose = mTabbedView.mAutoClose;
mTabbedView.mAutoClose = true;
var tabs = scope List<TabButton>();
for (var tab in mTabbedView.mTabs)
tabs.Add(tab);
if (tabs.IsEmpty)
{
if (var dockingFrame = mTabbedView.mParent as DockingFrame)
{
dockingFrame.RemoveDockedWidget(mTabbedView);
BFApp.sApp.DeferDelete(mTabbedView);
}
}
else
{
for (var tab in tabs)
tab.mCloseClickedEvent();
}
mTabbedView.mAutoClose = prevAutoClose;
});
menu.AddItem();
for (var tab in mTabbedView.mTabs)
{
menuItem = menu.AddItem(tab.mLabel);
menuItem.mOnMenuItemSelected.Add(new (selMenuItem) =>
{
TabbedView.TabButton activateTab = tab;
activateTab.Activate();
});
}
if (mTabbedView.mPopulateMenuEvent != null)
mTabbedView.mPopulateMenuEvent(menu);
if (menu.mItems.Count > 0)
{
MenuWidget menuWidget = DarkTheme.sDarkTheme.CreateMenuWidget(menu);
menuWidget.Init(this, x, y, true);
menuWidget.mWidgetWindow.mOnWindowClosed.Add(new => MenuClosed);
}
else
delete menu;
}
void MenuClicked(MouseEvent theEvent)
{
if (mMenuClosedTick != mUpdateCnt)
{
ShowMenu(mMenuButton.mX + GS!(14), mMenuButton.mY + GS!(6));
}
}
void MenuClosed(BFWindow window)
{
mMenuClosedTick = mUpdateCnt;
}
public override void MouseDown(float x, float y, int32 btn, int32 btnCount)
{
base.MouseDown(x, y, btn, btnCount);
if (btn == 1)
ShowMenu(x, y);
}
public override void Dock(DockingFrame frame, DockedWidget refWidget, DockingFrame.WidgetAlign align)
{
mTabbedView.Dock(frame, refWidget, align);
}
public override void Draw(Graphics g)
{
base.Draw(g);
g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.Grabber), mWidth - DarkTheme.sUnitSize, 0);
/*if (mMouseOver)
{
using (g.PushColor(0x80FF0000))
g.FillRect(0, 0, mWidth, mHeight);
}*/
}
public override void Activate(bool setFocus)
{
}
public override void Resize(float x, float y, float width, float height)
{
base.Resize(x, y, width, height);
if (mMenuButton != null)
mMenuButton.Resize(mWidth - GS!(30), GS!(3), GS!(14), GS!(12));
}
public override bool IsTotalWindowContent()
{
return (mTabbedView.mParentDockingFrame.mParentDockingFrame == null) &&
(mTabbedView.mParentDockingFrame.GetDockedWindowCount() == 1);
}
}
public this(SharedData sharedData = null) : base(sharedData)
{
mTabHeight = DarkTheme.sUnitSize;
mTabEnd = new DarkTabEnd();
mTabEnd.mTabbedView = this;
AddWidget(mTabEnd);
Object obj = this;
obj.[Friend]GCMarkMembers();
}
public ~this()
{
if (mRightTab != null)
{
Widget.RemoveAndDelete(mRightTab);
mRightTab = null;
}
}
public override void RehupScale(float oldScale, float newScale)
{
base.RehupScale(oldScale, newScale);
mTabHeight = DarkTheme.sUnitSize;
}
public override int GetTabCount()
{
int tabCount = base.GetTabCount();
if (mRightTab != null)
tabCount++;
return tabCount;
}
public override void WithTabs(Action<TabbedView.TabButton> func)
{
for (var tab in mTabs)
func(tab);
if (mRightTab != null)
func(mRightTab);
}
public override TabButton AddTab(String label, float width, Widget content, bool ownsContent)
{
float useWidth = width;
if (useWidth == 0)
useWidth = DarkTheme.sDarkTheme.mSmallFont.GetWidth(label) + GS!(30);
return base.AddTab(label, useWidth, content, ownsContent);
}
public override void RemoveTab(TabButton tabButton, bool deleteTab = true)
{
if (mRightTab == tabButton)
SetRightTab(null, deleteTab);
else
base.RemoveTab(tabButton, deleteTab);
}
protected override TabButton CreateTabButton()
{
return new DarkTabButton();
}
public override void Draw(Graphics g)
{
base.Draw(g);
using (g.PushColor(0x80FFFFFF))
g.DrawButton(DarkTheme.sDarkTheme.mImages[(int32)DarkTheme.ImageIdx.TabInactive], 0, 0, mWidth);
g.DrawBox(DarkTheme.sDarkTheme.mImages[(int32)DarkTheme.ImageIdx.Window], 0, mTabHeight - GS!(2), mWidth, mHeight - mTabHeight + GS!(2));
}
protected override void ResizeTabs(bool immediate)
{
if (mWidgetWindow == null)
return;
List<Widget> afterTabWidgets = scope List<Widget>(8);
// Remove tabs, then them back
for (int childIdx = mChildWidgets.Count - 1; childIdx >= 0; childIdx--)
{
var widget = mChildWidgets[childIdx];
if ((widget is TabButton) /*&& (widget != mTabEnd) && (widget != mRightTab)*/)
{
mChildWidgets.RemoveAt(childIdx);
}
else
{
afterTabWidgets.Add(widget);
mChildWidgets.RemoveAt(childIdx);
}
}
if (mRightTab != null)
mRightTab.mWidth = mRightTab.mWantWidth;
float maxAreaWidth = mWidth - GS!(36);
if (mRightTab != null)
maxAreaWidth -= mRightTab.mWidth + GS!(2);
float leftObscure = mLeftObscure;
int32 maxStackedDocs = 16;
float curX = 0;
int32 tabIdx = 0;
// Before active tab button
bool foundActiveTab = false;
for (tabIdx = 0; tabIdx < mTabs.Count; tabIdx++)
{
var tabButton = (DarkTabButton)mTabs[tabIdx];
float useWidth = tabButton.mWantWidth;
if (curX + useWidth > mWidth - GS!(36))
useWidth = Math.Max(mWidth - GS!(36), 0);
useWidth = (float)Math.Round(useWidth);
tabButton.Resize(curX, tabButton.mY, useWidth, tabButton.mHeight);
//float offset = tabIdx - leftObscure;
float widthSubtract = Math.Max(0, leftObscure);
leftObscure -= tabButton.mWantWidth;
float showWidth = 0;
tabButton.mVisible = leftObscure < 1536.0f;
if (tabButton.mVisible)
showWidth = (float)Math.Round(Math.Max(tabButton.mWantWidth - widthSubtract, GS!(cMinTabSize)));
if (widthSubtract > 0)
{
tabButton.mObscuredDir = -widthSubtract;
}
else
tabButton.mObscuredDir = 0;
if (mTabs.Count > 1)
{
// Truncate drawing so we don't have the right side of the button showing from under the right side of the
// next tab (if next tab is shorter and on top of the previous one)
if (tabIdx < mTabs.Count - 1)
{
tabButton.mWidth = (float)Math.Round(Math.Min(tabButton.mWantWidth, showWidth + GS!(8)));
tabButton.mCloseButton.mVisible = tabButton.mWidth >= tabButton.mWantWidth;
}
else
{
tabButton.mWidth = (float)Math.Round(tabButton.mWantWidth);
tabButton.mCloseButton.mVisible = true;
}
}
mChildWidgets.Add(tabButton);
float pixelsOffscreen = curX + tabButton.mWidth + Math.Min(maxStackedDocs, mTabs.Count - tabIdx - 1) * GS!(cMinTabSize) - maxAreaWidth;
curX += showWidth;
if ((pixelsOffscreen > 0) && (leftObscure <= 0))
{
if (tabButton.mObscuredDir != 0)
{
tabButton.mObscuredDir = 0;
}
else
{
tabButton.mObscuredDir = pixelsOffscreen;
}
}
tabButton.mVisible = true;
foundActiveTab |= tabButton.mIsActive;
if ((mRightTab != null) && (mRightTab.mIsActive))
foundActiveTab = true;
if ((foundActiveTab) && (tabButton.mObscuredDir >= -2))
{
tabIdx++;
break;
}
}
int32 numRightObscuredButtons = 0;
bool foundEnd = false;
float stackedSize = 0;
int selInsertPos = mChildWidgets.Count;
// After the active button
for (; tabIdx < mTabs.Count; tabIdx++)
{
var tabButton = (DarkTabButton)mTabs[tabIdx];
float showWidth = (float)Math.Round(tabButton.mWantWidth);
if (!foundEnd)
stackedSize = Math.Min(maxStackedDocs, mTabs.Count - tabIdx - 1) * GS!(cMinTabSize);
float maxX = (float)Math.Round(maxAreaWidth - showWidth - stackedSize);
tabButton.mWidth = showWidth;
tabButton.mVisible = true;
if (maxX < curX)
{
curX = maxX;
tabButton.mObscuredDir = 1;
if (numRightObscuredButtons > maxStackedDocs)
{
//int a = 0;
tabButton.mVisible = false;
}
numRightObscuredButtons++;
foundEnd = true;
stackedSize -= GS!(cMinTabSize);
var prevButton = (DarkTabbedView.DarkTabButton)mTabs[tabIdx - 1];
if (prevButton.mWidth < prevButton.mWantWidth - 1)
{
// Super-squished, fix for small label, make small enough that the label doesn't draw
prevButton.mWidth = GS!(16);
}
}
else
tabButton.mObscuredDir = 0;
tabButton.mCloseButton.mVisible = tabButton.mObscuredDir == 0;
mChildWidgets.Insert(0, tabButton);
tabButton.Resize(curX, tabButton.mY, tabButton.mWidth, tabButton.mHeight);
curX += showWidth;
}
if ((curX < maxAreaWidth) && (mLeftObscure > 0))
{
var activeTab = (DarkTabButton)GetActiveTab();
float pixelsLeft = maxAreaWidth - curX;
if ((activeTab != null) && (pixelsLeft > mAllowRightSpace))
activeTab.mObscuredDir = -pixelsLeft;
}
float tabX = 0;
if (mTabs.Count > 0)
tabX = Math.Min(mWidth - GS!(36), mTabs[mTabs.Count - 1].mX + mTabs[mTabs.Count - 1].mWidth);
if (mRightTab != null)
{
if (mRightTab.mIsActive)
selInsertPos = mChildWidgets.Count;
//tabX = Math.Min(tabX, maxAreaWidth);
//tabX += 2;
mRightTab.Resize(maxAreaWidth, 0, mRightTab.mWidth, DarkTheme.sUnitSize);
//tabX += mRightTab.mWidth;
mChildWidgets.Insert(selInsertPos, mRightTab);
}
for (int afterTabIdx = afterTabWidgets.Count - 1; afterTabIdx >= 0; afterTabIdx--)
{
mChildWidgets.Add(afterTabWidgets[afterTabIdx]);
}
mTabEnd.Resize(tabX, 0, mWidth - tabX - GS!(1), DarkTheme.sUnitSize);
mChildWidgets.Insert(selInsertPos, mTabEnd);
mNeedResizeTabs = false;
if (immediate)
UpdateTabView(true);
}
public void SetRightTab(DarkTabButton tabButton, bool deletePrev = true)
{
bool hadFocus = mWidgetWindow.mFocusWidget != null;
bool needsNewFocus = false;
mNeedResizeTabs = true;
if (mRightTab != null)
{
if (mRightTab.mIsActive)
{
needsNewFocus = true;
mRightTab.Deactivate();
}
mRightTab.mIsRightTab = false;
RemoveWidget(mRightTab);
mTabs.Remove(mRightTab);
/*if ((GetActiveTab() == null) && (mTabs.Count > 0))
mTabs[0].Activate((hadFocus) && (mWidgetWindow.mFocusWidget == null));*/
mRightTab.mTabbedView = null;
if (deletePrev)
BFApp.sApp.DeferDelete(mRightTab);
}
mRightTab = tabButton;
if (tabButton != null)
{
tabButton.mIsRightTab = true;
tabButton.mTabbedView = this;
AddWidgetAtIndex(0, mRightTab);
if (needsNewFocus)
tabButton.Activate((hadFocus) && (mWidgetWindow.mFocusWidget == null));
}
else
{
if (mTabs.Count > 0)
mTabs[0].Activate((hadFocus) && (mWidgetWindow.mFocusWidget == null));
}
}
void UpdateTabView(bool immediate)
{
var darkTabButton = (DarkTabButton)GetActiveTab();
if (darkTabButton != null)
{
if (darkTabButton.mObscuredDir != 0)
{
float obscureAdd = darkTabButton.mObscuredDir * 0.2f;
obscureAdd += Math.Sign(obscureAdd) * 1.5f;
if ((Math.Abs(obscureAdd) > Math.Abs(darkTabButton.mObscuredDir)) || (immediate))
{
obscureAdd = darkTabButton.mObscuredDir;
darkTabButton.mObscuredDir = 0;
}
mLeftObscure = Math.Max(0, mLeftObscure + obscureAdd);
if (mLeftObscure == 0)
darkTabButton.mObscuredDir = 0;
if (obscureAdd > 0)
mAllowRightSpace = GS!(cMinTabSize) + 0.1f; // To remove oscillations
ResizeTabs(false);
mAllowRightSpace = 0;
MarkDirty();
}
}
}
public override void Update()
{
base.Update();
UpdateTabView(false);
}
public override void Resize(float x, float y, float width, float height)
{
base.Resize(x, y, width, height);
ResizeTabs(true);
}
public override ICustomDock GetCustomDock(IDockable dockable, float x, float y)
{
if (y < mTabHeight)
{
bool alreadyContains = false;
var tabButton = dockable as TabButton;
if ((tabButton != null) && (mTabs.Contains(tabButton)))
{
// Resizing our own tabs...
// We do this twice so it switches back if our new mouse location doesn't contain ourselves.
// This avoids rapidly switching position back and foreth on edges of unequally-sized tabs
for (int32 pass = 0; pass < 2; pass++)
{
TabButton foundTab = FindWidgetByCoords(x, y) as TabButton;
if ((foundTab != null) && (foundTab != dockable))
{
int32 foundIndex = mTabs.IndexOf(foundTab);
if (foundIndex != -1)
{
int32 dragIndex = mTabs.IndexOf((TabButton)dockable);
mTabs[dragIndex] = mTabs[foundIndex];
mTabs[foundIndex] = (TabButton)dockable;
ResizeTabs(false);
}
}
}
alreadyContains = true;
}
return new DarkTabDock(this, alreadyContains);
}
return null;
}
public virtual void DrawDockPreview(Graphics g, TabButton tabButton)
{
using (g.PushColor(0x80FFFFFF))
{
g.DrawBox(DarkTheme.sDarkTheme.mImages[(int32)DarkTheme.ImageIdx.Window], 0, mTabHeight - GS!(2), mWidth, mHeight - mTabHeight + GS!(2));
using (g.PushTranslate(tabButton.mX, tabButton.mY))
tabButton.Draw(g);
}
}
}
}

View file

@ -0,0 +1,530 @@
using System;
using System.Collections.Generic;
using System.Text;
using Beefy.widgets;
using Beefy.gfx;
namespace Beefy.theme.dark
{
public class DarkTheme : ThemeFactory
{
public enum ImageIdx
{
Bkg,
Window,
Dots,
RadioOn,
RadioOff,
MainBtnUp,
MainBtnDown,
BtnUp,
BtnOver,
BtnDown,
Seperator,
TabActive,
TabActiveOver,
TabInactive,
TabInactiveOver,
EditBox,
Checkbox,
CheckboxOver,
CheckboxDown,
Check,
Close,
CloseOver,
DownArrow,
GlowDot,
ArrowRight,
WhiteCircle,
DropMenuButton,
ListViewHeader,
ListViewSortArrow,
Outline,
Scrollbar,
ScrollbarThumbOver,
ScrollbarThumb,
ScrollbarArrow,
ShortButton,
ShortButtonDown,
VertScrollbar,
VertScrollbarThumbOver,
VertScrollbarThumb,
VertScrollbarArrow,
VertShortButton,
VertShortButtonDown,
Grabber,
DropShadow,
Menu,
MenuSepVert,
MenuSepHorz,
MenuSelect,
TreeArrow,
UIPointer,
UIImage,
UIComposition,
UILabel,
UIButton,
UIEdit,
UICombobox,
UICheckbox,
UIRadioButton,
UIListView,
UITabView,
EditCorners,
EditCircle,
EditPathNode,
EditPathNodeSelected,
EditAnchor,
UIBone,
UIBoneJoint,
VisibleIcon,
LockIcon,
LeftArrow,
KeyframeMakeOff,
RightArrow,
LeftArrowDisabled,
KeyframeMakeOn,
RightArrowDisabled,
TimelineSelector,
TimelineBracket,
KeyframeOff,
KeyframeOn,
LinkedIcon,
CheckboxLarge,
ComboBox,
ComboEnd,
ComboSelectedIcon,
LinePointer,
RedDot,
Document,
ReturnPointer,
RefreshArrows,
MoveDownArrow,
IconObject,
IconObjectDeleted,
IconObjectAppend,
IconObjectStack,
IconValue,
IconPointer,
IconType,
IconError,
IconBookmark,
ProjectFolder,
Project,
ArrowMoveDown,
Workspace,
MemoryArrowSingle,
MemoryArrowDoubleTop,
MemoryArrowDoubleBottom,
MemoryArrowTripleTop,
MemoryArrowTripleMiddle,
MemoryArrowTripleBottom,
MemoryArrowRainbow,
Namespace,
ResizeGrabber,
AsmArrow,
AsmArrowRev,
AsmArrowShadow,
MenuNonFocusSelect,
StepFilter,
WaitSegment,
FindCaseSensitive,
FindWholeWord,
RedDotUnbound,
MoreInfo,
Interface,
Property,
Field,
Method,
Variable,
Constant,
Type_ValueType,
Type_Class,
LinePointer_Prev,
LinePointer_Opt,
RedDotEx,
RedDotExUnbound,
RedDotDisabled,
RedDotExDisabled,
RedDotRunToCursor,
GotoButton,
YesJmp,
NoJmp,
WhiteBox,
UpDownArrows,
EventInfo,
WaitBar,
HiliteOutline,
HiliteOutlineThin,
IconPayloadEnum,
StepFilteredDefault,
ThreadBreakpointMatch,
ThreadBreakpointNoMatch,
ThreadBreakpointUnbound,
Search,
CheckIndeterminate,
COUNT
};
public const uint32 COLOR_WINDOW = 0xFF595959;
public const uint32 COLOR_BKG = 0xFF262626;
public const uint32 COLOR_SELECTED_OUTLINE = 0xFFE6A800;
public const uint32 COLOR_MENU_FOCUSED = 0xFFFFA000;
public const uint32 COLOR_MENU_SELECTED = 0xFFD0A070;
public const uint32 COLOR_TIMELINE_SEP = 0xFF202020;
public const uint32 COLOR_TIMELINE_RULE = 0xFF4A4A4A;
public static float sScale = 1.0f;
public static int32 sSrcImgScale = 1;
public static int32 sSrcImgUnitSize = 20;
public static int32 sUnitSize = 20;
public static DarkTheme sDarkTheme ~ delete _;
Image mThemeImage ~ delete _;
public Image[] mImages = new Image[(int32) ImageIdx.COUNT] ~ delete _;
public Font mHeaderFont ~ delete _;
public Font mSmallFont ~ delete _;
public Font mSmallBoldFont ~ delete _;
public Image mTreeArrow ~ delete _;
public Image mWindowTopImage ~ delete _;
public Image mIconWarning ~ delete _;
public Image mIconError ~ delete _;
public static DesignToolboxEntry[] GetDesignToolboxEntries()
{
Get();
DesignToolboxEntry [] entries = new DesignToolboxEntry []
{
new DesignToolboxEntry("ButtonWidget", typeof(DarkButton), sDarkTheme.mImages[(int32)ImageIdx.UIButton]),
new DesignToolboxEntry("LabelWidget", null, sDarkTheme.mImages[(int32)DarkTheme.ImageIdx.UILabel]),
new DesignToolboxEntry("EditWidget", typeof(DarkEditWidget), sDarkTheme.mImages[(int32)DarkTheme.ImageIdx.UIEdit]),
new DesignToolboxEntry("ComboBox", null, sDarkTheme.mImages[(int32)DarkTheme.ImageIdx.UICombobox]),
new DesignToolboxEntry("CheckBox", typeof(DarkCheckBox), sDarkTheme.mImages[(int32)DarkTheme.ImageIdx.UICheckbox]),
new DesignToolboxEntry("RadioButton", null, sDarkTheme.mImages[(int32)DarkTheme.ImageIdx.UIRadioButton]),
new DesignToolboxEntry("ListView", typeof(DarkListView), sDarkTheme.mImages[(int32)DarkTheme.ImageIdx.UIListView]),
new DesignToolboxEntry("TabView", typeof(DarkTabbedView), sDarkTheme.mImages[(int32)DarkTheme.ImageIdx.UITabView])
};
for (DesignToolboxEntry entry in entries)
entry.mGroupName = "DarkTheme";
return entries;
}
public static DarkTheme Get()
{
if (sDarkTheme != null)
return sDarkTheme;
sDarkTheme = new DarkTheme();
sDarkTheme.Init();
return sDarkTheme;
}
public static int GetScaled(int val)
{
return (int)(val * sScale);
}
public static float GetScaled(float val)
{
return (val * sScale);
}
public static void SetScale(float scale)
{
sScale = scale;
sSrcImgScale = (int32)Math.Clamp((int)Math.Ceiling(scale), 1, 4);
if (sSrcImgScale == 3)
sSrcImgScale = 4;
sSrcImgUnitSize = (int32)(20.0f * sSrcImgScale);
sUnitSize = (int32)(sScale * 20);
if (sDarkTheme != null)
sDarkTheme.Rehup();
}
Image LoadSizedImage(StringView baseName)
{
var fileName = scope String();
fileName.Append(BFApp.sApp.mInstallDir, "images/");
fileName.Append(baseName);
if (sSrcImgScale > 1)
fileName.AppendF("_{0}", sSrcImgScale);
fileName.Append(".png");
var image = Image.LoadFromFile(fileName);
image.Scale(GS!(48) / image.mWidth);
return image;
}
public override void Init()
{
sDarkTheme = this;
//SetScale(2);
//String tempStr = scope String();
/*mIconError = Image.LoadFromFile(StringAppend!(tempStr, BFApp.sApp.mInstallDir, "images/IconError.png"));
mIconWarning = Image.LoadFromFile(StringAppend!(tempStr, BFApp.sApp.mInstallDir, "images/IconWarning.png"));*/
for (int32 i = 0; i < (int32)ImageIdx.COUNT; i++)
{
mImages[i] = new Image();
}
mHeaderFont = new Font();
mSmallFont = new Font();
mSmallBoldFont = new Font();
//SetScale(2.2f);
SetScale(sScale);
}
public void Rehup()
{
String tempStr = scope String();
if (mThemeImage != null)
{
delete mIconError;
delete mIconWarning;
delete mThemeImage;
delete mTreeArrow;
delete mWindowTopImage;
}
String uiFileName = null;
switch (sSrcImgScale)
{
case 1:
uiFileName = "DarkUI.png";
case 2:
uiFileName = "DarkUI_2.png";
case 4:
uiFileName = "DarkUI_4.png";
default:
Runtime.FatalError("Invalid scale");
}
mIconError = LoadSizedImage("IconError");
mIconWarning = LoadSizedImage("IconWarning");
mThemeImage = Image.LoadFromFile(scope String..Append(tempStr, BFApp.sApp.mInstallDir, "images/", uiFileName));
for (int32 i = 0; i < (int32)ImageIdx.COUNT; i++)
{
var image = mImages[i];
image.CreateImageSegment(mThemeImage, (i % 20) * sSrcImgUnitSize, (i / 20) * sSrcImgUnitSize, sSrcImgUnitSize, sSrcImgUnitSize);
image.SetDrawSize(sUnitSize, sUnitSize);
}
// Trim off outside pixels
mTreeArrow = mImages[(int32) ImageIdx.TreeArrow].CreateImageSegment(1, 1, DarkTheme.sSrcImgUnitSize - 2, DarkTheme.sSrcImgUnitSize - 2);
mWindowTopImage = mImages[(int32)ImageIdx.Window].CreateImageSegment(2 * DarkTheme.sSrcImgScale, 2 * DarkTheme.sSrcImgScale, 16 * DarkTheme.sSrcImgScale, (int)Math.Ceiling(sScale));
//mIconError.Scale(sScale);
//mIconWarning.Scale(sScale);
mTreeArrow.SetDrawSize((int)(18 * sScale), (int)(18 * sScale));
mHeaderFont.Dispose(true);
/*mHeaderFont.Load(StringAppend!(tempStr, BFApp.sApp.mInstallDir, "fonts/segoeui.ttf"), 11.7f * sScale); //8.8
mHeaderFont.AddAlternate(scope String(BFApp.sApp.mInstallDir, "fonts/segoeui.ttf"), 11.7f * sScale);
mHeaderFont.AddAlternate(scope String(BFApp.sApp.mInstallDir, "fonts/seguisym.ttf"), 11.7f * sScale);
mHeaderFont.AddAlternate(scope String(BFApp.sApp.mInstallDir, "fonts/seguihis.ttf"), 11.7f * sScale);*/
mHeaderFont.Load("Segoe UI", 11.7f * sScale); //8.8
mHeaderFont.AddAlternate("Segoe UI Symbol", 11.7f * sScale);
mHeaderFont.AddAlternate("Segoe UI Historic", 11.7f * sScale);
mHeaderFont.AddAlternate("Segoe UI Emoji", 11.7f * sScale);
mSmallFont.Dispose(true);
mSmallFont.Load("Segoe UI", 12.8f * sScale); // 10.0
mSmallFont.AddAlternate("Segoe UI Symbol", 12.8f * sScale);
mSmallFont.AddAlternate("Segoe UI Historic", 12.8f * sScale);
mSmallFont.AddAlternate("Segoe UI Emoji", 12.8f * sScale);
mSmallBoldFont.Dispose(true);
mSmallBoldFont.Dispose(true);
mSmallBoldFont.Load("Segoe UI Bold", 12.8f * sScale); // 10.0
mSmallBoldFont.AddAlternate("Segoe UI Symbol", 12.8f * sScale);
mSmallBoldFont.AddAlternate("Segoe UI Historic", 12.8f * sScale);
mSmallBoldFont.AddAlternate("Segoe UI Emoji", 12.8f * sScale);
/*mSmallBoldFont.Load(StringAppend!(tempStr, BFApp.sApp.mInstallDir, "fonts/segoeuib.ttf"), 12.8f * sScale); // 10.0
mSmallBoldFont.AddAlternate(scope String(BFApp.sApp.mInstallDir, "fonts/segoeui.ttf"), 12.8f * sScale);
mSmallBoldFont.AddAlternate(scope String(BFApp.sApp.mInstallDir, "fonts/seguisym.ttf"), 12.8f * sScale);
mSmallBoldFont.AddAlternate(scope String(BFApp.sApp.mInstallDir, "fonts/seguihis.ttf"), 12.8f * sScale);*/
}
public override void Update()
{
base.Update();
DarkTooltipManager.UpdateTooltip();
DarkTooltipManager.UpdateMouseover();
}
public ~this()
{
for (var image in mImages)
delete image;
}
public Image GetImage(ImageIdx idx)
{
return mImages[(int32)idx];
}
public override ButtonWidget CreateButton(Widget parent, String label, float x, float y, float width, float height)
{
DarkButton button = new DarkButton();
button.Resize(x, y, width, height);
button.Label = label;
if (parent != null)
parent.AddWidget(button);
return button;
}
public override EditWidget CreateEditWidget(Widget parent, float x = 0, float y = 0, float width = 0, float height = 0)
{
DarkEditWidget editWidget = new DarkEditWidget();
editWidget.Resize(x, y, width, height);
if (parent != null)
parent.AddWidget(editWidget);
return editWidget;
}
public override TabbedView CreateTabbedView(TabbedView.SharedData sharedData, Widget parent, float x, float y, float width, float height)
{
DarkTabbedView tabbedView = new DarkTabbedView(sharedData);
tabbedView.Resize(x, y, width, height);
if (parent != null)
parent.AddWidget(tabbedView);
return tabbedView;
}
public override DockingFrame CreateDockingFrame(DockingFrame parent)
{
DarkDockingFrame dockingFrame = new DarkDockingFrame();
if (parent == null)
dockingFrame.mWindowMargin = 1;
else if (var darkParent = parent as DarkDockingFrame)
{
dockingFrame.mDrawBkg = darkParent.mDrawBkg;
}
return dockingFrame;
}
public override ListView CreateListView()
{
return new DarkListView();
}
public override Scrollbar CreateScrollbar(Scrollbar.Orientation orientation)
{
DarkScrollbar scrollbar = new DarkScrollbar();
scrollbar.mOrientation = orientation;
return scrollbar;
}
public override InfiniteScrollbar CreateInfiniteScrollbar()
{
return new DarkInfiniteScrollbar();
}
public override CheckBox CreateCheckbox(Widget parent, float x = 0, float y = 0, float width = 0, float height = 0)
{
DarkCheckBox checkbox = new DarkCheckBox();
checkbox.Resize(x, y, width, height);
if (parent != null)
parent.AddWidget(checkbox);
return checkbox;
}
public override MenuWidget CreateMenuWidget(Menu menu)
{
return new DarkMenuWidget(menu);
}
public override Dialog CreateDialog(String title = null, String text = null, Image icon = null)
{
return new DarkDialog(title, text, icon);
}
public static bool CheckUnderlineKeyCode(StringView label, KeyCode keyCode)
{
int underlinePos = label.IndexOf('&');
if (underlinePos == -1)
return false;
char32 underlineC = label.GetChar32(underlinePos + 1).0;
underlineC = underlineC.ToUpper;
return ((char32)keyCode == underlineC);
}
public static void DrawUnderlined(Graphics g, StringView str, float x, float y, FontAlign alignment = FontAlign.Left, float width = 0, Beefy.gfx.FontOverflowMode overflowMode = .Overflow)
{
int underlinePos = str.IndexOf('&');
if ((underlinePos != -1) && (underlinePos < str.Length - 1))
{
String label = scope String();
label.Append(str, 0, underlinePos);
float underlineX = g.mFont.GetWidth(label);
char32 underlineC = str.GetChar32(underlinePos + 1).0;
float underlineWidth = g.mFont.GetWidth(underlineC);
FontMetrics fm = .();
label.Append(str, underlinePos + 1);
g.DrawString(label, x, y, alignment, width, overflowMode, &fm);
float drawX;
switch (alignment)
{
case .Centered:
drawX = x + underlineX + (width - fm.mMaxWidth) / 2;
default:
drawX = x + underlineX;
}
g.FillRect(drawX, y + g.mFont.GetAscent() + GS!(1), underlineWidth, (int)GS!(1.2f));
}
else
{
g.DrawString(str, x, y, alignment, width, overflowMode);
}
}
}
static
{
public static mixin GS(int32 val)
{
(int32)(val * DarkTheme.sScale)
}
public static mixin GS(int64 val)
{
(int64)(val * DarkTheme.sScale)
}
public static mixin GS(float val)
{
float fVal = val * DarkTheme.sScale;
fVal
}
}
}

View file

@ -0,0 +1,398 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Beefy;
using Beefy.events;
using Beefy.gfx;
using Beefy.theme.dark;
using Beefy.widgets;
using System.Diagnostics;
using Beefy.geom;
using Beefy.utils;
namespace Beefy.theme.dark
{
public class DarkTooltipContainer : Widget
{
public DarkTooltip mTooltip;
public override void Resize(float x, float y, float width, float height)
{
base.Resize(x, y, width, height);
mTooltip.Resize(0, GS!(4), width - GS!(DarkTooltip.cShadowSize), height - GS!(DarkTooltip.cShadowSize) - GS!(4));
}
}
public class DarkTooltip : Widget
{
public Event<Action> mCloseEvent ~ _.Dispose();
public Widget mRelWidget;
public Font mFont;
public String mText ~ delete _;
public bool mAllowResize;
public bool mHasClosed;
public Insets mRelWidgetMouseInsets ~ delete _;
public bool mAllowMouseInsideSelf;
public const float cShadowSize = 8;
public this(String text, Widget relWidget, float x, float y, float minWidth, float minHeight, bool allowResize, bool mouseVisible)
{
DarkTooltipContainer container = new DarkTooltipContainer();
container.mTooltip = this;
container.AddWidget(this);
Attach(relWidget);
mFont = DarkTheme.sDarkTheme.mSmallFont;
mText = new String(text);
mAllowResize = allowResize;
FontMetrics fontMetrics = .();
float height = mFont.Draw(null, mText, x, y, 0, 0, FontOverflowMode.Overflow, &fontMetrics);
mWidth = Math.Max(minWidth, fontMetrics.mMaxWidth + GS!(32));
mHeight = Math.Max(minHeight, height + GS!(16));
float screenX;
float screenY;
relWidget.SelfToRootTranslate(x, y, out screenX, out screenY);
screenX += relWidget.mWidgetWindow.mClientX;
screenY += relWidget.mWidgetWindow.mClientY;
//screenX -= 2;
//screenY += 14;
BFWindow.Flags windowFlags = BFWindow.Flags.ClientSized | BFWindow.Flags.PopupPosition | BFWindow.Flags.NoActivate | BFWindow.Flags.DestAlpha;
WidgetWindow widgetWindow = new WidgetWindow(relWidget.mWidgetWindow,
"Tooltip",
(int32)(screenX), (int32)(screenY),
(int32)(mWidth +GS!(cShadowSize)), (int32)(mHeight + GS!(cShadowSize)),
windowFlags,
container);
widgetWindow.SetMinimumSize((int32)widgetWindow.mWindowWidth, (int32)widgetWindow.mWindowHeight);
if (!mouseVisible)
widgetWindow.SetMouseVisible(mouseVisible);
if (allowResize)
widgetWindow.mOnHitTest.Add(new => HitTest);
WidgetWindow.sOnMouseDown.Add(new => HandleMouseDown);
WidgetWindow.sOnMouseWheel.Add(new => HandleMouseWheel);
WidgetWindow.sOnMenuItemSelected.Add(new => HandleSysMenuItemSelected);
WidgetWindow.sOnKeyDown.Add(new => HandleKeyDown);
}
public ~this()
{
Detach();
}
void Attach(Widget widget)
{
if (mRelWidget != null)
Detach();
mRelWidget = widget;
if (mRelWidget != null)
mRelWidget.mOnRemovedFromParent.Add(new => WidgetRemoved);
}
void Detach()
{
if (mRelWidget != null)
{
mRelWidget.mOnRemovedFromParent.Remove(scope => WidgetRemoved, true);
mRelWidget = null;
}
}
void WidgetRemoved(Widget widget, Widget prevParent, WidgetWindow widgetWindow)
{
Detach();
Close();
}
public void Reinit(String text, Widget relWidget, float x, float y, float minWidth, float minHeight, bool allowResize, bool mouseVisible)
{
mRelWidget = relWidget;
mFont = DarkTheme.sDarkTheme.mSmallFont;
mText.Set(text);
mAllowResize = allowResize;
FontMetrics fontMetrics = .();
float height = mFont.Draw(null, mText, x, y, 0, 0, FontOverflowMode.Overflow, &fontMetrics);
mWidth = Math.Max(minWidth, fontMetrics.mMaxWidth + GS!(32));
mHeight = Math.Max(minHeight, height + GS!(16));
float screenX;
float screenY;
relWidget.SelfToRootTranslate(x, y, out screenX, out screenY);
screenX += relWidget.mWidgetWindow.mClientX;
screenY += relWidget.mWidgetWindow.mClientY;
mWidgetWindow.Resize((int32)(screenX), (int32)(screenY),
(int32)(mWidth + GS!(cShadowSize)), (int32)(mHeight + GS!(cShadowSize)));
}
void HandleKeyDown(KeyDownEvent keyboardEvent)
{
mOnKeyDown(keyboardEvent);
if (keyboardEvent.mHandled)
return;
if (keyboardEvent.mKeyCode == KeyCode.Escape)
{
Close();
keyboardEvent.mHandled = true;
}
}
BFWindow.HitTestResult HitTest(int32 x, int32 y)
{
int32 relX = x - mWidgetWindow.mX;
int32 relY = y - mWidgetWindow.mY;
if ((relX >= mWidgetWindow.mWindowWidth - GS!(18)) && (relY >= mWidgetWindow.mWindowHeight - GS!(18)))
return BFWindowBase.HitTestResult.BottomRight;
return BFWindowBase.HitTestResult.Client;
}
public override void Draw(Graphics g)
{
base.Draw(g);
using (g.PushColor(0x80000000))
g.DrawBox(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.DropShadow), 0, 0, mWidth + cShadowSize, mHeight + cShadowSize);
using (g.PushColor(0xFFFFFFFF))
g.DrawBox(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.Menu), 0, 0, mWidth, mHeight);
g.SetFont(mFont);
g.DrawString(mText, 0, GS!(5), FontAlign.Centered, mWidth);
if (mAllowResize)
g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.ResizeGrabber), mWidth - GS!(22), mHeight - GS!(22));
}
public override void ParentDeleted()
{
Close();
base.ParentDeleted();
}
public void Close()
{
if (mHasClosed)
return;
mHasClosed = true;
if (mWidgetWindow != null)
{
WidgetWindow.sOnMouseDown.Remove(scope => HandleMouseDown, true);
WidgetWindow.sOnMouseWheel.Remove(scope => HandleMouseWheel, true);
WidgetWindow.sOnMenuItemSelected.Remove(scope => HandleSysMenuItemSelected, true);
WidgetWindow.sOnKeyDown.Remove(scope => HandleKeyDown, true);
mWidgetWindow.Close();
}
mCloseEvent();
}
void HandleMouseWheel(MouseEvent evt)
{
WidgetWindow widgetWindow = (WidgetWindow)evt.mSender;
if (widgetWindow == mWidgetWindow)
return;
Close();
}
void HandleMouseDown(MouseEvent evt)
{
WidgetWindow widgetWindow = (WidgetWindow)evt.mSender;
if (widgetWindow == mWidgetWindow)
return;
//if ((!(widgetWindow.mRootWidget is HoverWatch)) && (!(widgetWindow.mRootWidget is MenuWidget)))
Close();
}
void HandleSysMenuItemSelected(IMenu sysMenu)
{
Close();
}
public override void Update()
{
base.Update();
if (mWidgetWindow == null)
return;
/*var lastMouseWidget = IDEApp.sApp.sLastMouseWidget;
if ((lastMouseWidget != null) && (lastMouseWidget != mRelWidget) && (lastMouseWidget.mWidgetWindow != mWidgetWindow))
Close();*/
float rootX;
float rootY;
mRelWidget.SelfToRootTranslate(0, 0, out rootX, out rootY);
Rect checkRect = Rect(rootX, rootY, mRelWidget.mWidth, mRelWidget.mHeight);
mRelWidgetMouseInsets?.ApplyTo(ref checkRect);
if ((mRelWidget.mWidgetWindow != null) && (mRelWidget.mWidgetWindow.mHasMouseInside))
{
//checkRect.Inflate(8, 8);
if (checkRect.Contains(mRelWidget.mWidgetWindow.mClientMouseX, mRelWidget.mWidgetWindow.mClientMouseY))
return;
}
if ((mWidgetWindow.mHasMouseInside) && (mAllowMouseInsideSelf))
return;
Close();
}
}
static class DarkTooltipManager
{
public static DarkTooltip sTooltip;
public static Widget sLastMouseWidget;
public static int32 sMouseStillTicks;
public static Point sLastAbsMousePos;
public static Point sLastRelMousePos;
public static bool sWantsRefireMouseOver;
public static bool IsTooltipShown(Widget relWidget)
{
return (sTooltip != null) && (sTooltip.mRelWidget == relWidget);
}
public static DarkTooltip ShowTooltip(String text, Widget relWidget, float x, float y, float minWidth = 0, float minHeight = 0, bool allowResize = false, bool mouseVisible = false)
{
scope AutoBeefPerf("DarkTooltipManager.ShowTooltip");
if (sTooltip != null)
{
if (relWidget == sTooltip.mRelWidget)
{
sTooltip.Reinit(text, relWidget, x, y, minWidth, minHeight, allowResize, mouseVisible);
return null; // Only return the tooltip when a new one has been allocated
}
sTooltip.Close();
}
sTooltip = new DarkTooltip(text, relWidget, x, y, minWidth, minHeight, allowResize, mouseVisible);
sTooltip.mCloseEvent.Add(new () => {sTooltip = null; });
return sTooltip;
}
public static void CloseTooltip()
{
if (sTooltip != null)
sTooltip.Close();
}
public static void UpdateTooltip()
{
if (sTooltip == null)
return;
}
public static bool CheckMouseover(Widget checkWidget, int wantTicks, out Point mousePoint, bool continuous = false)
{
mousePoint = Point(Int32.MinValue, Int32.MinValue);
if (checkWidget != sLastMouseWidget)
return false;
for (var childWindow in checkWidget.mWidgetWindow.mChildWindows)
{
var childWidgetWindow = childWindow as WidgetWindow;
if (childWidgetWindow == null)
continue;
if (childWidgetWindow.mRootWidget is MenuContainer)
return false;
}
checkWidget.RootToSelfTranslate(sLastRelMousePos.x, sLastRelMousePos.y, out mousePoint.x, out mousePoint.y);
if ((continuous) && (sMouseStillTicks > wantTicks))
return true;
if (sWantsRefireMouseOver)
{
sWantsRefireMouseOver = false;
return true;
}
return sMouseStillTicks == wantTicks;
}
static void LastMouseWidgetDeleted(Widget widget)
{
if (sLastMouseWidget == widget)
sLastMouseWidget = null;
}
public static void RefireMouseOver()
{
sWantsRefireMouseOver = true;
}
static void SetLastMouseWidget(Widget newWidget)
{
if (sLastMouseWidget != null)
sLastMouseWidget.mOnDeleted.Remove(scope => LastMouseWidgetDeleted, true);
sLastMouseWidget = newWidget;
if (sLastMouseWidget != null)
sLastMouseWidget.mOnDeleted.Add(new => LastMouseWidgetDeleted);
}
public static void UpdateMouseover()
{
if (sMouseStillTicks != -1)
sMouseStillTicks++;
Widget overWidget = null;
int32 numOverWidgets = 0;
for (var window in BFApp.sApp.mWindows)
{
var widgetWindow = window as WidgetWindow;
widgetWindow.RehupMouse(false);
var windowOverWidget = widgetWindow.mCaptureWidget ?? widgetWindow.mOverWidget;
if ((windowOverWidget != null) && (widgetWindow.mAlpha == 1.0f) && (widgetWindow.mCaptureWidget == null))
{
overWidget = windowOverWidget;
numOverWidgets++;
if (overWidget != sLastMouseWidget)
{
SetLastMouseWidget(overWidget);
sMouseStillTicks = -1;
}
float actualX = widgetWindow.mClientX + widgetWindow.mMouseX;
float actualY = widgetWindow.mClientY + widgetWindow.mMouseY;
if ((sLastAbsMousePos.x != actualX) || (sLastAbsMousePos.y != actualY))
{
sMouseStillTicks = 0;
sLastAbsMousePos.x = actualX;
sLastAbsMousePos.y = actualY;
}
sLastRelMousePos.x = widgetWindow.mMouseX;
sLastRelMousePos.y = widgetWindow.mMouseY;
}
}
if (overWidget == null)
{
SetLastMouseWidget(null);
sMouseStillTicks = -1;
}
if (numOverWidgets > 1)
{
//int a = 0;
Debug.WriteLine("numOverWidgets > 1");
}
//Debug.Assert(numOverWidgets <= 1);
}
}
}

View file

@ -0,0 +1,161 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Beefy.theme.dark;
using Beefy.widgets;
using System.Diagnostics;
namespace Beefy.theme.dark
{
public class DarkVirtualListViewItem : DarkListViewItem
{
public DarkVirtualListViewItem mVirtualHeadItem;
public int32 mVirtualCount; // Including head item
public int32 mVirtualIdx;
public bool mDisabled = false;
public bool mUpdating = false;
public ~this()
{
Debug.Assert(!mUpdating);
}
public override void Update()
{
mUpdating = true;
defer { mUpdating = false; }
base.Update();
if (mParentItem == null)
return;
var virtualListView = (DarkVirtualListView)mListView;
if (mParentItem.mChildAreaHeight != 0)
{
float itemHeight = virtualListView.mFont.GetLineSpacing();
if (mVirtualHeadItem == this)
{
float ofsX;
float ofsY;
mParent.SelfToOtherTranslate(mListView, 0, 0, out ofsX, out ofsY);
ofsY -= (float)(mListView.mVertPos.mDest + mListView.mScrollContent.mY);
int32 curMemberIdx = 0;
DarkVirtualListViewItem prevVirtualListViewItem = null;
DarkVirtualListViewItem nextVirtualListViewItem = (DarkVirtualListViewItem)mParentItem.mChildItems[curMemberIdx];
int32 showCount = mVirtualCount;
float curY = mY;
float prevY = curY;
float lastBottomPadding = 0;
for (int32 idx = 0; idx < showCount; idx++)
{
DarkVirtualListViewItem curVirtualListViewItem = null;
if ((nextVirtualListViewItem != null) && (idx == nextVirtualListViewItem.mVirtualIdx))
{
curVirtualListViewItem = nextVirtualListViewItem;
curMemberIdx++;
if (curMemberIdx < mParentItem.mChildItems.Count)
{
nextVirtualListViewItem = (DarkVirtualListViewItem)mParentItem.mChildItems[curMemberIdx];
if (nextVirtualListViewItem.mVirtualHeadItem != this)
nextVirtualListViewItem = null;
if (nextVirtualListViewItem != null)
lastBottomPadding = nextVirtualListViewItem.mBottomPadding;
}
else
nextVirtualListViewItem = null;
}
bool wantsFillIn = (curY + ofsY + itemHeight >= 0) && (curY + ofsY < mListView.mHeight);
bool wantsDelete = !wantsFillIn;
if (mDisabled)
{
wantsFillIn = false;
wantsDelete = false;
}
if ((curVirtualListViewItem == null) && (wantsFillIn))
{
prevVirtualListViewItem.mBottomPadding = (curY - prevVirtualListViewItem.mY) - prevVirtualListViewItem.mSelfHeight - prevVirtualListViewItem.mChildAreaHeight;
curVirtualListViewItem = (DarkVirtualListViewItem)mParentItem.CreateChildItemAtIndex(curMemberIdx);
curVirtualListViewItem.mVisible = false;
curVirtualListViewItem.mX = mX;
curVirtualListViewItem.mVirtualHeadItem = this;
curVirtualListViewItem.mVirtualIdx = idx;
virtualListView.PopulateVirtualItem(curVirtualListViewItem);
curMemberIdx++;
}
if ((wantsDelete) && (idx != 0) && (curVirtualListViewItem != null) && (curVirtualListViewItem.mChildAreaHeight == 0))
{
curMemberIdx--;
mParentItem.RemoveChildItem(curVirtualListViewItem);
curVirtualListViewItem = null;
}
if (prevVirtualListViewItem != null)
{
if (mDisabled)
prevVirtualListViewItem.mBottomPadding = 0;
else
prevVirtualListViewItem.mBottomPadding = (curY - prevY) - prevVirtualListViewItem.mSelfHeight - prevVirtualListViewItem.mChildAreaHeight;
}
if (curVirtualListViewItem != null)
prevY = curY;
curY += itemHeight;
if (curVirtualListViewItem != null)
{
curY += curVirtualListViewItem.mChildAreaHeight;
prevVirtualListViewItem = curVirtualListViewItem;
}
}
if (prevVirtualListViewItem != null)
{
if (mDisabled)
prevVirtualListViewItem.mBottomPadding = 0;
else
prevVirtualListViewItem.mBottomPadding = (curY - prevY) - prevVirtualListViewItem.mSelfHeight - prevVirtualListViewItem.mChildAreaHeight;
if (prevVirtualListViewItem.mBottomPadding != lastBottomPadding)
mListView.mListSizeDirty = true;
}
while ((curMemberIdx > 0) && (curMemberIdx < mParentItem.mChildItems.Count))
{
var curVirtualListViewItem = (DarkVirtualListViewItem)mParentItem.mChildItems[curMemberIdx];
if (curVirtualListViewItem.mVirtualHeadItem != this)
break;
mParentItem.RemoveChildItem(curVirtualListViewItem);
if (mParentItem == null) // Last item
return;
}
}
}
}
}
public class DarkVirtualListView : DarkListView
{
protected override ListViewItem CreateListViewItem()
{
var anItem = new DarkVirtualListViewItem();
return anItem;
}
public virtual void PopulateVirtualItem(DarkVirtualListViewItem item)
{
}
}
}

View file

@ -0,0 +1,105 @@
using System;
using System.Runtime.InteropServices;
namespace Beefy.utils
{
static class BeefPerf
{
[StdCall, CLink]
extern static void BpInit(char8* severName, char8* sessionName);
[StdCall, CLink]
extern static void BpShutdown();
[StdCall, CLink]
extern static void BpRetryConnect();
[StdCall, CLink]
extern static void BpPause();
[StdCall, CLink]
extern static void BpUnpause();
[StdCall, CLink]
extern static void BpSetThreadName(char8* threadName);
[StdCall, CLink]
extern static void BpEnter(char8* name);
[StdCall, CLink]
extern static void BpLeave();
[StdCall, CLink]
extern static void BpEvent(char8* name, char8* details);
[StdCall, CLink]
extern static char8* BpDynStr(char8* string);
public static void Init(StringView serverName, StringView sessionName)
{
BpInit(serverName.ToScopeCStr!(), sessionName.ToScopeCStr!());
}
public static void RetryConnect()
{
BpRetryConnect();
}
public static void Shutdown()
{
BpShutdown();
}
public static void Pause()
{
BpPause();
}
public static void Unpause()
{
BpUnpause();
}
public static void SetThreadName(StringView threadName)
{
BpSetThreadName(threadName.ToScopeCStr!());
}
[Inline]
public static void Enter(char8* name)
{
BpEnter(name);
}
[Inline]
public static void Leave()
{
BpLeave();
}
[Inline]
public static void Event(char8* name, char8* details)
{
BpEvent(name, details);
}
[Inline]
public static char8* DynStr(char8* string)
{
return BpDynStr(string);
}
}
class AutoBeefPerf
{
public this(char8* name)
{
BeefPerf.Enter(name);
}
public ~this()
{
BeefPerf.Leave();
}
}
}

View file

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Beefy.utils
{
public delegate void DisposeProxyDelegate();
public class DisposeProxy : IDisposable
{
public DisposeProxyDelegate mDisposeProxyDelegate;
public this(DisposeProxyDelegate theDelegate = null)
{
mDisposeProxyDelegate = theDelegate;
}
public ~this()
{
delete mDisposeProxyDelegate;
}
public void Dispose()
{
mDisposeProxyDelegate();
}
}
}

View file

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Beefy.utils
{
public interface ISerializable
{
//TODO: void Serialize(StructuredData data);
//TODO: void Deserialize(StructuredData data);
}
}

View file

@ -0,0 +1,899 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading.Tasks;
namespace Beefy.utils
{
public struct IdSpan
{
enum Change
{
case Insert(int32 index, int32 id, int16 length);
case Remove(int32 index, int16 length);
public int32 GetIndex()
{
int32 index;
switch (this)
{
case .Insert(out index, ?, ?):
case .Remove(out index, ?):
}
return index;
}
}
enum ChangeKind
{
case None;
case Insert;
case Remove;
}
static uint8[] sEmptyData = new uint8[] { 0 } ~ delete _;
public uint8[] mData;
public int32 mLength;
List<Change> mChangeList;
public static int32 sId;
public int32 mId = ++sId;
public bool mAlwaysPrepare = false;
/*public uint8[] mData
{
get
{
Debug.Assert(mChangeList == null);
return mData;
}
}*/
public this()
{
mData = sEmptyData;
mLength = 0;
mChangeList = null;
}
public this(uint8[] data, int32 length)
{
mData = data;
mLength = length;
mChangeList = null;
}
bool HasChangeList
{
get
{
return mChangeList != null;
}
}
public bool IsEmpty
{
get
{
return mLength == 0;
}
}
struct Span
{
public int32 mIndex;
public int32 mId;
public int32 mLength;
public int32 mNext;
}
public void PrepareReverse() mut
{
PrepareReverse(0, mChangeList.Count);
DeleteAndNullify!(mChangeList);
}
// Decode change lists, move backwards through it applying changes, the reencode it.
// Repeats as necessary if items aren't in reverse order
void PrepareReverse(int startChangeIdx, int endChangeIdx) mut
{
int changeIdx = startChangeIdx;
List<Span> spans = scope List<Span>();
while (changeIdx < endChangeIdx)
{
spans.Clear();
int encodeIdx = 0;
int32 charId = 1;
int32 charIdx = 0;
while (true)
{
int32 cmd = Utils.DecodeInt(mData, ref encodeIdx);
if (cmd > 0)
{
charId = cmd;
}
else
{
int32 spanSize = -cmd;
if (cmd == 0)
break;
Span span;
span.mId = charId;
span.mIndex = charIdx;
span.mLength = spanSize;
span.mNext = -1;
spans.Add(span);
charId += spanSize;
charIdx += spanSize;
if (spans.Count > 1)
spans[spans.Count - 2].mNext = (int32)spans.Count - 1;
}
}
// Initialize to something so we can pick up the insert
if (spans.Count == 0)
{
Span span;
span.mId = 1;
span.mIndex = 0;
span.mLength = 0;
span.mNext = -1;
spans.Add(span);
}
int32 index = -1;
int32 length = -1;
int32 curId = -1;
ChangeKind changeKind = .None;
int32 checkIdx = (int32)spans.Count - 1;
int32 headSpanIdx = 0;
// Reverse find the first span
while ((checkIdx >= 0) && (changeIdx < endChangeIdx))
{
if (changeKind == .None)
{
switch (mChangeList[changeIdx])
{
case .Insert(out index, out curId, out length):
changeKind = .Insert;
case .Remove(out index, out length):
changeKind = .Remove;
}
}
var checkSpan = ref spans[checkIdx];
if ((index >= checkSpan.mIndex) && (index <= checkSpan.mIndex + checkSpan.mLength))
{
if (changeKind == .Insert)
{
if (index == checkSpan.mIndex)
{
if (checkSpan.mLength == 0)
{
checkSpan.mLength = length;
checkSpan.mId = curId;
}
else
{
int32 newSpanIdx = (.)spans.Count;
// Insert before span
Span span;
span.mIndex = index;
span.mId = curId;
span.mLength = length;
span.mNext = (.)checkIdx;
spans.Add(span);
if (checkIdx > 0)
{
Debug.Assert(spans[checkIdx - 1].mNext == checkIdx);
spans[checkIdx - 1].mNext = newSpanIdx;
}
else
headSpanIdx = newSpanIdx;
// Since we remapped the previous entries mNext, we are no longer in order and can't be reused this pass
checkIdx = checkIdx - 1;
}
}
else
{
var checkSpan;
// Split span
int32 leftLength = index - checkSpan.mIndex;
int32 newSpanId = (.)spans.Count;
int32 newRightId = (.)spans.Count + 1;
@checkSpan.mNext = newSpanId;
@checkSpan.mLength = leftLength;
Span span;
span.mIndex = index;
span.mId = curId;
span.mLength = length;
span.mNext = newRightId;
spans.Add(span);
Span rightSpan;
rightSpan.mIndex = index + span.mLength;
rightSpan.mId = checkSpan.mId + leftLength;
rightSpan.mLength = checkSpan.mLength - leftLength;
rightSpan.mNext = checkSpan.mNext;
spans.Add(rightSpan);
}
}
else // Remove
{
int removeSpanIdx = checkIdx;
if (index == checkSpan.mIndex)
{
// Removing from front of span. Handled in loop below.
}
else if (index + length >= checkSpan.mIndex + checkSpan.mLength)
{
// Removing up to or past end of span
int32 removeCount = Math.Min(length, checkSpan.mLength - (index - checkSpan.mIndex));
checkSpan.mLength -= removeCount;
length -= removeCount;
removeSpanIdx = checkSpan.mNext;
}
else
{
var checkSpan;
int32 splitIdx = index - checkSpan.mIndex;
int32 splitOfs = splitIdx + length;
int32 newRightId = (.)spans.Count;
@checkSpan.mNext = newRightId;
@checkSpan.mLength = index - checkSpan.mIndex;
Span rightSpan;
rightSpan.mIndex = checkSpan.mIndex + splitIdx;
rightSpan.mId = checkSpan.mId + splitOfs;
rightSpan.mLength = checkSpan.mLength - splitOfs;
rightSpan.mNext = checkSpan.mNext;
spans.Add(rightSpan);
length = 0;
if (newRightId == checkIdx + 1)
checkIdx = newRightId; // rightSpan index is valid now and it is next in sequence
}
while (length > 0)
{
var removeSpan = ref spans[removeSpanIdx];
// Remove from start of span
int32 removeCount = Math.Min(removeSpan.mLength, length);
removeSpan.mId += removeCount;
removeSpan.mLength -= removeCount;
length -= removeCount;
removeSpanIdx = removeSpan.mNext;
}
}
changeIdx++;
changeKind = .None;
continue;
}
checkIdx--;
}
curId = 1;
int32 spanIdx = headSpanIdx;
int curEncodeIdx = 0;
if (mData != sEmptyData)
delete mData;
mData = new uint8[spans.Count * 8];
while (spanIdx != -1)
{
var span = ref spans[spanIdx];
if (span.mLength == 0)
{
spanIdx = span.mNext;
continue;
}
if (span.mId != curId)
{
Utils.EncodeInt(mData, ref curEncodeIdx, span.mId);
curId = span.mId;
}
Utils.EncodeInt(mData, ref curEncodeIdx, -span.mLength);
curId += span.mLength;
spanIdx = span.mNext;
}
Utils.EncodeInt(mData, ref curEncodeIdx, 0);
mLength = (int32)curEncodeIdx;
}
}
void MaybePrepare() mut
{
// For sanity - only queue up so many changes
if (mChangeList.Count >= 8192)
{
Prepare();
}
}
public void Prepare() mut
{
if (mChangeList == null)
return;
scope AutoBeefPerf("IdSpan.Prepare");
int changeIdx = 0;
while (changeIdx < mChangeList.Count)
{
// Check to see if we have a reverse-order encoding. This can occur when undoing forward-ordered changes (for example)
bool hasReverse = false;
int reverseLastIdx = changeIdx;
int32 prevIndex = mChangeList[changeIdx].GetIndex();
for (int checkIdx = changeIdx + 1; checkIdx < mChangeList.Count; checkIdx++)
{
int32 nextIndex = mChangeList[checkIdx].GetIndex();
if (nextIndex > prevIndex)
break;
if (nextIndex < prevIndex)
hasReverse = true;
reverseLastIdx = checkIdx;
prevIndex = nextIndex;
}
if (hasReverse)
{
PrepareReverse(changeIdx, reverseLastIdx + 1);
changeIdx = reverseLastIdx + 1;
continue;
}
// We need space to encode '-length', the new span ID,
// reverting back to the original ID, and a split span length
uint8[] textIdData = new uint8[mLength + mChangeList.Count*16];
int prevCharIdx = 0;
int prevEncodeIdx = 0;
int prevLastSpanLength = 0;
int prevLastSpanIdStart = 1;
int curEncodeIdx = 0;
int curSpanIdStart = 1;
bool foundSpan = false;
int ignoreLength = 0;
int32 index;
int32 length;
int32 curId = -1;
ChangeKind changeKind;
switch (mChangeList[changeIdx++])
{
case .Insert(out index, out curId, out length):
changeKind = .Insert;
case .Remove(out index, out length):
changeKind = .Remove;
}
while (prevLastSpanIdStart != -1)
{
if (ignoreLength > 0)
{
int handleLength = Math.Min(prevLastSpanLength, ignoreLength);
ignoreLength -= handleLength;
prevLastSpanIdStart += handleLength;
prevLastSpanLength -= handleLength;
}
if ((curSpanIdStart != prevLastSpanIdStart) && (prevLastSpanLength > 0) && (ignoreLength == 0))
{
Utils.EncodeInt(textIdData, ref curEncodeIdx, prevLastSpanIdStart);
curSpanIdStart = prevLastSpanIdStart;
}
if ((prevCharIdx + prevLastSpanLength >= index) && (!foundSpan) && (ignoreLength == 0))
{
foundSpan = true;
if (curSpanIdStart != prevLastSpanIdStart)
{
Utils.EncodeInt(textIdData, ref curEncodeIdx, prevLastSpanIdStart);
curSpanIdStart = prevLastSpanIdStart;
}
if (changeKind case .Insert)
{
// Time to insert
int leftSplitLen = index - prevCharIdx;
if (leftSplitLen > 0)
{
Utils.EncodeInt(textIdData, ref curEncodeIdx, -leftSplitLen);
curSpanIdStart += leftSplitLen;
prevLastSpanIdStart += leftSplitLen;
prevCharIdx += leftSplitLen;
prevLastSpanLength -= leftSplitLen;
}
if (curSpanIdStart != curId)
{
curSpanIdStart = curId;
Utils.EncodeInt(textIdData, ref curEncodeIdx, curSpanIdStart);
}
curId += length;
if (length > 0)
{
Utils.EncodeInt(textIdData, ref curEncodeIdx, -length);
curSpanIdStart += length;
prevCharIdx += length;
}
}
else
{
ignoreLength = length;
// Time to insert
int leftSplitLen = index - prevCharIdx;
if (leftSplitLen > 0)
{
Utils.EncodeInt(textIdData, ref curEncodeIdx, -leftSplitLen);
curSpanIdStart += leftSplitLen;
prevLastSpanIdStart += leftSplitLen;
prevCharIdx += leftSplitLen;
prevLastSpanLength -= leftSplitLen;
}
}
if (changeIdx < mChangeList.Count)
{
switch (mChangeList[changeIdx])
{
case .Insert(out index, out curId, out length):
changeKind = .Insert;
case .Remove(out index, out length):
changeKind = .Remove;
}
if (index >= prevCharIdx)
{
// We are inserting into a just-removed location
foundSpan = false;
changeIdx++;
}
}
continue;
}
int cmd = Utils.DecodeInt(mData, ref prevEncodeIdx);
if (cmd >= 0)
{
if (prevLastSpanLength > 0)
{
Utils.EncodeInt(textIdData, ref curEncodeIdx, -prevLastSpanLength);
curSpanIdStart += prevLastSpanLength;
prevLastSpanIdStart += prevLastSpanLength;
prevCharIdx += prevLastSpanLength;
prevLastSpanLength = 0;
}
Debug.Assert(prevLastSpanLength == 0);
prevLastSpanIdStart = cmd;
if (cmd == 0)
break;
}
else
prevLastSpanLength += -cmd;
}
Utils.EncodeInt(textIdData, ref curEncodeIdx, 0);
mLength = (int32)curEncodeIdx;
if (mData != sEmptyData)
delete mData;
mData = textIdData;
}
DeleteAndNullify!(mChangeList);
}
public IdSpan GetPrepared() mut
{
Prepare();
return this;
}
public void Dispose() mut
{
if (mData != sEmptyData)
delete mData;
delete mChangeList;
mData = sEmptyData;
mLength = 0;
}
public void DuplicateFrom(ref IdSpan span) mut
{
Dispose();
this = span.Duplicate();
}
public static readonly IdSpan Empty = IdSpan(sEmptyData, 1);
public void Insert(int index, int length, ref int32 curId) mut
{
var index;
var length;
if (mChangeList == null)
mChangeList = new .();
else if (mChangeList.Count > 0)
{
var change = ref mChangeList.Back;
if (change case .Insert(let prevIndex, let prevId, var ref prevLength))
{
if ((prevIndex + prevLength == index) && (prevId + prevLength == curId))
{
int16 curLen = (int16)Math.Min(length, 0x7FFF - prevLength);
prevLength += curLen;
curId += curLen;
index += curLen;
length -= curLen;
}
}
}
while (length > 0)
{
int16 curLen = (int16)Math.Min(length, 0x7FFF);
mChangeList.Add(.Insert((int32)index, curId, curLen));
curId += curLen;
index += curLen;
length -= curLen;
}
if (mAlwaysPrepare)
Prepare();
else
MaybePrepare();
}
public void Remove(int index, int length) mut
{
var index;
var length;
if (mChangeList == null)
mChangeList = new .();
else if (mChangeList.Count > 0)
{
var change = ref mChangeList.Back;
if (change case .Remove(let prevIndex, var ref prevLength))
{
if (prevIndex == index)
{
int16 curLen = (int16)Math.Min(length, 0x7FFF - prevLength);
prevLength += curLen;
length -= curLen;
}
}
}
while (length > 0)
{
int16 curLen = (int16)Math.Min(length, 0x7FFF);
mChangeList.Add(.Remove((int32)index, curLen));
length -= curLen;
}
if (mAlwaysPrepare)
Prepare();
else
MaybePrepare();
}
public int GetIndexFromId(int32 findCharId)
{
Debug.Assert(!HasChangeList);
int encodeIdx = 0;
int charId = 1;
int charIdx = 0;
while (true)
{
int cmd = Utils.DecodeInt(mData, ref encodeIdx);
if (cmd > 0)
charId = cmd;
else
{
int spanSize = -cmd;
if ((findCharId >= charId) && (findCharId < charId + spanSize))
return charIdx + (findCharId - charId);
charId += spanSize;
charIdx += spanSize;
if (cmd == 0)
return -1;
}
}
}
public int32 GetIdAtIndex(int findIndex)
{
int encodeIdx = 0;
int32 charId = 1;
int charIdx = 0;
while (true)
{
int32 cmd = Utils.DecodeInt(mData, ref encodeIdx);
if (cmd > 0)
charId = cmd;
else
{
int32 spanSize = -cmd;
if ((findIndex >= charIdx) && (findIndex < charIdx + spanSize))
return charId + (int32)(findIndex - charIdx);
charId += spanSize;
charIdx += spanSize;
if (cmd == 0)
return -1;
}
}
}
public IdSpan Duplicate()
{
Debug.Assert(!HasChangeList);
IdSpan idSpan = IdSpan();
if (mData != null)
{
idSpan.mData = new uint8[mLength];
mData.CopyTo(idSpan.mData, 0, 0, mLength);
idSpan.mLength = mLength;
}
return idSpan;
}
public bool Equals(IdSpan idData2)
{
Debug.Assert(!HasChangeList);
Debug.Assert(!idData2.HasChangeList);
if ((mLength == 0) || (idData2.mLength == 0))
return (mLength == 0) && (idData2.mLength == 0);
int encodeIdx1 = 0;
int encodeIdx2 = 0;
int curSpanId1 = 1;
int curSpanId2 = 1;
int spanLen1 = 0;
int spanLen2 = 0;
while (true)
{
while (spanLen1 == 0)
{
int cmd = Utils.DecodeInt(mData, ref encodeIdx1);
if (cmd < 0)
{
spanLen1 = -cmd;
}
else
{
curSpanId1 = cmd;
if (cmd == 0)
break;
}
}
while (spanLen2 == 0)
{
int32 cmd = Utils.DecodeInt(idData2.mData, ref encodeIdx2);
if (cmd < 0)
{
spanLen2 = -cmd;
}
else
{
curSpanId2 = cmd;
if (cmd == 0)
{
// They are equal if both spans are at the end
return spanLen1 == 0;
}
}
}
if (curSpanId1 != curSpanId2)
return false;
int minLen = Math.Min(spanLen1, spanLen2);
curSpanId1 += minLen;
spanLen1 -= minLen;
curSpanId2 += minLen;
spanLen2 -= minLen;
}
}
public int GetTotalLength()
{
int len = 0;
int encodeIdx = 0;
while (true)
{
int cmd = Utils.DecodeInt(mData, ref encodeIdx);
if (cmd == 0)
return len;
if (cmd < 0)
len += -cmd;
}
}
static bool FindId(uint8[] idData, ref int encodeIdx, ref int32 curSpanId, ref int32 spanLen, int32 findSpanId)
{
while (true)
{
int32 cmd = Utils.DecodeInt(idData, ref encodeIdx);
if (cmd < 0)
{
spanLen = -cmd;
if ((findSpanId >= curSpanId) && (findSpanId < curSpanId + spanLen))
{
int32 delta = findSpanId - curSpanId;
curSpanId += delta;
spanLen -= delta;
return true;
}
curSpanId += spanLen;
}
else
{
curSpanId = cmd;
if (cmd == 0)
return false;
}
}
}
public bool IsRangeEqual(IdSpan idData2, int32 startCharId, int32 endCharId)
{
int encodeIdx1 = 0;
int encodeIdx2 = 0;
int32 curSpanId1 = 1;
int32 curSpanId2 = 1;
int32 spanLen1 = 0;
int32 spanLen2 = 0;
if (!FindId(mData, ref encodeIdx1, ref curSpanId1, ref spanLen1, startCharId))
return false;
if (!FindId(idData2.mData, ref encodeIdx2, ref curSpanId2, ref spanLen2, startCharId))
return false;
while (true)
{
while (spanLen1 == 0)
{
int32 cmd = Utils.DecodeInt(mData, ref encodeIdx1);
if (cmd < 0)
{
spanLen1 = -cmd;
}
else
{
curSpanId1 = cmd;
if (cmd == 0)
break;
}
}
while (spanLen2 == 0)
{
int32 cmd = Utils.DecodeInt(idData2.mData, ref encodeIdx2);
if (cmd < 0)
{
spanLen2 = -cmd;
}
else
{
curSpanId2 = cmd;
if (cmd == 0)
{
// They are equal if both spans are at the end
return spanLen1 == 0;
}
}
}
if (curSpanId1 != curSpanId2)
return false;
if (curSpanId1 == endCharId)
return true;
int minLen = Math.Min(spanLen1, spanLen2);
if ((endCharId >= curSpanId1) && (endCharId < curSpanId1 + minLen))
minLen = Math.Min(minLen, endCharId - curSpanId1);
if ((endCharId >= curSpanId2) && (endCharId < curSpanId2 + minLen))
minLen = Math.Min(minLen, endCharId - curSpanId2);
curSpanId1 += (int32)minLen;
spanLen1 -= (.)minLen;
curSpanId2 += (int32)minLen;
spanLen2 -= (.)minLen;
}
}
public static IdSpan GetDefault(int32 length)
{
uint8[] idData = new uint8[8];
int encodeIdx = 0;
Utils.EncodeInt(idData, ref encodeIdx, (int32)-length);
Utils.EncodeInt(idData, ref encodeIdx, 0);
IdSpan idSpan = IdSpan();
idSpan.mData = idData;
idSpan.mLength = (int32)encodeIdx;
return idSpan;
}
public void Dump() mut
{
Prepare();
Debug.WriteLine("IdSpan Dump:");
int encodeIdx = 0;
int charId = 1;
int charIdx = 0;
while (true)
{
int32 cmd = Utils.DecodeInt(mData, ref encodeIdx);
if (cmd > 0)
{
charId = cmd;
Debug.WriteLine(" Id: {0}", charId);
}
else
{
int32 spanSize = -cmd;
charId += spanSize;
charIdx += spanSize;
if (cmd == 0)
return;
Debug.WriteLine(" Len: {0}", spanSize);
}
}
}
}
}

View file

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime;
using System.Diagnostics;
namespace Beefy.utils
{
public static class ManualBreak
{
public static void Break()
{
ThrowUnimplemented();
//Debugger.Break();
}
}
}

View file

@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
namespace Beefy.utils
{
public abstract class PerfTimer
{
[StdCall, CLink]
extern static void PerfTimer_ZoneStart(char8* name);
[StdCall, CLink]
extern static void PerfTimer_ZoneEnd();
[StdCall, CLink]
extern static void PerfTimer_Message(char8* theString);
[StdCall, CLink]
extern static int32 PerfTimer_IsRecording();
[StdCall, CLink]
extern static void PerfTimer_StartRecording();
[StdCall, CLink]
extern static void PerfTimer_StopRecording();
[StdCall, CLink]
extern static void PerfTimer_DbgPrint();
static DisposeProxy mZoneEndDisposeProxy ~ delete _;
public static DisposeProxy ZoneStart(String name)
{
if (mZoneEndDisposeProxy == null)
mZoneEndDisposeProxy = new DisposeProxy(new => ZoneEnd);
PerfTimer_ZoneStart(name);
return mZoneEndDisposeProxy;
}
public static void ZoneEnd()
{
PerfTimer_ZoneEnd();
}
public static void Message(String theString)
{
PerfTimer_Message(theString);
}
public static void Message(String format, params Object[] theParams)
{
String outStr = scope String();
outStr.AppendF(format, params theParams);
Message(outStr);
}
public static bool IsRecording()
{
return PerfTimer_IsRecording() != 0;
}
public static void StartRecording()
{
PerfTimer_StartRecording();
}
public static void StopRecording()
{
PerfTimer_StopRecording();
}
public static void DbgPrint()
{
PerfTimer_DbgPrint();
}
}
}

View file

@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Beefy.utils
{
public class SmoothValue
{
public double mSrc;
public double mDest;
public float mPct;
public float mSpeed = 1.0f;
public float mSpeedScale = 1.0f;
public double v
{
get
{
if (mPct == 1.0f)
return mDest;
return mSrc + (mDest - mSrc) * EasedPct;
}
}
public double EasedPct
{
get
{
if (mPct == 1.0f)
return 1.0f;
return Utils.EaseInAndOut(mPct);
}
}
public bool IsMoving
{
get { return mPct != 1.0f; }
}
public void Update()
{
mPct = Math.Min(1.0f, mPct + mSpeed * mSpeedScale);
}
public void Set(double val, bool immediate = false)
{
if ((!immediate) && (val == mDest))
return;
if ((mPct != 1.0f) && (mPct != 0.0f))
{
double cur = v;
if (mPct > 0.80f)
mPct = 0.80f;
mDest = val;
mSrc = -(cur - mDest * EasedPct) / (EasedPct - 1);
mSpeedScale = (1.0f - mPct);
}
else
{
mSrc = v;
mPct = 0.0f;
mSpeedScale = 1.0f;
mDest = val;
}
if (immediate)
mPct = 1.0f;
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,161 @@
using System;
using System.Collections.Generic;
namespace Beefy.utils
{
class TextSearcher
{
enum CmdKind
{
Contains,
NotContains,
Equals,
}
struct CmdElement
{
public CmdKind mKind;
public String mString;
}
List<CmdElement> mCmds = new List<CmdElement>() ~
{
for (var cmdElement in mCmds)
{
delete cmdElement.mString;
}
delete _;
};
Result<void> EndCmd(String findStr, ref char8 startChar, String outError, OperatorDelegate operatorHandler)
{
if (findStr.IsEmpty)
return .Ok;
CmdElement cmdElement;
if ((findStr.StartsWith(":")) && (operatorHandler != null))
{
int opIdx = -1;
for (int i < findStr.Length)
{
char8 c = findStr[i];
if ((c == '=') || (c == '>') || (c == '<'))
{
if (opIdx != -1)
{
outError.Append("Multiple operators cannot be defined in the same text segment");
return .Err;
}
opIdx = i;
}
}
if (opIdx != -1)
{
if (startChar != 0)
{
outError.Append("Multiple operators cannot be defined in the same text segment");
return .Err;
}
if ((operatorHandler == null) || (!operatorHandler(scope String(findStr, 0, opIdx), findStr[opIdx], scope String(findStr, opIdx + 1))))
{
outError.AppendF("Invalid expression: {0}", findStr);
return .Err;
}
return .Ok;
}
}
if (startChar == '=')
cmdElement.mKind = .Equals;
else if (startChar == '-')
cmdElement.mKind = .NotContains;
else
cmdElement.mKind = .Contains;
cmdElement.mString = new String(findStr);
mCmds.Add(cmdElement);
findStr.Clear();
startChar = 0;
return .Ok;
}
delegate bool OperatorDelegate(String lhs, char8 op, String rhs);
public Result<void> Init(String searchStr, String outError, OperatorDelegate operatorHandler = null)
{
bool inQuote = false;
char8 startChar = 0;
String findStr = scope String();
for (int i < searchStr.Length)
{
var c = searchStr[i];
if (c == '\"')
{
inQuote = !inQuote;
continue;
}
if (c == '\"')
{
c = searchStr[++i];
findStr.Append(c);
continue;
}
if ((c == ' ') && (!inQuote))
{
Try!(EndCmd(findStr, ref startChar, outError, operatorHandler));
continue;
}
if ((startChar == 0) && (findStr.IsEmpty) && (!inQuote))
{
if ((c == '=') || (c == '-'))
{
startChar = c;
continue;
}
}
findStr.Append(c);
}
Try!(EndCmd(findStr, ref startChar, outError, operatorHandler));
return .Ok;
}
public bool Matches(String checkStr)
{
for (var cmd in ref mCmds)
{
switch (cmd.mKind)
{
case .Contains:
if (checkStr.IndexOf(cmd.mString, true) == -1)
return false;
case .NotContains:
if (checkStr.IndexOf(cmd.mString, true) != -1)
return false;
case .Equals:
if (!String.Equals(checkStr, cmd.mString, .OrdinalIgnoreCase))
return false;
}
}
return true;
}
public bool IsEmpty
{
get
{
return mCmds.Count == 0;
}
}
}
}

View file

@ -0,0 +1,311 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
namespace Beefy.utils
{
public class UndoAction
{
public virtual bool Merge(UndoAction nextAction)
{
return false;
}
public virtual bool Undo()
{
return true;
}
public virtual bool Redo()
{
return true;
}
public virtual int32 GetCost()
{
return 1;
}
}
public interface IUndoBatchStart
{
IUndoBatchEnd BatchEnd { get; }
String Name { get; }
}
public interface IUndoBatchEnd
{
IUndoBatchStart BatchStart { get; }
String Name { get; }
}
public class UndoBatchStart : UndoAction, IUndoBatchStart
{
public String mName;
public UndoBatchEnd mBatchEnd;
public int32 mBatchInsertIdx;
public int32 mBatchSize = 0; // Don't close out until we add the end
public String Name
{
get
{
return mName;
}
}
public this(String name)
{
mName = name;
mBatchEnd = new UndoBatchEnd();
mBatchEnd.mBatchStart = this;
}
public IUndoBatchEnd BatchEnd
{
get
{
return mBatchEnd;
}
}
public override int32 GetCost()
{
return Math.Min(mBatchSize, 256); // Don't allow a large batch (ie: rename) to cause us to pull too much out of the undo buffer
}
}
public class UndoBatchEnd : UndoAction, IUndoBatchEnd
{
public IUndoBatchStart mBatchStart;
public String Name
{
get
{
return mBatchStart.Name;
}
}
public IUndoBatchStart BatchStart
{
get
{
return mBatchStart;
}
}
}
public class UndoManager
{
List<UndoAction> mUndoList = new List<UndoAction>() ~ DeleteContainerAndItems!(_);
int32 mUndoIdx = 0;
int32 mMaxCost = 8192;
int32 mCurCost = 0;
bool mSkipNextMerge; // Don't merge after we do an undo or redo step
int32 mFreezeDeletes;
public ~this()
{
Clear();
}
public void WithActions(Action<UndoAction> func)
{
for (var action in mUndoList)
func(action);
}
public void Clear()
{
while (mUndoList.Count > 0)
{
var undoAction = mUndoList.PopBack();
delete undoAction;
}
mUndoIdx = 0;
mCurCost = 0;
}
public void Add(UndoAction action, bool allowMerge = true)
{
if (mFreezeDeletes == 0)
mCurCost += action.GetCost();
if (action is IUndoBatchStart)
{
mFreezeDeletes++;
if (var undoBatchStart = action as UndoBatchStart)
{
undoBatchStart.mBatchInsertIdx = GetActionCount();
}
}
else if (action is IUndoBatchEnd)
{
mFreezeDeletes--;
if (var undoBatchEnd = action as UndoBatchEnd)
{
if (var undoBatchStart = undoBatchEnd.mBatchStart as UndoBatchStart)
{
undoBatchStart.mBatchSize = GetActionCount() - undoBatchStart.mBatchInsertIdx;
int32 cost = undoBatchStart.GetCost();
Debug.Assert(cost >= 0);
mCurCost += cost;
}
}
}
if (mUndoIdx < mUndoList.Count)
{
int32 batchDepth = 0;
for (int checkIdx = mUndoIdx; checkIdx < mUndoList.Count; checkIdx++)
{
var checkAction = mUndoList[checkIdx];
if (batchDepth == 0)
mCurCost -= checkAction.GetCost();
if (checkAction is IUndoBatchStart)
batchDepth++;
else if (checkAction is IUndoBatchEnd)
batchDepth--;
delete checkAction;
}
Debug.Assert(batchDepth == 0);
mUndoList.RemoveRange(mUndoIdx, mUndoList.Count - mUndoIdx);
}
if ((allowMerge) && (!mSkipNextMerge))
{
UndoAction prevAction = GetLastUndoAction();
if (prevAction is UndoBatchStart)
{
var undoBatchStart = (UndoBatchStart)prevAction;
if (undoBatchStart.mBatchEnd == action)
{
// It's an empty batch!
mUndoList.PopBack();
delete prevAction;
delete action;
mUndoIdx--;
return;
}
}
if ((prevAction != null) && (prevAction.Merge(action)))
{
delete action;
return;
}
}
mSkipNextMerge = false;
mUndoList.Add(action);
mUndoIdx++;
if ((mCurCost > mMaxCost) && (mFreezeDeletes == 0))
{
int32 wantCost = (int32)(mMaxCost * 0.8f); // Don't just remove one item at a time
int32 checkIdx = 0;
int32 batchDepth = 0;
while (((mCurCost > wantCost) || (batchDepth > 0)) &&
(checkIdx < mUndoList.Count))
{
var checkAction = mUndoList[checkIdx];
if (batchDepth == 0)
mCurCost -= checkAction.GetCost();
if (checkAction is IUndoBatchStart)
batchDepth++;
else if (checkAction is IUndoBatchEnd)
batchDepth--;
delete checkAction;
checkIdx++;
}
mUndoList.RemoveRange(0, checkIdx);
mUndoIdx -= checkIdx;
}
}
public UndoAction GetLastUndoAction()
{
if (mUndoIdx == 0)
return null;
return mUndoList[mUndoIdx - 1];
}
public bool Undo()
{
mSkipNextMerge = true;
if (mUndoIdx == 0)
return false;
var undoAction = mUndoList[mUndoIdx - 1];
if (IUndoBatchEnd undoBatchEnd = undoAction as IUndoBatchEnd)
{
while (true)
{
if (!undoAction.Undo())
return false;
if (mUndoIdx == 0)
return false;
mUndoIdx--;
if ((undoAction == undoBatchEnd.BatchStart) || (mUndoIdx == 0))
return true;
undoAction = mUndoList[mUndoIdx - 1];
}
}
if (!undoAction.Undo())
return false;
mUndoIdx--;
return true;
}
public bool Redo()
{
mSkipNextMerge = true;
if (mUndoIdx >= mUndoList.Count)
return false;
int32 startUndoIdx = mUndoIdx;
var undoAction = mUndoList[mUndoIdx];
if (undoAction is UndoBatchStart)
{
UndoBatchStart undoBatchStart = (UndoBatchStart)undoAction;
while (true)
{
if (!undoAction.Redo())
{
mUndoIdx = startUndoIdx;
return false;
}
if (mUndoIdx >= mUndoList.Count)
return false;
mUndoIdx++;
if (undoAction == undoBatchStart.mBatchEnd)
return true;
undoAction = mUndoList[mUndoIdx];
}
}
if (!undoAction.Redo())
return false;
mUndoIdx++;
return true;
}
public int32 GetActionCount()
{
return mUndoIdx;
}
}
}

View file

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Text;
using Beefy.gfx;
using System.Runtime.InteropServices;
namespace Beefy.widgets
{
public class ButtonWidget : Widget
{
public bool mDisabled;
public override void Draw(Graphics g)
{
}
public override void KeyDown(KeyCode keyCode, bool isRepeat)
{
base.KeyDown(keyCode, isRepeat);
if ((keyCode == KeyCode.Return) || (keyCode == KeyCode.Space))
MouseDown(0, 0, 3, 1);
else
mParent.KeyDown(keyCode, isRepeat);
}
public override void KeyUp(KeyCode keyCode)
{
base.KeyUp(keyCode);
if ((keyCode == KeyCode.Return) || (keyCode == KeyCode.Space))
{
if (mMouseFlags != 0)
MouseUp(0, 0, 3);
}
else
mParent.KeyUp(keyCode);
}
}
}

View file

@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
namespace Beefy.widgets
{
public abstract class CheckBox : Widget
{
public enum State
{
Unchecked,
Checked,
Indeterminate
}
[DesignEditable]
public abstract bool Checked { get; set; }
public virtual State State
{
get
{
return Checked ? .Checked : .Unchecked;
}
set
{
Debug.Assert(value != .Indeterminate);
Checked = value != .Unchecked;
}
}
public Event<Action> mOnValueChanged ~ _.Dispose();
public override void MouseDown(float x, float y, int32 btn, int32 btnCount)
{
base.MouseDown(x, y, btn, btnCount);
}
public override void MouseUp(float x, float y, int32 btn)
{
if (mMouseOver)
{
Checked = !Checked;
mOnValueChanged();
}
base.MouseUp(x, y, btn);
}
}
}

View file

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Beefy.widgets
{
public class ComboBox : Widget
{
public bool mDisabled;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Beefy.widgets
{
public struct DesignEditableAttribute : Attribute
{
public String DisplayType { get; set; }
public String Group { get; set; }
public String DisplayName { get; set; }
public String SortName { get; set; }
public bool DefaultEditString { get; set; }
}
}

View file

@ -0,0 +1,383 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using Beefy.gfx;
using Beefy.events;
namespace Beefy.widgets
{
public delegate void DialogEventHandler(DialogEvent theEvent);
interface IHotKeyHandler
{
bool Handle(KeyCode keyCode);
}
public abstract class Dialog : Widget
{
public bool mInPopupWindow;
public String mTitle ~ delete _;
public String mText ~ delete _;
public Image mIcon;
public BFWindowBase.Flags mWindowFlags = .ClientSized | .TopMost | .Caption | .Border | .SysMenu | .Modal;
public List<ButtonWidget> mButtons = new List<ButtonWidget>() ~ delete _;
public List<Widget> mTabWidgets = new List<Widget>() ~ delete _;
public Dictionary<ButtonWidget, DialogEventHandler> mHandlers = new Dictionary<ButtonWidget, DialogEventHandler>() ~ delete _;
public ButtonWidget mDefaultButton;
public ButtonWidget mEscButton;
public EditWidget mDialogEditWidget;
public bool mClosed;
public Event<Action> mOnClosed ~ _.Dispose();
static String STRING_OK = "Ok";
static String STRING_CANCEL = "Cancel";
static String STRING_YES = "Yes";
static String STRING_NO = "No";
static String STRING_CLOSE = "Close";
public String Title
{
get
{
return mTitle;
}
set
{
String.NewOrSet!(mTitle, value);
if (mWidgetWindow != null)
mWidgetWindow.SetTitle(mTitle);
}
}
public this(String title = null, String text = null, Image icon = null)
{
if (title != null)
mTitle = new String(title);
if (text != null)
mText = new String(text);
mIcon = icon;
}
public ~this()
{
for (var handler in mHandlers.Values)
delete handler;
}
public virtual void Close()
{
mClosed = true;
if (mWidgetWindow == null)
return;
if (mInPopupWindow)
{
BFWindow lastChildWindow = null;
if (mWidgetWindow.mParent.mChildWindows != null)
{
for (var checkWindow in mWidgetWindow.mParent.mChildWindows)
if (checkWindow != mWidgetWindow)
lastChildWindow = checkWindow;
if (lastChildWindow != null)
lastChildWindow.SetForeground();
}
if (lastChildWindow == null)
{
if (!mWidgetWindow.mParent.mHasClosed)
mWidgetWindow.mParent.SetForeground();
}
mWidgetWindow.mOnWindowKeyDown.Remove(scope => WindowKeyDown, true);
mWidgetWindow.Close(true);
mInPopupWindow = false;
if (mOnClosed.HasListeners)
mOnClosed();
}
}
public virtual ButtonWidget CreateButton(String label)
{
return null;
}
public virtual EditWidget CreateEditWidget()
{
return null;
}
void HandleMouseClick(MouseEvent theEvent)
{
ButtonWidget widget = (ButtonWidget) theEvent.mSender;
DialogEvent dialogEvent = scope DialogEvent();
dialogEvent.mButton = widget;
dialogEvent.mCloseDialog = true;
dialogEvent.mSender = this;
if (mHandlers[widget] != null)
mHandlers[widget](dialogEvent);
if (dialogEvent.mCloseDialog)
Close();
}
public virtual ButtonWidget AddButton(String label, DialogEventHandler handler = null)
{
Debug.AssertNotStack(handler);
ButtonWidget button = CreateButton(label);
mHandlers[button] = handler;
button.mOnMouseClick.Add(new => HandleMouseClick);
mButtons.Add(button);
mTabWidgets.Add(button);
AddWidget(button);
return button;
}
public virtual EditWidget AddEdit(String text, DialogEventHandler handler = null)
{
Debug.AssertNotStack(handler);
var editWidget = CreateEditWidget();
AddWidget(editWidget);
if (text != null)
{
editWidget.SetText(text);
editWidget.Content.SelectAll();
}
mTabWidgets.Add(editWidget);
if (mDialogEditWidget == null)
mDialogEditWidget = editWidget;
editWidget.mOnSubmit.Add(new => EditSubmitHandler);
editWidget.mOnCancel.Add(new => EditCancelHandler);
return editWidget;
}
public virtual EditWidget AddEdit(EditWidget editWidget, DialogEventHandler handler = null)
{
Debug.AssertNotStack(handler);
if (editWidget.mParent == null)
AddWidget(editWidget);
mTabWidgets.Add(editWidget);
if (mDialogEditWidget == null)
mDialogEditWidget = editWidget;
editWidget.mOnSubmit.Add(new => EditSubmitHandler);
editWidget.mOnCancel.Add(new => EditCancelHandler);
return editWidget;
}
public void AddDialogComponent(Widget widget)
{
mTabWidgets.Add(widget);
AddWidget(widget);
}
protected void EditSubmitHandler(EditEvent theEvent)
{
Submit();
}
protected void EditCancelHandler(EditEvent theEvent)
{
if (mEscButton != null)
mEscButton.MouseClicked(0, 0, 3);
}
public virtual void AddCloseButton(DialogEventHandler closeHandler, int32 theDefault = -1)
{
Debug.AssertNotStack(closeHandler);
#unwarn
ButtonWidget closeButton = AddButton(STRING_CLOSE, closeHandler);
}
public virtual void AddYesNoButtons(DialogEventHandler yesHandler = null, DialogEventHandler noHandler = null, int32 theDefault = -1, int32 escapeBtn = -1)
{
Debug.AssertNotStack(yesHandler);
Debug.AssertNotStack(noHandler);
ButtonWidget yesButton = AddButton(STRING_YES, yesHandler);
ButtonWidget noButton = AddButton(STRING_NO, noHandler);
mDefaultButton = (theDefault == 0) ? yesButton : (theDefault == 1) ? noButton : null;
mEscButton = (escapeBtn == 0) ? yesButton : (escapeBtn == 1) ? noButton : null;
}
public virtual void AddOkCancelButtons(DialogEventHandler okHandler = null, DialogEventHandler cancelHandler = null, int32 theDefault = -1, int32 escapeBtn = -1)
{
Debug.AssertNotStack(okHandler);
Debug.AssertNotStack(cancelHandler);
ButtonWidget okButton = AddButton(STRING_OK, okHandler);
ButtonWidget cancelButton = AddButton(STRING_CANCEL, cancelHandler);
mDefaultButton = (theDefault == 0) ? okButton : (theDefault == 1) ? cancelButton : null;
mEscButton = (escapeBtn == 0) ? okButton : (escapeBtn == 1) ? cancelButton : null;
}
public virtual void PreShow()
{
}
public virtual void CalcSize()
{
}
public virtual void ResizeComponents()
{
}
public override void Resize(float x, float y, float width, float height)
{
base.Resize(x, y, width, height);
ResizeComponents();
}
public override void AddedToParent()
{
if (mDialogEditWidget != null)
mDialogEditWidget.SetFocus();
else if (mDefaultButton != null)
mDefaultButton.SetFocus();
else
SetFocus();
}
public virtual void PopupWindow(WidgetWindow parentWindow, float offsetX = 0, float offsetY = 0)
{
if (mClosed)
return;
mInPopupWindow = true;
CalcSize();
ResizeComponents();
int desktopWidth;
int desktopHeight;
BFApp.sApp.GetDesktopResolution(out desktopWidth, out desktopHeight);
//TODO: Clip to desktop width / height
mWidth = Math.Min(desktopWidth - 32, mWidth);
mHeight = Math.Min(desktopHeight - 32, mHeight);
float aX = parentWindow.mClientX + (parentWindow.mClientWidth - mWidth) / 2 + offsetX;
float aY = parentWindow.mClientY + (parentWindow.mClientHeight - mHeight) / 2 + offsetY;
for (int32 i = 0; i < parentWindow.mChildWindows.Count; i++)
{
var otherChild = parentWindow.mChildWindows[i];
if (otherChild.mWindowFlags.HasFlag(BFWindowBase.Flags.Modal))
{
aX = Math.Max(aX, otherChild.mClientX + 16);
aY = Math.Max(aY, otherChild.mClientY + 16);
}
}
WidgetWindow widgetWindow = new WidgetWindow(parentWindow,
mTitle ?? "Dialog",
(int32)(aX), (int32)(aY),
(int32)mWidth, (int32)mHeight,
mWindowFlags,
this);
WindowCloseQueryHandler windowCloseHandler = new (evt) =>
{
Close();
return false;
};
widgetWindow.mOnWindowCloseQuery.Add(windowCloseHandler);
widgetWindow.mOnWindowKeyDown.Add(new => WindowKeyDown);
}
public virtual bool HandleTab(int dir)
{
return Widget.HandleTab(dir, mTabWidgets);
}
public virtual void Escape()
{
mEscButton.MouseClicked(0, 0, 3);
}
public virtual void Submit()
{
if ((mDefaultButton != null) && (!mDefaultButton.mDisabled))
mDefaultButton.MouseClicked(0, 0, 3);
}
void WindowKeyDown(KeyDownEvent evt)
{
if ((evt.mKeyCode != .Alt) && (mWidgetWindow.IsKeyDown(.Alt)) && (!mWidgetWindow.IsKeyDown(.Control)))
{
if (mChildWidgets != null)
{
for (var widget in mChildWidgets)
{
if (let hotKeyHandler = widget as IHotKeyHandler)
{
if (hotKeyHandler.Handle(evt.mKeyCode))
{
evt.mHandled = true;
return;
}
}
}
}
}
if (evt.mKeyCode == .Escape)
{
if (mWidgetWindow.mFocusWidget != null)
{
let widgetWindow = mWidgetWindow;
let focusWidget = widgetWindow.mFocusWidget;
widgetWindow.mFocusWidget.KeyDown(evt);
if (focusWidget != widgetWindow.mFocusWidget)
evt.mHandled = true; // Infers we handled the event
if (evt.mHandled)
return;
evt.mHandled = true;
}
if (mEscButton != null)
{
if ((mDefaultButton != null) && (mDefaultButton.mMouseDown))
{
mDefaultButton.mMouseFlags = default;
mDefaultButton.mMouseDown = false;
}
else
Escape();
evt.mHandled = true;
}
}
if ((evt.mKeyCode == .Return) && (mDefaultButton != null))
{
if ((!(mWidgetWindow.mFocusWidget is EditWidget)) &&
(!(mWidgetWindow.mFocusWidget is ButtonWidget)))
{
Submit();
}
}
if (evt.mKeyCode == KeyCode.Tab)
{
int32 dir = mWidgetWindow.IsKeyDown(KeyCode.Shift) ? -1 : 1;
if (HandleTab(dir))
{
evt.mHandled = true;
}
}
}
}
}

View file

@ -0,0 +1,185 @@
using System;
using System.Collections.Generic;
using System.Text;
using Beefy.gfx;
using System.Diagnostics;
namespace Beefy.widgets
{
public interface IDockable
{
bool CanDock(DockingFrame frame, DockedWidget refWidget, DockingFrame.WidgetAlign align);
void Dock(DockingFrame frame, DockedWidget refWidget, DockingFrame.WidgetAlign align);
void DrawDockPreview(Graphics g);
}
public interface ICustomDock
{
void Draw(Graphics g);
void Dock(IDockable dockingWidget);
}
public class DockedWidget : Widget, IDockable
{
public bool mHasFillWidget;
public bool mIsFillWidget;
public bool mAutoClose = true; // Close when last tab is removed
public float mRequestedWidth; // Set when we drag a sizer
public float mRequestedHeight; // Set when we drag a sizer
public float mSizePriority;
public DockingFrame mParentDockingFrame;
public bool mAllowInterpolate;
public float mInterpolatePct = 1.0f;
public float mStartRootX;
public float mStartRootY;
public float mStartWidth;
public float mStartHeight;
public float mEndX;
public float mEndY;
public float mEndWidth;
public float mEndHeight;
public this()
{
mClipMouse = true;
mClipGfx = true;
}
public virtual DockingFrame GetRootDockingFrame()
{
var parentFrame = mParentDockingFrame;
while (parentFrame != null)
{
if (parentFrame.mParentDockingFrame == null)
break;
parentFrame = parentFrame.mParentDockingFrame;
}
return parentFrame;
}
public virtual void Rehup()
{
mHasFillWidget = mIsFillWidget;
}
public virtual void SetRequestedSize(float width, float height)
{
mRequestedWidth = width;
mRequestedHeight = height;
}
public virtual ICustomDock GetCustomDock(IDockable dockable, float x, float y)
{
return null;
}
public bool CanDock(DockingFrame frame, DockedWidget refWidget, DockingFrame.WidgetAlign align)
{
return true;
}
public virtual void Dock(DockingFrame frame, DockedWidget refWidget, DockingFrame.WidgetAlign align)
{
if (mParentDockingFrame != null) // TODO: Undock
{
}
}
public virtual void DrawDockPreview(Graphics g)
{
using (g.PushColor(0x80FFFFFF))
DrawAll(g);
}
public void StartInterpolate(float rootX, float rootY, float width, float height)
{
mInterpolatePct = 0;
mStartRootX = rootX;
mStartRootY = rootY;
mStartWidth = mWidth;
mStartHeight = mHeight;
}
public void StartInterpolate()
{
if ((mAllowInterpolate) && (mInterpolatePct == 1.0f))
{
mInterpolatePct = 0;
mParent.SelfToRootTranslate(mX, mY, out mStartRootX, out mStartRootY);
mStartWidth = mWidth;
mStartHeight = mHeight;
mEndX = mX;
mEndY = mY;
mEndWidth = mWidth;
mEndHeight = mHeight;
}
}
void UpdateInterpolation()
{
if (mInterpolatePct != 1.0f)
{
mInterpolatePct = Math.Min(mInterpolatePct + 0.1f, 1.0f);
float startX;
float startY;
mParent.RootToSelfTranslate(mStartRootX, mStartRootY, out startX, out startY);
Resize(
Utils.Lerp(startX, mEndX, mInterpolatePct),
Utils.Lerp(startY, mEndY, mInterpolatePct),
Utils.Lerp(mStartWidth, mEndWidth, mInterpolatePct),
Utils.Lerp(mStartHeight, mEndHeight, mInterpolatePct));
}
}
public override void Update()
{
base.Update();
UpdateInterpolation();
Debug.Assert((mParent == mParentDockingFrame) || (mParentDockingFrame == null));
// Allow to interpolate after first update
mAllowInterpolate = true;
}
public virtual void ResizeDocked(float x, float y, float width, float height)
{
if (mInterpolatePct != 1.0f)
{
mEndX = x;
mEndY = y;
mEndWidth = width;
mEndHeight = height;
UpdateInterpolation();
}
else
Resize(x, y, width, height);
}
public override void Resize(float x, float y, float width, float height)
{
base.Resize(x, y, width, height);
}
}
public class DockingProxy : DockedWidget
{
Widget mWidget;
public this(Widget widget)
{
mWidget = widget;
AddWidget(mWidget);
}
public override void Resize(float x, float y, float width, float height)
{
base.Resize(x, y, width, height);
mWidget.Resize(0, 0, width, height);
}
}
}

View file

@ -0,0 +1,989 @@
using System;
using System.Collections.Generic;
using System.Text;
using Beefy.theme;
using Beefy.gfx;
using System.Diagnostics;
using Beefy.geom;
namespace Beefy.widgets
{
public class DockingFrame : DockedWidget
{
public enum WidgetAlign
{
Inside,
Left,
Right,
Top,
Bottom
};
public enum SplitType
{
None,
Horz,
Vert
};
public List<DockedWidget> mDockedWidgets = new List<DockedWidget>() ~ delete _;
public SplitType mSplitType = SplitType.None;
public float mMinWindowSize = 32;
public float mDragMarginSize = 64;
public float mDragWindowMarginSize = 10;
public float mWindowMargin = 0;
public float mSplitterSize = 6.0f;
public float mWindowSpacing = 2.0f;
public int32 mDownSplitterNum = -1;
public IDockable mDraggingDock;
public DockedWidget mDraggingRef;
public WidgetAlign mDraggingAlign;
public ICustomDock mDraggingCustomDock ~ delete (Object)_;
public virtual void AddDockedWidget(DockedWidget widget, DockedWidget refWidget, WidgetAlign align)
{
SplitType wantSplitType = ((align == WidgetAlign.Left) || (align == WidgetAlign.Right)) ?
SplitType.Horz : SplitType.Vert;
if (mDockedWidgets.Count == 0)
wantSplitType = SplitType.None;
if ((wantSplitType != mSplitType) && (mSplitType != SplitType.None))
{
DockingFrame newChildFrame = ThemeFactory.mDefault.CreateDockingFrame(this);
bool hasFillWidget = mHasFillWidget;
if (refWidget != null)
{
newChildFrame.mRequestedWidth = refWidget.mRequestedWidth;
newChildFrame.mRequestedHeight = refWidget.mRequestedHeight;
ReplaceDockedWidget(refWidget, newChildFrame);
// Just split out the one referenced widget
newChildFrame.mParentDockingFrame = this;
refWidget.mParentDockingFrame = newChildFrame;
newChildFrame.mDockedWidgets.Add(refWidget);
newChildFrame.AddWidget(refWidget);
newChildFrame.AddDockedWidget(widget, null, align);
ResizeContent(true);
return;
}
else
{
newChildFrame.mRequestedWidth = 0;
newChildFrame.mRequestedHeight = 0;
for (DockedWidget aDockedWidget in mDockedWidgets)
{
if (mSplitType == SplitType.Horz)
{
newChildFrame.mRequestedWidth += aDockedWidget.mRequestedWidth;
newChildFrame.mRequestedHeight = Math.Max(newChildFrame.mRequestedHeight, aDockedWidget.mRequestedHeight);
}
else
{
newChildFrame.mRequestedWidth = Math.Max(mRequestedWidth, aDockedWidget.mRequestedWidth);
newChildFrame.mRequestedHeight += aDockedWidget.mRequestedHeight;
}
RemoveWidget(aDockedWidget);
newChildFrame.mDockedWidgets.Add(aDockedWidget);
newChildFrame.AddWidget(aDockedWidget);
aDockedWidget.mParentDockingFrame = newChildFrame;
hasFillWidget |= aDockedWidget.mHasFillWidget;
}
mDockedWidgets.Clear();
}
newChildFrame.mParentDockingFrame = this;
newChildFrame.mHasFillWidget = hasFillWidget;
newChildFrame.mSplitType = mSplitType;
newChildFrame.mWidth = newChildFrame.mRequestedWidth;
newChildFrame.mHeight = newChildFrame.mRequestedHeight;
mDockedWidgets.Add(newChildFrame);
AddWidget(newChildFrame);
}
widget.mParentDockingFrame = this;
//TODO:
/*if (fillWidget != null)
{
// Don't take more than half remaining space at a time
widget.mRequestedWidth = Math.Min(fillWidget.mWidth / 2, widget.mRequestedWidth);
widget.mRequestedHeight = Math.Min(fillWidget.mHeight / 2, widget.mRequestedHeight);
}*/
widget.mRequestedWidth = Math.Max(mMinWindowSize, widget.mRequestedWidth);
widget.mRequestedHeight = Math.Max(mMinWindowSize, widget.mRequestedHeight);
if ((align == WidgetAlign.Left) || (align == WidgetAlign.Top))
{
if (refWidget != null)
mDockedWidgets.Insert(mDockedWidgets.IndexOf(refWidget), widget);
else
mDockedWidgets.Insert(0, widget);
}
else
{
if (refWidget != null)
mDockedWidgets.Insert(mDockedWidgets.IndexOf(refWidget) + 1, widget);
else
mDockedWidgets.Add(widget);
}
AddWidget(widget);
mSplitType = wantSplitType;
if ((widget.mHasFillWidget) || (widget.mIsFillWidget))
widget.GetRootDockingFrame().Rehup();
ResizeContent(true);
}
public override void Rehup()
{
bool allHaveSizes = true;
bool didHaveFillWidget = mHasFillWidget;
base.Rehup();
for (var dockedWiget in mDockedWidgets)
{
dockedWiget.mHasFillWidget = dockedWiget.mIsFillWidget;
dockedWiget.Rehup();
if (dockedWiget.mHasFillWidget)
{
mHasFillWidget = true;
}
if ((dockedWiget.mWidth == 0) || (dockedWiget.mHeight == 0))
allHaveSizes = false;
}
// If we turn off mHasFillWidget and go to an all-non-fill situation then we reset the size priority
if (didHaveFillWidget != mHasFillWidget)
{
for (var dockedWiget in mDockedWidgets)
dockedWiget.mSizePriority = 0;
//if (!mHasFillWidget)
if (allHaveSizes)
{
for (var dockedWidget in mDockedWidgets)
{
if (mSplitType == .Horz)
dockedWidget.mRequestedWidth = dockedWidget.mWidth;
else
dockedWidget.mRequestedHeight = dockedWidget.mHeight;
}
}
}
for (var dockedWiget in mDockedWidgets)
{
// Reset the size priority to be whatever the current size is, it should stabilize sizes when
// toggling mIsFillWidget
if (dockedWiget.mHasFillWidget == mHasFillWidget)
{
if (dockedWiget.mSizePriority == 0)
{
if (mSplitType == .Horz)
dockedWiget.mSizePriority = dockedWiget.mWidth;
else
dockedWiget.mSizePriority = dockedWiget.mHeight;
}
}
else
dockedWiget.mSizePriority = 0;
}
}
public override DockingFrame GetRootDockingFrame()
{
var parentFrame = this;
while (parentFrame != null)
{
if (parentFrame.mParentDockingFrame == null)
break;
parentFrame = parentFrame.mParentDockingFrame;
}
return parentFrame;
}
public void WithAllDockedWidgets(Action<DockedWidget> func)
{
for (var dockedWidget in mDockedWidgets)
{
func(dockedWidget);
var dockingFrame = dockedWidget as DockingFrame;
if (dockingFrame != null)
{
dockingFrame.WithAllDockedWidgets(func);
}
}
}
public int GetDockedWindowCount()
{
return mDockedWidgets.Count;
}
public void Simplify()
{
if ((mDockedWidgets.Count == 0) && (mParentDockingFrame != null))
mParentDockingFrame.RemoveWidget(this);
else if ((mDockedWidgets.Count == 1) && (mParentDockingFrame != null))
{
// Just a single object, remove ourselves from the frame
DockedWidget aDockedWidget = mDockedWidgets[0];
mDockedWidgets.Clear();
RemoveWidget(aDockedWidget);
mParentDockingFrame.ReplaceDockedWidget(this, aDockedWidget);
BFApp.sApp.DeferDelete(this);
}
}
public virtual void RemoveDockedWidget(DockedWidget dockedWidget)
{
mDockedWidgets.Remove(dockedWidget);
RemoveWidget(dockedWidget);
if (!mIsFillWidget)
mHasFillWidget = GetHasFillWidget();
ResizeContent(true);
if ((mDockedWidgets.Count == 0) && (mParentDockingFrame == null))
{
// Automatically close when last docked widget is removed
// Should only happen on tool windows
mWidgetWindow.Close();
}
}
// Used when an embedded docking frame gets down to just a single widget
public virtual void ReplaceDockedWidget(DockedWidget dockedWidget, DockedWidget replaceWidget)
{
int32 index = mDockedWidgets.IndexOf(dockedWidget);
RemoveWidget(dockedWidget);
mDockedWidgets[index] = replaceWidget;
AddWidget(replaceWidget);
replaceWidget.mParentDockingFrame = this;
ResizeContent(true);
}
protected virtual bool GetHasFillWidget()
{
for (DockedWidget aDockedWidget in mDockedWidgets)
if (aDockedWidget.mHasFillWidget)
return true;
return false;
}
public virtual void StartContentInterpolate()
{
for (DockedWidget aDockedWidget in mDockedWidgets)
{
aDockedWidget.StartInterpolate();
}
}
public virtual void ResizeContent(bool interpolate = false)
{
float sizeLeft = (mSplitType == SplitType.Horz) ? (mWidth - mWindowMargin*2) : (mHeight - mWindowMargin*2);
sizeLeft -= (mDockedWidgets.Count - 1) * mWindowSpacing;
if (sizeLeft <= 0)
return;
List<DockedWidget> widgetsLeft = scope List<DockedWidget>();
for (DockedWidget aDockedWidget in mDockedWidgets)
{
widgetsLeft.Add(aDockedWidget);
if (interpolate)
aDockedWidget.StartInterpolate();
}
//DockedWidget fillWidget = GetFillWidget();
//if (fillWidget != null)
{
bool hasFillWidget = false;
for (int32 widgetIdx = 0; widgetIdx < widgetsLeft.Count; widgetIdx++)
{
DockedWidget aDockedWidget = widgetsLeft[widgetIdx];
if (aDockedWidget.mHasFillWidget)
{
hasFillWidget = true;
break;
}
}
if (hasFillWidget)
{
for (int32 widgetIdx = 0; widgetIdx < widgetsLeft.Count; widgetIdx++)
{
DockedWidget aDockedWidget = widgetsLeft[widgetIdx];
if (aDockedWidget.mHasFillWidget)
continue;
float requestedSize = (mSplitType == SplitType.Horz) ? (aDockedWidget.mRequestedWidth) : aDockedWidget.mRequestedHeight;
float size = Math.Round(Math.Max(mMinWindowSize, Math.Min(requestedSize, sizeLeft - (widgetsLeft.Count * mMinWindowSize))));
if (mSplitType == SplitType.Horz)
aDockedWidget.mWidth = size;
else
aDockedWidget.mHeight = size;
sizeLeft -= size;
widgetsLeft.RemoveAt(widgetIdx);
widgetIdx--;
}
}
//widgetsLeft.Add(fillWidget);
/*if (mSplitType == SplitType.Horz)
fillWidget.mWidth = sizeLeft;
else
fillWidget.mHeight = sizeLeft;*/
}
//if (fillWidget == null)
{
int32 totalPriCount = 0;
int32 newPriCounts = 0;
float totalPriAcc = 0.0f;
float avgPri = 0;
for (DockedWidget aDockedWidget in widgetsLeft)
{
totalPriAcc += aDockedWidget.mSizePriority;
if (aDockedWidget.mSizePriority == 0)
newPriCounts++;
else
totalPriCount++;
}
if (totalPriCount == 0)
{
// Totally uninitialized, possibly from having a document frame and then removing it
for (DockedWidget aDockedWidget in widgetsLeft)
if ((aDockedWidget.mSizePriority == 0) && (aDockedWidget.mWidth > 0) && (aDockedWidget.mHeight > 0))
{
aDockedWidget.mSizePriority = (mSplitType == .Horz) ? aDockedWidget.mWidth : aDockedWidget.mHeight;
totalPriCount++;
totalPriAcc += aDockedWidget.mSizePriority;
newPriCounts--;
}
}
if (newPriCounts > 0)
{
if (totalPriAcc > 0)
avgPri = totalPriAcc / totalPriCount;
else
avgPri = 1.0f / newPriCounts;
for (DockedWidget aDockedWidget in widgetsLeft)
if (aDockedWidget.mSizePriority == 0)
{
aDockedWidget.mSizePriority = avgPri;
totalPriCount++;
totalPriAcc += aDockedWidget.mSizePriority;
}
}
float sharedWidth = sizeLeft;
for (int32 widgetIdx = 0; widgetIdx < widgetsLeft.Count; widgetIdx++)
{
DockedWidget aDockedWidget = widgetsLeft[widgetIdx];
float size = (aDockedWidget.mSizePriority / totalPriAcc) * sharedWidth;
size = (float) Math.Round(size);
if (widgetIdx == widgetsLeft.Count - 1)
size = sizeLeft;
if (mSplitType == SplitType.Horz)
{
aDockedWidget.mWidth = size;
}
else
{
aDockedWidget.mHeight = size;
}
sizeLeft -= size;
widgetsLeft.RemoveAt(widgetIdx);
widgetIdx--;
}
}
/*DockedWidget fillWidget = GetFillWidget();
if (fillWidget == null)
{
int totalPriCount = 0;
int newPriCounts = 0;
float totalPriAcc = 0.0f;
float avgPri = 0;
foreach (DockedWidget aDockedWidget in widgetsLeft)
{
totalPriAcc += aDockedWidget.mSizePriority;
if (aDockedWidget.mSizePriority == 0)
newPriCounts++;
else
totalPriCount++;
}
if (newPriCounts > 0)
{
if (totalPriAcc > 0)
avgPri = totalPriAcc / totalPriCount;
else
avgPri = 1.0f / newPriCounts;
foreach (DockedWidget aDockedWidget in widgetsLeft)
if (aDockedWidget.mSizePriority == 0)
{
aDockedWidget.mSizePriority = avgPri;
totalPriCount++;
totalPriAcc += aDockedWidget.mSizePriority;
}
}
float sharedWidth = sizeLeft;
for (int widgetIdx = 0; widgetIdx < widgetsLeft.Count; widgetIdx++)
{
DockedWidget aDockedWidget = widgetsLeft[widgetIdx];
float size = (aDockedWidget.mSizePriority / totalPriAcc) * sharedWidth;
size = (float) Math.Round(size);
if (widgetIdx == widgetsLeft.Count - 1)
size = sizeLeft;
if (mSplitType == SplitType.Horz)
{
aDockedWidget.mWidth = size;
}
else
{
aDockedWidget.mHeight = size;
}
sizeLeft -= size;
widgetsLeft.RemoveAt(widgetIdx);
widgetIdx--;
}
}
else
{
widgetsLeft.Remove(fillWidget);
for (int widgetIdx = 0; widgetIdx < widgetsLeft.Count; widgetIdx++)
{
DockedWidget aDockedWidget = widgetsLeft[widgetIdx];
float requestedSize = (mSplitType == SplitType.Horz) ? (aDockedWidget.mRequestedWidth) : aDockedWidget.mRequestedHeight;
float size = Math.Max(mMinWindowSize, Math.Min(requestedSize, sizeLeft - (widgetsLeft.Count * mMinWindowSize)));
if (mSplitType == SplitType.Horz)
aDockedWidget.mWidth = size;
else
aDockedWidget.mHeight = size;
sizeLeft -= size;
widgetsLeft.RemoveAt(widgetIdx);
widgetIdx--;
}
if (mSplitType == SplitType.Horz)
fillWidget.mWidth = sizeLeft;
else
fillWidget.mHeight = sizeLeft;
}*/
float curPos = mWindowMargin;
for (DockedWidget aDockedWidget in mDockedWidgets)
{
float size = (mSplitType == SplitType.Horz) ? aDockedWidget.mWidth : aDockedWidget.mHeight;
if (mSplitType == SplitType.Horz)
{
aDockedWidget.ResizeDocked(curPos, mWindowMargin, size, mHeight - mWindowMargin*2);
}
else
{
aDockedWidget.ResizeDocked(mWindowMargin, curPos, mWidth - mWindowMargin*2, size);
}
curPos += size;
curPos += mWindowSpacing;
}
}
public override void Resize(float x, float y, float width, float height)
{
base.Resize(x, y, width, height);
ResizeContent();
}
public int32 FindSplitterAt(float x, float y)
{
if (!Contains(x, y))
return -1;
float curPos = 0;
float findPos = (mSplitType == SplitType.Horz) ? (x - mWindowMargin) : (y - mWindowMargin);
for (int32 widgetIdx = 0; widgetIdx < mDockedWidgets.Count; widgetIdx++)
{
DockedWidget aDockedWidget = mDockedWidgets[widgetIdx];
float size = (mSplitType == SplitType.Horz) ? aDockedWidget.mWidth : aDockedWidget.mHeight;
float diff = (findPos - curPos) + mWindowSpacing + (mSplitterSize - mWindowSpacing) / 2.0f;
if ((diff >= 0) && (diff < mSplitterSize))
return widgetIdx - 1;
curPos += size;
curPos += mWindowSpacing;
}
return -1;
}
DockingFrame GetDockingFrame(WidgetWindow widgetWindow)
{
if (widgetWindow == null)
return null;
DockingFrame dockingFrame = widgetWindow.mRootWidget as DockingFrame;
if (dockingFrame != null)
return dockingFrame;
for (var child in widgetWindow.mRootWidget.mChildWidgets)
{
dockingFrame = child as DockingFrame;
if (dockingFrame != null)
return dockingFrame;
}
return null;
}
public virtual void ShowDragTarget(IDockable draggingItem)
{
Debug.Assert(mParentDockingFrame == null);
for (BFWindow window in BFApp.sApp.mWindows)
{
if (window.mAlpha != 1.0f)
continue;
WidgetWindow widgetWindow = window as WidgetWindow;
var dockingFrame = GetDockingFrame(widgetWindow);
if (dockingFrame != null)
{
if ((widgetWindow.mHasMouseInside) || (widgetWindow.mHasProxyMouseInside))
dockingFrame.ShowDragTarget(draggingItem, widgetWindow.mMouseX, widgetWindow.mMouseY);
else
dockingFrame.HideDragTargets();
}
}
}
public virtual void HideDragTarget(IDockable draggingItem, bool executeDrag = false)
{
Debug.Assert(mParentDockingFrame == null);
for (int32 windowIdx = 0; windowIdx < BFApp.sApp.mWindows.Count; windowIdx++)
{
BFWindow window = BFApp.sApp.mWindows[windowIdx];
WidgetWindow widgetWindow = window as WidgetWindow;
if (widgetWindow != null)
{
DockingFrame dockingFrame = GetDockingFrame(widgetWindow);
if (dockingFrame != null)
dockingFrame.HideDragTargets(executeDrag);
}
}
}
void HideDragTargets(bool executeDrag = false)
{
if ((executeDrag) && (mDraggingDock != null))
{
if (mDraggingCustomDock != null)
mDraggingCustomDock.Dock(mDraggingDock);
else
mDraggingDock.Dock(this, mDraggingRef, mDraggingAlign);
}
mDraggingDock = null;
mDraggingRef = null;
delete (Object)mDraggingCustomDock;
mDraggingCustomDock = null;
for (int32 dockedWidgetIdx = 0; dockedWidgetIdx < mDockedWidgets.Count; dockedWidgetIdx++)
{
DockedWidget aDockedWidget = mDockedWidgets[dockedWidgetIdx];
DockingFrame childFrame = aDockedWidget as DockingFrame;
if (childFrame != null)
childFrame.HideDragTargets(executeDrag);
}
}
bool FindDragTarget(IDockable draggingItem, float x, float y, ref DockingFrame containingFrame, ref DockedWidget refWidget, ref WidgetAlign align, ref ICustomDock customDock, int32 minDist = -2)
{
//int foundMinDist = -1;
bool foundInSelf = false;
for (DockedWidget aDockedWidget in mDockedWidgets)
{
float childX;
float childY;
aDockedWidget.ParentToSelfTranslate(x, y, out childX, out childY);
var rect = Rect(-2, -2, aDockedWidget.mWidth + 4, aDockedWidget.mHeight + 4);
//if (aDockedWidget.Contains(childX, childY))
if (rect.Contains(childX, childY))
{
float leftDist = childX;
float topDist = childY;
float rightDist = aDockedWidget.mWidth - childX;
float botDist = aDockedWidget.mHeight - childY;
if (Math.Min(Math.Min(Math.Min(leftDist, topDist), rightDist), botDist) < minDist)
continue;
float marginX = Math.Min(mDragMarginSize, aDockedWidget.mWidth / 4);
float marginY = Math.Min(mDragMarginSize, aDockedWidget.mHeight / 4);
if ((marginX < Math.Min(leftDist, rightDist)) && (marginY < Math.Min(topDist, botDist)))
align = WidgetAlign.Inside;
else if (leftDist < Math.Min(topDist, Math.Min(rightDist, botDist)))
align = WidgetAlign.Left;
else if (topDist < Math.Min(rightDist, botDist))
align = WidgetAlign.Top;
else if (rightDist < botDist)
align = WidgetAlign.Right;
else
align = WidgetAlign.Bottom;
if (draggingItem.CanDock(this, aDockedWidget, align))
{
customDock = aDockedWidget.GetCustomDock(draggingItem, childX, childY);
containingFrame = this;
refWidget = aDockedWidget;
foundInSelf = true;
break;
}
}
}
int32 newMinDist = minDist;
if (foundInSelf)
newMinDist = Math.Max(minDist, 0) + 2;
for (DockedWidget aDockedWidget in mDockedWidgets)
{
float childX;
float childY;
aDockedWidget.ParentToSelfTranslate(x, y, out childX, out childY);
if (aDockedWidget.Contains(childX, childY))
{
DockingFrame childFrame = aDockedWidget as DockingFrame;
if (childFrame != null)
{
childFrame.FindDragTarget(draggingItem, childX, childY, ref containingFrame, ref refWidget, ref align, ref customDock, newMinDist);
break;
}
}
}
if ((mParentDockingFrame == null) && (customDock == null))
{
if (Contains(x, y))
{
float leftDist = x;
float topDist = y;
float rightDist = mWidth - x;
float botDist = mHeight - y;
float marginX = Math.Min(mDragWindowMarginSize, mWidth / 4);
float marginY = Math.Min(mDragWindowMarginSize, mHeight / 4);
WidgetAlign anAlign;
if ((marginX < Math.Min(leftDist, rightDist)) && (marginY < Math.Min(topDist, botDist)))
anAlign = WidgetAlign.Inside;
else if (leftDist < Math.Min(topDist, Math.Min(rightDist, botDist)))
anAlign = WidgetAlign.Left;
else if (topDist < Math.Min(rightDist, botDist))
anAlign = WidgetAlign.Top;
else if (rightDist < botDist)
anAlign = WidgetAlign.Right;
else
anAlign = WidgetAlign.Bottom;
if ((anAlign != WidgetAlign.Inside) && (draggingItem.CanDock(this, null, align)))
{
containingFrame = this;
refWidget = null;
customDock = null;
align = anAlign;
}
}
}
return containingFrame != null;
}
void ShowDragTarget(IDockable draggingItem, float x, float y)
{
DockingFrame containingFrame = null;
DockedWidget refWidget = null;
WidgetAlign align = .Inside;
ICustomDock customDock = null;
/*containingFrame = null;
refWidget = null;
align = WidgetAlign.Inside;
customDock = null;*/
MarkDirty();
HideDragTargets();
if ((y < 0) && (y >= -6) && (x >= 0) && (x < mWidgetWindow.mWindowWidth))
{
containingFrame = this;
containingFrame.mDraggingDock = draggingItem;
containingFrame.mDraggingRef = null;
containingFrame.mDraggingAlign = .Top;
delete (Object)containingFrame.mDraggingCustomDock;
containingFrame.mDraggingCustomDock = null;
return;
}
if (FindDragTarget(draggingItem, x, y, ref containingFrame, ref refWidget, ref align, ref customDock))
{
containingFrame.mDraggingDock = draggingItem;
containingFrame.mDraggingRef = refWidget;
containingFrame.mDraggingAlign = align;
delete (Object)containingFrame.mDraggingCustomDock;
containingFrame.mDraggingCustomDock = customDock;
}
}
public override Widget FindWidgetByCoords(float x, float y)
{
if (FindSplitterAt(x, y) != -1)
return this;
return base.FindWidgetByCoords(x, y);
}
public override void MouseDown(float x, float y, int32 btn, int32 btnCount)
{
base.MouseDown(x, y, btn, btnCount);
mDownSplitterNum = FindSplitterAt(x, y);
}
public override void MouseMove(float x, float y)
{
base.MouseMove(x, y);
if (mDownSplitterNum != -1)
{
float wantPos = (mSplitType == SplitType.Horz) ? x : y;
DockedWidget resizedWidget = null;
DockedWidget widget1 = mDockedWidgets[mDownSplitterNum];
DockedWidget widget2 = mDockedWidgets[mDownSplitterNum + 1];
List<DockedWidget> widgetsLeft = scope List<DockedWidget>();
float sizeLeft = (mSplitType == SplitType.Horz) ? (mWidth - mWindowMargin*2) : (mHeight - mWindowMargin*2);
if ((widget1.mHasFillWidget) && (widget2.mHasFillWidget))
{
bool foundWidget = false;
for (var dockedWidget in mDockedWidgets)
{
if (dockedWidget == widget1)
foundWidget = true;
if (dockedWidget.mHasFillWidget)
widgetsLeft.Add(dockedWidget);
else
{
float curSize = (mSplitType == SplitType.Horz) ? dockedWidget.mWidth : dockedWidget.mHeight;
if (!foundWidget)
wantPos -= curSize;
sizeLeft -= curSize;
}
}
}
else if (widget1.mHasFillWidget != widget2.mHasFillWidget)
{
if (widget2.mHasFillWidget)
{
if (mSplitType == .Horz)
widget1.mRequestedWidth = wantPos - widget1.mX;
else
widget1.mRequestedHeight = wantPos - widget1.mY;
}
else
{
if (mSplitType == .Horz)
{
float totalSize = widget1.mWidth + widget2.mWidth;
widget2.mRequestedWidth = Math.Max(0, widget2.mRequestedWidth + (widget2.mX - wantPos));
widget2.mRequestedWidth = Math.Min(widget2.mRequestedWidth, totalSize - mMinWindowSize * 2);
}
else
{
float totalSize = widget1.mHeight + widget2.mHeight;
widget2.mRequestedHeight = Math.Max(0, widget2.mRequestedHeight + (widget2.mY - wantPos));
widget2.mRequestedHeight = Math.Min(widget2.mRequestedHeight, totalSize - mMinWindowSize * 2);
}
resizedWidget = widget2;
//Debug.WriteLine("RW:{0} WH:{1}", widget2.mRequestedWidth, widget2.mRequestedHeight);
}
}
else
{
bool hasFillWidget = false;
for (var dockedWidget in mDockedWidgets)
{
if (dockedWidget.mHasFillWidget)
hasFillWidget = true;
}
if (hasFillWidget)
{
for (var dockedWidget in mDockedWidgets)
{
// Size prioritizes will get initialized later if we remove the fill widget
if (!dockedWidget.mHasFillWidget)
dockedWidget.mSizePriority = 0;
}
if (mSplitType == .Horz)
{
float totalSize = widget1.mWidth + widget2.mWidth;
widget2.mRequestedWidth = Math.Clamp(widget2.mRequestedWidth + (widget2.mX - wantPos), mMinWindowSize, totalSize - mMinWindowSize);
widget1.mRequestedWidth = totalSize - widget2.mRequestedWidth;
}
else
{
float totalSize = widget1.mHeight + widget2.mHeight;
widget2.mRequestedHeight = Math.Clamp(widget2.mRequestedHeight + (widget2.mY - wantPos), mMinWindowSize, totalSize - mMinWindowSize);
widget1.mRequestedHeight = totalSize - widget2.mRequestedHeight;
}
}
else
{
for (var dockedWidget in mDockedWidgets)
widgetsLeft.Add(dockedWidget);
}
}
if (widgetsLeft.Count > 0)
{
// First we normalize to 1.0
float sizePriTotal = 0.0f;
for (DockedWidget aDockedWidget in widgetsLeft)
sizePriTotal += aDockedWidget.mSizePriority;
for (DockedWidget aDockedWidget in widgetsLeft)
aDockedWidget.mSizePriority = aDockedWidget.mSizePriority / sizePriTotal;
float totalPrevSize = widget1.mSizePriority + widget2.mSizePriority;
float startCurPos = 0;
for (DockedWidget aDockedWidget in widgetsLeft)
{
if (aDockedWidget == widget1)
break;
startCurPos += aDockedWidget.mSizePriority * sizeLeft + mWindowSpacing;
}
float wantSize = Math.Max(mMinWindowSize, wantPos - startCurPos);
wantSize = Math.Min(wantSize, totalPrevSize * sizeLeft - mMinWindowSize);
wantSize /= sizeLeft;
widget1.mSizePriority = wantSize;
widget2.mSizePriority = totalPrevSize - wantSize;
}
ResizeContent();
// Set to actual used value
if (resizedWidget != null)
{
if (mSplitType == .Horz)
resizedWidget.mRequestedWidth = resizedWidget.mWidth;
else
resizedWidget.mRequestedHeight = resizedWidget.mHeight;
}
}
else
{
if (FindSplitterAt(x, y) != -1)
{
if (mSplitType == SplitType.Horz)
BFApp.sApp.SetCursor(Cursor.SizeWE);
else
BFApp.sApp.SetCursor(Cursor.SizeNS);
}
else
{
BFApp.sApp.SetCursor(Cursor.Pointer);
}
}
}
public override void MouseUp(float x, float y, int32 btn)
{
base.MouseUp(x, y, btn);
mDownSplitterNum = -1;
MouseMove(x, y);
}
public override void MouseLeave()
{
base.MouseLeave();
if (!mMouseDown)
BFApp.sApp.SetCursor(Cursor.Pointer);
}
public virtual void DrawDraggingDock(Graphics g)
{
}
public override void DrawAll(Graphics g)
{
base.DrawAll(g);
if (mDraggingDock != null)
DrawDraggingDock(g);
}
public override void Update()
{
base.Update();
Simplify();
}
}
}

View file

@ -0,0 +1,185 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using Beefy.events;
namespace Beefy.widgets
{
public interface IDragInterface
{
void DragStart();
void DragEnd();
void MouseDrag(float x, float y, float dX, float dY);
}
public class DragHelper
{
public float mMouseX;
public float mMouseY;
public float mMouseDownX;
public float mMouseDownY;
public float mTriggerDist = 2.0f;
public int32 mMinDownTicks = 0;
public bool mAllowDrag = true;
public bool mIsDragging = false;
public MouseFlag mMouseFlags;
IDragInterface mDragInterface;
public Widget mWidget;
public bool mAborted;
public bool mPreparingForWidgetMove;
int32 mDownUpdateCnt;
public WidgetWindow mTrackingWindow;
//MouseEventHandler mMouseDownHandler;
public float RelX
{
get
{
return mMouseX - mMouseDownX;
}
}
public float RelY
{
get
{
return mMouseY - mMouseDownY;
}
}
public this(Widget widget, IDragInterface dragInterface)
{
mWidget = widget;
mDragInterface = dragInterface;
mWidget.mOnMouseDown.Add(new => HandleMouseDown);
mWidget.mOnMouseUp.Add(new => HandleMouseUp);
mWidget.mOnMouseMove.Add(new => HandleMouseMove);
}
public ~this()
{
if (mIsDragging)
CancelDrag();
//Debug.Assert(!mIsDragging);
mWidget.mOnMouseDown.Remove(scope => HandleMouseDown, true);
mWidget.mOnMouseUp.Remove(scope => HandleMouseUp, true);
mWidget.mOnMouseMove.Remove(scope => HandleMouseMove, true);
}
void HandleMouseDown(MouseEvent e)
{
MouseDown(e.mX, e.mY, e.mBtn, e.mBtnCount);
}
void HandleMouseUp(MouseEvent e)
{
MouseUp(e.mX, e.mY, e.mBtn);
}
void HandleMouseMove(MouseEvent e)
{
MouseMove(e.mX, e.mY);
}
public void HandleRemoved(Widget widget, Widget prevParent, WidgetWindow prevWindow)
{
CancelDrag();
}
public void HandleKeydown(KeyDownEvent theEvent)
{
if (theEvent.mKeyCode == KeyCode.Escape)
{
mAborted = true;
CancelDrag();
}
}
public void MouseDown(float x, float y, int32 btn, int32 btnCount)
{
mDownUpdateCnt = mWidget.mUpdateCnt;
mMouseFlags |= (MouseFlag)(1 << btn);
mMouseDownX = x;
mMouseDownY = y;
}
void SetHooks(bool doSet)
{
//Debug.WriteLine("SetHooks {0} {1}", this, doSet);
if (doSet)
{
mTrackingWindow = mWidget.mWidgetWindow;
mTrackingWindow.mOnWindowKeyDown.Add(new => HandleKeydown);
mWidget.mOnRemovedFromParent.Add(new => HandleRemoved);
}
else
{
mTrackingWindow.mOnWindowKeyDown.Remove(scope => HandleKeydown, true);
mWidget.mOnRemovedFromParent.Remove(scope => HandleRemoved, true);
}
}
public void SetPreparingForWidgetMove(bool prepare)
{
SetHooks(!prepare);
}
public void CancelDrag()
{
if (!mIsDragging)
return;
SetHooks(false);
mMouseFlags = default;
mIsDragging = false;
mDragInterface.DragEnd();
}
public void MouseUp(float x, float y, int32 btn)
{
mMouseFlags &= (MouseFlag)(~(1 << btn));
if (((mMouseFlags & MouseFlag.Left) == 0) && (mIsDragging))
{
CancelDrag();
}
}
public void Update()
{
int32 ticksDown = mWidget.mUpdateCnt - mDownUpdateCnt;
if (((mMouseFlags & MouseFlag.Left) != 0) && (ticksDown >= mMinDownTicks))
{
if ((!mIsDragging) && (mAllowDrag))
{
if ((Math.Abs(mMouseX - mMouseDownX) >= mTriggerDist) || (Math.Abs(mMouseY - mMouseDownY) >= mTriggerDist))
{
SetHooks(true);
mPreparingForWidgetMove = false;
mAborted = false;
mIsDragging = true;
mDragInterface.DragStart();
}
}
if (mIsDragging)
{
mIsDragging = true;
mDragInterface.MouseDrag(mMouseX, mMouseY, mMouseX - mMouseDownX, mMouseY - mMouseDownY);
}
}
}
public void MouseMove(float x, float y)
{
mMouseX = x;
mMouseY = y;
Update();
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Beefy.widgets
{
public delegate void MenuItemSelectedHandler(IMenu menu);
public delegate void MenuItemUpdateHandler(IMenu menu);
public interface IMenu
{
void SetDisabled(bool enable);
}
public interface IMenuContainer
{
}
}

View file

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Text;
using Beefy.gfx;
namespace Beefy.widgets
{
public class ImageWidget : Widget
{
public IDrawable mImage;
public IDrawable mOverImage;
public IDrawable mDownImage;
public override void Draw(Graphics g)
{
if ((mMouseOver && mMouseDown) && (mDownImage != null))
g.Draw(mDownImage);
if ((mMouseOver) && (mOverImage != null))
g.Draw(mOverImage);
else
g.Draw(mImage);
}
}
}

View file

@ -0,0 +1,246 @@
using System;
using System.Collections.Generic;
using System.Text;
using Beefy.events;
using Beefy.geom;
using System.Diagnostics;
namespace Beefy.widgets
{
public delegate void InfiniteScrollEventHandler(double scrollDelta);
public abstract class InfiniteScrollbar : Widget, IDragInterface
{
/*public class Thumb : Widget, IDragInterface
{
public InfiniteScrollbar mScrollbar;
public DragHelper mDraggableHelper ~ delete _;
public this()
{
mDraggableHelper = new DragHelper(this, this);
}
public void DragStart()
{
}
public void DragEnd()
{
mScrollbar.ScrollSetLevel(0.0);
}
public void MouseDrag(float x, float y, float dX, float dY)
{
float parentX;
float parentY;
SelfToParentTranslate(x - mDraggableHelper.mMouseDownX, y - mDraggableHelper.mMouseDownY, out parentX, out parentY);
mScrollbar.ScrollSetLevel(mScrollbar.GetAccelFracAt(parentX, parentY));
}
}*/
public class Arrow : Widget
{
public InfiniteScrollbar mScrollbar;
public int32 mDownTick;
public bool mBack;
public override void MouseDown(float x, float y, int32 btn, int32 btnCount)
{
base.MouseDown(x, y, btn, btnCount);
DoScroll();
mDownTick = 0;
}
protected virtual void DoScroll()
{
mScrollbar.FixedScroll(mBack ? -1 : 1);
}
public override void Update()
{
base.Update();
if ((mMouseDown) && (mMouseOver))
{
mDownTick++;
if (mDownTick > 20)
DoScroll();
}
}
}
// configuration
public double mFixedScrollAmt; // base amount to scroll for fixed scroll events e.g arrow clicks
public double mScrollDeltaLevelAmount; // amount to change level during update while mouse is held down
//public double mScrollMaxAccel; // absolute value maximum of acceleration (transient accel will range from [-this,this])
public double mScrollMaxVelocity; // absolute value maximum of velocity; effective limit will be adjusted based on canonical acceleration
public double mScrollThumbFrac; // scaled canonical acceleration, [-1,1]
//public double mScrollAccel;
public double mScrollVelocity;
//public Thumb mThumb;
public Arrow mStartArrow;
public Arrow mEndArrow;
public float mBaseSize;
public float mDualBarSizeOffset;
public int32 mDownTick;
public float mLastMouseX;
public float mLastMouseY;
public Event<InfiniteScrollEventHandler> mOnInfiniteScrollEvent ~ _.Dispose();
public DragHelper mDraggableHelper ~ delete _;
public this()
{
mDraggableHelper = new DragHelper(this, this);
}
public void HandleScroll(double scrollDelta)
{
if (mOnInfiniteScrollEvent.HasListeners)
mOnInfiniteScrollEvent(scrollDelta);
}
public void DragStart()
{
let thumbPos = GetThumbPos();
if (thumbPos.Contains(mDraggableHelper.mMouseDownX, mDraggableHelper.mMouseDownY))
{
// Is dragging thumb now
}
else
{
mDraggableHelper.CancelDrag();
}
}
public void DragEnd()
{
ScrollSetLevel(0.0);
}
public void MouseDrag(float x, float y, float dX, float dY)
{
//float parentX;
//float parentY;
//SelfToParentTranslate(x - mDraggableHelper.mMouseDownX, y - mDraggableHelper.mMouseDownY, out parentX, out parentY);
//ScrollSetLevel(GetAccelFracAt(parentX, parentY));
float accelFrac = GetAccelFracAt(mDraggableHelper.RelX, mDraggableHelper.RelY);
ScrollSetLevel(accelFrac);
}
public virtual void FixedScroll(int32 delta)
{
HandleScroll(delta * mFixedScrollAmt);
}
public virtual void Init()
{
// test config values
mFixedScrollAmt = 1;
mScrollDeltaLevelAmount = 1.0;
//mScrollMaxAccel = 1000;
mScrollMaxVelocity = 250;
}
public virtual void ResizeContent()
{
}
public void UpdateScrollPosition()
{
if (Math.Abs(mScrollVelocity) > 0.0001)
HandleScroll(mScrollVelocity * 0.01);
}
public virtual void ScrollSetLevel(double scrollLevel)
{
double setScrollLevel = scrollLevel;
setScrollLevel = Math.Min(Math.Max(-1.0, setScrollLevel), 1.0);
mScrollThumbFrac = scrollLevel;
//double vel = Math.Exp(Math.Log(mScrollMaxVelocity)*Math.Abs(mScrollThumbFrac)) - 1.0;
double vel = Math.Pow(Math.Abs(mScrollThumbFrac) * 20, 1.8);
if (mScrollThumbFrac < 0)
vel = -vel;
mScrollVelocity = vel;
ResizeContent();
}
public virtual void ScrollDeltaLevel(double scrollAmount)
{
//ScrollSetLevel(mScrollThumbFrac + scrollAmount);
HandleScroll(scrollAmount); // clicking in the bar feels better as a fixed scroll rather than changing the acceleration
}
public virtual float GetAccelFracAt(float dx, float dy)
{
return 0;
}
public virtual Rect GetThumbPos()
{
return .();
}
public override void Update()
{
base.Update();
if ((mMouseDown) && (mMouseOver))
{
mDownTick++;
if ((mDownTick > 20) && (mUpdateCnt % 5 == 0) && (!mDraggableHelper.mIsDragging))
{
let thumbPos = GetThumbPos();
if (mLastMouseY < thumbPos.Top)
ScrollDeltaLevel(-mScrollDeltaLevelAmount);
else if (mLastMouseY > thumbPos.Bottom)
ScrollDeltaLevel(mScrollDeltaLevelAmount);
}
}
UpdateScrollPosition();
}
public override void MouseMove(float x, float y)
{
base.MouseMove(x, y);
mLastMouseX = x;
mLastMouseY = y;
}
public override void MouseDown(float x, float y, int32 btn, int32 btnCount)
{
mLastMouseX = x;
mLastMouseY = y;
let thumbPos = GetThumbPos();
base.MouseDown(x, y, btn, btnCount);
if (mLastMouseY < thumbPos.Top)
ScrollDeltaLevel(-mScrollDeltaLevelAmount);
else if (mLastMouseY > thumbPos.Bottom)
ScrollDeltaLevel(mScrollDeltaLevelAmount);
mDownTick = 0;
}
public override void MouseWheel(float x, float y, int32 delta)
{
FixedScroll(-delta);
}
}
}

View file

@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Text;
using Beefy.geom;
namespace Beefy.widgets
{
public class Insets
{
public float mTop;
public float mLeft;
public float mBottom;
public float mRight;
public float Horz
{
get { return mRight + mLeft; }
}
public float Vert
{
get { return mBottom + mTop; }
}
public this(float top = 0, float left = 0, float bottom = 0, float right = 0)
{
mTop = top;
mLeft = left;
mBottom = bottom;
mRight = right;
}
public void Set(float top, float left, float bottom, float right)
{
mTop = top;
mLeft = left;
mBottom = bottom;
mRight = right;
}
public void ApplyTo(ref Rect rect)
{
rect.mX += mLeft;
rect.mY += mTop;
rect.mWidth -= mLeft + mRight;
rect.mHeight -= mTop + mBottom;
}
public void Scale(float scale)
{
Utils.RoundScale(ref mTop, scale);
Utils.RoundScale(ref mLeft, scale);
Utils.RoundScale(ref mRight, scale);
Utils.RoundScale(ref mBottom, scale);
}
}
}

View file

@ -0,0 +1,137 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
namespace Beefy.widgets
{
public enum KeyCode
{
LButton = 0x01,
RButton = 0x02,
Break = 0x03,
MButton = 0x04,
Backspace = 0x08,
Tab = 0x09,
Clear = 0x0C,
Return = 0x0D,
Shift = 0x10,
Control = 0x11,
Alt = 0x12,
Menu = 0x12,
Pause = 0x13,
Capital = 0x14,
Kana = 0x15,
Hangul = 0x15,
Junja = 0x17,
Final = 0x18,
Hanja = 0x19,
Kanji = 0x19,
Escape = 0x1B,
Convert = 0x1C,
NonConvert = 0x1D,
Accept = 0x1E,
ModeChange = 0x1F,
Space = 0x20,
PageUp = 0x21,
PageDown = 0x22,
End = 0x23,
Home = 0x24,
Left = 0x25,
Up = 0x26,
Right = 0x27,
Down = 0x28,
Select = 0x29,
Print = 0x2A,
Execute = 0x2B,
Snapshot = 0x2C,
Insert = 0x2D,
Delete = 0x2E,
Help = 0x2F,
LWin = 0x5B,
RWin = 0x5C,
Apps = 0x5D,
Numpad0 = 0x60,
Numpad1 = 0x61,
Numpad2 = 0x62,
Numpad3 = 0x63,
Numpad4 = 0x64,
Numpad5 = 0x65,
Numpad6 = 0x66,
Numpad7 = 0x67,
Numpad8 = 0x68,
Numpad9 = 0x69,
Multiply = 0x6A,
Add = 0x6B,
Separator = 0x6C,
Subtract = 0x6D,
Decimal = 0x6E,
Divide = 0x6F,
F1 = 0x70,
F2 = 0x71,
F3 = 0x72,
F4 = 0x73,
F5 = 0x74,
F6 = 0x75,
F7 = 0x76,
F8 = 0x77,
F9 = 0x78,
F10 = 0x79,
F11 = 0x7A,
F12 = 0x7B,
Numlock = 0x90,
Scroll = 0x91,
Semicolon = 0xBA,
Equals = 0xBB,
Comma = 0xBC,
Minus = 0xBD,
Period = 0xBE,
Slash = 0xBF,
Grave = 0xC0,
LBracket = 0xDB,
Backslash = 0xDC,
RBracket = 0xDD,
Apostrophe = 0xDE,
Backtick = 0xDF,
Tilde = 0xC0,
Command = 0xF0,
COUNT = 0xFF,
Media_NextTrack = 0xB0,
Media_PreviousTrack = 0xB1,
Media_PlayPause = 0xB3,
//Z = 0xB4
}
extension KeyCode
{
public static Result<KeyCode> Parse(StringView str)
{
if (str.Length == 1)
{
char8 c = str[0];
if ((c >= 'A') && (c <= 'Z'))
return (KeyCode)c;
if ((c >= '0') && (c <= '9'))
return (KeyCode)c;
if ((c >= 'a') && (c <= 'a'))
return (KeyCode)(c.ToUpper);
}
return Enum.Parse<KeyCode>(str, true);
}
public override void ToString(String strBuffer)
{
if (((this >= (KeyCode)'A') && (this <= (KeyCode)'Z')) ||
((this >= (KeyCode)'0') && (this <= (KeyCode)'9')))
{
((char8)this).ToString(strBuffer);
return;
}
base.ToString(strBuffer);
}
}
}
///

View file

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Text;
using Beefy.gfx;
namespace Beefy.widgets
{
public class LabelWidget : Widget
{
public Font mFont;
public String mLabel;
public uint32 mColor = Color.White;
public override void Draw(Graphics g)
{
base.Draw(g);
g.SetFont(mFont);
using (g.PushColor(mColor))
g.DrawString(mLabel, 0, 0);
}
}
}

View file

@ -0,0 +1,930 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using Beefy.gfx;
namespace Beefy.widgets
{
public abstract class ListViewItem : Widget
{
public ListView mListView;
public String mLabel ~ delete _;
public IDrawable mIconImage;
public int32 mColumnIdx;
public float mSelfHeight; // Designed height only for ourselves, not including children
public float mChildAreaHeight;
public float mBottomPadding;
public int32 mDepth;
public float mShowChildPct;
public uint32 mIconImageColor;
public ListViewItem mParentItem;
public List<ListViewItem> mSubItems; // de DeleteContainerAndItems!(_);
public List<ListViewItem> mChildItems ~ DeleteContainerAndItems!(_); // Tree view
//int mIdx = sIdx++;
//static int sIdx = 0;
internal this()
{
}
public ~this()
{
if (mColumnIdx == 0)
delete mSubItems;
}
virtual public StringView Label
{
get { return (mLabel != null) ? mLabel : default; }
set { String.NewOrSet!(mLabel, value);}
}
virtual public IDrawable IconImage { get { return mIconImage; } set { mIconImage = value; } }
virtual public uint32 IconImageColor { get { return mIconImageColor; } set { mIconImageColor = value; } }
protected bool mIsSelected;
protected bool mIsFocused;
virtual public bool Selected
{
get
{
return mIsSelected;
}
set
{
if (mIsSelected != value)
{
mIsSelected = value;
MarkDirty();
}
if ((!mIsSelected) && (mIsFocused))
{
mIsFocused = false;
MarkDirty();
}
}
}
virtual public bool Focused
{
get
{
return mIsFocused;
}
set
{
if (mIsFocused != value)
{
Selected = true;
mIsFocused = value;
if (mListView.mOnFocusChanged.HasListeners)
mListView.mOnFocusChanged(this);
MarkDirty();
}
}
}
virtual public bool IsParent
{
get
{
return mChildItems != null;
}
set
{
if (value)
MakeParent();
else if (IsParent)
Runtime.FatalError("Cannot undo parent status");
}
}
public virtual float LabelX { get { return 0; } }
public virtual float LabelWidth { get { return mWidth; } }
public virtual void Init(ListView listView)
{
}
protected override void RemovedFromWindow()
{
base.RemovedFromWindow();
if (mChildItems != null)
{
for (var child in mChildItems)
child.RemovedFromWindow();
}
if ((mColumnIdx == 0) && (mSubItems != null))
{
for (var subItem in mSubItems)
{
if (subItem != this)
subItem.RemovedFromWindow();
}
}
}
public void WithItems(Action<ListViewItem> func)
{
if (mChildItems != null)
{
for (ListViewItem child in mChildItems)
{
func(child);
child.WithItems(func);
}
}
}
public void WithSelectedItems(Action<ListViewItem> func, bool skipSelectedChildrenOnSelectedItems = false)
{
bool selfSelected = Selected;
if (selfSelected)
func(this);
if ((mChildItems != null) && ((!skipSelectedChildrenOnSelectedItems) || (!selfSelected)))
{
for (ListViewItem child in mChildItems)
{
child.WithSelectedItems(func, skipSelectedChildrenOnSelectedItems);
}
}
}
public ListViewItem FindFirstSelectedItem()
{
if (Selected)
return this;
if (mChildItems != null)
{
for (ListViewItem child in mChildItems)
{
ListViewItem selected = child.FindFirstSelectedItem();
if (selected != null)
return selected;
}
}
return null;
}
public ListViewItem FindLastSelectedItem(bool filterToVisible = false)
{
ListViewItem selected = null;
if (mIsSelected && (!filterToVisible || mVisible))
selected = this;
if (mChildItems != null)
{
for (ListViewItem child in mChildItems)
{
ListViewItem checkSelected = child.FindLastSelectedItem(filterToVisible);
if (checkSelected != null)
selected = checkSelected;
}
}
return selected;
}
public ListViewItem FindFocusedItem()
{
ListViewItem selectedListViewItem = null;
WithSelectedItems(scope [&] (listViewItem) =>
{
if ((listViewItem.mIsFocused) || (selectedListViewItem == null))
selectedListViewItem = listViewItem;
});
return selectedListViewItem;
}
public void SelectItemExclusively(ListViewItem item)
{
WithSelectedItems(scope (listViewItem) =>
{
if (listViewItem != item)
listViewItem.Selected = false;
});
if (item != null)
{
item.Selected = true;
item.Focused = true;
}
}
public void SelectItem(ListViewItem item, bool checkKeyStates = false)
{
if (item == null)
{
SelectItemExclusively(null);
return;
}
if (item != null)
{
if ((mListView.mAllowMultiSelect) && (checkKeyStates) && (mWidgetWindow.IsKeyDown(KeyCode.Shift)))
{
var focusedItem = FindFocusedItem();
if ((focusedItem != item) && (focusedItem != null))
{
ListViewItem spanStart = null;
ListViewItem selectEndElement = null;
ListViewItem prevItem = null;
WithItems(scope [&] (checkItem) =>
{
defer { prevItem = checkItem; }
if (selectEndElement != null)
return;
if (checkItem == focusedItem)
{
if (spanStart != null)
{
// Done. Select from end.
selectEndElement = spanStart;
return;
}
spanStart = checkItem;
return;
}
if (spanStart != null)
{
if (!checkItem.Selected)
{
if (spanStart == focusedItem)
selectEndElement = prevItem;
spanStart = null;
}
}
else if (spanStart == null)
{
if (checkItem.Selected)
spanStart = checkItem;
}
});
focusedItem.Focused = false;
bool foundOldEnd = false;
bool foundNewHead = false;
WithItems(scope [&] (checkItem) =>
{
checkItem.Selected = foundNewHead ^ foundOldEnd;
if (checkItem == item)
{
foundNewHead = true;
checkItem.Selected = true;
}
if (checkItem == selectEndElement)
{
foundOldEnd = true;
checkItem.Selected = true;
}
});
item.Focused = true;
return;
}
}
if ((mListView.mAllowMultiSelect) && (checkKeyStates) && (mWidgetWindow.IsKeyDown(KeyCode.Control)))
{
if (!item.Focused)
item.Selected = !item.Selected;
}
else
{
SelectItemExclusively(item);
if (item.Selected)
item.Focused = true;
}
}
}
public virtual void Clear()
{
if (mChildItems == null)
return;
for (var childItem in mChildItems)
{
childItem.DoRemove(false);
delete childItem;
}
mChildItems.Clear();
}
void DoRemove(bool removeFromList)
{
if ((mSubItems != null) && (mColumnIdx == 0))
{
for (int32 i = 1; i < mSubItems.Count; i++)
{
var subItem = mSubItems[i];
subItem.Remove();
delete subItem;
}
mSubItems.Clear();
}
if (mColumnIdx == 0)
{
if (removeFromList)
{
mListView.mListSizeDirty = true;
mParentItem.mChildItems.Remove(this);
}
}
else
{
base.RemoveSelf();
}
mParentItem = null;
RemovedFromWindow();
}
public virtual void Remove()
{
DoRemove(true);
}
public int GetSubItemCount()
{
if (mSubItems == null)
return 0;
return mSubItems.Count;
}
public void InitSubItem(int columnIdx, ListViewItem subItem)
{
Debug.Assert(columnIdx > 0);
if (mSubItems == null)
{
mSubItems = new List<ListViewItem>();
mSubItems.Add(this); // Add ourselves to column zero
}
while (columnIdx >= mSubItems.Count)
mSubItems.Add(null);
subItem.mDepth = mDepth;
subItem.mListView = mListView;
subItem.mSubItems = mSubItems; // Share with column zero
subItem.mColumnIdx = (.)columnIdx;
subItem.mParentItem = mParentItem;
mSubItems[columnIdx] = subItem;
/*mParentItem.*/AddWidget(subItem);
mListView.mListSizeDirty = true;
}
public ListViewItem CreateSubItem(int columnIdx)
{
ListViewItem subItem = mListView.CreateListViewItem_Internal();
InitSubItem(columnIdx, subItem);
return subItem;
}
public ListViewItem GetMainItem()
{
if (mColumnIdx == 0)
return this;
return mSubItems[0];
}
public ListViewItem GetSubItem(int columnIdx)
{
if (mColumnIdx == columnIdx)
return this;
return mSubItems[columnIdx];
}
public virtual void MakeParent()
{
if (mChildItems == null)
mChildItems = new List<ListViewItem>();
}
public virtual void TryUnmakeParent()
{
if ((mChildItems != null) && (mChildItems.Count == 0))
{
DeleteAndNullify!(mChildItems);
}
}
public virtual bool Open(bool open, bool immediate = false)
{
return false;
}
public override void InitChildren()
{
base.InitChildren();
if (mChildItems != null)
{
for (var item in mChildItems)
{
item.mWidgetWindow = mWidgetWindow;
item.InitChildren();
}
}
}
public virtual ListViewItem CreateChildItemAtIndex(int idx)
{
MakeParent();
ListViewItem child = mListView.CreateListViewItem_Internal();
child.mListView = mListView;
child.mParentItem = this;
child.mDepth = mDepth + 1;
child.mVisible = mShowChildPct > 0.0f;
mChildItems.Insert(idx, child);
AddWidgetUntracked(child);
mListView.mListSizeDirty = true;
return child;
}
public virtual ListViewItem CreateChildItem()
{
return CreateChildItemAtIndex((mChildItems != null) ? mChildItems.Count : 0);
}
public int32 GetIndexOfChild(ListViewItem item)
{
return mChildItems.IndexOf(item);
}
public virtual void RemoveChildItemAt(int idx, bool deleteItem = true)
{
var item = mChildItems[idx];
mChildItems.RemoveAt(idx);
RemoveWidget(item);
item.mParentItem = null;
item.mListView = null;
mListView.mListSizeDirty = true;
if (deleteItem)
delete item;
}
public virtual void RemoveChildItem(ListViewItem item, bool deleteItem = true)
{
int32 idx = mChildItems.IndexOf(item);
RemoveChildItemAt(idx, deleteItem);
}
protected void SetDepth(int32 depth)
{
if (mDepth == depth)
return;
mDepth = depth;
if (mSubItems != null)
for (ListViewItem anItem in mSubItems)
anItem.SetDepth(depth);
if (mChildItems != null)
for (ListViewItem anItem in mChildItems)
anItem.SetDepth(depth + 1);
}
public virtual void AddChildAtIndex(int index, ListViewItem item)
{
mChildItems.Insert(index, item);
AddWidgetUntracked(item);
item.mParentItem = this;
item.mListView = mListView;
item.SetDepth(mDepth + 1);
mListView.mListSizeDirty = true;
}
public virtual void InsertChild(ListViewItem item, ListViewItem insertBefore)
{
int pos = (insertBefore != null) ? mChildItems.IndexOf(insertBefore) : mChildItems.Count;
AddChildAtIndex(pos, item);
}
public virtual void AddChild(ListViewItem item, ListViewItem addAfter = null)
{
int pos = (addAfter != null) ? (mChildItems.IndexOf(addAfter) + 1) : mChildItems.Count;
AddChildAtIndex(pos, item);
}
public int GetChildCount()
{
if (mChildItems == null)
return 0;
return mChildItems.Count;
}
public ListViewItem GetChildAtIndex(int idx)
{
return mChildItems[idx];
}
public virtual float CalculatedDesiredHeight()
{
mChildAreaHeight = 0;
if (mChildItems != null)
{
for (ListViewItem listViewItem in mChildItems)
mChildAreaHeight += listViewItem.CalculatedDesiredHeight();
}
mChildAreaHeight *= mShowChildPct;
return mChildAreaHeight + mSelfHeight + mBottomPadding;
}
public virtual float ResizeComponents(float xOffset)
{
float curY = 0;
if ((mSubItems != null) && (mColumnIdx == 0))
{
float curX = -mX - xOffset;
int columnCount = Math.Min(mSubItems.Count, mListView.mColumns.Count);
for (int32 aColumnIdx = 0; aColumnIdx < columnCount; aColumnIdx++)
{
ListViewColumn aColumn = mListView.mColumns[aColumnIdx];
if (aColumnIdx > 0)
{
ListViewItem subItem = mSubItems[aColumnIdx];
if (subItem != null)
{
subItem.mVisible = mVisible;
if (aColumnIdx == columnCount - 1)
subItem.Resize(curX, /*mY*/0, Math.Max(aColumn.mWidth, mWidth - curX), mHeight);
else
{
float aWidth = aColumn.mWidth;
subItem.Resize(curX, /*mY*/0, aWidth, mHeight);
}
if (aColumnIdx > 0)
subItem.ResizeComponents(xOffset);
}
}
curX += aColumn.mWidth;
}
}
curY += mSelfHeight;
if (mChildItems != null)
{
for (ListViewItem child in mChildItems)
{
child.mVisible = (mShowChildPct > 0.0f);
child.Resize(child.mX, curY, mWidth - child.mX, child.mSelfHeight);
float resizeXOfs = xOffset;
if (mParentItem != null)
resizeXOfs += mX;
float childSize = (int)child.ResizeComponents(resizeXOfs);
curY += childSize;
}
}
mChildAreaHeight = CalculatedDesiredHeight() - mSelfHeight - mBottomPadding;
return mChildAreaHeight + mSelfHeight + mBottomPadding;
}
public override void Resize(float x, float y, float width, float height)
{
base.Resize(x, y, width, height);
}
public override void MouseClicked(float x, float y, int32 btn)
{
base.MouseClicked(x, y, btn);
if (mParentItem != null) // Don't notify for root
mListView.mOnItemMouseClicked(this, x, y, btn);
}
public override void MouseDown(float x, float y, int32 btn, int32 btnCount)
{
base.MouseDown(x, y, btn, btnCount);
if (mParentItem != null) // Don't notify for root
mListView.mOnItemMouseDown(this, x, y, btn, btnCount);
}
}
public class ListViewColumn
{
public String mLabel ~ delete _;
public float mWidth;
public float mMinWidth;
public float mMaxWidth;
public String Label
{
get
{
return mLabel;
}
set
{
String.NewOrSet!(mLabel, value);
}
}
}
public class ListViewDragData
{
List<ListViewItem> mDragItems;
}
public abstract class ListView : ScrollableWidget
{
public List<ListViewColumn> mColumns = new List<ListViewColumn>() ~ DeleteContainerAndItems!(_);
protected ListViewItem mRoot;
public bool mListSizeDirty;
public float mHeaderHeight;
public Event<Action<ListViewItem>> mOnFocusChanged ~ _.Dispose();
public float mBottomInset = 8;
public bool mAllowMultiSelect = true;
public Event<delegate void(ListViewItem item, float x, float y, int32 btnNum, int32 btnCount)> mOnItemMouseDown ~ _.Dispose();
public Event<delegate void(ListViewItem item, float x, float y, int32 btnNum)> mOnItemMouseClicked ~ _.Dispose();
public this()
{
mRoot = CreateListViewItem();
mRoot.mSelfHeight = 0;
mRoot.mListView = this;
mListSizeDirty = true;
mScrollContent = mRoot;
mScrollContentContainer.AddWidget(mRoot);
}
public virtual ListViewColumn AddColumn(float width, String label)
{
ListViewColumn aColumn = new ListViewColumn();
aColumn.mWidth = width;
aColumn.Label = label;
aColumn.mMinWidth = 64;
mColumns.Add(aColumn);
return aColumn;
}
protected virtual ListViewItem CreateListViewItem()
{
return null;
}
internal virtual ListViewItem CreateListViewItem_Internal()
{
var listViewItem = CreateListViewItem();
listViewItem.Init(this);
return listViewItem;
}
public virtual ListViewItem GetRoot()
{
return mRoot;
}
protected virtual void ColumnResized(ListViewColumn column)
{
mListSizeDirty = true;
}
public override void Update()
{
base.Update();
}
public override void UpdateAll()
{
base.UpdateAll();
UpdateListSize();
}
public virtual float GetListWidth()
{
float columnWidths = 0;
for (ListViewColumn aColumn in mColumns)
columnWidths += aColumn.mWidth;
return columnWidths;
}
public void UpdateListSize()
{
// Do this in UpdateAll to give children a change to resize items
if (mListSizeDirty)
{
float listWidth = GetListWidth();
mRoot.mWidth = Math.Max(listWidth, mScrollContentContainer.mWidth);
mRoot.mHeight = mRoot.CalculatedDesiredHeight() + mBottomInset;
mRoot.ResizeComponents(0);
UpdateScrollbarData();
mListSizeDirty = false;
}
}
public override void Resize(float x, float y, float width, float height)
{
if ((x == mX) && (y == mY) && (width == mWidth) && (height == mHeight))
return;
base.Resize(x, y, width, height);
mListSizeDirty = true;
}
public void Resized()
{
mListSizeDirty = true;
}
public void EnsureItemVisible(ListViewItem item, bool centerView)
{
if (mVertScrollbar == null)
return;
if (mListSizeDirty)
UpdateListSize();
if (mScrollContentContainer.mHeight <= 0)
return;
float aX;
float aY;
item.SelfToOtherTranslate(mScrollContent, 0, 0, out aX, out aY);
float topInsets = 0;
float lineHeight = item.mSelfHeight;
if (aY < mVertPos.mDest + topInsets)
{
float scrollPos = aY - topInsets;
if (centerView)
{
scrollPos -= mScrollContentContainer.mHeight * 0.50f;
scrollPos = (float)Math.Round(scrollPos / lineHeight) * lineHeight;
}
VertScrollTo(scrollPos);
}
else if (aY + lineHeight + mBottomInset >= mVertPos.mDest + mScrollContentContainer.mHeight)
{
float scrollPos = aY + lineHeight + mBottomInset - mScrollContentContainer.mHeight;
if (centerView)
{
// Show slightly more content on bottom
scrollPos += mScrollContentContainer.mHeight * 0.50f;
scrollPos = (float)Math.Round(scrollPos / lineHeight) * lineHeight;
}
VertScrollTo(scrollPos);
}
}
ListViewItem FindClosestItemAtYPosition(ListViewItem parentItem, float y, bool addHeight)
{
if (parentItem.mChildItems != null)
{
for (var item in parentItem.mChildItems)
{
var closestItem = FindClosestItemAtYPosition(item, y + item.mY, addHeight);
if (closestItem != null)
return closestItem;
}
}
if ((y >= 0) && (y < parentItem.mSelfHeight))
return parentItem;
return null;
}
public override void KeyDown(KeyCode keyCode, bool isRepeat)
{
base.KeyDown(keyCode, isRepeat);
switch (keyCode)
{
case (KeyCode)'A':
if ((mAllowMultiSelect) && (mWidgetWindow.GetKeyFlags() == KeyFlags.Ctrl))
{
mRoot.WithItems(scope (listViewItem) =>
{
listViewItem.Selected = true;
});
}
break;
default:
}
bool isDoingSpanSelection = mAllowMultiSelect && mWidgetWindow.IsKeyDown(KeyCode.Shift);
//ListViewItem firstSelectedItem = isDoingSpanSelection ? mRoot.FindFirstSelectedItem() : mRoot.FindFocusedItem();
ListViewItem firstSelectedItem = mRoot.FindFocusedItem();
if (firstSelectedItem != null)
{
ListViewItem selectedItem = firstSelectedItem;
bool triedMove = false;
ListViewItem newSelection = null;
switch (keyCode)
{
case KeyCode.Home:
newSelection = GetRoot().mChildItems[0];
case KeyCode.End:
newSelection = GetRoot();
while (newSelection.mChildAreaHeight > 0)
{
newSelection = GetRoot().mChildItems[GetRoot().mChildItems.Count - 1];
}
case KeyCode.PageUp:
int32 numIterations = (int32)(mScrollContentContainer.mHeight / selectedItem.mSelfHeight);
for (int32 i = 0; i < numIterations; i++)
KeyDown(KeyCode.Up, false);
case KeyCode.PageDown:
int32 numIterations = (int32)(mScrollContentContainer.mHeight / selectedItem.mSelfHeight);
for (int32 i = 0; i < numIterations; i++)
KeyDown(KeyCode.Down, false);
case KeyCode.Up:
int32 idx = selectedItem.mParentItem.mChildItems.IndexOf(selectedItem);
if (idx > 0)
{
newSelection = selectedItem.mParentItem.mChildItems[idx - 1];
while (newSelection.mChildAreaHeight > 0)
newSelection = newSelection.mChildItems[newSelection.mChildItems.Count - 1];
}
else if (selectedItem.mParentItem != mRoot)
{
newSelection = selectedItem.mParentItem;
}
triedMove = true;
case KeyCode.Down:
if ((selectedItem.mChildItems != null) && (selectedItem.mChildItems.Count > 0) && (selectedItem.mChildAreaHeight > 0))
newSelection = selectedItem.mChildItems[0];
else
{
while (selectedItem != mRoot)
{
var childItems = selectedItem.mParentItem.mChildItems;
int32 idx = childItems.IndexOf(selectedItem);
if (idx < childItems.Count - 1)
{
newSelection = childItems[idx + 1];
break;
}
selectedItem = selectedItem.mParentItem;
}
}
triedMove = true;
case KeyCode.Left:
if (!selectedItem.Open(false))
{
if (selectedItem.mParentItem != GetRoot())
newSelection = selectedItem.mParentItem;
}
case KeyCode.Right:
if (!selectedItem.Open(true))
{
if ((selectedItem.mChildItems != null) && (selectedItem.mChildItems.Count > 0))
newSelection = selectedItem.mChildItems[0];
}
case KeyCode.Space:
if (!selectedItem.Open(false))
selectedItem.Open(true);
case KeyCode.Return:
var mouseEvent = scope Beefy.events.MouseEvent();
mouseEvent.mSender = selectedItem;
mouseEvent.mBtnCount = 2;
selectedItem.mOnMouseDown(mouseEvent);
default:
}
if (newSelection != null)
{
mRoot.SelectItem(newSelection, isDoingSpanSelection);
EnsureItemVisible(newSelection, false);
}
else if ((triedMove) && (!isDoingSpanSelection) && (firstSelectedItem != null))
mRoot.SelectItemExclusively(firstSelectedItem);
}
}
}
}

View file

@ -0,0 +1,576 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using Beefy.theme;
using Beefy.events;
using Beefy.sys;
using Beefy.gfx;
using Beefy.geom;
namespace Beefy.widgets
{
public class MenuItemWidget : Widget
{
public Menu mMenuItem;
public MenuWidget mMenuWidget;
public int32 mIndex;
public bool mIsSelected;
public MenuWidget mSubMenu;
public this(Menu menuItem)
{
mMenuItem = menuItem;
}
public override void MouseEnter()
{
base.MouseEnter();
if ((!mWidgetWindow.mIsMouseMoving) || (mUpdateCnt == 0))
{
// We can get a MouseEnter from the widget moving to under the mouse, but we
// only want to respond to the mouse actually moving
return;
}
if (mMenuItem.mLabel != null)
{
mMenuWidget.SetSelection(mIndex);
if (!mWidgetWindow.mHasFocus)
{
var parentMenuItemWidget = mMenuWidget.mParentMenuItemWidget;
while (parentMenuItemWidget != null)
{
parentMenuItemWidget.mMenuWidget.mOpeningSubMenu = true;
parentMenuItemWidget = parentMenuItemWidget.mMenuWidget.mParentMenuItemWidget;
}
mWidgetWindow.SetForeground();
parentMenuItemWidget = mMenuWidget.mParentMenuItemWidget;
while (parentMenuItemWidget != null)
{
parentMenuItemWidget.mMenuWidget.mOpeningSubMenu = false;
parentMenuItemWidget = parentMenuItemWidget.mMenuWidget.mParentMenuItemWidget;
}
}
}
}
public override void MouseLeave()
{
base.MouseLeave();
if ((mWidgetWindow == null) || (!mWidgetWindow.mIsMouseMoving))
return;
if (mSubMenu == null)
mMenuWidget.SetSelection(-1);
}
public override void MouseUp(float x, float y, int32 btn)
{
base.MouseUp(x, y, btn);
//if ((mIsOver) && (mMouseClickHandler != null))
//mMouseClickHandler(this);
if (!mMouseOver)
mMenuWidget.Close();
}
public override void MouseDown(float x, float y, int32 btn, int32 btnCount)
{
base.MouseDown(x, y, btn, btnCount);
ReleaseMouseCapture();
}
public virtual void Submit()
{
}
}
public class MenuContainer : ScrollableWidget
{
public bool mReverse;
}
public class MenuWidget : Widget
{
public MenuItemWidget mParentMenuItemWidget;
public Menu mMenu;
//public BFWindowBase.Flags mWindowFlags = BFWindowBase.Flags.ClientSized | BFWindowBase.Flags.NoActivate | BFWindowBase.Flags.NoMouseActivate;
public BFWindowBase.Flags mWindowFlags = .ClientSized | .FakeFocus;
public int32 mSelectIdx = -1;
public Widget mRelativeWidget;
public List<MenuItemWidget> mItemWidgets = new List<MenuItemWidget>() ~ delete _;
public Menu mItemSelected;
public bool mOpeningSubMenu;
public bool mReplacing;
public bool mReverse;
public float mMinContainerWidth = 0;
public float mMaxContainerWidth = Int32.MaxValue;
public Insets mPopupInsets = new Insets() ~ delete _;
public bool mHasClosed;
public Event<Action<int>> mOnSelectionChanged ~ _.Dispose();
public bool mWasInitialized;
public this(Menu menu)
{
mMenu = menu;
}
public ~this()
{
// Only delete when we're the top-level menu
if (mMenu.mParent == null)
{
delete mMenu;
}
if ((!mHasClosed) && (mWasInitialized))
Close();
//Debug only
/*for (var call in WidgetWindow.sWindowMovedHandler.GetInvocationList())
{
if ((int)call.GetTarget() == (int)(void*)this)
{
Runtime.FatalError("MenuWidget not removed from sWindowMovedHandler!");
}
}*/
}
public virtual MenuContainer CreateMenuContainer()
{
return new MenuContainer();
}
public virtual void ResizeComponents()
{
}
public virtual void CalcSize()
{
}
public virtual MenuItemWidget CreateMenuItemWidget(Menu menuItem)
{
return new MenuItemWidget(menuItem);
}
public virtual void EnsureItemVisible(int itemIdx)
{
}
public virtual void SetSelection(int selIdx)
{
MarkDirty();
EnsureItemVisible(selIdx);
mSelectIdx = (int32)selIdx;
if ((mParentMenuItemWidget != null) && (selIdx != -1))
mParentMenuItemWidget.mMenuWidget.SetSelection(mParentMenuItemWidget.mIndex);
mOnSelectionChanged(selIdx);
}
public override void Resize(float x, float y, float width, float height)
{
base.Resize(x, y, width, height);
}
public virtual void CalcContainerSize(MenuContainer menuContainer, bool allowScrollable, ref float screenX, ref float screenY, out float width, out float height)
{
Rect menuRect = menuContainer.CalcRectFromContent();
width = Math.Min(mMaxContainerWidth, Math.Max(mMinContainerWidth, menuRect.mWidth));
height = menuRect.mHeight;
int workspaceX;
int workspaceY;
int workspaceWidth;
int workspaceHeight;
BFApp.sApp.GetWorkspaceRect(out workspaceX, out workspaceY, out workspaceWidth, out workspaceHeight);
float maxY = workspaceY + workspaceHeight;
if ((!allowScrollable) && (screenX + width > workspaceWidth))
screenX = screenX - width + mPopupInsets.mRight;
else
screenX -= mPopupInsets.mLeft;
if (mReverse)
{
maxY = screenY;
screenY = Math.Max(workspaceY, screenY - height);
}
if ((!allowScrollable) && (screenY + height > maxY))
screenY = screenY - height + mPopupInsets.mBottom;
else
{
screenY -= mPopupInsets.mTop;
if (screenY + height > maxY)
{
menuContainer.InitScrollbars(false, true);
height = maxY - screenY;
}
else
menuContainer.InitScrollbars(false, false);
menuRect = menuContainer.CalcRectFromContent();
width = Math.Min(mMaxContainerWidth, Math.Max(mMinContainerWidth, menuRect.mWidth));
}
}
public virtual float GetReverseAdjust()
{
return 10;
}
public virtual void Init(Widget relativeWidget, float x, float y, bool allowScrollable = false, WidgetWindow widgetWindow = null)
{
mWasInitialized = true;
float curY = y;
WidgetWindow curWidgetWindow = widgetWindow;
bool reverse = false;
bool allowReverse = true;
if ((allowReverse) && (relativeWidget != null))
{
if (mHeight == 0)
CalcSize();
float minHeight;
if (allowScrollable)
minHeight = Math.Min(mHeight, 200) + 8;
else
minHeight = mHeight;
float screenX;
float screenY;
relativeWidget.SelfToRootTranslate(0, curY, out screenX, out screenY);
screenY += relativeWidget.mWidgetWindow.mClientY;
int wsX;
int wsY;
int wsWidth;
int wsHeight;
BFApp.sApp.GetWorkspaceRect(out wsX, out wsY, out wsWidth, out wsHeight);
float spaceLeft = (wsY + wsHeight) - (screenY);
if (spaceLeft < minHeight)
{
curY += GetReverseAdjust();
reverse = true;
}
}
mReverse = reverse;
int32 idx = 0;
for (Menu item in mMenu.mItems)
{
MenuItemWidget itemWidget = CreateMenuItemWidget(item);
itemWidget.mIndex = idx;
itemWidget.mMenuWidget = this;
mItemWidgets.Add(itemWidget);
AddWidget(itemWidget);
++idx;
}
float screenX = x;
float screenY = curY;
CalcSize();
if (relativeWidget != null)
relativeWidget.SelfToRootTranslate(x, curY, out screenX, out screenY);
screenX += relativeWidget.mWidgetWindow.mClientX;
screenY += relativeWidget.mWidgetWindow.mClientY;
MenuContainer menuContainer;
if (curWidgetWindow == null)
{
menuContainer = CreateMenuContainer();
menuContainer.mReverse = reverse;
menuContainer.mScrollContent = this;
menuContainer.mScrollContentContainer.AddWidget(this);
float screenWidth;
float screenHeight;
CalcContainerSize(menuContainer, allowScrollable, ref screenX, ref screenY, out screenWidth, out screenHeight);
WidgetWindow parentWindow = (relativeWidget != null) ? relativeWidget.mWidgetWindow : null;
curWidgetWindow = new WidgetWindow(parentWindow,
"Popup",
(int32)(screenX), (int32)(screenY),
(int32)screenWidth, (int32)screenHeight,
mWindowFlags,
menuContainer);
if (parentWindow != null)
{
parentWindow.TransferMouse(curWidgetWindow);
}
//Resize(0, 0, mWidth, mHeight);
menuContainer.UpdateScrollbars();
mRelativeWidget = relativeWidget;
}
else
{
menuContainer = (MenuContainer)curWidgetWindow.mRootWidget;
menuContainer.mReverse = reverse;
var oldMenuWidget = (MenuWidget)menuContainer.mScrollContent;
oldMenuWidget.mReplacing = true;
oldMenuWidget.RemoveSelf();
oldMenuWidget.Close();
delete oldMenuWidget;
menuContainer.mScrollContent = this;
menuContainer.AddWidget(this);
float screenWidth;
float screenHeight;
CalcContainerSize(menuContainer, allowScrollable, ref screenX, ref screenY, out screenWidth, out screenHeight);
curWidgetWindow.Resize((int32)(screenX), (int32)(screenY),
(int32)screenWidth, (int32)screenHeight);
}
menuContainer.mScrollContent.mWidth = menuContainer.mScrollContentContainer.mWidth;
ResizeComponents();
WidgetWindow.sOnKeyDown.Add(new => HandleKeyDown);
WidgetWindow.sOnWindowLostFocus.Add(new => WindowLostFocus);
WidgetWindow.sOnMouseDown.Add(new => HandleMouseDown);
WidgetWindow.sOnMouseWheel.Add(new => HandleMouseWheel);
WidgetWindow.sOnWindowMoved.Add(new => HandleWindowMoved);
WidgetWindow.sOnMenuItemSelected.Add(new => HandleSysMenuItemSelected);
if (mRelativeWidget != null)
{
mRelativeWidget.mOnRemovedFromParent.Add(new => HandleRemovedFromManager);
mRelativeWidget.mOnMouseUp.Add(new => HandleMouseUp);
}
curWidgetWindow.SetFocus(this);
}
public void SubmitSelection()
{
if (mSelectIdx == -1)
return;
//mItemWidgets[mSelectIdx].MouseClicked(0, 0, 0);
mItemWidgets[mSelectIdx].Submit();
}
void HandleRemovedFromManager(Widget widget, Widget prevParent, WidgetWindow widgetWindow)
{
Close();
}
void HandleMouseUp(MouseEvent theEvent)
{
if ((mSelectIdx != -1) && (theEvent.mBtn == 0))
{
//SubmitSelection();
}
}
void HandleMouseWheel(MouseEvent theEvent)
{
HandleMouseDown(theEvent);
}
void HandleMouseDown(MouseEvent theEvent)
{
WidgetWindow widgetWindow = (WidgetWindow)theEvent.mSender;
if (!(widgetWindow.mRootWidget is MenuContainer))
Close();
}
void HandleSysMenuItemSelected(IMenu sysMenu)
{
Close();
}
bool IsMenuWindow(BFWindow window)
{
var newWidgetWindow = window as WidgetWindow;
if (newWidgetWindow != null)
{
if (newWidgetWindow.mRootWidget is MenuContainer)
return true;
}
return false;
}
void WindowLostFocus(BFWindow window, BFWindow newFocus)
{
if (mOpeningSubMenu)
return;
if (IsMenuWindow(newFocus))
return;
if ((mWidgetWindow != null) && (!mWidgetWindow.mHasFocus))
Close();
}
void HandleKeyDown(KeyDownEvent evt)
{
if (evt.mKeyCode == KeyCode.Escape)
{
evt.mHandled = true;
Close();
}
}
void HandleWindowMoved(BFWindow window)
{
if (IsMenuWindow(window))
return;
Close();
}
public void RemoveHandlers()
{
WidgetWindow.sOnMouseDown.Remove(scope => HandleMouseDown, true);
WidgetWindow.sOnMouseWheel.Remove(scope => HandleMouseWheel, true);
WidgetWindow.sOnWindowLostFocus.Remove(scope => WindowLostFocus, true);
WidgetWindow.sOnWindowMoved.Remove(scope => HandleWindowMoved, true);
WidgetWindow.sOnMenuItemSelected.Remove(scope => HandleSysMenuItemSelected, true);
WidgetWindow.sOnKeyDown.Remove(scope => HandleKeyDown, true);
if (mRelativeWidget != null)
{
mRelativeWidget.mOnRemovedFromParent.Remove(scope => HandleRemovedFromManager, true);
mRelativeWidget.mOnMouseUp.Remove(scope => HandleMouseUp, true);
}
}
public void Close()
{
if (mHasClosed)
return;
mHasClosed = true;
RemoveHandlers();
//Debug.Assert(mWidgetWindow != null);
if (mWidgetWindow != null)
{
mWidgetWindow.Close();
if (mMenu.mOnMenuClosed.HasListeners)
mMenu.mOnMenuClosed(mMenu, mItemSelected);
}
}
public override void LostFocus()
{
base.LostFocus();
if (mOpeningSubMenu)
return;
if (!mReplacing)
{
Close();
}
else
{
RemoveHandlers();
}
}
public override void KeyDown(KeyCode keyCode, bool isRepeat)
{
base.KeyDown(keyCode, isRepeat);
if (mItemWidgets.Count > 0)
{
int32 itemsPerPage = (int32)Math.Ceiling((mParent.mHeight - 8) / mItemWidgets[0].mHeight) - 1;
switch (keyCode)
{
case .Home:
SetSelection(0);
break;
case .End:
SetSelection(mItemWidgets.Count - 1);
break;
case .PageUp:
SetSelection(Math.Max(0, mSelectIdx - itemsPerPage));
break;
case .PageDown:
SetSelection(Math.Min(mItemWidgets.Count - 1, Math.Max(0, mSelectIdx) + itemsPerPage));
break;
case .Up:
if (mSelectIdx == -1)
{
SetSelection(0);
break;
}
else if (mSelectIdx > 0)
SetSelection(mSelectIdx - 1);
break;
case .Down:
if (mSelectIdx == -1)
{
SetSelection(0);
break;
}
else if (mSelectIdx < mItemWidgets.Count - 1)
SetSelection(mSelectIdx + 1);
break;
case .Return:
SubmitSelection();
case .Right:
if (mItemSelected != null)
{
if (!mItemSelected.mItems.IsEmpty)
{
}
}
default:
}
}
}
}
public class Menu : IMenu
{
public Menu mParent;
public String mLabel ~ delete _;
public List<Menu> mItems = new List<Menu>() ~ DeleteContainerAndItems!(_);
public Event<delegate void(Menu menu)> mOnMenuItemSelected ~ _.Dispose();
public Event<delegate void(Menu menu)> mOnMenuItemUpdate ~ _.Dispose();
public Event<delegate void(Menu menu, Menu itemSelected)> mOnMenuClosed ~ _.Dispose();
public Object mThemeData;
public bool mDisabled;
public bool mBold;
public IDrawable mIconImage;
public ~this()
{
}
public virtual Menu AddItem(StringView label = default)
{
Menu item = new Menu();
item.mParent = this;
if (!label.IsEmpty)
item.mLabel = new String(label);
mItems.Add(item);
return item;
}
public void SetDisabled(bool disabled)
{
mDisabled = disabled;
}
}
}

View file

@ -0,0 +1,279 @@
using System;
using System.Collections.Generic;
using System.Text;
using Beefy.widgets;
using Beefy.theme;
using Beefy.events;
using Beefy.utils;
using Beefy.geom;
using Beefy.gfx;
using System.Diagnostics;
namespace Beefy.widgets
{
public class ScrollContentContainer : Widget
{
public this()
{
mSelfMouseVisible = false;
}
public override void Draw(Graphics g)
{
base.Draw(g);
//using (g.PushColor(0x80FF0000))
//g.FillRect(0, 0, mWidth, mHeight);
}
public override void Resize(float x, float y, float width, float height)
{
base.Resize(x, y, width, height);
}
}
public class ScrollableWidget : Widget
{
public Scrollbar mHorzScrollbar;
public Scrollbar mVertScrollbar;
public Insets mScrollbarInsets = new Insets() ~ delete _;
public ScrollContentContainer mScrollContentContainer;
public Widget mScrollContent;
public Insets mScrollContentInsets = new Insets() ~ delete _;
public SmoothValue mVertPos = new SmoothValue() ~ delete _;
public SmoothValue mHorzPos = new SmoothValue() ~ delete _;
public bool mUpdatingContentPosition;
public float mScrollbarBaseContentSizeOffset = 0;
public this()
{
mScrollContentContainer = new ScrollContentContainer();
mScrollContentContainer.mClipGfx = true;
mScrollContentContainer.mClipMouse = true;
AddWidget(mScrollContentContainer);
}
public ~this()
{
//TODO: Assert mScrollContentContainer is deleted
}
public virtual void InitScrollbars(bool wantHorz, bool wantVert)
{
// We use 'AddWidgetAtIndex' to ensure that scrollbars are always the first child widget
// This is for enduring update order - so we make sure the scroll position is updated
// before the children are updated
if (wantHorz)
{
if (mHorzScrollbar == null)
{
mHorzScrollbar = ThemeFactory.mDefault.CreateScrollbar(Scrollbar.Orientation.Horz);
mHorzScrollbar.Init();
mHorzScrollbar.mOnScrollEvent.Add(new => ScrollEventHandler);
AddWidgetAtIndex(0, mHorzScrollbar);
}
}
if (wantVert)
{
if (mVertScrollbar == null)
{
mVertScrollbar = ThemeFactory.mDefault.CreateScrollbar(Scrollbar.Orientation.Vert);
mVertScrollbar.Init();
mVertScrollbar.mOnScrollEvent.Add(new => ScrollEventHandler);
AddWidgetAtIndex(0, mVertScrollbar);
}
}
else
{
if (mVertScrollbar != null)
{
RemoveWidget(mVertScrollbar);
mVertPos.Set(0, true);
mScrollContent.Resize(0, 0, mScrollContent.mWidth, mScrollContent.mHeight);
delete mVertScrollbar;
mVertScrollbar = null;
}
}
}
public bool HorzScrollTo(double horzPos)
{
double aHorzPos = Math.Max(0, Math.Min(horzPos, mScrollContent.mWidth - mScrollContentContainer.mWidth));
if (aHorzPos == mHorzPos.mDest)
return false;
mHorzPos.Set(aHorzPos);
if (mHorzScrollbar != null)
{
mHorzScrollbar.mContentPos = mHorzPos.v;
mHorzScrollbar.ResizeContent();
}
UpdateContentPosition();
return true;
}
public bool VertScrollTo(double vertPos, bool immediate = false)
{
double aVertPos = Math.Max(0, Math.Min(vertPos, mScrollContent.mHeight - mScrollContentContainer.mHeight));
if (aVertPos == mVertPos.mDest)
return false;
if (mVertScrollbar != null)
aVertPos = mVertScrollbar.AlignPosition(aVertPos);
mVertPos.Set(aVertPos, immediate);
if (mVertScrollbar != null)
{
mVertScrollbar.mContentPos = mVertPos.v;
mVertScrollbar.ResizeContent();
}
UpdateContentPosition();
return true;
}
public virtual void UpdateScrollbarData()
{
if (mHorzScrollbar != null)
{
mHorzScrollbar.mPageSize = mScrollContentContainer.mWidth;
mHorzScrollbar.mContentSize = mScrollContent.mWidth;
mHorzScrollbar.UpdateData();
}
if (mVertScrollbar != null)
{
mVertScrollbar.mPageSize = mScrollContentContainer.mHeight;
mVertScrollbar.mContentSize = mScrollContent.mHeight;
mVertScrollbar.UpdateData();
}
}
public virtual void UpdateScrollbars()
{
bool hasMultipleScrollbars = (mHorzScrollbar != null) && (mVertScrollbar != null);
if (mHorzScrollbar != null)
{
float yPos = Math.Max(0, mHeight - mScrollbarInsets.mBottom - mHorzScrollbar.mBaseSize);
mHorzScrollbar.Resize(mScrollbarInsets.mLeft, yPos,
Math.Max(mWidth - mScrollbarInsets.mLeft - mScrollbarInsets.mRight - (hasMultipleScrollbars ? mVertScrollbar.mBaseSize + mVertScrollbar.mDualBarSizeOffset : 0), 0),
mHorzScrollbar.mBaseSize);
}
if (mVertScrollbar != null)
mVertScrollbar.Resize(Math.Max(mWidth - mScrollbarInsets.mRight - mVertScrollbar.mBaseSize, 0), mScrollbarInsets.mTop, mVertScrollbar.mBaseSize,
Math.Max(mHeight - mScrollbarInsets.mTop - mScrollbarInsets.mBottom - (hasMultipleScrollbars ? mHorzScrollbar.mBaseSize + mVertScrollbar.mDualBarSizeOffset : 0), 0));
mScrollContentContainer.Resize(mScrollbarInsets.mLeft + mScrollContentInsets.mLeft, mScrollbarInsets.mTop + mScrollContentInsets.mTop,
Math.Max(mWidth - mScrollbarInsets.mLeft - mScrollbarInsets.mRight - mScrollContentInsets.mLeft - mScrollContentInsets.mRight - ((mVertScrollbar != null) ? (mVertScrollbar.mBaseSize - mScrollbarBaseContentSizeOffset) : 0), 0),
Math.Max(mHeight - mScrollbarInsets.mTop - mScrollbarInsets.mBottom - mScrollContentInsets.mTop - mScrollContentInsets.mBottom - ((mHorzScrollbar != null) ? (mHorzScrollbar.mBaseSize - mScrollbarBaseContentSizeOffset) : 0), 0));
UpdateScrollbarData();
ScrollPositionChanged();
}
public Rect CalcRectFromContent()
{
return Rect(-mScrollbarInsets.mLeft - mScrollContentInsets.mLeft, -mScrollbarInsets.mTop - mScrollContentInsets.mTop,
Math.Max(mScrollContent.mWidth + mScrollbarInsets.mLeft + mScrollbarInsets.mRight + mScrollContentInsets.mLeft + mScrollContentInsets.mRight + ((mVertScrollbar != null) ? (mVertScrollbar.mBaseSize + mScrollbarBaseContentSizeOffset) : 0), 0),
Math.Max(mScrollContent.mHeight + mScrollbarInsets.mTop + mScrollbarInsets.mBottom + mScrollContentInsets.mTop + mScrollContentInsets.mBottom + ((mHorzScrollbar != null) ? (mHorzScrollbar.mBaseSize + mScrollbarBaseContentSizeOffset) : 0), 0));
}
void ScrollEventHandler(ScrollEvent theEvent)
{
ScrollPositionChanged();
}
public virtual void UpdateContentPosition()
{
mUpdatingContentPosition = true;
if ((mHorzScrollbar != null) && (mHorzScrollbar.mContentPos != mHorzPos.v))
{
mHorzScrollbar.mContentPos = mHorzPos.v;
mHorzScrollbar.UpdateData();
MarkDirty();
}
if ((mVertScrollbar != null) && (mVertScrollbar.mContentPos != mVertPos.v))
{
mVertScrollbar.mContentPos = mVertPos.v;
mVertScrollbar.UpdateData();
MarkDirty();
}
if (mScrollContent != null)
{
mScrollContent.Resize((int32)(-mHorzPos.v), (int32)(-mVertPos.v),
mScrollContent.mWidth, mScrollContent.mHeight);
}
mUpdatingContentPosition = false;
}
public virtual void ScrollPositionChanged()
{
if (mUpdatingContentPosition)
return;
if (mScrollContent != null)
{
if ((mHorzScrollbar != null) && (!mHorzPos.IsMoving))
mHorzPos.Set(mHorzScrollbar.mContentPos, true);
if ((mVertScrollbar != null) && (!mVertPos.IsMoving))
mVertPos.Set(mVertScrollbar.mContentPos, true);
UpdateContentPosition();
}
}
public override void Resize(float x, float y, float width, float height)
{
base.Resize(x, y, width, height);
UpdateScrollbars();
}
public override void Update()
{
base.Update();
if ((mHorzPos.IsMoving) || (mVertPos.IsMoving))
{
mHorzPos.Update();
mVertPos.Update();
UpdateContentPosition();
MarkDirty();
}
}
public override void MouseWheel(float x, float y, int32 delta)
{
base.MouseWheel(x, y, delta);
if (mVertScrollbar != null)
mVertScrollbar.MouseWheel(x, y, delta);
else if (mHorzScrollbar != null)
mHorzScrollbar.MouseWheel(x, y, delta);
}
public override void RehupScale(float oldScale, float newScale)
{
double showPctV = 0;
//double showPctH = 0;
if (mVertScrollbar != null)
{
showPctV = (mVertScrollbar.mContentPos + mVertScrollbar.mPageSize * 0.5f) / mVertScrollbar.mContentSize;
}
mScrollContentInsets.Scale(newScale / oldScale);
base.RehupScale(oldScale, newScale);
if ((mVertScrollbar != null) && (mVertScrollbar.mContentPos != 0))
{
mVertScrollbar.mContentPos = showPctV * mVertScrollbar.mContentSize - mVertScrollbar.mPageSize * 0.5f;
mVertScrollbar.Scroll(0);
}
}
public virtual Rect GetVisibleContentRange()
{
return Rect(-mScrollContent.mX, -mScrollContent.mY, mScrollContentContainer.mWidth, mScrollContentContainer.mHeight);
}
}
}

View file

@ -0,0 +1,232 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using Beefy.events;
namespace Beefy.widgets
{
public delegate void ScrollEventHandler(ScrollEvent theEvent);
public abstract class Scrollbar : Widget
{
public class Thumb : Widget, IDragInterface
{
public Scrollbar mScrollbar;
public DragHelper mDraggableHelper ~ delete _;
public Event<Action<float, float>> mOnDrag ~ _.Dispose();
public this()
{
mDraggableHelper = new DragHelper(this, this);
}
public void DragStart()
{
}
public void DragEnd()
{
}
public void MouseDrag(float x, float y, float dX, float dY)
{
float parentX;
float parentY;
SelfToParentTranslate(x - mDraggableHelper.mMouseDownX, y - mDraggableHelper.mMouseDownY, out parentX, out parentY);
mOnDrag(parentX, parentY);
mScrollbar.ScrollTo(mScrollbar.GetContentPosAt(parentX, parentY));
}
public override void MouseDown(float x, float y, int32 btn, int32 btnCount)
{
// For popups that normally don't get focus, give them focus so thumb tracking works
if ((mWidgetWindow.mParent != null) && (mWidgetWindow.mParent.mHasFocus))
mWidgetWindow.GotFocus();
base.MouseDown(x, y, btn, btnCount);
}
}
public class Arrow : Widget
{
public Scrollbar mScrollbar;
public int32 mDownTick;
public bool mBack;
public override void MouseDown(float x, float y, int32 btn, int32 btnCount)
{
base.MouseDown(x, y, btn, btnCount);
DoScroll();
mDownTick = 0;
}
protected virtual void DoScroll(float pct = 1.0f)
{
float anAmt = mScrollbar.GetScrollIncrement() * pct;
if (mBack)
anAmt = -anAmt;
mScrollbar.Scroll(anAmt);
}
public override void Update()
{
base.Update();
if ((mMouseDown) && (mMouseOver))
{
mDownTick++;
if (mDownTick > 20)
{
if (mScrollbar.mAlignItems)
{
if ((mDownTick - 20) % 3 == 0)
DoScroll();
}
else
{
DoScroll(1.0f / 3.0f);
}
}
}
}
}
public enum Orientation
{
Horz,
Vert
}
public double mContentSize;
public double mPageSize;
public double mContentPos;
public Orientation mOrientation;
public Thumb mThumb;
public Arrow mStartArrow;
public Arrow mEndArrow;
public float mBaseSize;
public float mDualBarSizeOffset;
public int32 mScrollDir;
public int32 mDownTick;
public float mLastMouseX;
public float mLastMouseY;
public float mScrollIncrement;
public bool mAlignItems;
public bool mDoAutoClamp = true;
public Event<ScrollEventHandler> mOnScrollEvent ~ _.Dispose();
public float GetScrollIncrement()
{
if (mScrollIncrement == 0)
return (float)(mPageSize / 10.0f);
return mScrollIncrement;
}
public virtual void Init()
{
}
public virtual void ResizeContent()
{
}
public virtual double AlignPosition(double pos)
{
if (mAlignItems)
return (float)Math.Round(pos / mScrollIncrement) * mScrollIncrement;
return pos;
}
public virtual void ScrollTo(double pos)
{
MarkDirty();
double oldPos = mContentPos;
mContentPos = pos;
mContentPos = Math.Min(mContentPos, mContentSize - mPageSize);
mContentPos = Math.Max(mContentPos, 0);
if (mAlignItems)
mContentPos = (float)Math.Round(mContentPos / mScrollIncrement) * mScrollIncrement;
ResizeContent();
if ((mOnScrollEvent.HasListeners) && (oldPos != mContentPos))
{
ScrollEvent scrollEvent = scope ScrollEvent();
scrollEvent.mOldPos = oldPos;
scrollEvent.mNewPos = mContentPos;
mOnScrollEvent(scrollEvent);
}
}
public virtual void UpdateData()
{
if (mDoAutoClamp)
Scroll(0);
ResizeContent();
}
public virtual void Scroll(double amt)
{
ScrollTo(mContentPos + amt);
}
public virtual double GetContentPosAt(float x, float y)
{
return 0;
}
public override void Update()
{
base.Update();
if ((mMouseDown) && (mMouseOver))
{
mDownTick++;
if ((mDownTick > 20) && (mUpdateCnt % 5 == 0))
{
if (((mOrientation == Orientation.Horz) && (mLastMouseX < mThumb.mX)) ||
((mOrientation == Orientation.Vert) && (mLastMouseY < mThumb.mY)))
Scroll(-mPageSize);
else
Scroll(mPageSize);
}
}
}
public override void MouseMove(float x, float y)
{
base.MouseMove(x, y);
mLastMouseX = x;
mLastMouseY = y;
}
public override void MouseDown(float x, float y, int32 btn, int32 btnCount)
{
mLastMouseX = x;
mLastMouseY = y;
base.MouseDown(x, y, btn, btnCount);
if (((mOrientation == Orientation.Horz) && (mLastMouseX < mThumb.mX)) ||
((mOrientation == Orientation.Vert) && (mLastMouseY < mThumb.mY)))
Scroll(-mPageSize);
else
Scroll(mPageSize);
mDownTick = 0;
}
public override void MouseWheel(float x, float y, int32 delta)
{
Scroll(GetScrollIncrement() * -delta);
}
}
}

View file

@ -0,0 +1,564 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using Beefy.gfx;
using Beefy.theme;
namespace Beefy.widgets
{
public abstract class TabbedView : DockedWidget
{
public class TabButton : Widget, IDockable, IDragInterface
{
public bool mIsActive;
public String mLabel ~ delete _;
public TabbedView mTabbedView;
public WidgetWindow mNewDraggingWindow;
public WidgetWindow mSrcDraggingWindow;
public float mWindowDragRelX;
public float mWindowDragRelY;
public float mLastMouseX;
public float mLastMouseY;
public bool mOwnsContent = true;
public Widget mContent;
public Insets mContentInsets = new Insets() ~ delete _;
public DragHelper mDragHelper ~ delete _;
public Event<Action> mCloseClickedEvent ~ _.Dispose();
delegate void Hey();
public float mWantWidth;
public WidgetWindow mMouseDownWindow;
public String Label
{
get
{
return mLabel;
}
set
{
String.NewOrSet!(mLabel, value);
}
}
public this()
{
mDragHelper = new DragHelper(this, this);
}
public ~this()
{
Debug.Assert(mMouseDownWindow == null);
if ((mContent != null) && (mOwnsContent))
{
if (mContent.mParent != null)
mContent.RemoveSelf();
delete mContent;
}
}
public override void RehupScale(float oldScale, float newScale)
{
float valScale = newScale / oldScale;
mContentInsets.Scale(valScale);
//Utils.SnapScale(ref mWidth, valScale);
mWantWidth *= valScale;
//Utils.SnapScale(ref mHeight, valScale);
base.RehupScale(oldScale, newScale);
}
public virtual void Activate(bool setFocus = true)
{
TabButton button = mTabbedView.GetActiveTab();
if (button != this)
{
if (button != null)
button.Deactivate();
mIsActive = true;
mTabbedView.mNeedResizeTabs = true;
mTabbedView.AddWidget(mContent);
if ((setFocus) && (mWidgetWindow != null))
mContent.SetFocus();
ResizeContent();
}
else if ((setFocus) && (mWidgetWindow != null))
mContent.SetFocus();
}
public virtual void ResizeContent()
{
mContent.Resize(mContentInsets.mLeft, mTabbedView.mTabHeight + mContentInsets.mTop,
Math.Max(mTabbedView.mWidth - mContentInsets.mLeft - mContentInsets.mRight, 0),
Math.Max(mTabbedView.mHeight - mTabbedView.mTabHeight - mContentInsets.mTop - mContentInsets.mBottom, 0));
}
public virtual void Deactivate()
{
if (mIsActive)
{
mIsActive = false;
mTabbedView.RemoveWidget(mContent);
}
}
public override void MouseDown(float x, float y, int32 btn, int32 btnCount)
{
bool wasMouseDown = mMouseDown;
base.MouseDown(x, y, btn, btnCount);
Activate();
if ((mMouseDown) && (!wasMouseDown))
{
//Debug.WriteLine("MouseDown {0} {1}", this, mWidgetWindow);
mMouseDownWindow = mWidgetWindow;
mMouseDownWindow.mOnMouseLeftWindow.Add(new => MouseLeftWindow);
}
}
public override void MouseMove(float x, float y)
{
base.MouseMove(x, y);
mLastMouseX = x;
mLastMouseY = y;
if (mNewDraggingWindow != null)
{
float rootX;
float rootY;
SelfToRootTranslate(mLastMouseX, mLastMouseY, out rootX, out rootY);
mNewDraggingWindow.SetClientPosition(
(int32)(mSrcDraggingWindow.mClientX + rootX - mWindowDragRelX), (int32)(mSrcDraggingWindow.mClientY + rootY - mWindowDragRelY));
}
}
public void DragStart()
{
mSrcDraggingWindow = mWidgetWindow;
if ((IsTotalWindowContent()) && (!mWidgetWindow.mIsMainWindow))
{
// Drag around entire window if we are the only content in it
SelfToRootTranslate(mDragHelper.mMouseDownX, mDragHelper.mMouseDownY, out mWindowDragRelX, out mWindowDragRelY);
mNewDraggingWindow = mWidgetWindow;
mNewDraggingWindow.SetAlpha(0.5f, 0, false);
mNewDraggingWindow.mOnWindowLostFocus.Add(new => WindowDragLostFocusHandler);
}
}
public void DragEnd()
{
//mWidgetWindow.mMouseLeftWindowDelegate.Remove(scope => MouseLeftWindow, true);
if (mSrcDraggingWindow != null)
mSrcDraggingWindow.mCaptureWidget = null;
mTabbedView.mParentDockingFrame.GetRootDockingFrame().HideDragTarget(this, !mDragHelper.mAborted);
if (mNewDraggingWindow != null)
{
mNewDraggingWindow.mOnWindowLostFocus.Remove(scope => WindowDragLostFocusHandler, true);
DockingFrame dockingFrame = (DockingFrame)mNewDraggingWindow.mRootWidget;
if (dockingFrame.GetDockedWindowCount() > 0)
mNewDraggingWindow.SetAlpha(1.0f, 0, true);
mNewDraggingWindow = null;
}
mSrcDraggingWindow = null;
}
public void MouseDrag(float x, float y, float dX, float dY)
{
mTabbedView.mParentDockingFrame.GetRootDockingFrame().ShowDragTarget(this);
}
public override void MouseUp(float x, float y, int32 btn)
{
bool wasMouseDown = mMouseDown;
base.MouseUp(x, y, btn);
if ((wasMouseDown) && (!mMouseDown))
{
mMouseDownWindow.mOnMouseLeftWindow.Remove(scope => MouseLeftWindow, true);
mMouseDownWindow = null;
}
}
public override void RemovedFromParent(Widget previousParent, WidgetWindow window)
{
base.RemovedFromParent(previousParent, window);
}
public virtual bool IsTotalWindowContent()
{
return (mTabbedView.mParentDockingFrame.mParentDockingFrame == null) &&
(mTabbedView.mParentDockingFrame.GetDockedWindowCount() == 1) &&
(mTabbedView.GetTabCount() == 1);
}
void WindowDragLostFocusHandler(BFWindow window, BFWindow newFocus)
{
mDragHelper.CancelDrag();
}
public void MouseLeftWindow(BFWindow window)
{
if (mDragHelper.mIsDragging)
{
if (mNewDraggingWindow == null)
{
mDragHelper.SetPreparingForWidgetMove(true);
WidgetWindow prevWidgetWindow = mWidgetWindow;
mTabbedView.mParentDockingFrame.GetRootDockingFrame().HideDragTarget(this);
mWindowDragRelX = mDragHelper.mMouseDownX;
mWindowDragRelY = mDragHelper.mMouseDownY;
float rootX;
float rootY;
SelfToRootTranslate(mLastMouseX, mLastMouseY, out rootX, out rootY);
DockingFrame subFrame = ThemeFactory.mDefault.CreateDockingFrame();
var parentWindow = mWidgetWindow;
while (parentWindow.mParent != null)
parentWindow = (WidgetWindow)parentWindow.mParent;
mNewDraggingWindow = new WidgetWindow(parentWindow, "",
(int32)(mSrcDraggingWindow.mClientX + rootX - mDragHelper.mMouseDownX), (int32)(mSrcDraggingWindow.mClientY + rootY - mDragHelper.mMouseDownY),
300, 500,
BFWindowBase.Flags.Border | BFWindowBase.Flags.ThickFrame | BFWindowBase.Flags.Resizable | BFWindowBase.Flags.SysMenu |
BFWindowBase.Flags.Caption | BFWindowBase.Flags.Minimize | BFWindowBase.Flags.ToolWindow | BFWindowBase.Flags.TopMost |
BFWindowBase.Flags.UseParentMenu,
subFrame);
Dock(subFrame, null, DockingFrame.WidgetAlign.Top);
//subFrame.AddDockedWidget(fourthTabbedView, null, DockingFrame.WidgetAlign.Left, false);
prevWidgetWindow.SetNonExclusiveMouseCapture();
mNewDraggingWindow.SetAlpha(0.5f, 0, false);
//mNewDraggingWindow.SetMouseCapture();
//mNewDraggingWindow.mCaptureWidget = sub
mDragHelper.SetPreparingForWidgetMove(false);
// We need the previous window to keep tracking the mouse dragging, so we need to restore mCaptureWidget since it would be removed
// when the widget gets removed from the previous parent
prevWidgetWindow.mCaptureWidget = this;
mNewDraggingWindow.mOnWindowLostFocus.Add(new => WindowDragLostFocusHandler);
if (mTabbedView.mSharedData.mOpenNewWindowDelegate.HasListeners)
mTabbedView.mSharedData.mOpenNewWindowDelegate(mTabbedView, mNewDraggingWindow);
}
}
}
public override void Update()
{
base.Update();
if (mNewDraggingWindow != null)
{
if (!mNewDraggingWindow.mHasFocus)
{
//int a = 0;
}
}
}
public override void MouseLeave()
{
base.MouseLeave();
}
public bool CanDock(DockingFrame frame, DockedWidget refWidget, DockingFrame.WidgetAlign align)
{
return (align != DockingFrame.WidgetAlign.Inside) || (refWidget is TabbedView);
}
public virtual void Dock(DockingFrame frame, DockedWidget refWidget, DockingFrame.WidgetAlign align)
{
if ((refWidget != null) && (refWidget.mWidgetWindow != mWidgetWindow) && (mWidgetWindow != null))
mWidgetWindow.SetForeground();
if (mTabbedView.GetTabCount() == 1)
{
mTabbedView.Dock(frame, refWidget, align);
return;
}
TabbedView prevTabbedView = mTabbedView;
frame.StartContentInterpolate();
if (align == DockingFrame.WidgetAlign.Inside)
{
TabbedView tabbedView = (TabbedView)refWidget;
if (tabbedView != mTabbedView)
{
mTabbedView.RemoveTab(this, false);
tabbedView.AddTab(this, tabbedView.GetInsertPositionFromCursor());
Activate();
}
}
else
{
// Create new tabbed view to put this in
TabbedView tabbedView = ThemeFactory.mDefault.CreateTabbedView(mTabbedView.mSharedData);
//tabbedView.mSharedData = mTabbedView.mSharedData.Ref();
//tabbedView.mSharedData.mOpenNewWindowDelegate = mTabbedView.mSharedData.mOpenNewWindowDelegate;
tabbedView.SetRequestedSize(mTabbedView.mWidth, mTabbedView.mHeight);
mTabbedView.RemoveTab(this, false);
tabbedView.AddTab(this);
float rootX;
float rootY;
prevTabbedView.SelfToRootTranslate(mX, mY, out rootX, out rootY);
tabbedView.StartInterpolate(rootX, rootY, mWidth, mHeight);
tabbedView.Dock(frame, refWidget, align);
}
if (prevTabbedView.GetTabCount() == 0)
{
prevTabbedView.mParentDockingFrame.RemoveDockedWidget(prevTabbedView);
}
}
public virtual void DrawDockPreview(Graphics g)
{
using (g.PushTranslate(-mX - mDragHelper.mMouseDownX, -mY - mDragHelper.mMouseDownY))
mTabbedView.DrawDockPreview(g);
}
}
public class SharedData
{
int32 mRefCount = 1;
public Event<OpenNewWindowDelegate> mOpenNewWindowDelegate ~ _.Dispose();
public Event<Action<TabbedView>> mTabbedViewClosed ~ _.Dispose();
public SharedData Ref()
{
mRefCount++;
return this;
}
public void Deref()
{
if (--mRefCount == 0)
delete this;
}
}
public delegate void OpenNewWindowDelegate(TabbedView tabbedView, WidgetWindow newWindow);
public Action<Menu> mPopulateMenuEvent;
public float mTabHeight;
public float mTabAreaWidth;
public bool mNeedResizeTabs;
public SharedData mSharedData ~ _.Deref();
public List<TabButton> mTabs = new List<TabButton>() ~ delete _;
public this(SharedData sharedData)
{
if (sharedData != null)
mSharedData = sharedData.Ref();
else
mSharedData = new SharedData();
}
public ~this()
{
for (var tab in mTabs)
Widget.RemoveAndDelete(tab);
}
public void Closed()
{
mSharedData.mTabbedViewClosed(this);
}
public override void RehupScale(float oldScale, float newScale)
{
float valScale = newScale / oldScale;
Utils.RoundScale(ref mTabHeight, valScale);
Utils.RoundScale(ref mTabAreaWidth, valScale);
base.RehupScale(oldScale, newScale);
mNeedResizeTabs = true;
}
public virtual TabButton FindTabForContent(Widget content)
{
for (TabButton aTabButton in mTabs)
if (aTabButton.mContent == content)
return aTabButton;
return null;
}
protected virtual TabButton CreateTabButton()
{
return new TabButton();
}
public virtual int GetTabCount()
{
return mTabs.Count;
}
public virtual void WithTabs(Action<TabbedView.TabButton> func)
{
for (var tab in mTabs)
func(tab);
}
public virtual TabButton GetActiveTab()
{
TabButton activeTab = null;
WithTabs(scope [&] (tab) =>
{
if (tab.mIsActive)
activeTab = tab;
});
return activeTab;
}
public virtual TabButton AddTab(String label, float width, Widget content, bool ownsContent)
{
TabButton aTabButton = CreateTabButton();
aTabButton.mTabbedView = this;
aTabButton.mOwnsContent = ownsContent;
aTabButton.Label = label;
aTabButton.mWantWidth = width;
aTabButton.mHeight = mTabHeight;
aTabButton.mContent = content;
AddTab(aTabButton);
return aTabButton;
}
public virtual int GetInsertPositionFromCursor()
{
int bestIdx = mTabs.Count;
while (bestIdx > 0)
{
var checkTab = mTabs[bestIdx - 1];
float tabCenterX;
float tabCenterY;
checkTab.SelfToRootTranslate(checkTab.mWidth / 2, checkTab.mHeight / 2, out tabCenterX, out tabCenterY);
if (mWidgetWindow.mMouseX > tabCenterX)
break;
bestIdx--;
}
return bestIdx;
}
public virtual void AddTab(TabButton tabButton, int insertIdx = 0)
{
AddWidget(tabButton);
mTabs.Insert(insertIdx, tabButton);
tabButton.mTabbedView = this;
if (mTabs.Count == 1)
tabButton.Activate();
mNeedResizeTabs = true;
}
public virtual void RemoveTab(TabButton tabButton, bool deleteTab = true)
{
bool hadFocus = mWidgetWindow.mFocusWidget != null;
if (tabButton.mIsActive)
tabButton.Deactivate();
RemoveWidget(tabButton);
mTabs.Remove(tabButton);
if ((GetActiveTab() == null) && (mTabs.Count > 0))
mTabs[0].Activate((hadFocus) && (mWidgetWindow.mFocusWidget == null));
mNeedResizeTabs = true;
if (deleteTab)
BFApp.sApp.DeferDelete(tabButton);
}
protected virtual void ResizeTabs(bool immediate)
{
float curX = 0;
for (TabButton aTabButton in mTabs)
{
aTabButton.Resize(curX, aTabButton.mY, aTabButton.mWidth, aTabButton.mHeight);
curX += aTabButton.mWidth;
}
mTabAreaWidth = curX;
mNeedResizeTabs = false;
}
public void FinishTabAnim()
{
if (mNeedResizeTabs)
{
ResizeTabs(true);
}
}
public override void Update()
{
base.Update();
if (mNeedResizeTabs)
ResizeTabs(false);
}
public override void Dock(DockingFrame frame, DockedWidget refWidget, DockingFrame.WidgetAlign align)
{
if (this == refWidget)
return;
if ((refWidget != null) && (refWidget.mWidgetWindow != mWidgetWindow))
refWidget.mWidgetWindow.SetForeground();
if (align == DockingFrame.WidgetAlign.Inside)
{
TabbedView tabbedView = (TabbedView)refWidget;
while (mTabs.Count > 0)
{
TabButton tab = mTabs[0];
RemoveTab(tab, false);
tabbedView.AddTab(tab, tabbedView.GetInsertPositionFromCursor());
tab.Activate();
}
mParentDockingFrame.RemoveDockedWidget(this);
Closed();
BFApp.sApp.DeferDelete(this);
}
else
{
if (frame.mWidgetWindow != mWidgetWindow)
mAllowInterpolate = false;
if (mWidth != 0)
SetRequestedSize(mWidth, mHeight);
frame.StartContentInterpolate();
if (mParentDockingFrame != null)
{
mParentDockingFrame.StartContentInterpolate();
mParentDockingFrame.RemoveDockedWidget(this);
}
frame.AddDockedWidget(this, refWidget, align);
}
}
public override void Resize(float x, float y, float width, float height)
{
base.Resize(x, y, width, height);
ResizeTabs(true);
TabButton tab = GetActiveTab();
if (tab != null)
tab.ResizeContent();
}
}
}

View file

@ -0,0 +1,824 @@
using System;
using System.Collections.Generic;
using System.Text;
using Beefy.gfx;
using Beefy.events;
using System.Diagnostics;
using Beefy.utils;
using Beefy.geom;
namespace Beefy.widgets
{
public enum MouseFlag
{
Left = 1,
Right = 2,
Middle = 4,
Kbd = 8
}
public delegate void ResizedHandler(Widget widget);
public delegate void LostFocusHandler(Widget widget);
public delegate void MouseEventHandler(MouseEvent mouseArgs);
public delegate void RemovedFromParentHandler(Widget widget, Widget prevParent, WidgetWindow widgetWindow);
public delegate void AddedToParentHandler(Widget widget);
public class Widget : ILeakIdentifiable
{
static int32 sIdx;
public int32 mIdx = sIdx++;
public void ToLeakString(String str)
{
str.AppendF("Idx:{0}", mIdx);
}
public class TransformData
{
public float a;
public float b;
public float c;
public float d;
};
public TransformData mTransformData ~ delete _;
public float mX;
public float mY;
public float mWidth;
public float mHeight;
public int32 mUpdateCnt;
public String mIdStr ~ delete _;
public List<Widget> mChildWidgets;
public MouseFlag mMouseFlags;
public int32 mMouseCaptureCount;
public Widget mParent;
public WidgetWindow mWidgetWindow;
public bool mMouseOver;
public bool mMouseDown;
public bool mClipGfx;
public bool mClipMouse;
public bool mVisible = true;
public bool mHasFocus;
public bool mSelfMouseVisible = true;
public bool mMouseVisible = true;
public bool mDeferredDelete;
public Insets mMouseInsets ~ delete _;
public bool mAutoFocus;
public float X { get { return mX; } set { mX = value; } }
public float Y { get { return mY; } set { mY = value; } }
public float CenterX { get { return mX + mWidth / 2; } }
public float CenterY { get { return mY + mHeight / 2; } }
[DesignEditable(SortName="0pos2")]
public float Width { get { return mWidth; } set { mWidth = value; } }
[DesignEditable(SortName="0pos3")]
public float Height { get { return mHeight; } set { mHeight = value; } }
[DesignEditable(SortName="0name")]
public String Id { get { return mIdStr; } set { mIdStr = value; } }
public Event<LostFocusHandler> mOnLostFocus ~ _.Dispose();
//public event MouseEventHandler mMouseMoveHandler;
public Event<MouseEventHandler> mOnMouseDown ~ _.Dispose();
public Event<MouseEventHandler> mOnMouseUp ~ _.Dispose();
public Event<MouseEventHandler> mOnMouseClick ~ _.Dispose();
public Event<MouseEventHandler> mOnMouseMove ~ _.Dispose();
public Event<ResizedHandler> mOnResized ~ _.Dispose();
public Event<RemovedFromParentHandler> mOnRemovedFromParent ~ _.Dispose();
public Event<AddedToParentHandler> mOnAddedToParent ~ _.Dispose();
public Event<KeyDownHandler> mOnKeyDown ~ _.Dispose();
public Event<Action<Widget>> mOnDeleted ~ _.Dispose();
public Matrix Transform
{
get
{
if (mTransformData == null)
return Matrix(1, 0, 0, 1, mX, mY);
return Matrix(mTransformData.a, mTransformData.b, mTransformData.c, mTransformData.d, mX, mY);
}
set
{
if ((value.a == 1) && (value.b == 0) && (value.c == 0) && (value.d == 1))
{
mTransformData = null;
mX = value.tx;
mY = value.ty;
}
else
{
if (mTransformData == null)
mTransformData = new TransformData();
mTransformData.a = value.a;
mTransformData.b = value.b;
mTransformData.c = value.c;
mTransformData.d = value.d;
mX = value.tx;
mY = value.ty;
}
}
}
public this()
{
}
public ~this()
{
if (mChildWidgets != null)
{
for (var child in mChildWidgets)
child.ParentDeleted();
delete mChildWidgets;
}
if ((mParent != null) && (mOnRemovedFromParent.HasListeners))
{
var prevParent = mParent;
mParent = null;
mOnRemovedFromParent(this, prevParent, mWidgetWindow);
}
mOnDeleted(this);
}
public void MarkDirty()
{
if (mWidgetWindow != null)
{
mWidgetWindow.mIsDirty = true;
}
}
public static void RemoveAndDelete(Widget widget)
{
if ((widget != null) && (widget.mParent != null))
widget.RemoveSelf();
delete widget;
}
public virtual void ParentDeleted()
{
delete this;
}
public virtual void DefaultDesignInit()
{
mIdStr = new String();
GetType().GetName(mIdStr);
mWidth = 32;
mHeight = 32;
}
public virtual void Serialize(StructuredData data)
{
//data.Add("X", mX);
//data.Add("Y", mY);
data.Add("W", mWidth);
data.Add("H", mHeight);
}
public virtual bool Deserialize(StructuredData data)
{
//mX = data.GetFloat("X");
//mY = data.GetFloat("Y");
mWidth = data.GetFloat("W");
mHeight = data.GetFloat("H");
return true;
}
public virtual void AddWidgetUntracked(Widget widget)
{
Debug.Assert(widget.mParent == null);
widget.mParent = this;
widget.mWidgetWindow = mWidgetWindow;
if (mWidgetWindow != null)
widget.InitChildren();
widget.AddedToParent();
}
public virtual void AddWidgetAtIndex(int idx, Widget widget)
{
Debug.Assert(widget.mParent == null);
if (mChildWidgets == null)
mChildWidgets = new List<Widget>();
widget.mParent = this;
widget.mWidgetWindow = mWidgetWindow;
mChildWidgets.Insert(idx, widget);
if (mWidgetWindow != null)
widget.InitChildren();
widget.AddedToParent();
}
public bool HasParent(Widget wantParent)
{
var checkParent = mParent;
while (checkParent != null)
{
if (checkParent == wantParent)
return true;
checkParent = checkParent.mParent;
}
return false;
}
public virtual void AddWidget(Widget widget)
{
int pos = (mChildWidgets != null) ? mChildWidgets.Count : 0;
AddWidgetAtIndex(pos, widget);
}
public virtual void AddWidget(Widget widget, Widget addAfter)
{
int pos = mChildWidgets.IndexOf(addAfter) + 1;
AddWidgetAtIndex(pos, widget);
}
public virtual void InsertWidget(Widget widget, Widget insertBefore)
{
int pos = (insertBefore != null) ? mChildWidgets.IndexOf(insertBefore) : mChildWidgets.Count;
AddWidgetAtIndex(pos, widget);
}
public virtual void InitChildren()
{
if (mChildWidgets != null)
{
for (Widget child in mChildWidgets)
{
if (child.mWidgetWindow != mWidgetWindow)
{
child.mWidgetWindow = mWidgetWindow;
child.InitChildren();
}
}
}
}
protected virtual void RemovedFromWindow()
{
WidgetWindow prevWidgetWindow = mWidgetWindow;
//TODO: Why did we set it to NULL here? That caused LostFocus to not have the widget in window
//mWidgetWindow = null;
if (prevWidgetWindow != null)
{
if (prevWidgetWindow.mFocusWidget == this)
{
prevWidgetWindow.mFocusWidget = null;
if (mHasFocus)
LostFocus();
else
Debug.Assert(!prevWidgetWindow.mHasFocus);
}
if (prevWidgetWindow.mCaptureWidget == this)
prevWidgetWindow.mCaptureWidget = null;
if (prevWidgetWindow.mOverWidget == this)
{
prevWidgetWindow.mOverWidget = null;
MouseLeave();
}
}
mWidgetWindow = null;
if (mChildWidgets != null)
{
for (int32 childIdx = 0; childIdx < mChildWidgets.Count; childIdx++)
{
var child = mChildWidgets[childIdx];
child.RemovedFromWindow();
}
}
}
public virtual void RemoveWidget(Widget widget)
{
Debug.Assert(widget.mParent == this);
WidgetWindow prevWidgetWindow = widget.mWidgetWindow;
widget.mParent = null;
if (mChildWidgets != null)
mChildWidgets.Remove(widget);
widget.RemovedFromWindow();
widget.RemovedFromParent(this, prevWidgetWindow);
}
public Widget FindByIdStr(String name, bool recurseChildren = true)
{
if (mIdStr == name)
return this;
if ((recurseChildren) && (mChildWidgets != null))
{
for (Widget child in mChildWidgets)
{
Widget found = child.FindByIdStr(name, recurseChildren);
if (found != null)
return found;
}
}
return null;
}
public virtual void RemoveSelf()
{
mParent.RemoveWidget(this);
}
public virtual bool ContainsWidget(Widget widget)
{
if (mChildWidgets != null)
{
for (Widget child in mChildWidgets)
{
if (child == widget)
return true;
if (child.ContainsWidget(widget))
return true;
}
}
return false;
}
public virtual void CaptureMouse()
{
//Debug.WriteLine("CaptureMouse {0}", this);
if (mWidgetWindow.mHasFocus)
mWidgetWindow.mCaptureWidget = this;
}
public virtual void ReleaseMouseCapture()
{
if (mWidgetWindow == null)
return;
mWidgetWindow.mCaptureWidget = null;
mWidgetWindow.RehupMouse(false);
}
public virtual void SetFocus()
{
MarkDirty();
mWidgetWindow.SetFocus(this);
}
public virtual void SetVisible(bool visible)
{
mVisible = visible;
}
public virtual void AddedToParent()
{
if (mOnAddedToParent.HasListeners)
{
mOnAddedToParent(this);
}
}
public virtual void RemovedFromParent(Widget previousParent, WidgetWindow window)
{
if (mOnRemovedFromParent.HasListeners)
{
mOnRemovedFromParent(this, previousParent, window);
}
}
public virtual void GotFocus()
{
Debug.Assert(!mHasFocus);
mHasFocus = true;
}
public virtual void LostFocus()
{
Debug.Assert(mHasFocus);
mHasFocus = false;
mOnLostFocus(this);
}
public virtual void Draw(Graphics g)
{
}
public virtual void DrawAll(Graphics g)
{
if (!mVisible)
return;
if (mClipGfx)
{
if ((mWidth <= 0) || (mHeight <= 0))
return;
g.PushClip(0, 0, mWidth, mHeight);
}
Draw(g);
if (mChildWidgets != null)
{
for (int anIdx = 0; anIdx < mChildWidgets.Count; anIdx++)
{
Widget child = mChildWidgets[anIdx];
Debug.Assert(child.mParent == this);
if (child.mTransformData != null)
{
Matrix m = child.Transform;
g.PushMatrix(m);
}
else
{
g.PushTranslate(child.mX, child.mY);
}
child.DrawAll(g);
g.PopMatrix();
}
}
if (mClipGfx)
g.PopClip();
}
public virtual void Update()
{
mUpdateCnt++;
}
public void DeferDelete()
{
mDeferredDelete = true;
}
public virtual void UpdateAll()
{
Update();
if (mDeferredDelete)
{
delete this;
return;
}
// Removed self?
if (mWidgetWindow == null)
return;
if (mChildWidgets != null)
{
for (int32 anIdx = 0; anIdx < mChildWidgets.Count; anIdx++)
{
Widget child = mChildWidgets[anIdx];
Debug.Assert(child.mParent == this);
Debug.Assert(child.mWidgetWindow == mWidgetWindow);
child.UpdateAll();
}
}
}
public virtual void Resize(float x, float y, float width, float height)
{
Debug.Assert(width >= 0);
Debug.Assert(height >= 0);
mX = x;
mY = y;
mWidth = width;
mHeight = height;
if (mOnResized.HasListeners)
mOnResized(this);
}
public void ResizeClamped(float x, float y, float width, float height)
{
Resize(x, y, Math.Max(width, 0), Math.Max(height, 0));
}
public void RehupSize()
{
Resize(mX, mY, mWidth, mHeight);
}
public virtual void RehupScale(float oldScale, float newScale)
{
if (mChildWidgets != null)
{
for (var child in mChildWidgets)
child.RehupScale(oldScale, newScale);
}
}
public Rect GetRect()
{
return Rect(mX, mY, mWidth, mHeight);
}
public virtual void MouseMove(float x, float y)
{
MarkDirty();
if (mOnMouseMove.HasListeners)
{
MouseEvent e = scope MouseEvent();
e.mX = x;
e.mY = y;
mOnMouseMove(e);
}
}
public virtual Widget FindWidgetByCoords(float x, float y)
{
if (!mVisible || !mMouseVisible)
return null;
if (mClipMouse)
{
if (!Contains(x, y))
return null;
}
if (mChildWidgets != null)
{
for (int i = mChildWidgets.Count - 1; i >= 0; --i)
{
Widget child = mChildWidgets[i];
child.ParentToSelfTranslate(x, y, var childX, var childY);
Widget aFoundWidget = child.FindWidgetByCoords(childX, childY);
if (aFoundWidget != null)
return aFoundWidget;
}
}
if (!mSelfMouseVisible)
return null;
if (mClipMouse)
return this;
if (Contains(x, y))
return this;
return null;
}
public virtual bool Contains(float x, float y)
{
if (mMouseInsets != null)
{
return ((mWidth != 0) &&
(x >= mMouseInsets.mLeft) && (y >= mMouseInsets.mTop) &&
(x < mWidth - mMouseInsets.mRight) && (y < mHeight - mMouseInsets.mBottom));
}
return ((mWidth != 0) &&
(x >= 0) && (y >= 0) && (x < mWidth) && (y < mHeight));
}
public virtual void SelfToOtherTranslate(Widget targetWidget, float x, float y, out float transX, out float transY)
{
if (targetWidget == this)
{
transX = x;
transY = y;
return;
}
SelfToParentTranslate(x, y, out transX, out transY);
if (targetWidget.mParent == mParent)
{
mParent.ParentToSelfTranslate(transX, transY, out transX, out transY);
return;
}
if (mParent != null)
{
float parentX = transX;
float parentY = transY;
mParent.SelfToOtherTranslate(targetWidget, parentX, parentY, out transX, out transY);
}
}
public virtual void SelfToRootTranslate(float x, float y, out float transX, out float transY)
{
SelfToParentTranslate(x, y, out transX, out transY);
if (mParent != null)
{
float parentX = transX;
float parentY = transY;
mParent.SelfToRootTranslate(parentX, parentY, out transX, out transY);
}
}
public virtual void RootToSelfTranslate(float x, float y, out float transX, out float transY)
{
float curX = x;
float curY = y;
if (mParent != null)
{
mParent.RootToSelfTranslate(curX, curY, out transX, out transY);
curX = transX;
curY = transY;
}
ParentToSelfTranslate(curX, curY, out transX, out transY);
}
public virtual void ParentToSelfTranslate(float x, float y, out float transX, out float transY)
{
if (mTransformData == null)
{
transX = x - mX;
transY = y - mY;
}
else
{
Matrix m = Transform;
float den = m.a * m.d - m.b * m.c;
float a = m.d / den;
float b = -m.b / den;
float c = -m.c / den;
float d = m.a / den;
float tx = (m.c * m.ty - m.d * m.tx) / den;
float ty = -(m.a * m.ty - m.b * m.tx) / den;
transX = tx + a * (x - mX) + c * (y - mY);
transY = ty + b * (x - mX) + d * (y - mY);
}
}
public virtual void SelfToParentTranslate(float x, float y, out float transX, out float transY)
{
if (mTransformData == null)
{
transX = x + mX;
transY = y + mY;
}
else
{
transX = mX + mTransformData.a * x + mTransformData.c * y;
transY = mY + mTransformData.b * x + mTransformData.d * y;
}
}
public virtual void MouseDown(float x, float y, int32 btn, int32 btnCount)
{
MarkDirty();
if (mAutoFocus)
SetFocus();
mMouseFlags |= (MouseFlag)(1 << btn);
mMouseDown = true;
if (btn != 3) // Not kbd
CaptureMouse();
if (mOnMouseDown.HasListeners)
{
MouseEvent mouseEvent = scope MouseEvent();
mouseEvent.mSender = this;
mouseEvent.mX = x;
mouseEvent.mY = y;
mouseEvent.mBtn = btn;
mouseEvent.mBtnCount = btnCount;
mOnMouseDown(mouseEvent);
}
}
public virtual void MouseClicked(float x, float y, int32 btn)
{
if (mOnMouseClick.HasListeners)
{
MouseEvent mouseEvent = scope MouseEvent();
mouseEvent.mSender = this;
mouseEvent.mX = x;
mouseEvent.mY = y;
mouseEvent.mBtn = btn;
mOnMouseClick(mouseEvent);
}
}
public virtual void MouseWheel(float x, float y, int32 delta)
{
MarkDirty();
if (mParent != null)
{
// Keep passing it up until some is interested in using it...
float aX;
float aY;
SelfToParentTranslate(x, y, out aX, out aY);
mParent.MouseWheel(aX, aY, delta);
}
}
public virtual void MouseUp(float x, float y, int32 btn)
{
MarkDirty();
bool hadPhysMouseDown = mMouseFlags != MouseFlag.Kbd;
mMouseFlags &= (MouseFlag)(~(1 << btn));
bool hasPhysMouseDown = (mMouseFlags != MouseFlag.Kbd) && (mMouseFlags != 0);
mMouseDown = mMouseFlags != 0;
if ((hadPhysMouseDown) && (!hasPhysMouseDown))
ReleaseMouseCapture();
if ((mMouseOver) || (btn == 3))
MouseClicked(x, y, btn);
if (mOnMouseUp.HasListeners)
{
MouseEvent mouseEvent = scope MouseEvent();
mouseEvent.mSender = this;
mouseEvent.mX = x;
mouseEvent.mY = y;
mouseEvent.mBtn = btn;
mOnMouseUp(mouseEvent);
}
}
public virtual void MouseEnter()
{
mMouseOver = true;
}
public virtual void MouseLeave()
{
mMouseOver = false;
}
public virtual bool WantsMouseEvent(float x, float y, ref bool foundWidget)
{
if ((mMouseFlags != 0) || (mMouseCaptureCount > 0))
return true;
if (mChildWidgets != null)
{
for (int i = mChildWidgets.Count - 1; i >= 0; --i)
{
Widget child = mChildWidgets[i];
float childX, childY;
child.ParentToSelfTranslate(x, y, out childX, out childY);
if (child.WantsMouseEvent(childX, childY, ref foundWidget))
return true;
}
}
return false;
}
public virtual void KeyChar(KeyCharEvent keyEvent)
{
if (!keyEvent.mHandled)
KeyChar(keyEvent.mChar);
}
public virtual void KeyChar(char32 c)
{
}
public virtual void KeyDown(KeyDownEvent keyEvent)
{
if (!keyEvent.mHandled)
mOnKeyDown(keyEvent);
if (!keyEvent.mHandled)
KeyDown(keyEvent.mKeyCode, keyEvent.mIsRepeat);
}
public virtual void KeyDown(KeyCode keyCode, bool isRepeat)
{
}
public virtual void KeyUp(KeyCode keyCode)
{
}
public static bool HandleTab(int dir, List<Widget> tabWidgets)
{
for (int32 idx = 0; idx < tabWidgets.Count; idx++)
{
Widget widget = tabWidgets[idx];
if (widget.mHasFocus)
{
var nextWidget = tabWidgets[(idx + tabWidgets.Count + dir) % tabWidgets.Count];
nextWidget.SetFocus();
if (var editWidget = nextWidget as EditWidget)
editWidget.mEditWidgetContent.SelectAll();
return true;
}
}
return false;
}
}
}

View file

@ -0,0 +1,764 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using Beefy.gfx;
using Beefy.events;
using Beefy.sys;
using Beefy.geom;
namespace Beefy.widgets
{
public delegate void MouseLeftWindowHandler(BFWindow window);
public delegate void WindowLostFocusHandler(BFWindow window, BFWindow newFocus);
public delegate bool WindowCloseQueryHandler(BFWindow window);
public delegate void WindowClosedHandler(BFWindow window);
public delegate void WindowMovedHandler(BFWindow window);
public delegate void MouseWheelHandler(MouseEvent mouseEvent);
public delegate void KeyDownHandler(KeyDownEvent keyboardEvent);
//public delegate void CloseTemporaryHandler(WidgetWindow window);
public class WidgetWindow : BFWindow
{
public Event<MouseLeftWindowHandler> mOnMouseLeftWindow ~ _.Dispose();
public Event<WindowLostFocusHandler> mOnWindowLostFocus ~ _.Dispose();
public Event<MouseEventHandler> mOnMouseDown ~ _.Dispose();
public Event<WindowCloseQueryHandler> mOnWindowCloseQuery ~ _.Dispose();
public Event<WindowClosedHandler> mOnWindowClosed ~ _.Dispose();
public Event<WindowMovedHandler> mOnWindowMoved ~ _.Dispose();
public Event<MouseWheelHandler> mOnMouseWheel ~ _.Dispose();
public Event<MenuItemSelectedHandler> mOnMenuItemSelected ~ _.Dispose();
public Event<KeyDownHandler> mOnWindowKeyDown ~ _.Dispose();
public Event<Func<int32, int32, HitTestResult>> mOnHitTest ~ _.Dispose();
public static Event<MouseLeftWindowHandler> sOnMouseLeftWindow ~ _.Dispose();
public static Event<WindowLostFocusHandler> sOnWindowLostFocus ~ _.Dispose();
public static Event<MouseEventHandler> sOnMouseDown ~ _.Dispose();
public static Event<WindowClosedHandler> sOnWindowClosed ~ _.Dispose();
public static Event<WindowMovedHandler> sOnWindowMoved ~ _.Dispose();
public static Event<MouseWheelHandler> sOnMouseWheel ~ _.Dispose();
public static Event<MenuItemSelectedHandler> sOnMenuItemSelected ~ _.Dispose();
public static Event<KeyDownHandler> sOnKeyDown ~ _.Dispose();
//public Event<CloseTemporaryHandler> sOnCloseTemporaries ~ _.Dispose();
public bool mIsMainWindow;
public MouseFlag mMouseFlags;
public Widget mRootWidget;
public Widget mCaptureWidget;
public Widget mOverWidget;
public Widget mFocusWidget;
public int32 mClientMouseX;
public int32 mClientMouseY;
public float mMouseX;
public float mMouseY;
public bool mIsMouseMoving;
public bool mHasMouseInside;
public bool mHasProxyMouseInside;
public bool mIsKeyDownHandled;
public int32 mContentClientWidth;
public int32 mContentClientHeight;
public static WidgetWindow sMouseInsideWindow;
public static int32 sLastClosedTick = -1;
public Matrix mScaleMatrix = Matrix.IdentityMatrix;
public Matrix mInvScaleMatrix = Matrix.IdentityMatrix;
public bool[] mIsKeyDown = new bool[(int32)KeyCode.COUNT] ~ delete _;
public this(BFWindow parent, String title, int x, int y, int width, int height, BFWindow.Flags windowFlags, Widget rootWidget) :
//TODO: Do BFWindow init outside constructor.. see where it be hanging
base(parent, title, x, y, width, height, windowFlags)
{
if (mWindowFlags.HasFlag(Flags.ClientSized))
{
mContentClientWidth = (int32)width;
mContentClientHeight = (int32)height;
}
if (!mWindowFlags.HasFlag(BFWindowBase.Flags.NoActivate))
GotFocus();
SetRootWidget(rootWidget);
//Debug.WriteLine("WidgetWindow.this {0} {1} {2}", this, mTitle, mRootWidget);
//Debug.WriteLine("WidgetWindow this");
}
public ~this()
{
if (sMouseInsideWindow == this)
sMouseInsideWindow = null;
delete mRootWidget;
//Debug.WriteLine("WidgetWindow ~this");
}
public void SetRootWidget(Widget rootWidget)
{
if (mRootWidget != null)
mRootWidget.[Friend]RemovedFromWindow();
mRootWidget = rootWidget;
if (mRootWidget != null)
{
mRootWidget.mWidgetWindow = this;
mRootWidget.InitChildren();
mRootWidget.AddedToParent();
Moved(); // Rehup size
}
}
public KeyFlags GetKeyFlags()
{
KeyFlags keyFlags = default;
if (IsKeyDown(KeyCode.Shift))
keyFlags |= KeyFlags.Shift;
if (IsKeyDown(KeyCode.Control))
keyFlags |= KeyFlags.Ctrl;
if (IsKeyDown(KeyCode.Menu))
keyFlags |= KeyFlags.Alt;
return keyFlags;
}
public bool IsKeyDown(KeyCode keyCode)
{
return mIsKeyDown[(int32)keyCode];
}
public override void Draw(Graphics g)
{
if (mRootWidget == null)
return;
base.Draw(g);
mRootWidget.DrawAll(g);
}
public override void Update()
{
if (mOverWidget != null)
Debug.Assert(mHasMouseInside);
if (mRootWidget == null)
return;
base.Update();
RehupMouse(false);
mRootWidget.UpdateAll();
}
public override int32 CloseQuery()
{
bool hadFalse = false;
for (var handler in mOnWindowCloseQuery)
{
if (!handler(this))
{
hadFalse = true;
break;
}
}
if (hadFalse)
return 0;
return base.CloseQuery();
}
public override void Closed()
{
sLastClosedTick = BFApp.sApp.mUpdateCnt;
if (!mHasClosed)
{
//Debug.WriteLine("WidgetWindow.Closed {0} {1} {2}", this, mTitle, mRootWidget);
if (mRootWidget != null)
{
mRootWidget.[Friend]RemovedFromWindow();
mRootWidget.RemovedFromParent(null, this);
}
//Debug.WriteLine("Window closing: {0}", this);
mOnWindowClosed(this);
sOnWindowClosed(this);
base.Closed();
}
}
public override void PreDraw(Graphics g)
{
base.PreDraw(g);
g.mMatrix.Set(mScaleMatrix);
}
public override void Moved()
{
base.Moved();
if (mWindowFlags.HasFlag(Flags.ScaleContent))
{
float scaleX = mClientWidth / (float)mContentClientWidth;
float scaleY = mClientHeight / (float)mContentClientHeight;
if (scaleX > scaleY)
{
float scale = scaleY;
mScaleMatrix.a = scale;
mScaleMatrix.b = 0;
mScaleMatrix.c = 0;
mScaleMatrix.d = scale;
mScaleMatrix.tx = (int32)(mClientWidth - (scale * mContentClientWidth)) / 2;
mScaleMatrix.ty = 0;
}
else
{
float scale = scaleX;
mScaleMatrix.a = scale;
mScaleMatrix.b = 0;
mScaleMatrix.c = 0;
mScaleMatrix.d = scale;
mScaleMatrix.tx = 0;
mScaleMatrix.ty = (int32)(mClientHeight - (scale * mContentClientHeight)) / 2;
}
mInvScaleMatrix.Set(mScaleMatrix);
mInvScaleMatrix.Invert();
mRootWidget.Resize(0, 0, mContentClientWidth, mContentClientHeight);
}
else
{
mInvScaleMatrix = Matrix.IdentityMatrix;
mScaleMatrix = Matrix.IdentityMatrix;
mContentClientWidth = mClientWidth;
mContentClientHeight = mClientHeight;
mRootWidget.Resize(0, 0, mClientWidth, mClientHeight);
}
mOnWindowMoved(this);
sOnWindowMoved(this);
}
public void SetFocus(Widget widget)
{
/*if (widget != null)
Debug.WriteLine("SetFocus: {0} {1}", widget.GetType(), widget.mWidgetWindow.mTitle);*/
if (mFocusWidget == widget)
return;
Widget oldFocusWidget = mFocusWidget;
mFocusWidget = null;
if (oldFocusWidget != null)
{
Debug.Assert(oldFocusWidget.mWidgetWindow == this);
if (oldFocusWidget.mHasFocus)
oldFocusWidget.LostFocus();
else
Debug.Assert(!mHasFocus);
}
if (mFocusWidget != null)
return; // Already got a new focus after LostFocus callbacks
mFocusWidget = widget;
if ((mFocusWidget != null) && (mHasFocus))
mFocusWidget.GotFocus();
}
public override void GotFocus()
{
if (mHasFocus)
return;
mIsDirty = true;
//Debug.WriteLine("GotFocus {0}", mIsMainWindow);
for (var window in sWindowDictionary.Values)
{
// Don't let more than one window think it has the focus
var widgetWindow = window as WidgetWindow;
if ((widgetWindow != null) && (widgetWindow != this) && (widgetWindow.mHasFocus))
{
widgetWindow.LostFocus(this);
break;
}
}
//Debug.WriteLine("GotFocus {0} {1}", mTitle, mIsMainWindow);
base.GotFocus();
if (mFocusWidget != null)
{
//Debug.Assert(!mFocusWidget.mHasFocus);
if (!mFocusWidget.mHasFocus)
mFocusWidget.GotFocus();
}
}
public override void LostFocus(BFWindow newFocus)
{
//Debug.WriteLine("LostFocus {0}", mIsMainWindow);
if (!mHasFocus)
return;
mIsDirty = true;
for (int32 i = 0; i < (int32)mIsKeyDown.Count; i++)
if (mIsKeyDown[i])
KeyUp(i);
// We could have a mousedown in one window while another window has focus still<6C><6C><EFBFBD>
// So this check isn't valid.
/*for (int btnIdx = 0; btnIdx < 3; btnIdx++)
{
if (((int)mMouseFlags & (1 << btnIdx)) != 0)
{
MouseUp(mClientMouseX, mClientMouseY, btnIdx);
}
}*/
base.LostFocus(newFocus);
if (mCaptureWidget != null)
mCaptureWidget = null;
if (mFocusWidget != null)
{
Debug.Assert(mFocusWidget.mWidgetWindow == this);
var prevFocus = mFocusWidget;
prevFocus.LostFocus();
}
mOnWindowLostFocus(this, newFocus);
sOnWindowLostFocus(this, newFocus);
//Debug.Assert(!mHasFocus);
}
public override void KeyChar(char32 c)
{
var fakeFocusWindow = GetFakeFocusWindow();
if (fakeFocusWindow != null)
{
fakeFocusWindow.KeyChar(c);
return;
}
mIsDirty = true;
if ((mHasFocus) && (mFocusWidget != null) && (!mIsKeyDownHandled))
{
let keyEvent = scope KeyCharEvent();
keyEvent.mChar = c;
keyEvent.mSender = mFocusWidget;
mFocusWidget.KeyChar(keyEvent);
}
}
BFWindow GetFakeFocusWindow()
{
/*for (int windowIdx = BFApp.sApp.mWindows.Count - 1; windowIdx >= 0; windowIdx--)
{
var checkWindow = BFApp.sApp.mWindows[windowIdx];
if (checkWindow == this)
return null;
if ((checkWindow.mWindowFlags.HasFlag(.FakeFocus)) && (checkWindow.mHasFocus))
return checkWindow;
}*/
return null;
}
public override bool KeyDown(int32 keyCode, int32 isRepeat)
{
var fakeFocusWindow = GetFakeFocusWindow();
if (fakeFocusWindow != null)
return fakeFocusWindow.KeyDown(keyCode, isRepeat);
mIsDirty = true;
mIsKeyDownHandled = false;
if (keyCode < mIsKeyDown.Count)
mIsKeyDown[keyCode] = true;
KeyDownEvent e = scope KeyDownEvent();
e.mSender = this;
e.mKeyFlags = GetKeyFlags();
e.mKeyCode = (KeyCode)keyCode;
e.mIsRepeat = isRepeat != 0;
if ((sOnKeyDown.HasListeners) || (mOnWindowKeyDown.HasListeners))
{
if (sOnKeyDown.HasListeners)
sOnKeyDown(e);
if (mOnWindowKeyDown.HasListeners)
mOnWindowKeyDown(e);
}
if ((mFocusWidget != null) && (!e.mHandled))
{
e.mSender = mFocusWidget;
mFocusWidget.KeyDown(e);
}
if (e.mHandled)
mIsKeyDownHandled = true;
return e.mHandled;
}
public override void KeyUp(int32 keyCode)
{
mIsDirty = true;
if (keyCode < mIsKeyDown.Count)
mIsKeyDown[keyCode] = false;
if (mFocusWidget != null)
mFocusWidget.KeyUp((KeyCode)keyCode);
var fakeFocusWindow = GetFakeFocusWindow();
if (fakeFocusWindow != null)
fakeFocusWindow.KeyUp(keyCode);
}
public override HitTestResult HitTest(int32 x, int32 y)
{
if (mOnHitTest.HasListeners)
{
var result = mOnHitTest(x, y);
if (result != HitTestResult.NotHandled)
return result;
}
if (mWindowFlags.HasFlag(Flags.Resizable))
{
if ((x - mX > mWindowWidth - 24) && (y - mY > mWindowHeight - 24))
return HitTestResult.BottomRight;
}
return HitTestResult.NotHandled;
}
public void RehupMouse(bool force)
{
if (!mHasMouseInside)
return;
Widget aWidget = mRootWidget.FindWidgetByCoords(mMouseX, mMouseY);
if (mCaptureWidget != null)
{
bool didSomething = false;
if (mCaptureWidget == aWidget)
{
if (!mCaptureWidget.mMouseOver)
{
mCaptureWidget.MouseEnter();
didSomething = true;
}
mOverWidget = aWidget;
}
else
{
if (mCaptureWidget.mMouseOver)
{
mCaptureWidget.MouseLeave();
didSomething = true;
}
mOverWidget = null;
}
if ((didSomething) || (force))
{
float childX;
float childY;
mCaptureWidget.RootToSelfTranslate(mMouseX, mMouseY, out childX, out childY);
mCaptureWidget.MouseMove(childX, childY);
}
}
else
{
if (mOverWidget != aWidget)
{
if (mOverWidget != null)
{
mOverWidget.MouseLeave();
if (!IsMouseCaptured())
{
// Clear out mousedowns if we don't have capture anymore
for (int32 btn < 4)
{
let checkFlag = (MouseFlag)(1 << btn);
if (mMouseFlags.HasFlag(checkFlag))
{
mMouseFlags &= ~checkFlag;
float childX;
float childY;
mOverWidget.RootToSelfTranslate(mMouseX, mMouseY, out childX, out childY);
mOverWidget.MouseUp(childX, childY, btn);
}
}
}
}
mOverWidget = aWidget;
if (aWidget != null)
{
aWidget.MouseEnter();
}
//Debug.WriteLine("Set OverWidget {0} in {1}", mOverWidget, this);
}
else if (!force)
return;
if (aWidget != null)
{
float childX;
float childY;
aWidget.RootToSelfTranslate(mMouseX, mMouseY, out childX, out childY);
aWidget.MouseMove(childX, childY);
}
}
}
void CheckOtherMouseCaptures()
{
if (mCaptureWidget == null)
{
/*foreach (WidgetWindow window in sWindowDictionary.Values)
{
if (window.mCaptureWidget != null)
{
//int a = 0;
}
}*/
}
}
void SetMouseInside()
{
if (sMouseInsideWindow == this)
{
//Debug.WriteLine("Mouse already inside");
return;
}
//Debug.WriteLine("SetMouseInside 1 {0} prev={1}", this, sMouseInsideWindow);
var prevMouseInsideWindow = sMouseInsideWindow;
sMouseInsideWindow = null;
if (prevMouseInsideWindow != null)
prevMouseInsideWindow.MouseLeave();
if (sMouseInsideWindow != null)
{
//Debug.WriteLine("SetMouseInside {0} aborted", this);
return;
}
sMouseInsideWindow = this;
mHasMouseInside = true;
//Debug.WriteLine("SetMouseInside 2 {0}", this);
}
public override void MouseMove(int32 inX, int32 inY)
{
//Debug.WriteLine("MouseMove {0} {1},{2}", this, inX, inY);
mClientMouseX = inX;
mClientMouseY = inY;
float x;
float y;
TranslateMouseCoords(inX, inY, out x, out y);
SetMouseInside();
CheckOtherMouseCaptures();
if ((mMouseX == x) && (mMouseY == y))
{
RehupMouse(false);
return;
}
mIsMouseMoving = true;
mMouseX = x;
mMouseY = y;
RehupMouse(true);
mIsMouseMoving = false;
}
public override void MouseProxyMove(int32 x, int32 y)
{
// This is just from dragging from another so we don't acknowledge the moves directly
//SetMouseInside();
mHasProxyMouseInside = true;
mMouseX = x;
mMouseY = y;
CheckOtherMouseCaptures();
}
void TranslateMouseCoords(int32 x, int32 y, out float outX, out float outY)
{
var pt = mInvScaleMatrix.Multiply(Point(x, y));
outX = pt.x;
outY = pt.y;
}
public override void MouseDown(int32 inX, int32 inY, int32 btn, int32 btnCount)
{
mMouseFlags |= (MouseFlag)(1 << btn);
if ((!mHasFocus) && (mParent == null))
{
//INVESTIGATE: This shouldn't be required normally
// Fixes some focus issues while debugging?
GotFocus();
}
float x;
float y;
TranslateMouseCoords(inX, inY, out x, out y);
MouseMove(inX, inY);
if ((mOnMouseDown.HasListeners) || (sOnMouseDown.HasListeners))
{
MouseEvent anEvent = scope MouseEvent();
anEvent.mSender = this;
anEvent.mX = x;
anEvent.mY = y;
mOnMouseDown(anEvent);
sOnMouseDown(anEvent);
if (anEvent.mHandled)
return;
}
Widget aWidget = mCaptureWidget ?? mOverWidget;
if (aWidget != null)
{
Debug.Assert(aWidget.mWidgetWindow == this);
float childX;
float childY;
aWidget.RootToSelfTranslate(mMouseX, mMouseY, out childX, out childY);
aWidget.MouseDown(childX, childY, btn, btnCount);
}
}
public override void MouseUp(int32 inX, int32 inY, int32 btn)
{
//Debug.WriteLine("MouseUp {0} {1} {2}", mIsMainWindow, this, mMouseFlags);
if (((int32)mMouseFlags & (1 << btn)) == 0)
return;
MouseMove(inX, inY);
mMouseFlags &= (MouseFlag)(~(1 << btn));
Widget aWidget = mCaptureWidget ?? mOverWidget;
if (aWidget != null)
{
float childX;
float childY;
aWidget.RootToSelfTranslate(mMouseX, mMouseY, out childX, out childY);
aWidget.MouseUp(childX, childY, btn);
}
}
public override void MouseWheel(int32 inX, int32 inY, int32 delta)
{
float x;
float y;
TranslateMouseCoords(inX, inY, out x, out y);
MouseMove(inX, inY);
if (sOnMouseWheel.HasListeners)
{
MouseEvent anEvent = scope MouseEvent();
anEvent.mX = x;
anEvent.mY = y;
anEvent.mWheelDelta = delta;
anEvent.mSender = this;
sOnMouseWheel(anEvent);
if (anEvent.mHandled)
return;
}
if (mOnMouseWheel.HasListeners)
{
MouseEvent anEvent = scope MouseEvent();
anEvent.mX = x;
anEvent.mY = y;
anEvent.mWheelDelta = delta;
anEvent.mSender = this;
mOnMouseWheel(anEvent);
if (anEvent.mHandled)
return;
}
Widget aWidget = mOverWidget;
if (aWidget != null)
{
float childX;
float childY;
aWidget.RootToSelfTranslate(mMouseX, mMouseY, out childX, out childY);
aWidget.MouseWheel(childX, childY, delta);
}
}
public override void MouseLeave()
{
/*if (mTitle == "Tooltip")
{
NOP!();
}
Debug.WriteLine("MouseLeave {0}", this);*/
// This line breaks the ability to drag a tab outside the window. Why did we have it?
/*if (sMouseInsideWindow == this)
return;*/
//Debug.WriteLine("MouseLeave {0}", this);
if (sMouseInsideWindow == this)
{
sMouseInsideWindow = null;
}
mHasProxyMouseInside = false;
mHasMouseInside = false;
if (mOverWidget != null)
{
mOverWidget.MouseLeave();
mOverWidget = null;
}
mOnMouseLeftWindow(this);
sOnMouseLeftWindow(this);
}
public override void MenuItemSelected(SysMenu sysMenu)
{
mOnMenuItemSelected(sysMenu);
sOnMenuItemSelected(sysMenu);
base.MenuItemSelected(sysMenu);
}
public void TransferMouse(WidgetWindow newMouseWindow)
{
if (mWindowFlags.HasFlag(.FakeFocus))
return;
if (mMouseFlags == default)
return;
newMouseWindow.mMouseFlags = mMouseFlags;
newMouseWindow.CaptureMouse();
mMouseFlags = default;
}
}
}