1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 03:28:20 +02:00
Beef/BeefLibs/Beefy2D/src/widgets/WidgetWindow.bf

773 lines
22 KiB
Beef
Raw Normal View History

2019-08-23 11:56:54 -07:00
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();
RehupSize();
2019-08-23 11:56:54 -07:00
}
}
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);
g.mMatrixStack[g.mMatrixStackIdx] = g.mMatrix;
2019-08-23 11:56:54 -07:00
}
public override void RehupSize()
{
base.RehupSize();
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);
}
}
2019-08-23 11:56:54 -07:00
public override void Moved()
{
base.Moved();
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)
{
2019-09-10 11:25:53 -07:00
if ((result == .Transparent) || (result == .Caption))
{
if (mHasMouseInside)
MouseLeave();
}
2019-08-23 11:56:54 -07:00
return result;
}
2019-08-23 11:56:54 -07:00
}
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);
2019-08-23 11:56:54 -07:00
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 (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;
}
public void SetContentSize(int width, int height)
{
mContentClientWidth = (.)width;
mContentClientHeight = (.)height;
RehupSize();
}
2019-08-23 11:56:54 -07:00
}
}