Bug表现
在UE4 4.23版本,实现自定义控件时,继承了RetainerBox,发现当内部包裹子控件时,先编辑器面板选中子物体,发现选中位置有偏移。
发现当窗口最小化时,偏移位置更加明显,感觉这个选中框跟整个window的位置有关了。
定位问题
使用UE4工具“控件反射器”,定位到UI编辑器页面是在源码"SDesignerView.cpp"中
查找头文件,发现有个关键函数“DrawSelectionAndHoverOutline”此函数就是画出Slate控件选中边框的关键函数。
看看此函数具体实现为:
对选中SelectedSlateWidget,
调用“FDesignTimeUtils::GetArrangedWidgetRelativeToWindow(Widget, ArrangedWidget)”
然后生成SelectionGeometry,并根据此Geometry生成SelectionZone也就是选中框范围,根据SelectionZone的4个顶点调用FSlateDrawElement::Makelines画出选中控件的边缘框。
推测是原因Widget的Geometry错误。 FDesignTimeUtils::GetArrangedWidgetRelativeToWindow此函数的计算方法不对,导致返回的Geometry有误差。
跟踪此方法:
GetArrangedWidgetRelativeToWindow作用是,通过传入的Widget通过FindWidgetWindow找到它的最上层WidgetWindow,然后通过最上层window计算传入Widget的Geometry。
通过断点调试,CanvasPanel和RetainerBox子物体选中时函数执行状况。发现问题:
正确的情况下,FindWidgetWindow会找到传入Widget的最外层Window,但是在RetainerBox中,它返回了一个SVirtualWindow,它是SRetainerWidget内部的Window,并非最外层Window,导致计算的Geometry出错了。
修改方法
修改"DesignTimeUtils.cpp"中的GetArrangedWidgetRelativeToWindow方法,根据传入的Widegt,循环找到最外层的正确方法,如下
在38行,增加一个循环找到最外层Window的方法,赋值给WidegetWindow。修改后RetainerBox的选中框能正确显示了
后续
在UE4官方GitHub源码库中了解到,之前也有其他开发者报告了此问题,后续官方会在4.24中修复,官方会用一个更好的方法进行修复。
https://github.com/EpicGames/UnrealEngine/pull/6402
RetainerBox DrawSelectionAndHoverOutline SelectionZone Incorrect in DesignerView