1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-16 23:34:10 +02:00
Beef/IDE/src/ui/ThreadPanel.bf

542 lines
15 KiB
Beef

using System;
using System.Collections;
using System.Text;
using System.Threading.Tasks;
using Beefy;
using Beefy.widgets;
using Beefy.theme;
using Beefy.gfx;
using Beefy.theme.dark;
using Beefy.events;
using Beefy.utils;
using IDE.Debugger;
namespace IDE.ui
{
public class ThreadListViewItem : DarkListViewItem
{
public int32 mThreadId;
public bool mFrozen;
override public bool Selected
{
set
{
if (mIsSelected != value)
{
var threadListView = (ThreadListView)mListView;
if (threadListView.mThreadPanel.mCallstackPopup != null)
threadListView.mThreadPanel.mCallstackPopup.Close();
}
base.Selected = value;
}
}
public override void Draw(Graphics g)
{
if (mColumnIdx == 2)
{
float width = mListView.mParent.mWidth - LabelX;
//
g.SetFont(DarkTheme.sDarkTheme.mSmallFont);
g.DrawString(mLabel, GS!(6), GS!(0), .Left, width - GS!(42), .Ellipsis);
g.Draw(DarkTheme.sDarkTheme.GetImage(.ComboEnd), width - GS!(36), (mHeight - GS!(24)) / 2);
return;
}
base.Draw(g);
//var threadPanel = ((ThreadListView)mListView).mThreadPanel;
if (mColumnIdx == 0)
{
if (mFrozen)
g.Draw(DarkTheme.sDarkTheme.GetImage(.IconError), 16, 0);
}
}
public override void DrawAll(Graphics g)
{
base.DrawAll(g);
}
public void PopupCallStackPanel()
{
var baseItem = (ThreadListViewItem)GetSubItem(0);
mListView.GetRoot().SelectItemExclusively(baseItem);
int32 threadId = baseItem.mThreadId;
var threadListView = (ThreadListView)mListView;
if (threadListView.mThreadPanel.mThreadCallstackJustClosed == threadId)
return;
for (var window in gApp.mWindows)
{
if (var widgetWindow = window as WidgetWindow)
{
if (var panelPopup = widgetWindow.mRootWidget as PanelPopup)
{
if (var callstackPanel = panelPopup.mPanel as CallStackPanel)
{
if (callstackPanel.mThreadId == threadId)
{
panelPopup.Close();
return;
}
}
}
}
}
CallStackPanel callStackPanel = new CallStackPanel();
callStackPanel.mThreadId = threadId;
float width = mListView.mParent.mWidth - LabelX;
PanelPopup panelPopup = new PanelPopup();
threadListView.mThreadPanel.mCallstackPopup = panelPopup;
panelPopup.mWindowFlags = .ClientSized | .DestAlpha | .Resizable;
panelPopup.Init(callStackPanel, this, GS!(12), mHeight, width + GS!(24), GS!(192));
panelPopup.mOnRemovedFromParent.Add(new (widget, prevParent, widgetWindow) =>
{
threadListView.mThreadPanel.mCallstackPopup = null;
threadListView.SetFocus();
});
callStackPanel.mListView.mOnKeyDown.Add(new (keyEvent) =>
{
if (keyEvent.mKeyCode == .Left)
panelPopup.Close();
});
callStackPanel.mWidgetWindow.SetForeground();
callStackPanel.mListView.SetFocus();
callStackPanel.Update();
var root = callStackPanel.mListView.GetRoot();
if (root.GetChildCount() > 0)
root.GetChildAtIndex(0).Focused = true;
panelPopup.mOnRemovedFromParent.Add(new (widget, prevParent, widgetWindow) =>
{
threadListView.mThreadPanel.mThreadCallstackJustClosed = threadId;
});
}
public override void MouseDown(float x, float y, int32 btn, int32 btnCount)
{
base.MouseDown(x, y, btn, btnCount);
if ((x >= mListView.mParent.mWidth - LabelX - GS!(34)) && (mParentItem != null))
{
PopupCallStackPanel();
}
}
public override bool WantsTooltip(float mouseX, float mouseY)
{
if ((mColumnIdx == 2) && (mouseX >= mListView.mParent.mWidth - LabelX - GS!(34)))
return false;
return base.WantsTooltip(mouseX, mouseY);
}
}
public class ThreadListView : DarkListView
{
public ThreadPanel mThreadPanel;
protected override ListViewItem CreateListViewItem()
{
return new ThreadListViewItem();
}
public override void KeyDown(KeyCode keyCode, bool isRepeat)
{
base.KeyDown(keyCode, isRepeat);
switch (keyCode)
{
case .Right:
let focusedItem = GetRoot().FindFocusedItem();
if (focusedItem != null)
{
let callstackItem = (ThreadListViewItem)focusedItem.GetSubItem(2);
callstackItem.PopupCallStackPanel();
}
case .Apps:
mThreadPanel.[Friend]ShowRightClickMenu(this);
default:
}
}
}
public class ThreadPanel : Panel
{
public ThreadListView mListView;
public bool mThreadsDirty = true;
public int32 mDisabledTicks;
public bool mWantsKeyboardFocus;
public PanelPopup mCallstackPopup;
public bool mDeselectOnFocusLost = true;
public int mThreadCallstackJustClosed = -1;
public this()
{
mListView = new ThreadListView();
mListView.mShowGridLines = true;
mListView.mThreadPanel = this;
mListView.InitScrollbars(true, true);
mListView.mHorzScrollbar.mPageSize = GS!(100);
mListView.mHorzScrollbar.mContentSize = GS!(500);
mListView.mVertScrollbar.mPageSize = GS!(100);
mListView.mVertScrollbar.mContentSize = GS!(500);
mListView.mAutoFocus = true;
mListView.mOnLostFocus.Add(new (evt) =>
{
if ((mCallstackPopup == null) && (mDeselectOnFocusLost))
mListView.GetRoot().SelectItemExclusively(null);
});
mListView.UpdateScrollbars();
//mListView.mDragEndHandler += HandleDragEnd;
//mListView.mDragUpdateHandler += HandleDragUpdate;
AddWidget(mListView);
//mListView.mMouseDownHandler += ListViewMouseDown;
ListViewColumn column = mListView.AddColumn(100, "Id");
column.mMinWidth = GS!(100);
column = mListView.AddColumn(GS!(200), "Name");
column.mMinWidth = GS!(100);
column = mListView.AddColumn(GS!(200), "Location");
column.mMinWidth = GS!(100);
//RebuildUI();
SetScaleData();
}
void SetScaleData()
{
mListView.mIconX = GS!(0);
mListView.mOpenButtonX = GS!(4);
mListView.mLabelX = GS!(32);
mListView.mChildIndent = GS!(16);
mListView.mHiliteOffset = GS!(-2);
}
public override void RemovedFromParent(Widget previousParent, WidgetWindow window)
{
if (mCallstackPopup != null)
mCallstackPopup.Close();
base.RemovedFromParent(previousParent, window);
}
public override void RehupScale(float oldScale, float newScale)
{
base.RehupScale(oldScale, newScale);
SetScaleData();
}
public override void Serialize(StructuredData data)
{
base.Serialize(data);
data.Add("Type", "ThreadPanel");
}
public override bool Deserialize(StructuredData data)
{
return base.Deserialize(data);
}
protected override void ShowRightClickMenu(Widget relWidget, float x, float y)
{
mDeselectOnFocusLost = false;
defer:: { mDeselectOnFocusLost = true; }
if (mListView.GetRoot().FindFirstSelectedItem() == null)
return;
Menu menu = new Menu();
bool hasFrozen = false;
bool hasThawed = false;
mListView.GetRoot().WithSelectedItems(scope [&] (item) =>
{
var threadListViewItem = (ThreadListViewItem)item;
if (threadListViewItem.mFrozen)
hasFrozen = true;
else
hasThawed = true;
});
Menu item;
if (hasThawed)
{
item = menu.AddItem("Freeze");
item.mOnMenuItemSelected.Add(new (menu) =>
{
if (!gApp.mDebugger.IsPaused())
return;
mListView.GetRoot().WithSelectedItems(scope (item) =>
{
var threadItem = (ThreadListViewItem)item;
gApp.mDebugger.FreezeThread(threadItem.mThreadId);
mThreadsDirty = true;
});
});
}
item = menu.AddItem("Freeze All Except");
item.mOnMenuItemSelected.Add(new (menu) =>
{
if (!gApp.mDebugger.IsPaused())
return;
mListView.GetRoot().WithItems(scope (item) =>
{
var threadItem = (ThreadListViewItem)item;
if (item.Selected)
gApp.mDebugger.ThawThread(threadItem.mThreadId);
else
gApp.mDebugger.FreezeThread(threadItem.mThreadId);
mThreadsDirty = true;
});
});
if (hasFrozen)
{
item = menu.AddItem("Thaw");
item.mOnMenuItemSelected.Add(new (menu) =>
{
if (!gApp.mDebugger.IsPaused())
return;
mListView.GetRoot().WithSelectedItems(scope (item) =>
{
var threadItem = (ThreadListViewItem)item;
gApp.mDebugger.ThawThread(threadItem.mThreadId);
mThreadsDirty = true;
});
});
}
MenuWidget menuWidget = ThemeFactory.mDefault.CreateMenuWidget(menu);
menuWidget.Init(mListView.GetRoot(), x, y);
}
void ValueClicked(MouseEvent evt)
{
DarkListViewItem clickedItem = (DarkListViewItem)evt.mSender;
DarkListViewItem item = (DarkListViewItem)clickedItem.GetSubItem(0);
if ((clickedItem.mColumnIdx == 2) && (evt.mX > clickedItem.mWidth - GS!(22)))
{
return;
}
mListView.SetFocus();
if (evt.mBtn == 1)
{
if (!item.Selected)
mListView.GetRoot().SelectItem(item, true);
var widget = (Widget)evt.mSender;
widget.SelfToOtherTranslate(mListView.GetRoot(), evt.mX, evt.mY, var x, var y);
ShowRightClickMenu(mListView, x, y);
}
else if (evt.mBtn == 0)
{
mListView.GetRoot().SelectItem(item, true);
}
if ((evt.mBtn == 0) && (evt.mBtnCount > 1))
{
if (!gApp.mExecutionPaused)
return;
for (int32 childIdx = 0; childIdx < mListView.GetRoot().GetChildCount(); childIdx++)
{
var checkListViewItem = mListView.GetRoot().GetChildAtIndex(childIdx);
checkListViewItem.IconImage = null;
}
#unwarn
int selectedIdx = mListView.GetRoot().GetIndexOfChild(item);
int32 threadId = int32.Parse(item.mLabel);
gApp.mDebugger.SetActiveThread(threadId);
gApp.mDebugger.mCallStackDirty = true;
gApp.mCallStackPanel.MarkCallStackDirty();
gApp.mCallStackPanel.Update();
gApp.mWatchPanel.MarkWatchesDirty(true);
gApp.mAutoWatchPanel.MarkWatchesDirty(true);
gApp.mMemoryPanel.MarkViewDirty();
gApp.ShowPCLocation(gApp.mDebugger.mActiveCallStackIdx, false, true);
item.IconImage = DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.ArrowRight);
}
}
public override void Resize(float x, float y, float width, float height)
{
base.Resize(x, y, width, height);
mListView.Resize(0, 0, width, height);
}
public void MarkThreadsDirty()
{
mThreadsDirty = true;
}
public override void FocusForKeyboard()
{
mListView.SetFocus();
mWantsKeyboardFocus = true;
}
void UpdateThreads()
{
if ((!gApp.mExecutionPaused) || (!mThreadsDirty))
return;
HashSet<int> selectedIds = scope .();
int focusedId = -1;
mListView.GetRoot().WithItems(scope [&] (item) =>
{
var threadListViewItem = (ThreadListViewItem)item;
if (item.Selected)
selectedIds.Add(threadListViewItem.mThreadId);
if (item.Focused)
focusedId = threadListViewItem.mThreadId;
});
mListView.GetRoot().Clear();
String threadInfo = scope String();
gApp.mDebugger.GetThreadInfo(threadInfo);
var threadInfos = scope List<StringView>(threadInfo.Split('\n'));
if (threadInfos.Count < 2)
return;
int32 currentThreadId = int32.Parse(scope String(threadInfos[0]));
String label = scope String(256);
for (int32 threadIdx = 1; threadIdx < threadInfos.Count; threadIdx++)
{
var elementData = scope List<StringView>(scope String(threadInfos[threadIdx]).Split('\t'));
if (elementData.Count == 1)
break;
var listViewItem = (ThreadListViewItem)mListView.GetRoot().CreateChildItem();
listViewItem.mOnMouseDown.Add(new => ValueClicked);
listViewItem.Label = scope String(elementData[0]);
int32 threadId = int32.Parse(scope String(elementData[0]));
if (threadId == currentThreadId)
listViewItem.IconImage = DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.ArrowRight);
listViewItem.mThreadId = threadId;
if (threadId == focusedId)
listViewItem.Focused = true;
if (selectedIds.Contains(threadId))
listViewItem.Selected = true;
var subItem = (ThreadListViewItem)listViewItem.CreateSubItem(1);
subItem.Label = scope String(elementData[1]);
subItem.mOnMouseDown.Add(new => ValueClicked);
subItem = (ThreadListViewItem)listViewItem.CreateSubItem(2);
label.Clear();
label.Append(elementData[2]);
IDEUtils.ColorizeCodeString(label, .Callstack);
subItem.Label = label;
subItem.mOnMouseDown.Add(new => ValueClicked);
if (elementData.Count > 3)
{
listViewItem.mFrozen = elementData[3].Contains("Fr");
}
}
mThreadsDirty = false;
}
public override void Update()
{
base.Update();
mThreadCallstackJustClosed = -1;
UpdateThreads();
bool disabled = !gApp.mExecutionPaused;
if (!disabled)
{
mDisabledTicks = 0;
}
else
{
mDisabledTicks++;
if ((mDisabledTicks > 40) && (!gApp.mDebuggerPerformingTask))
{
var root = mListView.GetRoot();
if (root.GetChildCount() != 0)
{
mListView.GetRoot().Clear();
MarkDirty();
}
}
}
if (!mListView.mHasFocus)
mWantsKeyboardFocus = false;
if (mWantsKeyboardFocus)
{
for (int32 i = 0; i < mListView.GetRoot().GetChildCount(); i++)
{
var listViewItem = (DarkListViewItem)mListView.GetRoot().GetChildAtIndex(i);
if (listViewItem.mIconImage != null)
{
mListView.GetRoot().SelectItem(listViewItem);
mListView.EnsureItemVisible(listViewItem, true);
mWantsKeyboardFocus = false;
break;
}
}
/*if ((wantSelectIdx != -1) && (wantSelectIdx < mListView.GetRoot().GetChildCount()))
mListView.GetRoot().SelectItem(mListView.GetRoot().GetChildAtIndex(wantSelectIdx));*/
}
}
public override void DrawAll(Graphics g)
{
base.DrawAll(g);
}
public override bool HasAffinity(Widget otherPanel)
{
return base.HasAffinity(otherPanel) || (otherPanel is CallStackPanel);
}
public void SelectThreadId(int threadId)
{
UpdateThreads();
for (int threadIdx < mListView.GetRoot().GetChildCount())
{
var lvItem = (ThreadListViewItem)mListView.GetRoot().GetChildAtIndex(threadIdx);
if (lvItem.mThreadId == threadId)
{
mListView.GetRoot().SelectItemExclusively(lvItem);
mListView.EnsureItemVisible(lvItem, true);
break;
}
}
}
}
}