├── .gitattributes ├── .gitignore ├── Content ├── BPs │ └── BP_Template_Window.uasset ├── Materials │ ├── MAT_Frame.uasset │ ├── MAT_Viewport.uasset │ └── MF_Gradiant.uasset └── Widgets │ ├── Debug │ ├── UI_Child_Window.uasset │ ├── UI_ToolTip.uasset │ └── UI_Window.uasset │ └── Draggable │ └── UI_Draggable.uasset ├── LICENSE ├── README.md ├── Resources └── Icon128.png ├── Source └── WindowSystem │ ├── Private │ ├── CustomViewport.cpp │ ├── DragDropHandler.cpp │ ├── WindowInstance.cpp │ ├── WindowManager.cpp │ ├── WindowSystem.cpp │ └── WindowSystemBPLibrary.cpp │ ├── Public │ ├── CustomViewport.h │ ├── DragDropHandler.h │ ├── WindowEnums.h │ ├── WindowInstance.h │ ├── WindowManager.h │ ├── WindowSystem.h │ ├── WindowSystemBPLibrary.h │ └── WindowSystem_Includes.h │ └── WindowSystem.Build.cs └── WindowSystem.uplugin /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Visual Studio 2015 user specific files 2 | .vs/ 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | 22 | # Compiled Static libraries 23 | *.lai 24 | *.la 25 | *.a 26 | *.lib 27 | 28 | # Executables 29 | *.exe 30 | *.out 31 | *.app 32 | *.ipa 33 | 34 | # These project files can be generated by the engine 35 | *.xcodeproj 36 | *.xcworkspace 37 | *.sln 38 | *.suo 39 | *.opensdf 40 | *.sdf 41 | *.VC.db 42 | *.VC.opendb 43 | 44 | # Precompiled Assets 45 | SourceArt/**/*.png 46 | SourceArt/**/*.tga 47 | 48 | # Binary Files 49 | Binaries/* 50 | Plugins/*/Binaries/* 51 | 52 | # Builds 53 | Build/* 54 | 55 | # Whitelist PakBlacklist-.txt files 56 | !Build/*/ 57 | Build/*/** 58 | !Build/*/PakBlacklist*.txt 59 | 60 | # Don't ignore icon files in Build 61 | !Build/**/*.ico 62 | 63 | # Built data for maps 64 | *_BuiltData.uasset 65 | 66 | # Configuration files generated by the Editor 67 | Saved/* 68 | 69 | # Compiled source files for the engine to use 70 | Intermediate/* 71 | Plugins/*/Intermediate/* 72 | 73 | # Cache files for the editor to use 74 | DerivedDataCache/* 75 | -------------------------------------------------------------------------------- /Content/BPs/BP_Template_Window.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Frozen-Projects/WindowSystem/64267d00ed7691bfbc43e25cbe1c83585dcf7982/Content/BPs/BP_Template_Window.uasset -------------------------------------------------------------------------------- /Content/Materials/MAT_Frame.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Frozen-Projects/WindowSystem/64267d00ed7691bfbc43e25cbe1c83585dcf7982/Content/Materials/MAT_Frame.uasset -------------------------------------------------------------------------------- /Content/Materials/MAT_Viewport.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Frozen-Projects/WindowSystem/64267d00ed7691bfbc43e25cbe1c83585dcf7982/Content/Materials/MAT_Viewport.uasset -------------------------------------------------------------------------------- /Content/Materials/MF_Gradiant.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Frozen-Projects/WindowSystem/64267d00ed7691bfbc43e25cbe1c83585dcf7982/Content/Materials/MF_Gradiant.uasset -------------------------------------------------------------------------------- /Content/Widgets/Debug/UI_Child_Window.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Frozen-Projects/WindowSystem/64267d00ed7691bfbc43e25cbe1c83585dcf7982/Content/Widgets/Debug/UI_Child_Window.uasset -------------------------------------------------------------------------------- /Content/Widgets/Debug/UI_ToolTip.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Frozen-Projects/WindowSystem/64267d00ed7691bfbc43e25cbe1c83585dcf7982/Content/Widgets/Debug/UI_ToolTip.uasset -------------------------------------------------------------------------------- /Content/Widgets/Debug/UI_Window.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Frozen-Projects/WindowSystem/64267d00ed7691bfbc43e25cbe1c83585dcf7982/Content/Widgets/Debug/UI_Window.uasset -------------------------------------------------------------------------------- /Content/Widgets/Draggable/UI_Draggable.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Frozen-Projects/WindowSystem/64267d00ed7691bfbc43e25cbe1c83585dcf7982/Content/Widgets/Draggable/UI_Draggable.uasset -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | # Business Source License 1.1 2 | 3 | This Business Source License (the "License") applies to the source code in this repository, as specified by [ffreality] (the "Licensor"). 4 | 5 | ## 1. Licensed Grant 6 | Subject to the terms of this License, **the Licensor** hereby grants you, free of charge, 7 | - the right to use in commercial, non-commercial and academic products, 8 | - **reproduce, modify, and create derivative works** of **FF_PugiXml** plugin in this repository (the "Plugin"), 9 | solely for non-restricted use cases. 10 | 11 | Reproduce, modify and create derivate works mean merely create a **fork** of this repository while pointing reference to original repository and preserving license terms **without selling** it directly. 12 | 13 | ## 2. Restricted Use 14 | You may **not** use **the Plugin** to provide or operate a product or service that competes directly with **the Plugin**. 15 | 16 | Examples of competing use cases include, but are not limited to: 17 | - Sharing this plugin on any game development marketplaces with profit purpose. 18 | - Sharing this plugin on any game development marketplace with non-profit purpose but without credits. 19 | - Publishing this plugin on any code collaboration and/or public repository storage platform without forking or giving reference to original repository. 20 | - Changing license 21 | 22 | For a commercial license that allows restricted use, please contact with the Licensor [frozenforeststduio@gmail.com]. 23 | 24 | ## 3. Change Date 25 | The restrictions outlined in Section 2 above will terminate after 4 years after the Licensor **archived** it. 26 | On the Change Date, **the Plugin** will be made available under the **MIT License**. 27 | 28 | ## 4. Other Terms 29 | Except as expressly stated herein, no other rights or licenses are granted under this License, whether by implication, estoppel, or otherwise. All rights not expressly granted are reserved by Licensor. 30 | 31 | ## 5. Warranty Disclaimer 32 | THE SOFTWARE IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 33 | 34 | --- 35 | 36 | **FAQs** 37 | 38 | 1. **What is a "competing use case"?** 39 | Any use of the Software that creates or powers a product/service that competes with **FF_PUGIXML** such as 40 | - sharing it on Gumroad, Unreal Engine Marketplace/Fab and etc. with price tag. 41 | - sharing it on Gumroad, Unreal Engine Marketplace/Fab and etc. as free but without reference. 42 | - publishing on GitHub, GitLab or Bitbucket like public repository platform without forking or giving reference. As it like the actual developer of the plugin is you. 43 | 44 | But you can use it in your commercial products such as 45 | - a commercial game or simulation project with XML processing feature. 46 | 47 | 2. **Can I use this Software for personal or educational purposes?** 48 | Yes, as long as it does not fall under a restricted use case. 49 | 50 | 3. **When will the Software be fully open source?** 51 | After 4 years after the Licensor **archived** this project. 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PURPOSES AND FEATURES: 2 | - Runtime external window creation with UMG support. 3 | - Runtime **Save and Select File Dialogs** 4 | - File drag drop supports for main game window and external windows. It returns, content paths and content locations. 5 | - If operating system is **Windows 11**, created windows will have **rounded corners**. 6 | - Hover, window move and close detection events. 7 | 8 | # USAGE: 9 | - Place **BP_Template_WinMan** to world. This is your window manager. 10 | - It is in **Plugins/WindowSystem/Content/BPs** 11 | - You can create a new one based on your needs. 12 | - It has to be only **one manager actor** in scene. 13 | 14 | - Create and construct your widget. 15 | 16 | - **Spawn Actor From Class** (use **EachWindow** class) 17 | 18 | - Connect your manager and widget to **Spawn Actor From Class** 19 | 20 | - Set your settings 21 | - You have to give a unique tag **(optionally meaningful)** to your window. 22 | 23 | - All window control functions are virtual functions of spawned EachWindow class object. 24 | 25 | - You can access all created windows with **Manager->MAP_Windows (FName, AEachWindow)**. If you know the tag of window which you want to control, you can access it from this map. 26 | 27 | - If you want to enable file drag drop feature for created window, you have two options. 28 | - You can enable it when set enable **bIsFileDropEnabled** boolean on **Spawn Actor from Class** 29 | - You can enable it in the future with **Set File Drag Drop Support** function. 30 | 31 | - If you want to enable file drag drop feature for main window, just enable **bAllowMainWindow** boolean anytime. 32 | 33 | # DISCLAIMER 34 | - If you close your window accidentally, you will lost your widget as well. This can trigger errors and even crashes with other mechanics which depends on these widgets. 35 | - If you use **HideFromTaskBar** features and accidentally throw your window to the backward, you will have to bring it back with **Bring Window Front** 36 | 37 | # ADDITIONAL CONTROL FUNCTIONS FOR WINDOWS 38 | - Take Screenshot of Window 39 | 40 | - **(Variable)** WindowTag 41 | - **(UMG Widget Variable)** ContentWidget 42 | 43 | - Set Window Opacity 44 | - Set Window State **(Minimmized / Restored / Maximized) 45 | - Set Window Shape **(Give new position and or size with an animation)** 46 | - Set Window Position 47 | - Set Window Title 48 | 49 | - Get Window State **(returns enums Minimized / Restored / Maximized)** 50 | - Get Window Position **(return screen position as FVector2D)** 51 | - Get Window Title 52 | 53 | - Is Window Top Most 54 | - Bring Window Front 55 | 56 | - Toggle Top Most Option 57 | - Toggle Show On Task Bar 58 | - Toggle Opacity 59 | 60 | - Set File Drag Drop Support 61 | 62 | - **(Event)** On Window Hovered 63 | - **(Event)** On Window Moved 64 | - **(Event)** On Window Closed 65 | 66 | # ADDITINAL FUNCTIONS FOR GENERAL USAGE 67 | - Get Main Window Title 68 | - Set Main Window Title 69 | - Select File Dialog 70 | - Save File Dialog 71 | 72 | # WINDOW VARIABLES: 73 | - Is Top Most 74 | 75 | - Has Close 76 | - We recommend to set this false. Because if user close a runtime generated window, all contents and its referances will be gone. So, use it with cautious. 77 | 78 | - Force Volatile 79 | 80 | - Preserve Aspect Ratio 81 | - When user change size of that window, should window preserve it's aspect ratio or not. 82 | 83 | - Minimized 84 | - Create window as minimized or not. 85 | 86 | - Supports Maximize 87 | 88 | - Supports Minimize 89 | 90 | - Set Mirror Window 91 | 92 | - In Window Tag (You should absolutely set this with a meaningful and unique tag without space. Because we use this to record and get windows) 93 | 94 | - In Window Title 95 | - If you give a title, it will be visible on window's title bar. 96 | - It is different than WindowTag as variable aspect but you can use same value. 97 | - You can use more readable values with it. 98 | 99 | - In Tooltip 100 | - When user hovered that window, there will be additional information as tooltip. 101 | 102 | - Window Size 103 | 104 | - Minimum Size 105 | - Minimum acceptable size for that window. 106 | 107 | - Window Position 108 | 109 | - Border Thick 110 | - Thickness between frame and UMG content. 111 | 112 | - In Opacity 113 | - Initial opacity value of that window. 114 | 115 | # PLATFORM: 116 | - This plugin created only for **Windows** operating system and **Unreal Engine 5.** -------------------------------------------------------------------------------- /Resources/Icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Frozen-Projects/WindowSystem/64267d00ed7691bfbc43e25cbe1c83585dcf7982/Resources/Icon128.png -------------------------------------------------------------------------------- /Source/WindowSystem/Private/CustomViewport.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #include "CustomViewport.h" 4 | 5 | #include "Engine/Canvas.h" 6 | #include "CanvasItem.h" 7 | 8 | UCustomViewport::UCustomViewport() : Super(FObjectInitializer::Get()) 9 | { 10 | MaxSplitscreenPlayers = 4; 11 | } 12 | 13 | void UCustomViewport::UpdateActiveSplitscreenType() 14 | { 15 | Super::UpdateActiveSplitscreenType(); 16 | } 17 | 18 | void UCustomViewport::Tick(float DeltaTime) 19 | { 20 | Super::Tick(DeltaTime); 21 | } 22 | 23 | void UCustomViewport::LayoutPlayers() 24 | { 25 | UpdateActiveSplitscreenType(); 26 | 27 | const ESplitScreenType::Type SplitType = GetCurrentSplitscreenConfiguration(); 28 | const TArray& PlayerList = GetOuterUEngine()->GetGamePlayers(this); 29 | const size_t Player_Count = PlayerList.Num(); 30 | 31 | if (Player_Count <= 0) 32 | { 33 | return; 34 | } 35 | 36 | TArray Array_Views; 37 | 38 | if (Player_Count == 1) 39 | { 40 | if (!this->bIsInitialsLoaded || this->LastPlayerCount != Player_Count) 41 | { 42 | this->bIsInitialsLoaded = false; 43 | 44 | PlayerList[0]->Size = FVector2D(0.9f); 45 | PlayerList[0]->Origin = FVector2D(0.05); 46 | 47 | this->bIsInitialsLoaded = true; 48 | this->LastPlayerCount = Player_Count; 49 | } 50 | 51 | for (int32 PlayerIdx = 0; PlayerIdx < Player_Count; PlayerIdx++) 52 | { 53 | FPlayerViews View; 54 | View.Size = PlayerList[PlayerIdx]->Size; 55 | View.Position = (PlayerList[PlayerIdx]->Origin); 56 | Array_Views.Add(View); 57 | } 58 | 59 | DelegateNewLayout.Broadcast(Array_Views); 60 | return; 61 | } 62 | 63 | else if (Player_Count == 2) 64 | { 65 | if (!this->bIsInitialsLoaded || this->LastPlayerCount != Player_Count) 66 | { 67 | this->bIsInitialsLoaded = false; 68 | 69 | // Player 1 = Right 70 | 71 | PlayerList[0]->Size = FVector2D(0.425, 0.9); 72 | PlayerList[0]->Origin = FVector2D(0.525, 0.05); 73 | 74 | // Player 2 = Left 75 | 76 | PlayerList[1]->Size = FVector2D(0.425, 0.9); 77 | PlayerList[1]->Origin = FVector2D(0.05); 78 | 79 | this->bIsInitialsLoaded = true; 80 | this->LastPlayerCount = Player_Count; 81 | } 82 | 83 | for (int32 PlayerIdx = 0; PlayerIdx < Player_Count; PlayerIdx++) 84 | { 85 | FPlayerViews View; 86 | View.Size = PlayerList[PlayerIdx]->Size; 87 | View.Position = (PlayerList[PlayerIdx]->Origin); 88 | Array_Views.Add(View); 89 | } 90 | 91 | DelegateNewLayout.Broadcast(Array_Views); 92 | return; 93 | } 94 | 95 | else if (Player_Count == 3) 96 | { 97 | if (!this->bIsInitialsLoaded || this->LastPlayerCount != Player_Count) 98 | { 99 | this->bIsInitialsLoaded = false; 100 | 101 | // Player 1 = Right 102 | 103 | PlayerList[0]->Size = FVector2D(0.425, 0.9); 104 | PlayerList[0]->Origin = FVector2D(0.525, 0.5); 105 | 106 | // Player 2 = Top Left 107 | 108 | PlayerList[1]->Size = FVector2D(0.425); 109 | PlayerList[1]->Origin = FVector2D(0.05); 110 | 111 | //Player 3 = Bottom Left 112 | 113 | PlayerList[2]->Size = FVector2D(0.425); 114 | PlayerList[2]->Origin = FVector2D(0.05, 0.525); 115 | 116 | this->bIsInitialsLoaded = true; 117 | this->LastPlayerCount = Player_Count; 118 | } 119 | 120 | for (int32 PlayerIdx = 0; PlayerIdx < Player_Count; PlayerIdx++) 121 | { 122 | FPlayerViews View; 123 | View.Size = PlayerList[PlayerIdx]->Size; 124 | View.Position = (PlayerList[PlayerIdx]->Origin); 125 | Array_Views.Add(View); 126 | } 127 | 128 | DelegateNewLayout.Broadcast(Array_Views); 129 | return; 130 | } 131 | 132 | else if (Player_Count == 4) 133 | { 134 | if (!this->bIsInitialsLoaded || this->LastPlayerCount != Player_Count) 135 | { 136 | this->bIsInitialsLoaded = false; 137 | 138 | // Player 1 = Bottom Right 139 | 140 | PlayerList[0]->Size = FVector2D(0.425); 141 | PlayerList[0]->Origin = FVector2D(0.525); 142 | 143 | // Player 2 = Top Right 144 | 145 | PlayerList[1]->Size = FVector2D(0.425); 146 | PlayerList[1]->Origin = FVector2D(0.525, 0.05); 147 | 148 | // Player 3 = Top Left 149 | 150 | PlayerList[2]->Size = FVector2D(0.425); 151 | PlayerList[2]->Origin = FVector2D(0.05); 152 | 153 | // Player 4 = Bottom Left 154 | 155 | PlayerList[3]->Size = FVector2D(0.425); 156 | PlayerList[3]->Origin = FVector2D(0.05, 0.525); 157 | 158 | this->bIsInitialsLoaded = true; 159 | this->LastPlayerCount = Player_Count; 160 | } 161 | 162 | for (int32 PlayerIdx = 0; PlayerIdx < Player_Count; PlayerIdx++) 163 | { 164 | FPlayerViews View; 165 | View.Size = PlayerList[PlayerIdx]->Size; 166 | View.Position = (PlayerList[PlayerIdx]->Origin); 167 | Array_Views.Add(View); 168 | } 169 | 170 | DelegateNewLayout.Broadcast(Array_Views); 171 | return; 172 | } 173 | 174 | else if (Player_Count > 4) 175 | { 176 | UE_LOG(LogTemp, Fatal, TEXT("Player count shouldn't exceed 4. Requested number = %d"), Player_Count); 177 | return; 178 | } 179 | } 180 | 181 | void UCustomViewport::Draw(FViewport* In_Viewport, FCanvas* In_SceneCanvas) 182 | { 183 | Super::Draw(In_Viewport, In_SceneCanvas); 184 | 185 | // Put your logic to change background after Super::Draw() 186 | 187 | if (IsValid(this->BG_Material) && this->BG_Material->GetRenderProxy() && !this->bStopBackground) 188 | { 189 | const FVector2D RectSize = In_Viewport->GetSizeXY(); 190 | const FVector2D UV_Top_Left = FVector2D(0.0f, 0.0f); 191 | const FVector2D UV_Bottom_Right = FVector2D(1.0f, 1.0f); 192 | 193 | FCanvasTileItem TileItem(FVector2D(0, 0), this->BG_Material->GetRenderProxy(), RectSize, UV_Top_Left, UV_Bottom_Right); 194 | TileItem.BlendMode = SE_BLEND_Opaque; 195 | 196 | In_SceneCanvas->DrawItem(TileItem); 197 | } 198 | } 199 | 200 | bool UCustomViewport::ChangePlayerViewSize(const int32 PlayerId, FVector2D NewRatio, FVector2D NewOrigin) 201 | { 202 | UEngine* const REF_Engine = GameInstance->GetEngine(); 203 | const int32 NumPlayers = REF_Engine->GetNumGamePlayers(this); 204 | 205 | if (NumPlayers > PlayerId + 1) 206 | { 207 | return false; 208 | } 209 | 210 | const TArray& PlayerList = GetOuterUEngine()->GetGamePlayers(this); 211 | PlayerList[PlayerId]->Size = NewRatio; 212 | PlayerList[PlayerId]->Origin = NewOrigin; 213 | 214 | return true; 215 | } 216 | 217 | bool UCustomViewport::SetBackgrounMaterial(UMaterialInterface* In_Material) 218 | { 219 | if (!IsValid(In_Material)) 220 | { 221 | return false; 222 | } 223 | 224 | this->BG_Material = In_Material; 225 | return true; 226 | } 227 | 228 | UMaterialInterface* UCustomViewport::GetBackgroundMaterial() 229 | { 230 | if (!IsValid(this->BG_Material)) 231 | { 232 | return nullptr; 233 | } 234 | 235 | return this->BG_Material; 236 | } 237 | 238 | void UCustomViewport::ToggleBackground(bool bStop) 239 | { 240 | this->bStopBackground = bStop; 241 | } -------------------------------------------------------------------------------- /Source/WindowSystem/Private/DragDropHandler.cpp: -------------------------------------------------------------------------------- 1 | #include "DragDropHandler.h" 2 | #include "WindowManager.h" 3 | 4 | bool FDragDropHandler::ProcessMessage(HWND Hwnd, uint32 Message, WPARAM WParam, LPARAM LParam, int32& OutResult) 5 | { 6 | TObjectPtr Viewport = GEngine->GameViewport; 7 | 8 | if (!IsValid(Viewport)) 9 | { 10 | return false; 11 | } 12 | 13 | UWorld* World = Viewport->GetWorld(); 14 | 15 | if (!IsValid(World)) 16 | { 17 | return false; 18 | } 19 | 20 | UFF_WindowSubystem* WindowSubsystem = World->GetGameInstance()->GetSubsystem(); 21 | 22 | if (!IsValid(WindowSubsystem)) 23 | { 24 | return false; 25 | } 26 | 27 | HWND MainWindowHandle; 28 | HDROP DropInfo = (HDROP)WParam; 29 | 30 | // Drop Location. 31 | POINT DropLocation; 32 | 33 | // Out Structure. 34 | FDroppedFileStruct DropFileStruct; 35 | TArray OutArray; 36 | 37 | // Read Regedit To Get Windows Build Number. 38 | HKEY hKey; 39 | LONG Result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_READ, &hKey); 40 | DWORD BufferSize; 41 | RegQueryValueEx(hKey, L"CurrentBuildNumber", 0, nullptr, NULL, &BufferSize); 42 | TCHAR* Buffer = (TCHAR*)malloc(BufferSize); 43 | RegQueryValueEx(hKey, L"CurrentBuildNumber", 0, nullptr, reinterpret_cast(Buffer), &BufferSize); 44 | int32 BuildNumber = FCString::Atoi(Buffer); 45 | 46 | free(Buffer); 47 | Buffer = nullptr; 48 | 49 | switch (Message) 50 | { 51 | case WM_ERASEBKGND: 52 | return false; 53 | 54 | case WM_PAINT: 55 | { 56 | if (BuildNumber >= 22000) 57 | { 58 | /* 59 | * Window Roundness Preference. 60 | * DWMWCP_DEFAULT = 0 61 | * DWMWCP_DONOTROUND = 1 62 | * DWMWCP_ROUND = 2 63 | * DWMWCP_ROUNDSMALL = 3 64 | */ 65 | DWM_WINDOW_CORNER_PREFERENCE preference = DWMWCP_ROUND; 66 | DwmSetWindowAttribute(Hwnd, DWMWA_WINDOW_CORNER_PREFERENCE, &preference, sizeof(preference)); 67 | } 68 | 69 | return true; 70 | } 71 | 72 | case WM_DROPFILES: 73 | { 74 | // If message sender window is main window and user not want to get files on it, return false. 75 | if (WindowSubsystem->bAllowMainWindow == false) 76 | { 77 | MainWindowHandle = reinterpret_cast(GEngine->GameViewport->GetWindow()->GetNativeWindow()->GetOSWindowHandle()); 78 | if (Hwnd == MainWindowHandle) 79 | { 80 | return false; 81 | } 82 | } 83 | 84 | DragQueryPoint(DropInfo, &DropLocation); 85 | 86 | const UINT DroppedFileCount = DragQueryFileA(DropInfo, 0xFFFFFFFF, NULL, NULL); 87 | for (UINT FileIndex = 0; FileIndex < DroppedFileCount; FileIndex++) 88 | { 89 | UINT PathSize = DragQueryFileA(DropInfo, FileIndex, NULL, 0); 90 | if (PathSize > 0) 91 | { 92 | DropFileStruct.DropLocation = FVector2D(DropLocation.x, DropLocation.y); 93 | 94 | char* DroppedFile = (char*)malloc(size_t(PathSize)); 95 | DragQueryFileA(DropInfo, FileIndex, DroppedFile, PathSize + 1); 96 | 97 | if (GetFileAttributesA(DroppedFile) != FILE_ATTRIBUTE_DIRECTORY) 98 | { 99 | DropFileStruct.FilePath = DroppedFile; 100 | DropFileStruct.bIsFolder = false; 101 | } 102 | 103 | if (GetFileAttributesA(DroppedFile) == FILE_ATTRIBUTE_DIRECTORY) 104 | { 105 | DropFileStruct.FilePath = DroppedFile; 106 | DropFileStruct.bIsFolder = true; 107 | } 108 | 109 | OutArray.Add(DropFileStruct); 110 | 111 | free(DroppedFile); 112 | DroppedFile = nullptr; 113 | } 114 | } 115 | 116 | WindowSubsystem->OnFileDrop.Broadcast(OutArray); 117 | OutArray.Empty(); 118 | 119 | DragFinish(DropInfo); 120 | 121 | return true; 122 | } 123 | 124 | default: 125 | return false; 126 | } 127 | } -------------------------------------------------------------------------------- /Source/WindowSystem/Private/WindowInstance.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #include "WindowInstance.h" 4 | 5 | // UE Includes. 6 | #include "Framework/Application/SWindowTitleBar.h" 7 | 8 | // Sets default values. 9 | AEachWindow_SWindow::AEachWindow_SWindow() 10 | { 11 | // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. 12 | PrimaryActorTick.bCanEverTick = true; 13 | } 14 | 15 | // Called every frame. 16 | void AEachWindow_SWindow::Tick(float DeltaTime) 17 | { 18 | Super::Tick(DeltaTime); 19 | } 20 | 21 | // Called when the game starts or when spawned. 22 | void AEachWindow_SWindow::BeginPlay() 23 | { 24 | Super::BeginPlay(); 25 | 26 | this->WindowSubsystem = GEngine->GetCurrentPlayWorld()->GetGameInstance()->GetSubsystem(); 27 | 28 | if (!IsValid(WindowSubsystem)) 29 | { 30 | UE_LOG(LogTemp, Warning, TEXT("Window creation aborted because \"Manager\" are not valid for: %s"), *FString(WindowTag.ToString())); 31 | this->Destroy(); 32 | return; 33 | } 34 | 35 | if (!IsValid(ContentWidget)) 36 | { 37 | UE_LOG(LogTemp, Warning, TEXT("Window creation aborted because \"ContentWidget\" are not valid for: %s"), *FString(WindowTag.ToString())); 38 | this->Destroy(); 39 | return; 40 | } 41 | 42 | // If there is a problem with window creation, actor class will be destroyed and won't go any further. 43 | if (!this->CreateNewWindow()) 44 | { 45 | UE_LOG(LogTemp, Warning, TEXT("Window creation aborted because there is a problem in \"CreateNewWindow\" function for: %s"), *FString(WindowTag.ToString())); 46 | this->Destroy(); 47 | return; 48 | } 49 | 50 | // Add created window actor class to the list. 51 | WindowSubsystem->MAP_Windows.Add(WindowTag, this); 52 | 53 | // Start window hover detection. 54 | 55 | if (this->bEnableHoverDetection) 56 | { 57 | Hover_Delegate = FTimerDelegate::CreateUObject(this, &AEachWindow_SWindow::NotifyWindowHovered, false); 58 | GEngine->GetCurrentPlayWorld()->GetTimerManager().SetTimer(Hover_Timer, Hover_Delegate, this->HoverDetectionTime, true); 59 | } 60 | } 61 | 62 | // Called when the game ends. 63 | void AEachWindow_SWindow::EndPlay(const EEndPlayReason::Type EndPlayReason) 64 | { 65 | if (Hover_Timer.IsValid()) 66 | { 67 | Hover_Timer.Invalidate(); 68 | } 69 | 70 | if (IsValid(this->WindowSubsystem)) 71 | { 72 | this->WindowSubsystem->OnWindowClosed.Broadcast(WindowTag); 73 | } 74 | 75 | this->CloseWindowCallback(); 76 | 77 | Super::EndPlay(EndPlayReason); 78 | } 79 | 80 | // Protected Functions. 81 | 82 | void AEachWindow_SWindow::NotifyWindowClosed(const TSharedRef& Window) 83 | { 84 | if (IsValid(this)) 85 | { 86 | this->Destroy(); 87 | } 88 | } 89 | 90 | void AEachWindow_SWindow::NotifyWindowMoved(const TSharedRef& Window) 91 | { 92 | if (!IsValid(this)) 93 | { 94 | return; 95 | } 96 | 97 | if (!IsValid(this->WindowSubsystem)) 98 | { 99 | return; 100 | } 101 | 102 | if (!this->WindowPtr.IsValid()) 103 | { 104 | return; 105 | } 106 | 107 | this->WindowSubsystem->OnWindowMoved.Broadcast(this); 108 | } 109 | 110 | void AEachWindow_SWindow::NotifyWindowHovered(bool bUseDirectHover) 111 | { 112 | if (!IsValid(this)) 113 | { 114 | return; 115 | } 116 | 117 | if (!IsValid(this->WindowSubsystem)) 118 | { 119 | return; 120 | } 121 | 122 | if (!this->WindowPtr.IsValid()) 123 | { 124 | return; 125 | } 126 | 127 | if (!(bUseDirectHover ? WindowPtr.ToSharedRef().Get().IsDirectlyHovered() : WindowPtr.ToSharedRef().Get().IsHovered())) 128 | { 129 | return; 130 | } 131 | 132 | this->WindowSubsystem->OnWindowHovered.Broadcast(this); 133 | } 134 | 135 | bool AEachWindow_SWindow::CreateNewWindow() 136 | { 137 | if (!IsValid(this->WindowSubsystem)) 138 | { 139 | UE_LOG(LogTemp, Error, TEXT("Window manager is not valid : %s"), *FString(WindowTag.ToString())); 140 | return false; 141 | } 142 | 143 | if (WindowTag.IsNone() || WindowTag.ToString().IsEmpty()) 144 | { 145 | UE_LOG(LogTemp, Error, TEXT("Window tag is empty.")); 146 | return false; 147 | } 148 | 149 | if (this->WindowSubsystem->MAP_Windows.Contains(WindowTag)) 150 | { 151 | UE_LOG(LogTemp, Error, TEXT("There is a window with that tag.")); 152 | return false; 153 | } 154 | 155 | // Styles 156 | EWindowType WindowType = EWindowType::GameWindow; 157 | switch (WindowTypeBp) 158 | { 159 | case EWindowTypeBp::Normal: 160 | WindowType = EWindowType::Normal; 161 | break; 162 | case EWindowTypeBp::Menu: 163 | WindowType = EWindowType::Menu; 164 | break; 165 | case EWindowTypeBp::ToolTip: 166 | WindowType = EWindowType::ToolTip; 167 | break; 168 | case EWindowTypeBp::Notification: 169 | WindowType = EWindowType::Notification; 170 | break; 171 | case EWindowTypeBp::CursorDecorator: 172 | WindowType = EWindowType::CursorDecorator; 173 | break; 174 | case EWindowTypeBp::GameWindow: 175 | WindowType = EWindowType::GameWindow; 176 | break; 177 | } 178 | 179 | // Blueprints exposed UObject should contain TSharedPtr NOT TSharedRef. 180 | TSharedPtr WidgetWindow = SNew(SWindow) 181 | .bDragAnywhere(true) 182 | .ClientSize(WindowSize) 183 | .LayoutBorder(BorderThick) 184 | .UserResizeBorder(BorderThick) 185 | .Title(InWindowTitle) 186 | .ToolTipText(InToolTip) 187 | .ForceVolatile(bForceVolatile) 188 | .ShouldPreserveAspectRatio(bPreserveAspectRatio) 189 | .IsInitiallyMinimized(bMinimized) 190 | .FocusWhenFirstShown(true) 191 | .HasCloseButton(bHasClose) 192 | .SupportsMinimize(bSupportsMinimized) 193 | .SupportsMaximize(bSupportsMaximized) 194 | .SupportsTransparency(EWindowTransparency::PerWindow) 195 | .IsTopmostWindow(bIsTopMost) 196 | .Type(WindowType) 197 | .UseOSWindowBorder(bUseNativeBorder) 198 | .AdjustInitialSizeAndPositionForDPIScale(true) 199 | ; 200 | 201 | if (!WidgetWindow.IsValid()) 202 | { 203 | UE_LOG(LogTemp, Error, TEXT("Widget window pointer is not valid : %s"), *FString(WindowTag.ToString())); 204 | return false; 205 | } 206 | 207 | FWindowSizeLimits SizeLimits = FWindowSizeLimits(); 208 | SizeLimits.SetMinWidth(MinSize.X); 209 | SizeLimits.SetMinHeight(MinSize.Y); 210 | 211 | if (MaxSize.X == 0) 212 | { 213 | MaxSize.X = GEngine->GetGameUserSettings()->GetScreenResolution().X; 214 | } 215 | 216 | else 217 | { 218 | SizeLimits.SetMaxWidth(MaxSize.X); 219 | } 220 | 221 | if (MaxSize.Y == 0) 222 | { 223 | MaxSize.Y = GEngine->GetGameUserSettings()->GetScreenResolution().Y; 224 | } 225 | 226 | else 227 | { 228 | SizeLimits.SetMaxHeight(MaxSize.Y); 229 | } 230 | 231 | WidgetWindow->SetContent(ContentWidget->TakeWidget()); 232 | WidgetWindow->SetAllowFastUpdate(true); 233 | WidgetWindow->SetMirrorWindow(bSetMirrorWindow); 234 | WidgetWindow->MoveWindowTo(StartPosition); 235 | WidgetWindow->SetTag(WindowTag); 236 | WidgetWindow->SetNativeWindowButtonsVisibility(bHasClose); 237 | WidgetWindow->SetForegroundColor(TitleColor); 238 | WidgetWindow->SetSizeLimits(SizeLimits); 239 | WidgetWindow->SetOnWindowMoved(FOnWindowClosed::CreateUObject(this, &AEachWindow_SWindow::NotifyWindowMoved)); 240 | WidgetWindow->SetOnWindowClosed(FOnWindowClosed::CreateUObject(this, &AEachWindow_SWindow::NotifyWindowClosed)); 241 | 242 | // Add created window to Slate. 243 | FSlateApplication::Get().AddWindow(WidgetWindow.ToSharedRef(), true); 244 | 245 | // If we use this and hide window from taskbar, minimized window will still be visible at left corner as small form. 246 | //FSlateApplication::Get().AddWindowAsNativeChild(WidgetWindow.ToSharedRef(), GEngine->GameViewport->GetWindow().ToSharedRef()); 247 | 248 | // Hide Window from Taskbar. 249 | HWND WidgetWindowHandle = reinterpret_cast(WidgetWindow.ToSharedRef().Get().GetNativeWindow().ToSharedRef().Get().GetOSWindowHandle()); 250 | 251 | if (bShowOnTaskBar == true) 252 | { 253 | SetWindowLongPtr(WidgetWindowHandle, GWL_EXSTYLE, WS_EX_APPWINDOW | WS_EX_TRANSPARENT); 254 | } 255 | 256 | else 257 | { 258 | SetWindowLongPtr(WidgetWindowHandle, GWL_EXSTYLE, WS_EX_NOACTIVATE | WS_EX_TRANSPARENT); 259 | } 260 | 261 | this->WindowPtr = WidgetWindow; 262 | 263 | return true; 264 | } 265 | 266 | void AEachWindow_SWindow::CloseWindowCallback() 267 | { 268 | if (IsValid(ContentWidget)) 269 | { 270 | ContentWidget->ReleaseSlateResources(true); 271 | } 272 | 273 | if (WindowPtr.IsValid()) 274 | { 275 | WindowPtr->HideWindow(); 276 | WindowPtr->RequestDestroyWindow(); 277 | WindowPtr.Reset(); 278 | } 279 | 280 | if (IsValid(this->WindowSubsystem) && this->WindowSubsystem->MAP_Windows.Contains(WindowTag)) 281 | { 282 | this->WindowSubsystem->MAP_Windows.Remove(WindowTag); 283 | } 284 | } 285 | 286 | // UFUNCTIONS. 287 | 288 | bool AEachWindow_SWindow::SetFileDragDropSupport() 289 | { 290 | if (!WindowPtr.IsValid()) 291 | { 292 | return false; 293 | } 294 | 295 | if (bIsFileDropEnabled) 296 | { 297 | return false; 298 | } 299 | 300 | bIsFileDropEnabled = true; 301 | 302 | HWND WidgetWindowHandle = reinterpret_cast(WindowPtr.ToSharedRef().Get().GetNativeWindow().ToSharedRef().Get().GetOSWindowHandle()); 303 | DragAcceptFiles(WidgetWindowHandle, true); 304 | 305 | return true; 306 | } 307 | 308 | bool AEachWindow_SWindow::TakeSSWindow(UTextureRenderTarget2D*& OutTextureRenderTarget2D) 309 | { 310 | if (!WindowPtr.IsValid()) 311 | { 312 | return false; 313 | } 314 | 315 | AEachWindow_SWindow::BringWindowFront(false); 316 | 317 | FVector2D TargetWindowSize = WindowPtr->GetClientSizeInScreen(); 318 | UTextureRenderTarget2D* TextureTarget = FWidgetRenderer::CreateTargetFor(TargetWindowSize, TextureFilter::TF_Default, false); 319 | 320 | FWidgetRenderer* WidgetRenderer = new FWidgetRenderer(true); 321 | WidgetRenderer->DrawWidget(TextureTarget, ContentWidget->TakeWidget(), TargetWindowSize, 0, false); 322 | 323 | if (!IsValid(TextureTarget)) 324 | { 325 | return false; 326 | } 327 | 328 | OutTextureRenderTarget2D = TextureTarget; 329 | return true; 330 | } 331 | 332 | bool AEachWindow_SWindow::IsWindowTopMost(bool bUseNative) 333 | { 334 | if (!WindowPtr.IsValid()) 335 | { 336 | return false; 337 | } 338 | 339 | if (bUseNative == true) 340 | { 341 | HWND WidgetWindowHandle = reinterpret_cast(WindowPtr.ToSharedRef().Get().GetNativeWindow().ToSharedRef().Get().GetOSWindowHandle()); 342 | 343 | if (GetWindowLong(WidgetWindowHandle, GWL_EXSTYLE) & WS_EX_TOPMOST) 344 | { 345 | return true; 346 | } 347 | 348 | else 349 | { 350 | return false; 351 | } 352 | } 353 | 354 | else 355 | { 356 | return WindowPtr->IsTopmostWindow(); 357 | } 358 | } 359 | 360 | bool AEachWindow_SWindow::BringWindowFront(bool bFlashWindow) 361 | { 362 | if (WindowPtr.IsValid() == false) 363 | { 364 | return false; 365 | } 366 | 367 | WindowPtr.Get()->BringToFront(); 368 | 369 | if (bFlashWindow == true) 370 | { 371 | WindowPtr.Get()->FlashWindow(); 372 | } 373 | 374 | return true; 375 | } 376 | 377 | bool AEachWindow_SWindow::ToggleTopMostOption() 378 | { 379 | if (WindowPtr.IsValid() == false) 380 | { 381 | return false; 382 | } 383 | 384 | HWND WidgetWindowHandle = reinterpret_cast(WindowPtr.ToSharedRef().Get().GetNativeWindow().ToSharedRef().Get().GetOSWindowHandle()); 385 | 386 | if (GetWindowLong(WidgetWindowHandle, GWL_EXSTYLE) & WS_EX_TOPMOST) 387 | { 388 | SetWindowPos(WidgetWindowHandle, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); 389 | return true; 390 | } 391 | 392 | else 393 | { 394 | SetWindowPos(WidgetWindowHandle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); 395 | return true; 396 | } 397 | } 398 | 399 | bool AEachWindow_SWindow::ToggleShowOnTaskBar(bool In_bShowOnTaskBar) 400 | { 401 | if (WindowPtr.IsValid() == false) 402 | { 403 | return false; 404 | } 405 | 406 | HWND WidgetWindowHandle = reinterpret_cast(WindowPtr.ToSharedRef().Get().GetNativeWindow().ToSharedRef().Get().GetOSWindowHandle()); 407 | 408 | if (In_bShowOnTaskBar == true) 409 | { 410 | if (this->bIsTransparent == true) 411 | { 412 | SetWindowLongPtr(WidgetWindowHandle, GWL_EXSTYLE, WS_EX_APPWINDOW | WS_EX_TRANSPARENT | WS_EX_LAYERED); 413 | } 414 | 415 | else 416 | { 417 | SetWindowLongPtr(WidgetWindowHandle, GWL_EXSTYLE, WS_EX_APPWINDOW); 418 | } 419 | 420 | AEachWindow_SWindow::BringWindowFront(true); 421 | 422 | this->bShowOnTaskBar = In_bShowOnTaskBar; 423 | } 424 | 425 | else 426 | { 427 | if (this->bIsTransparent == true) 428 | { 429 | SetWindowLongPtr(WidgetWindowHandle, GWL_EXSTYLE, WS_EX_NOACTIVATE | WS_EX_TRANSPARENT | WS_EX_LAYERED); 430 | } 431 | 432 | else 433 | { 434 | SetWindowLongPtr(WidgetWindowHandle, GWL_EXSTYLE, WS_EX_NOACTIVATE); 435 | } 436 | 437 | this->bShowOnTaskBar = In_bShowOnTaskBar; 438 | } 439 | 440 | return true; 441 | } 442 | 443 | bool AEachWindow_SWindow::ToggleOpacity(bool bEnable, bool bPassDragDrop) 444 | { 445 | if (WindowPtr.IsValid() == false) 446 | { 447 | return false; 448 | } 449 | 450 | if (bIsFileDropEnabled == true && bPassDragDrop == false) 451 | { 452 | return false; 453 | } 454 | 455 | HWND WidgetWindowHandle = reinterpret_cast(WindowPtr.ToSharedRef().Get().GetNativeWindow().ToSharedRef().Get().GetOSWindowHandle()); 456 | 457 | if (bEnable) 458 | { 459 | if (bShowOnTaskBar == true) 460 | { 461 | SetWindowLongPtr(WidgetWindowHandle, GWL_EXSTYLE, WS_EX_APPWINDOW | WS_EX_TRANSPARENT | WS_EX_LAYERED); 462 | } 463 | 464 | else 465 | { 466 | SetWindowLongPtr(WidgetWindowHandle, GWL_EXSTYLE, WS_EX_NOACTIVATE | WS_EX_TRANSPARENT | WS_EX_LAYERED); 467 | } 468 | 469 | bIsTransparent = true; 470 | } 471 | 472 | else 473 | { 474 | if (bShowOnTaskBar == true) 475 | { 476 | SetWindowLongPtr(WidgetWindowHandle, GWL_EXSTYLE, WS_EX_APPWINDOW | WS_EX_TRANSPARENT); 477 | } 478 | 479 | else 480 | { 481 | SetWindowLongPtr(WidgetWindowHandle, GWL_EXSTYLE, WS_EX_NOACTIVATE | WS_EX_TRANSPARENT); 482 | } 483 | 484 | bIsTransparent = false; 485 | } 486 | 487 | return true; 488 | } 489 | 490 | bool AEachWindow_SWindow::SetWindowOpacity(float NewOpacity) 491 | { 492 | if (WindowPtr.IsValid() == false) 493 | { 494 | return false; 495 | } 496 | 497 | WindowPtr->SetOpacity(NewOpacity); 498 | return true; 499 | } 500 | 501 | bool AEachWindow_SWindow::SetWindowState(EWindowState OutWindowState) 502 | { 503 | if (!WindowPtr.IsValid()) 504 | { 505 | return false; 506 | } 507 | 508 | switch (OutWindowState) 509 | { 510 | 511 | case EWindowState::None: 512 | return false; 513 | 514 | case EWindowState::Minimized: 515 | WindowPtr->Minimize(); 516 | return true; 517 | break; 518 | 519 | case EWindowState::Restored: 520 | WindowPtr->Restore(); 521 | return true; 522 | break; 523 | 524 | case EWindowState::Maximized: 525 | WindowPtr->Maximize(); 526 | return true; 527 | break; 528 | } 529 | 530 | return true; 531 | } 532 | 533 | bool AEachWindow_SWindow::SetWindowShape(FMargin InExtend, float InDuration, float NewOpacity) 534 | { 535 | if (!WindowPtr.IsValid()) 536 | { 537 | return false; 538 | } 539 | 540 | FSlateRect CurrentShape = WindowPtr.ToSharedRef().Get().GetRectInScreen(); 541 | 542 | FSlateRect NewShape; 543 | NewShape.Top = CurrentShape.Top + InExtend.Top; 544 | NewShape.Left = CurrentShape.Left + InExtend.Left; 545 | NewShape.Bottom = CurrentShape.Bottom + InExtend.Bottom; 546 | NewShape.Right = CurrentShape.Right + InExtend.Right; 547 | 548 | FCurveSequence CurveSequence; 549 | CurveSequence.AddCurve(0, InDuration, ECurveEaseFunction::CubicInOut); 550 | 551 | WindowPtr.ToSharedRef().Get().MorphToShape(CurveSequence, NewOpacity, NewShape); 552 | 553 | return true; 554 | } 555 | 556 | bool AEachWindow_SWindow::SetWindowPosition(FVector2D InNewPosition) 557 | { 558 | if (!WindowPtr.IsValid()) 559 | { 560 | return false; 561 | } 562 | 563 | WindowPtr.Get()->MoveWindowTo(InNewPosition); 564 | return true; 565 | } 566 | 567 | bool AEachWindow_SWindow::SetWindowTitle(FText InNewTitle) 568 | { 569 | if (!WindowPtr.IsValid()) 570 | { 571 | return false; 572 | } 573 | 574 | WindowPtr.Get()->SetTitle(InNewTitle); 575 | 576 | return true; 577 | } 578 | 579 | bool AEachWindow_SWindow::GetWindowState(EWindowState& OutWindowState) 580 | { 581 | if (!WindowPtr.IsValid()) 582 | { 583 | return false; 584 | } 585 | 586 | WINDOWPLACEMENT WindowPlacement; 587 | GetWindowPlacement(reinterpret_cast(WindowPtr.ToSharedRef().Get().GetNativeWindow().ToSharedRef().Get().GetOSWindowHandle()), &WindowPlacement); 588 | 589 | switch (WindowPlacement.showCmd) 590 | { 591 | case SW_NORMAL: 592 | 593 | OutWindowState = EWindowState::Restored; 594 | return true; 595 | 596 | case SW_MAXIMIZE: 597 | 598 | OutWindowState = EWindowState::Maximized; 599 | return true; 600 | 601 | case SW_SHOWMINIMIZED: 602 | 603 | OutWindowState = EWindowState::Minimized; 604 | return true; 605 | } 606 | 607 | return false; 608 | } 609 | 610 | bool AEachWindow_SWindow::GetWindowPosition(FVector2D& OutPosition) 611 | { 612 | if (!WindowPtr.IsValid()) 613 | { 614 | return false; 615 | } 616 | 617 | OutPosition = WindowPtr->GetPositionInScreen(); 618 | return true; 619 | } 620 | 621 | bool AEachWindow_SWindow::GetWindowTitle(FText& OutWindowTitle) 622 | { 623 | if (!WindowPtr.IsValid()) 624 | { 625 | OutWindowTitle = FText::FromString(TEXT("")); 626 | return false; 627 | } 628 | 629 | OutWindowTitle = WindowPtr.ToSharedRef().Get().GetTitle(); 630 | return true; 631 | } -------------------------------------------------------------------------------- /Source/WindowSystem/Private/WindowManager.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #include "WindowManager.h" 4 | 5 | // Custom Includes. 6 | #include "WindowInstance.h" // CloseAllWindows -> Destrow window actor. 7 | 8 | void UFF_WindowSubystem::Initialize(FSubsystemCollectionBase& Collection) 9 | { 10 | Super::Initialize(Collection); 11 | FWorldDelegates::OnWorldTickStart.AddUObject(this, &UFF_WindowSubystem::OnWorldTickStart); 12 | } 13 | 14 | void UFF_WindowSubystem::Deinitialize() 15 | { 16 | if (this->MouseHook_Color) 17 | { 18 | UnhookWindowsHookEx(MouseHook_Color); 19 | this->LastPickedColor = nullptr; 20 | } 21 | 22 | this->RemoveDragDropHandlerFromMV(); 23 | this->CloseAllWindows(); 24 | 25 | Super::Deinitialize(); 26 | } 27 | 28 | void UFF_WindowSubystem::OnWorldTickStart(UWorld* World, ELevelTick TickType, float DeltaTime) 29 | { 30 | if (IsValid(World)) 31 | { 32 | this->CustomViewport = Cast(GEngine->GameViewport.Get()); 33 | 34 | if (IsValid(this->CustomViewport)) 35 | { 36 | this->DetectLayoutChanges(); 37 | } 38 | 39 | // We need to addd handler at every tick. 40 | this->AddDragDropHandlerToMV(); 41 | } 42 | } 43 | 44 | void UFF_WindowSubystem::AddDragDropHandlerToMV() 45 | { 46 | if (!this->CustomViewport) 47 | { 48 | return; 49 | } 50 | 51 | HWND WindowHandle = reinterpret_cast(this->CustomViewport->GetWindow()->GetNativeWindow()->GetOSWindowHandle()); 52 | 53 | DragAcceptFiles(WindowHandle, true); 54 | 55 | FSlateApplication& SlateApplication = FSlateApplication::Get(); 56 | 57 | if (!SlateApplication.IsInitialized()) 58 | { 59 | return; 60 | } 61 | 62 | if (!SlateApplication.IsActive()) 63 | { 64 | return; 65 | } 66 | 67 | TSharedPtr GenericApp = SlateApplication.GetPlatformApplication(); 68 | 69 | if (!GenericApp.IsValid()) 70 | { 71 | return; 72 | } 73 | 74 | FWindowsApplication* WindowsApplication = (FWindowsApplication*)GenericApp.Get(); 75 | 76 | if (WindowsApplication) 77 | { 78 | WindowsApplication->AddMessageHandler(DragDropHandler); 79 | } 80 | } 81 | 82 | void UFF_WindowSubystem::RemoveDragDropHandlerFromMV() 83 | { 84 | FSlateApplication& SlateApplication = FSlateApplication::Get(); 85 | 86 | if (!SlateApplication.IsInitialized()) 87 | { 88 | return; 89 | } 90 | 91 | if (!SlateApplication.IsActive()) 92 | { 93 | return; 94 | } 95 | 96 | TSharedPtr GenericApp = SlateApplication.GetPlatformApplication(); 97 | 98 | if (!GenericApp.IsValid()) 99 | { 100 | return; 101 | } 102 | 103 | FWindowsApplication* WindowsApplication = (FWindowsApplication*)GenericApp.Get(); 104 | 105 | if (WindowsApplication) 106 | { 107 | WindowsApplication->RemoveMessageHandler(DragDropHandler); 108 | } 109 | } 110 | 111 | bool UFF_WindowSubystem::CompareViews(TMap A, TMap B) 112 | { 113 | if (A.Num() != B.Num()) 114 | { 115 | return false; 116 | } 117 | 118 | TArray A_Keys; 119 | A.GenerateKeyArray(A_Keys); 120 | 121 | TArray A_Values; 122 | A.GenerateValueArray(A_Values); 123 | 124 | TArray B_Keys; 125 | B.GenerateKeyArray(B_Keys); 126 | 127 | TArray B_Values; 128 | B.GenerateValueArray(B_Values); 129 | 130 | if (A_Keys == B_Keys && A_Values == B_Values) 131 | { 132 | return true; 133 | } 134 | 135 | else 136 | { 137 | return false; 138 | } 139 | } 140 | 141 | void UFF_WindowSubystem::DetectLayoutChanges() 142 | { 143 | if (!this->CustomViewport) 144 | { 145 | return; 146 | } 147 | 148 | this->CustomViewport->DelegateNewLayout.AddLambda([this](const TArray& Views) 149 | { 150 | this->ChangeBackgroundOnNewPlayer(Views); 151 | this->OnLayoutChanged.Broadcast(Views); 152 | } 153 | ); 154 | } 155 | 156 | void UFF_WindowSubystem::ChangeBackgroundOnNewPlayer(TArray const& Out_Views) 157 | { 158 | if (!this->CustomViewport) 159 | { 160 | this->LastError = "Custom viewport is not valid !"; 161 | return; 162 | } 163 | 164 | if (Out_Views.IsEmpty()) 165 | { 166 | this->LastError = "Views are empty !"; 167 | return; 168 | } 169 | 170 | if (!IsValid(this->MAT_BG)) 171 | { 172 | this->LastError = "Background material is not valid !"; 173 | return; 174 | } 175 | 176 | if (!IsValid(this->MAT_Brush)) 177 | { 178 | this->LastError = "Brush material is not valid !"; 179 | return; 180 | } 181 | 182 | if (this->CanvasName.IsNone()) 183 | { 184 | this->LastError = "Canvas name is empty !"; 185 | return; 186 | } 187 | 188 | FVector2D ViewportSize = FVector2D(); 189 | this->CustomViewport->GetViewportSize(ViewportSize); 190 | 191 | if (ViewportSize == FVector2D(0.f)) 192 | { 193 | this->LastError = "Viewport size shouldn't be zero !"; 194 | return; 195 | } 196 | 197 | TMap Temp_Views; 198 | 199 | for (const FPlayerViews Each_View : Out_Views) 200 | { 201 | const FVector2D ActualPosition = ViewportSize * Each_View.Position; 202 | const FVector2D ActualSize = ViewportSize * Each_View.Size; 203 | 204 | TMap Temp_ViewLayout; 205 | Temp_ViewLayout.Add("Full Size", ViewportSize); 206 | Temp_ViewLayout.Add("Actual Position", ActualPosition); 207 | Temp_ViewLayout.Add("Actual Size", ActualSize); 208 | Temp_ViewLayout.Add("UV Position", Each_View.Position); 209 | Temp_ViewLayout.Add("UV Size", Each_View.Size); 210 | this->ViewLayout = Temp_ViewLayout; 211 | 212 | Temp_Views.Add(ActualPosition, ActualSize); 213 | } 214 | 215 | if (Temp_Views.IsEmpty()) 216 | { 217 | this->LastError = "Actual size map is empty !"; 218 | return; 219 | } 220 | 221 | if (this->CompareViews(this->MAP_Views, Temp_Views)) 222 | { 223 | this->LastError = "Views are not changed !"; 224 | return; 225 | } 226 | 227 | this->MAP_Views = Temp_Views; 228 | const bool RetVal = UWindowSystemBPLibrary::SetBackgroundMaterial(this->MAT_BG, this->MAT_Brush, this->CanvasName, this->MAP_Views); 229 | 230 | if (!RetVal) 231 | { 232 | this->LastError = "There was a problem while changing background !"; 233 | } 234 | } 235 | 236 | bool UFF_WindowSubystem::CloseAllWindows() 237 | { 238 | if (this->MAP_Windows.IsEmpty()) 239 | { 240 | return false; 241 | } 242 | 243 | for (TPair EachPair : this->MAP_Windows) 244 | { 245 | AEachWindow_SWindow* EachWindow = EachPair.Value; 246 | if (IsValid(EachWindow)) 247 | { 248 | EachWindow->Destroy(); 249 | } 250 | } 251 | 252 | this->MAP_Windows.Empty(); 253 | return true; 254 | } 255 | 256 | bool UFF_WindowSubystem::ToggleWindowState(FName InTargetWindow, bool bFlashWindow) 257 | { 258 | if (InTargetWindow.IsNone()) 259 | { 260 | return false; 261 | } 262 | 263 | if (InTargetWindow.ToString().IsEmpty()) 264 | { 265 | return false; 266 | } 267 | 268 | AEachWindow_SWindow* TargetWindow = *this->MAP_Windows.Find(InTargetWindow); 269 | 270 | if (!TargetWindow) 271 | { 272 | return false; 273 | } 274 | 275 | EWindowState WindowState = EWindowState::None; 276 | if (!TargetWindow->GetWindowState(WindowState)) 277 | { 278 | return false; 279 | } 280 | 281 | switch (WindowState) 282 | { 283 | case EWindowState::None: 284 | 285 | return false; 286 | 287 | case EWindowState::Minimized: 288 | 289 | TargetWindow->SetWindowOpacity(1.f); 290 | TargetWindow->ToggleOpacity(false, true); 291 | TargetWindow->SetWindowState(EWindowState::Restored); 292 | TargetWindow->BringWindowFront(bFlashWindow); 293 | 294 | return true; 295 | 296 | case EWindowState::Restored: 297 | 298 | TargetWindow->SetWindowOpacity(1.f); 299 | TargetWindow->ToggleOpacity(false, true); 300 | 301 | if (TargetWindow->IsWindowTopMost()) 302 | { 303 | TargetWindow->SetWindowState(EWindowState::Minimized); 304 | } 305 | 306 | else 307 | { 308 | TargetWindow->BringWindowFront(bFlashWindow); 309 | } 310 | 311 | return true; 312 | 313 | case EWindowState::Maximized: 314 | 315 | TargetWindow->SetWindowOpacity(1.f); 316 | TargetWindow->ToggleOpacity(false, true); 317 | 318 | if (TargetWindow->IsWindowTopMost()) 319 | { 320 | TargetWindow->SetWindowState(EWindowState::Minimized); 321 | } 322 | 323 | else 324 | { 325 | TargetWindow->BringWindowFront(bFlashWindow); 326 | } 327 | 328 | return true; 329 | 330 | default: 331 | 332 | return false; 333 | } 334 | } 335 | 336 | bool UFF_WindowSubystem::BringFrontOnHover(AEachWindow_SWindow* TargetWindow) 337 | { 338 | if (!TargetWindow) 339 | { 340 | return false; 341 | } 342 | 343 | if (this->HoveredWindow == TargetWindow) 344 | { 345 | return false; 346 | } 347 | 348 | TargetWindow->SetWindowOpacity(1.f); 349 | TargetWindow->ToggleOpacity(false, true); 350 | 351 | for (TPair Each_Pair : this->MAP_Windows) 352 | { 353 | AEachWindow_SWindow* EachWindow = Each_Pair.Value; 354 | 355 | if (IsValid(EachWindow) && this->HoveredWindow != TargetWindow) 356 | { 357 | EWindowState WindowState = EWindowState::None; 358 | EachWindow->GetWindowState(WindowState); 359 | 360 | if (WindowState == EWindowState::Minimized) 361 | { 362 | continue; 363 | } 364 | 365 | EachWindow->ToggleOpacity(true, true); 366 | EachWindow->SetWindowOpacity(0.5f); 367 | } 368 | } 369 | 370 | this->HoveredWindow = TargetWindow; 371 | 372 | return true; 373 | } 374 | 375 | void UFF_WindowSubystem::Toggle_Color_Picker(bool& bIsActive) 376 | { 377 | if (this->MouseHook_Color) 378 | { 379 | UnhookWindowsHookEx(MouseHook_Color); 380 | this->MouseHook_Color = NULL; 381 | this->LastPickedColor = nullptr; 382 | 383 | bIsActive = false; 384 | return; 385 | } 386 | 387 | else 388 | { 389 | thread_local UColorPickerObject* ColorPickerContainer = NewObject(); 390 | this->LastPickedColor = ColorPickerContainer; 391 | 392 | auto Callback_Hook = [](int nCode, WPARAM wParam, LPARAM lParam)->LRESULT 393 | { 394 | if (wParam == WM_LBUTTONDOWN) 395 | { 396 | HWND ScreenHandle = GetDesktopWindow(); 397 | 398 | if (!ScreenHandle) 399 | { 400 | UE_LOG(LogTemp, Error, TEXT("Read Screen Color -> Error -> Screen Handle is not valid !")); 401 | return CallNextHookEx(0, nCode, wParam, lParam); 402 | } 403 | 404 | HDC ScreenContext = GetDC(ScreenHandle); 405 | if (!ScreenContext) 406 | { 407 | UE_LOG(LogTemp, Error, TEXT("Read Screen Color -> Error -> Screen Context is not valid !")); 408 | return CallNextHookEx(0, nCode, wParam, lParam); 409 | } 410 | 411 | POINT RawPos; 412 | if (!GetCursorPos(&RawPos)) 413 | { 414 | UE_LOG(LogTemp, Warning, TEXT("Read Screen Color -> Error -> Got Cursor Pos : %d"), GetLastError()); 415 | } 416 | 417 | COLORREF RawColor = GetPixel(ScreenContext, RawPos.x, RawPos.y); 418 | FLinearColor PositionColor = FLinearColor(); 419 | PositionColor.R = GetRValue(RawColor); 420 | PositionColor.G = GetGValue(RawColor); 421 | PositionColor.B = GetBValue(RawColor); 422 | PositionColor.A = 255; 423 | 424 | ColorPickerContainer->Color = PositionColor; 425 | ColorPickerContainer->Position = FVector2D(RawPos.x, RawPos.y); 426 | 427 | ReleaseDC(ScreenHandle, ScreenContext); 428 | } 429 | 430 | return CallNextHookEx(0, nCode, wParam, lParam); 431 | }; 432 | 433 | this->MouseHook_Color = SetWindowsHookEx(WH_MOUSE_LL, Callback_Hook, NULL, 0); 434 | bIsActive = true; 435 | } 436 | } 437 | 438 | bool UFF_WindowSubystem::IsColorPickerActive() 439 | { 440 | return this->MouseHook_Color ? true : false; 441 | } 442 | 443 | FString UFF_WindowSubystem::ViewLayoutLog() 444 | { 445 | FString OutString = ""; 446 | 447 | for (TPair EachPair : this->ViewLayout) 448 | { 449 | OutString += EachPair.Key + EachPair.Value.ToString() + "\n"; 450 | } 451 | 452 | return OutString; 453 | } 454 | 455 | void UFF_WindowSubystem::GetLastPickedColorPos(FVector2D& Position, FLinearColor& Color) 456 | { 457 | if (this->LastPickedColor) 458 | { 459 | Position = this->LastPickedColor->Position; 460 | Color = this->LastPickedColor->Color; 461 | } 462 | } -------------------------------------------------------------------------------- /Source/WindowSystem/Private/WindowSystem.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "WindowSystem.h" 4 | 5 | #define LOCTEXT_NAMESPACE "FWindowSystemModule" 6 | 7 | void FWindowSystemModule::StartupModule() 8 | { 9 | // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module 10 | 11 | } 12 | 13 | void FWindowSystemModule::ShutdownModule() 14 | { 15 | // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, 16 | // we call this function before unloading the module. 17 | 18 | } 19 | 20 | #undef LOCTEXT_NAMESPACE 21 | 22 | IMPLEMENT_MODULE(FWindowSystemModule, WindowSystem) -------------------------------------------------------------------------------- /Source/WindowSystem/Private/WindowSystemBPLibrary.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "WindowSystemBPLibrary.h" 4 | #include "WindowSystem.h" 5 | 6 | UWindowSystemBPLibrary::UWindowSystemBPLibrary(const FObjectInitializer& ObjectInitializer) 7 | : Super(ObjectInitializer) 8 | { 9 | 10 | } 11 | 12 | void UWindowSystemBPLibrary::SetMainWindowPosition(FVector2D InNewPosition) 13 | { 14 | GEngine->GameViewport->GetWindow().ToSharedRef().Get().MoveWindowTo(InNewPosition); 15 | } 16 | 17 | FText UWindowSystemBPLibrary::GetMainWindowTitle() 18 | { 19 | return GEngine->GameViewport->GetWindow().ToSharedRef().Get().GetTitle(); 20 | } 21 | 22 | void UWindowSystemBPLibrary::SelectFileFromDialog(FDelegateOpenFile DelegateFileNames, const FString InDialogName, const FString InOkLabel, const FString InDefaultPath, TMap InExtensions, int32 DefaultExtensionIndex, bool bIsNormalizeOutputs, bool bAllowFolderSelection) 23 | { 24 | AsyncTask(ENamedThreads::AnyNormalThreadNormalTask, [DelegateFileNames, InDialogName, InOkLabel, InDefaultPath, InExtensions, DefaultExtensionIndex, bIsNormalizeOutputs, bAllowFolderSelection]() 25 | { 26 | IFileOpenDialog* FileOpenDialog; 27 | HRESULT FileDialogInstance = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL, IID_PPV_ARGS(&FileOpenDialog)); 28 | 29 | IShellItemArray* ShellItems; 30 | TArray Array_FilePaths; 31 | 32 | // If dialog instance successfully created. 33 | if (SUCCEEDED(FileDialogInstance)) 34 | { 35 | // https://stackoverflow.com/questions/70174174/c-com-comdlg-filterspec-array-overrun 36 | int32 ExtensionCount = InExtensions.Num(); 37 | COMDLG_FILTERSPEC* ExtensionArray = new COMDLG_FILTERSPEC[ExtensionCount]; 38 | COMDLG_FILTERSPEC* EachExtension = ExtensionArray; 39 | 40 | TArray ExtensionKeys; 41 | InExtensions.GetKeys(ExtensionKeys); 42 | 43 | TArray ExtensionValues; 44 | InExtensions.GenerateValueArray(ExtensionValues); 45 | 46 | for (int32 ExtensionIndex = 0; ExtensionIndex < ExtensionCount; ExtensionIndex++) 47 | { 48 | EachExtension->pszName = *ExtensionKeys[ExtensionIndex]; 49 | EachExtension->pszSpec = *ExtensionValues[ExtensionIndex]; 50 | ++EachExtension; 51 | } 52 | 53 | FileOpenDialog->SetFileTypes(ExtensionCount, ExtensionArray); 54 | 55 | // Starts from 1 56 | FileOpenDialog->SetFileTypeIndex(DefaultExtensionIndex + 1); 57 | 58 | DWORD dwOptions; 59 | FileOpenDialog->GetOptions(&dwOptions); 60 | 61 | if (bAllowFolderSelection == true) 62 | { 63 | // https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/ne-shobjidl_core-_fileopendialogoptions 64 | FileOpenDialog->SetOptions(dwOptions | FOS_PICKFOLDERS | FOS_ALLOWMULTISELECT | FOS_FILEMUSTEXIST | FOS_OKBUTTONNEEDSINTERACTION); 65 | } 66 | 67 | else 68 | { 69 | // https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/ne-shobjidl_core-_fileopendialogoptions 70 | FileOpenDialog->SetOptions(dwOptions | FOS_ALLOWMULTISELECT | FOS_FILEMUSTEXIST | FOS_OKBUTTONNEEDSINTERACTION); 71 | } 72 | 73 | if (InDialogName.IsEmpty() != true) 74 | { 75 | FileOpenDialog->SetTitle(*InDialogName); 76 | } 77 | 78 | if (InOkLabel.IsEmpty() != true) 79 | { 80 | FileOpenDialog->SetOkButtonLabel(*InOkLabel); 81 | } 82 | 83 | if (InDefaultPath.IsEmpty() != true) 84 | { 85 | FString DefaultPathString = InDefaultPath; 86 | 87 | FPaths::MakePlatformFilename(DefaultPathString); 88 | 89 | IShellItem* DefaultFolder = NULL; 90 | HRESULT DefaultPathResult = SHCreateItemFromParsingName(*DefaultPathString, nullptr, IID_PPV_ARGS(&DefaultFolder)); 91 | 92 | if (SUCCEEDED(DefaultPathResult)) 93 | { 94 | FileOpenDialog->SetFolder(DefaultFolder); 95 | DefaultFolder->Release(); 96 | } 97 | } 98 | 99 | HWND WindowHandle = reinterpret_cast(GEngine->GameViewport->GetWindow()->GetNativeWindow()->GetOSWindowHandle()); 100 | FileDialogInstance = FileOpenDialog->Show(WindowHandle); 101 | 102 | // If dialog successfully showed up. 103 | if (SUCCEEDED(FileDialogInstance)) 104 | { 105 | FileDialogInstance = FileOpenDialog->GetResults(&ShellItems); 106 | 107 | // Is results got. 108 | if (SUCCEEDED(FileDialogInstance)) 109 | { 110 | DWORD ItemCount; 111 | ShellItems->GetCount(&ItemCount); 112 | 113 | for (DWORD ItemIndex = 0; ItemIndex < ItemCount; ItemIndex++) 114 | { 115 | IShellItem* EachItem; 116 | ShellItems->GetItemAt(ItemIndex, &EachItem); 117 | 118 | PWSTR EachFilePathSTR = NULL; 119 | EachItem->GetDisplayName(SIGDN_FILESYSPATH, &EachFilePathSTR); 120 | 121 | FString EachFilePath = EachFilePathSTR; 122 | 123 | if (bIsNormalizeOutputs == true) 124 | { 125 | FPaths::NormalizeFilename(EachFilePath); 126 | } 127 | 128 | Array_FilePaths.Add(EachFilePath); 129 | 130 | EachItem->Release(); 131 | } 132 | 133 | ShellItems->Release(); 134 | FileOpenDialog->Release(); 135 | CoUninitialize(); 136 | 137 | AsyncTask(ENamedThreads::GameThread, [DelegateFileNames, Array_FilePaths, bAllowFolderSelection]() 138 | { 139 | if (Array_FilePaths.IsEmpty() == false) 140 | { 141 | FSelectedFiles SelectedFiles; 142 | SelectedFiles.IsSuccessfull = true; 143 | SelectedFiles.IsFolder = bAllowFolderSelection; 144 | SelectedFiles.Strings = Array_FilePaths; 145 | 146 | DelegateFileNames.ExecuteIfBound(SelectedFiles); 147 | } 148 | 149 | else 150 | { 151 | FSelectedFiles SelectedFiles; 152 | SelectedFiles.IsSuccessfull = false; 153 | SelectedFiles.IsFolder = bAllowFolderSelection; 154 | 155 | DelegateFileNames.ExecuteIfBound(SelectedFiles); 156 | } 157 | } 158 | ); 159 | 160 | } 161 | 162 | // Function couldn't get results. 163 | else 164 | { 165 | AsyncTask(ENamedThreads::GameThread, [DelegateFileNames, ShellItems, FileOpenDialog, bAllowFolderSelection]() 166 | { 167 | FileOpenDialog->Release(); 168 | CoUninitialize(); 169 | 170 | FSelectedFiles SelectedFiles; 171 | SelectedFiles.IsSuccessfull = false; 172 | SelectedFiles.IsFolder = bAllowFolderSelection; 173 | 174 | DelegateFileNames.ExecuteIfBound(SelectedFiles); 175 | } 176 | ); 177 | } 178 | } 179 | 180 | // Dialog didn't show up. 181 | else 182 | { 183 | AsyncTask(ENamedThreads::GameThread, [DelegateFileNames, FileOpenDialog, ShellItems, bAllowFolderSelection]() 184 | { 185 | FileOpenDialog->Release(); 186 | CoUninitialize(); 187 | 188 | FSelectedFiles SelectedFiles; 189 | SelectedFiles.IsSuccessfull = false; 190 | SelectedFiles.IsFolder = bAllowFolderSelection; 191 | 192 | DelegateFileNames.ExecuteIfBound(SelectedFiles); 193 | } 194 | ); 195 | } 196 | } 197 | 198 | // Function couldn't create dialog. 199 | else 200 | { 201 | AsyncTask(ENamedThreads::GameThread, [DelegateFileNames, FileOpenDialog, ShellItems, bAllowFolderSelection]() 202 | { 203 | FileOpenDialog->Release(); 204 | CoUninitialize(); 205 | 206 | FSelectedFiles SelectedFiles; 207 | SelectedFiles.IsSuccessfull = false; 208 | SelectedFiles.IsFolder = bAllowFolderSelection; 209 | 210 | DelegateFileNames.ExecuteIfBound(SelectedFiles); 211 | } 212 | ); 213 | } 214 | } 215 | ); 216 | } 217 | 218 | void UWindowSystemBPLibrary::SaveFileDialog(FDelegateSaveFile DelegateSaveFile, const FString InDialogName, const FString InOkLabel, const FString InDefaultPath, TMap InExtensions, int32 DefaultExtensionIndex, bool bIsNormalizeOutputs) 219 | { 220 | AsyncTask(ENamedThreads::AnyNormalThreadNormalTask, [DelegateSaveFile, InDialogName, InOkLabel, InDefaultPath, InExtensions, DefaultExtensionIndex, bIsNormalizeOutputs]() 221 | { 222 | IFileSaveDialog* SaveFileDialog; 223 | HRESULT SaveDialogInstance = CoCreateInstance(CLSID_FileSaveDialog, NULL, CLSCTX_ALL, IID_PPV_ARGS(&SaveFileDialog)); 224 | 225 | IShellItem* ShellItem; 226 | 227 | // If dialog instance successfully created. 228 | if (SUCCEEDED(SaveDialogInstance)) 229 | { 230 | // https://stackoverflow.com/questions/70174174/c-com-comdlg-filterspec-array-overrun 231 | int32 ExtensionCount = InExtensions.Num(); 232 | COMDLG_FILTERSPEC* ExtensionArray = new COMDLG_FILTERSPEC[ExtensionCount]; 233 | COMDLG_FILTERSPEC* EachExtension = ExtensionArray; 234 | 235 | TArray ExtensionKeys; 236 | InExtensions.GetKeys(ExtensionKeys); 237 | 238 | TArray ExtensionValues; 239 | InExtensions.GenerateValueArray(ExtensionValues); 240 | 241 | for (int32 ExtensionIndex = 0; ExtensionIndex < ExtensionCount; ExtensionIndex++) 242 | { 243 | EachExtension->pszName = *ExtensionKeys[ExtensionIndex]; 244 | EachExtension->pszSpec = *ExtensionValues[ExtensionIndex]; 245 | ++EachExtension; 246 | } 247 | 248 | SaveFileDialog->SetFileTypes(ExtensionCount, ExtensionArray); 249 | 250 | // Starts from 1 251 | SaveFileDialog->SetFileTypeIndex(DefaultExtensionIndex + 1); 252 | 253 | DWORD dwOptions; 254 | SaveFileDialog->GetOptions(&dwOptions); 255 | 256 | if (InDialogName.IsEmpty() != true) 257 | { 258 | SaveFileDialog->SetTitle(*InDialogName); 259 | } 260 | 261 | if (InOkLabel.IsEmpty() != true) 262 | { 263 | SaveFileDialog->SetOkButtonLabel(*InOkLabel); 264 | } 265 | 266 | if (InDefaultPath.IsEmpty() != true) 267 | { 268 | FString DefaultPathString = InDefaultPath; 269 | 270 | FPaths::MakePlatformFilename(DefaultPathString); 271 | 272 | IShellItem* DefaultFolder = NULL; 273 | HRESULT DefaultPathResult = SHCreateItemFromParsingName(*DefaultPathString, nullptr, IID_PPV_ARGS(&DefaultFolder)); 274 | 275 | if (SUCCEEDED(DefaultPathResult)) 276 | { 277 | SaveFileDialog->SetFolder(DefaultFolder); 278 | DefaultFolder->Release(); 279 | } 280 | } 281 | 282 | HWND WindowHandle = reinterpret_cast(GEngine->GameViewport->GetWindow()->GetNativeWindow()->GetOSWindowHandle()); 283 | SaveDialogInstance = SaveFileDialog->Show(WindowHandle); 284 | 285 | // Dialog didn't show up. 286 | if (SUCCEEDED(SaveDialogInstance)) 287 | { 288 | SaveFileDialog->GetResult(&ShellItem); 289 | 290 | UINT FileTypeIndex = 0; 291 | SaveFileDialog->GetFileTypeIndex(&FileTypeIndex); 292 | FString ExtensionString = ExtensionValues[FileTypeIndex - 1]; 293 | 294 | TArray ExtensionParts; 295 | ExtensionString.ParseIntoArray(ExtensionParts, TEXT("."), true); 296 | 297 | PWSTR pFileName; 298 | ShellItem->GetDisplayName(SIGDN_FILESYSPATH, &pFileName); 299 | 300 | FString FilePath = FString(pFileName) + TEXT(".") + ExtensionParts[1]; 301 | 302 | if (bIsNormalizeOutputs == true) 303 | { 304 | FPaths::NormalizeFilename(FilePath); 305 | } 306 | 307 | ShellItem->Release(); 308 | SaveFileDialog->Release(); 309 | CoUninitialize(); 310 | 311 | AsyncTask(ENamedThreads::GameThread, [DelegateSaveFile, FilePath]() 312 | { 313 | DelegateSaveFile.ExecuteIfBound(true, FilePath); 314 | } 315 | ); 316 | } 317 | 318 | else 319 | { 320 | AsyncTask(ENamedThreads::GameThread, [DelegateSaveFile]() 321 | { 322 | DelegateSaveFile.ExecuteIfBound(false, TEXT("")); 323 | } 324 | ); 325 | } 326 | } 327 | 328 | // Function couldn't create dialog. 329 | else 330 | { 331 | AsyncTask(ENamedThreads::GameThread, [DelegateSaveFile]() 332 | { 333 | DelegateSaveFile.ExecuteIfBound(false, TEXT("")); 334 | } 335 | ); 336 | } 337 | } 338 | ); 339 | } 340 | 341 | bool UWindowSystemBPLibrary::PossesLocalPlayer(const int32 PlayerId, const int32 ControllerId) 342 | { 343 | UWorld* World = GEngine->GetCurrentPlayWorld(); 344 | UEngine* const REF_Engine = GEngine->GameViewport->GetGameInstance()->GetEngine(); 345 | const int32 NumPlayers = REF_Engine->GetNumGamePlayers(World); 346 | 347 | if (NumPlayers > PlayerId + 1 || ControllerId < -1) 348 | { 349 | return false; 350 | } 351 | 352 | int32 PlayerControllerId = 0; 353 | if (ControllerId == -1) 354 | { 355 | PlayerControllerId = UGameplayStatics::GetPlayerControllerID(REF_Engine->GetGamePlayer(World, 0)->GetPlayerController(World)); 356 | } 357 | 358 | else 359 | { 360 | PlayerControllerId = ControllerId; 361 | } 362 | 363 | REF_Engine->GetGamePlayer(World, PlayerId)->SetControllerId(PlayerControllerId); 364 | 365 | return true; 366 | } 367 | 368 | bool UWindowSystemBPLibrary::ChangePlayerViewSize(const int32 PlayerId, FVector2D NewRatio, FVector2D NewOrigin) 369 | { 370 | UCustomViewport* CustomViewport = Cast(GEngine->GameViewport.Get()); 371 | 372 | if (!CustomViewport) 373 | { 374 | return false; 375 | } 376 | 377 | return CustomViewport->ChangePlayerViewSize(PlayerId, NewRatio, NewOrigin); 378 | } 379 | 380 | bool UWindowSystemBPLibrary::ToggleWidgetState(UWidget* TargetWidget, ESlateVisibility OffMethod) 381 | { 382 | if (!TargetWidget) 383 | { 384 | return false; 385 | } 386 | 387 | ESlateVisibility CurrentState = TargetWidget->GetVisibility(); 388 | 389 | if (CurrentState == ESlateVisibility::Visible) 390 | { 391 | TargetWidget->SetVisibility(OffMethod); 392 | return true; 393 | } 394 | 395 | else 396 | { 397 | TargetWidget->SetVisibility(ESlateVisibility::Visible); 398 | return true; 399 | } 400 | } 401 | 402 | bool UWindowSystemBPLibrary::SetBackgroundMaterial(UMaterialInterface* MAT_BG, UMaterialInterface* MAT_Brush, FName CRT_Name, TMap Views) 403 | { 404 | if (Views.IsEmpty()) 405 | { 406 | return false; 407 | } 408 | 409 | if (!IsValid(MAT_BG) || !IsValid(MAT_Brush)) 410 | { 411 | return false; 412 | } 413 | 414 | UCustomViewport* CustomViewport = Cast(GEngine->GameViewport.Get()); 415 | 416 | if (!CustomViewport) 417 | { 418 | return false; 419 | } 420 | 421 | UWorld* World = GEngine->GetCurrentPlayWorld(); 422 | 423 | if (!IsValid(World)) 424 | { 425 | return false; 426 | } 427 | 428 | FVector2D CRT_Size; 429 | CustomViewport->GetViewportSize(CRT_Size); 430 | 431 | if (CRT_Size == FVector2D(0.f)) 432 | { 433 | return false; 434 | } 435 | 436 | UCanvasRenderTarget2D* CRT = UCanvasRenderTarget2D::CreateCanvasRenderTarget2D(World, UCanvasRenderTarget2D::StaticClass(), CRT_Size.X, CRT_Size.Y); 437 | 438 | if (!IsValid(CRT)) 439 | { 440 | return false; 441 | } 442 | 443 | UCanvas* Canvas = nullptr; 444 | FVector2D Size = FVector2D(); 445 | FDrawToRenderTargetContext Context = FDrawToRenderTargetContext(); 446 | UKismetRenderingLibrary::BeginDrawCanvasToRenderTarget(World, CRT, Canvas, Size, Context); 447 | 448 | for (const TPair Each_View : Views) 449 | { 450 | Canvas->K2_DrawMaterial(MAT_Brush, Each_View.Key, Each_View.Value, FVector2D(0.f), FVector2D(1.f)); 451 | } 452 | 453 | UKismetRenderingLibrary::EndDrawCanvasToRenderTarget(World, Context); 454 | 455 | UMaterialInstanceDynamic* MI_BG = UMaterialInstanceDynamic::Create(MAT_BG, World); 456 | MI_BG->SetTextureParameterValue(CRT_Name, CRT); 457 | 458 | return CustomViewport->SetBackgrounMaterial(MI_BG); 459 | } 460 | 461 | bool UWindowSystemBPLibrary::ToggleBackground(bool bStop) 462 | { 463 | UCustomViewport* CustomViewport = Cast(GEngine->GameViewport.Get()); 464 | 465 | if (!CustomViewport) 466 | { 467 | return false; 468 | } 469 | 470 | CustomViewport->ToggleBackground(bStop); 471 | return true; 472 | } 473 | -------------------------------------------------------------------------------- /Source/WindowSystem/Public/CustomViewport.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | 7 | #include "Engine/GameViewportClient.h" 8 | #include "Engine/ViewportSplitScreen.h" 9 | 10 | #include "CustomViewport.generated.h" 11 | 12 | USTRUCT(BlueprintType) 13 | struct WINDOWSYSTEM_API FPlayerViews 14 | { 15 | GENERATED_BODY() 16 | 17 | public: 18 | 19 | UPROPERTY(BlueprintReadWrite) 20 | FVector2D Size = FVector2D(); 21 | 22 | UPROPERTY(BlueprintReadWrite) 23 | FVector2D Position = FVector2D(); 24 | }; 25 | 26 | DECLARE_MULTICAST_DELEGATE_OneParam(FDelegateNewLayout, const TArray&); 27 | 28 | UCLASS() 29 | class WINDOWSYSTEM_API UCustomViewport : public UGameViewportClient 30 | { 31 | GENERATED_BODY() 32 | 33 | protected: 34 | 35 | // If there is a new player, we need to reset views. 36 | int32 LastPlayerCount = 0; 37 | 38 | // Views shouldn't reset to defaults if there is no change. 39 | bool bIsInitialsLoaded = false; 40 | 41 | // We use this to forcefully stop background rendering. For example there is only one view and it is in full screen state. 42 | bool bStopBackground = false; 43 | 44 | UMaterialInterface* BG_Material = nullptr; 45 | 46 | public: 47 | 48 | UCustomViewport(); 49 | 50 | virtual void Tick(float DeltaTime) override; 51 | 52 | virtual void UpdateActiveSplitscreenType() override; 53 | 54 | virtual void LayoutPlayers() override; 55 | 56 | virtual void Draw(FViewport* In_Viewport, FCanvas* In_SceneCanvas) override; 57 | 58 | FDelegateNewLayout DelegateNewLayout; 59 | 60 | virtual bool ChangePlayerViewSize(const int32 PlayerId, FVector2D NewRatio, FVector2D NewOrigin); 61 | virtual bool SetBackgrounMaterial(UMaterialInterface* In_Material); 62 | virtual UMaterialInterface* GetBackgroundMaterial(); 63 | virtual void ToggleBackground(bool bStop); 64 | 65 | }; -------------------------------------------------------------------------------- /Source/WindowSystem/Public/DragDropHandler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | #include "WindowSystemBPLibrary.h" 5 | 6 | // File Drag Drop Message Handler Subclass. 7 | class FDragDropHandler : public IWindowsMessageHandler 8 | { 9 | 10 | public: 11 | 12 | bool ProcessMessage(HWND Hwnd, uint32 Message, WPARAM WParam, LPARAM LParam, int32& OutResult) override; 13 | 14 | }; -------------------------------------------------------------------------------- /Source/WindowSystem/Public/WindowEnums.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CoreMinimal.h" 4 | 5 | UENUM(BlueprintType) 6 | enum class EWindowState : uint8 7 | { 8 | None UMETA(DisplayName = "None"), 9 | Minimized UMETA(DisplayName = "Minimized"), 10 | Restored UMETA(DisplayName = "Restored"), 11 | Maximized UMETA(DisplayName = "Maximized"), 12 | }; 13 | ENUM_CLASS_FLAGS(EWindowState) 14 | 15 | UENUM(BlueprintType) 16 | enum class EWindowTypeBp : uint8 17 | { 18 | Normal UMETA(DisplayName = "Normal"), 19 | Menu UMETA(DisplayName = "Menu"), 20 | ToolTip UMETA(DisplayName = "ToolTip"), 21 | Notification UMETA(DisplayName = "Notification"), 22 | CursorDecorator UMETA(DisplayName = "CursorDecorator"), 23 | GameWindow UMETA(DisplayName = "GameWindow"), 24 | }; 25 | ENUM_CLASS_FLAGS(EWindowTypeBp) -------------------------------------------------------------------------------- /Source/WindowSystem/Public/WindowInstance.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "GameFramework/Actor.h" 7 | 8 | // Custom Includes. 9 | #include "WindowManager.h" 10 | 11 | #include "WindowInstance.generated.h" 12 | 13 | UCLASS() 14 | class WINDOWSYSTEM_API AEachWindow_SWindow : public AActor 15 | { 16 | GENERATED_BODY() 17 | 18 | private: 19 | 20 | UFF_WindowSubystem* WindowSubsystem = nullptr; 21 | 22 | protected: 23 | 24 | // Called when the game starts or when spawned. 25 | virtual void BeginPlay() override; 26 | 27 | // Called when the game ends. 28 | virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; 29 | 30 | // Window Close Delegate. 31 | void NotifyWindowClosed(const TSharedRef& Window); 32 | 33 | // Window Movement Delegate. 34 | void NotifyWindowMoved(const TSharedRef& Window); 35 | 36 | // We use it with Timer_Hover. 37 | virtual void NotifyWindowHovered(bool bUseDirectHover); 38 | 39 | // We use it to construct window. 40 | virtual bool CreateNewWindow(); 41 | 42 | // We use it to destroy contents of window. 43 | virtual void CloseWindowCallback(); 44 | 45 | FTimerHandle Hover_Timer; 46 | 47 | FTimerDelegate Hover_Delegate; 48 | 49 | public: 50 | 51 | // Sets default values for this actor's properties. 52 | AEachWindow_SWindow(); 53 | 54 | // Called every frame. 55 | virtual void Tick(float DeltaTime) override; 56 | 57 | TSharedPtr WindowPtr; 58 | 59 | UPROPERTY(BlueprintReadOnly, meta = (ToolTip = "")) 60 | bool bIsTransparent = false; 61 | 62 | UPROPERTY(BlueprintReadOnly, meta = (ToolTip = "")) 63 | bool bIsFileDropEnabled = false; 64 | 65 | UPROPERTY(BlueprintReadWrite, meta = (ToolTip = "", ExposeOnSpawn = "true")) 66 | UUserWidget* ContentWidget = nullptr; 67 | 68 | UPROPERTY(BlueprintReadOnly, meta = (ToolTip = "", ExposeOnSpawn = "true")) 69 | EWindowTypeBp WindowTypeBp = EWindowTypeBp::Normal; 70 | 71 | UPROPERTY(BlueprintReadOnly, meta = (ToolTip = "", ExposeOnSpawn = "true")) 72 | FName WindowTag = NAME_None; 73 | 74 | UPROPERTY(BlueprintReadOnly, meta = (ToolTip = "", ExposeOnSpawn = "true")) 75 | FText InWindowTitle = INVTEXT("None"); 76 | 77 | UPROPERTY(BlueprintReadOnly, meta = (ToolTip = "", ExposeOnSpawn = "true")) 78 | FText InToolTip = INVTEXT("None"); 79 | 80 | UPROPERTY(BlueprintReadOnly, meta = (ToolTip = "If you hide your taskbar, you have a chance to lost your window when it goes to backward. In that case, use \"Bring Window Front\" function with some delay.", ExposeOnSpawn = "true")) 81 | bool bShowOnTaskBar = true; 82 | 83 | UPROPERTY(BlueprintReadWrite, meta = (ToolTip = "", ExposeOnSpawn = "true")) 84 | bool bIsTopMost = false; 85 | 86 | UPROPERTY(BlueprintReadOnly, meta = (ToolTip = "If you close your window, you will lost your widget and its contents.", ExposeOnSpawn = "true")) 87 | bool bHasClose = false; 88 | 89 | UPROPERTY(BlueprintReadOnly, meta = (ToolTip = "", ExposeOnSpawn = "true")) 90 | bool bForceVolatile = true; 91 | 92 | UPROPERTY(BlueprintReadOnly, meta = (ToolTip = "", ExposeOnSpawn = "true")) 93 | bool bPreserveAspectRatio = false; 94 | 95 | UPROPERTY(BlueprintReadOnly, meta = (ToolTip = "", ExposeOnSpawn = "true")) 96 | bool bMinimized = false; 97 | 98 | UPROPERTY(BlueprintReadOnly, meta = (ToolTip = "", ExposeOnSpawn = "true")) 99 | bool bSupportsMaximized = true; 100 | 101 | UPROPERTY(BlueprintReadOnly, meta = (ToolTip = "", ExposeOnSpawn = "true")) 102 | bool bSupportsMinimized = true; 103 | 104 | UPROPERTY(BlueprintReadOnly, meta = (ToolTip = "", ExposeOnSpawn = "true")) 105 | bool bSetMirrorWindow = true; 106 | 107 | UPROPERTY(BlueprintReadOnly, meta = (ToolTip = "", ExposeOnSpawn = "true")) 108 | bool bUseNativeBorder = false; 109 | 110 | UPROPERTY(BlueprintReadOnly, meta = (ToolTip = "", ExposeOnSpawn = "true")) 111 | bool bEnableHoverDetection = false; 112 | 113 | UPROPERTY(BlueprintReadOnly, meta = (ToolTip = "", ExposeOnSpawn = "true")) 114 | float HoverDetectionTime = 0.03; 115 | 116 | UPROPERTY(BlueprintReadOnly, meta = (ToolTip = "", ExposeOnSpawn = "true")) 117 | FLinearColor TitleColor = FLinearColor::White; 118 | 119 | UPROPERTY(BlueprintReadOnly, meta = (ToolTip = "", ExposeOnSpawn = "true")) 120 | FVector2D WindowSize = FVector2D::ZeroVector; 121 | 122 | UPROPERTY(BlueprintReadOnly, meta = (ToolTip = "", ExposeOnSpawn = "true")) 123 | FVector2D MinSize = FVector2D::ZeroVector; 124 | 125 | UPROPERTY(BlueprintReadOnly, meta = (ToolTip = "", ExposeOnSpawn = "true")) 126 | FVector2D MaxSize = FVector2D::ZeroVector; 127 | 128 | UPROPERTY(BlueprintReadOnly, meta = (ToolTip = "", ExposeOnSpawn = "true")) 129 | FVector2D StartPosition = FVector2D::ZeroVector; 130 | 131 | UPROPERTY(BlueprintReadOnly, meta = (ToolTip = "", ExposeOnSpawn = "true")) 132 | FMargin BorderThick = FMargin(5.f); 133 | 134 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Set File Drag Drop Support", Keywords = "set, file, drag, drop, support, child, window, windows"), Category = "Window System|Set") 135 | virtual bool SetFileDragDropSupport(); 136 | 137 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Take Screenshot of Window", ToolTip = "Export To Disk functions should come after a delay node.", Keywords = "take, ss, screenshot, window"), Category = "Window System|Export") 138 | virtual bool TakeSSWindow(UTextureRenderTarget2D*& OutTextureRenderTarget2D); 139 | 140 | UFUNCTION(BlueprintPure, meta = (DisplayName = "Is Window Top Most", Keywords = "is, window, top, most"), Category = "Window System|Check") 141 | virtual bool IsWindowTopMost(bool bUseNative = true); 142 | 143 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Bring Window Front", ToolTip = "It brings UE SWindow to front.", Keywords = "bring, window, front"), Category = "Window System|Set") 144 | virtual bool BringWindowFront(bool bFlashWindow); 145 | 146 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Toggle Top Most Option", ToolTip = "Description.", Keywords = "set, window, positon, location, move"), Category = "Window System|Set") 147 | virtual bool ToggleTopMostOption(); 148 | 149 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Toggle Show On Task Bar", Keywords = "toggle, show, taskbar, hide"), Category = "Window System|Set") 150 | virtual bool ToggleShowOnTaskBar(bool In_bShowOnTaskBar); 151 | 152 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Toggle Opacity", Keywords = "set, all, window, windows, opacity"), Category = "Window System|Set") 153 | virtual bool ToggleOpacity(bool bEnable, bool bPassDragDrop); 154 | 155 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Set Window Opacity", Keywords = "set, window, opacity"), Category = "Window System|Set") 156 | virtual bool SetWindowOpacity(float NewOpacity); 157 | 158 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Set Window State", Keywords = "set, window, state"), Category = "Window System|Set") 159 | virtual bool SetWindowState(EWindowState OutWindowState); 160 | 161 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Set Window Shape", ToolTip = "Description.", Keywords = "set, window, shape"), Category = "Window System|Set") 162 | virtual bool SetWindowShape(FMargin InExtend, float InDuration, float NewOpacity); 163 | 164 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Set Window Position", ToolTip = "Description.", Keywords = "set, window, positon, location, move"), Category = "Window System|Set") 165 | virtual bool SetWindowPosition(FVector2D InNewPosition); 166 | 167 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Set Window Title", ToolTip = "Description.", Keywords = "set, window, title"), Category = "Window System|Set") 168 | virtual bool SetWindowTitle(FText InNewTitle); 169 | 170 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Get Window State", Keywords = "get, window, state"), Category = "Window System|Get") 171 | virtual bool GetWindowState(EWindowState& OutWindowState); 172 | 173 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Get Window Position", Keywords = "get, window, position, location"), Category = "Window System|Get") 174 | virtual bool GetWindowPosition(FVector2D& OutPosition); 175 | 176 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Get Window Title", Keywords = "get, window, title"), Category = "Window System|Get") 177 | virtual bool GetWindowTitle(FText& OutWindowTitle); 178 | 179 | }; -------------------------------------------------------------------------------- /Source/WindowSystem/Public/WindowManager.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "GameFramework/Actor.h" 7 | 8 | // Custom Includes. 9 | #include "DragDropHandler.h" 10 | 11 | #include "WindowManager.generated.h" 12 | 13 | class AEachWindow_SWindow; 14 | 15 | UCLASS() 16 | class WINDOWSYSTEM_API UColorPickerObject : public UObject 17 | { 18 | GENERATED_BODY() 19 | 20 | public: 21 | FVector2D Position; 22 | FLinearColor Color; 23 | }; 24 | 25 | // File drag drop system. 26 | USTRUCT(BlueprintType) 27 | struct WINDOWSYSTEM_API FDroppedFileStruct 28 | { 29 | GENERATED_BODY() 30 | 31 | public: 32 | 33 | UPROPERTY(BlueprintReadWrite) 34 | FString FilePath; 35 | 36 | UPROPERTY(BlueprintReadWrite) 37 | FVector2D DropLocation = FVector2D(); 38 | 39 | UPROPERTY(BlueprintReadWrite) 40 | bool bIsFolder = false; 41 | }; 42 | 43 | DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDelegateFileDrop, const TArray&, OutMap); 44 | DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDelegateWindowClosed, const FName&, WindowTag); 45 | DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDelegateWindowMoved, AEachWindow_SWindow* const&, Window); 46 | DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDelegateWindowHovered, AEachWindow_SWindow* const&, OutHovered); 47 | DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDelegateLayoutChanged, const TArray&, Out_Views); 48 | 49 | UCLASS() 50 | class WINDOWSYSTEM_API UFF_WindowSubystem : public UGameInstanceSubsystem 51 | { 52 | GENERATED_BODY() 53 | 54 | private: 55 | 56 | #pragma region Viewport 57 | 58 | UCustomViewport* CustomViewport = nullptr; 59 | TMap MAP_Views; 60 | 61 | UFUNCTION() 62 | virtual void DetectLayoutChanges(); 63 | 64 | UFUNCTION() 65 | virtual void ChangeBackgroundOnNewPlayer(TArray const& Out_Views); 66 | 67 | UFUNCTION() 68 | virtual bool CompareViews(TMap A, TMap B); 69 | 70 | #pragma endregion Viewport 71 | 72 | #pragma region Drag_Drop 73 | 74 | FDragDropHandler DragDropHandler; 75 | 76 | UFUNCTION() 77 | virtual void AddDragDropHandlerToMV(); 78 | 79 | UFUNCTION() 80 | virtual void RemoveDragDropHandlerFromMV(); 81 | 82 | #pragma endregion Drap_Drop 83 | 84 | #pragma region Color_Picker 85 | UColorPickerObject* LastPickedColor = nullptr; 86 | HHOOK MouseHook_Color = NULL; 87 | #pragma endregion Color_Picker 88 | 89 | #pragma region Window_System 90 | AEachWindow_SWindow* HoveredWindow = nullptr; 91 | #pragma endregion Window_System 92 | 93 | void OnWorldTickStart(UWorld* World, ELevelTick TickType, float DeltaTime); 94 | 95 | public: 96 | 97 | TMap MAP_Windows; 98 | 99 | virtual void Initialize(FSubsystemCollectionBase& Collection) override; 100 | virtual void Deinitialize() override; 101 | 102 | UPROPERTY(BlueprintReadWrite, EditAnywhere, meta = (ToolTip = "It allows main window to support file drag drop.", ExposeOnSpawn = "true"), Category = "Frozen Forest|Window System|Window") 103 | bool bAllowMainWindow = true; 104 | 105 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Frozen Forest|Window System|Viewport") 106 | UMaterialInterface* MAT_BG = nullptr; 107 | 108 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Frozen Forest|Window System|Wiewport") 109 | UMaterialInterface* MAT_Brush = nullptr; 110 | 111 | UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "Frozen Forest|Window System|Wiewport") 112 | FName CanvasName = TEXT("Canvas"); 113 | 114 | UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Frozen Forest|Window System|Wiewport") 115 | FString LastError; 116 | 117 | UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Frozen Forest|Window System|Wiewport") 118 | TMap ViewLayout; 119 | 120 | UPROPERTY(BlueprintAssignable, Category = "Frozen Forest|Window System|Window|Events") 121 | FDelegateFileDrop OnFileDrop; 122 | 123 | UPROPERTY(BlueprintAssignable, Category = "Frozen Forest|Window System|Window|Events") 124 | FDelegateWindowClosed OnWindowClosed; 125 | 126 | UPROPERTY(BlueprintAssignable, Category = "Frozen Forest|Window System|Window|Events") 127 | FDelegateWindowMoved OnWindowMoved; 128 | 129 | UPROPERTY(BlueprintAssignable, Category = "Frozen Forest|Window System|Window|Events") 130 | FDelegateWindowHovered OnWindowHovered; 131 | 132 | UPROPERTY(BlueprintAssignable, Category = "Frozen Forest|Window System|Viewport|Events") 133 | FDelegateLayoutChanged OnLayoutChanged; 134 | 135 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Close All Windows", Keywords = "close, all, window"), Category = "Frozen Forest|Window System|Window") 136 | virtual bool CloseAllWindows(); 137 | 138 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Toggle Window State", ToolTip = "", Keywords = "toggle, switch, window, state, minimize, restore, maximize"), Category = "Frozen Forest|Window System|Window") 139 | virtual bool ToggleWindowState(FName InTargetWindow, bool bFlashWindow); 140 | 141 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Bring Front on Hover", ToolTip = "", Keywords = "hover, system, bring, window, front"), Category = "Frozen Forest|Window System|Window") 142 | virtual bool BringFrontOnHover(AEachWindow_SWindow* TargetWindow); 143 | 144 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Toggle Color Picker", ToolTip = "It will give cursor position and color under cursor.", Keywords = "cursor, mouse, color, pixel, position, location"), Category = "Frozen Forest|Window System|Window") 145 | virtual void Toggle_Color_Picker(bool& bIsActive); 146 | 147 | UFUNCTION(BlueprintPure) 148 | virtual bool IsColorPickerActive(); 149 | 150 | UFUNCTION(BlueprintPure) 151 | virtual FString ViewLayoutLog(); 152 | 153 | UFUNCTION(BlueprintPure) 154 | void GetLastPickedColorPos(FVector2D& Position, FLinearColor& Color); 155 | 156 | }; -------------------------------------------------------------------------------- /Source/WindowSystem/Public/WindowSystem.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "Modules/ModuleManager.h" 6 | 7 | class FWindowSystemModule : public IModuleInterface 8 | { 9 | public: 10 | 11 | /** IModuleInterface implementation */ 12 | virtual void StartupModule() override; 13 | virtual void ShutdownModule() override; 14 | }; 15 | -------------------------------------------------------------------------------- /Source/WindowSystem/Public/WindowSystemBPLibrary.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "Kismet/BlueprintFunctionLibrary.h" 6 | 7 | // Custom Includes. 8 | #include "WindowSystem_Includes.h" 9 | 10 | #include "WindowSystemBPLibrary.generated.h" 11 | 12 | // Select file from dialog. 13 | USTRUCT(BlueprintType) 14 | struct WINDOWSYSTEM_API FSelectedFiles 15 | { 16 | GENERATED_BODY() 17 | 18 | public: 19 | 20 | UPROPERTY(BlueprintReadOnly) 21 | bool IsSuccessfull = false; 22 | 23 | UPROPERTY(BlueprintReadOnly) 24 | bool IsFolder = false; 25 | 26 | UPROPERTY(BlueprintReadOnly) 27 | TArray Strings; 28 | }; 29 | 30 | UDELEGATE(BlueprintAuthorityOnly) 31 | DECLARE_DYNAMIC_DELEGATE_OneParam(FDelegateOpenFile, FSelectedFiles, OutFileNames); 32 | 33 | UDELEGATE(BlueprintAuthorityOnly) 34 | DECLARE_DYNAMIC_DELEGATE_TwoParams(FDelegateSaveFile, bool, bIsSaveSuccessful, FString, OutFileName); 35 | 36 | UCLASS() 37 | class UWindowSystemBPLibrary : public UBlueprintFunctionLibrary 38 | { 39 | GENERATED_UCLASS_BODY() 40 | 41 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Get Main Window Title", Keywords = "get, window, title, main"), Category = "Window System|Get") 42 | static FText GetMainWindowTitle(); 43 | 44 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Set Main Window Position", ToolTip = "Set Main Window Position", Keywords = "set, main, window, position"), Category = "Window System|Set|System") 45 | static void SetMainWindowPosition(FVector2D InNewPosition); 46 | 47 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Select File From Dialog", ToolTip = "If you enable \"Allow Folder Selection\", extension filtering will be disabled. \nExtension filtering uses a String to String MAP variable. \nKey is description and value is extension's itself. You need to write like this without quotes \"*.extension\". \nIf one extension group has multiple extensions, you need to use \";\" after each one.", Keywords = "select, file, folder, dialog, windows, explorer"), Category = "Window System|File Dialog") 48 | static void SelectFileFromDialog(FDelegateOpenFile DelegateFileNames, const FString InDialogName, const FString InOkLabel, const FString InDefaultPath, TMap InExtensions, int32 DefaultExtensionIndex, bool bIsNormalizeOutputs = true, bool bAllowFolderSelection = false); 49 | 50 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Save File with Dialog", ToolTip = "Each extension group must have only one extension. \nIf that group has multiple variation, you should define one by one all of them if you need them. \nAlso you need to write them as \"*.extension\".", Keywords = "save, file, dialog, windows, explorer"), Category = "Window System|File Dialog") 51 | static void SaveFileDialog(FDelegateSaveFile DelegateSaveFile, const FString InDialogName, const FString InOkLabel, const FString InDefaultPath, TMap InExtensions, int32 DefaultExtensionIndex, bool bIsNormalizeOutputs = true); 52 | 53 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Posses Local Player", ToolTip = "This is customized version of UGameViewportClient::SSSwapControllers which works on Shipping Builds.\nIf controller id is \"-1\", it will use main player's controller id which is probably 0. But else, it will use given index.", Keywords = "assign, new, controller, player, local"), Category = "Window System") 54 | static bool PossesLocalPlayer(const int32 PlayerId, const int32 ControllerId = -1); 55 | 56 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Change Player View Size", ToolTip = "", Keywords = "change, player, view, viewport, size, position, ratio"), Category = "Window System") 57 | static bool ChangePlayerViewSize(const int32 PlayerId, FVector2D NewRatio, FVector2D NewOrigin); 58 | 59 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Toggle Widget State", ToolTip = "", Keywords = "toggle, switch, widget, state, visible, hidden, collapse"), Category = "Window System") 60 | static bool ToggleWidgetState(UWidget* TargetWidget, ESlateVisibility OffMethod = ESlateVisibility::Collapsed); 61 | 62 | /* 63 | * @param Views Key = View position ; Value = View size. 64 | */ 65 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Set Background Material", Keywords = "set, background, material, layout, customize, splitscreen, viewport"), Category = "Window System") 66 | static bool SetBackgroundMaterial(UMaterialInterface* MAT_BG, UMaterialInterface* MAT_Brush, FName CRT_Name, TMap Views); 67 | 68 | UFUNCTION(BlueprintCallable, meta = (DisplayName = "Toggle Background", ToolTip = "We suggest you to use this when there is only one view and it is fullscreen.", Keywords = "background, layout, customize, splitscreen, viewport, toggle"), Category = "Window System") 69 | static bool ToggleBackground(bool bStop); 70 | 71 | }; -------------------------------------------------------------------------------- /Source/WindowSystem/Public/WindowSystem_Includes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Unreal Engine Includes. 4 | #include "Kismet/GameplayStatics.h" 5 | #include "Kismet/KismetRenderingLibrary.h" 6 | 7 | #include "Subsystems/GameInstanceSubsystem.h" 8 | #include "Subsystems/WorldSubsystem.h" 9 | 10 | #include "Widgets/SWindow.h" // Create Window. 11 | #include "Widgets/SWidget.h" // Add Widget to Window. 12 | #include "Slate/WidgetRenderer.h" // Take Screenshot of Window 13 | #include "Runtime/UMG/Public/UMG.h" // Take Screenshot of Window 14 | #include "Blueprint/UserWidget.h" 15 | 16 | // Custom Includes. 17 | #include "WindowEnums.h" 18 | #include "CustomViewport.h" 19 | 20 | THIRD_PARTY_INCLUDES_START 21 | #ifdef _WIN64 22 | #include "Windows/WindowsHWrapper.h" // Necessary include. 23 | #include "Windows/WindowsApplication.h" // File Drag Drop Message Handler. 24 | #include "shellapi.h" // File Drag Drop Callback. 25 | #include "dwmapi.h" // Windows 11 Rounded Window Include. 26 | #include // Regedit access. 27 | #include "winuser.h" // Necessary include. 28 | #include "Windows/MinWindows.h" // Necessary include. 29 | #endif 30 | THIRD_PARTY_INCLUDES_END -------------------------------------------------------------------------------- /Source/WindowSystem/WindowSystem.Build.cs: -------------------------------------------------------------------------------- 1 | // Some copyright should be here... 2 | 3 | using System; 4 | using System.IO; 5 | using UnrealBuildTool; 6 | public class WindowSystem : ModuleRules 7 | { 8 | public WindowSystem(ReadOnlyTargetRules Target) : base(Target) 9 | { 10 | PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; 11 | 12 | if (UnrealTargetPlatform.Win64 == Target.Platform) 13 | { 14 | 15 | } 16 | 17 | PublicDependencyModuleNames.AddRange( 18 | new string[] 19 | { 20 | "Core", 21 | // ... add other public dependencies that you statically link with here ... 22 | } 23 | ); 24 | 25 | PrivateDependencyModuleNames.AddRange( 26 | new string[] 27 | { 28 | "CoreUObject", 29 | "Engine", 30 | "Slate", 31 | "SlateCore", 32 | "UMG", 33 | "InputCore", 34 | "RHI", 35 | "RenderCore", 36 | // ... add private dependencies that you statically link with here ... 37 | } 38 | ); 39 | 40 | DynamicallyLoadedModuleNames.AddRange( 41 | new string[] 42 | { 43 | // ... add any modules that your module loads dynamically here ... 44 | } 45 | ); 46 | } 47 | } -------------------------------------------------------------------------------- /WindowSystem.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "Version": 1, 4 | "VersionName": "1.0", 5 | "FriendlyName": "WindowSystem", 6 | "Description": "", 7 | "Category": "Frozen Forest", 8 | "CreatedBy": "Frozen Forest Reality Technologies", 9 | "CreatedByURL": "", 10 | "DocsURL": "", 11 | "MarketplaceURL": "", 12 | "SupportURL": "", 13 | "CanContainContent": true, 14 | "IsBetaVersion": false, 15 | "IsExperimentalVersion": false, 16 | "Installed": false, 17 | "SupportedTargetPlatforms": [ 18 | "Win64" 19 | ], 20 | "Modules": [ 21 | { 22 | "Name": "WindowSystem", 23 | "Type": "Runtime", 24 | "LoadingPhase": "PreLoadingScreen", 25 | "PlatformAllowList": [ 26 | "Win64" 27 | ] 28 | } 29 | ] 30 | } --------------------------------------------------------------------------------