Arma Reforger Explorer  1.1.0.42
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
SCR_PooledListComponent.c
Go to the documentation of this file.
1 /*
2 Pooled scrollable list that can endless simulate scrolling with only small amount of widgets.
3 The component assumes two pages to be filled with entries, were Page 0 is by default on top of Page 1
4 These pages are shuffled every time the scrolling is about to reach the top/bottom of a page, thus resulting in continuous scrolling.
5 Size widgets above and below the pages are used to fill space and simulate the presence of other entries.
6 Used with layout {611B71C627F5C61C}UI/layouts/WidgetLibraryExtended/ListView/WLib_PooledList.layout
7 */
8 
9 //------------------------------------------------------------------------------------------------
11 {
12  // Entries
13  [Attribute("", UIWidgets.ResourceNamePicker, desc: "Entry widget that list is filled with.")]
14  protected ResourceName m_sEntry;
15 
16  // Animation effects attributes
17  [Attribute("1", UIWidgets.EditBox, desc: "Time for apearing animation")]
18  protected float m_fAnimationAppearTime;
19 
20  [Attribute("20", UIWidgets.EditBox, desc: "How many entries should be created for single page")]
21  protected int m_iPageEntriesCount;
22 
23  [Attribute("15", UIWidgets.EditBox, desc: "On which entry from border page should be changed")]
24  protected int m_iPageChangeOffset;
25 
26  // Count of all entries in list
27  protected int m_iAllEntriesCount;
28 
29  // Constant sizing
30  const float SIZE_UNMEASURED = -1;
31  const int CHECK_ENTRY_SIZE_DELAY = 100;
32 
33  // Move offset when moving up in scroll layout
34  const float ENTRY_OFFSET_UP = 0.1;
35 
36  // Constant content z orders
37  const int ZORDER_OFFSET_TOP;
38  const int ZORDER_OFFSET_BOTTOM = 3;
39 
40  // Constant idget names
41  const string WIDGET_PAGES = "VPages";
42  const string WIDGET_PAGE0 = "VPage0";
43  const string WIDGET_PAGE1 = "VPage1";
44 
45  const string WIDGET_SCROLL_LAYOUT = "ScrollLayout";
46  const string WIDGET_SIZE_OFFSET_TOP = "SizeOffsetTop";
47  const string WIDGET_SIZE_OFFSET_BOTTOM = "SizeOffsetBottom";
48 
49  const string WIDGET_FOCUS_REST = "FocusRest";
50 
51  // Sizes
52  protected float m_fListPxHeight = SIZE_UNMEASURED;
53  protected float m_fPagePxHeight = SIZE_UNMEASURED;
54  protected float m_fViewPxHeight = SIZE_UNMEASURED;
55  protected float m_fEntryPxHeight = SIZE_UNMEASURED;
56 
57  // Content widgets
58  protected ScrollLayoutWidget m_wScroll;
59 
60  protected Widget m_wPagesWrap;
61  protected Widget m_wPage0;
62  protected Widget m_wPage1;
63 
64  protected Widget m_wPage0FirstEntry;
65  protected Widget m_wPage0LastEntry;
66  protected Widget m_wPage1FirstEntry;
67  protected Widget m_wPage1LastEntry;
68 
69  protected Widget m_wLastFocused;
70 
71  protected ref array<Widget> m_aEntryWidgets = {};
72 
73  protected SizeLayoutWidget m_wSizeOffsetTop;
74  protected SizeLayoutWidget m_wSizeOffsetBottom;
75 
76  // Pages switching
77  protected bool m_bPagesInverted;
78  protected int m_iCurrentPage;
79 
80  // Scroll vars
81  protected float m_ScrollLastY;
82 
83  // Focus
84  bool m_bIsListFocused;
85 
86  // Invokers
87  protected ref ScriptInvokerInt m_OnSetPage;
88 
89  //-------------------------------------
90  // ScriptedWidgetComponent override
91  //-------------------------------------
92 
93  //------------------------------------------------------------------------------------------------
94  override void HandlerAttached(Widget w)
95  {
96  super.HandlerAttached(w);
97 
98  // Accessing handlers and widgets
99  AccessingHandlers();
100 
101  CreateEntriesWidgets();
102 
103  SetupOffsets(0);
104 
105  /*
106  // Setup actions
107  GetGame().GetInputManager().AddActionListener("MenuUp", EActionTrigger.DOWN, OnActionUp);
108  GetGame().GetInputManager().AddActionListener("MenuDown", EActionTrigger.DOWN, OnActionDown);
109  */
110  }
111 
112  //-------------------------------------
113  // Input actions
114  //-------------------------------------
115  //------------------------------------------------------------------------------------------------
117  protected void OnActionUp()
118  {
119  if (!m_bIsListFocused)
120  return;
121 
122  WorkspaceWidget workspace = GetGame().GetWorkspace();
123 
124  if (m_wLastFocused)
125  workspace.SetFocusedWidget(m_wLastFocused);
126 
127  if (m_fPagePxHeight == 0)
128  return;
129 
130  float invisibleEntries = EntriesOutOfView();
131  if (invisibleEntries == 0)
132  return;
133 
134  // Check current page
135  float scrollPageRatio = (m_iPageEntriesCount + ENTRY_OFFSET_UP) / invisibleEntries;
136  int currentPage = Math.Floor(m_ScrollLastY / scrollPageRatio);
137 
138  if (m_iCurrentPage == currentPage)
139  return;
140 
141  // Is focus on first entry of one of page
142  Widget focus = workspace.GetFocusedWidget();
143 
144  if (focus != m_wPage0FirstEntry && focus != m_wPage1FirstEntry)
145  return;
146 
147  // Switch pages
148  SetCurrentPage(m_iCurrentPage - 1);
149 
150  if (focus == m_wPage0FirstEntry)
151  workspace.SetFocusedWidget(m_wPage1LastEntry);
152  else if (focus == m_wPage1FirstEntry)
153  workspace.SetFocusedWidget(m_wPage0LastEntry);
154  }
155 
156  //------------------------------------------------------------------------------------------------
158  protected void OnActionDown()
159  {
160  if (!m_bIsListFocused)
161  return;
162 
163  WorkspaceWidget workspace = GetGame().GetWorkspace();
164 
165  if (m_wLastFocused)
166  workspace.SetFocusedWidget(m_wLastFocused);
167  }
168 
169  //------------------------------------------------------------------------------------------------
171  protected void OnScroll(float scrollY)
172  {
173  if (m_fPagePxHeight == 0)
174  return;
175 
176  // Check if page is changed
177  int currentPage = CurrentPageFromScrollPos(scrollY);
178 
179  if (m_iCurrentPage != currentPage)
180  SetCurrentPage(currentPage);
181  }
182 
183  //------------------------------------------------------------------------------------------------
185  protected int CurrentPageFromScrollPos(float scrollY)
186  {
187  // Zero checks
188  if (m_iPageEntriesCount == 0)
189  return 0;
190 
191  int entriesOutOfView = EntriesOutOfView();
192  if (entriesOutOfView == 0)
193  return 0;
194 
195  // Ratio between entries in single page and entries out of view
196  float scrollPageRatio = m_iPageEntriesCount / entriesOutOfView;
197 
198  // Check if page is changed
199  float pos = scrollY / scrollPageRatio;
200 
201  // Moving up
202  pos = pos - (m_iPageChangeOffset / m_iPageEntriesCount);
203 
204  //TODO: find out why moving down gets stuck
205 
206  int currentPage = Math.Floor(pos);
207  if (currentPage < 0)
208  return 0;
209 
210  return currentPage;
211  }
212 
213  //------------------------------------------------------------------------------------------------
215  protected float EntriesOutOfView()
216  {
217  if (m_fPagePxHeight == 0)
218  return 0;
219 
220  // What percent of whole list can bee seen
221  float pageViewRatio = m_fViewPxHeight / m_fPagePxHeight;
222  float outOfView = m_iAllEntriesCount - m_iPageEntriesCount * pageViewRatio;
223 
224  if (outOfView < 0)
225  return 0;
226 
227  return outOfView;
228  }
229 
230  //-------------------------------------
231  // public functions
232  //-------------------------------------
233 
234  //------------------------------------------------------------------------------------------------
236  void UpdateEntries(bool animated = false)
237  {
238  // Fill list with data
239  foreach (Widget w : m_aEntryWidgets)
240  {
241  if (w)
242  FillEntry(w);
243  }
244 
245  SetupOffsets(m_iCurrentPage);
246  }
247 
248  //------------------------------------------------------------------------------------------------
250  void UpdateScroll()
251  {
252  WorkspaceWidget workspace = GetGame().GetWorkspace();
253  // Get scroll positions
254  float scrollX, scrollY = 0;
255  m_wScroll.GetSliderPos(scrollX, scrollY);
256 
257  // Check if current focus is in list
258  Widget focused = FocusedWidgetFromEntryList();
259 
260  if (focused)
261  m_wLastFocused = focused;
262 
263  // Is list foucused - is current focus on list fallback widget or list entry?
264  Widget workspaceFocused = workspace.GetFocusedWidget();
265  m_bIsListFocused = (workspaceFocused == m_wLastFocused);
266  // Check scroll change
267  if (m_ScrollLastY == scrollY)
268  return;
269 
270  OnScroll(scrollY);
271  m_ScrollLastY = scrollY;
272 
273  // Check sizes
274  if (m_fEntryPxHeight == SIZE_UNMEASURED || m_fEntryPxHeight == 0 && m_iAllEntriesCount != 0)
275  {
276  CheckEntrySize();
277  MoveToTop();
278  }
279  }
280 
281  //------------------------------------------------------------------------------------------------
283  protected void SetCurrentPage(int page)
284  {
285  SetCurrentPageBase(page);
286  UpdateEntries();
287  }
288 
289  //------------------------------------------------------------------------------------------------
291  protected void SetCurrentPageBase(int page)
292  {
293  m_iCurrentPage = page;
294 
295  // Check if pages are inverted to simulate scrolling
296  bool pagesInverted = (m_iCurrentPage % 2 != 0);
297 
298  // Don't switch on last page
299  int lastPage = Math.Floor(m_iAllEntriesCount / m_iPageEntriesCount);
300 
301  if (m_iCurrentPage >= lastPage - 1)
302  pagesInverted = false;
303 
304  SwitchPages(pagesInverted, m_fPagePxHeight, m_iCurrentPage);
305  m_OnSetPage.Invoke(page);
306  }
307 
308  //------------------------------------------------------------------------------------------------
310  protected void SwitchPages(bool invert, float size, int page)
311  {
312  // Check entries count
313  if (m_iAllEntriesCount == 0)
314  return;
315 
316  // Check vertical list widgets
317  if (!m_wPage0 || !m_wPage1)
318  return;
319 
320  // Save invert
321  m_bPagesInverted = invert;
322 
323  // Check end of scrolling
324  int maxPages = Math.Floor(m_iAllEntriesCount / m_iPageEntriesCount);
325  if (page > maxPages - 1)
326  return;
327 
328  SetupOffsets(page);
329 
330  // Inverted order
331  m_wPage0.SetZOrder(invert);
332  m_wPage1.SetZOrder(!invert);
333  }
334 
335  //------------------------------------------------------------------------------------------------
337  protected void SetupOffsets(int page)
338  {
339  int overflowEntryCount = m_iAllEntriesCount - m_iPageEntriesCount*2;
340  float offset = m_fEntryPxHeight * overflowEntryCount - m_fPagePxHeight * page;
341 
342  // testing calculations
343  float pageSizeBaseOnEntry = m_fEntryPxHeight * m_iPageEntriesCount;
344 
345  m_wSizeOffsetTop.SetHeightOverride(m_fPagePxHeight * page);
346  m_wSizeOffsetBottom.SetHeightOverride(offset);
347 
348  m_wSizeOffsetTop.SetZOrder(ZORDER_OFFSET_TOP);
349  m_wSizeOffsetBottom.SetZOrder(ZORDER_OFFSET_BOTTOM);
350  }
351 
352  //------------------------------------------------------------------------------------------------
354  void FocusFirstAvailableEntry()
355  {
356  // Get and check widget
357  Widget availableEntry = FirstAvailableEntry();
358  if (!availableEntry)
359  return;
360 
361  // Focus
362  GetGame().GetWorkspace().SetFocusedWidget(null);
363  GetGame().GetWorkspace().SetFocusedWidget(availableEntry);
364  }
365 
366  //------------------------------------------------------------------------------------------------
368  void MoveToTop()
369  {
370  SetCurrentPage(0);
371  OnScroll(0);
372 
373  m_bPagesInverted = false;
374  SwitchPages(m_bPagesInverted, 0, 0);
375 
376  m_ScrollLastY = 0;
377  m_wScroll.SetSliderPos(0, 0);
378  }
379 
380  //------------------------------------------------------------------------------------------------
382  void ShowScrollbar(bool show)
383  {
384  if (show)
385  {
386  m_wRoot.ClearFlags(WidgetFlags.CLIPCHILDREN);
387  m_wRoot.SetFlags(WidgetFlags.INHERIT_CLIPPING);
388  }
389  else
390  {
391  m_wRoot.SetFlags(WidgetFlags.CLIPCHILDREN);
392  m_wRoot.ClearFlags(WidgetFlags.INHERIT_CLIPPING);
393  }
394  }
395 
396  //-------------------------------------
397  // protected functions
398  //-------------------------------------
399 
400  //------------------------------------------------------------------------------------------------
402  protected void AccessingHandlers()
403  {
404  // Base scroll layout
405  m_wScroll = ScrollLayoutWidget.Cast(m_wRoot.FindAnyWidget(WIDGET_SCROLL_LAYOUT));
406 
407  // Find pages
408  m_wPagesWrap = m_wRoot.FindAnyWidget(WIDGET_PAGES);
409  m_wPage0 = m_wRoot.FindAnyWidget(WIDGET_PAGE0);
410  m_wPage1 = m_wRoot.FindAnyWidget(WIDGET_PAGE1);
411 
412  // Find size offsets
413  m_wSizeOffsetTop = SizeLayoutWidget.Cast(m_wRoot.FindAnyWidget(WIDGET_SIZE_OFFSET_TOP));
414  m_wSizeOffsetBottom = SizeLayoutWidget.Cast(m_wRoot.FindAnyWidget(WIDGET_SIZE_OFFSET_BOTTOM));
415  }
416 
417  //------------------------------------------------------------------------------------------------
419  protected void CreateEntriesWidgets()
420  {
421  if(!m_wRoot || m_sEntry.IsEmpty())
422  return;
423 
424  CreateEntriesWidgetsInPage(m_wPage0, m_iPageEntriesCount, m_wPage0FirstEntry, m_wPage0LastEntry);
425  CreateEntriesWidgetsInPage(m_wPage1, m_iPageEntriesCount, m_wPage1FirstEntry, m_wPage1LastEntry);
426  }
427 
428  //------------------------------------------------------------------------------------------------
430  protected void CreateEntriesWidgetsInPage(Widget parent, int count, out Widget firstEntry, out Widget lastEntry)
431  {
432  for (int i = 0; i < count; i++)
433  {
434  Widget entry = CreateEntry(parent);
435 
436  // Assing first entry of page
437  if (i == 0)
438  firstEntry = entry;
439 
440  // Assing last entry of page
441  if (i == count - 1)
442  lastEntry = entry;
443  }
444  }
445 
446  //------------------------------------------------------------------------------------------------
448  protected Widget CreateEntry(Widget parent)
449  {
450  WorkspaceWidget workspace = GetGame().GetWorkspace();
451 
452  if (!m_sEntry || !parent || !workspace)
453  return null;
454 
455  Widget widget = workspace.CreateWidgets(m_sEntry, parent);
456  if (!widget)
457  return null;
458 
459  // Setup widget entry behavior
460  SetupEntryBehavior(widget);
461 
462  // Save in widget list
463  m_aEntryWidgets.Insert(widget);
464 
465  return widget;
466  }
467 
468  protected bool m_bIsMeasured = false;
469 
470  //------------------------------------------------------------------------------------------------
472  protected void CheckEntrySize()
473  {
474  if (m_aEntryWidgets.IsEmpty())
475  return;
476 
477  // Check widgets
478  if (!m_bIsMeasured)
479  {
480  ShowScrollbar(false);
481 
482  // Entry height in px - with bottom padding
483  float x;
484  m_aEntryWidgets[0].GetScreenSize(x, m_fEntryPxHeight);
485 
486  // Page height in px
487  m_wPage0.GetScreenSize(x, m_fPagePxHeight);
488 
489  // Height of view int px - how much can user see in px
490  m_wRoot.GetScreenSize(x, m_fViewPxHeight);
491 
492  // Check measures
493  if (m_fEntryPxHeight != 0)
494  {
495  m_bIsMeasured = true;
496  ShowScrollbar(true);
497 
498  SetupOffsets(m_iCurrentPage);
499  }
500  }
501 
502  // Whole height size in px - cut last bottom padding
503  m_fListPxHeight = m_fEntryPxHeight * m_iAllEntriesCount;
504  }
505 
506  //------------------------------------------------------------------------------------------------
511  protected void SetupEntryBehavior(Widget entry) { }
512 
513  //------------------------------------------------------------------------------------------------
518  protected void FillEntry(Widget w) {}
519 
520  //------------------------------------------------------------------------------------------------
522  protected void AnimateEntryOpacity(Widget w, int delay, float animTime, float opacityEnd, float opacityStart = -1)
523  {
524  if (opacityStart != -1)
525  w.SetOpacity(opacityStart);
526 
527  GetGame().GetCallqueue().CallLater(OpacityAnimation, delay, false, w, animTime, opacityEnd);
528  }
529 
530  //------------------------------------------------------------------------------------------------
531  protected void OpacityAnimation(Widget w, int time, float opacityEnd)
532  {
533  AnimateWidget.Opacity(w, opacityEnd, time);
534  }
535 
536  //------------------------------------------------------------------------------------------------
538  protected void ShowEntries(int dataCount)
539  {
540  // Show entries
541  for (int i = 0; i < m_aEntryWidgets.Count(); i++)
542  {
543  bool show = i < dataCount;
544  m_aEntryWidgets[i].SetVisible(show);
545  }
546 
547  // Check if offsets needed
548  int overflow = dataCount - m_iPageEntriesCount*2;
549  bool addOffsets = overflow > 0;
550 
551  // Set offstes visible if needed
552  m_wSizeOffsetTop.SetVisible(addOffsets);
553  m_wSizeOffsetBottom.SetVisible(addOffsets);
554 
555  if (!addOffsets)
556  return;
557  }
558 
559  //------------------------------------------------------------------------------------------------
561  protected Widget FocusedWidgetFromEntryList()
562  {
563  Widget focused = GetGame().GetWorkspace().GetFocusedWidget();
564 
565  int focusedEntryId = m_aEntryWidgets.Find(focused);
566 
567  if (focusedEntryId == -1)
568  return null;
569 
570  return focused;
571  }
572 
573  //-------------------------------------
574  // Get & Set
575  //-------------------------------------
576  //------------------------------------------------------------------------------------------------
577  int GetPageEntriesCount()
578  {
579  return m_iPageEntriesCount;
580  }
581 
582  //------------------------------------------------------------------------------------------------
583  array<Widget> GetEntryWidgets()
584  {
585  return m_aEntryWidgets;
586  }
587 
588  //------------------------------------------------------------------------------------------------
589  // Setup entries defaults and total count
590  void SetDataEntries(int entriesCount)
591  {
592  // Set data
593  m_iAllEntriesCount = entriesCount;
594 
595  // Show entries
596  ShowEntries(entriesCount);
597 
598  // Check sizes
599  GetGame().GetCallqueue().CallLater(CheckEntrySize, CHECK_ENTRY_SIZE_DELAY);
600  }
601 
602  //------------------------------------------------------------------------------------------------
604  Widget FirstAvailableEntry()
605  {
606  foreach (Widget entry : m_aEntryWidgets)
607  {
608  if (entry.IsVisible() && entry.IsEnabled())
609  return entry;
610  }
611 
612  return null;
613  }
614 
615  //------------------------------------------------------------------------------------------------
616  void SetIsListFocused(bool focused)
617  {
618  m_bIsListFocused = focused;
619  }
620 
621  //------------------------------------------------------------------------------------------------
622  bool IsListFocused()
623  {
624  return m_bIsListFocused;
625  }
626 
627  //------------------------------------------------------------------------------------------------
628  Widget GetScrollWidget()
629  {
630  return m_wScroll;
631  }
632 
633  //------------------------------------------------------------------------------------------------
634  ScriptInvokerInt GetOnSetPage()
635  {
636  if (!m_OnSetPage)
637  m_OnSetPage = new ScriptInvokerInt();
638 
639  return m_OnSetPage;
640  }
641 }
m_iCurrentPage
protected int m_iCurrentPage
Definition: SCR_ContentBrowser_AddonsSubMenu.c:75
m_wRoot
protected Widget m_wRoot
Definition: SCR_ScenarioFrameworkLayerTaskDefend.c:59
ScriptInvokerInt
ScriptInvokerBase< ScriptInvokerIntMethod > ScriptInvokerInt
Definition: SCR_ScriptInvokerHelper.c:24
GetGame
ArmaReforgerScripted GetGame()
Definition: game.c:1424
desc
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
Definition: SCR_RespawnBriefingComponent.c:17
Attribute
typedef Attribute
Post-process effect of scripted camera.
SCR_PooledListComponent
Definition: SCR_PooledListComponent.c:10
SCR_ScriptedWidgetComponent
Definition: SCR_ScriptedWidgetComponent.c:7