diff --git a/BeefLibs/Beefy2D/src/theme/dark/DarkTabbedView.bf b/BeefLibs/Beefy2D/src/theme/dark/DarkTabbedView.bf index 5f1dd96e..73c5a999 100644 --- a/BeefLibs/Beefy2D/src/theme/dark/DarkTabbedView.bf +++ b/BeefLibs/Beefy2D/src/theme/dark/DarkTabbedView.bf @@ -27,7 +27,13 @@ namespace Beefy.theme.dark 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)); + { + var tabButton = mParent as TabButton; + if (tabButton.mIsPinned == true) + g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.PinnedTab), GS!(-5), GS!(-5)); + else + g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.Close), GS!(-4), GS!(-4)); + } } public override void MouseClicked(float x, float y, float origX, float origY, int32 btn) @@ -260,19 +266,25 @@ namespace Beefy.theme.dark menuItem = menu.AddItem("Close"); menuItem.mOnMenuItemSelected.Add(new (evt) => { - mTabbedView.CloseTabs(true, true); + mTabbedView.CloseTabs(true, true, true); }); menuItem = menu.AddItem("Close Tabs"); menuItem.mOnMenuItemSelected.Add(new (evt) => { - mTabbedView.CloseTabs(false, true); + mTabbedView.CloseTabs(false, true, true); }); menuItem = menu.AddItem("Close Tabs Except Current"); menuItem.mOnMenuItemSelected.Add(new (evt) => { - mTabbedView.CloseTabs(false, false); + mTabbedView.CloseTabs(false, false, true); + }); + + menuItem = menu.AddItem("Close All Except Pinned"); + menuItem.mOnMenuItemSelected.Add(new (menu) => + { + mTabbedView.CloseTabs(false, true, false); }); menu.AddItem(); @@ -280,6 +292,9 @@ namespace Beefy.theme.dark for (var tab in mTabbedView.mTabs) { menuItem = menu.AddItem(tab.mLabel); + if (tab.mIsPinned) + menuItem.mIconImage = DarkTheme.sDarkTheme.GetImage(.PinnedTab); + menuItem.mOnMenuItemSelected.Add(new (selMenuItem) => { TabbedView.TabButton activateTab = tab; diff --git a/BeefLibs/Beefy2D/src/theme/dark/DarkTheme.bf b/BeefLibs/Beefy2D/src/theme/dark/DarkTheme.bf index 3c41d7f0..eb56ec8e 100644 --- a/BeefLibs/Beefy2D/src/theme/dark/DarkTheme.bf +++ b/BeefLibs/Beefy2D/src/theme/dark/DarkTheme.bf @@ -191,6 +191,8 @@ namespace Beefy.theme.dark PrevBookmarkInFolder, NextBookmarkInFolder, + PinnedTab, + COUNT }; diff --git a/BeefLibs/Beefy2D/src/widgets/TabbedView.bf b/BeefLibs/Beefy2D/src/widgets/TabbedView.bf index 1b7de3be..77bcc848 100644 --- a/BeefLibs/Beefy2D/src/widgets/TabbedView.bf +++ b/BeefLibs/Beefy2D/src/widgets/TabbedView.bf @@ -12,6 +12,7 @@ namespace Beefy.widgets public class TabButton : Widget, IDockable, IDragInterface { public bool mIsActive; + public bool mIsPinned; public String mLabel ~ delete _; public TabbedView mTabbedView; public WidgetWindow mNewDraggingWindow; @@ -159,6 +160,7 @@ namespace Beefy.widgets public void DragEnd() { //mWidgetWindow.mMouseLeftWindowDelegate.Remove(scope => MouseLeftWindow, true); + AdjustPinnedState(); if ((mSrcDraggingWindow != null) && (mSrcDraggingWindow.mCaptureWidget != null)) mSrcDraggingWindow.ReleaseMouseCaptures(); @@ -175,6 +177,25 @@ namespace Beefy.widgets mSrcDraggingWindow = null; } + public void AdjustPinnedState() + { + // Adjusts Tab.mIsPinned state based on neighbouring tabs + bool prevIsPinned = true; + bool nextIsPinned = false; + + int position = mTabbedView.mTabs.IndexOf(this); + + if (position - 1 >= 0) + prevIsPinned = mTabbedView.mTabs[position - 1].mIsPinned; + + if (position + 1 < mTabbedView.mTabs.Count) + nextIsPinned = mTabbedView.mTabs[position + 1].mIsPinned; + + mIsPinned = (prevIsPinned == nextIsPinned) + ? prevIsPinned + : mIsPinned; + } + public void MouseDrag(float x, float y, float dX, float dY) { mTabbedView.mParentDockingFrame?.GetRootDockingFrame().ShowDragTarget(this); @@ -309,6 +330,7 @@ namespace Beefy.widgets mTabbedView.RemoveTab(this, false); tabbedView.AddTab(this, tabbedView.GetInsertPositionFromCursor()); Activate(); + AdjustPinnedState(); } } else @@ -391,7 +413,7 @@ namespace Beefy.widgets return ThemeFactory.mDefault.CreateTabbedView(sharedData); } - public void CloseTabs(bool autoClose, bool closeCurrent) + public void CloseTabs(bool autoClose, bool closeCurrent, bool closePinned) { let prevAutoClose = mAutoClose; mAutoClose = autoClose; @@ -414,8 +436,14 @@ namespace Beefy.widgets { for (var tab in tabs) { + // Close all except active tab if ((!closeCurrent) && (tab.mIsActive)) continue; + + // Close all except pinned tabs + if (closePinned == false && tab.mIsPinned == true) + continue; + tab.mCloseClickedEvent(); } } @@ -570,6 +598,7 @@ namespace Beefy.widgets RemoveTab(tab, false); tabbedView.AddTab(tab, tabbedView.GetInsertPositionFromCursor()); tab.Activate(); + tab.AdjustPinnedState(); } mParentDockingFrame.RemoveDockedWidget(this); @@ -603,5 +632,32 @@ namespace Beefy.widgets if (tab != null) tab.ResizeContent(); } + + public void TogglePinned(TabButton tabButton) + { + // Toggles 'mIsPinned' of the tab button + // and adjusts its position in TabbedView. + + tabButton.mIsPinned = !tabButton.mIsPinned; + + // Remove target tabButton from the tabs + mTabs.Remove(tabButton); + + // Find index of right-most non-pinned tab + int index = 0; + for (index = 0; index < mTabs.Count; index++) + { + if (mTabs[index].mIsPinned == false) + break; + } + + // Re-insert target tab button + if (index == 0) + mTabs.AddFront(tabButton); + else + mTabs.Insert(index, tabButton); + + RehupSize(); + } } } diff --git a/IDE/dist/images/DarkUI.png b/IDE/dist/images/DarkUI.png index 25e31e85..67a00436 100644 Binary files a/IDE/dist/images/DarkUI.png and b/IDE/dist/images/DarkUI.png differ diff --git a/IDE/dist/images/DarkUI.psd b/IDE/dist/images/DarkUI.psd index aeeed60a..c4eb0eb2 100644 Binary files a/IDE/dist/images/DarkUI.psd and b/IDE/dist/images/DarkUI.psd differ diff --git a/IDE/dist/images/DarkUI_2.png b/IDE/dist/images/DarkUI_2.png index c30b7fd5..e8309267 100644 Binary files a/IDE/dist/images/DarkUI_2.png and b/IDE/dist/images/DarkUI_2.png differ diff --git a/IDE/dist/images/DarkUI_4.png b/IDE/dist/images/DarkUI_4.png index dc95f782..8d737910 100644 Binary files a/IDE/dist/images/DarkUI_4.png and b/IDE/dist/images/DarkUI_4.png differ diff --git a/IDE/src/IDEApp.bf b/IDE/src/IDEApp.bf index 1b18da48..aa91bbd4 100644 --- a/IDE/src/IDEApp.bf +++ b/IDE/src/IDEApp.bf @@ -1295,7 +1295,7 @@ namespace IDE void CloseTabs() { WithDocumentTabbedViewsOf(window, scope (tabbedView) => { - tabbedView.CloseTabs(false, true); + tabbedView.CloseTabs(false, true, true); }); } @@ -1750,6 +1750,7 @@ namespace IDE data.Add("TabLabel", tabWidget.mLabel); data.Add("TabWidth", tabWidget.mWantWidth / DarkTheme.sScale); + data.Add("TabIsPinned", tabWidget.mIsPinned); if (var watchPanel = tabWidget.mContent as WatchPanel) { @@ -3186,6 +3187,7 @@ namespace IDE var newTabButton = new SourceViewTabButton(); newTabButton.Label = ""; data.GetString("TabLabel", newTabButton.mLabel); + newTabButton.mIsPinned = data.GetBool("TabIsPinned"); newTabButton.mOwnsContent = panel.mAutoDelete; newTabButton.mTabWidthOffset = panel.TabWidthOffset; //newTabButton.mWantWidth = (float)Math.Round(data.GetFloat("TabWidth") * DarkTheme.sScale); @@ -6251,9 +6253,22 @@ namespace IDE int GetTabInsertIndex(TabbedView tabs) { if (mSettings.mUISettings.mInsertNewTabs == .RightOfExistingTabs) + { return tabs.mTabs.Count; - else - return 0; + } + + // Find right-most non-pinned tab + // after which we will put our new tab + int index = 0; + for (index = 0; index < tabs.mTabs.Count; index++) + { + if (tabs.mTabs[index].mIsPinned == false) + { + break; + } + } + + return index; } public class SourceViewTabButton : DarkTabbedView.DarkTabButton @@ -6347,7 +6362,16 @@ namespace IDE Menu menu = new Menu(); if (var sourceViewPanel = mContent as SourceViewPanel) { - var item = menu.AddItem("Copy Full Path"); + var item = menu.AddItem(this.mIsPinned ? "Unpin this tab" : "Pin this tab"); + item.mOnMenuItemSelected.Add(new (menu) => + { + if (mIsRightTab) + IDEApp.sApp.MakeTabPermanent(this); + + mTabbedView.TogglePinned(this); + }); + + item = menu.AddItem("Copy Full Path"); item.mOnMenuItemSelected.Add(new (menu) => { gApp.SetClipboardText(sourceViewPanel.mFilePath); @@ -6378,7 +6402,12 @@ namespace IDE item = menu.AddItem("Close All Except This"); item.mOnMenuItemSelected.Add(new (menu) => { - mTabbedView.CloseTabs(false, false); + mTabbedView.CloseTabs(false, false, true); + }); + item = menu.AddItem("Close All Except Pinned"); + item.mOnMenuItemSelected.Add(new (menu) => + { + mTabbedView.CloseTabs(false, true, false); }); }