Arma Reforger Explorer  1.1.0.42
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
SCR_GamepadScroll.c
Go to the documentation of this file.
1 
8 // #define GAMEPAD_SCROLL_DEBUG
9 
11 {
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;
14 
15  [Attribute("1", UIWidgets.CheckBox, "Set scroll's initial state")]
16  protected bool m_bScrollEnabled;
17 
18  // Constants
19  static const float SCROLL_SPEED_PX_PER_SECOND_MAX = 1500;
20  static const float WIDGET_DETECTION_MARGIN_PX = 3;
21 
22  protected ScrollLayoutWidget m_wScroll;
23 
24 #ifdef GAMEPAD_SCROLL_DEBUG
25  // Handling of debug markers
26  protected ref array<Widget> m_aDebugMarksPool;
27  protected ref array<Widget> m_aDebugMarksBusy;
28 #endif
29 
30  protected bool m_bShouldBeEnabled = true;
31  protected bool m_bForceDisabled;
32 
33  //------------------------------------------------------------------------------------------------
34  override void HandlerAttached(Widget w)
35  {
36  super.HandlerAttached(w);
37 
38  m_wScroll = ScrollLayoutWidget.Cast(w);
39 
40  if (!SCR_Global.IsEditMode() && m_bScrollEnabled)
41  GetGame().GetCallqueue().CallLater(OnEachFrame, 1, true);
42 
43  #ifdef GAMEPAD_SCROLL_DEBUG
44  m_aDebugMarksPool = {};
45  m_aDebugMarksBusy = {};
46  #endif
47 
48  SCR_MenuHelper.GetOnMenuFocusGained().Insert(OnMenuFocusGained);
49  SCR_MenuHelper.GetOnMenuFocusLost().Insert(OnMenuFocusLost);
50  }
51 
52  //------------------------------------------------------------------------------------------------
53  override void HandlerDeattached(Widget w)
54  {
55  if (!SCR_Global.IsEditMode())
56  {
57  ArmaReforgerScripted game = GetGame();
58  if (game && game.GetCallqueue())
59  game.GetCallqueue().Remove(OnEachFrame);
60  }
61 
62  SCR_MenuHelper.GetOnMenuFocusGained().Remove(OnMenuFocusGained);
63  SCR_MenuHelper.GetOnMenuFocusLost().Remove(OnMenuFocusLost);
64  }
65 
66  //------------------------------------------------------------------------------------------------
67  protected void OnMenuFocusGained(ChimeraMenuBase menu)
68  {
69  if (menu == ChimeraMenuBase.GetOwnerMenu(m_wRoot))
70  SetForceDisabled(false);
71  }
72 
73  //------------------------------------------------------------------------------------------------
74  protected void OnMenuFocusLost(ChimeraMenuBase menu)
75  {
76  if (menu == ChimeraMenuBase.GetOwnerMenu(m_wRoot))
77  SetForceDisabled(true);
78  }
79 
80  //------------------------------------------------------------------------------------------------
81  void OnEachFrame()
82  {
83  if (!m_wScroll)
84  return;
85 
86  float tDelta = ftime / 1000.0; // ftime is milliseconds!
87  Update(tDelta, m_wScroll);
88  }
89 
90  //------------------------------------------------------------------------------------------------
92  void Update(float tDelta, ScrollLayoutWidget wScroll)
93  {
94 #ifdef GAMEPAD_SCROLL_DEBUG
95  DebugMarks_StartUpdate();
96 
97  vector refPoint = GetReferencePoint(wScroll);
98  PlaceDebugMark(refPoint, "-");
99 #endif
100 
101  // Bail if scroll content is smaller than scroll
102  SCR_Rect2D scrollRect = GetWidgetRect(m_wScroll);
103  Widget scrollContent = m_wScroll.GetChildren();
104  if (!scrollContent)
105  return;
106  SCR_Rect2D scrollContentRect = GetWidgetRect(scrollContent);
107  if (scrollContentRect.GetHeight() <= scrollRect.GetHeight())
108  return;
109 
110  // Handle scrolling, try find a new focus
111  float vScrollInput = GetVScrollInput();
112  if (vScrollInput != 0)
113  {
114  HandleGamepadScrolling(tDelta, wScroll);
115  if (m_bTryFindNewFocus)
116  TryFindNewFocus(wScroll);
117  }
118 
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();
123 #endif
124  }
125 
126  //------------------------------------------------------------------------------------------------
127  protected static void HandleGamepadScrolling(float tDelta, ScrollLayoutWidget wScroll)
128  {
129  Widget scrollContent = wScroll.GetChildren();
130 
131  if (!scrollContent)
132  return;
133 
134  float xScrollSize, yScrollSize;
135  wScroll.GetScreenSize(xScrollSize, yScrollSize);
136 
137  float xContentSize, yContentSize;
138  scrollContent.GetScreenSize(xContentSize, yContentSize);
139 
140  if (yContentSize <= yScrollSize || yContentSize == 0)
141  return;
142 
143  float scrollInputValue = GetVScrollInput();
144  float xPosAbsCurrent, yPosAbsCurrent;
145  wScroll.GetSliderPosPixels(xPosAbsCurrent, yPosAbsCurrent);
146 
147  float yPosAbsNew = yPosAbsCurrent - scrollInputValue * tDelta * SCROLL_SPEED_PX_PER_SECOND_MAX;
148 
149  //yPosRelNew = Math.Clamp(yPosRelNew, 0, 1);
150 
151  wScroll.SetSliderPosPixels(0, yPosAbsNew);
152  }
153 
154  //------------------------------------------------------------------------------------------------
155  protected static float GetVScrollInput()
156  {
157  return GetGame().GetInputManager().GetActionValue("MenuScrollVertical");
158  }
159 
160  //------------------------------------------------------------------------------------------------
161  // Try to unfocus current focused widget if it's out of scroll bounds
162  protected static void TryUnfocusWidget(Widget scroll)
163  {
164  Widget currentFocus = GetGame().GetWorkspace().GetFocusedWidget();
165 
166  if (!currentFocus)
167  return;
168 
169  SCR_Rect2D scrollRect = GetWidgetRect(scroll);
170  SCR_Rect2D focusedWidgetRect = GetWidgetRect(scroll);
171 
172  if (!scrollRect.HasInside(focusedWidgetRect))
173  GetGame().GetWorkspace().SetFocusedWidget(null);
174  }
175 
176  //------------------------------------------------------------------------------------------------
178  protected void TryFindNewFocus(ScrollLayoutWidget wScroll)
179  {
180  SCR_Rect2D scrollRect = GetWidgetRect(wScroll);
181  scrollRect.ExpandAllDirections(WIDGET_DETECTION_MARGIN_PX); // Otherwise the strict conditions omit some widgets at the edge sometimes
182 
183  // Find all potential focus widgets in base
184  array<Widget> allButtons = {};
185  FindAllButtons(wScroll, allButtons);
186 
187  // Find widgets within view area of Base
188  array<Widget> widgetsInFrame = {};
189  foreach (Widget w : allButtons)
190  {
191  SCR_Rect2D wRect = GetWidgetRect(w);
192  if (scrollRect.HasInside(wRect))
193  widgetsInFrame.Insert(w);
194  }
195 
196  if (widgetsInFrame.IsEmpty())
197  return;
198 
199  vector refPos = GetReferencePoint(wScroll);
200 
201  // Sort widgets by their proximity to prev focus, or just select any widget
202  Widget newFocus = null;
203 
204  float metricMin;
205  metricMin = GetFocusMetric(refPos, widgetsInFrame[0]);
206  newFocus = widgetsInFrame[0];
207 
208  foreach (Widget w : widgetsInFrame)
209  {
210  float m = GetFocusMetric(refPos, w);
211 
212 #ifdef GAMEPAD_SCROLL_DEBUG
213  SCR_Rect2D wRect = GetWidgetRect(w);
214  vector wRectCenter = wRect.GetCenter(); // !! The actual GetFocusMetric is not calculated between widget center!
215  PlaceDebugMark(wRectCenter, string.Format("%1", m));
216 #endif
217 
218  if (m < metricMin)
219  {
220  newFocus = w;
221  metricMin = m;
222  }
223  }
224 
225  GetGame().GetWorkspace().SetFocusedWidget(newFocus);
226  }
227 
228  //------------------------------------------------------------------------------------------------
230  protected static vector GetReferencePoint(ScrollLayoutWidget wScroll)
231  {
232  SCR_Rect2D scrollRect = GetWidgetRect(wScroll);
233  float sliderX, sliderY;
234  wScroll.GetSliderPos(sliderX, sliderY);
235  vector refPoint;
236  refPoint[0] = scrollRect.p0[0];
237  refPoint[1] = scrollRect.p0[1] + sliderY * scrollRect.GetHeight();
238 
239  if (!SCR_WidgetTools.InHierarchy(GetGame().GetWorkspace().GetFocusedWidget(), wScroll))
240  {
241  return refPoint;
242  }
243 
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;
250 
251  return refPoint;
252  }
253 
254  //------------------------------------------------------------------------------------------------
255  // Returns metric between a point and a widget
256  protected static float GetFocusMetric(vector v0, Widget w)
257  {
258  // Find left midpoint of widget
259  SCR_Rect2D wRect = GetWidgetRect(w);
260  vector wRectPoint = wRect.p0;
261  wRectPoint[1] = 0.5 * (wRect.p0[1] + wRect.p1[1]);
262 
263  vector v1 = wRectPoint;
264 
265  float dx = v0[0] - v1[0];
266  float dy = v0[1] - v1[1];
267 
268  return 100 *Math.AbsFloat(dy) + Math.AbsFloat(dx);
269  }
270 
271  //------------------------------------------------------------------------------------------------
273  static SCR_Rect2D GetWidgetRect(Widget w)
274  {
275  vector pos;
276  vector size;
277 
278  float posX, posY;
279  float sizeX, sizeY;
280 
281  w.GetScreenPos(posX, posY);
282  w.GetScreenSize(sizeX, sizeY);
283 
284  pos[0] = posX;
285  pos[1] = posY;
286  size[0] = sizeX;
287  size[1] = sizeY;
288 
289  return SCR_Rect2D.FromPosAndSize(pos, size);
290  }
291 
292  //------------------------------------------------------------------------------------------------
294  static void FindAllButtons(Widget w, array<Widget> arrayOut)
295  {
296  // Add yourself to button list
297  ButtonWidget wb = ButtonWidget.Cast(w);
298  if (wb)
299  {
300  if (wb.IsFocusable())
301  arrayOut.Insert(wb);
302  }
303 
304  Widget child = w.GetChildren();
305  while (child)
306  {
307  FindAllButtons(child, arrayOut);
308  child = child.GetSibling();
309  }
310  }
311 
312  //------------------------------------------------------------------------------------------------
313  void SetTryFindNewFocus(bool tryFindNewFocus)
314  {
315  m_bTryFindNewFocus = tryFindNewFocus;
316  }
317 
318  //------------------------------------------------------------------------------------------------
319  void SetEnabled(bool enabled)
320  {
321  m_bShouldBeEnabled = enabled;
322  SetEnabled_Internal(enabled);
323  }
324 
325  //------------------------------------------------------------------------------------------------
326  void SetEnabled_Internal(bool enabled)
327  {
328  GetGame().GetCallqueue().Remove(OnEachFrame);
329 
330  m_bScrollEnabled = enabled && !m_bForceDisabled;
331  if (m_bScrollEnabled)
332  GetGame().GetCallqueue().CallLater(OnEachFrame, 1, true);
333  }
334 
335  //------------------------------------------------------------------------------------------------
336  protected void SetForceDisabled(bool forceDisabled)
337  {
338  m_bForceDisabled = forceDisabled;
339 
340  if (forceDisabled)
341  SetEnabled_Internal(false);
342  else
343  SetEnabled_Internal(m_bShouldBeEnabled);
344  }
345 
346 //------------------------------------------------------------------------------------------------
347 // Handling of debug markers
348 //------------------------------------------------------------------------------------------------
349 
350 #ifdef GAMEPAD_SCROLL_DEBUG
351 
352  //------------------------------------------------------------------------------------------------
353  protected void DebugMarks_StartUpdate()
354  {
355  // Return debug marks back to pool
356  foreach (Widget w : m_aDebugMarksBusy)
357  {
358  m_aDebugMarksPool.Insert(w);
359  }
360 
361  m_aDebugMarksBusy.Clear();
362  }
363 
364  //------------------------------------------------------------------------------------------------
365  // Puts a debug mark on this position. Must be called each frame!!
366  // This must be used only between DebugMarks_StartUpdate and DebugMarks_EndUpdate
367  protected void PlaceDebugMark(vector pos, string text)
368  {
369  WorkspaceWidget workspace = GetGame().GetWorkspace();
370 
371  Widget w;
372  if (!m_aDebugMarksPool.IsEmpty())
373  {
374  w = m_aDebugMarksPool[0];
375  m_aDebugMarksPool.Remove(0);
376  }
377  else
378  {
379  Widget workspaceRoot = GetGame().GetWorkspace();
380  w = workspace.CreateWidgets("{79FC19F28E381C1E}UI/layouts/Menus/DebugTextMark.layout", workspaceRoot);
381  }
382 
383  m_aDebugMarksBusy.Insert(w);
384 
385  TextWidget wtext = TextWidget.Cast(w.FindWidget("Text"));
386  wtext.SetText(text);
387  w.SetVisible(true);
388  FrameSlot.SetPos(w, workspace.DPIUnscale(pos[0]), workspace.DPIUnscale(pos[1]));
389  }
390 
391  //------------------------------------------------------------------------------------------------
392  protected void DebugMarks_EndUpdate()
393  {
394  // Hide all in the pool - by now they have either been used or they are not needed now
395  foreach (Widget w : m_aDebugMarksPool)
396  {
397  w.SetVisible(false);
398  }
399  }
400 
401 #endif
402 }
ChimeraMenuBase
Constant variables used in various menus.
Definition: ChimeraMenuBase.c:70
SCR_WidgetTools
Definition: SCR_WidgetTools.c:1
m_wRoot
protected Widget m_wRoot
Definition: SCR_ScenarioFrameworkLayerTaskDefend.c:59
GetGame
ArmaReforgerScripted GetGame()
Definition: game.c:1424
SCR_Rect2D
Definition: SCR_Rect2D.c:5
SCR_GamepadScrollComponent
Definition: SCR_GamepadScroll.c:10
Attribute
typedef Attribute
Post-process effect of scripted camera.
SCR_MenuHelper
Definition: SCR_MenuHelper.c:15
SCR_Global
Definition: Functions.c:6
SCR_ScriptedWidgetComponent
Definition: SCR_ScriptedWidgetComponent.c:7