diff --git a/BeefLibs/Beefy2D/src/theme/dark/DarkTheme.bf b/BeefLibs/Beefy2D/src/theme/dark/DarkTheme.bf index 930e7cff..edb3d970 100644 --- a/BeefLibs/Beefy2D/src/theme/dark/DarkTheme.bf +++ b/BeefLibs/Beefy2D/src/theme/dark/DarkTheme.bf @@ -360,20 +360,20 @@ namespace Beefy.theme.dark mHeaderFont.AddAlternate(scope String(BFApp.sApp.mInstallDir, "fonts/seguihis.ttf"), 11.7f * sScale);*/ mHeaderFont.Load("Segoe UI", 11.7f * sScale); //8.8 - mHeaderFont.AddAlternate("Segoe UI Symbol", 11.7f * sScale); + mHeaderFont.AddAlternate("Segoe UI Symbol", 11.7f * sScale).IgnoreError(); mHeaderFont.AddAlternate("Segoe UI Historic", 11.7f * sScale).IgnoreError(); mHeaderFont.AddAlternate("Segoe UI Emoji", 11.7f * sScale).IgnoreError(); mSmallFont.Dispose(true); mSmallFont.Load("Segoe UI", 12.8f * sScale); // 10.0 - mSmallFont.AddAlternate("Segoe UI Symbol", 12.8f * sScale); + mSmallFont.AddAlternate("Segoe UI Symbol", 12.8f * sScale).IgnoreError(); mSmallFont.AddAlternate("Segoe UI Historic", 12.8f * sScale).IgnoreError(); mSmallFont.AddAlternate("Segoe UI Emoji", 12.8f * sScale).IgnoreError(); mSmallBoldFont.Dispose(true); mSmallBoldFont.Dispose(true); mSmallBoldFont.Load("Segoe UI Bold", 12.8f * sScale); // 10.0 - mSmallBoldFont.AddAlternate("Segoe UI Symbol", 12.8f * sScale); + mSmallBoldFont.AddAlternate("Segoe UI Symbol", 12.8f * sScale).IgnoreError(); mSmallBoldFont.AddAlternate("Segoe UI Historic", 12.8f * sScale).IgnoreError(); mSmallBoldFont.AddAlternate("Segoe UI Emoji", 12.8f * sScale).IgnoreError(); /*mSmallBoldFont.Load(StringAppend!(tempStr, BFApp.sApp.mInstallDir, "fonts/segoeuib.ttf"), 12.8f * sScale); // 10.0 diff --git a/BeefLibs/Beefy2D/src/theme/dark/DarkTooltip.bf b/BeefLibs/Beefy2D/src/theme/dark/DarkTooltip.bf index e9991e9c..22a2fe29 100644 --- a/BeefLibs/Beefy2D/src/theme/dark/DarkTooltip.bf +++ b/BeefLibs/Beefy2D/src/theme/dark/DarkTooltip.bf @@ -36,6 +36,7 @@ namespace Beefy.theme.dark public bool mAllowMouseInsideSelf; public bool mAllowMouseOutside; public int mAutoCloseDelay; + public bool mIsClipped; public const float cShadowSize = 8; @@ -50,19 +51,26 @@ namespace Beefy.theme.dark mText = new String(text); mAllowResize = allowResize; + BFApp.sApp.GetWorkspaceRect(var workspaceX, var workspaceY, var workspaceWidth, var workspaceHeight); + float maxWidth = workspaceWidth - GS!(32); + FontMetrics fontMetrics = .(); float height = mFont.Draw(null, mText, x, y, 0, 0, FontOverflowMode.Overflow, &fontMetrics); - mWidth = Math.Max(minWidth, fontMetrics.mMaxWidth + GS!(32)); - mHeight = Math.Max(minHeight, height + GS!(16)); - + mWidth = Math.Max(fontMetrics.mMaxWidth + GS!(32), minWidth); + mHeight = Math.Clamp(height + GS!(16), minHeight, workspaceHeight); + + if (mWidth > maxWidth) + { + mIsClipped = true; + mWidth = maxWidth; + } + float screenX; float screenY; relWidget.SelfToRootTranslate(x, y, out screenX, out screenY); screenX += relWidget.mWidgetWindow.mClientX; screenY += relWidget.mWidgetWindow.mClientY; - //screenX -= 2; - //screenY += 14; - + BFWindow.Flags windowFlags = BFWindow.Flags.ClientSized | BFWindow.Flags.PopupPosition | BFWindow.Flags.NoActivate | BFWindow.Flags.DestAlpha; WidgetWindow widgetWindow = new WidgetWindow(relWidget.mWidgetWindow, "Tooltip", @@ -170,7 +178,10 @@ namespace Beefy.theme.dark g.DrawBox(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.Menu), 0, 0, mWidth, mHeight); g.SetFont(mFont); - g.DrawString(mText, 0, GS!(5), FontAlign.Centered, mWidth); + if (mIsClipped) + g.DrawString(mText, GS!(8), GS!(5), .Left, mWidth - GS!(16), .Ellipsis); + else + g.DrawString(mText, 0, GS!(5), .Centered, mWidth); if (mAllowResize) g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.ResizeGrabber), mWidth - GS!(22), mHeight - GS!(22)); diff --git a/IDE/src/IDEApp.bf b/IDE/src/IDEApp.bf index 7c7c2dd2..11da9234 100644 --- a/IDE/src/IDEApp.bf +++ b/IDE/src/IDEApp.bf @@ -4368,6 +4368,9 @@ namespace IDE void ShowPanel(Panel panel, String label, bool setFocus = true) { + if (!mInitialized) + return; + mLastActivePanel = panel; RecordHistoryLocation(); ShowTab(panel, label, false, setFocus); @@ -6843,6 +6846,11 @@ namespace IDE var window = (WidgetWindow)evt.mSender; + if (window.mFocusWidget is KeysEditWidget) + { + return; + } + IDECommand.ContextFlags useFlags = .None; var activeWindow = GetActiveWindow(); bool isMainWindow = activeWindow.mRootWidget is MainFrame; @@ -10646,19 +10654,22 @@ namespace IDE float fontSize = DarkTheme.sScale * mSettings.mEditorSettings.mFontSize; float tinyFontSize = fontSize * 8.0f/9.0f; - bool failed = false; + String err = scope String(); void FontFail(StringView name) { - String err = scope String()..AppendF("Failed to load font '{}'", name); - OutputErrorLine(err); - if (!failed) - Fail(err); - failed = true; + if (!err.IsEmpty) + err.Append("\n"); + err.AppendF("Failed to load font '{}'", name); } bool isFirstFont = true; - for (let fontName in mSettings.mEditorSettings.mFonts) + for (var fontName in mSettings.mEditorSettings.mFonts) + FontLoop: { + bool isOptional; + if (isOptional = fontName.StartsWith("?")) + fontName = scope:FontLoop String(fontName, "?".Length); + if (isFirstFont) { mTinyCodeFont.Dispose(true); @@ -10670,7 +10681,7 @@ namespace IDE else { mTinyCodeFont.AddAlternate(fontName, tinyFontSize).IgnoreError(); - if (mCodeFont.AddAlternate(fontName, fontSize) case .Err) + if ((mCodeFont.AddAlternate(fontName, fontSize) case .Err) && (!isOptional)) FontFail(fontName); } } @@ -10685,12 +10696,12 @@ namespace IDE if (mCodeFont.GetWidth('😊') == 0) { mCodeFont.AddAlternate("Segoe UI", fontSize); - mCodeFont.AddAlternate("Segoe UI Symbol", fontSize); + mCodeFont.AddAlternate("Segoe UI Symbol", fontSize).IgnoreError(); mCodeFont.AddAlternate("Segoe UI Historic", fontSize).IgnoreError(); mCodeFont.AddAlternate("Segoe UI Emoji", fontSize).IgnoreError(); mTinyCodeFont.AddAlternate("Segoe UI", tinyFontSize); - mTinyCodeFont.AddAlternate("Segoe UI Symbol", tinyFontSize); + mTinyCodeFont.AddAlternate("Segoe UI Symbol", tinyFontSize).IgnoreError(); mTinyCodeFont.AddAlternate("Segoe UI Historic", tinyFontSize).IgnoreError(); mTinyCodeFont.AddAlternate("Segoe UI Emoji", tinyFontSize).IgnoreError(); @@ -10703,6 +10714,12 @@ namespace IDE mTinyCodeFont.AddAlternate(new String("fonts/seguihis.ttf"), tinyFontSize);*/ } + if (!err.IsEmpty) + { + OutputErrorLine(err); + Fail(err); + } + //mTinyCodeFont.Load(scope String(BFApp.sApp.mInstallDir, "fonts/SourceCodePro-Regular.ttf"), fontSize); float squiggleScale = DarkTheme.sScale; diff --git a/IDE/src/Settings.bf b/IDE/src/Settings.bf index c8ed8b63..8c04e8a7 100644 --- a/IDE/src/Settings.bf +++ b/IDE/src/Settings.bf @@ -39,20 +39,36 @@ namespace IDE { sd.GetString("Bin32Path", mBin32Path); sd.GetString("Bin64Path", mBin64Path); - ClearAndDeleteItems(mLib32Paths); - for (sd.Enumerate("Lib32Paths")) + + void ReadPaths(String pathName, List paths) { - var str = new String(); - sd.GetCurString(str); - mLib32Paths.Add(str); - } - ClearAndDeleteItems(mLib64Paths); - for (sd.Enumerate("Lib64Paths")) - { - var str = new String(); - sd.GetCurString(str); - mLib64Paths.Add(str); + HashSet newPaths = scope .(); + List prevPaths = scope .(); + for (var str in paths) + prevPaths.Add(str); + paths.Clear(); + + for (sd.Enumerate(pathName)) + { + var str = new String(); + sd.GetCurString(str); + if (newPaths.Add(str)) + paths.Add(str); + else + delete str; + } + + for (var path in prevPaths) + { + if (!newPaths.Contains(path)) + paths.Add(path); + else + delete path; + } } + + ReadPaths("Lib32Paths", mLib32Paths); + ReadPaths("Lib64Paths", mLib64Paths); } [CLink, StdCall] @@ -68,6 +84,9 @@ namespace IDE #if BF_PLATFORM_WINDOWS StringView vsInfo = .(VSSupport_Find()); + ClearAndDeleteItems(mLib32Paths); + ClearAndDeleteItems(mLib64Paths); + for (var infoStr in vsInfo.Split('\n')) { if (infoStr.IsEmpty) @@ -354,9 +373,9 @@ namespace IDE { mFonts.Add(new String("fonts/SourceCodePro-Regular.ttf")); mFonts.Add(new String("Segoe UI")); - mFonts.Add(new String("Segoe UI Symbol")); - mFonts.Add(new String("Segoe UI Historic")); - mFonts.Add(new String("Segoe UI Emoji")); + mFonts.Add(new String("?Segoe UI Symbol")); + mFonts.Add(new String("?Segoe UI Historic")); + mFonts.Add(new String("?Segoe UI Emoji")); } public void Apply() diff --git a/IDE/src/ui/AutoComplete.bf b/IDE/src/ui/AutoComplete.bf index b47dbed3..0ccf105a 100644 --- a/IDE/src/ui/AutoComplete.bf +++ b/IDE/src/ui/AutoComplete.bf @@ -397,15 +397,6 @@ namespace IDE.ui int32 startIdx = (int32)(scrollPos / mAutoCompleteListWidget.mItemSpacing); int32 endIdx = Math.Min((int32)((scrollPos + mAutoCompleteListWidget.mHeight)/ mAutoCompleteListWidget.mItemSpacing) + 1, (int32)mAutoCompleteListWidget.mEntryList.Count); - for (int32 itemIdx = startIdx; itemIdx < endIdx; itemIdx++) - { - var entry = (EntryWidget)mAutoCompleteListWidget.mEntryList[itemIdx]; - - float curY = entry.Y; - using (g.PushTranslate(4, curY)) - entry.Draw(g); - } - if (mAutoCompleteListWidget.mSelectIdx != -1) { var selectedEntry = mAutoCompleteListWidget.mEntryList[mAutoCompleteListWidget.mSelectIdx]; @@ -421,6 +412,15 @@ namespace IDE.ui g.DrawButton(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.MenuSelect), GS!(4), selectedEntry.Y - GS!(2), width); } } + + for (int32 itemIdx = startIdx; itemIdx < endIdx; itemIdx++) + { + var entry = (EntryWidget)mAutoCompleteListWidget.mEntryList[itemIdx]; + + float curY = entry.Y; + using (g.PushTranslate(4, curY)) + entry.Draw(g); + } } public override void MouseDown(float x, float y, int32 btn, int32 btnCount) diff --git a/IDE/src/ui/ProjectPanel.bf b/IDE/src/ui/ProjectPanel.bf index 4d328ab6..871b88f1 100644 --- a/IDE/src/ui/ProjectPanel.bf +++ b/IDE/src/ui/ProjectPanel.bf @@ -1923,12 +1923,51 @@ namespace IDE.ui Menu menu = new Menu(); bool handled = false; + void AddOpenContainingFolder() + { + var item = menu.AddItem("Open Containing Folder"); + item.mOnMenuItemSelected.Add(new (item) => + { + let projectItem = GetSelectedProjectItem(); + String path = scope String(); + if (projectItem == null) + { + path.Set(gApp.mWorkspace.mDir); + } + else if (let projectFolder = projectItem as ProjectFolder) + { + if (projectFolder.mParentFolder == null) + { + path.Set(projectFolder.mProject.mProjectDir); + } + else + projectFolder.GetFullImportPath(path); + } + else + projectItem.mParentFolder.GetFullImportPath(path); + + if (!path.IsWhiteSpace) + { + ProcessStartInfo psi = scope ProcessStartInfo(); + psi.SetFileName(path); + psi.UseShellExecute = true; + psi.SetVerb("Open"); + + var process = scope SpawnedProcess(); + process.Start(psi).IgnoreError(); + } + }); + } + if (projectItem == null) { Menu anItem; if (gApp.mWorkspace.IsInitialized) { + AddOpenContainingFolder(); + menu.AddItem(); + anItem = menu.AddItem("Add New Project..."); anItem.mOnMenuItemSelected.Add(new (item) => { AddNewProject(); }); @@ -1938,6 +1977,7 @@ namespace IDE.ui anItem = menu.AddItem("Add From Installed..."); anItem.mOnMenuItemSelected.Add(new (item) => { mImportInstalledDeferred = true; }); + menu.AddItem(); anItem = menu.AddItem("Properties..."); anItem.mOnMenuItemSelected.Add(new (item) => { ShowWorkspaceProperties(); }); @@ -2027,6 +2067,8 @@ namespace IDE.ui } }); + AddOpenContainingFolder(); + menu.AddItem(); } @@ -2168,24 +2210,7 @@ namespace IDE.ui }); - item = menu.AddItem("Open Containing Folder"); - item.mOnMenuItemSelected.Add(new (item) => - { - let projectItem = GetSelectedProjectItem(); - String path = scope String(); - if (let projectFolder = projectItem as ProjectFolder) - projectFolder.GetFullImportPath(path); - else - projectItem.mParentFolder.GetFullImportPath(path); - - ProcessStartInfo psi = scope ProcessStartInfo(); - psi.SetFileName(path); - psi.UseShellExecute = true; - psi.SetVerb("Open"); - - var process = scope SpawnedProcess(); - process.Start(psi).IgnoreError(); - }); + AddOpenContainingFolder(); menu.AddItem(); } diff --git a/IDE/src/ui/PropertiesDialog.bf b/IDE/src/ui/PropertiesDialog.bf index 142c789c..2898f59f 100644 --- a/IDE/src/ui/PropertiesDialog.bf +++ b/IDE/src/ui/PropertiesDialog.bf @@ -80,7 +80,7 @@ namespace IDE.ui { if ((evt.mKeyFlags == 0) && ((evt.mKeyCode == .Delete) || (evt.mKeyCode == .Backspace)) && - (Content.HasSelection())) + ((Content.HasSelection()) || (!HasKeys))) { if ((evt.mKeyCode == .Delete) || (evt.mKeyCode == .Backspace)) { @@ -949,10 +949,11 @@ namespace IDE.ui if (curVariantType == typeof(List)) { - if (!newValue.IsEmpty && !newValue.StartsWith("<")) + if (!newValue.StartsWith("<")) { let entries = new List(); - KeyState.Parse(newValue, entries); + if (!newValue.IsEmpty) + KeyState.Parse(newValue, entries); editingProp.mCurValue = Variant.Create(entries, true); } else @@ -1386,6 +1387,13 @@ namespace IDE.ui var str = scope String(); KeyState.ToString(keyList, str); + if (str.IsEmpty) + { + let origEntry = propEntry.mOrigValue.Get>(); + if (!origEntry.IsEmpty) + str.Append("< Removed >"); + } + valueItem.Label = str; } else if (curVariantType.IsGenericType) diff --git a/IDE/src/ui/SourceEditWidgetContent.bf b/IDE/src/ui/SourceEditWidgetContent.bf index d218d164..dbf04512 100644 --- a/IDE/src/ui/SourceEditWidgetContent.bf +++ b/IDE/src/ui/SourceEditWidgetContent.bf @@ -632,7 +632,7 @@ namespace IDE.ui } else if (elementFlags.HasFlag(SourceElementFlags.SpellingError)) { - underlineColor = 0xE0FF3000; + underlineColor = 0x80FFD200; } if (underlineColor != 0) diff --git a/IDEHelper/Compiler/BfContext.cpp b/IDEHelper/Compiler/BfContext.cpp index ec1fb778..224b42cc 100644 --- a/IDEHelper/Compiler/BfContext.cpp +++ b/IDEHelper/Compiler/BfContext.cpp @@ -1335,8 +1335,8 @@ void BfContext::SaveDeletingType(BfType* type) } else { - savedTypeData = *savedTypeDataPtr; - BF_DBG_FATAL("mSavedTypeData already had type name"); + // This can happen if we have a conflicting type definition + savedTypeData = *savedTypeDataPtr; } savedTypeData->mTypeId = type->mTypeId; while ((int)mSavedTypeData.size() <= savedTypeData->mTypeId) diff --git a/IDEHelper/Compiler/BfExprEvaluator.cpp b/IDEHelper/Compiler/BfExprEvaluator.cpp index f3721585..63082e27 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.cpp +++ b/IDEHelper/Compiler/BfExprEvaluator.cpp @@ -3827,7 +3827,7 @@ BfTypedValue BfExprEvaluator::LookupField(BfAstNode* targetSrc, BfTypedValue tar mModule->TypeToString(curCheckType).c_str(), mPropDef->mName.c_str()), targetSrc); } } - + if (prop->mIsStatic) mPropTarget = BfTypedValue(curCheckType); else if (isBaseLookup) @@ -3837,6 +3837,13 @@ BfTypedValue BfExprEvaluator::LookupField(BfAstNode* targetSrc, BfTypedValue tar } else mPropTarget = target; + + if (mPropTarget.mType->IsStructPtr()) + { + mPropTarget = mModule->LoadValue(mPropTarget); + mPropTarget = BfTypedValue(mPropTarget.mValue, mPropTarget.mType->GetUnderlyingType(), mPropTarget.IsReadOnly() ? BfTypedValueKind_ReadOnlyAddr : BfTypedValueKind_Addr); + } + mOrigPropTarget = mPropTarget; auto autoComplete = GetAutoComplete(); @@ -12118,6 +12125,14 @@ BfTypedValue BfExprEvaluator::MakeCallableTarget(BfAstNode* targetSrc, BfTypedVa return target; } + if ((target.mType->IsPointer()) && (target.mType->GetUnderlyingType()->IsObjectOrInterface())) + { + mModule->Fail(StrFormat("Methods cannot be called on type '%s' because the type is a pointer to a reference type (ie: a double-reference).", + mModule->TypeToString(target.mType).c_str()), targetSrc); + target.mType = mModule->mContext->mBfObjectType; + return target; + } + if (target.mType->IsWrappableType()) { auto primStructType = mModule->GetWrappedStructType(target.mType); @@ -12148,7 +12163,7 @@ BfTypedValue BfExprEvaluator::MakeCallableTarget(BfAstNode* targetSrc, BfTypedVa if ((!target.mType->IsTypeInstance()) && (!target.mType->IsConcreteInterfaceType())) { - mModule->Fail(StrFormat("Invalid target type: '%s'", mModule->TypeToString(target.mType).c_str()), targetSrc); + mModule->Fail(StrFormat("Methods cannot be called on type '%s'", mModule->TypeToString(target.mType).c_str()), targetSrc); return BfTypedValue(); } diff --git a/IDEHelper/Compiler/BfModule.cpp b/IDEHelper/Compiler/BfModule.cpp index 861500df..863f73f1 100644 --- a/IDEHelper/Compiler/BfModule.cpp +++ b/IDEHelper/Compiler/BfModule.cpp @@ -2372,7 +2372,9 @@ bool BfModule::CheckProtection(BfProtectionCheckFlags& flags, BfTypeInstance* me auto mixinOwner = mCurMethodState->mMixinState->mMixinMethodInstance->GetOwner(); curCheckType = mixinOwner; } - bool allowPrivate = (memberOwner->mTypeDef == curCheckType->mTypeDef) || (IsInnerType(curCheckType->mTypeDef, memberOwner->mTypeDef)); + bool allowPrivate = (memberOwner->mTypeDef == curCheckType->mTypeDef); + if (curCheckType != NULL) + allowPrivate |= IsInnerType(curCheckType->mTypeDef, memberOwner->mTypeDef); if (allowPrivate) flags = (BfProtectionCheckFlags)(flags | BfProtectionCheckFlag_AllowPrivate | BfProtectionCheckFlag_CheckedPrivate); else diff --git a/IDEHelper/Compiler/BfModuleTypeUtils.cpp b/IDEHelper/Compiler/BfModuleTypeUtils.cpp index fd61dd55..bc43f103 100644 --- a/IDEHelper/Compiler/BfModuleTypeUtils.cpp +++ b/IDEHelper/Compiler/BfModuleTypeUtils.cpp @@ -2984,6 +2984,9 @@ bool BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy std::function splatIterate; splatIterate = [&](BfType* checkType) { + if (checkType->IsValueType()) + PopulateType(checkType, BfPopulateType_Data); + if (checkType->IsMethodRef()) { // For simplicitly, any methodRef inside a struct makes the struct non-splattable. This reduces cases of needing to @@ -2991,8 +2994,7 @@ bool BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy hadNonSplattable = true; } else if (checkType->IsStruct()) - { - PopulateType(checkType, BfPopulateType_Data); + { auto checkTypeInstance = checkType->ToTypeInstance(); if (checkTypeInstance->mBaseType != NULL) splatIterate(checkTypeInstance->mBaseType);