mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-10 12:32:20 +02:00
Initial checkin
This commit is contained in:
parent
c74712dad9
commit
078564ac9e
3242 changed files with 1616395 additions and 0 deletions
576
BeefLibs/Beefy2D/src/widgets/Menu.bf
Normal file
576
BeefLibs/Beefy2D/src/widgets/Menu.bf
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue