diff --git a/BeefLibs/Beefy2D/src/theme/dark/DarkTheme.bf b/BeefLibs/Beefy2D/src/theme/dark/DarkTheme.bf index 2ff33e08..c0e2bf7e 100644 --- a/BeefLibs/Beefy2D/src/theme/dark/DarkTheme.bf +++ b/BeefLibs/Beefy2D/src/theme/dark/DarkTheme.bf @@ -184,6 +184,8 @@ namespace Beefy.theme.dark CollapseClosed, CollapseOpened, + IconBookmarkDisabled, + COUNT }; diff --git a/IDE/dist/images/DarkUI.png b/IDE/dist/images/DarkUI.png index 151d4f4b..320ba4dd 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 fc54eba2..d9519b98 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 99dff161..f4335bd1 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 0e970d43..d0666830 100644 Binary files a/IDE/dist/images/DarkUI_4.png and b/IDE/dist/images/DarkUI_4.png differ diff --git a/IDE/dist/images/DarkUI_4.psd b/IDE/dist/images/DarkUI_4.psd index 7f642106..0e48c48b 100644 Binary files a/IDE/dist/images/DarkUI_4.psd and b/IDE/dist/images/DarkUI_4.psd differ diff --git a/IDE/src/BookmarkManager.bf b/IDE/src/BookmarkManager.bf index c921fde1..83ddafc9 100644 --- a/IDE/src/BookmarkManager.bf +++ b/IDE/src/BookmarkManager.bf @@ -48,8 +48,10 @@ namespace IDE } public class Bookmark : TrackedTextElement - { + { + public String mTitle ~ delete _; public String mNotes ~ delete _; + public bool mIsDisabled; } public class BookmarkManager @@ -61,18 +63,47 @@ namespace IDE delete _; }; public int32 mBookmarkIdx; + + private int32 _createdBookmarks; - public Bookmark CreateBookmark(String fileName, int wantLineNum, int wantColumn) - { + public bool AllBookmarksDisabled + { + get + { + for (Bookmark b in mBookmarkList) + { + if (!b.mIsDisabled) + return false; + } + + return true; + } + } + + public Bookmark CreateBookmark(String fileName, int wantLineNum, int wantColumn, bool isDisabled = false, String title = null) + { mBookmarkIdx = (int32)mBookmarkList.Count; + _createdBookmarks++; Bookmark bookmark = new Bookmark(); bookmark.mFileName = new String(fileName); bookmark.mLineNum = (int32)wantLineNum; bookmark.mColumn = (int32)wantColumn; + + if (title == null) + bookmark.mTitle = new $"Bookmark {_createdBookmarks++}"; + else + bookmark.mTitle = new String(title); + + bookmark.mIsDisabled = isDisabled; + mBookmarkList.Add(bookmark); gApp.mDebugger.mBreakpointsChangedDelegate(); + + //gApp.mBookmarksPanel.UpdateBookmarks(); + gApp.mBookmarksPanel.mBookmarksDirty = true; + return bookmark; } @@ -86,6 +117,9 @@ namespace IDE mBookmarkIdx = (int32)mBookmarkList.Count - 1; gApp.mDebugger.mBreakpointsChangedDelegate(); bookmark.Kill(); + + //gApp.mBookmarksPanel.UpdateBookmarks(); + gApp.mBookmarksPanel.mBookmarksDirty = true; } public void Clear() @@ -95,6 +129,9 @@ namespace IDE mBookmarkList.Clear(); mBookmarkIdx = 0; gApp.mDebugger.mBreakpointsChangedDelegate(); + + //gApp.mBookmarksPanel.UpdateBookmarks(); + gApp.mBookmarksPanel.mBookmarksDirty = true; } public void PrevBookmark() @@ -102,12 +139,22 @@ namespace IDE if (mBookmarkList.Count == 0) return; - mBookmarkIdx--; - if (mBookmarkIdx < 0) - mBookmarkIdx = (int32)mBookmarkList.Count - 1; + int32 currentIdx = mBookmarkIdx; - var bookmark = mBookmarkList[mBookmarkIdx]; - gApp.ShowSourceFileLocation(bookmark.mFileName, -1, -1, bookmark.mLineNum, bookmark.mColumn, LocatorType.Smart); + Bookmark prevBookmark; + + repeat + { + mBookmarkIdx++; + if (mBookmarkIdx >= mBookmarkList.Count) + mBookmarkIdx = 0; + + prevBookmark = mBookmarkList[mBookmarkIdx]; + } + // skip disabled bookmarks, stop when we reach starting point + while (prevBookmark.mIsDisabled && (currentIdx != mBookmarkIdx)); + + GotoBookmark(prevBookmark); } public void NextBookmark() @@ -115,12 +162,27 @@ namespace IDE if (mBookmarkList.Count == 0) return; - mBookmarkIdx++; - if (mBookmarkIdx >= mBookmarkList.Count) - mBookmarkIdx = 0; + int32 currentIdx = mBookmarkIdx; - var bookmark = mBookmarkList[mBookmarkIdx]; - gApp.ShowSourceFileLocation(bookmark.mFileName, -1, -1, bookmark.mLineNum, bookmark.mColumn, LocatorType.Smart); + Bookmark nextBookmark; + + repeat + { + mBookmarkIdx--; + if (mBookmarkIdx < 0) + mBookmarkIdx = (int32)mBookmarkList.Count - 1; + + nextBookmark = mBookmarkList[mBookmarkIdx]; + } + // skip disabled bookmarks, stop when we reach starting point + while (nextBookmark.mIsDisabled && (currentIdx != mBookmarkIdx)); + + GotoBookmark(nextBookmark); } + + public void GotoBookmark(Bookmark bookmark) + { + gApp.ShowSourceFileLocation(bookmark.mFileName, -1, -1, bookmark.mLineNum, bookmark.mColumn, LocatorType.Smart); + } } } diff --git a/IDE/src/Commands.bf b/IDE/src/Commands.bf index 0b3ba082..57e1ff8c 100644 --- a/IDE/src/Commands.bf +++ b/IDE/src/Commands.bf @@ -294,6 +294,7 @@ namespace IDE Add("Settings", new => gApp.ShowSettings); Add("Show Auto Watches", new => gApp.ShowAutoWatches); Add("Show Autocomplete Panel", new => gApp.ShowAutoCompletePanel); + Add("Show Bookmarks", new => gApp.ShowBookmarks); Add("Show Breakpoints", new => gApp.ShowBreakpoints); Add("Show Call Stack", new => gApp.ShowCallstack); Add("Show Class View", new => gApp.ShowClassViewPanel); diff --git a/IDE/src/IDEApp.bf b/IDE/src/IDEApp.bf index b9ffe346..3073285a 100644 --- a/IDE/src/IDEApp.bf +++ b/IDE/src/IDEApp.bf @@ -213,6 +213,7 @@ namespace IDE public Widget mLastActivePanel; public SourceViewPanel mLastActiveSourceViewPanel; public AutoCompletePanel mAutoCompletePanel; + public BookmarksPanel mBookmarksPanel; public Rect mRequestedWindowRect = Rect(64, 64, 1200, 1024); public BFWindow.ShowKind mRequestedShowKind; @@ -703,6 +704,7 @@ namespace IDE RemoveAndDelete!(mProfilePanel); RemoveAndDelete!(mPropertiesPanel); RemoveAndDelete!(mAutoCompletePanel); + RemoveAndDelete!(mBookmarksPanel); if (mSymbolReferenceHelper != null) mSymbolReferenceHelper.Close(); @@ -1908,6 +1910,9 @@ namespace IDE sd.Add("File", relPath); sd.Add("Line", bookmark.mLineNum); sd.Add("Column", bookmark.mColumn); + sd.Add("Title", bookmark.mTitle); + if (bookmark.mIsDisabled) + sd.Add("Disabled", true); } } } @@ -2404,6 +2409,8 @@ namespace IDE mErrorsPanel.Clear(); + mBookmarksPanel.Clear(); + OutputLine("Workspace closed."); } @@ -3311,7 +3318,12 @@ namespace IDE mWorkspace.GetWorkspaceAbsPath(relPath, absPath); int32 lineNum = data.GetInt("Line"); int32 column = data.GetInt("Column"); - mBookmarkManager.CreateBookmark(absPath, lineNum, column); + String title = scope String(); + data.GetString("Title", title); + + bool isDisabled = data.GetBool("Disabled", false); + + mBookmarkManager.CreateBookmark(absPath, lineNum, column, isDisabled, title); } for (var referenceId in data.Enumerate("DebuggerDisplayTypes")) @@ -3879,6 +3891,10 @@ namespace IDE { watchPanel.TryRenameItem(); } + else if (var bookmarksPanel = activePanel as BookmarksPanel) + { + bookmarksPanel.TryRenameItem(); + } } [IDECommand] @@ -4974,6 +4990,12 @@ namespace IDE ShowPanel(mProfilePanel, "Profile"); } + [IDECommand] + public void ShowBookmarks() + { + ShowPanel(mBookmarksPanel, "Bookmarks"); + } + [IDECommand] public void ShowQuickWatch() { @@ -5655,6 +5677,7 @@ namespace IDE subMenu = root.AddMenuItem("&View"); AddMenuItem(subMenu, "AutoComplet&e", "Show Autocomplete Panel"); AddMenuItem(subMenu, "&Auto Watches", "Show Auto Watches"); + AddMenuItem(subMenu, "Boo&kmarks", "Show Bookmarks"); AddMenuItem(subMenu, "&Breakpoints", "Show Breakpoints"); AddMenuItem(subMenu, "&Call Stack", "Show Call Stack"); AddMenuItem(subMenu, "C&lass View", "Show Class View"); @@ -11902,6 +11925,8 @@ namespace IDE mPropertiesPanel.mAutoDelete = false; mAutoCompletePanel = new AutoCompletePanel(); mAutoCompletePanel.mAutoDelete = false; + mBookmarksPanel = new BookmarksPanel(); + mBookmarksPanel.mAutoDelete = false; GetVersionInfo(var exeDate); let localExeDate = exeDate.ToLocalTime(); diff --git a/IDE/src/ui/BookmarksPanel.bf b/IDE/src/ui/BookmarksPanel.bf new file mode 100644 index 00000000..63d03542 --- /dev/null +++ b/IDE/src/ui/BookmarksPanel.bf @@ -0,0 +1,282 @@ +using Beefy.theme.dark; +using Beefy.utils; +using Beefy.widgets; +using System; +using System.Collections; +using Beefy.theme; +using Beefy.events; + +namespace IDE.ui +{ + class BookmarksPanel : Panel + { + public class BookmarksListView : IDEListView + { + protected override ListViewItem CreateListViewItem() + { + return new BookmarksListViewItem(); + } + } + + public class BookmarksListViewItem : IDEListViewItem + { + public Bookmark Bookmark; + public String BookmarkLine ~ delete _; + + public void Goto() + { + gApp.mBookmarkManager.GotoBookmark(Bookmark); + } + } + + public BookmarksListView mBookmarksLV; + + public this() + { + mBookmarksLV = new .(); + mBookmarksLV.mOnEditDone.Add(new => HandleEditDone); + + mBookmarksLV.InitScrollbars(true, true); + mBookmarksLV.mLabelX = GS!(6); + mBookmarksLV.mOnItemMouseClicked.Add(new => ListViewItemMouseClicked); + + mBookmarksLV.AddColumn(200, "Bookmark"); + mBookmarksLV.AddColumn(400, "File"); + mBookmarksLV.AddColumn(120, "Line"); + + mBookmarksLV.mOnItemMouseDown.Add(new (item, x, y, btnNum, btnCount) => + { + if ((btnNum == 0) && (btnCount == 2)) + { + let mainItem = (BookmarksListViewItem)item.GetSubItem(0); + mainItem.Goto(); + } + + ListViewItemMouseDown(item, x, y, btnNum, btnCount); + }); + mBookmarksLV.mOnItemMouseClicked.Add(new => ListViewItemMouseClicked); + mBookmarksLV.mOnKeyDown.Add(new => BookmarksLV_OnKeyDown); + + AddWidget(mBookmarksLV); + + } + + private void BookmarksLV_OnKeyDown(KeyDownEvent event) + { + if (event.mKeyCode == KeyCode.Delete) + { + DeleteSelectedItem(); + } + + ListViewKeyDown_ShowMenu(event); + } + + /// Tries to rename the currently selected bookmark + public void TryRenameItem() + { + ListViewItem selectedItem = mBookmarksLV.GetRoot().FindFirstSelectedItem(); + RenameItem(selectedItem); + } + + public void DeleteSelectedItem() + { + ListViewItem selectedItem = mBookmarksLV.GetRoot().FindFirstSelectedItem(); + if (var bookmarkItem = selectedItem as BookmarksListViewItem) + { + gApp.mBookmarkManager.DeleteBookmark(bookmarkItem.Bookmark); + } + } + + private void HandleEditDone(EditWidget editWidget, bool cancelled) + { + String newValue = scope String(); + editWidget.GetText(newValue); + newValue.Trim(); + + ListViewItem listViewItem = mBookmarksLV.mEditingItem; + + if (var item = listViewItem as BookmarksListViewItem) + { + item.Bookmark.mTitle.Clear(); + item.Bookmark.mTitle.Append(newValue); + listViewItem.Label = item.Bookmark.mTitle; + } + + } + + public ~this() + { + + } + + protected override void ShowRightClickMenu(Widget relWidget, float x, float y) + { + base.ShowRightClickMenu(relWidget, x, y); + + var root = relWidget as ListViewItem; + var listView = root.mListView; + if (listView.GetRoot().FindFirstSelectedItem() != null) + { + Menu menu = new Menu(); + Menu anItem; + anItem = menu.AddItem("Delete"); + anItem.mOnMenuItemSelected.Add(new (item) => + { + listView.GetRoot().WithSelectedItems(scope (item) => + { + if (var bookmarkItem = item as BookmarksListViewItem) + { + gApp.mBookmarkManager.DeleteBookmark(bookmarkItem.Bookmark); + } + }); + + }); + + anItem = menu.AddItem("Rename"); + anItem.mOnMenuItemSelected.Add(new (item) => + { + var selectedItem = mBookmarksLV.GetRoot().FindFirstSelectedItem(); + if (selectedItem != null) + RenameItem(selectedItem); + }); + + menu.AddItem(); + + if (gApp.mBookmarkManager.AllBookmarksDisabled) + { + anItem = menu.AddItem("Enable all Bookmarks"); + anItem.mOnMenuItemSelected.Add(new (item) => + { + for (Bookmark b in gApp.mBookmarkManager.mBookmarkList) + { + b.mIsDisabled = false; + } + mBookmarksDirty = true; + }); + } + else + { + anItem = menu.AddItem("Disable all Bookmarks"); + anItem.mOnMenuItemSelected.Add(new (item) => + { + for (Bookmark b in gApp.mBookmarkManager.mBookmarkList) + { + b.mIsDisabled = true; + } + mBookmarksDirty = true; + }); + } + + MenuWidget menuWidget = ThemeFactory.mDefault.CreateMenuWidget(menu); + menuWidget.Init(relWidget, x, y); + } + } + + void EditListViewItem(ListViewItem listViewItem) + { + mBookmarksLV.EditListViewItem(listViewItem); + } + + void RenameItem(ListViewItem listViewItem) + { + EditListViewItem(listViewItem); + } + + public override void Serialize(StructuredData data) + { + base.Serialize(data); + + data.Add("Type", "BookmarksPanel"); + } + + public override void Resize(float x, float y, float width, float height) + { + base.Resize(x, y, width, height); + mBookmarksLV.Resize(0, 0, width, height); + } + + public void Clear() + { + + } + + public bool mBookmarksDirty; + + + public override void Update() + { + if (mBookmarksDirty) + UpdateBookmarks(); + + base.Update(); + } + + private void UpdateBookmarks() + { + var root = mBookmarksLV.GetRoot(); + + root.Clear(); + + for (Bookmark bookmark in gApp.mBookmarkManager.mBookmarkList) + { + BookmarksListViewItem listViewItem = (.)root.CreateChildItem(); + SetupListViewItem(listViewItem, bookmark); + } + + mBookmarksDirty = false; + } + + private void SetupListViewItem(BookmarksListViewItem listViewItem, Bookmark bookmark) + { + listViewItem.Bookmark = bookmark; + + var subViewItem = (DarkListViewItem)listViewItem.GetOrCreateSubItem(0); + + DarkCheckBox cb = new DarkCheckBox(); + cb.Checked = !bookmark.mIsDisabled; + cb.Resize(GS!(-16), 0, GS!(22), GS!(22)); + cb.mOnValueChanged.Add(new () => { + bookmark.mIsDisabled = !cb.Checked; + }); + subViewItem.AddWidget(cb); + + subViewItem.Label = bookmark.mTitle; + subViewItem.Resize(GS!(22), 0, 0, 0); + + subViewItem = (DarkListViewItem)listViewItem.GetOrCreateSubItem(1); + subViewItem.Label = bookmark.mFileName; + + // Internally lines are 0-based -> add one for display + listViewItem.BookmarkLine = new $"{bookmark.mLineNum + 1}"; + + subViewItem = (DarkListViewItem)listViewItem.GetOrCreateSubItem(2); + subViewItem.Label = listViewItem.BookmarkLine; + } + + public override void KeyDown(KeyCode keyCode, bool isRepeat) + { + mBookmarksLV.KeyDown(keyCode, isRepeat); + + base.KeyDown(keyCode, isRepeat); + } + + private void DeleteSelectedItems() + { + var root = mBookmarksLV.GetRoot(); + List selectedItems = scope List(); + root.WithSelectedItems(scope (listViewItem) => + { + selectedItems.Add(listViewItem); + }); + + // Go through in reverse, to process children before their parents + for (int itemIdx = selectedItems.Count - 1; itemIdx >= 0; itemIdx--) + { + BookmarksListViewItem item = (.)selectedItems[itemIdx]; + + if (item.Bookmark != null) + gApp.mBookmarkManager.DeleteBookmark(item.Bookmark); + } + } + } +} \ No newline at end of file diff --git a/IDE/src/ui/Panel.bf b/IDE/src/ui/Panel.bf index 964e2f3d..57ad9e18 100644 --- a/IDE/src/ui/Panel.bf +++ b/IDE/src/ui/Panel.bf @@ -155,6 +155,10 @@ namespace IDE.ui { panel = gApp.mModulePanel; } + else if (type == "BookmarksPanel") + { + panel = gApp.mBookmarksPanel; + } if (panel != null) { diff --git a/IDE/src/ui/SourceViewPanel.bf b/IDE/src/ui/SourceViewPanel.bf index 3634edaa..81c56e65 100644 --- a/IDE/src/ui/SourceViewPanel.bf +++ b/IDE/src/ui/SourceViewPanel.bf @@ -4641,7 +4641,8 @@ namespace IDE.ui if (ewc.IsLineCollapsed(drawLineNum)) continue; //hadLineIcon[drawLineNum - lineStart] = true; - g.Draw(DarkTheme.sDarkTheme.GetImage(.IconBookmark), Math.Max(GS!(-5), mEditWidget.mX - GS!(30) - leftAdjust), + Image image = DarkTheme.sDarkTheme.GetImage(bookmark.mIsDisabled ? .IconBookmarkDisabled : .IconBookmark); + g.Draw(image, Math.Max(GS!(-5), mEditWidget.mX - GS!(30) - leftAdjust), 0 + bookmark.mLineNum * lineSpacing); var curLineFlags = ref lineFlags[drawLineNum - lineStart];