using System; using System.Collections; using System.Text; using System.Threading.Tasks; using Beefy.events; using Beefy.gfx; using Beefy.theme.dark; using Beefy.widgets; using IDE.Compiler; using System.Diagnostics; namespace IDE.ui { class NavigationBar : DarkComboBox { enum EntryType { Unknown, Method, ExtMethod, Property, Class, Enum, Struct, TypeAlias } class Entry { public String mText ~ delete _; public EntryType mEntryType; public int32 mLine; public int32 mLineChar; } public static Dictionary sMRU = new Dictionary() ~ delete _; public static int32 sCurrentMRUIndex = 1; SourceViewPanel mSourceViewPanel; List mEntries = new List() ~ DeleteContainerAndItems!(_); List mShownEntries = new List() ~ delete _; String mFilterString ~ delete _; String mCurLocation = new String() ~ delete _; bool mIgnoreChange = false; public this(SourceViewPanel sourceViewPanel) { mSourceViewPanel = sourceViewPanel; mLabelAlign = FontAlign.Left; Label = ""; mLabelX = GS!(16); mPopulateMenuAction.Add(new => PopulateNavigationBar); MakeEditable(); mEditWidget.mOnContentChanged.Add(new => NavigationBarChanged); mEditWidget.mOnKeyDown.Add(new => EditKeyDownHandler); mEditWidget.mOnGotFocus.Add(new (widget) => mEditWidget.mEditWidgetContent.SelectAll()); mEditWidget.mEditWidgetContent.mWantsUndo = false; mFocusDropdown = false; } public ~this() { } static ~this() { for (var key in sMRU.Keys) delete key; } public void SetLocation(String location) { if (mCurMenuWidget == null) { mIgnoreChange = true; mEditWidget.SetText(location); // SetText can attempt to scroll to the right to make the cursor position visible. Just scroll back to the start. mEditWidget.HorzScrollTo(0); mIgnoreChange = false; } } void EditKeyDownHandler(KeyDownEvent evt) { if (evt.mKeyCode == KeyCode.Escape) mSourceViewPanel.FocusEdit(); } void GetEntries() { ClearAndDeleteItems(mEntries); ResolveParams resolveParams = scope ResolveParams(); mSourceViewPanel.Classify(ResolveType.GetNavigationData, resolveParams); if (resolveParams.mNavigationData != null) { var autocompleteLines = scope List(resolveParams.mNavigationData.Split('\n')); autocompleteLines.Sort(scope (a, b) => StringView.Compare(a, b, true)); for (var autocompleteLineView in autocompleteLines) { if (autocompleteLineView.Length == 0) continue; Entry entry = new Entry(); int idx = 0; for (var lineData in autocompleteLineView.Split('\t')) { switch (idx) { case 0: entry.mText = new String(lineData); case 1: switch(lineData) { case "method": entry.mEntryType = .Method; case "extmethod": entry.mEntryType = .ExtMethod; case "property": entry.mEntryType = .Property; case "class": entry.mEntryType = .Class; case "enum": entry.mEntryType = .Enum; case "struct": entry.mEntryType = .Struct; case "typealias": entry.mEntryType = .TypeAlias; default: } case 2: entry.mLine = int32.Parse(lineData); case 3: entry.mLineChar = int32.Parse(lineData); } ++idx; } mEntries.Add(entry); } } } private void PopulateNavigationBar(Menu menu) { List findStrs = null; if (mFilterString != null) findStrs = scope:: List(mFilterString.Split(' ')); EntryLoop: for (int32 entryIdx = 0; entryIdx < mEntries.Count; entryIdx++) { var entry = mEntries[entryIdx]; if (findStrs != null) { for (let findStr in findStrs) { if (entry.mText.IndexOf(findStr, true) == -1) continue EntryLoop; } } mShownEntries.Add(entry); var menuItem = menu.AddItem(entry.mText); switch (entry.mEntryType) { case .Method: menuItem.mIconImage = DarkTheme.sDarkTheme.GetImage(.Method); case .ExtMethod: menuItem.mIconImage = DarkTheme.sDarkTheme.GetImage(.ExtMethod); case .Property: menuItem.mIconImage = DarkTheme.sDarkTheme.GetImage(.Property); case .Class: menuItem.mIconImage = DarkTheme.sDarkTheme.GetImage(.Type_Class); case .Enum: menuItem.mIconImage = DarkTheme.sDarkTheme.GetImage(.Type_ValueType); case .Struct: menuItem.mIconImage = DarkTheme.sDarkTheme.GetImage(.Type_ValueType); case .TypeAlias: menuItem.mIconImage = DarkTheme.sDarkTheme.GetImage(.MemoryArrowSingle); default: } menuItem.mOnMenuItemSelected.Add(new (evt) => ShowEntry(entryIdx, entry)); } } void ShowEntry(int32 entryIdx, Entry entry) { //sMRU[entry.mText] = sCurrentMRUIndex++; String* keyPtr; int32* valPtr; if (sMRU.TryAdd(entry.mText, out keyPtr, out valPtr)) *keyPtr = new String(entry.mText); *valPtr = sCurrentMRUIndex++; mSourceViewPanel.ShowFileLocation(-1, entry.mLine, entry.mLineChar, LocatorType.Always); } private void NavigationBarChanged(EditEvent theEvent) { if (mIgnoreChange) return; var editWidget = (EditWidget)theEvent.mSender; var searchText = scope String(); editWidget.GetText(searchText); searchText.Trim(); mFilterString = searchText; ShowDropdown(); mFilterString = null; } bool mIgnoreShowDropdown; public override MenuWidget ShowDropdown() { if (mIgnoreShowDropdown) return null; mIgnoreShowDropdown = true; defer { mIgnoreShowDropdown = false; } /*var stopWatch = scope Stopwatch(); stopWatch.Start();*/ //Profiler.StartSampling(); if (!mEditWidget.mHasFocus) SetFocus(); if (mCurMenuWidget == null) GetEntries(); if (mFilterString == null) mEditWidget.Content.SelectAll(); mShownEntries.Clear(); base.ShowDropdown(); int32 bestItem = -1; int32 bestPri = -1; var menuWidget = (DarkMenuWidget)mCurMenuWidget; for (int32 itemIdx = 0; itemIdx < menuWidget.mItemWidgets.Count; itemIdx++) { var menuItemWidget = (DarkMenuItem)menuWidget.mItemWidgets[itemIdx]; int32 pri; sMRU.TryGetValue(menuItemWidget.mMenuItem.mLabel, out pri); if (pri > bestPri) { bestItem = itemIdx; bestPri = pri; } } if (bestItem != -1) { mCurMenuWidget.mOnSelectionChanged.Add(new => SelectionChanged); mCurMenuWidget.SetSelection(bestItem); } //Profiler.StopSampling(); //stopWatch.Stop(); //Debug.WriteLine("Time: {0}", stopWatch.ElapsedMilliseconds); return menuWidget; } void SelectionChanged(int selIdx) { if (mEditWidget.mEditWidgetContent.HasSelection()) { bool prevIgnoreShowDropdown = mIgnoreShowDropdown; mIgnoreShowDropdown = true; mEditWidget.SetText(""); mIgnoreShowDropdown = prevIgnoreShowDropdown; } } public override void MenuClosed() { mSourceViewPanel.FocusEdit(); } } }