using System; using System.Collections; using System.Text; using System.Threading.Tasks; using Beefy; using Beefy.gfx; using Beefy.widgets; using Beefy.theme.dark; using Beefy.geom; using IDE.Debugger; using IDE; using System.Diagnostics; using IDE.Compiler; namespace IDE.ui { public class StatusBar : Widget { public int32 mClangCommandQueueSize; public DarkComboBox mConfigComboBox; public DarkComboBox mPlatformComboBox; public bool mWasCompiling; public int mEvalCount; public ImageWidget mCancelSymSrvButton; public int mDirtyDelay; public int mStatusBoxUpdateCnt = -1; public this() { mConfigComboBox = new DarkComboBox(); mConfigComboBox.mFrameKind = .Frameless; mConfigComboBox.mPopulateMenuAction.Add(new => PopulateConfigMenu); AddWidget(mConfigComboBox); mPlatformComboBox = new DarkComboBox(); mPlatformComboBox.mFrameKind = .Frameless; mPlatformComboBox.mPopulateMenuAction.Add(new => PopulatePlatformMenu); AddWidget(mPlatformComboBox); } void PopulateConfigMenu(Menu menu) { var nameList = scope List(gApp.mWorkspace.mConfigs.Keys); nameList.Sort(scope (lhs, rhs) => lhs.CompareTo(rhs, true)); for (var configName in nameList) { String dispStr = configName; /*if (gApp.mConfigName == configName) { dispStr = stack String(); dispStr.AppendF("Active({0})", configName); }*/ var item = menu.AddItem(dispStr); if (gApp.mConfigName == configName) item.mBold = true; item.mOnMenuItemSelected.Add(new (evt) => { SelectConfig(configName); }); } } public void SelectConfig(String configName) { mConfigComboBox.Label = configName; gApp.mConfigName.Set(configName); gApp.CurrentWorkspaceConfigChanged(); MarkDirty(); } void SelectPlatform(String platformName) { mPlatformComboBox.Label = platformName; gApp.mPlatformName.Set(platformName); gApp.CurrentWorkspaceConfigChanged(); MarkDirty(); } void PopulatePlatformMenu(Menu menu) { var nameList = scope List(); gApp.mWorkspace.GetPlatformList(nameList); for (var platformName in nameList) { String dispStr = platformName; /*if (gApp.mPlatformName == platformName) { dispStr = stack String(); dispStr.AppendF("Active({0})", platformName); }*/ var item = menu.AddItem(dispStr); if (gApp.mPlatformName == platformName) item.mBold = true; item.mOnMenuItemSelected.Add(new (evt) => { SelectPlatform(platformName); }); } } void ResizeComponents() { int btnLeft = gApp.mSettings.mEnableDevMode ? GS!(380) : GS!(300); mConfigComboBox.Resize(mWidth - btnLeft, GS!(0), GS!(120), GS!(24)); mPlatformComboBox.Resize(mWidth - btnLeft - GS!(120), GS!(0), GS!(120), GS!(24)); if (mCancelSymSrvButton != null) mCancelSymSrvButton.Resize(GS!(546), 0, GS!(20), GS!(20)); } public override void Resize(float x, float y, float width, float height) { base.Resize(x, y, width, height); ResizeComponents(); } // This marks us dirty on the next frame also void MarkDirtyEx(int dirtyDelay = 1) { mDirtyDelay = Math.Min(dirtyDelay, mDirtyDelay); MarkDirty(); } public override void Update() { base.Update(); if (mDirtyDelay > 0) { if (--mDirtyDelay == 0) MarkDirty(); } if (mStatusBoxUpdateCnt != -1) mStatusBoxUpdateCnt++; if (gApp.mWorkspace.IsInitialized) { mConfigComboBox.Label = gApp.mConfigName; mPlatformComboBox.Label = gApp.mPlatformName; } else { mConfigComboBox.Label = ""; mPlatformComboBox.Label = ""; } bool canChangeConfig = !gApp.IsCompiling && !gApp.mDebugger.mIsRunning && !gApp.AreTestsRunning() && gApp.mWorkspace.IsInitialized; mConfigComboBox.mDisabled = !canChangeConfig; mPlatformComboBox.mDisabled = !canChangeConfig; if ((gApp.IsCompiling || gApp.mRunningTestScript) && (mUpdateCnt % 8 == 0)) MarkDirtyEx(8); var debugState = gApp.mDebugger.GetRunState(); //if (mWidgetWindow.IsKeyDown(.Control)) //debugState = .SearchingSymSrv; if (debugState == .DebugEval) { mEvalCount++; MarkDirtyEx(); } else mEvalCount = 0; if (debugState == .SearchingSymSrv) { MarkDirtyEx(); if (mCancelSymSrvButton == null) { mCancelSymSrvButton = new ImageWidget(); mCancelSymSrvButton.mImage = DarkTheme.sDarkTheme.GetImage(.Close); mCancelSymSrvButton.mOverImage = DarkTheme.sDarkTheme.GetImage(.CloseOver); mCancelSymSrvButton.mOnMouseClick.Add(new (evt) => { gApp.mDebugger.CancelSymSrv(); }); AddWidget(mCancelSymSrvButton); ResizeComponents(); } float len = GS!(200); float x = GS!(350); Rect completionRect = Rect(x, GS!(1), len, GS!(17)); Point mousePos; if (DarkTooltipManager.CheckMouseover(this, 25, out mousePos, true)) { if (completionRect.Contains(mousePos.x, mousePos.y)) { DarkTooltipManager.ShowTooltip(gApp.mSymSrvStatus, this, mousePos.x, mousePos.y); } } } else { if ((DarkTooltipManager.sTooltip != null) && (DarkTooltipManager.sTooltip.mRelWidget == this)) DarkTooltipManager.sTooltip.Close(); if (mCancelSymSrvButton != null) { RemoveAndDelete(mCancelSymSrvButton); mCancelSymSrvButton = null; } } if ((gApp.mBfResolveCompiler != null) && (gApp.mBfResolveCompiler.IsPerformingBackgroundOperation()) #if IDE_C_SUPPORT || (gApp.mResolveClang.IsPerformingBackgroundOperation()) || (gApp.mDepClang.IsPerformingBackgroundOperation()) #endif ) { MarkDirtyEx(); } } public override void Draw(Graphics g) { bool atError = gApp.mDebugger.GetRunState() == DebugManager.RunState.Exception; uint32 bkgColor = 0xFF404040; if (atError) bkgColor = 0xFF800000; if (gApp.IsCompiling) bkgColor = 0xFF35306A; else if (gApp.mDebugger.mIsRunning) bkgColor = 0xFFB65D08; else if (gApp.AreTestsRunning()) bkgColor = 0xFF562143; using (g.PushColor(bkgColor)) g.FillRect(0, 0, mWidth, mHeight); // Helps debug MarkDirtys /*using (g.PushColor(Color.FromHSV(Utils.RandFloat(), 1.0f, 1.0f))) { g.FillRect(0, mHeight - 4, 4, 4); }*/ g.SetFont(DarkTheme.sDarkTheme.mSmallFont); var activeDocument = gApp.GetActiveDocumentPanel(); if (activeDocument is SourceViewPanel) { var sourceViewPanel = (SourceViewPanel)activeDocument; sourceViewPanel = sourceViewPanel.GetActivePanel(); int32 line; int32 column; sourceViewPanel.GetCursorPosition(out line, out column); if (gApp.mSettings.mEnableDevMode) g.DrawString(StackStringFormat!("Idx {0}", sourceViewPanel.mEditWidget.Content.CursorTextPos), mWidth - GS!(240), 0); g.DrawString(StackStringFormat!("Ln {0}", line + 1), mWidth - GS!(150), 0); g.DrawString(StackStringFormat!("Col {0}", column + 1), mWidth - GS!(78), 0); } using (g.PushColor(0xFF101010)) { //g.FillRect(50 + ((mUpdateCnt) % 50) * 2, 0, 3, mHeight); } var bfBuildCompiler = gApp.mBfBuildCompiler; #if IDE_C_SUPPORT var buildClang = gApp.mDepClang; #endif float? completionPct = null; if (bfBuildCompiler.HasQueuedCommands()) { completionPct = bfBuildCompiler.GetCompletionPercentage(); Debug.Assert(completionPct.GetValueOrDefault() >= 0); } #if IDE_C_SUPPORT else if ((gApp.IsCompiling) && (buildClang.mCompileWaitsForQueueEmpty)) { mClangCommandQueueSize = Math.Max(mClangCommandQueueSize, buildClang.GetCommandQueueSize()); if (mClangCommandQueueSize != 0) { int32 itemsLeft = buildClang.GetCommandQueueSize(); completionPct = (mClangCommandQueueSize - itemsLeft) / (float)mClangCommandQueueSize; Debug.Assert(completionPct.GetValueOrDefault() >= 0); if (itemsLeft == 0) mClangCommandQueueSize = 0; } } #endif float statusLabelPos = (int)GS!(-1.3f); //completionPct = 0.4f; if (completionPct.HasValue) { Rect completionRect = Rect(GS!(200), GS!(2), GS!(120), GS!(15)); using (g.PushColor(0xFF000000)) g.FillRect(completionRect.mX, completionRect.mY, completionRect.mWidth, completionRect.mHeight); completionRect.Inflate(GS!(-1), GS!(-1)); using (g.PushColor(0xFF00FF00)) g.FillRect(completionRect.mX, completionRect.mY, completionRect.mWidth * completionPct.Value, completionRect.mHeight); } else if ((gApp.mDebugger.mIsRunning) && (gApp.HaveSourcesChanged())) { Rect completionRect = Rect(GS!(200), GS!(1), GS!(120), GS!(17)); using (g.PushColor(0x60000000)) g.FillRect(completionRect.mX, completionRect.mY, completionRect.mWidth, completionRect.mHeight); completionRect.Inflate(-1, -1); using (g.PushColor(0x40202080)) g.FillRect(completionRect.mX, completionRect.mY, completionRect.mWidth, completionRect.mHeight); g.DrawString("Source Changed", GS!(200), statusLabelPos, FontAlign.Centered, GS!(120)); } void DrawStatusBox(StringView str, int32 updateCnt = -1) { if (mStatusBoxUpdateCnt == -1) mStatusBoxUpdateCnt = 0; float len = GS!(200); float x = GS!(350); Rect completionRect = Rect(x, GS!(1), len, GS!(17)); using (g.PushColor(0x60000000)) g.FillRect(completionRect.mX, completionRect.mY, completionRect.mWidth, completionRect.mHeight); completionRect.Inflate(-1, -1); //float pulseSpeed = Math.Min(mStatusBoxUpdateCnt * 0.001f, 0.2f); float pulseSpeed = 0.2f; if (updateCnt != -1) pulseSpeed = Math.Max(0.14f, pulseSpeed - updateCnt * 0.00005f); float pulsePct = -Math.Cos(Math.Max(mStatusBoxUpdateCnt - 30, 0) * pulseSpeed); if (updateCnt != -1) pulsePct *= Math.Max(0.4f, 1.0f - updateCnt * 0.0004f); using (g.PushColor(Color.FromHSV(0.1f, 0.5f, (float)Math.Max(pulsePct * 0.15f + 0.3f, 0.3f)))) g.FillRect(completionRect.mX, completionRect.mY, completionRect.mWidth, completionRect.mHeight); if (mCancelSymSrvButton != null) mCancelSymSrvButton.mX = completionRect.Right - GS!(16); g.DrawString(str, x, statusLabelPos, FontAlign.Centered, len); } if (gApp.mKeyChordState != null) { String chordState = scope String(); gApp.mKeyChordState.mCommandMap.ToString(chordState); chordState.Append(", ..."); DrawStatusBox(chordState); } else if (mCancelSymSrvButton != null) { DrawStatusBox("Retrieving Debug Symbols... "); } else if (mEvalCount > 20) { DrawStatusBox("Evaluating Expression"); } else if (gApp.mRunningTestScript) { DrawStatusBox("Running Script"); } else if ((gApp.mBuildContext != null) && (!completionPct.HasValue)) { DrawStatusBox("Custom Build Commands...", gApp.mBuildContext.mUpdateCnt); } else mStatusBoxUpdateCnt = -1; /// { if (gApp.mErrorsPanel.mErrorCount > 0) { g.Draw(DarkTheme.sDarkTheme.GetImage(.CodeError), GS!(6), 0); } else if (gApp.mErrorsPanel.mWarningCount > 0) { g.Draw(DarkTheme.sDarkTheme.GetImage(.CodeWarning), GS!(6), 0); } } if (gApp.mSettings.mEnableDevMode) { g.DrawString(StackStringFormat!("FPS: {0}", gApp.mLastFPS), GS!(32), 0); String resolveStr = scope String(); let bfResolveCompiler = gApp.mBfResolveCompiler; if ((bfResolveCompiler != null) && (gApp.mBfResolveCompiler.mThreadWorker.mThreadRunning)) { resolveStr.Append("B"); } if ((bfResolveCompiler != null) && (gApp.mBfResolveCompiler.mThreadWorkerHi.mThreadRunning)) { resolveStr.Append("H"); } #if IDE_C_SUPPORT if (gApp.mResolveClang.IsPerformingBackgroundOperation()) { if (resolveStr.Length > 0) resolveStr.Append(" & "); resolveStr.Append("Clang"); } if (gApp.mDepClang.IsPerformingBackgroundOperation()) { if (resolveStr.Length > 0) resolveStr.Append(" "); resolveStr.Append("ClangB"); } #endif /*if (BfPassInstance.sPassInstances.Count > 0) { //resolveStr += String.Format(" PassInstances: {0}", BfPassInstance.sPassInstances.Count); resolveStr += "ResolvePasses: {"; //foreach (var passInstance in BfPassInstance.sPassInstances) for (int passIdx = 0; passIdx < BfPassInstance.sPassInstances.Count; passIdx++) { var passInstance = BfPassInstance.sPassInstances[passIdx]; if (passIdx > 0) resolveStr += ", "; if (passInstance.mDbgStr != null) resolveStr += passInstance.mDbgStr; resolveStr += String.Format(" #{0}", passInstance.mId); } resolveStr += "}"; }*/ if (resolveStr.Length != 0) g.DrawString(resolveStr, GS!(100), 0); } } public override void MouseDown(float x, float y, int32 btn, int32 btnCount) { base.MouseDown(x, y, btn, btnCount); if (Rect(GS!(6), 0, GS!(20), mHeight).Contains(x, y)) { gApp.mErrorsPanel.ShowErrorNext(); return; } } } }