添加基础的桌面透明功能
This commit is contained in:
parent
dc714bfd03
commit
dac6c48d38
@ -8028,6 +8028,18 @@
|
||||
{
|
||||
"Path": "$(ProjectDir)/Binaries/Win64/UnrealEditor.modules",
|
||||
"Type": "RequiredResource"
|
||||
},
|
||||
{
|
||||
"Path": "$(ProjectDir)/Plugins/DeskMode/Binaries/Win64/UnrealEditor-DeskMode.dll",
|
||||
"Type": "DynamicLibrary"
|
||||
},
|
||||
{
|
||||
"Path": "$(ProjectDir)/Plugins/DeskMode/Binaries/Win64/UnrealEditor-DeskMode.pdb",
|
||||
"Type": "SymbolFile"
|
||||
},
|
||||
{
|
||||
"Path": "$(ProjectDir)/Plugins/DeskMode/Binaries/Win64/UnrealEditor.modules",
|
||||
"Type": "RequiredResource"
|
||||
}
|
||||
],
|
||||
"RuntimeDependencies": [
|
||||
@ -33759,6 +33771,10 @@
|
||||
"Path": "$(EngineDir)/Plugins/XGEController/XGEController.uplugin",
|
||||
"Type": "UFS"
|
||||
},
|
||||
{
|
||||
"Path": "$(ProjectDir)/Plugins/DeskMode/DeskMode.uplugin",
|
||||
"Type": "UFS"
|
||||
},
|
||||
{
|
||||
"Path": "$(ProjectDir)/ProjectFish.uproject",
|
||||
"Type": "UFS"
|
||||
@ -33827,6 +33843,7 @@
|
||||
"Dataflow",
|
||||
"DatasmithContent",
|
||||
"DeformerGraph",
|
||||
"DeskMode",
|
||||
"DumpGPUServices",
|
||||
"EOSShared",
|
||||
"EditorDataStorage",
|
||||
|
||||
Binary file not shown.
@ -29,10 +29,12 @@ r.Lumen.TraceMeshSDFs=0
|
||||
r.Lumen.Reflections.HardwareRayTracing.Translucent.Refraction.EnableForProject=True
|
||||
r.Lumen.TranslucencyReflections.FrontLayer.EnableForProject=False
|
||||
r.Shadow.TranslucentPerObject.ProjectEnabled=False
|
||||
r.PostProcessing.PropagateAlpha=True
|
||||
r.D3D11.UseAllowTearing=0
|
||||
|
||||
[/Script/WindowsTargetPlatform.WindowsTargetSettings]
|
||||
DefaultGraphicsRHI=DefaultGraphicsRHI_DX12
|
||||
DefaultGraphicsRHI=DefaultGraphicsRHI_DX12
|
||||
DefaultGraphicsRHI=DefaultGraphicsRHI_DX11
|
||||
DefaultGraphicsRHI=DefaultGraphicsRHI_DX11
|
||||
-D3D12TargetedShaderFormats=PCD3D_SM5
|
||||
+D3D12TargetedShaderFormats=PCD3D_SM6
|
||||
-D3D11TargetedShaderFormats=PCD3D_SM5
|
||||
|
||||
BIN
ProjectFish/Content/Maps/DeskModeTest.umap
Normal file
BIN
ProjectFish/Content/Maps/DeskModeTest.umap
Normal file
Binary file not shown.
BIN
ProjectFish/Content/Materials/M_DeskModeMat.uasset
Normal file
BIN
ProjectFish/Content/Materials/M_DeskModeMat.uasset
Normal file
Binary file not shown.
Binary file not shown.
@ -0,0 +1,7 @@
|
||||
{
|
||||
"BuildId": "37670630",
|
||||
"Modules":
|
||||
{
|
||||
"DeskMode": "UnrealEditor-DeskMode.dll"
|
||||
}
|
||||
}
|
||||
24
ProjectFish/Plugins/DeskMode/DeskMode.uplugin
Normal file
24
ProjectFish/Plugins/DeskMode/DeskMode.uplugin
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"FileVersion": 3,
|
||||
"Version": 1,
|
||||
"VersionName": "1.0",
|
||||
"FriendlyName": "DeskMode",
|
||||
"Description": "",
|
||||
"Category": "Other",
|
||||
"CreatedBy": "",
|
||||
"CreatedByURL": "",
|
||||
"DocsURL": "",
|
||||
"MarketplaceURL": "",
|
||||
"SupportURL": "",
|
||||
"CanContainContent": true,
|
||||
"IsBetaVersion": false,
|
||||
"IsExperimentalVersion": false,
|
||||
"Installed": false,
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "DeskMode",
|
||||
"Type": "Runtime",
|
||||
"LoadingPhase": "Default"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
ProjectFish/Plugins/DeskMode/Resources/Icon128.png
Normal file
BIN
ProjectFish/Plugins/DeskMode/Resources/Icon128.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
@ -0,0 +1,54 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class DeskMode : ModuleRules
|
||||
{
|
||||
public DeskMode(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PublicIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add public include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add other private include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PublicDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
// ... add other public dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
// ... add private dependencies that you statically link with here ...
|
||||
"UnrealEd"
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
DynamicallyLoadedModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
// ... add any modules that your module loads dynamically here ...
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,73 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "DeskMode.h"
|
||||
|
||||
#include "WindowTransparency.h"
|
||||
#include "Features/EditorFeatures.h"
|
||||
#include "Features/IPluginsEditorFeature.h"
|
||||
DEFINE_LOG_CATEGORY_STATIC(LogDeskMode, Log, All);
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FDeskModeModule"
|
||||
|
||||
void FDeskModeModule::StartupModule()
|
||||
{
|
||||
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
|
||||
//绑定module注册回调
|
||||
IModularFeatures& ModularFeatures = IModularFeatures::Get();
|
||||
ModularFeatures.OnModularFeatureRegistered().AddRaw(this, &FDeskModeModule::OnModularFeatureRegistered);
|
||||
//防止plugin模块先加载
|
||||
if (ModularFeatures.IsModularFeatureAvailable(EditorFeatures::PluginsEditor))
|
||||
{
|
||||
OnModularFeatureRegistered(EditorFeatures::PluginsEditor, &ModularFeatures.GetModularFeature<IPluginsEditorFeature>(EditorFeatures::PluginsEditor));
|
||||
}
|
||||
}
|
||||
|
||||
void FDeskModeModule::ShutdownModule()
|
||||
{
|
||||
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
|
||||
// we call this function before unloading the module.
|
||||
if (IsValid(WindowTransparency))
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
UE_LOG(LogDeskMode, Log, TEXT("WindowTransparency is valid, attempting to restore settings."));
|
||||
WindowTransparency->RestoreDefaultWindowSettings();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void FDeskModeModule::OnModularFeatureRegistered(const FName& Name, IModularFeature* ModularFeature)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
TObjectPtr<class UWindowTransparency> FDeskModeModule::GetWindowTransparency()
|
||||
{
|
||||
FDeskModeModule* Module = FModuleManager::GetModulePtr<FDeskModeModule>("DeskMode");
|
||||
if (Module)
|
||||
{
|
||||
Module->WindowTransparency = nullptr;
|
||||
#if PLATFORM_WINDOWS
|
||||
if (!Module->WindowTransparency && !IsRunningCommandlet() && !IsRunningDedicatedServer() && GEngine)
|
||||
{
|
||||
Module->WindowTransparency = NewObject<UWindowTransparency>();
|
||||
if (Module->WindowTransparency->Initialize())
|
||||
{
|
||||
UE_LOG(LogDeskMode, Log, TEXT("WindowTransparency instance created and initialized."));
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogDeskMode, Error, TEXT("WindowTransparency instance created but failed to initialize HWND."));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return Module->WindowTransparency;
|
||||
}
|
||||
UE_LOG(LogDeskMode, Log, TEXT("WindowTransparency is null, GetWindowTransparency() error."));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
IMPLEMENT_MODULE(FDeskModeModule, DeskMode)
|
||||
@ -0,0 +1,22 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "DeskModeFunctionLibrary.h"
|
||||
|
||||
#include "DeskMode.h"
|
||||
#include "WindowTransparency.h"
|
||||
|
||||
void UDeskModeFunctionLibrary::SetWindowFeatures(bool bEnableWindowTransparency, bool bEnableBorderless,
|
||||
bool bEnableClickThrough, bool bSetWindowTopmost)
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
TObjectPtr<UWindowTransparency> WindowTransparency = FDeskModeModule::GetWindowTransparency();
|
||||
if (IsValid(WindowTransparency))
|
||||
{
|
||||
WindowTransparency->EnableBorderless(bEnableWindowTransparency);
|
||||
WindowTransparency->EnableWindowTransparency(bEnableWindowTransparency);
|
||||
WindowTransparency->EnableClickThrough(bEnableClickThrough);
|
||||
WindowTransparency->SetWIndowsTopmost(bSetWindowTopmost);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@ -0,0 +1,372 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "WindowTransparency.h"
|
||||
|
||||
#include "Widgets/SViewport.h"
|
||||
DEFINE_LOG_CATEGORY_STATIC(LogWindowTransparency, Log, All);
|
||||
|
||||
HWND UWindowTransparency::GetGameHWnd()
|
||||
{
|
||||
if (GEngine->GameViewport->GetWindow())
|
||||
{
|
||||
TSharedPtr<SWindow> StrongSWindow = GEngine->GameViewport->GetWindow();
|
||||
if (StrongSWindow.IsValid() && StrongSWindow->GetNativeWindow().IsValid()) {
|
||||
void* Handle = StrongSWindow->GetNativeWindow()->GetOSWindowHandle();
|
||||
UE_LOG(LogWindowTransparency, Verbose, TEXT("GetGameHWnd: Returning HWND from cached GameSWindowPtr: %p"), Handle);
|
||||
return static_cast<HWND>(Handle);
|
||||
}
|
||||
}
|
||||
// if (GEngine && GEngine->GameViewport && GEngine->GameViewport->GetWindow().IsValid())
|
||||
// {
|
||||
// TSharedPtr<SWindow> GameSWindow = GEngine->GameViewport->GetWindow();
|
||||
// const_cast<UWindowTransparencyHelper*>(this)->GameSWindowPtr = GameSWindow;
|
||||
// if (GameSWindow.IsValid() && GameSWindow->GetNativeWindow().IsValid())
|
||||
// {
|
||||
// void* Handle = GameSWindow->GetNativeWindow()->GetOSWindowHandle();
|
||||
// UE_LOG(LogWindowTransparency, Verbose, TEXT("GetGameHWnd: Returning HWND from GEngine->GameViewport and caching SWindow: %p"), Handle);
|
||||
// return static_cast<HWND>(Handle);
|
||||
// }
|
||||
// }
|
||||
// if (FSlateApplication::IsInitialized())
|
||||
// {
|
||||
// TSharedPtr<SWindow> ActiveWindow = FSlateApplication::Get().GetActiveTopLevelWindow();
|
||||
// if (ActiveWindow.IsValid() && ActiveWindow->GetNativeWindow().IsValid())
|
||||
// {
|
||||
// void* Handle = ActiveWindow->GetNativeWindow()->GetOSWindowHandle();
|
||||
// UE_LOG(LogWindowTransparency, Verbose, TEXT("GetGameHWnd: Returning HWND from GetActiveTopLevelWindow (fallback): %p"), Handle);
|
||||
// return static_cast<HWND>(Handle);
|
||||
// }
|
||||
// }
|
||||
// UE_LOG(LogWindowTransparency, Warning, TEXT("GetGameHWnd: Could not retrieve HWND."));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool UWindowTransparency::Initialize()
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
if (GameHWnd)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
GameHWnd = GetGameHWnd();
|
||||
if (GameHWnd)
|
||||
{
|
||||
OriginalWindowStyle = GetWindowLongPtr(GameHWnd, GWL_STYLE);
|
||||
OriginalExWindowStyle = GetWindowLongPtr(GameHWnd, GWL_EXSTYLE);
|
||||
|
||||
LONG_PTR CurrentExStyle = GetWindowLongPtr(GameHWnd, GWL_EXSTYLE);
|
||||
bIsClickThroughStateOS = (CurrentExStyle & WS_EX_TRANSPARENT) != 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
UE_LOG(LogWindowTransparency, Warning, TEXT("WindowTransparencyHelper: Could not get game HWND during Initialize."));
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void UWindowTransparency::RestoreDefaultWindowSettings()
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
UE_LOG(LogWindowTransparency, Log, TEXT("Attempting to restore default window settings..."));
|
||||
if (!GameHWnd || !IsWindow(GameHWnd))
|
||||
{
|
||||
UE_LOG(LogWindowTransparency, Warning, TEXT("Cannot restore default settings: HWND is null or invalid."));
|
||||
|
||||
bIsClickThroughStateOS = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetWindowLongPtr(GameHWnd, GWL_EXSTYLE) != OriginalExWindowStyle)
|
||||
{
|
||||
SetWindowLongPtr(GameHWnd, GWL_EXSTYLE, OriginalExWindowStyle);
|
||||
UE_LOG(LogWindowTransparency, Log, TEXT("Restored OriginalExWindowStyle."));
|
||||
|
||||
}
|
||||
|
||||
SetWindowPos(GameHWnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE);
|
||||
InvalidateRect(GameHWnd, NULL, true);
|
||||
UpdateWindow(GameHWnd);
|
||||
UE_LOG(LogWindowTransparency, Log, TEXT("Window settings restoration commands issued."));
|
||||
#endif
|
||||
}
|
||||
|
||||
void UWindowTransparency::EnableBorderless(bool bEnableWindowTransparency)
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
if (!GameHWnd)
|
||||
{
|
||||
UE_LOG(LogWindowTransparency, Error, TEXT("EnableBorderless failed, GameHWnd is null"));
|
||||
return;
|
||||
}
|
||||
LONG_PTR CurrentStyle = GetWindowLongPtr(GameHWnd, GWL_STYLE);
|
||||
bool bIsCurrentlyBorderless = !(CurrentStyle & WS_CAPTION) && !(CurrentStyle & WS_THICKFRAME);
|
||||
|
||||
LONG_PTR NewStyle = bEnableWindowTransparency ? ((OriginalWindowStyle & ~WS_OVERLAPPEDWINDOW) | WS_POPUP) : OriginalWindowStyle;
|
||||
SetWindowLongPtr(GameHWnd, GWL_STYLE, NewStyle);
|
||||
SetWindowPos(GameHWnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE);
|
||||
InvalidateRect(GameHWnd, NULL, true);
|
||||
UpdateWindow(GameHWnd);
|
||||
UE_LOG(LogWindowTransparency, Log, TEXT("Borderless mode set to: %s"), bEnableWindowTransparency ? TEXT("true") : TEXT("false"));
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void UWindowTransparency::EnableWindowTransparency(bool bEnableWindowTransparency)
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
if (!GameHWnd)
|
||||
{
|
||||
UE_LOG(LogWindowTransparency, Error, TEXT("EnableWindowTransparency failed, GameHWnd is null"));
|
||||
return;
|
||||
}
|
||||
|
||||
//设置窗口透明
|
||||
MARGINS margins = bEnableWindowTransparency ? MARGINS{ -1 } : MARGINS{ 0, 0, 0, 0 };
|
||||
HRESULT hr = DwmExtendFrameIntoClientArea(GameHWnd, &margins);
|
||||
if (!SUCCEEDED(hr))
|
||||
{
|
||||
UE_LOG(LogWindowTransparency, Error, TEXT("DwmExtendFrameIntoClientArea %s failed with HRESULT: 0x%08lX"), bEnableWindowTransparency ? TEXT("enable") : TEXT("disable"), hr);
|
||||
}
|
||||
|
||||
InvalidateRect(GameHWnd, NULL, true);
|
||||
UpdateWindow(GameHWnd);
|
||||
UE_LOG(LogWindowTransparency, Log, TEXT("DWM Transparency set to: %s"), bEnableWindowTransparency ? TEXT("true") : TEXT("false"));
|
||||
#endif
|
||||
}
|
||||
|
||||
void UWindowTransparency::EnableClickThrough(bool bEnableClickThrough)
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
if (!GameHWnd)
|
||||
{
|
||||
UE_LOG(LogWindowTransparency, Warning, TEXT("EnableClickThrough: Not initialized or HWND is null. Cannot set OS click-through state."));
|
||||
return;
|
||||
}
|
||||
|
||||
LONG_PTR CurrentExStyle = GetWindowLongPtr(GameHWnd, GWL_EXSTYLE);
|
||||
bool bIsCurrentlyClickThroughOSLevel = (CurrentExStyle & WS_EX_TRANSPARENT) != 0;
|
||||
|
||||
LONG_PTR NewExStyle;
|
||||
if (bEnableClickThrough)
|
||||
{
|
||||
NewExStyle = CurrentExStyle | WS_EX_LAYERED | WS_EX_TRANSPARENT;
|
||||
}
|
||||
else
|
||||
{
|
||||
NewExStyle = CurrentExStyle & ~WS_EX_TRANSPARENT;
|
||||
|
||||
}
|
||||
bIsClickThroughStateOS = bEnableClickThrough;
|
||||
SetWindowLongPtr(GameHWnd, GWL_EXSTYLE, NewExStyle);
|
||||
LONG_PTR StyleAfterSet = GetWindowLongPtr(GameHWnd, GWL_EXSTYLE);
|
||||
bool bSetSuccessfully = (bEnableClickThrough && (StyleAfterSet & WS_EX_TRANSPARENT)) || (!bEnableClickThrough && !(StyleAfterSet & WS_EX_TRANSPARENT));
|
||||
|
||||
UE_LOG(LogWindowTransparency, Log, TEXT("EnableClickThrough: OS Click-Through set to %s. OldExStyle: 0x%p, Attempted NewExStyle: 0x%p, Actual StyleAfterSet: 0x%p. Success: %s"),
|
||||
bEnableClickThrough ? TEXT("true") : TEXT("false"),
|
||||
(void*)CurrentExStyle,
|
||||
(void*)NewExStyle,
|
||||
(void*)StyleAfterSet,
|
||||
bSetSuccessfully ? TEXT("Yes") : TEXT("No"));
|
||||
|
||||
if (bSetSuccessfully) {
|
||||
SetWindowPos(GameHWnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE);
|
||||
}
|
||||
else {
|
||||
UE_LOG(LogWindowTransparency, Error, TEXT("EnableClickThrough: Failed to apply desired ExStyle change!"));
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void UWindowTransparency::SetWIndowsTopmost(bool bSetWindowTopmost)
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
if (!GameHWnd)
|
||||
{
|
||||
UE_LOG(LogWindowTransparency, Warning, TEXT("SetWIndowsTopmost: Not initialized or HWND is null."));
|
||||
return;
|
||||
}
|
||||
LONG_PTR CurrentExStyle = GetWindowLongPtr(GameHWnd, GWL_EXSTYLE);
|
||||
|
||||
HWND HwndInsertAfter = bSetWindowTopmost ? HWND_TOPMOST : HWND_NOTOPMOST;
|
||||
SetWindowPos(GameHWnd, HwndInsertAfter, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
||||
UE_LOG(LogWindowTransparency, Log, TEXT("Window topmost set to: %s"), bSetWindowTopmost ? TEXT("true") : TEXT("false"));
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void UWindowTransparency::Tick(float DeltaTime)
|
||||
{
|
||||
|
||||
if (!bIsClickThroughStateOS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateHitDetectionLogic(DeltaTime);
|
||||
|
||||
bool bShouldBeClickThroughLogically = !bIsMouseOverOpaqueAreaLogic;
|
||||
|
||||
if (bIsClickThroughStateOS != bShouldBeClickThroughLogically)
|
||||
{
|
||||
UE_LOG(LogWindowTransparency, Verbose, TEXT("Tick: Logic dictates click-through: %s. OS state is: %s. Updating OS state."),
|
||||
bShouldBeClickThroughLogically ? TEXT("true") : TEXT("false"),
|
||||
bIsClickThroughStateOS ? TEXT("true") : TEXT("false"));
|
||||
EnableClickThrough(bShouldBeClickThroughLogically);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TStatId UWindowTransparency::GetStatId() const
|
||||
{
|
||||
RETURN_QUICK_DECLARE_CYCLE_STAT(UWindowTransparency, STATGROUP_Tickables);
|
||||
}
|
||||
|
||||
void UWindowTransparency::UpdateHitDetectionLogic(float DeltaTime)
|
||||
{
|
||||
#if PLATFORM_WINDOWS
|
||||
bool bMousePosSuccess;
|
||||
FVector2D MousePosInWindow = GetMousePositionInWindow(bMousePosSuccess);
|
||||
|
||||
if (!bMousePosSuccess)
|
||||
{
|
||||
bIsMouseOverOpaqueAreaLogic = false;
|
||||
UE_LOG(LogWindowTransparency, Verbose, TEXT("UpdateHitDetectionLogic: Mouse position not retrieved. Assuming transparent area."));
|
||||
return;
|
||||
}
|
||||
|
||||
bIsMouseOverOpaqueAreaLogic = SimulateGameRaycastUnderMouse(MousePosInWindow);
|
||||
// const UEnum* EnumPtr = StaticEnum<ECollisionChannel>();
|
||||
// FString ChannelName = EnumPtr ? EnumPtr->GetNameStringByValue(static_cast<int64>(GameRaycastTraceChannelLogic)) : FString::FromInt(static_cast<int32>(GameRaycastTraceChannelLogic));
|
||||
// UE_LOG(LogWindowTransparency, Log, TEXT("GameRaycastTest Result: bIsMouseOverOpaqueAreaLogic = %s at Pos: %s (Channel: %s)"),
|
||||
// bIsMouseOverOpaqueAreaLogic ? TEXT("true (Opaque)") : TEXT("false (Transparent)"), *MousePosInWindow.ToString(), *ChannelName);
|
||||
#endif
|
||||
}
|
||||
|
||||
FVector2D UWindowTransparency::GetMousePositionInWindow(bool& bMousePosSuccess)
|
||||
{
|
||||
bMousePosSuccess = false;
|
||||
#if PLATFORM_WINDOWS
|
||||
|
||||
POINT CursorPosScreen;
|
||||
if (::GetCursorPos(&CursorPosScreen))
|
||||
{
|
||||
RECT WindowRect;
|
||||
if (::GetWindowRect(GameHWnd, &WindowRect))
|
||||
{
|
||||
bMousePosSuccess = true;
|
||||
return FVector2D(static_cast<float>(CursorPosScreen.x - WindowRect.left), static_cast<float>(CursorPosScreen.y - WindowRect.top));
|
||||
}
|
||||
else
|
||||
{
|
||||
DWORD ErrorCode = GetLastError();
|
||||
UE_LOG(LogWindowTransparency, Error, TEXT("GetMousePositionInWindow: GetWindowRect failed for HWND %p. Error code: %u"), GameHWnd, ErrorCode);
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return FVector2D::ZeroVector;
|
||||
}
|
||||
|
||||
bool UWindowTransparency::SimulateGameRaycastUnderMouse(FVector2D MousePos)
|
||||
{
|
||||
|
||||
UWorld* World = GetWorld();
|
||||
APlayerController* PC = nullptr;
|
||||
if (World)
|
||||
{
|
||||
PC = Cast<APlayerController>(GetWorld()->GetFirstLocalPlayerFromController());
|
||||
}
|
||||
if (!PC)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
//检测3D世界是否有碰撞
|
||||
FHitResult HitResult3D;
|
||||
FCollisionQueryParams CollisionParams3D(SCENE_QUERY_STAT(WindowTransparencyRaycast3D), true);
|
||||
|
||||
bool bHit3D = PC->GetHitResultAtScreenPosition(
|
||||
MousePos,
|
||||
ECC_Visibility,
|
||||
CollisionParams3D,
|
||||
HitResult3D
|
||||
);
|
||||
|
||||
if (bHit3D && HitResult3D.GetActor())
|
||||
{
|
||||
// AActor* HitActor = HitResult3D.GetActor();
|
||||
// const UEnum* EnumPtr = StaticEnum<ECollisionChannel>();
|
||||
// FString ChannelName = EnumPtr ? EnumPtr->GetNameStringByValue(static_cast<int64>(this->GameRaycastTraceChannelLogic)) : FString::FromInt(static_cast<int32>(this->GameRaycastTraceChannelLogic));
|
||||
// UE_LOG(LogWindowHelper, Verbose, TEXT("GameRaycastTest: Hit 3D Actor: %s (Component: %s) using Channel %s"),
|
||||
// *HitActor->GetName(),
|
||||
// HitResult3D.GetComponent() ? *HitResult3D.GetComponent()->GetName() : TEXT("None"),
|
||||
// *ChannelName);
|
||||
return true;
|
||||
}
|
||||
//2D检测是否有可点击UI
|
||||
if (FSlateApplication::IsInitialized() && GEngine && GEngine->GameViewport)
|
||||
{
|
||||
TSharedPtr<SWindow> GameSWindow = GEngine->GameViewport->GetWindow();
|
||||
if (GameSWindow.IsValid())
|
||||
{
|
||||
TArray<TSharedRef<SWindow>> WindowsToSearch;
|
||||
WindowsToSearch.Add(GameSWindow.ToSharedRef());
|
||||
|
||||
FVector2D MousePosScreen = MousePos + GEngine->GameViewport->GetGameViewportWidget()->GetCachedGeometry().GetAbsolutePosition();
|
||||
FWidgetPath WidgetPath = FSlateApplication::Get().LocateWindowUnderMouse(
|
||||
MousePosScreen,
|
||||
WindowsToSearch,
|
||||
false, /*bAllowDisabledWidgets*/
|
||||
PC->GetLocalPlayer() ? PC->GetLocalPlayer()->GetControllerId() : 0
|
||||
);
|
||||
|
||||
if (WidgetPath.IsValid() && WidgetPath.Widgets.Num() > 0)
|
||||
{
|
||||
const FArrangedWidget& LastWidgetArranged = WidgetPath.Widgets.Last();
|
||||
TSharedRef<SWidget> HitWidget = LastWidgetArranged.Widget;
|
||||
FString WidgetType = HitWidget->GetTypeAsString();
|
||||
FString WidgetDesc = HitWidget->ToString();
|
||||
|
||||
UE_LOG(LogWindowTransparency, Verbose, TEXT("GameRaycastTest: UI Hit Candidate: Type: %s, Desc: %s, Visible: %d, Enabled: %d, PathLen: %d"),
|
||||
*WidgetType, *WidgetDesc, HitWidget->GetVisibility().IsVisible(), HitWidget->IsEnabled(), WidgetPath.Widgets.Num());
|
||||
|
||||
if (HitWidget->GetVisibility().IsVisible() && HitWidget->IsEnabled())
|
||||
{
|
||||
if (WidgetType == TEXT("SWindow") ||
|
||||
WidgetType == TEXT("SGameLayerManager") ||
|
||||
WidgetType == TEXT("SViewport") ||
|
||||
(WidgetType == TEXT("SBorder") && WidgetPath.Widgets.Num() <= 2) ||
|
||||
(WidgetType == TEXT("SOverlay") && WidgetPath.Widgets.Num() <= 2) ||
|
||||
(WidgetType == TEXT("SScaleBox") && WidgetPath.Widgets.Num() <= 2) ||
|
||||
(WidgetType == TEXT("SCanvasPanel") && WidgetPath.Widgets.Num() <= 2) ||
|
||||
(WidgetType == TEXT("SObjectWidget") && WidgetPath.Widgets.Num() <= 1)
|
||||
)
|
||||
{
|
||||
UE_LOG(LogWindowTransparency, Verbose, TEXT("GameRaycastTest: UI Hit on %s (%s), but considering it non-blocking/transparent due to type/context."), *WidgetType, *WidgetDesc);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogWindowTransparency, Verbose, TEXT("GameRaycastTest: Hit blocking UI Widget: %s (%s)"), *WidgetType, *WidgetDesc);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// const UEnum* EnumPtr = StaticEnum<ECollisionChannel>();
|
||||
// FString ChannelName = EnumPtr ? EnumPtr->GetNameStringByValue(static_cast<int64>(this->GameRaycastTraceChannelLogic)) : FString::FromInt(static_cast<int32>(this->GameRaycastTraceChannelLogic));
|
||||
// UE_LOG(LogWindowTransparency, Verbose, TEXT("GameRaycastTest: No blocking hit found on 3D (using Channel %s) or UI. Assuming transparent."), *ChannelName);
|
||||
return false;
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@ -0,0 +1,19 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
class FDeskModeModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
|
||||
/** IModuleInterface implementation */
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
void OnModularFeatureRegistered(const FName& Name, IModularFeature* ModularFeature);
|
||||
|
||||
static TObjectPtr<class UWindowTransparency> GetWindowTransparency();
|
||||
private:
|
||||
TObjectPtr<class UWindowTransparency> WindowTransparency;
|
||||
};
|
||||
@ -0,0 +1,20 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "DeskModeFunctionLibrary.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class DESKMODE_API UDeskModeFunctionLibrary : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, Category = "DeskMode")
|
||||
static void SetWindowFeatures(bool bEnableWindowTransparency, bool bEnableBorderless, bool bEnableClickThrough, bool bSetWindowTopmost);
|
||||
};
|
||||
@ -0,0 +1,54 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#include "Windows/AllowWindowsPlatformTypes.h"
|
||||
#include <dwmapi.h>
|
||||
#include <WinUser.h>
|
||||
#include "Windows/HideWindowsPlatformTypes.h"
|
||||
#endif
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/Object.h"
|
||||
#include "WindowTransparency.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class DESKMODE_API UWindowTransparency : public UObject, public FTickableGameObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
HWND GetGameHWnd();
|
||||
bool Initialize();
|
||||
void RestoreDefaultWindowSettings();
|
||||
//设置窗口无边框
|
||||
void EnableBorderless(bool bEnableWindowTransparency);
|
||||
//设置窗口透明
|
||||
void EnableWindowTransparency(bool bEnableWindowTransparency);
|
||||
//设置窗口是否点击穿透
|
||||
void EnableClickThrough(bool bEnableClickThrough);
|
||||
//设置窗口顶层
|
||||
void SetWIndowsTopmost(bool bSetWindowTopmost);
|
||||
FVector2D GetMousePositionInWindow(bool& bMousePosSuccess);
|
||||
bool SimulateGameRaycastUnderMouse(FVector2D MousePos);
|
||||
//更新鼠标位置逻辑判断
|
||||
void UpdateHitDetectionLogic(float DeltaTime);
|
||||
//////////Tick////////////////
|
||||
virtual void Tick(float DeltaTime) override;
|
||||
virtual ETickableTickType GetTickableTickType() const override { return ETickableTickType::Always; }
|
||||
virtual TStatId GetStatId() const override;
|
||||
virtual bool IsTickableWhenPaused() const override { return true; }
|
||||
virtual bool IsTickableInEditor() const override { return false; }
|
||||
private:
|
||||
#if PLATFORM_WINDOWS
|
||||
HWND GameHWnd;
|
||||
LONG_PTR OriginalWindowStyle;
|
||||
LONG_PTR OriginalExWindowStyle;
|
||||
#endif
|
||||
bool bIsMouseOverOpaqueAreaLogic;; //鼠标是否经过程序的可点击区域
|
||||
bool bIsClickThroughStateOS; //当前的点击穿透状态
|
||||
};
|
||||
|
||||
@ -24,11 +24,13 @@
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=72263A5F_002DD5F9_002D3915_002D8DA0_002DCD45C38A70E5_002Fdl_003ASource_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_002Fd_003ARuntime_002Fd_003ACoreUObject_002Fd_003APublic_002Fd_003AUObject_002Ff_003AUObjectGlobals_002Eh/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=72263A5F_002DD5F9_002D3915_002D8DA0_002DCD45C38A70E5_002Fdl_003ASource_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_002Fd_003ARuntime_002Fd_003ACore_002Fd_003APrivate_002Fd_003AMisc_002Ff_003AGuid_002Ecpp/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=72263A5F_002DD5F9_002D3915_002D8DA0_002DCD45C38A70E5_002Fdl_003ASource_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_002Fd_003ARuntime_002Fd_003ACore_002Fd_003APublic_002Fd_003AContainers_002Ff_003AArray_002Eh/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=72263A5F_002DD5F9_002D3915_002D8DA0_002DCD45C38A70E5_002Fdl_003ASource_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_002Fd_003ARuntime_002Fd_003ACore_002Fd_003APublic_002Fd_003AContainers_002Ff_003AMap_002Eh/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=72263A5F_002DD5F9_002D3915_002D8DA0_002DCD45C38A70E5_002Fdl_003ASource_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_002Fd_003ARuntime_002Fd_003ACore_002Fd_003APublic_002Fd_003ADelegates_002Ff_003ADelegateSignatureImpl_002Einl/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=72263A5F_002DD5F9_002D3915_002D8DA0_002DCD45C38A70E5_002Fdl_003ASource_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_002Fd_003ARuntime_002Fd_003ACore_002Fd_003APublic_002Fd_003ADelegates_002Ff_003ADelegate_002Eh/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=72263A5F_002DD5F9_002D3915_002D8DA0_002DCD45C38A70E5_002Fdl_003ASource_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_002Fd_003ARuntime_002Fd_003ACore_002Fd_003APublic_002Fd_003AMath_002Ff_003AUnrealMathUtility_002Eh/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=72263A5F_002DD5F9_002D3915_002D8DA0_002DCD45C38A70E5_002Fdl_003ASource_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_002Fd_003ARuntime_002Fd_003ACore_002Fd_003APublic_002Fd_003AMisc_002Ff_003AGuid_002Eh/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=72263A5F_002DD5F9_002D3915_002D8DA0_002DCD45C38A70E5_002Fdl_003ASource_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_002Fd_003ARuntime_002Fd_003ACore_002Fd_003APublic_002Fd_003AMisc_002Ff_003AMTAccessDetector_002Eh/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=72263A5F_002DD5F9_002D3915_002D8DA0_002DCD45C38A70E5_002Fdl_003ASource_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_002Fd_003ARuntime_002Fd_003ACore_002Fd_003APublic_002Fd_003AModules_002Ff_003AModuleInterface_002Eh/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=72263A5F_002DD5F9_002D3915_002D8DA0_002DCD45C38A70E5_002Fdl_003ASource_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_002Fd_003ARuntime_002Fd_003ACore_002Fd_003APublic_002Fd_003ATasks_002Ff_003ATaskPrivate_002Eh/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=72263A5F_002DD5F9_002D3915_002D8DA0_002DCD45C38A70E5_002Fdl_003ASource_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_002Fd_003ARuntime_002Fd_003ACoreUObject_002Fd_003APrivate_002Fd_003AUObject_002Ff_003AClass_002Ecpp/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=72263A5F_002DD5F9_002D3915_002D8DA0_002DCD45C38A70E5_002Fdl_003ASource_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_002Fd_003ARuntime_002Fd_003ACoreUObject_002Fd_003APrivate_002Fd_003AUObject_002Ff_003AGarbageCollection_002Ecpp/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
@ -38,6 +40,7 @@
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=72263A5F_002DD5F9_002D3915_002D8DA0_002DCD45C38A70E5_002Fdl_003ASource_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_002Fd_003ARuntime_002Fd_003ACoreUObject_002Fd_003APublic_002Fd_003AUObject_002Ff_003AUnrealType_002Eh/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=72263A5F_002DD5F9_002D3915_002D8DA0_002DCD45C38A70E5_002Fdl_003ASource_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_002Fd_003ARuntime_002Fd_003ACoreUObject_002Fd_003APublic_002Fd_003AUObject_002Ff_003AUObjectArray_002Eh/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=72263A5F_002DD5F9_002D3915_002D8DA0_002DCD45C38A70E5_002Fdl_003ASource_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_002Fd_003ARuntime_002Fd_003ACoreUObject_002Fd_003APublic_002Fd_003AUObject_002Ff_003AUObjectBase_002Eh/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=72263A5F_002DD5F9_002D3915_002D8DA0_002DCD45C38A70E5_002Fdl_003ASource_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_002Fd_003ARuntime_002Fd_003ACore_002Fd_003APublic_002Fd_003ATemplates_002Ff_003ATuple_002Eh/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=72263A5F_002DD5F9_002D3915_002D8DA0_002DCD45C38A70E5_002Fdl_003ASource_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_002Fd_003ARuntime_002Fd_003AEngine_002Fd_003AClasses_002Fd_003AGameFramework_002Ff_003ACharacterMovementComponent_002Eh/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=72263A5F_002DD5F9_002D3915_002D8DA0_002DCD45C38A70E5_002Fdl_003ASource_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_002Fd_003ARuntime_002Fd_003AEngine_002Fd_003APrivate_002Fd_003AComponents_002Ff_003ACharacterMovementComponent_002Ecpp/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=72263A5F_002DD5F9_002D3915_002D8DA0_002DCD45C38A70E5_002Fdl_003ASource_003AF_0021_003FEpic_003FUE_005F5_002E5_003FEngine_003FSource_002Fd_003ARuntime_002Fd_003AEngine_002Fd_003APrivate_002Ff_003AGameplayStatics_002Ecpp/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user