diff --git a/BeefBuild/BeefProj.toml b/BeefBuild/BeefProj.toml index 0fc316c1a..6c56885f0 100644 --- a/BeefBuild/BeefProj.toml +++ b/BeefBuild/BeefProj.toml @@ -40,7 +40,7 @@ TargetName = "$(ProjectName)_d" OtherLinkFlags = "$(LinkFlags) ./libIDEHelper_d.a ./libBeefySysLib_d.a ./libhunspell.so $(Var IDEHelperLibs) -Wl,-rpath -Wl,$ORIGIN" CLibType = "Dynamic" PreBuildCmds = ["ReadFile(\"$(WorkspaceDir)/../IDE/dist/IDEHelper_libs_d.txt\", \"IDEHelperLibs\")"] -DebugCommandArguments = "-proddir=..\\ -config=Debug -platform=Win64" +DebugCommandArguments = "-proddir=..\\ -config=Debug -platform=Linux64" DebugWorkingDirectory = "$(WorkspaceDir)/../IDE/dist" PreprocessorMacros = ["DEBUG", "CLI"] @@ -58,7 +58,7 @@ TargetDirectory = "$(WorkspaceDir)/../IDE/dist" OtherLinkFlags = "$(LinkFlags) ./libIDEHelper.a ./libBeefySysLib.a ./libhunspell.so $(Var IDEHelperLibs) -Wl,-rpath -Wl,$ORIGIN" CLibType = "Dynamic" PreBuildCmds = ["ReadFile(\"$(WorkspaceDir)/../IDE/dist/IDEHelper_libs.txt\", \"IDEHelperLibs\")"] -DebugCommandArguments = "-proddir=..\\ -config=Debug -platform=Win64" +DebugCommandArguments = "-proddir=..\\ -config=Debug -platform=Linux64" DebugWorkingDirectory = "$(WorkspaceDir)/../IDE/dist" PreprocessorMacros = ["CLI"] diff --git a/BeefLibs/Beefy2D/BeefProj.toml b/BeefLibs/Beefy2D/BeefProj.toml index f7bbe7043..f9dcf2f53 100644 --- a/BeefLibs/Beefy2D/BeefProj.toml +++ b/BeefLibs/Beefy2D/BeefProj.toml @@ -16,6 +16,12 @@ CLibType = "Static" BeefLibType = "Static" PostBuildCmds = ["CopyToDependents(\"$(ProjectDir)/dist/BeefySysLib64_d.dll\")", "CopyToDependents(\"$(ProjectDir)/dist/BeefySysLib64_d.pdb\")"] +[Configs.Debug.Linux64] +OtherLinkFlags = "$(LinkFlags) \"$(ProjectDir)/dist/libBeefySysLib_d.a\"" +CLibType = "Static" +BeefLibType = "Static" +PostBuildCmds = ["CopyToDependents(\"$(ProjectDir)/dist/libBeefySysLib_d.a\")"] + [Configs.Release.Win32] OtherLinkFlags = "" PreprocessorMacros = ["RELEASE", "BF32"] @@ -24,6 +30,12 @@ PreprocessorMacros = ["RELEASE", "BF32"] OtherLinkFlags = "$(LinkFlags) \"$(ProjectDir)/dist/BeefySysLib64.lib\"" PostBuildCmds = ["CopyToDependents(\"$(ProjectDir)/dist/BeefySysLib64.dll\")", "CopyToDependents(\"$(ProjectDir)/dist/BeefySysLib64.pdb\")"] +[Configs.Release.Linux64] +OtherLinkFlags = "$(LinkFlags) \"$(ProjectDir)/dist/libBeefySysLib.a\"" +CLibType = "Static" +BeefLibType = "Static" +PostBuildCmds = ["CopyToDependents(\"$(ProjectDir)/dist/libBeefySysLib.a\")"] + [Configs.Paranoid.Win32] CLibType = "Static" BeefLibType = "Static" diff --git a/BeefLibs/Beefy2D/src/BFWindow.bf b/BeefLibs/Beefy2D/src/BFWindow.bf index f5cbe2a56..5079a0465 100644 --- a/BeefLibs/Beefy2D/src/BFWindow.bf +++ b/BeefLibs/Beefy2D/src/BFWindow.bf @@ -52,6 +52,7 @@ namespace Beefy AcceptFiles = 0x1000'0000, NoShow = 0x2000'0000, NoMouse = 0x4000'0000, + Tooltip = 0x8000'0000 }; [AllowDuplicates] diff --git a/BeefLibs/Beefy2D/src/gfx/Font.bf b/BeefLibs/Beefy2D/src/gfx/Font.bf index 005c00b23..9e6557113 100644 --- a/BeefLibs/Beefy2D/src/gfx/Font.bf +++ b/BeefLibs/Beefy2D/src/gfx/Font.bf @@ -1,4 +1,5 @@ using System; +using System.Interop; using System.Collections; using System.Text; using System.IO; @@ -259,6 +260,86 @@ namespace Beefy.gfx List mFontEffectStack ~ delete _; DisposeProxy mFontEffectDisposeProxy = new .(new () => { PopFontEffect(); }) ~ delete _; +#if BF_PLATFORM_LINUX + + const String FONTCONFIG_LIB = "libfontconfig.so"; + static bool IsFontconfigAvailable { get; private set; } = true; + + [StaticInitPriority(100)] + private static class FontconfigAllowFail + { + static this() + { + Runtime.AddErrorHandler(new (stage, error) => { + if (stage == .PreFail) + { + if (let err = error as Runtime.LoadSharedLibraryError && err.mPath == FONTCONFIG_LIB) + { + SelfOuter.IsFontconfigAvailable = false; + return .Ignore; + } + } + + return .ContinueFailure; + }); + } + } + + struct FcConfig; + struct FcPattern; + struct FcObjectSet; + + [CRepr] + struct FcFontSet + { + public c_int nfont; + public c_int sfont; + public FcPattern** fonts; + } + + enum FcResult : c_int + { + Match, + NoMatch, + TypeMismatch, + NoId, + OutOfMemory + } + + const String FC_FAMILY = "family"; + const String FC_STYLE = "style"; + const String FC_WEIGHT = "weight"; + const String FC_SLANT = "slant"; + const String FC_FILE = "file"; + + [CLink, Import(FONTCONFIG_LIB)] + static extern c_int FcInit(); + [CLink, Import(FONTCONFIG_LIB)] + static extern void FcFini(); + + [CLink, Import(FONTCONFIG_LIB)] + static extern FcPattern* FcPatternCreate(); + [CLink, Import(FONTCONFIG_LIB)] + static extern void FcPatternDestroy(FcPattern* p); + + [CLink, Import(FONTCONFIG_LIB)] + static extern FcObjectSet* FcObjectSetBuild(c_char* first, ...); + [CLink, Import(FONTCONFIG_LIB)] + static extern void FcObjectSetDestroy(FcObjectSet* os); + + [CLink, Import(FONTCONFIG_LIB)] + static extern FcFontSet* FcFontList(FcConfig* config, FcPattern* p, FcObjectSet* os); + [CLink, Import(FONTCONFIG_LIB)] + static extern void FcFontSetDestroy(FcFontSet* fs); + + [CLink, Import(FONTCONFIG_LIB)] + static extern FcResult FcPatternGetString(FcPattern* p, c_char* object, c_int n, char8** s); + + [CLink, Import(FONTCONFIG_LIB)] + static extern FcResult FcPatternGetInteger(FcPattern* p, c_char* object, c_int n, c_int *i); + +#endif + public this() { } @@ -288,84 +369,156 @@ namespace Beefy.gfx static void BuildFontNameCache() { -#if BF_PLATFORM_WINDOWS using (sMonitor.Enter()) { sFontNameMap = new .(); - for (int pass < 2) +#if BF_PLATFORM_WINDOWS + + for (int pass < 2) + { + Windows.HKey hkey; + + if (pass == 0) { - Windows.HKey hkey; + if (Windows.RegOpenKeyExA(Windows.HKEY_LOCAL_MACHINE, @"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts", 0, + Windows.KEY_QUERY_VALUE | Windows.KEY_WOW64_32KEY | Windows.KEY_ENUMERATE_SUB_KEYS, out hkey) != Windows.S_OK) + continue; + } + else + { + if (Windows.RegOpenKeyExA(Windows.HKEY_CURRENT_USER, @"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts", 0, + Windows.KEY_QUERY_VALUE | Windows.KEY_WOW64_32KEY | Windows.KEY_ENUMERATE_SUB_KEYS, out hkey) != Windows.S_OK) + continue; + } - if (pass == 0) + defer Windows.RegCloseKey(hkey); + + for (int32 i = 0; true; i++) + { + char16[256] fontNameArr; + uint32 nameLen = 255; + uint32 valType = 0; + + char16[256] data; + uint32 dataLen = 256 * 2; + int32 result = Windows.RegEnumValueW(hkey, i, &fontNameArr, &nameLen, null, &valType, &data, &dataLen); + if (result == 0) { - if (Windows.RegOpenKeyExA(Windows.HKEY_LOCAL_MACHINE, @"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts", 0, - Windows.KEY_QUERY_VALUE | Windows.KEY_WOW64_32KEY | Windows.KEY_ENUMERATE_SUB_KEYS, out hkey) != Windows.S_OK) - continue; + if (valType == 1) + { + String fontName = scope String(&fontNameArr); + int parenPos = fontName.IndexOf(" ("); + if (parenPos != -1) + fontName.RemoveToEnd(parenPos); + fontName.ToUpper(); + String fontPath = scope String(&data); + if ((!fontPath.EndsWith(".TTF", .OrdinalIgnoreCase)) && (!fontPath.EndsWith(".TTC", .OrdinalIgnoreCase))) + continue; + + if (fontName.Contains('&')) + { + int collectionIdx = 0; + for (var namePart in fontName.Split('&', .RemoveEmptyEntries)) + { + namePart.Trim(); + if (sFontNameMap.TryAddAlt(namePart, var keyPtr, var valuePtr)) + { + *keyPtr = new String(namePart); + *valuePtr = new $"{fontPath}@{collectionIdx}"; + collectionIdx++; + } + } + } + else if (sFontNameMap.TryAdd(fontName, var keyPtr, var valuePtr)) + { + *keyPtr = new String(fontName); + *valuePtr = new String(fontPath); + } + } } else { - if (Windows.RegOpenKeyExA(Windows.HKEY_CURRENT_USER, @"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts", 0, - Windows.KEY_QUERY_VALUE | Windows.KEY_WOW64_32KEY | Windows.KEY_ENUMERATE_SUB_KEYS, out hkey) != Windows.S_OK) + if (result == Windows.ERROR_MORE_DATA) continue; + + break; } + } + } +#elif BF_PLATFORM_LINUX + if (!IsFontconfigAvailable) + return; - defer Windows.RegCloseKey(hkey); + if (FcInit() == 0) + return; + + defer FcFini(); + + let pattern = FcPatternCreate(); + defer FcPatternDestroy(pattern); + let objectset = FcObjectSetBuild(FC_FAMILY, FC_STYLE, FC_WEIGHT, FC_SLANT, FC_FILE, null); + defer FcObjectSetDestroy(objectset); - for (int32 i = 0; true; i++) + let fontSet = FcFontList(null, pattern, objectset); + if (fontSet == null) + return; + defer FcFontSetDestroy(fontSet); + + for (let i < fontSet.nfont) + { + let font = fontSet.fonts[i]; + + char8* pFile = null; + if (FcPatternGetString(font, FC_FILE, 0, &pFile) != .Match) + { + continue; + } + + char8* pFamily = null; + if (FcPatternGetString(font, FC_FAMILY, 0, &pFamily) != .Match) + { + continue; + } + + char8* pStyle = null; + FcPatternGetString(font, FC_STYLE, 0, &pStyle); + c_int weight = 0; + let isRegular = (FcPatternGetInteger(font, FC_WEIGHT, 0, &weight) == .Match) && (weight == 80); + c_int slant = 0; + let isRoman = (FcPatternGetInteger(font, FC_SLANT, 0, &slant) == .Match) && (slant == 0); + + let filepath = StringView(pFile); + let familyName = StringView(pFamily); + + String fontName = scope .(48); + fontName.Append(familyName); + if (pStyle != null) { - char16[256] fontNameArr; - uint32 nameLen = 255; - uint32 valType = 0; - - char16[256] data; - uint32 dataLen = 256 * 2; - int32 result = Windows.RegEnumValueW(hkey, i, &fontNameArr, &nameLen, null, &valType, &data, &dataLen); - if (result == 0) + if (isRegular && isRoman) { - if (valType == 1) + fontName.ToUpper(); + + if (sFontNameMap.TryAdd(fontName, let pKey, let pVal)) { - String fontName = scope String(&fontNameArr); - int parenPos = fontName.IndexOf(" ("); - if (parenPos != -1) - fontName.RemoveToEnd(parenPos); - fontName.ToUpper(); - String fontPath = scope String(&data); - if ((!fontPath.EndsWith(".TTF", .OrdinalIgnoreCase)) && (!fontPath.EndsWith(".TTC", .OrdinalIgnoreCase))) - continue; - - if (fontName.Contains('&')) - { - int collectionIdx = 0; - for (var namePart in fontName.Split('&', .RemoveEmptyEntries)) - { - namePart.Trim(); - if (sFontNameMap.TryAddAlt(namePart, var keyPtr, var valuePtr)) - { - *keyPtr = new String(namePart); - *valuePtr = new $"{fontPath}@{collectionIdx}"; - collectionIdx++; - } - } - } - else if (sFontNameMap.TryAdd(fontName, var keyPtr, var valuePtr)) - { - *keyPtr = new String(fontName); - *valuePtr = new String(fontPath); - } + (*pKey) = new String(fontName); + (*pVal) = new String(filepath); } } - else - { - if (result == Windows.ERROR_MORE_DATA) - continue; - break; - } + fontName..Append(' ').Append(pStyle); + } + + fontName.ToUpper(); + + if (sFontNameMap.TryAdd(fontName, let pKey, let pVal)) + { + (*pKey) = new String(fontName); + (*pVal) = new String(filepath); } - } - } + } #endif + } } public static void ClearFontNameCache() @@ -536,9 +689,9 @@ namespace Beefy.gfx BuildFontNameCache(); String pathStr; let lookupStr = scope String(fontName)..ToUpper(); -#if BF_PLATFORM_WINDOWS if (sFontNameMap.TryGetValue(lookupStr, out pathStr)) { +#if BF_PLATFORM_WINDOWS if (!pathStr.Contains(':')) { char8[256] windowsDir; @@ -546,14 +699,26 @@ namespace Beefy.gfx path.Append(&windowsDir); path.Append(@"\Fonts\"); } +#endif path.Append(pathStr); return; } -#endif if ((sFontFailMap != null) && (sFontFailMap.TryGetValue(lookupStr, out pathStr))) { - path.Append(pathStr); + StringView lastPath = pathStr; + for (let fontFailName in pathStr.Split('\0', .RemoveEmptyEntries)) + { + let failName = scope String(fontFailName)..ToUpper(); + if (sFontNameMap.TryGetValue(failName, out pathStr)) + { + GetFontPath(fontFailName, path); + return; + } + lastPath = fontFailName; + } + + path.Append(lastPath); return; } } diff --git a/BeefLibs/Beefy2D/src/theme/dark/DarkTooltip.bf b/BeefLibs/Beefy2D/src/theme/dark/DarkTooltip.bf index 3fe64668b..abf95b828 100644 --- a/BeefLibs/Beefy2D/src/theme/dark/DarkTooltip.bf +++ b/BeefLibs/Beefy2D/src/theme/dark/DarkTooltip.bf @@ -71,7 +71,7 @@ namespace Beefy.theme.dark screenX += relWidget.mWidgetWindow.mClientX; screenY += relWidget.mWidgetWindow.mClientY; - BFWindow.Flags windowFlags = BFWindow.Flags.ClientSized | BFWindow.Flags.PopupPosition | BFWindow.Flags.NoActivate | BFWindow.Flags.DestAlpha; + BFWindow.Flags windowFlags = BFWindow.Flags.ClientSized | BFWindow.Flags.PopupPosition | BFWindow.Flags.NoActivate | BFWindow.Flags.DestAlpha | BFWindow.Flags.Tooltip; WidgetWindow widgetWindow = new WidgetWindow(relWidget.mWidgetWindow, "Tooltip", (int32)(screenX), (int32)(screenY), diff --git a/BeefLibs/corlib/src/IO/FolderBrowserDialog.bf b/BeefLibs/corlib/src/IO/FolderBrowserDialog.bf index 7c8e6d044..101a83842 100644 --- a/BeefLibs/corlib/src/IO/FolderBrowserDialog.bf +++ b/BeefLibs/corlib/src/IO/FolderBrowserDialog.bf @@ -246,7 +246,14 @@ public class FolderBrowserDialog : CommonDialog } set { - mInitialDir.Set(value); + if(mInitialDir == null) + { + mInitialDir = new .(value); + } + else + { + mInitialDir.Set(value); + } } } diff --git a/BeefTools/ImgCreate/CMakeLists.txt b/BeefTools/ImgCreate/CMakeLists.txt new file mode 100644 index 000000000..b806eb30e --- /dev/null +++ b/BeefTools/ImgCreate/CMakeLists.txt @@ -0,0 +1,109 @@ +cmake_minimum_required(VERSION 3.5) + +################### Variables. #################### +# Change if you want modify path or other values. # +################################################### + +set(PROJECT_NAME ImgCreate) +# Output Variables +set(OUTPUT_DEBUG Debug/bin) +set(CMAKE_DEBUG_POSTFIX "_d") +set(OUTPUT_RELEASE Release/bin) + +############## CMake Project ################ +# The main options of project # +############################################# + +project(${PROJECT_NAME} CXX C) + +# Define Debug by default. +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Debug") + message(STATUS "Build type not specified: Use Debug by default.") +endif(NOT CMAKE_BUILD_TYPE) + +include_directories( + . + ../../ + ../../BeefySysLib/ +) + +file(GLOB SRC_FILES + ../../BeefySysLib/Common.cpp + ../../BeefySysLib/BFApp.cpp + ../../BeefySysLib/BFWindow.cpp + ../../BeefySysLib/DataStream.cpp + ../../BeefySysLib/FileStream.cpp + ../../BeefySysLib/util/Hash.cpp + ../../BeefySysLib/util/UTF8.cpp + ../../BeefySysLib/util/String.cpp + ../../BeefySysLib/util/PerfTimer.cpp + ../../BeefySysLib/util/CubicFuncSpline.cpp + ../../BeefySysLib/util/Vector.cpp + ../../BeefySysLib/img/ImageAdjustments.cpp + ../../BeefySysLib/img/ImageData.cpp + ../../BeefySysLib/img/ImageUtils.cpp + ../../BeefySysLib/img/ImgEffects.cpp + ../../BeefySysLib/img/PNGData.cpp + ../../BeefySysLib/img/PSDReader.cpp + ../../BeefySysLib/third_party/png/png.c + ../../BeefySysLib/third_party/png/pngerror.c + ../../BeefySysLib/third_party/png/pngget.c + ../../BeefySysLib/third_party/png/pngmem.c + ../../BeefySysLib/third_party/png/pngpread.c + ../../BeefySysLib/third_party/png/pngread.c + ../../BeefySysLib/third_party/png/pngrio.c + ../../BeefySysLib/third_party/png/pngrtran.c + ../../BeefySysLib/third_party/png/pngrutil.c + ../../BeefySysLib/third_party/png/pngset.c + ../../BeefySysLib/third_party/png/pngtrans.c + ../../BeefySysLib/third_party/png/pngvcrd.c + ../../BeefySysLib/third_party/png/pngwio.c + ../../BeefySysLib/third_party/png/pngwrite.c + ../../BeefySysLib/third_party/png/pngwtran.c + ../../BeefySysLib/third_party/png/pngwutil.c + ../../BeefySysLib/third_party/utf8proc/utf8proc.c + ../../BeefySysLib/third_party/zlib/adler32.c + ../../BeefySysLib/third_party/zlib/compress.c + ../../BeefySysLib/third_party/zlib/crc32.c + ../../BeefySysLib/third_party/zlib/deflate.c + ../../BeefySysLib/third_party/zlib/gzio.c + ../../BeefySysLib/third_party/zlib/infblock.c + ../../BeefySysLib/third_party/zlib/infcodes.c + ../../BeefySysLib/third_party/zlib/inffast.c + ../../BeefySysLib/third_party/zlib/inflate.c + ../../BeefySysLib/third_party/zlib/inftrees.c + ../../BeefySysLib/third_party/zlib/infutil.c + ../../BeefySysLib/third_party/zlib/trees.c + ../../BeefySysLib/third_party/zlib/uncompr.c + ../../BeefySysLib/third_party/zlib/zutil.c + ../../BeefySysLib/third_party/putty/wildcard.c +) + +if (${WIN32}) + include_directories( + ../../BeefySysLib/platform/win32 + ) + file(GLOB SRC_FILES_OS + ../../BeefySysLib/platform/win32/BFPlatform.cpp + ../../BeefySysLib/platform/win32/Platform.cpp + ) +elseif (${APPLE}) + include_directories( + ../../BeefySysLib/platform/darwin + ) + file(GLOB SRC_FILES_OS + ../../BeefySysLib/platform/darwin/BFPlatform.cpp + ../../BeefySysLib/platform/darwin/DarwinCommon.cpp + ) +else() + include_directories( + ../../BeefySysLib/platform/linux + ) + file(GLOB SRC_FILES_OS + ../../BeefySysLib/platform/linux/BFPlatform.cpp + ../../BeefySysLib/platform/linux/LinuxCommon.cpp + ) +endif() + +add_executable(${PROJECT_NAME} ImgCreate.cpp ${SRC_FILES_OS} ${SRC_FILES}) \ No newline at end of file diff --git a/BeefTools/ImgCreate/ImgCreate.cpp b/BeefTools/ImgCreate/ImgCreate.cpp index 7f4cb08b4..c5a12ca75 100644 --- a/BeefTools/ImgCreate/ImgCreate.cpp +++ b/BeefTools/ImgCreate/ImgCreate.cpp @@ -2,7 +2,6 @@ #include "BeefySysLib/util/Array.h" #include "BeefySysLib/img/PSDReader.h" #include "BeefySysLib/img/PNGData.h" -#include USING_NS_BF; @@ -290,8 +289,11 @@ int main() }; if (isThemeDir) - _mkdir("cache"); - + { + BfpFileResult result; + BfpDirectory_Create("cache", &result); + } + for (int size = 0; size < 3; size++) { int scale = 1 << size; diff --git a/BeefTools/ImgCreate/build.sh b/BeefTools/ImgCreate/build.sh new file mode 100755 index 000000000..c8da72e9a --- /dev/null +++ b/BeefTools/ImgCreate/build.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +PATH=/usr/local/bin:$PATH:$HOME/bin +SCRIPTPATH=$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P) + +echo Building from $SCRIPTPATH +cd $SCRIPTPATH + +if command -v ninja >/dev/null 2>&1 ; then + if [ ! -d build ] || [ -f build/build.ninja ]; then + USE_NINJA="-GNinja" + else + echo "Ninja couldn't be enabled for this build, consider doing a clean build to start using Ninja for faster build speeds." + fi +fi + +if [ ! -d build ]; then + mkdir build +fi + +cd build +cmake $USE_NINJA -DCMAKE_BUILD_TYPE=RelWithDebInfo ../ +cmake --build . + +cd $SCRIPTPATH/../../IDE/dist/images +ln -s -f $SCRIPTPATH/build/ImgCreate ImgCreate \ No newline at end of file diff --git a/BeefySysLib/BFWindow.h b/BeefySysLib/BFWindow.h index 5196dcdfd..bd5a846c1 100644 --- a/BeefySysLib/BFWindow.h +++ b/BeefySysLib/BFWindow.h @@ -57,7 +57,8 @@ enum BFWINDOW_ALLOW_FULLSCREEN = 0x8000000, BFWINDOW_ACCEPTFILES = 0x10000000, BFWINDOW_NOSHOW = 0x20000000, - BFWINDOW_NO_MOUSE = 0x40000000 + BFWINDOW_NO_MOUSE = 0x40000000, + BFWINDOW_TOOLTIP = 0x80000000 }; diff --git a/BeefySysLib/CMakeLists.txt b/BeefySysLib/CMakeLists.txt index 08ee16ffb..a7cc08bc6 100644 --- a/BeefySysLib/CMakeLists.txt +++ b/BeefySysLib/CMakeLists.txt @@ -327,10 +327,11 @@ else() endif() if (DEFINED BF_ENABLE_SDL) - file(GLOB SRC_FILES_OS + file(GLOB SRC_FILES_SDL platform/sdl/SdlBFApp.cpp platform/sdl/GLRenderDevice.cpp ) + list(APPEND SRC_FILES_OS ${SRC_FILES_SDL}) endif() # Add library to build. diff --git a/BeefySysLib/platform/linux/LinuxCommon.h b/BeefySysLib/platform/linux/LinuxCommon.h index 466ce3552..680d899c4 100644 --- a/BeefySysLib/platform/linux/LinuxCommon.h +++ b/BeefySysLib/platform/linux/LinuxCommon.h @@ -25,6 +25,7 @@ #include #include #include +#include //#define offsetof(type, member) __builtin_offsetof (type, member) diff --git a/BeefySysLib/platform/sdl/GLRenderDevice.cpp b/BeefySysLib/platform/sdl/GLRenderDevice.cpp index 28d3a5583..160031630 100644 --- a/BeefySysLib/platform/sdl/GLRenderDevice.cpp +++ b/BeefySysLib/platform/sdl/GLRenderDevice.cpp @@ -1,9 +1,10 @@ #include "GLRenderDevice.h" +#include "BFApp.h" #include "SdlBFApp.h" #include "BFWindow.h" #include "img/ImageData.h" #include "util/PerfTimer.h" -#include +#include USING_NS_BF; @@ -11,7 +12,7 @@ USING_NS_BF; #define NOT_IMPL throw "Not implemented" #endif -//#pragma comment(lib, "SDL2.lib") +//#pragma comment(lib, "SDL3.lib") #ifdef _WIN32 #ifdef BF_PLATFORM_OPENGL_ES2 @@ -39,8 +40,9 @@ USING_NS_BF; #endif extern void* (SDLCALL* bf_SDL_GL_GetProcAddress)(const char* proc); -extern void (SDLCALL* bf_SDL_GetWindowSize)(SDL_Window* window, int* w, int* h); -extern void (SDLCALL* bf_SDL_GL_SwapWindow)(SDL_Window* window); +extern bool (SDLCALL* bf_SDL_GetWindowSize)(SDL_Window* window, int* w, int* h); +extern bool (SDLCALL* bf_SDL_GL_SwapWindow)(SDL_Window* window); +extern bool (SDLCALL* bf_SDL_GL_MakeCurrent)(SDL_Window* window, SDL_GLContext context); typedef void (APIENTRYP GL_DEBUGPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam); @@ -427,6 +429,8 @@ GLRenderWindow::~GLRenderWindow() void GLRenderWindow::PhysSetAsTarget() { + bf_SDL_GL_MakeCurrent(mSDLWindow, ((SdlBFApp*)gBFApp)->mGLContext ); + GLfloat matrix[4][4]; CreateOrthographicOffCenter(0.0f, (float)mWidth, (float)mHeight, 0.0f, -100.0f, 100.0f, matrix); glViewport(0, 0, (GLsizei)mWidth, (GLsizei)mHeight); @@ -439,6 +443,7 @@ void GLRenderWindow::SetAsTarget() //TODO: Handle this more elegantly when we actually handle draw layers properly... //if (mRenderDevice->mCurRenderTarget != NULL) //mRenderDevice->mCurDrawLayer->Flush(); + bf_SDL_GL_MakeCurrent(mSDLWindow, ((SdlBFApp*)gBFApp)->mGLContext ); mHasBeenTargeted = true; mRenderDevice->mCurRenderTarget = this; diff --git a/BeefySysLib/platform/sdl/GLRenderDevice.h b/BeefySysLib/platform/sdl/GLRenderDevice.h index 241ff57fc..db072d50f 100644 --- a/BeefySysLib/platform/sdl/GLRenderDevice.h +++ b/BeefySysLib/platform/sdl/GLRenderDevice.h @@ -4,9 +4,9 @@ #include "util/Dictionary.h" #ifdef BF_PLATFORM_OPENGL_ES2 -#include +#include #else -#include +#include #endif #include "gfx/Shader.h" diff --git a/BeefySysLib/platform/sdl/SdlBFApp.cpp b/BeefySysLib/platform/sdl/SdlBFApp.cpp index d310f6db7..90c62da26 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.cpp +++ b/BeefySysLib/platform/sdl/SdlBFApp.cpp @@ -1,7 +1,26 @@ #include "SdlBFApp.h" +#include "BFApp.h" +#include "Common.h" #include "GLRenderDevice.h" #include "platform/PlatformHelper.h" -#include +#include "platform/PlatformInterface.h" +#include "img/PNGData.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include USING_NS_BF; @@ -10,39 +29,107 @@ USING_NS_BF; #pragma comment(lib, "imm32.lib") #pragma comment(lib, "version.lib") -SDL_Window* (SDLCALL* bf_SDL_CreateWindow)(const char* title, int x, int y, int w, int h, Uint32 flags); -int (SDLCALL* bf_SDL_GL_SetAttribute)(SDL_GLattr attr, int value); -Uint32 (SDLCALL* bf_SDL_GetWindowID)(SDL_Window* window); +bool (SDLCALL* bf_SDL_Init)(SDL_InitFlags flags); +void (SDLCALL* bf_SDL_Quit)(void); +void* (SDLCALL* bf_SDL_malloc)(size_t size); +void (SDLCALL* bf_SDL_free)(void* mem); +void (SDLCALL* bf_SDL_memset)(void* dest, int c, size_t len); +void* (SDLCALL* bf_SDL_memcpy)(void *dst, const void *src, size_t len); + +SDL_PropertiesID (SDLCALL* bf_SDL_CreateProperties)(void); +bool (SDLCALL* bf_SDL_SetNumberProperty)(SDL_PropertiesID props, const char* name, int64_t value); +bool (SDLCALL* bf_SDL_SetBooleanProperty)(SDL_PropertiesID props, const char* name, bool value); +bool (SDLCALL* bf_SDL_SetStringProperty)(SDL_PropertiesID props, const char* name, const char* value); +bool (SDLCALL* bf_SDL_SetPointerProperty)(SDL_PropertiesID props, const char *name, void *value); + +SDL_Window* (SDLCALL* bf_SDL_CreateWindowWithProperties)(SDL_PropertiesID props); +SDL_WindowID (SDLCALL* bf_SDL_GetWindowID)(SDL_Window* window); void (SDLCALL* bf_SDL_DestroyWindow)(SDL_Window* window); -int (SDLCALL* bf_SDL_Init)(Uint32 flags); -void (SDLCALL* bf_SDL_GetWindowPosition)(SDL_Window* window,int* x, int* y); +bool (SDLCALL* bf_SDL_GetWindowPosition)(SDL_Window* window,int* x, int* y); +bool (SDLCALL* bf_SDL_SetWindowPosition)(SDL_Window* window, int x, int y); +bool (SDLCALL* bf_SDL_GetWindowSize)(SDL_Window* window, int* w, int* h); +bool (SDLCALL* bf_SDL_SetWindowSize)(SDL_Window* window, int w, int h); +bool (SDLCALL* bf_SDL_SetWindowMinimumSize)(SDL_Window* window, int min_w, int min_h); +bool (SDLCALL* bf_SDL_SetWindowTitle)(SDL_Window* window, const char* title); +bool (SDLCALL* bf_SDL_SetWindowIcon)(SDL_Window* window, SDL_Surface* icon); +SDL_Window* (SDLCALL* bf_SDL_GetMouseFocus)(); + +bool (SDLCALL* bf_SDL_ShowCursor)(void); +bool (SDLCALL* bf_SDL_HideCursor)(void); + char* (SDLCALL* bf_SDL_GetClipboardText)(void); -int (SDLCALL* bf_SDL_SetClipboardText)(const char* text); -void* (SDLCALL* bf_SDL_GL_GetProcAddress)(const char* proc); -void (SDLCALL* bf_SDL_GetWindowSize)(SDL_Window* window, int* w, int* h); -void (SDLCALL* bf_SDL_GL_SwapWindow)(SDL_Window* window); -void (SDLCALL* bf_SDL_free)(void* mem); -void (SDLCALL* bf_SDL_SetWindowPosition)(SDL_Window* window, int x, int y); -int (SDLCALL* bf_SDL_PollEvent)(SDL_Event* event); +bool (SDLCALL* bf_SDL_SetClipboardText)(const char* text); +void* (SDLCALL* bf_SDL_GetClipboardData)(const char *mime_type, size_t *size); +bool (SDLCALL* bf_SDL_SetClipboardData)(SDL_ClipboardDataCallback callback, SDL_ClipboardCleanupCallback cleanup, void *userdata, const char **mime_types, size_t num_mime_types); +bool (SDLCALL* bf_SDL_StartTextInput)(SDL_Window* window); +bool (SDLCALL* bf_SDL_StopTextInput)(SDL_Window* window); + +bool (SDLCALL* bf_SDL_PollEvent)(SDL_Event* event); +bool (SDLCALL* bf_SDL_PushEvent)(SDL_Event* event); +void (SDLCALL* bf_SDL_PumpEvents)(void); +int (SDLCALL* bf_SDL_PeepEvents)(SDL_Event *events, int numevents, SDL_EventAction action, Uint32 minType, Uint32 maxType); +bool (SDLCALL* bf_SDL_SetError)(const char *fmt, ...); const char* (SDLCALL* bf_SDL_GetError)(void); + +bool (SDLCALL* bf_SDL_SetHint)(const char *name, const char *value); + +SDL_DisplayID (SDLCALL* bf_SDL_GetPrimaryDisplay)(void); +SDL_DisplayID* (SDLCALL* bf_SDL_GetDisplays)(int* count); +bool (SDLCALL* bf_SDL_GetDisplayBounds)(SDL_DisplayID displayID, SDL_Rect* rect); +SDL_DisplayMode* (SDLCALL* bf_SDL_GetDesktopDisplayMode)(SDL_DisplayID displayID); +bool (SDLCALL* bf_SDL_HasRectIntersection)(const SDL_Rect* A, const SDL_Rect* B); + +SDL_Surface* (SDLCALL* bf_SDL_CreateSurfaceFrom)(int width, int height, SDL_PixelFormat format, void *pixels, int pitch); + SDL_GLContext (SDLCALL* bf_SDL_GL_CreateContext)(SDL_Window* window); -void (SDLCALL* bf_SDL_Quit)(void); +bool (SDLCALL* bf_SDL_GL_MakeCurrent)(SDL_Window* window, SDL_GLContext context); +bool (SDLCALL* bf_SDL_GL_SetAttribute)(SDL_GLAttr attr, int value); +void* (SDLCALL* bf_SDL_GL_GetProcAddress)(const char* proc); +bool (SDLCALL* bf_SDL_GL_SwapWindow)(SDL_Window* window); + +static SDL_Cursor* (SDLCALL* bf_SDL_GetDefaultCursor)(); +static SDL_Cursor* (SDLCALL* bf_SDL_CreateSystemCursor)(SDL_SystemCursor id); +static bool (SDLCALL* bf_SDL_SetCursor)(SDL_Cursor* cursor); + +struct AdjustedMonRect +{ + int mMonCount; + int mX; + int mY; + int mWidth; + int mHeight; +}; + +static int bfMouseBtnOf[4] = {NULL, 0, 2, 1}; // Translate SDL mouse buttons to what Beef expects. + +static const char* mimeTypes[] = +{ + "text/plain;charset=utf-8", + "text/vnd.beeflang.bf-text", + "text/vnd.beeflang.file-list" +}; static HMODULE gSDLModule; +static HMODULE gSDLImageModule; static HMODULE GetSDLModule(const StringImpl& installDir) { if (gSDLModule == NULL) { - String loadPath = installDir + "SDL2.dll"; +#if defined (BF_PLATFORM_WINDOWS) + String loadPath = installDir + "SDL3.dll"; gSDLModule = ::LoadLibraryA(loadPath.c_str()); +#elif defined (BF_PLATFORM_LINUX) + String loadPath = "libSDL3.so"; + gSDLModule = dlopen(loadPath.c_str(), RTLD_LAZY); +#endif if (gSDLModule == NULL) { #ifdef BF_PLATFORM_WINDOWS - ::MessageBoxA(NULL, "Failed to load SDL2.dll", "FATAL ERROR", MB_OK | MB_ICONERROR); + ::MessageBoxA(NULL, "Failed to load SDL3.dll", "FATAL ERROR", MB_OK | MB_ICONERROR); ::ExitProcess(1); #endif - BF_FATAL("Failed to load SDL2.dll"); + BF_FATAL("Failed to load libSDL3.so"); } } return gSDLModule; @@ -51,24 +138,70 @@ static HMODULE GetSDLModule(const StringImpl& installDir) template static void BFGetSDLProc(T& proc, const char* name, const StringImpl& installDir) { +#if defined (BF_PLATFORM_WINDOWS) proc = (T)::GetProcAddress(GetSDLModule(installDir), name); +#elif defined (BF_PLATFORM_LINUX) + proc = (T)dlsym(GetSDLModule(installDir), name); +#endif } #define BF_GET_SDLPROC(name) BFGetSDLProc(bf_##name, #name, mInstallDir) +static SDL_Surface* gAppIconSurface; + +BF_EXPORT void BF_CALLTYPE BFApp_RegisterAppIcon(uint8* imageData, int size) +{ + PNGData* image = new PNGData(); + image->LoadFromMemory(imageData, size); + gAppIconSurface = bf_SDL_CreateSurfaceFrom(image->mWidth, image->mHeight, SDL_PIXELFORMAT_ABGR8888, image->mBits, image->mWidth*4); +} + SdlBFWindow::SdlBFWindow(BFWindow* parent, const StringImpl& title, int x, int y, int width, int height, int windowFlags) { - int sdlWindowFlags = 0; - if (windowFlags & BFWINDOW_RESIZABLE) - sdlWindowFlags |= SDL_WINDOW_RESIZABLE; - sdlWindowFlags |= SDL_WINDOW_OPENGL; - if (windowFlags & BFWINDOW_FULLSCREEN) - sdlWindowFlags |= SDL_WINDOW_FULLSCREEN; + SDL_PropertiesID props = bf_SDL_CreateProperties(); + + bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN, true); + bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_RESIZABLE_BOOLEAN, (windowFlags & BFWINDOW_RESIZABLE) > 0); + bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_FULLSCREEN_BOOLEAN, (windowFlags & BFWINDOW_FULLSCREEN) > 0); + bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_BORDERLESS_BOOLEAN, (windowFlags & BFWINDOW_BORDER) == 0); + bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_TOOLTIP_BOOLEAN, (windowFlags & BFWINDOW_TOOLTIP) > 0); + bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_TRANSPARENT_BOOLEAN, (windowFlags & BFWINDOW_DEST_ALPHA) > 0); + bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_MENU_BOOLEAN, (windowFlags & BFWINDOW_FAKEFOCUS) > 0); + bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_FOCUSABLE_BOOLEAN, (windowFlags & BFWINDOW_FAKEFOCUS) == 0); + bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_MODAL_BOOLEAN, (windowFlags & BFWINDOW_MODAL) > 0); + bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_ALWAYS_ON_TOP_BOOLEAN, (windowFlags & BFWINDOW_TOPMOST) > 0); + + if (parent != NULL) + { + SDL_Window* parentWindow = ((SdlBFWindow*)parent)->mSDLWindow; + bf_SDL_SetPointerProperty(props, SDL_PROP_WINDOW_CREATE_PARENT_POINTER, parentWindow); + if ((windowFlags & BFWINDOW_TOOLTIP | BFWINDOW_FAKEFOCUS) > 0) // Tooltips and menus have relative positioning to their parent. + { + int parentX, parentY; + bf_SDL_GetWindowPosition(parentWindow, &parentX, &parentY); + x -= parentX; + y -= parentY; + } + } + + if (windowFlags) #ifdef BF_PLATFORM_FULLSCREEN - sdlWindowFlags |= SDL_WINDOW_FULLSCREEN; + bf_SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_FULLSCREEN_BOOLEAN, true); #endif - mSDLWindow = bf_SDL_CreateWindow(title.c_str(), x, y, width, height, sdlWindowFlags); + bf_SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, x); + bf_SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, y); + bf_SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, width); + bf_SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, height); + bf_SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, title.c_str()); + + mSDLWindow = bf_SDL_CreateWindowWithProperties(props); + +// printf("Created %i : %s\n", bf_SDL_GetWindowID(mSDLWindow), title.c_str()); + + if (gAppIconSurface) + bf_SDL_SetWindowIcon(mSDLWindow, gAppIconSurface); + bf_SDL_StartTextInput(mSDLWindow); #ifndef BF_PLATFORM_OPENGL_ES2 bf_SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); @@ -76,20 +209,23 @@ SdlBFWindow::SdlBFWindow(BFWindow* parent, const StringImpl& title, int x, int y bf_SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); #endif - if (!bf_SDL_GL_CreateContext(mSDLWindow)) + if(((SdlBFApp*)gBFApp)->mGLContext == NULL) { - String str = StrFormat( -#ifdef BF_PLATFORM_OPENGL_ES2 - "Unable to create SDL OpenGLES context: %s" -#else - "Unable to create SDL OpenGL context: %s" -#endif - , bf_SDL_GetError()); - - - BF_FATAL(str.c_str()); - bf_SDL_Quit(); - exit(2); + if (!(((SdlBFApp*)gBFApp)->mGLContext = bf_SDL_GL_CreateContext(mSDLWindow))) + { + String str = StrFormat( + #ifdef BF_PLATFORM_OPENGL_ES2 + "Unable to create SDL OpenGLES context: %s" + #else + "Unable to create SDL OpenGL context: %s" + #endif + , bf_SDL_GetError()); + + + BF_FATAL(str.c_str()); + bf_SDL_Quit(); + exit(2); + } } #ifndef BF_PLATFORM_OPENGL_ES2 @@ -105,10 +241,13 @@ SdlBFWindow::SdlBFWindow(BFWindow* parent, const StringImpl& title, int x, int y #endif mIsMouseInside = false; + mIsMouseVisible = true; + mHasPositionInit = false; mRenderWindow = new GLRenderWindow((GLRenderDevice*)gBFApp->mRenderDevice, mSDLWindow); mRenderWindow->mWindow = this; gBFApp->mRenderDevice->AddRenderWindow(mRenderWindow); + mParent = parent; if (parent != NULL) parent->mChildren.push_back(this); } @@ -116,96 +255,124 @@ SdlBFWindow::SdlBFWindow(BFWindow* parent, const StringImpl& title, int x, int y SdlBFWindow::~SdlBFWindow() { if (mSDLWindow != NULL) - TryClose(); + Destroy(); } -bool SdlBFWindow::TryClose() +void SdlBFWindow::Destroy() { +// printf("Destroy %i\n", bf_SDL_GetWindowID(this->mSDLWindow)); + SdlBFApp* app = (SdlBFApp*)gBFApp; app->mSdlWindowMap.Remove(bf_SDL_GetWindowID(mSDLWindow)); + bf_SDL_StopTextInput(mSDLWindow); bf_SDL_DestroyWindow(mSDLWindow); mSDLWindow = NULL; - return true; } -static int SDLConvertScanCode(int scanCode) +bool SdlBFWindow::TryClose() +{ +// printf("TryClose %i\n", bf_SDL_GetWindowID(this->mSDLWindow)); + + mLostFocusFunc(this); + if(this->mParent != NULL) + { + mGotFocusFunc(this->mParent); + } + + gBFApp->RemoveWindow(this); + return mSDLWindow == NULL; +} + +static void RefreshMouseVisibility(SdlBFWindow* window) +{ + if (window->mIsMouseVisible) + { + bf_SDL_ShowCursor(); + } + else + { + bf_SDL_HideCursor(); + } +} + +static int SDLConvertKeyCode(SDL_Keycode scanCode) { - if ((scanCode >= SDL_SCANCODE_A) && (scanCode <= SDL_SCANCODE_Z)) - return (scanCode - SDL_SCANCODE_A) + 'A'; - if ((scanCode >= SDL_SCANCODE_1) && (scanCode <= SDL_SCANCODE_9)) - return (scanCode - SDL_SCANCODE_1) + '1'; + if ((scanCode >= SDLK_A) && (scanCode <= SDLK_Z)) + return (scanCode - SDLK_A) + 'A'; + if ((scanCode >= SDLK_1) && (scanCode <= SDLK_9)) + return (scanCode - SDLK_1) + '1'; switch (scanCode) { - case SDL_SCANCODE_9: return '0'; - case SDL_SCANCODE_CANCEL: return 0x03; - case SDL_SCANCODE_AC_BACK: return 0x08; - case SDL_SCANCODE_TAB: return 0x09; - case SDL_SCANCODE_CLEAR: return 0x0C; - case SDL_SCANCODE_RETURN: return 0x0D; - case SDL_SCANCODE_LSHIFT: return 0x10; - case SDL_SCANCODE_RSHIFT: return 0x10; - case SDL_SCANCODE_LCTRL: return 0x11; - case SDL_SCANCODE_RCTRL: return 0x11; - case SDL_SCANCODE_MENU: return 0x12; - case SDL_SCANCODE_PAUSE: return 0x13; - case SDL_SCANCODE_LANG1: return 0x15; - case SDL_SCANCODE_LANG2: return 0x15; - case SDL_SCANCODE_LANG3: return 0x17; - case SDL_SCANCODE_LANG4: return 0x18; - case SDL_SCANCODE_LANG5: return 0x19; - case SDL_SCANCODE_LANG6: return 0x19; - case SDL_SCANCODE_ESCAPE: return 0x1B; - case SDL_SCANCODE_SPACE: return 0x20; - case SDL_SCANCODE_PAGEUP: return 0x21; - case SDL_SCANCODE_PAGEDOWN: return 0x22; - case SDL_SCANCODE_END: return 0x23; - case SDL_SCANCODE_HOME: return 0x24; - case SDL_SCANCODE_LEFT: return 0x25; - case SDL_SCANCODE_UP: return 0x26; - case SDL_SCANCODE_RIGHT: return 0x27; - case SDL_SCANCODE_DOWN: return 0x28; - case SDL_SCANCODE_SELECT: return 0x29; - case SDL_SCANCODE_PRINTSCREEN: return 0x2A; - case SDL_SCANCODE_EXECUTE: return 0x2B; - case SDL_SCANCODE_INSERT: return 0x2D; - case SDL_SCANCODE_DELETE: return 0x2E; - case SDL_SCANCODE_HELP: return 0x2F; - case SDL_SCANCODE_LGUI: return 0x5B; - case SDL_SCANCODE_RGUI: return 0x5C; - case SDL_SCANCODE_KP_0: return 0x60; - case SDL_SCANCODE_KP_1: return 0x61; - case SDL_SCANCODE_KP_2: return 0x62; - case SDL_SCANCODE_KP_3: return 0x63; - case SDL_SCANCODE_KP_4: return 0x64; - case SDL_SCANCODE_KP_5: return 0x65; - case SDL_SCANCODE_KP_6: return 0x66; - case SDL_SCANCODE_KP_7: return 0x67; - case SDL_SCANCODE_KP_8: return 0x68; - case SDL_SCANCODE_KP_9: return 0x69; - case SDL_SCANCODE_KP_MULTIPLY: return 0x6A; - case SDL_SCANCODE_KP_PLUS: return 0x6B; - case SDL_SCANCODE_SEPARATOR: return 0x6C; - case SDL_SCANCODE_KP_MINUS: return 0x6D; - case SDL_SCANCODE_KP_PERIOD: return 0x6E; - case SDL_SCANCODE_KP_DIVIDE: return 0x6F; - case SDL_SCANCODE_F1: return 0x70; - case SDL_SCANCODE_F2: return 0x71; - case SDL_SCANCODE_F3: return 0x72; - case SDL_SCANCODE_F4: return 0x73; - case SDL_SCANCODE_F5: return 0x74; - case SDL_SCANCODE_F6: return 0x75; - case SDL_SCANCODE_F7: return 0x76; - case SDL_SCANCODE_F8: return 0x77; - case SDL_SCANCODE_F9: return 0x78; - case SDL_SCANCODE_F10: return 0x79; - case SDL_SCANCODE_F11: return 0x7A; - case SDL_SCANCODE_F12: return 0x7B; - case SDL_SCANCODE_NUMLOCKCLEAR: return 0x90; - case SDL_SCANCODE_SCROLLLOCK: return 0x91; - case SDL_SCANCODE_GRAVE: return 0xC0; - //case SDL_SCANCODE_COMMAND: return 0xF0; + case SDLK_CANCEL: return 0x03; + case SDLK_BACKSPACE: return 0x08; + case SDLK_TAB: return 0x09; + case SDLK_CLEAR: return 0x0C; + case SDLK_RETURN: return 0x0D; + case SDLK_LSHIFT: return 0x10; + case SDLK_RSHIFT: return 0x10; + case SDLK_LCTRL: return 0x11; + case SDLK_RCTRL: return 0x11; + case SDLK_LALT: return 0x12; + case SDLK_RALT: return 0x12; + case SDLK_PAUSE: return 0x13; + case SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LANG1): return 0x15; + case SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LANG2): return 0x15; + case SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LANG3): return 0x17; + case SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LANG4): return 0x18; + case SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LANG5): return 0x19; + case SDL_SCANCODE_TO_KEYCODE(SDL_SCANCODE_LANG6): return 0x19; + case SDLK_ESCAPE: return 0x1B; + case SDLK_SPACE: return 0x20; + case SDLK_PAGEUP: return 0x21; + case SDLK_PAGEDOWN: return 0x22; + case SDLK_END: return 0x23; + case SDLK_HOME: return 0x24; + case SDLK_LEFT: return 0x25; + case SDLK_UP: return 0x26; + case SDLK_RIGHT: return 0x27; + case SDLK_DOWN: return 0x28; + case SDLK_SELECT: return 0x29; + case SDLK_PRINTSCREEN: return 0x2A; + case SDLK_EXECUTE: return 0x2B; + case SDLK_INSERT: return 0x2D; + case SDLK_DELETE: return 0x2E; + case SDLK_HELP: return 0x2F; + case SDLK_LGUI: return 0x5B; + case SDLK_RGUI: return 0x5C; + case SDLK_KP_0: return 0x60; + case SDLK_KP_1: return 0x61; + case SDLK_KP_2: return 0x62; + case SDLK_KP_3: return 0x63; + case SDLK_KP_4: return 0x64; + case SDLK_KP_5: return 0x65; + case SDLK_KP_6: return 0x66; + case SDLK_KP_7: return 0x67; + case SDLK_KP_8: return 0x68; + case SDLK_KP_9: return 0x69; + case SDLK_KP_MULTIPLY: return 0x6A; + case SDLK_KP_PLUS: return 0x6B; + case SDLK_SEPARATOR: return 0x6C; + case SDLK_KP_MINUS: return 0x6D; + case SDLK_KP_PERIOD: return 0x6E; + case SDLK_KP_DIVIDE: return 0x6F; + case SDLK_F1: return 0x70; + case SDLK_F2: return 0x71; + case SDLK_F3: return 0x72; + case SDLK_F4: return 0x73; + case SDLK_F5: return 0x74; + case SDLK_F6: return 0x75; + case SDLK_F7: return 0x76; + case SDLK_F8: return 0x77; + case SDLK_F9: return 0x78; + case SDLK_F10: return 0x79; + case SDLK_F11: return 0x7A; + case SDLK_F12: return 0x7B; + case SDLK_NUMLOCKCLEAR: return 0x90; + case SDLK_SCROLLLOCK: return 0x91; + case SDLK_GRAVE: return 0xC0; + //case SDLK_COMMAND: return 0xF0; } return 0; } @@ -218,6 +385,7 @@ SdlBFApp::SdlBFApp() { mRunning = false; mRenderDevice = NULL; + mSdlClipboardData = new SdlClipboardData(); Beefy::String exePath; BfpGetStrHelper(exePath, [](char* outStr, int* inOutStrSize, BfpResult* result) @@ -238,35 +406,82 @@ SdlBFApp::SdlBFApp() mInstallDir += "/"; - if (bf_SDL_CreateWindow == NULL) + mIsControlDown = false; + + if (bf_SDL_Init == NULL) { - BF_GET_SDLPROC(SDL_CreateWindow); - BF_GET_SDLPROC(SDL_GL_SetAttribute); + BF_GET_SDLPROC(SDL_Init); + BF_GET_SDLPROC(SDL_Quit); + BF_GET_SDLPROC(SDL_malloc); + BF_GET_SDLPROC(SDL_free); + BF_GET_SDLPROC(SDL_memset); + BF_GET_SDLPROC(SDL_memcpy); + + BF_GET_SDLPROC(SDL_CreateProperties); + BF_GET_SDLPROC(SDL_SetNumberProperty); + BF_GET_SDLPROC(SDL_SetBooleanProperty); + BF_GET_SDLPROC(SDL_SetStringProperty); + BF_GET_SDLPROC(SDL_SetPointerProperty); + + BF_GET_SDLPROC(SDL_CreateWindowWithProperties); BF_GET_SDLPROC(SDL_GetWindowID); BF_GET_SDLPROC(SDL_DestroyWindow); - BF_GET_SDLPROC(SDL_Init); BF_GET_SDLPROC(SDL_GetWindowPosition); + BF_GET_SDLPROC(SDL_SetWindowPosition); + BF_GET_SDLPROC(SDL_GetWindowSize); + BF_GET_SDLPROC(SDL_SetWindowSize); + BF_GET_SDLPROC(SDL_SetWindowMinimumSize); + BF_GET_SDLPROC(SDL_SetWindowTitle); + BF_GET_SDLPROC(SDL_SetWindowIcon); + BF_GET_SDLPROC(SDL_GetMouseFocus); + + BF_GET_SDLPROC(SDL_ShowCursor); + BF_GET_SDLPROC(SDL_HideCursor); + BF_GET_SDLPROC(SDL_GetClipboardText); BF_GET_SDLPROC(SDL_SetClipboardText); - BF_GET_SDLPROC(SDL_GL_GetProcAddress); - BF_GET_SDLPROC(SDL_GetWindowSize); - BF_GET_SDLPROC(SDL_GL_SwapWindow); - BF_GET_SDLPROC(SDL_free); - BF_GET_SDLPROC(SDL_SetWindowPosition); + BF_GET_SDLPROC(SDL_GetClipboardData); + BF_GET_SDLPROC(SDL_SetClipboardData); + BF_GET_SDLPROC(SDL_StartTextInput); + BF_GET_SDLPROC(SDL_StopTextInput); + BF_GET_SDLPROC(SDL_PollEvent); + BF_GET_SDLPROC(SDL_PushEvent); + BF_GET_SDLPROC(SDL_PumpEvents); + BF_GET_SDLPROC(SDL_PeepEvents); + BF_GET_SDLPROC(SDL_SetError); BF_GET_SDLPROC(SDL_GetError); + + BF_GET_SDLPROC(SDL_SetHint); + + BF_GET_SDLPROC(SDL_GetPrimaryDisplay); + BF_GET_SDLPROC(SDL_GetDisplays); + BF_GET_SDLPROC(SDL_GetDisplayBounds); + BF_GET_SDLPROC(SDL_GetDesktopDisplayMode); + BF_GET_SDLPROC(SDL_HasRectIntersection); + + BF_GET_SDLPROC(SDL_CreateSurfaceFrom); + BF_GET_SDLPROC(SDL_GL_CreateContext); - BF_GET_SDLPROC(SDL_Quit); + BF_GET_SDLPROC(SDL_GL_MakeCurrent); + BF_GET_SDLPROC(SDL_GL_SetAttribute); + BF_GET_SDLPROC(SDL_GL_GetProcAddress); + BF_GET_SDLPROC(SDL_GL_SwapWindow); + + BF_GET_SDLPROC(SDL_GetDefaultCursor); + BF_GET_SDLPROC(SDL_CreateSystemCursor); + BF_GET_SDLPROC(SDL_SetCursor); } mDataDir = mInstallDir; - if (bf_SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) < 0) + if (bf_SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD) < 0) BF_FATAL(StrFormat("Unable to initialize SDL: %s", bf_SDL_GetError()).c_str()); } SdlBFApp::~SdlBFApp() { + delete mSdlClipboardData; } SdlBFWindow* SdlBFApp::GetSdlWindowFromId(uint32 id) @@ -281,6 +496,8 @@ void SdlBFApp::Init() mRunning = true; mInMsgProc = false; +// bf_SDL_SetHint(SDL_HINT_EVENT_LOGGING, "1"); + mRenderDevice = new GLRenderDevice(); mRenderDevice->Init(this); } @@ -302,54 +519,141 @@ void SdlBFApp::Run() switch (sdlEvent.type) { - case SDL_QUIT: - //gBFApp->RemoveWindow(sdlEvent.window); - Shutdown(); + case SDL_EVENT_WINDOW_CLOSE_REQUESTED: + { + SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.window.windowID); + if(sdlBFWindow != NULL) + if (sdlBFWindow->mCloseQueryFunc(sdlBFWindow) != 0) + gBFApp->RemoveWindow(sdlBFWindow); + } + break; + case SDL_EVENT_WINDOW_FOCUS_GAINED: + { + SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.window.windowID); + if(sdlBFWindow != NULL) + sdlBFWindow->mGotFocusFunc(sdlBFWindow); + } + break; + case SDL_EVENT_WINDOW_FOCUS_LOST: + { + SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.window.windowID); + if(sdlBFWindow != NULL) + { + sdlBFWindow->mLostFocusFunc(sdlBFWindow); + mIsControlDown = false; + } + } break; - case SDL_MOUSEBUTTONUP: + case SDL_EVENT_MOUSE_BUTTON_UP: { SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.button.windowID); if (sdlBFWindow != NULL) - sdlBFWindow->mMouseUpFunc(sdlBFWindow, sdlEvent.button.x, sdlEvent.button.y, sdlEvent.button.button); + sdlBFWindow->mMouseUpFunc(sdlBFWindow, sdlEvent.button.x, sdlEvent.button.y, bfMouseBtnOf[sdlEvent.button.button]); } break; - case SDL_MOUSEBUTTONDOWN: + case SDL_EVENT_MOUSE_BUTTON_DOWN: { SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.button.windowID); if (sdlBFWindow != NULL) - sdlBFWindow->mMouseDownFunc(sdlBFWindow, sdlEvent.button.x, sdlEvent.button.y, sdlEvent.button.button, 1); + sdlBFWindow->mMouseDownFunc(sdlBFWindow, sdlEvent.button.x, sdlEvent.button.y, bfMouseBtnOf[sdlEvent.button.button], sdlEvent.button.clicks); } break; - case SDL_MOUSEMOTION: + case SDL_EVENT_MOUSE_MOTION: { SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.button.windowID); if (sdlBFWindow != NULL) sdlBFWindow->mMouseMoveFunc(sdlBFWindow, sdlEvent.button.x, sdlEvent.button.y); } break; - case SDL_KEYDOWN: + case SDL_EVENT_MOUSE_WHEEL: + { + uint ucNumLines = 3; // Default + SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.wheel.windowID); + if(sdlBFWindow != NULL) + sdlBFWindow->mMouseWheelFunc(sdlBFWindow, sdlEvent.wheel.mouse_x, sdlEvent.wheel.mouse_y, sdlEvent.wheel.x, sdlEvent.wheel.y * (float)ucNumLines); + } + break; + case SDL_EVENT_WINDOW_MOUSE_ENTER: + { + SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.window.windowID); + if(sdlBFWindow != NULL) + RefreshMouseVisibility(sdlBFWindow); + } + break; + case SDL_EVENT_WINDOW_DESTROYED: + { + SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(bf_SDL_GetWindowID(bf_SDL_GetMouseFocus())); + if(sdlBFWindow != NULL) + RefreshMouseVisibility(sdlBFWindow); + } + break; + case SDL_EVENT_KEY_DOWN: { SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.key.windowID); if (sdlBFWindow != NULL) { - sdlBFWindow->mKeyDownFunc(sdlBFWindow, SDLConvertScanCode(sdlEvent.key.keysym.scancode), sdlEvent.key.repeat); + sdlBFWindow->mKeyDownFunc(sdlBFWindow, SDLConvertKeyCode(sdlEvent.key.key), sdlEvent.key.repeat); + switch (sdlEvent.key.key) // These keys are not handled by SDL_TEXTINPUT + { + case SDLK_RETURN: + sdlBFWindow->mKeyCharFunc(sdlBFWindow, '\n'); + break; + case SDLK_BACKSPACE: + sdlBFWindow->mKeyCharFunc(sdlBFWindow, mIsControlDown ? '\x7F' : '\b'); + break; + case SDLK_TAB: + sdlBFWindow->mKeyCharFunc(sdlBFWindow, '\t'); + break; + case SDLK_LCTRL: + mIsControlDown = true; + break; + + default:; + } } } break; - case SDL_TEXTINPUT: + case SDL_EVENT_TEXT_INPUT: { - SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.key.windowID); + SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.text.windowID); if (sdlBFWindow != NULL) { - sdlBFWindow->mKeyCharFunc(sdlBFWindow, *(wchar_t*)sdlEvent.text.text); + const auto wideString = Beefy::UTF8Decode(sdlEvent.text.text); + for (int i = 0; i < wideString.length(); i++) + sdlBFWindow->mKeyCharFunc(sdlBFWindow, wideString[i]); } } break; - case SDL_KEYUP: + case SDL_EVENT_KEY_UP: { SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.key.windowID); if (sdlBFWindow != NULL) - sdlBFWindow->mKeyUpFunc(sdlBFWindow, SDLConvertScanCode(sdlEvent.key.keysym.scancode)); + sdlBFWindow->mKeyUpFunc(sdlBFWindow, SDLConvertKeyCode(sdlEvent.key.key)); + + if (sdlEvent.key.key == SDLK_LCTRL) + mIsControlDown = false; + } + break; + case SDL_EVENT_WINDOW_MOVED: +// case SDL_EVENT_WINDOW_RESIZED: + case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: + { + SdlBFWindow* sdlBFWindow = GetSdlWindowFromId(sdlEvent.window.windowID); + if (sdlBFWindow != NULL) + { + if (sdlBFWindow->mHasPositionInit) + { + if (sdlEvent.type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) + { + sdlBFWindow->mRenderWindow->Resized(); + } + sdlBFWindow->mMovedFunc(sdlBFWindow); + } + else + { + sdlBFWindow->mHasPositionInit = true; + } + } } break; } @@ -369,7 +673,7 @@ void SdlBFApp::Draw() glDisable(GL_SCISSOR_TEST); glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); - glClearColor(1.0f, 0.0f, 1.0f, 1.0f); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); gPixelsDrawn = 0; @@ -396,21 +700,54 @@ void SdlBFWindow::GetPosition(int* x, int* y, int* width, int* height, int* clie { bf_SDL_GetWindowPosition(mSDLWindow, x, y); bf_SDL_GetWindowSize(mSDLWindow, width, height); - *clientWidth = *width; - *clientHeight = *height; + + if (clientX) + *clientX = *x; + if (clientY) + *clientY = *y; + if (clientWidth) + *clientWidth = *width; + if (clientHeight) + *clientHeight = *height; } void SdlBFApp::PhysSetCursor() { - + static SDL_Cursor* cursors[] = + { + bf_SDL_GetDefaultCursor(), + bf_SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_POINTER), + bf_SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_MOVE), + bf_SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_TEXT), + + bf_SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NOT_ALLOWED), + bf_SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_MOVE), + bf_SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NESW_RESIZE), + bf_SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NS_RESIZE), + bf_SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NWSE_RESIZE), + bf_SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_EW_RESIZE), + bf_SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAIT), + NULL + }; + + bf_SDL_SetCursor(cursors[mCursor]); } void SdlBFWindow::SetClientPosition(int x, int y) { bf_SDL_SetWindowPosition(mSDLWindow, x, y); +} + +void SdlBFWindow::Resize(int x, int y, int width, int height, ShowKind showKind) +{ + bf_SDL_SetWindowPosition(mSDLWindow, x, y); + bf_SDL_SetWindowSize(mSDLWindow, width, height); +} - if (mMovedFunc != NULL) - mMovedFunc(this); +void SdlBFWindow::GetPlacement(int* normX, int* normY, int* normWidth, int* normHeight, int* showKind) +{ + bf_SDL_GetWindowPosition(mSDLWindow, normX, normY); + bf_SDL_GetWindowSize(mSDLWindow, normWidth, normHeight); } void SdlBFWindow::SetAlpha(float alpha, uint32 destAlphaSrcMask, bool isMouseVisible) @@ -418,14 +755,29 @@ void SdlBFWindow::SetAlpha(float alpha, uint32 destAlphaSrcMask, bool isMouseVis // Not supported } -uint32 SdlBFApp::GetClipboardFormat(const StringImpl& format) +const char* SdlBFApp::GetClipboardFormat(const StringImpl& format) { - return /*CF_TEXT*/1; + if (format == "text" || format == "atext") + { + return "text/plain;charset=utf-8"; + } + if (format == "bf_text") + { + return "text/vnd.beeflang.bf-text"; + } + if (format == "code/file-list") + { + return "text/vnd.beeflang.file-list"; + } + return format.c_str(); } void* SdlBFApp::GetClipboardData(const StringImpl& format, int* size) { - return bf_SDL_GetClipboardText(); + size_t outSize; + void* data = bf_SDL_GetClipboardData(GetClipboardFormat(format), &outSize); + *size = outSize; + return data; } void SdlBFApp::ReleaseClipboardData(void* ptr) @@ -433,9 +785,39 @@ void SdlBFApp::ReleaseClipboardData(void* ptr) bf_SDL_free(ptr); } +const void* SDLClipboardCallback(void* userData, const char* mimeType, size_t* outSize) +{ + SdlClipboardData* clipboard = *(SdlClipboardData**)userData; + + void* data; + if (clipboard->TryGetValue(StringImpl::MakeRef(mimeType), &data)) + { + *outSize = strlen((const char*) data); + } + return data; +} + void SdlBFApp::SetClipboardData(const StringImpl& format, const void* ptr, int size, bool resetClipboard) { - bf_SDL_SetClipboardText((const char*)ptr); + void* buffer = bf_SDL_malloc(size); + if (buffer == NULL) + { + bf_SDL_SetError("Out of memory for clipboard"); + } + else + { + StringImpl mime = StringImpl::MakeRef(GetClipboardFormat(format)); + + void* previous; + if (mSdlClipboardData->TryGetValue(mime, &previous)) + { + bf_SDL_free(previous); + } + bf_SDL_memcpy(buffer, ptr, size); + (*mSdlClipboardData)[mime] = buffer; + + bf_SDL_SetClipboardData(SDLClipboardCallback, NULL, &mSdlClipboardData, mimeTypes, 3); + } } BFMenu* SdlBFWindow::AddMenuItem(BFMenu* parent, int insertIdx, const char* text, const char* hotKey, BFSysBitmap* bitmap, bool enabled, int checkState, bool radioCheck) @@ -458,6 +840,29 @@ void SdlBFWindow::ModalsRemoved() //::SetFocus(mHWnd); } +void SdlBFWindow::SetTitle(const char* title) +{ + bf_SDL_SetWindowTitle(mSDLWindow, title); +} + +void SdlBFWindow::SetMinimumSize(int minWidth, int minHeight, bool clientSized) +{ + bf_SDL_SetWindowMinimumSize(mSDLWindow, minWidth, minHeight); +} + +void SdlBFWindow::SetMouseVisible(bool isMouseVisible) +{ + mIsMouseVisible = isMouseVisible; + if (isMouseVisible) + { + bf_SDL_ShowCursor(); + } + else + { + bf_SDL_HideCursor(); + } +} + DrawLayer* SdlBFApp::CreateDrawLayer(BFWindow* window) { GLDrawLayer* drawLayer = new GLDrawLayer(); @@ -473,14 +878,98 @@ DrawLayer* SdlBFApp::CreateDrawLayer(BFWindow* window) void SdlBFApp::GetDesktopResolution(int& width, int& height) { - width = 1024; - height = 768; + SDL_DisplayID display = bf_SDL_GetPrimaryDisplay(); + if (display != 0) + { + SDL_DisplayMode* displayMode = bf_SDL_GetDesktopDisplayMode(display); + if (displayMode != NULL) { + width = displayMode->w; + height = displayMode->h; + } + } +} + +static bool InflateRectToMonitor(SDL_DisplayID monitor, AdjustedMonRect* inflatedRect) +{ + SDL_Rect bounds; + if(!bf_SDL_GetDisplayBounds(monitor, &bounds)) + return false; + + inflatedRect->mMonCount++; + if (inflatedRect->mMonCount == 1) + { + inflatedRect->mX = bounds.x; + inflatedRect->mY = bounds.y; + inflatedRect->mWidth = bounds.w; + inflatedRect->mHeight = bounds.h; + } + else + { + int minLeft = BF_MIN(inflatedRect->mX, bounds.x); + int minTop = BF_MIN(inflatedRect->mY, bounds.y); + int maxRight = BF_MAX(inflatedRect->mX + inflatedRect->mWidth, bounds.x + bounds.w); + int maxBottom = BF_MAX(inflatedRect->mY + inflatedRect->mHeight, bounds.y + bounds.h); + + inflatedRect->mX = minLeft; + inflatedRect->mY = minTop; + inflatedRect->mWidth = maxRight - minLeft; + inflatedRect->mHeight = maxBottom - minTop; + } + + return true; } void SdlBFApp::GetWorkspaceRect(int& x, int& y, int& width, int& height) { - x = 0; - y = 0; - width = 1024; - height = 768; + AdjustedMonRect inflateRect = { 0 }; + + int displayCount; + SDL_DisplayID* displays = bf_SDL_GetDisplays(&displayCount); + if (!displays) + return; + + for (int i = 0; i < displayCount; i++) + { + InflateRectToMonitor(displays[i], &inflateRect); + } + bf_SDL_free(displays); + + x = inflateRect.mX; + y = inflateRect.mY; + width = inflateRect.mWidth; + height = inflateRect.mHeight; +} + +void SdlBFApp::GetWorkspaceRectFrom(int fromX, int fromY, int fromWidth, int fromHeight, int& outX, int& outY, int& outWidth, int& outHeight) +{ + SDL_Rect bounds; + SDL_Rect clip = {fromX, fromY, fromWidth == 0 ? 1 : fromWidth, fromHeight == 0 ? 1 : fromHeight}; + AdjustedMonRect inflateRect = { 0 }; + + int displayCount; + SDL_DisplayID* displays = bf_SDL_GetDisplays(&displayCount); + if (displays == NULL) + return; + + for (int i = 0; i < displayCount; i++) + { + bf_SDL_GetDisplayBounds(displays[i], &bounds); + if (bf_SDL_HasRectIntersection(&clip, &bounds)) + { + InflateRectToMonitor(displays[i], &inflateRect); + } + } + bf_SDL_free(displays); + + if (inflateRect.mMonCount == 0) + { + GetWorkspaceRect(outX, outY, outWidth, outHeight); + return; + } + + outX = inflateRect.mX; + outY = inflateRect.mY; + outWidth = inflateRect.mWidth; + outHeight = inflateRect.mHeight; } + diff --git a/BeefySysLib/platform/sdl/SdlBFApp.h b/BeefySysLib/platform/sdl/SdlBFApp.h index f22f45fb8..94611e889 100644 --- a/BeefySysLib/platform/sdl/SdlBFApp.h +++ b/BeefySysLib/platform/sdl/SdlBFApp.h @@ -2,6 +2,7 @@ #include "BFApp.h" #include "BFWindow.h" +#include #include #include "util/Dictionary.h" @@ -17,19 +18,21 @@ class SdlBFWindow : public BFWindow SDL_Window* mSDLWindow; bool mIsMouseInside; int mModalCount; + bool mHasPositionInit; + bool mIsMouseVisible; public: SdlBFWindow(BFWindow* parent, const StringImpl& title, int x, int y, int width, int height, int windowFlags); ~SdlBFWindow(); - virtual void* GetUnderlying() {return mSDLWindow; }; - virtual void Destroy() { } + virtual void* GetUnderlying() { return mSDLWindow; }; + virtual void Destroy() override; - virtual void SetTitle(const char* title) override {} - virtual void SetMinimumSize(int minWidth, int minHeight, bool clientSized) override {} - virtual void GetPlacement(int* normX, int* normY, int* normWidth, int* normHeight, int* showKind) override { } - virtual void Resize(int x, int y, int width, int height, ShowKind showKind) override {} - virtual void SetMouseVisible(bool isMouseVisible) override {} + virtual void SetTitle(const char* title) override; + virtual void SetMinimumSize(int minWidth, int minHeight, bool clientSized) override; + virtual void GetPlacement(int* normX, int* normY, int* normWidth, int* normHeight, int* showKind) override; + virtual void Resize(int x, int y, int width, int height, ShowKind showKind) override; + virtual void SetMouseVisible(bool isMouseVisible) override; virtual bool TryClose() override; virtual void GetPosition(int* x, int* y, int* width, int* height, int* clientX, int* clientY, int* clientWidth, int* clientHeight) override; @@ -48,18 +51,23 @@ class SdlBFWindow : public BFWindow }; typedef Dictionary SdlWindowMap; +typedef Dictionary SdlClipboardData; class SdlBFApp : public BFApp { public: bool mInMsgProc; + bool mIsControlDown; SdlWindowMap mSdlWindowMap; + SdlClipboardData* mSdlClipboardData; + SDL_GLContext mGLContext; + protected: virtual void Draw() override; virtual void PhysSetCursor() override; - uint32 GetClipboardFormat(const StringImpl& format); + const char* GetClipboardFormat(const StringImpl& format); SdlBFWindow* GetSdlWindowFromId(uint32 id); public: @@ -79,6 +87,7 @@ class SdlBFApp : public BFApp virtual BFSysBitmap* LoadSysBitmap(const wchar_t* fileName) override; virtual void GetDesktopResolution(int& width, int& height) override; virtual void GetWorkspaceRect(int& x, int& y, int& width, int& height) override; + virtual void GetWorkspaceRectFrom(int fromX, int fromY, int fromWidth, int fromHeight, int& outX, int& outY, int& outWidth, int& outHeight) override; }; NS_BF_END; \ No newline at end of file diff --git a/IDE/BeefProj.toml b/IDE/BeefProj.toml index 3ae2269e3..f161cac00 100644 --- a/IDE/BeefProj.toml +++ b/IDE/BeefProj.toml @@ -29,6 +29,16 @@ DebugWorkingDirectory = "$(ProjectDir)\\.." EnvironmentVars = ["_NO_DEBUG_HEAP=1"] PreprocessorMacros = ["DEBUG,HASGIT"] +[Configs.Debug.Linux64] +TargetDirectory = "$(ProjectDir)/dist" +TargetName = "BeefIDE_d" +OtherLinkFlags = "$(LinkFlags) libIDEHelper_d.a libBeefySysLib_d.a libhunspell.so $(Var IDEHelperLibs)" +PreBuildCmds = ["ReadFile(\"$(WorkspaceDir)/dist/IDEHelper_libs_d.txt\", \"IDEHelperLibs\")"] +DebugCommandArguments = "-proddir=\"$(ProjectDir)\\..\\BeefBuild\"" +DebugWorkingDirectory = "$(ProjectDir)\\.." +EnvironmentVars = ["_NO_DEBUG_HEAP=1"] +PreprocessorMacros = ["DEBUG,HASGIT"] + [Configs.Release.Win32] TargetName = "" OtherLinkFlags = "" @@ -42,6 +52,16 @@ DebugWorkingDirectory = "$(ProjectDir)\\dist" EnvironmentVars = ["z_NO_DEBUG_HEAP=1"] PreprocessorMacros = ["RELEASE,HASGIT"] +[Configs.Release.Linux64] +TargetDirectory = "$(ProjectDir)/dist" +TargetName = "BeefIDE" +OtherLinkFlags = "libBeefRT.a libIDEHelper.a libBeefySysLib.a libhunspell.so $(Var IDEHelperLibs)" +PreBuildCmds = ["ReadFile(\"$(WorkspaceDir)/dist/IDEHelper_libs_d.txt\", \"IDEHelperLibs\")"] +DebugCommandArguments = "-proddir=\"$(ProjectDir)\\..\\BeefBuild\"" +DebugWorkingDirectory = "$(ProjectDir)\\dist" +EnvironmentVars = ["z_NO_DEBUG_HEAP=1"] +PreprocessorMacros = ["RELEASE,HASGIT"] + [Configs.Debug2.Win32] TargetName = "" OtherLinkFlags = "" diff --git a/IDE/BeefSpace.toml b/IDE/BeefSpace.toml index 9831a0f82..b1100309a 100644 --- a/IDE/BeefSpace.toml +++ b/IDE/BeefSpace.toml @@ -15,6 +15,9 @@ ConfigSelections = {IDEHelper = {Enabled = false}} [Configs.Debug.Win64] EnableSideStack = true +[Configs.Debug.Linux64] +EnableSideStack = true + [Configs.Release.Win32] BfOptimizationLevel = "OgPlus" EmitDebugInfo = "No" @@ -25,6 +28,10 @@ ConfigSelections = {IDEHelper = {Enabled = false}} BfOptimizationLevel = "O3" AllocStackTraceDepth = 0 +[Configs.Release.Linux64] +BfOptimizationLevel = "O3" +AllocStackTraceDepth = 0 + [Configs.Debug-IDE.Win32] EmitDebugInfo = "No" EmitDynamicCastCheck = false @@ -43,6 +50,15 @@ EmitObjectAccessCheck = false EnableRealtimeLeakCheck = false ConfigSelections = {IDE = {Config = "Debug"}, Beefy2D = {Config = "Debug"}, IDEHelper = {Config = "Debug"}, Debugger64 = {Config = "Debug"}, BeefySysLib = {Config = "Debug"}} +[Configs.Debug-IDE.Linux64] +BfOptimizationLevel = "O0" +EmitDebugInfo = "No" +EmitDynamicCastCheck = false +EnableObjectDebugFlags = false +EmitObjectAccessCheck = false +EnableRealtimeLeakCheck = false +ConfigSelections = {IDE = {Config = "Debug"}, Beefy2D = {Config = "Debug"}, IDEHelper = {Config = "Debug"}, Debugger64 = {Config = "Debug"}, BeefySysLib = {Config = "Debug"}} + [Configs.Debug2.Win32] EmitDebugInfo = "No" EmitDynamicCastCheck = false @@ -56,6 +72,10 @@ ConfigSelections = {Beefy2D = {Config = "Debug"}, IDEHelper = {Config = "Debug"} PreprocessorMacros = ["NEWFONT"] ConfigSelections = {Beefy2D = {Config = "Debug"}, IDEHelper = {Enabled = false, Config = "Debug"}, Debugger64 = {Enabled = false, Config = "Debug"}, BeefySysLib = {Enabled = false, Config = "Debug"}} +[Configs.Debug2.Linux64] +PreprocessorMacros = ["NEWFONT"] +ConfigSelections = {Beefy2D = {Config = "Debug"}, IDEHelper = {Enabled = false, Config = "Debug"}, Debugger64 = {Enabled = false, Config = "Debug"}, BeefySysLib = {Enabled = false, Config = "Debug"}} + [Configs.Paranoid.Win32] EmitDynamicCastCheck = false EnableObjectDebugFlags = false @@ -73,6 +93,15 @@ EnableSideStack = false COptimizationLevel = "O2" ConfigSelections = {IDEHelper = {Config = "Debug"}, Debugger64 = {Config = "Debug"}, BeefySysLib = {Config = "Debug"}} +[Configs.Paranoid.Linux64] +EmitDynamicCastCheck = false +EnableObjectDebugFlags = false +EmitObjectAccessCheck = false +EnableRealtimeLeakCheck = false +EnableSideStack = false +COptimizationLevel = "O2" +ConfigSelections = {IDEHelper = {Config = "Debug"}, Debugger64 = {Config = "Debug"}, BeefySysLib = {Config = "Debug"}} + [Configs.Test.Win32] EmitDynamicCastCheck = false EnableObjectDebugFlags = false @@ -87,6 +116,13 @@ EmitObjectAccessCheck = false EnableRealtimeLeakCheck = false COptimizationLevel = "O2" +[Configs.Test.Linux64] +EmitDynamicCastCheck = false +EnableObjectDebugFlags = false +EmitObjectAccessCheck = false +EnableRealtimeLeakCheck = false +COptimizationLevel = "O2" + [Configs.Debug3.Win32] EmitDebugInfo = "No" EmitDynamicCastCheck = false @@ -100,6 +136,10 @@ AllocStackTraceDepth = 0 BfOptimizationLevel = "O0" ConfigSelections = {Beefy2D = {Config = "Debug"}, IDEHelper = {Config = "Debug"}, Debugger64 = {Config = "Debug"}, BeefySysLib = {Config = "Debug"}} +[Configs.Debug3.Linux64] +BfOptimizationLevel = "O0" +ConfigSelections = {Beefy2D = {Config = "Debug"}, IDEHelper = {Config = "Debug"}, Debugger64 = {Config = "Debug"}, BeefySysLib = {Config = "Debug"}} + [Configs.Debug_NoDeps.Win32] InitLocalVariables = true EmitObjectAccessCheck = false @@ -110,3 +150,7 @@ ConfigSelections = {IDEHelper = {Enabled = false, Config = "Debug"}, IDE = {Conf [Configs.Debug_NoDeps.Win64] EnableSideStack = true ConfigSelections = {IDE = {Config = "Debug"}, Beefy2D = {Config = "Debug"}, IDEHelper = {Enabled = false, Config = "Debug"}, Debugger64 = {Enabled = false, Config = "Debug"}, BeefySysLib = {Enabled = false, Config = "Debug"}} + +[Configs.Debug_NoDeps.Linux64] +EnableSideStack = true +ConfigSelections = {IDE = {Config = "Debug"}, Beefy2D = {Config = "Debug"}, IDEHelper = {Enabled = false, Config = "Debug"}, Debugger64 = {Enabled = false, Config = "Debug"}, BeefySysLib = {Enabled = false, Config = "Debug"}} diff --git a/IDE/Resources/beef.ico.png b/IDE/Resources/beef.ico.png new file mode 100644 index 000000000..585bd4d16 Binary files /dev/null and b/IDE/Resources/beef.ico.png differ diff --git a/IDE/src/IDEApp.bf b/IDE/src/IDEApp.bf index 596a85dda..b55b70377 100644 --- a/IDE/src/IDEApp.bf +++ b/IDE/src/IDEApp.bf @@ -121,6 +121,14 @@ namespace IDE public static String sRTVersionStr = "042"; public const String cVersion = "0.43.6"; +#if BF_PLATFORM_LINUX + public const uint8[?] cAppIcon = [IgnoreErrors]{ Compiler.ReadBinary("Resources/beef.ico.png") }; + + [CallingConvention(.Stdcall), CLink] + static extern void BFApp_RegisterAppIcon(uint8* imageData, int size); +#endif + + #if BF_PLATFORM_WINDOWS public static readonly String sPlatform64Name = "Win64"; public static readonly String sPlatform32Name = "Win32"; @@ -12822,6 +12830,11 @@ namespace IDE mGitManager.Init(); +#if BF_PLATFORM_LINUX + let icon = (Span)cAppIcon; + BFApp_RegisterAppIcon(icon.Ptr, icon.Length); +#endif + //Yoop(); /*for (int i = 0; i < 100*1024*1024; i++) @@ -12861,7 +12874,8 @@ namespace IDE #endif } - Font.AddFontFailEntry("Segoe UI", scope String()..AppendF("{}fonts/NotoSans-Regular.ttf", mInstallDir)); + Font.AddFontFailEntry("Segoe UI", scope $"Noto Sans\0{mInstallDir}fonts/NotoSans-Regular.ttf"); + Font.AddFontFailEntry("Segoe UI Bold", scope $"Noto Sans Bold\0{mInstallDir}fonts/NotoSans-Bold.ttf"); DarkTheme aTheme = new DarkTheme(); mSettings.mUISettings.Apply(); // Apply again to set actual theme @@ -13673,7 +13687,9 @@ namespace IDE DeleteAndNullify!(mBfBuildSystem); /// +#if BF_PLATFORM_WINDOWS mDebugger.FullReportMemory(); +#endif var workspaceBuildDir = scope String(); GetWorkspaceBuildDir(workspaceBuildDir); diff --git a/IDE/src/Settings.bf b/IDE/src/Settings.bf index ec77c8189..3d2ebb2c4 100644 --- a/IDE/src/Settings.bf +++ b/IDE/src/Settings.bf @@ -529,7 +529,14 @@ namespace IDE mColors.Deserialize(sd); } - String imgCreatePath = scope String(gApp.mInstallDir, "images/ImgCreate.exe"); + const String EXECUTABLE_PATH = +#if BF_PLATFORM_WINDOWS + "images/ImgCreate.exe"; +#else + "images/ImgCreate"; +#endif + + String imgCreatePath = scope String(gApp.mInstallDir, EXECUTABLE_PATH); let imgCreateExeTime = File.GetLastWriteTime(imgCreatePath).GetValueOrDefault(); for (let theme in mTheme) diff --git a/IDE/src/ui/AutoComplete.bf b/IDE/src/ui/AutoComplete.bf index 76eebe55f..c14774763 100644 --- a/IDE/src/ui/AutoComplete.bf +++ b/IDE/src/ui/AutoComplete.bf @@ -288,7 +288,7 @@ namespace IDE.ui return; } - if ((mAutoComplete.mIgnoreMove == 0) && (mWidgetWindow != null) && (!mWidgetWindow.mHasClosed)) + if ((mWidgetWindow != null) && (!mWidgetWindow.mHasClosed) && (mWidgetWindow.HasParent(window))) mAutoComplete.Close(); } @@ -645,11 +645,9 @@ namespace IDE.ui int windowHeight = (int)(mWantHeight + Math.Max(0, mDocHeight - GS!(32))); - mAutoComplete.mIgnoreMove++; mWidgetWindow.Resize(mWidgetWindow.mNormX, mWidgetWindow.mNormY, windowWidth, windowHeight); mScrollContent.mWidth = mWidth; //Resize(0, 0, mWidgetWindow.mClientWidth, mWidgetWindow.mClientHeight); - mAutoComplete.mIgnoreMove--; ResizeContent(-1, -1, mVertScrollbar != null); } } @@ -997,9 +995,7 @@ namespace IDE.ui { if (mOwnsWindow) { - mAutoComplete.mIgnoreMove++; mAutoComplete.UpdateWindow(ref mWidgetWindow, this, mAutoComplete.mInvokeSrcPositions[0], (int32)extWidth, (int32)extHeight); - mAutoComplete.mIgnoreMove--; } else { @@ -1395,7 +1391,6 @@ namespace IDE.ui public bool mIsDocumentationPass; public bool mIsUserRequested; - public int mIgnoreMove; bool mClosed; bool mPopulating; float mWantX; @@ -1527,7 +1522,7 @@ namespace IDE.ui { //Debug.WriteLine($"UpdateWindow Create {screenX},{screenY}"); - BFWindow.Flags windowFlags = BFWindow.Flags.ClientSized | BFWindow.Flags.PopupPosition | BFWindow.Flags.NoActivate | BFWindow.Flags.NoMouseActivate | BFWindow.Flags.DestAlpha; + BFWindow.Flags windowFlags = BFWindow.Flags.ClientSized | BFWindow.Flags.PopupPosition | BFWindow.Flags.NoActivate | BFWindow.Flags.NoMouseActivate | BFWindow.Flags.DestAlpha | BFWindow.Flags.Tooltip; widgetWindow = new WidgetWindow(mTargetEditWidget.mWidgetWindow, "Autocomplete", (int32)screenX, (int32)screenY, @@ -1558,8 +1553,6 @@ namespace IDE.ui public void Update() { - Debug.Assert((mIgnoreMove >= 0) && (mIgnoreMove <= 4)); - if ((mInvokeWindow != null) && (!mInvokeWidget.mIsAboveText)) { int textIdx = mTargetEditWidget.Content.mTextCursors.Front.mCursorTextPos; @@ -1588,13 +1581,11 @@ namespace IDE.ui int insertLine = line; if ((insertLine != invokeLine) && ((insertLine - invokeLine) * gApp.mCodeFont.GetHeight() < GS!(40))) { - mIgnoreMove++; mInvokeWidget.mIsAboveText = true; mInvokeWidget.ResizeContent(false); UpdateWindow(ref mInvokeWindow, mInvokeWidget, mInvokeSrcPositions[0], (int32)mInvokeWidget.mWidth, (int32)mInvokeWidget.mHeight); if (mListWindow != null) UpdateWindow(ref mListWindow, mAutoCompleteListWidget, mInsertStartIdx.Value, mListWindow.mWindowWidth, mListWindow.mWindowHeight); - mIgnoreMove--; } } @@ -1915,11 +1906,6 @@ namespace IDE.ui return hadMatch; } - public void SetIgnoreMove(bool ignoreMove) - { - mIgnoreMove += ignoreMove ? 1 : -1; - } - // IDEHelper/third_party/FtsFuzzyMatch.h [CallingConvention(.Stdcall), CLink] static extern bool fts_fuzzy_match(char8* pattern, char8* str, ref int32 outScore, uint8* matches, int maxMatches); @@ -2157,8 +2143,6 @@ namespace IDE.ui SelectEntry(""); } - SetIgnoreMove(true); - gApp.mAutoCompletePanel.StartBind(this); int32 prevInvokeSelect = 0; @@ -2200,7 +2184,6 @@ namespace IDE.ui HandleAutoCompleteListWidget(visibleCount); } gApp.mAutoCompletePanel.FinishBind(); - SetIgnoreMove(false); if ((mAutoCompleteListWidget != null) && (!mAutoCompleteListWidget.mIsInitted)) mAutoCompleteListWidget.Init(); @@ -2407,7 +2390,6 @@ namespace IDE.ui { if (mAutoCompleteListWidget != null) { - mIgnoreMove++; if (IsInPanel()) { mAutoCompleteListWidget.RemoveSelf(); @@ -2421,7 +2403,6 @@ namespace IDE.ui else delete mAutoCompleteListWidget; mAutoCompleteListWidget = null; - mIgnoreMove--; } } if (mAutoCompleteListWidget == null) diff --git a/IDE/src/ui/HoverWatch.bf b/IDE/src/ui/HoverWatch.bf index f73c4886f..4cf91733f 100644 --- a/IDE/src/ui/HoverWatch.bf +++ b/IDE/src/ui/HoverWatch.bf @@ -459,20 +459,23 @@ namespace IDE.ui { base.Update(); - for (let item in mListView.GetRoot().mChildItems) - { - let listViewItem = (HoverListViewItem)item; - if (listViewItem.mWatchEntry?.mIsPending == true) - { - if ((gApp.mDebugger.IsPaused(true)) && (!gApp.mDebugger.HasMessages())) - { - let listView = (HoverListView)listViewItem.mListView; - DoListViewItem(listView, listViewItem, listViewItem.mWatchEntry.mName, listViewItem.mWatchEntry.mEvalStr, true); - FinishListView(listView, listView.mX, listView.mY, true); - gApp.RefreshVisibleViews(); - } - } - } + if(mListView != null) + { + for (let item in mListView.GetRoot().mChildItems) + { + let listViewItem = (HoverListViewItem)item; + if (listViewItem.mWatchEntry?.mIsPending == true) + { + if ((gApp.mDebugger.IsPaused(true)) && (!gApp.mDebugger.HasMessages())) + { + let listView = (HoverListView)listViewItem.mListView; + DoListViewItem(listView, listViewItem, listViewItem.mWatchEntry.mName, listViewItem.mWatchEntry.mEvalStr, true); + FinishListView(listView, listView.mX, listView.mY, true); + gApp.RefreshVisibleViews(); + } + } + } + } if (mCloseDelay > 1) mCloseDelay--; @@ -1082,7 +1085,7 @@ namespace IDE.ui var parentWidget = GetParentWidget(); - BFWindow.Flags windowFlags = BFWindow.Flags.ClientSized /*| BFWindow.Flags.PopupPosition*/ | BFWindow.Flags.NoActivate | BFWindow.Flags.DestAlpha; + BFWindow.Flags windowFlags = BFWindow.Flags.ClientSized /*| BFWindow.Flags.PopupPosition*/ | BFWindow.Flags.NoActivate | BFWindow.Flags.DestAlpha | BFWindow.Flags.Tooltip; #unwarn WidgetWindow widgetWindow = new WidgetWindow(parentWidget.mWidgetWindow, "HoverWatch", @@ -1108,11 +1111,7 @@ namespace IDE.ui autocomplete = watchEditWidgetContent.mAutoComplete; } - if (autocomplete != null) - autocomplete.SetIgnoreMove(true); mWidgetWindow.Resize((int32)(mOrigScreenX - mTaskbarXOffset + minX), (int32)(mOrigScreenY - mTaskbarYOffset + minY), (int32)(maxX - minX), (int32)(maxY - minY)); - if (autocomplete != null) - autocomplete.SetIgnoreMove(false); } } @@ -1686,7 +1685,7 @@ namespace IDE.ui /*float screenWidth = mListView.mWidth + 8; float screenHeight = mListView.mHeight + 8; - BFWindow.Flags windowFlags = BFWindow.Flags.ClientSized /*| BFWindow.Flags.PopupPosition*/ | BFWindow.Flags.NoActivate | BFWindow.Flags.DestAlpha; + BFWindow.Flags windowFlags = BFWindow.Flags.ClientSized /*| BFWindow.Flags.PopupPosition*/ | BFWindow.Flags.NoActivate | BFWindow.Flags.DestAlpha | BFWindow.Flags.Tooltip; #unwarn WidgetWindow widgetWindow = new WidgetWindow(parentWidget.mWidgetWindow, "HoverWatch", diff --git a/IDE/src/ui/MainFrame.bf b/IDE/src/ui/MainFrame.bf index 966e9872a..3df50c1e5 100644 --- a/IDE/src/ui/MainFrame.bf +++ b/IDE/src/ui/MainFrame.bf @@ -15,12 +15,20 @@ namespace IDE.ui public StatusBar mStatusBar; public DarkDockingFrame mDockingFrame; +#if BF_PLATFORM_LINUX + public MenuBar mMenuBar; +#endif + public this() { mStatusBar = new StatusBar(); AddWidget(mStatusBar); mDockingFrame = (DarkDockingFrame)ThemeFactory.mDefault.CreateDockingFrame(); AddWidget(mDockingFrame); +#if BF_PLATFORM_LINUX + mMenuBar = new MenuBar(); + AddWidget(mMenuBar); +#endif } public void Reset() @@ -40,8 +48,15 @@ namespace IDE.ui public override void Resize(float x, float y, float width, float height) { base.Resize(x, y, width, height); - int32 statusHeight = GS!(20); + + int32 statusHeight = GS!(20); +#if BF_PLATFORM_LINUX + int32 menuHeight = GS!(20); + mDockingFrame.Resize(0, menuHeight, width, height - statusHeight - menuHeight); + mMenuBar.Resize(0, 0, width, menuHeight); +#else mDockingFrame.Resize(0, 0, width, height - statusHeight); +#endif mStatusBar.Resize(0, mHeight - statusHeight, width, statusHeight); } } diff --git a/IDE/src/ui/MenuBar.bf b/IDE/src/ui/MenuBar.bf new file mode 100644 index 000000000..77e0205bb --- /dev/null +++ b/IDE/src/ui/MenuBar.bf @@ -0,0 +1,459 @@ +using Beefy; +using Beefy.gfx; +using Beefy.widgets; +using Beefy.theme.dark; +using Beefy.geom; +using IDE; +using IDE.util; +using System; +using System.Diagnostics; + +namespace IDE.ui; + +public class MenuBar : Widget +{ + public class Button : ButtonWidget + { + private String mLabel ~ delete _; + + public StringView Label + { + get + { + return mLabel; + } + + set + { + String.NewOrSet!(mLabel, value); + } + } + + public override void Draw(Graphics g) + { + base.Draw(g); + + if (mMouseOver) + { + using (g.PushColor(0x40FFFFFF)) + { + g.FillRect(0, 0, mWidth, mHeight); + } + } + + g.SetFont(DarkTheme.sDarkTheme.mSmallFont); + if (mLabel != null) + { + using (g.PushColor(mDisabled ? 0x80FFFFFF : Color.White)) + { + using (g.PushColor(DarkTheme.COLOR_TEXT)) + DarkTheme.DrawUnderlined(g, mLabel, GS!(2), (mHeight - GS!(20)) / 2, .Centered, mWidth - GS!(4), .Truncate); + } + } + } + } + + public Button mFileButton; + public Button mEditButton; + public Button mViewButton; + public Button mBuildButton; + public Button mDebugButton; + public Button mTestButton; + public Button mWindowButton; + public Button mHelpButton; + + public this() + { + mFileButton = new Button(); + mFileButton.Label = "File"; + mFileButton.mOnMouseClick.Add(new (evt) => ShowFileMenu()); + AddWidget(mFileButton); + + mEditButton = new Button(); + mEditButton.Label = "Edit"; + mEditButton.mOnMouseClick.Add(new (evt) => ShowEditMenu()); + AddWidget(mEditButton); + + mViewButton = new Button(); + mViewButton.Label = "View"; + mViewButton.mOnMouseClick.Add(new (evt) => ShowViewMenu()); + AddWidget(mViewButton); + + mBuildButton = new Button(); + mBuildButton.Label = "Build"; + mBuildButton.mOnMouseClick.Add(new (evt) => ShowBuildMenu()); + AddWidget(mBuildButton); + + mDebugButton = new Button(); + mDebugButton.Label = "Debug"; + mDebugButton.mOnMouseClick.Add(new (evt) => ShowDebugMenu()); + AddWidget(mDebugButton); + + mTestButton = new Button(); + mTestButton.Label = "Test"; + mTestButton.mOnMouseClick.Add(new (evt) => ShowTestMenu()); + AddWidget(mTestButton); + + mWindowButton = new Button(); + mWindowButton.Label = "Window"; + mWindowButton.mOnMouseClick.Add(new (evt) => ShowWindowMenu()); + AddWidget(mWindowButton); + + mHelpButton = new Button(); + mHelpButton.Label = "Help"; + mHelpButton.mOnMouseClick.Add(new (evt) => ShowHelpMenu()); + AddWidget(mHelpButton); + } + + Menu AddMenuItem(Menu menu, StringView label, StringView command, bool disabled = false, bool isChecked = false) + { + let labelStr = scope String(label); + let hasCommand = gApp.mCommands.mCommandMap.TryGetAlt(command, var matchKey, var ideCommand); + + if(hasCommand) + { + labelStr.Append("|"); + ideCommand.ToString(labelStr); + } + + return AddMenuItem(menu, labelStr, hasCommand ? new (evt) => ideCommand.mAction() : null, disabled, isChecked); + } + + Menu AddMenuItem(Menu menu, StringView label, delegate void(Menu menu) action, bool disabled = false, bool isChecked = false) + { + Menu item = menu.AddItem(label); + item.SetDisabled(disabled); + + if (isChecked) + { + item.mIconImage = DarkTheme.sDarkTheme.GetImage(.Check); + } + + if (action != null) + { + item.mOnMenuItemSelected.Add(action); + } + + return item; + } + + void ShowFileMenu() + { + let subMenu = new Menu(); + let newMenu = subMenu.AddItem("New"); + AddMenuItem(newMenu, "New Workspace", "New Workspace"); + AddMenuItem(newMenu, "New Project", "New Project"); + AddMenuItem(newMenu, "New Debug Session", "New Debug Session"); + AddMenuItem(newMenu, "New File", "New File"); + + let openMenu = subMenu.AddItem("Open"); + AddMenuItem(openMenu, "Open Workspace...", "Open Workspace"); + AddMenuItem(openMenu, "Open Project...", "Open Project"); + AddMenuItem(openMenu, "Open Debug Session...", "Open Debug Session"); + AddMenuItem(openMenu, "Open File...", "Open File"); + AddMenuItem(openMenu, "Open File in Workspace", "Open File in Workspace"); + AddMenuItem(openMenu, "Open Corresponding (cpp/h)", "Open Corresponding"); + AddMenuItem(openMenu, "Open Crash Dump...", "Open Crash Dump"); + + let recentMenu = subMenu.AddItem("Open Recent"); + PopulateRecentMenu(.OpenedWorkspace, recentMenu.AddItem("Open Recent Workspace")); + PopulateRecentMenu(.OpenedDebugSession, recentMenu.AddItem("Open Recent Debug Session")); + PopulateRecentMenu(.OpenedFile, recentMenu.AddItem("Open Recent File")); + PopulateRecentMenu(.OpenedCrashDump, recentMenu.AddItem("Open Recent Crash Dump")); + + AddMenuItem(subMenu, "Save File", "Save File", gApp.GetActiveDocumentPanel() == null); + AddMenuItem(subMenu, "Save As...", "Save As", gApp.GetActiveDocumentPanel() == null); + AddMenuItem(subMenu, "Save All", "Save All"); + let prefMenu = subMenu.AddItem("Preferences"); + AddMenuItem(prefMenu, "Settings", "Settings"); + AddMenuItem(prefMenu, "Reload Settings", "Reload Settings"); + AddMenuItem(prefMenu, "Reset UI", "Reset UI"); + AddMenuItem(prefMenu, "Safe Mode", "Safe Mode Toggle", false, gApp.mSafeMode); + AddMenuItem(subMenu, "Close Workspace", "Close Workspace", !gApp.mWorkspace.IsInitialized); + AddMenuItem(subMenu, "Exit", "Exit"); + + let menuWidget = DarkTheme.sDarkTheme.CreateMenuWidget(subMenu); + menuWidget.Init(mFileButton, 0, mHeight); + } + + void PopulateRecentMenu(RecentFiles.RecentKind kind, Menu menu) + { + let recents = gApp.mSettings.mRecentFiles.GetRecentList(kind); + let itemCount = Math.Min(recents.Count, 10); + + menu.SetDisabled(itemCount < 1); + + for(int i = 0; i < itemCount; i++) + { + AddMenuItem(menu, scope $"{i+1} {recents[i]}", new (evt) => + { + gApp.[Friend]ShowRecentFile(kind, i); + }); + } + } + + void ShowEditMenu() + { + let subMenu = new Menu(); + AddMenuItem(subMenu, "Quick Find...", "Find in Document", gApp.GetActivePanel() == null); + AddMenuItem(subMenu, "Quick Replace...", "Replace in Document", gApp.GetActiveDocumentPanel() == null); + AddMenuItem(subMenu, "Find in Files...", "Find in Files"); + AddMenuItem(subMenu, "Replace in Files...", "Replace in Files"); + AddMenuItem(subMenu, "Find Prev", "Find Prev", gApp.GetActivePanel() == null); + AddMenuItem(subMenu, "Find Next", "Find Next", gApp.GetActivePanel() == null); + AddMenuItem(subMenu, "Show Current", "Show Current"); + + AddMenuItem(subMenu, "Goto Line...", "Goto Line", gApp.GetActiveDocumentPanel() == null); + AddMenuItem(subMenu, "Goto Method...", "Goto Method", gApp.GetActiveDocumentPanel() == null); + AddMenuItem(subMenu, "Rename Symbol", "Rename Symbol", gApp.GetActiveDocumentPanel() == null); + AddMenuItem(subMenu, "Show Fixit", "Show Fixit", gApp.GetActiveDocumentPanel() == null); + AddMenuItem(subMenu, "Find All References", "Find All References", gApp.GetActiveDocumentPanel() == null); + AddMenuItem(subMenu, "Find Class...", "Find Class"); + subMenu.AddItem(null); + var encodingMenu = subMenu.AddItem("Encoding"); + var lineEndingMenu = encodingMenu.AddItem("Line Ending"); + void AddLineEndingKind(String name, LineEndingKind lineEndingKind) + { + let item = lineEndingMenu.AddItem(name); + let sourceViewPanel = gApp.GetActiveSourceViewPanel(true); + + if (sourceViewPanel != null) + { + if (sourceViewPanel.mEditData.mLineEndingKind == lineEndingKind) + { + item.mIconImage = DarkTheme.sDarkTheme.GetImage(.Check); + } + item.mOnMenuItemSelected.Add(new (evt) => + { + if (sourceViewPanel.mEditData.mLineEndingKind != lineEndingKind) + { + sourceViewPanel.EditWidget.Content.mData.mCurTextVersionId++; + sourceViewPanel.mEditData.mLineEndingKind = lineEndingKind; + } + }); + } + } + AddLineEndingKind("Windows", .CrLf); + AddLineEndingKind("Unix", .Lf); + AddLineEndingKind("Mac OS 9", .Cr); + + var bookmarkMenu = subMenu.AddItem("Bookmarks"); + AddMenuItem(bookmarkMenu, "Toggle Bookmark", "Bookmark Toggle"); + AddMenuItem(bookmarkMenu, "Next Bookmark", "Bookmark Next"); + AddMenuItem(bookmarkMenu, "Previous Bookmark", "Bookmark Prev"); + AddMenuItem(bookmarkMenu, "Clear Bookmarks", "Bookmark Clear"); + + var comptimeMenu = subMenu.AddItem("Comptime"); + var emitViewCompiler = comptimeMenu.AddItem("Emit View Compiler"); + AddMenuItem(emitViewCompiler, "Resolve", new (evt) => gApp.SetEmbedCompiler(.Resolve), false, gApp.mSettings.mEditorSettings.mEmitCompiler == .Resolve); + AddMenuItem(emitViewCompiler, "Build", new (evt) => gApp.SetEmbedCompiler(.Build), false, gApp.mSettings.mEditorSettings.mEmitCompiler == .Build); + + var advancedEditMenu = subMenu.AddItem("Advanced"); + AddMenuItem(advancedEditMenu, "Duplicate Line", "Duplicate Line"); + AddMenuItem(advancedEditMenu, "Move Line Up", "Move Line Up"); + AddMenuItem(advancedEditMenu, "Move Line Down", "Move Line Down"); + AddMenuItem(advancedEditMenu, "Move Statement Up", "Move Statement Up"); + AddMenuItem(advancedEditMenu, "Move Statement Down", "Move Statement Down"); + advancedEditMenu.AddItem(null); + AddMenuItem(advancedEditMenu, "Make Uppercase", "Make Uppercase"); + AddMenuItem(advancedEditMenu, "Make Lowercase", "Make Lowercase"); + AddMenuItem(advancedEditMenu, "Comment Block", "Comment Block"); + AddMenuItem(advancedEditMenu, "Comment Lines", "Comment Lines"); + AddMenuItem(advancedEditMenu, "Comment Toggle", "Comment Toggle"); + AddMenuItem(advancedEditMenu, "Uncomment Selection", "Uncomment Selection"); + AddMenuItem(advancedEditMenu, "Reformat Document", "Reformat Document"); + AddMenuItem(advancedEditMenu, "View White Space", "View White Space", false, gApp.mViewWhiteSpace.Bool); + + if (gApp.mSettings.mEnableDevMode) + { + subMenu.AddItem(null); + var internalEditMenu = subMenu.AddItem("Internal"); + AddMenuItem(internalEditMenu, "Hilight Cursor References", new (menu) => { gApp.mSettings.mEditorSettings.mHiliteCursorReferences = !gApp.mSettings.mEditorSettings.mHiliteCursorReferences; }, false, gApp.mSettings.mEditorSettings.mHiliteCursorReferences); + AddMenuItem(internalEditMenu, "Delayed Autocomplete", new (menu) => { gApp.mDbgDelayedAutocomplete = !gApp.mDbgDelayedAutocomplete; }, false, gApp.mDbgDelayedAutocomplete); + AddMenuItem(internalEditMenu, "Time Autocomplete", new (menu) => { gApp.mDbgTimeAutocomplete = !gApp.mDbgTimeAutocomplete; }, false, gApp.mDbgTimeAutocomplete); + AddMenuItem(internalEditMenu, "Perf Autocomplete", new (menu) => { gApp.mDbgPerfAutocomplete = !gApp.mDbgPerfAutocomplete; }, false, gApp.mDbgPerfAutocomplete); + AddMenuItem(internalEditMenu, "Dump Undo Buffer", new (menu) => + { + if (var panel = gApp.GetActiveSourceViewPanel()) + { + var str = panel.mEditWidget.mEditWidgetContent.mData.mUndoManager.ToString(.. scope .()); + Debug.WriteLine(str); + } + }, false, gApp.mDbgPerfAutocomplete); + } + + let menuWidget = DarkTheme.sDarkTheme.CreateMenuWidget(subMenu); + menuWidget.Init(mEditButton, 0, mHeight); + } + + void ShowViewMenu() + { + let subMenu = new Menu(); + AddMenuItem(subMenu, "AutoComplete", "Show Autocomplete Panel"); + AddMenuItem(subMenu, "Auto Watches", "Show Auto Watches"); + AddMenuItem(subMenu, "Bookmarks", "Show Bookmarks"); + AddMenuItem(subMenu, "Breakpoints", "Show Breakpoints"); + AddMenuItem(subMenu, "Call Stack", "Show Call Stack"); + AddMenuItem(subMenu, "Class View", "Show Class View"); + AddMenuItem(subMenu, "Diagnostics", "Show Diagnostics"); + AddMenuItem(subMenu, "Errors", "Show Errors"); + AddMenuItem(subMenu, "Find Results", "Show Find Results"); + AddMenuItem(subMenu, "Terminal", "Show Terminal"); + AddMenuItem(subMenu, "Console", "Show Console"); + AddMenuItem(subMenu, "Immediate Window", "Show Immediate"); + AddMenuItem(subMenu, "Memory", "Show Memory"); + AddMenuItem(subMenu, "Modules", "Show Modules"); + AddMenuItem(subMenu, "Output", "Show Output"); + AddMenuItem(subMenu, "Profiler", "Show Profiler"); + AddMenuItem(subMenu, "Threads", "Show Threads"); + AddMenuItem(subMenu, "Watches", "Show Watches"); + AddMenuItem(subMenu, "Workspace Explorer", "Show Workspace Explorer"); + subMenu.AddItem(null); + AddMenuItem(subMenu, "Next Document Panel", "Next Document Panel"); + AddMenuItem(subMenu, "Navigate Backwards", "Navigate Backwards"); + AddMenuItem(subMenu, "Navigate Forwards", "Navigate Forwards"); + + let menuWidget = DarkTheme.sDarkTheme.CreateMenuWidget(subMenu); + menuWidget.Init(mViewButton, 0, mHeight); + } + + void ShowBuildMenu() + { + let subMenu = new Menu(); + AddMenuItem(subMenu, "Build Workspace", "Build Workspace", !gApp.mWorkspace.IsInitialized); + AddMenuItem(subMenu, "Debug Comptime", "Debug Comptime", gApp.mDebugger.mIsRunning || !gApp.mWorkspace.IsInitialized); + AddMenuItem(subMenu, "Clean", "Clean", gApp.mDebugger.mIsRunning || !gApp.mWorkspace.IsInitialized); + AddMenuItem(subMenu, "Clean Beef", "Clean Beef", gApp.mDebugger.mIsRunning || !gApp.mWorkspace.IsInitialized); + AddMenuItem(subMenu, "Compile Current File", new (menu) => { gApp.[Friend]CompileCurrentFile(); }); + AddMenuItem(subMenu, "Cancel Build", "Cancel Build", !gApp.IsCompiling); + AddMenuItem(subMenu, "Verbose", new (menu) => + { + if (gApp.mVerbosity != .Diagnostic) + gApp.mVerbosity = .Diagnostic; + else + gApp.mVerbosity = .Normal; + }, false, (gApp.mVerbosity == .Diagnostic)); + + if (gApp.mSettings.mEnableDevMode) + { + var internalBuildMenu = subMenu.AddItem("Internal"); + AddMenuItem(internalBuildMenu, "Autobuild (Debug)", new (menu) => { gApp.mDebugAutoBuild = !gApp.mDebugAutoBuild; }); + AddMenuItem(internalBuildMenu, "Autorun (Debug)", new (menu) => { gApp.mDebugAutoRun = !gApp.mDebugAutoRun; }); + AddMenuItem(internalBuildMenu, "Disable Compiling", new (menu) => { gApp.mDisableBuilding = !gApp.mDisableBuilding; }, false, gApp.mDisableBuilding); + } + + let menuWidget = DarkTheme.sDarkTheme.CreateMenuWidget(subMenu); + menuWidget.Init(mBuildButton, 0, mHeight); + } + + void ShowDebugMenu() + { + let subMenu = new Menu(); + AddMenuItem(subMenu, "Start Debugging", "Start Debugging", true); // TODO: Debugging status + AddMenuItem(subMenu, "Start Without Debugging", "Start Without Debugging", gApp.mDebugger.mIsRunning || !gApp.mWorkspace.IsInitialized); + AddMenuItem(subMenu, "Start Without Compiling", "Start Without Compiling", gApp.mDebugger.mIsRunning || !gApp.mWorkspace.IsInitialized); + AddMenuItem(subMenu, "Launch Process...", "Launch Process", gApp.mDebugger.mIsRunning); + AddMenuItem(subMenu, "Attach to Process...", "Attach to Process", gApp.mDebugger.mIsRunning); + AddMenuItem(subMenu, "Stop Debugging", "Stop Debugging", !gApp.mDebugger.mIsRunning && (gApp.mTestManager == null)); + AddMenuItem(subMenu, "Break All", "Break All", !gApp.mDebugger.mIsRunning || gApp.mExecutionPaused); + AddMenuItem(subMenu, "Remove All Breakpoints", "Remove All Breakpoints"); + AddMenuItem(subMenu, "Show Disassembly", "Show Disassembly"); + AddMenuItem(subMenu, "Quick Watch", "Show QuickWatch", !gApp.mDebugger.mIsRunning || !gApp.mExecutionPaused); + AddMenuItem(subMenu, "Profile", "Profile", !gApp.mWorkspace.IsInitialized); + subMenu.AddItem(null); + AddMenuItem(subMenu, "Step Into", "Step Into", (gApp.mDebugger.mIsRunning && !gApp.mExecutionPaused) || !gApp.mWorkspace.IsInitialized); + AddMenuItem(subMenu, "Step Over", "Step Over", (gApp.mDebugger.mIsRunning && !gApp.mExecutionPaused) || !gApp.mWorkspace.IsInitialized); + AddMenuItem(subMenu, "Step Out", "Step Out", !gApp.mDebugger.mIsRunning || !gApp.mExecutionPaused); + subMenu.AddItem(null); + AddMenuItem(subMenu, "Toggle Breakpoint", "Breakpoint Toggle", gApp.GetActiveDocumentPanel() == null); + AddMenuItem(subMenu, "Toggle Thread Breakpoint", "Breakpoint Toggle Thread", gApp.GetActiveDocumentPanel() == null); + var newBreakpointMenu = subMenu.AddItem("New Breakpoint"); + AddMenuItem(newBreakpointMenu, "Memory Breakpoint...", "Breakpoint Memory", !gApp.mDebugger.mIsRunning); + AddMenuItem(newBreakpointMenu, "Symbol Breakpoint...", "Breakpoint Symbol"); + //TODO: dev mode + + let menuWidget = DarkTheme.sDarkTheme.CreateMenuWidget(subMenu); + menuWidget.Init(mDebugButton, 0, mHeight); + } + + void ShowTestMenu() + { + let testMenu = new Menu(); + var testRunMenu = testMenu.AddItem("Run"); + testRunMenu.SetDisabled(!gApp.mWorkspace.IsInitialized); + AddMenuItem(testRunMenu, "Normal Tests", "Run Normal Tests"); + AddMenuItem(testRunMenu, "All Tests", "Run All Tests"); + + var testDebugMenu = testMenu.AddItem("Debug"); + testDebugMenu.SetDisabled(!gApp.mWorkspace.IsInitialized); + AddMenuItem(testDebugMenu, "Normal Tests", "Debug Normal Tests"); + AddMenuItem(testDebugMenu, "All Tests", "Debug All Tests"); + testDebugMenu.AddItem(null); + AddMenuItem(testDebugMenu, "Break on Failure", new (evt) => { gApp.mTestBreakOnFailure = !gApp.mTestBreakOnFailure; }, false, gApp.mTestBreakOnFailure); + AddMenuItem(testMenu, "Enable Console", "Test Enable Console", false, gApp.mTestEnableConsole); + + let menuWidget = DarkTheme.sDarkTheme.CreateMenuWidget(testMenu); + menuWidget.Init(mTestButton, 0, mHeight); + } + + void ShowWindowMenu() + { + let windowMenu = new Menu(); + AddMenuItem(windowMenu, "Close Document", "Close Document", gApp.GetLastActiveDocumentPanel() == null); + AddMenuItem(windowMenu, "Close Panel", "Close Panel", gApp.GetActivePanel() == null); + AddMenuItem(windowMenu, "Close All", "Close All Panels"); + AddMenuItem(windowMenu, "Close All Except Current", "Close All Panels Except"); + AddMenuItem(windowMenu, "New View into File", "View New", gApp.GetActiveDocumentPanel() == null); + AddMenuItem(windowMenu, "Split View", "View Split", gApp.GetActiveDocumentPanel() == null); + + let menuWidget = DarkTheme.sDarkTheme.CreateMenuWidget(windowMenu); + menuWidget.Init(mWindowButton, 0, mHeight); + } + + void ShowHelpMenu() + { + let subMenu = new Menu(); + AddMenuItem(subMenu, "About", "About"); + + let menuWidget = DarkTheme.sDarkTheme.CreateMenuWidget(subMenu); + menuWidget.Init(mHelpButton, 0, mHeight); + } + + void ResizeComponents() + { + let font = DarkTheme.sDarkTheme.mSmallFont; + let padding = GS!(20); + float offset = 0; + + + void ResizeButton(Button button) + { + let width = font.GetWidth(button.Label) + padding; + button.Resize(offset, 0, width, mHeight); + offset += width; + } + + ResizeButton(mFileButton); + ResizeButton(mEditButton); + ResizeButton(mViewButton); + ResizeButton(mBuildButton); + ResizeButton(mDebugButton); + ResizeButton(mTestButton); + ResizeButton(mWindowButton); + ResizeButton(mHelpButton); + } + + public override void Resize(float x, float y, float width, float height) + { + base.Resize(x, y, width, height); + ResizeComponents(); + } + + public override void Draw(Graphics g) + { + using (g.PushColor(DarkTheme.COLOR_BKG)) + { + g.FillRect(0, 0, mWidth, mHeight); + } + } +} \ No newline at end of file diff --git a/IDE/src/ui/ProjectPanel.bf b/IDE/src/ui/ProjectPanel.bf index a2ca1ee49..2f1b1970c 100644 --- a/IDE/src/ui/ProjectPanel.bf +++ b/IDE/src/ui/ProjectPanel.bf @@ -2993,6 +2993,7 @@ namespace IDE.ui if (!path.IsWhiteSpace) { +#if BF_PLATFORM_WINDOWS ProcessStartInfo psi = scope ProcessStartInfo(); psi.SetFileName(path); psi.UseShellExecute = true; @@ -3000,6 +3001,46 @@ namespace IDE.ui var process = scope SpawnedProcess(); process.Start(psi).IgnoreError(); +#elif BF_PLATFORM_LINUX + if (!Linux.IsSystemdAvailable) + return; + + Linux.DBus* userDBus = ?; + if (Linux.SdBusOpenUser(&userDBus) < 0) + return; + defer Linux.SdBusUnref(userDBus); + + Linux.DBusMsg* msg = ?; + if (Linux.SdBusNewMethodCall(userDBus, &msg, + "org.freedesktop.FileManager1", + "/org/freedesktop/FileManager1", + "org.freedesktop.FileManager1", + "ShowFolders") < 0) + return; + defer Linux.SdBusMessageUnref(msg); + + if (Linux.SdBusMessageOpenContainer(msg, .Array, "s") < 0) + return; + + String arg = new:ScopedAlloc! .(path.Length + 10); + arg..Append("file://")..Append(path); + + if (Linux.SdBusMessageAppend(msg, "s", arg.CStr()) < 0) + return; + + if (Linux.SdBusMessageCloseContainer(msg) < 0) + return; + + if (Linux.SdBusMessageAppend(msg, "s", "".CStr()) < 0) + return; + + Linux.DBusErr error = default; + if (Linux.SdBusCall(userDBus, msg, 0, &error, null) < 0) + { + Linux.SdBusErrorFree(&error); + return; + } +#endif } }); diff --git a/IDE/src/ui/StartupPanel.bf b/IDE/src/ui/StartupPanel.bf index f11c17d61..d3bcbb704 100644 --- a/IDE/src/ui/StartupPanel.bf +++ b/IDE/src/ui/StartupPanel.bf @@ -229,7 +229,7 @@ namespace IDE.ui ShowNotFoundDialog(); return; } - +#if BF_PLATFORM_WINDOWS ProcessStartInfo psi = scope ProcessStartInfo(); psi.SetFileName(mPath); psi.UseShellExecute = true; @@ -237,6 +237,46 @@ namespace IDE.ui var process = scope SpawnedProcess(); process.Start(psi).IgnoreError(); +#elif BF_PLATFORM_LINUX + if (!Linux.IsSystemdAvailable) + return; + + Linux.DBus* userDBus = ?; + if (Linux.SdBusOpenUser(&userDBus) < 0) + return; + defer Linux.SdBusUnref(userDBus); + + Linux.DBusMsg* msg = ?; + if (Linux.SdBusNewMethodCall(userDBus, &msg, + "org.freedesktop.FileManager1", + "/org/freedesktop/FileManager1", + "org.freedesktop.FileManager1", + "ShowFolders") < 0) + return; + defer Linux.SdBusMessageUnref(msg); + + if (Linux.SdBusMessageOpenContainer(msg, .Array, "s") < 0) + return; + + String arg = new:ScopedAlloc! .(mPath.Length + 10); + arg..Append("file://")..Append(mPath); + + if (Linux.SdBusMessageAppend(msg, "s", arg.CStr()) < 0) + return; + + if (Linux.SdBusMessageCloseContainer(msg) < 0) + return; + + if (Linux.SdBusMessageAppend(msg, "s", "".CStr()) < 0) + return; + + Linux.DBusErr error = default; + if (Linux.SdBusCall(userDBus, msg, 0, &error, null) < 0) + { + Linux.SdBusErrorFree(&error); + return; + } +#endif } void OpenInTerminal() @@ -251,7 +291,6 @@ namespace IDE.ui psi.SetFileName(gApp.mSettings.mWindowsTerminal); psi.UseShellExecute = true; psi.SetWorkingDirectory(mPath); - var process = scope SpawnedProcess(); process.Start(psi).IgnoreError(); } @@ -412,13 +451,50 @@ namespace IDE.ui void OpenDocumentation() { + const String DOCS_URL = "https://www.beeflang.org/docs/"; +#if BF_PLATFORM_WINDOWS ProcessStartInfo psi = scope ProcessStartInfo(); - psi.SetFileName("https://www.beeflang.org/docs/"); + psi.SetFileName(DOCS_URL); psi.UseShellExecute = true; psi.SetVerb("Open"); var process = scope SpawnedProcess(); process.Start(psi).IgnoreError(); +#elif BF_PLATFORM_LINUX + if (!Linux.IsSystemdAvailable) + return; + + Linux.DBus* userDBus = ?; + if (Linux.SdBusOpenUser(&userDBus) < 0) + return; + defer Linux.SdBusUnref(userDBus); + + Linux.DBusMsg* msg = ?; + if (Linux.SdBusNewMethodCall(userDBus, &msg, + "org.freedesktop.portal.Desktop", + "/org/freedesktop/portal/desktop", + "org.freedesktop.portal.OpenURI", + "OpenURI") < 0) + return; + + defer Linux.SdBusMessageUnref(msg); + + if (Linux.SdBusMessageAppend(msg, "ss", "".CStr(), DOCS_URL.CStr()) < 0) + return; + + if (Linux.SdBusMessageOpenContainer(msg, .Array, "{sv}") < 0) + return; + + if (Linux.SdBusMessageCloseContainer(msg) < 0) + return; + + Linux.DBusErr error = default; + if (Linux.SdBusCall(userDBus, msg, 0, &error, null) < 0) + { + Linux.SdBusErrorFree(&error); + return; + } +#endif } } } \ No newline at end of file diff --git a/IDE/src/ui/WelcomePanel.bf b/IDE/src/ui/WelcomePanel.bf index 8ec896d41..e983e6d59 100644 --- a/IDE/src/ui/WelcomePanel.bf +++ b/IDE/src/ui/WelcomePanel.bf @@ -70,17 +70,17 @@ namespace IDE.ui public this() { - mSampleImg0 = Image.LoadFromFile(scope String()..AppendF(@"{}\images\welcome_sample0.png", gApp.mInstallDir)); - mSampleImg1 = Image.LoadFromFile(scope String()..AppendF(@"{}\images\welcome_sample1.png", gApp.mInstallDir)); + mSampleImg0 = Image.LoadFromFile(scope String()..AppendF(@"{}/images/welcome_sample0.png", gApp.mInstallDir)); + mSampleImg1 = Image.LoadFromFile(scope String()..AppendF(@"{}/images/welcome_sample1.png", gApp.mInstallDir)); mSampleBtn0 = new .(); - mSampleBtn0.mPath = new String()..AppendF(@"{}\..\Samples\SpaceGame\BeefSpace.toml", gApp.mInstallDir); + mSampleBtn0.mPath = new String()..AppendF(@"{}/../Samples/SpaceGame/BeefSpace.toml", gApp.mInstallDir); mSampleBtn0.mLabel = new String("Space Game"); mSampleBtn0.mImage = mSampleImg0; AddWidget(mSampleBtn0); mSampleBtn1 = new .(); - mSampleBtn1.mPath = new String()..AppendF(@"{}\..\Samples\HelloWorld\BeefSpace.toml", gApp.mInstallDir); + mSampleBtn1.mPath = new String()..AppendF(@"{}/../Samples/HelloWorld/BeefSpace.toml", gApp.mInstallDir); mSampleBtn1.mLabel = new String("Hello World"); mSampleBtn1.mImage = mSampleImg1; AddWidget(mSampleBtn1); diff --git a/IDEHelper/CMakeLists.txt b/IDEHelper/CMakeLists.txt index db51f53c3..e5e41fbd8 100644 --- a/IDEHelper/CMakeLists.txt +++ b/IDEHelper/CMakeLists.txt @@ -193,6 +193,11 @@ if (HAVE_BACKTRACE_HEADERS) string(STRIP ${TARGET_LIBS_OS} TARGET_LIBS_OS) endif() +if (DEFINED BF_ENABLE_SDL) + string(APPEND TARGET_LIBS_OS " -lGL") + string(STRIP ${TARGET_LIBS_OS} TARGET_LIBS_OS) +endif() + if (CMAKE_BUILD_TYPE STREQUAL "Debug" ) FILE(WRITE "${CMAKE_CURRENT_SOURCE_DIR}/../IDE/dist/IDEHelper_libs_d.txt" ${TARGET_LIBS_OS}) else() diff --git a/IDEHelper/DebugManager.cpp b/IDEHelper/DebugManager.cpp index aa1709a6a..fc5ba3a2d 100644 --- a/IDEHelper/DebugManager.cpp +++ b/IDEHelper/DebugManager.cpp @@ -1304,7 +1304,9 @@ BF_EXPORT StringView BF_CALLTYPE Debugger_Evaluate(const char* expr, int callSta String& outString = *gTLStrReturn.Get(); outString.clear(); +#ifdef BF_PLATFORM_WINDOWS outString = debugger->Evaluate(expr, callStackIdx, cursorPos, language, (DwEvalExpressionFlags)expressionFlags); +#endif #ifdef BF_WANTS_LOG_DBG { int crPos = (int)outString.IndexOf('\n'); diff --git a/bin/build.sh b/bin/build.sh index 1a1a6e018..1faff2bb1 100755 --- a/bin/build.sh +++ b/bin/build.sh @@ -19,6 +19,7 @@ do if [[ $i == "sdl" ]]; then echo "Using SDL" USE_SDL="-DBF_ENABLE_SDL=1" + SDL_LINKOPTS="-lGL" fi if [[ $i == "no_ffi" ]]; then @@ -146,7 +147,7 @@ ln -s -f $ROOTPATH/jbuild/Release/bin/libIDEHelper.a ../../BeefLibs/Beefy2D/dist ### DEBUG ### echo Building BeefBuild_bootd -../../jbuild_d/Debug/bin/BeefBoot --out="BeefBuild_bootd" --src=../src --src=../../BeefBuild/src --src=../../BeefLibs/corlib/src --src=../../BeefLibs/Beefy2D/src --define=CLI --define=DEBUG --startup=BeefBuild.Program --linkparams="./libBeefRT_d.a ./libIDEHelper_d.a ./libBeefySysLib_d.a ./libhunspell.$LIBEXT $(< ../../IDE/dist/IDEHelper_libs_d.txt) $LINKOPTS" +../../jbuild_d/Debug/bin/BeefBoot --out="BeefBuild_bootd" --src=../src --src=../../BeefBuild/src --src=../../BeefLibs/corlib/src --src=../../BeefLibs/Beefy2D/src --define=CLI --define=DEBUG --startup=BeefBuild.Program --linkparams="./libBeefRT_d.a ./libIDEHelper_d.a ./libBeefySysLib_d.a ./libhunspell.$LIBEXT $(< ../../IDE/dist/IDEHelper_libs_d.txt) $SDL_LINKOPTS $LINKOPTS" echo Building BeefBuild_d ./BeefBuild_bootd -clean -proddir=../../BeefBuild -config=Debug echo Testing IDEHelper/Tests in BeefBuild_d @@ -155,8 +156,18 @@ echo Testing IDEHelper/Tests in BeefBuild_d ### RELEASE ### echo Building BeefBuild_boot -../../jbuild/Release/bin/BeefBoot --out="BeefBuild_boot" --src=../src --src=../../BeefBuild/src --src=../../BeefLibs/corlib/src --src=../../BeefLibs/Beefy2D/src --define=CLI --startup=BeefBuild.Program --linkparams="./libBeefRT.a ./libIDEHelper.a ./libBeefySysLib.a ./libhunspell.$LIBEXT $(< ../../IDE/dist/IDEHelper_libs.txt) $LINKOPTS" +../../jbuild/Release/bin/BeefBoot --out="BeefBuild_boot" --src=../src --src=../../BeefBuild/src --src=../../BeefLibs/corlib/src --src=../../BeefLibs/Beefy2D/src --define=CLI --startup=BeefBuild.Program --linkparams="./libBeefRT.a ./libIDEHelper.a ./libBeefySysLib.a ./libhunspell.$LIBEXT $(< ../../IDE/dist/IDEHelper_libs.txt) $SDL_LINKOPTS $LINKOPTS" echo Building BeefBuild ./BeefBuild_boot -clean -proddir=../../BeefBuild -config=Release echo Testing IDEHelper/Tests in BeefBuild ./BeefBuild -proddir=../../IDEHelper/Tests -test + +### IDE ### +if [ -n "$USE_SDL" ]; then + + echo Building BeefIDE_d + ./BeefBuild -clean -proddir=../ -config=Debug + echo Building BeefIDE + ./BeefBuild -clean -proddir=../ -config=Release + +fi \ No newline at end of file