原创文章,转载请注明出处。
最近做的功能点需要在用户点击各种关闭程序时做一些我们自己的逻辑判断,所以看了一下UE的窗体监听部分。
本文着重介绍Windows窗口关闭拦截,其他的情况都是类似的处理。
如果你也有对Windows窗体监听的需求,如以下的情况,可以看看文章的内容,希望对你有所帮助。
scene1> 窗口丢失焦点时: 鼠标离开当前程序
scene2> 窗口重新拿到焦点时:鼠标重新回到当前程序
scene3> 窗口被激活时
scene4> 窗口被取消激活时
scene5> 窗口即将关闭时
scene6> 窗口被移动时
可以触发 Windows 关闭 的有以下几种情况 如图。
编辑器各种调试模式下和Shipping发布下的各个关闭按钮都是OK的。
1>Windows左上角关闭
2>Windows右上角关闭
3>Windows任务栏关闭
4>Alt+F4关闭
有一种是没办法拦截的,Kill进程(通过任务管理器或者命令行方式)。
下面只介绍对正常关闭的拦截,Kill进程 不做介绍。
SetRequestDestroyWindowOverride是在SWindow.h里面。
在你的程序内加入下面的SetRequestDestroyWindowOverride部分代码, 注意需要GameViewport已经有值的情况下加。找到合适的位置添加上即可。
//重写Windows退出
GEngine->GameViewport->GetWindow()->SetRequestDestroyWindowOverride(FRequestDestroyWindowOverride::CreateUObject(GGameInstance, &USPGameInstance::OnExit));
//参数:当前要关闭的SWindow。PS:注意智能引用,如果你习惯性写一些lambda表达式的话, 用Lambda的时候要注意悬空引用问题。
//参数:当前要关闭的SWindow,注意智能引用。用Lambda的时候要注意悬空指针问题
void OnExit(const TSharedRef<SWindow>& window);
//退出的逻辑
//你的逻辑对bCanDestroy进行操作, 某种情况下将bCanDestroy修改成false那么就达到了关闭拦截的效果
void USPGameInstance::OnExit(const TSharedRef<SWindow>& window)
{
//你的逻辑对bCanDestroy进行操作, 某种情况下将bCanDestroy修改成false那么就达到了关闭拦截的效果
bool bCanDestroy = true;
{
//你的逻辑的相关处理
}
if (bCanDestroy)
{
// Destroy the window
FSlateApplication::Get().RequestDestroyWindow(window);
}
else
{
// Some of the logic cannot be closed, so we cannot close the window.
}
}
延展阅读:因为SetRequestDestroyWindowOverride是在SWindow.h里面,
如果你是 里面也提供了窗体激活/窗体失去激活等代理,你也同样可以对其进行监听。参考上面的关闭监听即可。
/** Invoked when the window has been activated. */
FOnWindowActivated OnWindowActivated;
FOnWindowActivatedEvent WindowActivatedEvent;
/** Invoked when the window has been deactivated. */
FOnWindowDeactivated OnWindowDeactivated;
FOnWindowDeactivatedEvent WindowDeactivatedEvent;
/** Invoked when the window is about to be closed. */
FOnWindowClosed OnWindowClosed;
FOnWindowClosedEvent WindowClosedEvent;
/** Invoked when the window is moved */
FOnWindowMoved OnWindowMoved;
UGameViewportClient类中有很多类似的方法,下面我将代码贴出来,一看便知。
感兴趣的话也可以进去看看源码,还是很有意思的。
比如编辑下怎么做的多个运行窗口,就是在ViewportClient的SplitscreenInfo上面。
/**
* 我们的USPGameViewportClient类
*/
UCLASS()
class SPPROJECT_API USPGameViewportClient : public UGameViewportClient
{
GENERATED_BODY()
public:
//失去焦点
virtual void LostFocus(FViewport* InViewport) override;
//重新接收焦点
virtual void ReceivedFocus(FViewport* InViewport) override;
//是否有焦点
virtual bool IsFocused(FViewport* Viewport) override;
//哪个Viewport被激活,
virtual void Activated(FViewport* InViewport, const FWindowActivateEvent& InActivateEvent) override;
//哪个Viewport失去激活
virtual void Deactivated(FViewport* InViewport, const FWindowActivateEvent& InActivateEvent) override;
//是不是被永久捕获
virtual bool IsInPermanentCapture() override;
//窗口关闭时的监听
virtual bool WindowCloseRequested() override;
virtual void CloseRequested(FViewport* Viewport) override;
};
和SWindow.h类似,UGameViewportClient里面也有很多可以监听的代理。比如下图
**我对下面这个关闭的代理做了一个测试。可以监听你的关闭逻辑, 左上角关闭, 右下关闭, 唯独编辑器调试下(New Editor Windows)模式下,右上角的关闭按钮监听不到。
测试代码,通过UGameViewportClient里面的OnWindowCloseRequested来做退出程序的监听测试。
编辑器调试下(New Editor Windows)模式下,右上角的关闭按钮监听不到。
UGameViewportClient* viewPortClient = GGameInstance->GetGameViewportClient();
if (viewPortClient)
{
viewPortClient->OnWindowCloseRequested().BindUObject(GGameInstance, &USPGameInstance::OnExit);
}
//配合布尔返回值使用, 返回true,则关闭窗体,反之不会关闭
bool USPGameInstance::OnExit()
{
if (/*your logic*/)
{
return true;
}
return false;
}
如果你有想监听Windows窗体,关闭/移动/激活/离开/Tick/Focus等需求的话
可以查阅以下几个类的源码,相信会有帮助
GameViewportDelegates.h
UGameViewportClient
SWindow.h
控件反射器Widget Reflector使用说明
通过 控件反射器Widget Reflector 我们可以找到UE引擎的编辑器实现的源码定位。
下面为默认的几种窗体模式
namespace EWindowMode
{
enum Type
{
/** The window is in true fullscreen mode */
Fullscreen,
/** The window has no border and takes up the entire area of the screen */
WindowedFullscreen,
/** The window has a border and may not take up the entire screen area */
Windowed,
/** The total number of supported window modes */
NumWindowModes
};
}
//设置窗口模式代码
//设置窗口模式
UGameUserSettings::GetGameUserSettings()->SetFullscreenMode(EWindowMode::Windowed);
UGameUserSettings::GetGameUserSettings()->SaveSettings();
UGameUserSettings::GetGameUserSettings()->ApplyResolutionSettings(false);
最后,欢迎大家指正,谢谢。
谢谢,创作不易,大侠请留步… 动起可爱的双手,来个赞再走呗 <( ̄︶ ̄)>