12 [
Attribute(
"1", UIWidgets.CheckBox,
"When true and scroll input is detected, the component will try to find a new focused widget, or will reset focused widget to null if no suitable widget found")]
13 protected bool m_bTryFindNewFocus;
15 [
Attribute(
"1", UIWidgets.CheckBox,
"Set scroll's initial state")]
16 protected bool m_bScrollEnabled;
19 static const float SCROLL_SPEED_PX_PER_SECOND_MAX = 1500;
20 static const float WIDGET_DETECTION_MARGIN_PX = 3;
22 protected ScrollLayoutWidget m_wScroll;
24 #ifdef GAMEPAD_SCROLL_DEBUG
26 protected ref array<Widget> m_aDebugMarksPool;
27 protected ref array<Widget> m_aDebugMarksBusy;
30 protected bool m_bShouldBeEnabled =
true;
31 protected bool m_bForceDisabled;
34 override void HandlerAttached(Widget w)
36 super.HandlerAttached(w);
38 m_wScroll = ScrollLayoutWidget.Cast(w);
40 if (!
SCR_Global.IsEditMode() && m_bScrollEnabled)
41 GetGame().GetCallqueue().CallLater(OnEachFrame, 1,
true);
43 #ifdef GAMEPAD_SCROLL_DEBUG
44 m_aDebugMarksPool = {};
45 m_aDebugMarksBusy = {};
53 override void HandlerDeattached(Widget w)
57 ArmaReforgerScripted game =
GetGame();
58 if (game && game.GetCallqueue())
59 game.GetCallqueue().Remove(OnEachFrame);
70 SetForceDisabled(
false);
77 SetForceDisabled(
true);
86 float tDelta = ftime / 1000.0;
87 Update(tDelta, m_wScroll);
92 void Update(
float tDelta, ScrollLayoutWidget wScroll)
94 #ifdef GAMEPAD_SCROLL_DEBUG
95 DebugMarks_StartUpdate();
97 vector refPoint = GetReferencePoint(wScroll);
98 PlaceDebugMark(refPoint,
"-");
102 SCR_Rect2D scrollRect = GetWidgetRect(m_wScroll);
103 Widget scrollContent = m_wScroll.GetChildren();
106 SCR_Rect2D scrollContentRect = GetWidgetRect(scrollContent);
107 if (scrollContentRect.GetHeight() <= scrollRect.GetHeight())
111 float vScrollInput = GetVScrollInput();
112 if (vScrollInput != 0)
114 HandleGamepadScrolling(tDelta, wScroll);
115 if (m_bTryFindNewFocus)
116 TryFindNewFocus(wScroll);
119 #ifdef GAMEPAD_SCROLL_DEBUG
120 PlaceDebugMark(
"200 40 0",
string.Format(
"V Scroll Input: notNull: %1, value: %2", vScrollInput != 0, vScrollInput));
121 PlaceDebugMark(
"700 40 0",
string.Format(
"Current focus: %1",
GetGame().GetWorkspace().GetFocusedWidget()));
122 DebugMarks_EndUpdate();
127 protected static void HandleGamepadScrolling(
float tDelta, ScrollLayoutWidget wScroll)
129 Widget scrollContent = wScroll.GetChildren();
134 float xScrollSize, yScrollSize;
135 wScroll.GetScreenSize(xScrollSize, yScrollSize);
137 float xContentSize, yContentSize;
138 scrollContent.GetScreenSize(xContentSize, yContentSize);
140 if (yContentSize <= yScrollSize || yContentSize == 0)
143 float scrollInputValue = GetVScrollInput();
144 float xPosAbsCurrent, yPosAbsCurrent;
145 wScroll.GetSliderPosPixels(xPosAbsCurrent, yPosAbsCurrent);
147 float yPosAbsNew = yPosAbsCurrent - scrollInputValue * tDelta * SCROLL_SPEED_PX_PER_SECOND_MAX;
151 wScroll.SetSliderPosPixels(0, yPosAbsNew);
155 protected static float GetVScrollInput()
157 return GetGame().GetInputManager().GetActionValue(
"MenuScrollVertical");
162 protected static void TryUnfocusWidget(Widget scroll)
164 Widget currentFocus =
GetGame().GetWorkspace().GetFocusedWidget();
169 SCR_Rect2D scrollRect = GetWidgetRect(scroll);
170 SCR_Rect2D focusedWidgetRect = GetWidgetRect(scroll);
172 if (!scrollRect.HasInside(focusedWidgetRect))
173 GetGame().GetWorkspace().SetFocusedWidget(
null);
178 protected void TryFindNewFocus(ScrollLayoutWidget wScroll)
180 SCR_Rect2D scrollRect = GetWidgetRect(wScroll);
181 scrollRect.ExpandAllDirections(WIDGET_DETECTION_MARGIN_PX);
184 array<Widget> allButtons = {};
185 FindAllButtons(wScroll, allButtons);
188 array<Widget> widgetsInFrame = {};
189 foreach (Widget w : allButtons)
192 if (scrollRect.HasInside(wRect))
193 widgetsInFrame.Insert(w);
196 if (widgetsInFrame.IsEmpty())
199 vector refPos = GetReferencePoint(wScroll);
202 Widget newFocus =
null;
205 metricMin = GetFocusMetric(refPos, widgetsInFrame[0]);
206 newFocus = widgetsInFrame[0];
208 foreach (Widget w : widgetsInFrame)
210 float m = GetFocusMetric(refPos, w);
212 #ifdef GAMEPAD_SCROLL_DEBUG
214 vector wRectCenter = wRect.GetCenter();
215 PlaceDebugMark(wRectCenter,
string.Format(
"%1", m));
225 GetGame().GetWorkspace().SetFocusedWidget(newFocus);
230 protected static vector GetReferencePoint(ScrollLayoutWidget wScroll)
232 SCR_Rect2D scrollRect = GetWidgetRect(wScroll);
233 float sliderX, sliderY;
234 wScroll.GetSliderPos(sliderX, sliderY);
236 refPoint[0] = scrollRect.p0[0];
237 refPoint[1] = scrollRect.p0[1] + sliderY * scrollRect.GetHeight();
244 Widget focusedWidget =
GetGame().GetWorkspace().GetFocusedWidget();
245 float xPos, yPos, width, height;
246 focusedWidget.GetScreenPos(xPos, yPos);
247 focusedWidget.GetScreenSize(width, height);
248 SCR_Rect2D focusedRect = GetWidgetRect(focusedWidget);
249 refPoint[0] = width * 0.5 + xPos - 1;
256 protected static float GetFocusMetric(vector v0, Widget w)
260 vector wRectPoint = wRect.p0;
261 wRectPoint[1] = 0.5 * (wRect.p0[1] + wRect.p1[1]);
263 vector v1 = wRectPoint;
265 float dx = v0[0] - v1[0];
266 float dy = v0[1] - v1[1];
268 return 100 *Math.AbsFloat(dy) + Math.AbsFloat(dx);
281 w.GetScreenPos(posX, posY);
282 w.GetScreenSize(sizeX, sizeY);
294 static void FindAllButtons(Widget w, array<Widget> arrayOut)
297 ButtonWidget wb = ButtonWidget.Cast(w);
300 if (wb.IsFocusable())
304 Widget child = w.GetChildren();
307 FindAllButtons(child, arrayOut);
308 child = child.GetSibling();
313 void SetTryFindNewFocus(
bool tryFindNewFocus)
315 m_bTryFindNewFocus = tryFindNewFocus;
319 void SetEnabled(
bool enabled)
321 m_bShouldBeEnabled = enabled;
322 SetEnabled_Internal(enabled);
326 void SetEnabled_Internal(
bool enabled)
328 GetGame().GetCallqueue().Remove(OnEachFrame);
330 m_bScrollEnabled = enabled && !m_bForceDisabled;
331 if (m_bScrollEnabled)
332 GetGame().GetCallqueue().CallLater(OnEachFrame, 1,
true);
336 protected void SetForceDisabled(
bool forceDisabled)
338 m_bForceDisabled = forceDisabled;
341 SetEnabled_Internal(
false);
343 SetEnabled_Internal(m_bShouldBeEnabled);
350 #ifdef GAMEPAD_SCROLL_DEBUG
353 protected void DebugMarks_StartUpdate()
356 foreach (Widget w : m_aDebugMarksBusy)
358 m_aDebugMarksPool.Insert(w);
361 m_aDebugMarksBusy.Clear();
367 protected void PlaceDebugMark(vector pos,
string text)
369 WorkspaceWidget workspace =
GetGame().GetWorkspace();
372 if (!m_aDebugMarksPool.IsEmpty())
374 w = m_aDebugMarksPool[0];
375 m_aDebugMarksPool.Remove(0);
379 Widget workspaceRoot =
GetGame().GetWorkspace();
380 w = workspace.CreateWidgets(
"{79FC19F28E381C1E}UI/layouts/Menus/DebugTextMark.layout", workspaceRoot);
383 m_aDebugMarksBusy.Insert(w);
385 TextWidget wtext = TextWidget.Cast(w.FindWidget(
"Text"));
388 FrameSlot.SetPos(w, workspace.DPIUnscale(pos[0]), workspace.DPIUnscale(pos[1]));
392 protected void DebugMarks_EndUpdate()
395 foreach (Widget w : m_aDebugMarksPool)