Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_ContentBrowser_AddonsSubMenu.c
Go to the documentation of this file.
1/*
2Submenu for local or online addons.
3
4!! If you want filters to work, provide filters through the layout prefab or by some other way - into m_FilterCategories variable.
5*/
6
8{
9 MODE_OFFLINE, // Gets items from Workshop offline API
10 MODE_ONLINE, // Gets items from Workshop online API
11 MODE_EXTERNAL_ITEM_ARRAY // Operates on fixed array of items supplied externally
12}
13
14class SCR_ContentBrowser_AddonsSubMenu : SCR_SubMenuBase
15{
16 // This is quite universal and can work in many modes...
17 [Attribute("0", UIWidgets.ComboBox, "This menu is quite universal and can work in many modes - what do you want it to be?", "", ParamEnumArray.FromEnum(EContentBrowserAddonsSubMenuMode))]
19
20 [Attribute("")]
21 protected string m_sOnlinePageType;
22
23 [Attribute("SOUND_FE_TURN_PAGE")]
25
26 // --- Constants ---
27 protected const int GRID_N_COLUMNS = 4;
28 protected const int GRID_N_ROWS = 3;
29 protected const int LOAD_PAGE_DELAY = 500;
30
31 protected const ResourceName LAYOUT_GRID_TILE = "{67B0EFBDF7EAFF27}UI/layouts/Menus/ContentBrowser/Tile/WorkshopTile.layout";
32
33 protected const string TEXT_PAGE_INDICATOR = "#AR-Editor_ContentBrowser_PageIndex_Text";
34
35 // Message tags
36 protected const string MESSAGE_TAG_NOTHING_FOUND = "nothing_found";
37 protected const string MESSAGE_TAG_NOTHING_DOWNLOADED = "nothing_downloaded";
38 protected const string MESSAGE_TAG_NOTHING_DOWNLOADED_2 = "nothing_downloaded2";
39 protected const string MESSAGE_TAG_ERROR_CONNECTION = "error_connection";
40 protected const string MESSAGE_TAG_LOADING = "loading";
41 protected const string MESSAGE_TAG_NO_SAVES_FOUND = "no_saves_found";
42
43 // --- Protected / Private ---
45
47
49
51
52 protected ref array<SCR_ContentBrowserTileComponent> m_aTileComponents = {};
53
54 protected bool m_bConnected; // True when we are connected to backend, used to determine when to start sending requests
55 protected bool m_bFailToLoad;
56
57 protected bool m_bClearCacheAtNextRequest; // Will force workshop to clear page cache. Should be set wherever sorting or filtering changes.
58
59 protected ref array<ref SCR_WorkshopItem> m_aExternalItems = {}; // Array with externally provided items - used only in MODE_EXTERNAL_ITEM_ARRAY
60
61 protected SCR_ContentBrowserTileComponent m_LastFocusedTile; // Mainly used to restore focus back to something
62
65
66 protected bool m_bFirstPage = true; // True before first page is received.
67
68 protected bool m_bPanelsModeEmpty; // True when we are showing empty panels
69
70 protected bool m_bMousePageUsed;
71
72 protected bool m_bFirstLoad = true;
73
74 // State of paging
75 protected int m_iCurrentPage;
76 protected int m_iPageCount;
77
78 // Navigation buttons
85
86 // Focus
87 protected SCR_EListMenuWidgetFocus m_eFocusedWidgetState;
88
89 // Items found message
90 protected int m_iItemsTotal;
93 protected ref BackendCallback m_PageCallback = null;
94
95 // Mouse wheel input
96 protected bool m_bCanPlayMouseWheelSound = true;
97 protected const int MOUSE_WHEEL_SOUND_COOLDOWN = 75;
98
99 // --- Overrides ---
100 //------------------------------------------------------------------------------------------------
101 override void OnTabCreate(Widget menuRoot, ResourceName buttonsLayout, int index)
102 {
103 super.OnTabCreate(menuRoot, buttonsLayout, index);
104
105 InitWidgets();
106
107 // Init main parts of the layout
109
110 // Try to load filters
111 m_Widgets.m_FilterPanelComponent.TryLoad();
112
113 m_WorkshopApi = GetGame().GetBackendApi().GetWorkshop();
115
116 // Init event handlers
118
119 switch (m_eMode)
120 {
121 case EContentBrowserAddonsSubMenuMode.MODE_ONLINE:
122 {
124 if (m_bConnected)
125 OnConnected();
126 else
127 SetPanelsMode(true, false, MESSAGE_TAG_ERROR_CONNECTION, false);
128
129 break;
130 }
131
132 case EContentBrowserAddonsSubMenuMode.MODE_OFFLINE:
133 {
134 // Offline mode - request page instantly
135 RequestPage(0);
136 break;
137 }
138
139 // For EContentBrowserAddonsSubMenuMode.MODE_EXTERNAL_ITEM_ARRAY we need to wait for the items
140 }
141
142 // Subscribe to addon manager events
144
145 // Subscribe to page refresh request events
147
148 // Listen for input device change to update navigation buttons
149 GetGame().OnInputDeviceUserChangedInvoker().Insert(OnInputDeviceUserChanged);
150 }
151
152 //------------------------------------------------------------------------------------------------
153 override void OnTabShow()
154 {
155 // Check and show backend down
156 super.OnTabShow();
157
158 // Init workshop API
159 // We do it on tab show becasue this tab and others persists when all other tabs are closed,
160 // But we can switch back to it later, and we must setup the workshop api again
162
164 m_MenuActionsComponent.ActivateActions();
165
166 if (m_bFailToLoad || m_eMode == EContentBrowserAddonsSubMenuMode.MODE_EXTERNAL_ITEM_ARRAY)
168
169 if (m_bShown)
170 {
172 GetGame().GetCallqueue().Call(SwitchFocus, SCR_EListMenuWidgetFocus.NULL);
173 else
174 GetGame().GetCallqueue().Call(SwitchFocus, m_eFocusedWidgetState);
175 }
176
177 //SCR_AnalyticsApplication.GetInstance().WorkshopSetTab(m_iIndex);
178 }
179
180 //------------------------------------------------------------------------------------------------
181 override void OnTabHide()
182 {
183 super.OnTabHide();
184
185 // Save configuration of filters
186 m_Widgets.m_FilterPanelComponent.Save();
187
189 m_MenuActionsComponent.DeactivateActions();
190
191 // clear callbacks to kill pending requests
192 m_PageCallback = null;
194
195 // put browser into state so that it again properly loads when shown again
196 m_bFailToLoad = true;
197 m_bFirstLoad = true;
198 m_bFirstPage = true;
199 }
200
201 //------------------------------------------------------------------------------------------------
202 override void OnTabRemove()
203 {
204 super.OnTabRemove();
205
206 // Unsubscribe from addon manager events
208
209 // Unsubscribe from page refresh request events
211
213 m_LastFocusedTile.GetOnChange().Remove(OnTileStateChange);
214
215 // clear callbacks to kill pending requests
216 m_PageCallback = null;
218 }
219
220 //------------------------------------------------------------------------------------------------
221 override void OnMenuShow()
222 {
223 super.OnMenuShow();
224
226 }
227
228 //------------------------------------------------------------------------------------------------
230 override void OnMenuUpdate(float tDelta)
231 {
232 // Update connection state, check if we have reconnected
233 bool connectedOld = m_bConnected;
234 bool connectedNew = SCR_ServicesStatusHelper.IsAuthenticated();
235 m_bConnected = connectedNew;
236 if (connectedNew != connectedOld)
237 {
238 // Only call OnConnected if it's an online tab
239 if (connectedNew && m_eMode == EContentBrowserAddonsSubMenuMode.MODE_ONLINE)
240 OnConnected();
241 }
242
243 super.OnMenuUpdate(tDelta);
244 }
245
246 //------------------------------------------------------------------------------------------------
247 override void OnMenuFocusGained()
248 {
249 if (m_bShown)
251
253 m_MenuActionsComponent.ActivateActions();
254
255 super.OnMenuFocusGained();
256 }
257
258 // --- Protected ---
259 //------------------------------------------------------------------------------------------------
260 protected void InitWidgets()
261 {
262 m_Widgets.Init(GetRootWidget());
263
264 // Create nav buttons
265 if (m_eMode != EContentBrowserAddonsSubMenuMode.MODE_EXTERNAL_ITEM_ARRAY)
266 {
267 m_NavFilter = CreateNavigationButton("MenuFilter", "#AR-FieldManual_Page_EditorAssetBrowser_Text_3", false);
268 m_NavDetails = CreateNavigationButton(UIConstants.MENU_ACTION_SELECT, "#AR-Workshop_Details_MenuTitle", true);
269 }
270
271 m_NavRepair = CreateNavigationButton(UIConstants.MENU_ACTION_SELECT_HOLD, "#AR-FactionCommander_TaskRepair", true);
272 m_NavEnable = CreateNavigationButton(UIConstants.MENU_ACTION_ENABLE, SCR_WorkshopUiCommon.LABEL_ENABLE, true);
273 m_NavPrimary = CreateNavigationButton(UIConstants.MENU_ACTION_SELECT_HOLD, SCR_WorkshopUiCommon.LABEL_DOWNLOAD, true);
274 m_NavFavourite = CreateNavigationButton(UIConstants.MENU_ACTION_FAVORITE, UIConstants.FAVORITE_LABEL_ADD, true);
275 }
276
277 //------------------------------------------------------------------------------------------------
278 protected void InitWidgetEventHandlers()
279 {
280 m_Widgets.m_PrevPageButton_NoScrollComponent.m_OnActivated.Insert(OnPrevPageButton);
281 m_Widgets.m_NextPageButton_NoScrollComponent.m_OnActivated.Insert(OnNextPageButton);
282
283 m_Widgets.m_FilterPanelComponent.GetWidgets().m_FilterSearchComponent.m_OnConfirm.Insert(OnFilterSearchConfirm);
284 m_Widgets.m_FilterPanelComponent.GetOnFilterPanelToggled().Insert(OnFilterPanelToggled);
285 m_Widgets.m_SortingHeaderComponent.m_OnChanged.Insert(OnSortingHeaderChange);
286 m_Widgets.m_FilterPanelComponent.GetOnFilterChanged().Insert(OnFilterChange);
287
290 m_MenuActionsComponent.GetOnAction().Insert(OnInputAction);
291
292 // Event handlers of nav buttons
293 if (m_NavFilter)
294 m_NavFilter.m_OnActivated.Insert(OnFilterButton);
295
296 if (m_NavDetails)
297 m_NavDetails.m_OnClicked.Insert(OnDetailsButton);
298
299 m_NavEnable.m_OnActivated.Insert(OnEnableButton);
300 m_NavPrimary.m_OnActivated.Insert(OnPrimaryButton);
301 m_NavFavourite.m_OnActivated.Insert(OnFavouriteButton);
302 m_NavRepair.m_OnActivated.Insert(OnRepairButton);
303 }
304
305 //------------------------------------------------------------------------------------------------
306 // Inits workshop API according to current mode
307 protected void InitWorkshopApi()
308 {
309 WorkshopItem.SetThumbnailGridScale(GetGame().GetWorkspace().DPIScale(SCR_ContentBrowserTileComponent.GetTileWidth()));
310
311 switch (m_eMode)
312 {
313 case EContentBrowserAddonsSubMenuMode.MODE_ONLINE:
315 break;
316
317 case EContentBrowserAddonsSubMenuMode.MODE_OFFLINE:
318 m_WorkshopApi.ScanOfflineItems();
319 break;
320 }
321
322 // Register tags for filters
323 SCR_FilterSet filterSet = m_Widgets.m_FilterPanelComponent.m_FilterSet;
324 if (!filterSet)
325 return;
326
328 foreach (SCR_FilterCategory category : filterSet.GetFilterCategories())
329 {
330 foreach (SCR_FilterEntry filterEntry : category.GetFilters())
331 {
332 filterTag = SCR_ContentBrowserFilterTag.Cast(filterEntry);
333 if (filterTag)
334 filterTag.RegisterInWorkshopApi(m_WorkshopApi);
335 }
336 }
337 }
338
339 //------------------------------------------------------------------------------------------------
340 // Requests page from backend or displays items according to current mode
341 protected void RequestPage(int pageId)
342 {
343 // When we request page, we also must clean the grid and side panel
345 m_Widgets.m_AddonDetailsPanelComponent.SetWorkshopItem(null);
346 SetPanelsMode(showEmptyPanel: m_bFirstPage, messagePresetTag: MESSAGE_TAG_LOADING);
347
348 m_iCurrentPage = pageId;
349 m_bFailToLoad = false;
350
351 switch (m_eMode)
352 {
353 case EContentBrowserAddonsSubMenuMode.MODE_EXTERNAL_ITEM_ARRAY:
354 DisplayExternalItems(pageId);
355 break;
356
357 case EContentBrowserAddonsSubMenuMode.MODE_ONLINE:
358 if (m_bFirstLoad)
360 else
361 RequestOnlinePage(pageId);
362 break;
363
364 case EContentBrowserAddonsSubMenuMode.MODE_OFFLINE:
365 DisplayOfflineItems(pageId);
366 break;
367 }
368 }
369
370 // --- Backend page requests ---
371 //------------------------------------------------------------------------------------------------
373 protected void RequestOnlinePageUnfiltered(int pageId)
374 {
375 // Hotfix: 0 is not allowed page size and there is no other way to get unfiltered total count
376 // Hotfix #2: 1 is now also not allowed page size
377 m_WorkshopApi.SetPageSize(2);
378
381
384
387
389 }
390
391 //------------------------------------------------------------------------------------------------
393 protected void RequestOnlinePage(int pageId)
394 {
396
397 if (!m_PageCallback)
398 {
402 }
403
406
410
411 //m_GetAssetListParams.PackToFile("$profile:GetAssetList.json");
412 #ifdef WORKSHOP_DEBUG
413 ContentBrowserUI._print(string.Format("WorkshopApi.RequestPage() limit: %1, offset: %2", m_GetAssetListParams.limit, m_GetAssetListParams.offset));
414 #endif
415
417
419 }
420
421 //------------------------------------------------------------------------------------------------
423 protected void DisplayExternalItems(int pageId)
424 {
426
427 array<ref SCR_WorkshopItem> itemsAtPage = SelectItemsAtPage(m_aExternalItems, pageId);
428
429 // Filtering is disabled in this mode
430 // Update page count
431 float nItemsPerPage = GRID_N_ROWS * GRID_N_COLUMNS;
432 m_iPageCount = Math.Ceil(m_aExternalItems.Count() / nItemsPerPage);
433
434 SetPanelsMode(false);
435
436 foreach(SCR_WorkshopItem item: itemsAtPage)
437 item.LoadDetails();
438
439 GridScreen_DisplayItems(itemsAtPage);
440
441 m_bFirstPage = false;
442
444 }
445
446 //------------------------------------------------------------------------------------------------
447 protected void DisplayOfflineItems(int pageId)
448 {
450
451 // Get offline items from API
452 array<WorkshopItem> rawWorkshopItems = {};
453 m_WorkshopApi.GetOfflineItems(rawWorkshopItems);
454
455 // Bail if there is no downloaded content
456 if (rawWorkshopItems.IsEmpty())
457 {
458 SetPanelsMode(showEmptyPanel: true, messagePresetTag: MESSAGE_TAG_NOTHING_DOWNLOADED);
460 return;
461 }
462
463 // Register items in Addon Manager
464 array<ref SCR_WorkshopItem> itemsUnfiltered = {};
465 SCR_WorkshopItem itemRegistered;
466 foreach (WorkshopItem i : rawWorkshopItems)
467 {
468 // Skip local items
469 if (WorldSaveItem.Cast(i) && !i.GetActiveRevision())
470 continue;
471
472 itemRegistered = SCR_AddonManager.GetInstance().Register(i);
473 itemsUnfiltered.Insert(itemRegistered);
474 }
475
476 // Filter items
477 array<ref SCR_WorkshopItem> itemsFiltered = Filter_OfflineFilterAndSearch(itemsUnfiltered);
478
479 // Bail if no items after filtering
480 if (itemsFiltered.IsEmpty())
481 {
482 SetPanelsMode(showEmptyPanel: false, forceFiltersList: true, messagePresetTag: MESSAGE_TAG_NOTHING_DOWNLOADED_2);
483 UpdateItemsFoundMessage(0, rawWorkshopItems.Count());
484 return;
485 }
486
487 // Sort items
488 array<SCR_WorkshopItem> itemsSortedWeakPtrs = {};
489 foreach (SCR_WorkshopItem i : itemsFiltered)
490 {
491 itemsSortedWeakPtrs.Insert(i);
492 }
493
494 bool sortOrder = !m_Widgets.m_SortingHeaderComponent.GetSortOrderAscending();
495 string currentSortingItem = m_Widgets.m_SortingHeaderComponent.GetSortElementName();
496 if (currentSortingItem.IsEmpty())
497 {
498 currentSortingItem = "name";
499 sortOrder = false;
500 }
501
502 switch (currentSortingItem)
503 {
504 case "name":
505 SCR_Sorting<SCR_WorkshopItem, SCR_CompareWorkshopItemName>.HeapSort(itemsSortedWeakPtrs, sortOrder);
506 break;
507 case "time_since_played":
508 SCR_Sorting<SCR_WorkshopItem, SCR_CompareWorkshopItemTimeSinceLastPlay>.HeapSort(itemsSortedWeakPtrs, sortOrder);
509 break;
510 case "time_since_downloaded":
511 SCR_Sorting<SCR_WorkshopItem, SCR_CompareWorkshopItemTimeSinceFirstDownload>.HeapSort(itemsSortedWeakPtrs, sortOrder);
512 break;
513 }
514
515 array<ref SCR_WorkshopItem> itemsSorted = {};
516 foreach (SCR_WorkshopItem i : itemsSortedWeakPtrs)
517 {
518 itemsSorted.Insert(i);
519 }
520
521 // Update page count
522 m_iPageCount = Math.Ceil(itemsSorted.Count() / (GRID_N_ROWS * GRID_N_COLUMNS));
523
524 // Display items
525 SetPanelsMode(false, m_Widgets.m_FilterPanelComponent.AnyFilterButtonsVisible(), string.Empty, m_bFirstPage);
526 array<ref SCR_WorkshopItem> itemsAtPage = SelectItemsAtPage(itemsSorted, pageId);
527 GridScreen_DisplayItems(itemsAtPage);
528
529 m_bFirstPage = false;
530
531 UpdateItemsFoundMessage(itemsSorted.Count(), rawWorkshopItems.Count());
532 }
533
534 // --- Widgets ---
535 //------------------------------------------------------------------------------------------------
536 // When pages are scrolled, tiles are not deleted, we show different data in the tiles instead to avoid unneeded widget allocations.
537 // Here we create tile widgets in advance.
538 protected void GridScreen_Init()
539 {
540 for (int i = 0; i < GRID_N_COLUMNS * GRID_N_ROWS; i++)
541 {
542 int colId = i % GRID_N_COLUMNS;
543 int rowId = i / GRID_N_COLUMNS;
544
545 // Create tile widgets
546 Widget w = GetGame().GetWorkspace().CreateWidgets(LAYOUT_GRID_TILE, m_Widgets.m_Grid);
547
548 // Set grid positioning
549 GridSlot.SetRow(w, rowId);
550 GridSlot.SetColumn(w, colId);
551
552 // Init the tile component
554 comp.SetWorkshopItem(null); // Widget will be hidden because of this
555
556 // Tile event handlers
557 comp.m_OnClick.Insert(OnTileClick);
558 comp.GetOnFocus().Insert(OnTileFocus);
559 comp.GetOnFocusLost().Insert(OnTileFocusLost);
560
561 m_aTileComponents.Insert(comp);
562 }
563 }
564
565 //------------------------------------------------------------------------------------------------
567 protected void GridScreen_Clear()
568 {
570 {
571 comp.SetWorkshopItem(null);
572 }
573 }
574
575 //------------------------------------------------------------------------------------------------
576 protected void GridScreen_DisplayItems(array<ref SCR_WorkshopItem> items)
577 {
578 // Clear old items
580
581 int nItems = Math.ClampInt(items.Count(), 0, GRID_N_COLUMNS * GRID_N_ROWS);
582
583 for (int i = 0; i < nItems; i++)
584 {
586 comp.SetWorkshopItem(items[i]);
587 }
588
589 if (nItems > 0 && m_eFocusedWidgetState != SCR_EListMenuWidgetFocus.FILTERING)
590 SwitchFocus(SCR_EListMenuWidgetFocus.LIST);
591
593 }
594
595 //------------------------------------------------------------------------------------------------
596 protected void SetPanelsMode(bool showEmptyPanel, bool forceFiltersList = false, string messagePresetTag = string.Empty, bool animateFiltersList = false)
597 {
598 m_bPanelsModeEmpty = showEmptyPanel;
599
600 // Show either main+filter panel or empty panel
601 m_Widgets.m_EmptyPanel.SetVisible(showEmptyPanel);
602 m_Widgets.m_FilterPanel.SetVisible(!showEmptyPanel);
603 m_Widgets.m_LeftPanelArea.SetVisible(!showEmptyPanel);
604
605 // Show message or hide it
606 m_Widgets.m_EmptyPanelMessage.SetVisible(!messagePresetTag.IsEmpty());
607 m_Widgets.m_MainPanelMessage.SetVisible(!messagePresetTag.IsEmpty());
608
609 // Set message based on tag
610 if (!messagePresetTag.IsEmpty())
611 {
612 if (showEmptyPanel)
613 m_Widgets.m_EmptyPanelMessageComponent.SetContentFromPreset(messagePresetTag);
614 else
615 m_Widgets.m_MainPanelMessageComponent.SetContentFromPreset(messagePresetTag);
616 }
617
618 // Force-show filter list
619 if (forceFiltersList)
620 {
621 m_Widgets.m_FilterPanelComponent.ShowFilterListBox(true, animateFiltersList);
622 if (!m_Widgets.m_FilterPanelComponent.GetFocused())
623 SwitchFocus(SCR_EListMenuWidgetFocus.FILTERING);
624 }
625
627 }
628
629 //------------------------------------------------------------------------------------------------
630 protected void UpdateItemsFoundMessage(int current, int total)
631 {
632 if (!m_Widgets || !m_Widgets.m_FilterPanelComponent)
633 return;
634
635 m_Widgets.m_FilterPanelComponent.SetItemsFoundMessage(current, total, current != total);
636
637 }
638
639 //------------------------------------------------------------------------------------------------
642 {
643 Widget focusedWidget = GetGame().GetWorkspace().GetFocusedWidget();
644
645 if (!focusedWidget)
646 return null;
647
649
650 if (!comp)
651 return null;
652
653 return comp.GetWorkshopItem();
654 }
655
656 //------------------------------------------------------------------------------------------------
658 protected void UpdatePagingWidgets()
659 {
660 m_Widgets.m_PagingButtons.SetVisible(m_iPageCount != 0);
661
662 if (m_iPageCount == 0)
663 return;
664
665 m_Widgets.m_PageIndicatorText.SetTextFormat(TEXT_PAGE_INDICATOR, m_iCurrentPage + 1, m_iPageCount);
666
667 m_Widgets.m_PrevPageButton_NoScrollComponent.SetEnabled(m_iCurrentPage > 0, false);
668 m_Widgets.m_NextPageButton_NoScrollComponent.SetEnabled(m_iCurrentPage < m_iPageCount - 1, false);
669 }
670
671 //------------------------------------------------------------------------------------------------
672 protected void UpdateNavigationButtons()
673 {
674 // Filters button
675 SetNavigationButtonVisible(m_NavFilter, !m_bPanelsModeEmpty);
676
677 // Item buttons
679 if (!item || item.GetRestricted())
680 {
681 SetNavigationButtonVisible(m_NavDetails, false);
682 SetNavigationButtonVisible(m_NavFavourite, false);
683 SetNavigationButtonVisible(m_NavEnable, false);
684 SetNavigationButtonVisible(m_NavPrimary, false);
685 SetNavigationButtonVisible(m_NavRepair, false);
686 return;
687 }
688
689 bool show = GetGame().GetInputManager().GetLastUsedInputDevice() != EInputDeviceType.MOUSE && !m_bPanelsModeEmpty;
690 string primaryLabel = SCR_WorkshopUiCommon.GetPrimaryActionName(item);
691
692 SCR_EAddonPrimaryActionState state = SCR_WorkshopUiCommon.GetPrimaryActionState(item);
693
694 SetNavigationButtonVisible(m_NavDetails, show && m_eMode != EContentBrowserAddonsSubMenuMode.MODE_EXTERNAL_ITEM_ARRAY);
695 SetNavigationButtonVisible(m_NavFavourite, show);
696 SetNavigationButtonVisible(m_NavEnable, show && item.GetOffline() && !SCR_WorkshopUiCommon.IsDownloadingAddonOrDependencies(item) && !(!item.GetEnabled() && state == SCR_EAddonPrimaryActionState.DEPENDENCIES_DOWNLOAD));
697 SetNavigationButtonVisible(m_NavPrimary, show && !primaryLabel.IsEmpty());
698 SetNavigationButtonVisible(m_NavRepair, show && primaryLabel.IsEmpty() && item.GetCorrupted());
699
700 // Favorite
701 if (m_NavFavourite)
702 m_NavFavourite.SetLabel(UIConstants.GetFavoriteLabel(item.GetFavourite()));
703
704 // Enable
705 if (m_NavEnable && item.GetOffline())
706 {
707 string enableLabel;
708 if (item.GetEnabled())
709 enableLabel = SCR_WorkshopUiCommon.LABEL_DISABLE;
710 else
711 enableLabel = SCR_WorkshopUiCommon.LABEL_ENABLE;
712
713 m_NavEnable.SetLabel(enableLabel);
714 }
715
716 // Primary
717 if (!primaryLabel.IsEmpty() && m_NavPrimary)
718 m_NavPrimary.SetLabel(primaryLabel);
719 }
720
721 //------------------------------------------------------------------------------------------------
723 protected bool IsWidgetUnderCursor(Widget w)
724 {
725 Widget wCurrent = WidgetManager.GetWidgetUnderCursor();
726 while (wCurrent)
727 {
728 if (wCurrent == w)
729 return true;
730 wCurrent = wCurrent.GetParent();
731 }
732 return false;
733 }
734
735 //------------------------------------------------------------------------------------------------
737 protected array<ref SCR_WorkshopItem> SelectItemsAtPage(array<ref SCR_WorkshopItem> items, int pageId)
738 {
739 array<ref SCR_WorkshopItem> itemsOut = {};
740
741 float nItemsPerPage = GRID_N_ROWS * GRID_N_COLUMNS;
742 int nPages = Math.Ceil(items.Count() / nItemsPerPage);
743
744 if (pageId < 0 || pageId >= nPages)
745 return itemsOut;
746
747 int idStart = nItemsPerPage * pageId; // Select from this
748 int idEnd = nItemsPerPage * (pageId + 1) - 1; // Up to this (including this)
749
750 if (idEnd >= items.Count())
751 idEnd = items.Count() - 1;
752
753 for (int i = idStart; i <= idEnd; i++)
754 {
755 itemsOut.Insert(items[i]);
756 }
757
758 return itemsOut;
759 }
760
761 //------------------------------------------------------------------------------------------------
763 protected void SwitchFocus(SCR_EListMenuWidgetFocus focus)
764 {
765 m_eFocusedWidgetState = focus;
766 Widget focusTarget;
767
768 switch (focus)
769 {
770 case SCR_EListMenuWidgetFocus.LIST:
771 {
772 if (m_LastFocusedTile && m_LastFocusedTile.IsVisible())
773 focusTarget = m_LastFocusedTile.GetRootWidget();
774 else if (m_Widgets.m_Grid)
775 focusTarget = m_Widgets.m_Grid.GetChildren();
776
777 break;
778 }
779
780 case SCR_EListMenuWidgetFocus.FILTERING:
781 {
782 focusTarget = m_Widgets.m_FilterPanelComponent.GetWidgets().m_FilterButton;
783 break;
784 }
785
786 case SCR_EListMenuWidgetFocus.SORTING:
787 {
788 focusTarget = m_Widgets.m_SortingHeader.GetChildren();
789 break;
790 }
791
792 case SCR_EListMenuWidgetFocus.NULL:
793 {
794 GetGame().GetWorkspace().SetFocusedWidget(null);
795 return;
796 }
797 }
798
799 // Fallback
800 if (!focusTarget || !focusTarget.IsVisible())
801 focusTarget = m_Widgets.m_SortingHeader.GetChildren();
802
803 // Execute focusing
804 GetGame().GetWorkspace().SetFocusedWidget(focusTarget);
805
806 // If we've focused a tile, we need to manually update the details panel (by switching page we focus the same widget, but the tile data is different)
807 if (!focusTarget)
808 return;
809
811 if (comp)
812 OnTileFocus(comp)
813 }
814
815 // --- Events ---
816 //------------------------------------------------------------------------------------------------
817 protected void OnInputDeviceUserChanged(EInputDeviceType oldDevice, EInputDeviceType newDevice)
818 {
820 }
821
822 //------------------------------------------------------------------------------------------------
823 // Called when we have lost connection and connected again, or when the tab is opened first time and connection is already active
824 // Not called for offline tab.
825 protected void OnConnected()
826 {
827 // Reinit the workshop API
829
830 // Ask the same page again
832 }
833
834 //------------------------------------------------------------------------------------------------
836 {
838
839 if (!comp || m_eMode == EContentBrowserAddonsSubMenuMode.MODE_EXTERNAL_ITEM_ARRAY)
840 return;
841
842 SCR_WorkshopItem selectedItem = comp.GetWorkshopItem();
843 if (!selectedItem)
844 return;
845
847 }
848
849 //------------------------------------------------------------------------------------------------
851 {
853 if (!tile)
854 return;
855
856 SCR_WorkshopItem item = tile.GetWorkshopItem();
857
859 tile.GetOnChange().Insert(OnTileStateChange);
860
861 if (!item || m_Widgets.m_AddonDetailsPanelComponent.GetItem() == item)
862 return;
863
864 m_Widgets.m_AddonDetailsPanelComponent.SetWorkshopItem(item);
865
866 m_LastFocusedTile = tile;
867 }
868
869 //------------------------------------------------------------------------------------------------
871 {
873 if (!tile)
874 return;
875
877 tile.GetOnChange().Remove(OnTileStateChange);
878 }
879
880 //------------------------------------------------------------------------------------------------
885
886 //------------------------------------------------------------------------------------------------
887 protected void OnFilterPanelToggled(bool shown)
888 {
889 if (shown)
890 SwitchFocus(SCR_EListMenuWidgetFocus.FILTERING);
891 else
892 SwitchFocus(SCR_EListMenuWidgetFocus.LIST);
893
894 //SCR_AnalyticsApplication.GetInstance().WorkshopUseFilterOn();
895 }
896
897 //------------------------------------------------------------------------------------------------
898 protected void OnInputAction(string name, float multiplier)
899 {
900 if (!m_bShown || GetGame().GetInputManager().GetLastUsedInputDevice() != EInputDeviceType.MOUSE)
901 return;
902
903 switch(name)
904 {
905 case UIConstants.MENU_ACTION_SELECT: OpenItemDetails(); break;
906 case UIConstants.MENU_ACTION_ENABLE: SetItemEnabled(); break;
907 case UIConstants.MENU_ACTION_SELECT_HOLD: ExecuteItemPrimaryAction(); break;
908 case UIConstants.MENU_ACTION_FAVORITE: SetItemFavorite(); break;
909 case UIConstants.MENU_ACTION_MOUSE_WHEEL: OnScrollPage(name); break;
910 }
911 }
912
913 //------------------------------------------------------------------------------------------------
915 {
916 // Toggle the state of the filter panel
917 bool show = !m_Widgets.m_FilterPanelComponent.GetFilterListBoxShown();
918 m_Widgets.m_FilterPanelComponent.ShowFilterListBox(show);
919 }
920
921 //------------------------------------------------------------------------------------------------
923 {
925 }
926
927 //------------------------------------------------------------------------------------------------
929 {
931 if (!item || item.GetRestricted())
932 return;
933
935 }
936
937 //------------------------------------------------------------------------------------------------
939 {
941 }
942
943 //------------------------------------------------------------------------------------------------
944 protected void SetItemEnabled()
945 {
947 if (!item)
948 return;
949
950 SCR_EAddonPrimaryActionState state = SCR_WorkshopUiCommon.GetPrimaryActionState(item);
951 if (item.GetRestricted() || !item.GetOffline() || SCR_WorkshopUiCommon.IsDownloadingAddonOrDependencies(item) || (!item.GetEnabled() && state == SCR_EAddonPrimaryActionState.DEPENDENCIES_DOWNLOAD))
952 return;
953
955 }
956
957 //------------------------------------------------------------------------------------------------
959 {
961 }
962
963 //------------------------------------------------------------------------------------------------
964 protected void SetItemRepair()
965 {
967 if (!item || item.GetRestricted())
968 return;
969
970 SCR_ValidateRepair_Dialog dialogValidator = SCR_CommonDialogs.CreateValidateRepairDialog();
971 dialogValidator.LoadAddon(item);
972 }
973
974 //------------------------------------------------------------------------------------------------
976 {
978 }
979
980 //------------------------------------------------------------------------------------------------
981 protected void SetItemFavorite()
982 {
984 if (!item || item.GetRestricted())
985 return;
986
987 item.SetFavourite(!item.GetFavourite());
988 }
989
990 //------------------------------------------------------------------------------------------------
992 {
994 }
995
996 //------------------------------------------------------------------------------------------------
997 protected void OpenItemDetails()
998 {
1000 if (m_eMode == EContentBrowserAddonsSubMenuMode.MODE_EXTERNAL_ITEM_ARRAY || !item || item.GetRestricted())
1001 return;
1002
1004 }
1005
1006 //------------------------------------------------------------------------------------------------
1014
1015 //------------------------------------------------------------------------------------------------
1017 {
1018 // Search Box Message
1019 m_iOnlineFilteredItems = m_WorkshopApi.GetTotalItemCount();
1020
1021 array<WorkshopItem> items = {};
1022 m_WorkshopApi.GetPageItems(items);
1023
1024 if (m_iOnlineFilteredItems < items.Count())
1025 m_iOnlineFilteredItems = items.Count();
1026
1029
1030 if (!m_bFirstLoad)
1032
1033 array<ref SCR_WorkshopItem> itemsRegistered = {};
1034 SCR_WorkshopItem iRegistered
1035 foreach (WorkshopItem i : items)
1036 {
1037 iRegistered = SCR_AddonManager.GetInstance().Register(i);
1038 if (iRegistered)
1039 itemsRegistered.Insert(iRegistered);
1040 }
1041
1042 if (!itemsRegistered.IsEmpty())
1043 {
1044 // There are some items received
1045 SetPanelsMode(false, m_bFirstPage && m_Widgets.m_FilterPanelComponent.AnyFilterButtonsVisible(), string.Empty, m_bFirstPage);
1046 }
1047 else
1048 {
1049 // No items received
1050 SetPanelsMode(false, messagePresetTag: MESSAGE_TAG_NOTHING_FOUND, forceFiltersList: true);
1051 }
1052
1053 m_iPageCount = m_WorkshopApi.GetPageCount();
1054 GridScreen_DisplayItems(itemsRegistered);
1055
1056 m_bFirstPage = false;
1057 }
1058
1059 //------------------------------------------------------------------------------------------------
1061 {
1062 if (GetShown())
1063 {
1064 // This submenu is shown, show the timeout dialog
1065 SCR_ConfigurableDialogUi dlg = SCR_CommonDialogs.CreateTimeoutTryAgainCancelDialog();
1068 }
1069 else
1070 {
1071 // This submenu is not shown (probably we switched to another tab)
1072 // Try to perform the request again
1075 }
1076
1077 m_bFailToLoad = true;
1078
1079 m_iPageCount = 0;
1080 }
1081
1082 //------------------------------------------------------------------------------------------------
1084 {
1085 // Set message
1087 RequestTabChange(EWorkshopTabId.OFFLINE);
1088 }
1089
1090 //------------------------------------------------------------------------------------------------
1096
1097 //------------------------------------------------------------------------------------------------
1099 protected void Callback_OnAddonOfflineStateChanged(SCR_WorkshopItem item, bool newState)
1100 {
1101 if (m_eMode == EContentBrowserAddonsSubMenuMode.MODE_OFFLINE)
1102 {
1103 // Some addon was installed or uninstalled, we must refresh the page
1105 }
1106 }
1107
1108 //------------------------------------------------------------------------------------------------
1110 protected void Callback_OnAddonReportedStateChanged(SCR_WorkshopItem item, bool newReported)
1111 {
1112 // Refresh current page if it has this addon
1113 bool found = false;
1115 {
1116 if (comp.GetWorkshopItem() == item)
1117 {
1120 break;
1121 }
1122 }
1123 }
1124
1125 // --- Paging ---
1126 //------------------------------------------------------------------------------------------------
1127 protected void OnPrevPageButton()
1128 {
1129 EInputDeviceType inputType = -1;
1130 if (m_bMousePageUsed)
1131 inputType = EInputDeviceType.MOUSE;
1132
1133 m_bMousePageUsed = false;
1134
1135 if (inputType == EInputDeviceType.MOUSE && !IsWidgetUnderCursor(m_Widgets.m_LeftPanelArea))
1136 return;
1137
1139 m_iCurrentPage = Math.ClampInt(m_iCurrentPage, 0, m_iPageCount - 1);
1140
1143 }
1144
1145 //------------------------------------------------------------------------------------------------
1146 protected void OnNextPageButton()
1147 {
1148 EInputDeviceType inputType = -1;
1149 if (m_bMousePageUsed)
1150 inputType = EInputDeviceType.MOUSE;
1151
1152 m_bMousePageUsed = false;
1153
1154 if (inputType == EInputDeviceType.MOUSE && !IsWidgetUnderCursor(m_Widgets.m_LeftPanelArea))
1155 return;
1156
1158 m_iCurrentPage = Math.ClampInt(m_iCurrentPage, 0, m_iPageCount - 1);
1159
1162 }
1163
1164 //------------------------------------------------------------------------------------------------
1165 protected void OnScrollPage(string name)
1166 {
1167 float value = GetGame().GetInputManager().GetActionValue(name);
1168 if (value != 0)
1169 m_bMousePageUsed = true;
1170
1171 bool playSound;
1172
1173 if (value < 0 && m_iCurrentPage < m_iPageCount - 1)
1174 {
1176 playSound = true;
1177 }
1178 else if (value > 0 && m_iCurrentPage > 0)
1179 {
1181 playSound = true;
1182 }
1183
1184 if (playSound && m_bCanPlayMouseWheelSound && IsWidgetUnderCursor(m_Widgets.m_LeftPanelArea))
1185 {
1189 }
1190 }
1191
1192 //------------------------------------------------------------------------------------------------
1194 {
1196 }
1197
1198 // --- Filters and Sorting ---
1199 //------------------------------------------------------------------------------------------------
1201 protected void OnFilterChange(SCR_FilterEntry filter)
1202 {
1204 RequestPage(0);
1205 //SCR_AnalyticsApplication.GetInstance().WorkshopSetFilter(filter.GetCategory().m_sInternalName, filter.m_sInternalName);
1206 }
1207
1208 //------------------------------------------------------------------------------------------------
1210 protected void OnFilterSearchConfirm(SCR_EditBoxComponent comp, string newValue)
1211 {
1213 RequestPage(0);
1214 //SCR_AnalyticsApplication.GetInstance().WorkshopUseSearch();
1215 }
1216
1217 //------------------------------------------------------------------------------------------------
1220 {
1222 RequestPage(0);
1223 //SCR_AnalyticsApplication.GetInstance().WorkshopSetSorting(sortHeader.GetSortElementName());
1224 }
1225
1226 //------------------------------------------------------------------------------------------------
1227 protected array<ref SCR_WorkshopItem> SearchItems(array<ref SCR_WorkshopItem> items, string searchStr)
1228 {
1229 array<ref SCR_WorkshopItem> itemsOut = {};
1230
1231 if (searchStr.IsEmpty())
1232 {
1233 foreach (SCR_WorkshopItem i : items)
1234 {
1235 itemsOut.Insert(i);
1236 }
1237
1238 return itemsOut;
1239 }
1240
1241 string searchStrLower = searchStr;
1242 searchStrLower.ToLower();
1243 string element;
1244 array<WorkshopTag> tags = {};
1245 bool foundTag;
1246 foreach (SCR_WorkshopItem i : items)
1247 {
1248 // Name
1249 element = i.GetName();
1250 element.ToLower();
1251 if (element.Contains(searchStrLower))
1252 {
1253 itemsOut.Insert(i);
1254 continue;
1255 }
1256
1257 // Author
1258 element = i.GetAuthorName();
1259 element.ToLower();
1260 if (element.Contains(searchStrLower))
1261 {
1262 itemsOut.Insert(i);
1263 continue;
1264 }
1265
1266 // Description
1267 element = i.GetDescription();
1268 element.ToLower();
1269 if (element.Contains(searchStrLower))
1270 {
1271 itemsOut.Insert(i);
1272 continue;
1273 }
1274
1275 // Tags
1276 tags.Clear();
1277 i.GetWorkshopItem().GetTags(tags);
1278 foundTag = false;
1279 foreach (WorkshopTag tag : tags)
1280 {
1281 element = tag.Name();
1282 element.ToLower();
1283 if (element == searchStrLower)
1284 {
1285 foundTag = true;
1286 break;
1287 }
1288 }
1289
1290 if (foundTag)
1291 {
1292 itemsOut.Insert(i);
1293 continue;
1294 }
1295 }
1296
1297 return itemsOut;
1298 }
1299
1300 //------------------------------------------------------------------------------------------------
1303 protected array<ref SCR_WorkshopItem> Filter_OfflineFilterAndSearch(array<ref SCR_WorkshopItem> itemsIn)
1304 {
1305 SCR_FilterSet filterSet = m_Widgets.m_FilterPanelComponent.GetFilter();
1306 string searchStr = m_Widgets.m_FilterPanelComponent.GetEditBoxSearch().GetValue();
1307 array<ref SCR_WorkshopItem> items = SearchItems(itemsIn, searchStr);
1308
1309 // ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
1310 // ! ! Filter categories and filter names are bound by strings, be careful while changing them ! !
1311 // ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
1312 // Arrays produced by each category. In the end we find intersection of those arrays.
1313 array<ref array<ref SCR_WorkshopItem>> arraysFromCategories = {};
1314
1315 // Quick filters - only one is selected at a time
1316 SCR_FilterCategory catQuick = filterSet.FindFilterCategory("quick_filters");
1317 SCR_FilterEntry filterAll = catQuick.FindFilter("all");
1318 SCR_FilterEntry filterFav = catQuick.FindFilter("favourite");
1319 SCR_FilterEntry filterAddonsOnly = catQuick.FindFilter("addons_only");
1320 SCR_FilterEntry filterSavensOnly = catQuick.FindFilter("saves_only");
1321
1322 if (filterAll.GetSelected())
1323 arraysFromCategories.Insert(items);
1324
1325 else if (filterFav.GetSelected())
1326 arraysFromCategories.Insert(SCR_AddonManager.SelectItemsBasic(items, EWorkshopItemQuery.FAVOURITE));
1327
1328 else if (filterAddonsOnly.GetSelected())
1329 arraysFromCategories.Insert(SCR_AddonManager.SelectItemsBasic(items, EWorkshopItemQuery.ONLY_WORKSHOP_ITEM));
1330
1331 else if (filterSavensOnly.GetSelected())
1332 arraysFromCategories.Insert(SCR_AddonManager.SelectItemsBasic(items, EWorkshopItemQuery.ONLY_WORLD_SAVES));
1333
1334 // Type tags
1335 array<WorkshopTag> selectedWorkshopTags = {};
1336 SCR_ContentBrowserFilterTag filterEntryTag;
1337 SCR_FilterCategory catMANWType = filterSet.FindFilterCategory("manw_winners");
1338 if (catMANWType)
1339 {
1340 foreach (SCR_FilterEntry f : catMANWType.GetFilters())
1341 {
1342 filterEntryTag = SCR_ContentBrowserFilterTag.Cast(f);
1343 if (filterEntryTag && filterEntryTag.GetSelected())
1344 selectedWorkshopTags.Insert(filterEntryTag.GetWorkshopTag());
1345 }
1346 }
1347 SCR_FilterCategory catType = filterSet.FindFilterCategory("type");
1348 foreach (SCR_FilterEntry f : catType.GetFilters())
1349 {
1350 filterEntryTag = SCR_ContentBrowserFilterTag.Cast(f);
1351 if (filterEntryTag && filterEntryTag.GetSelected())
1352 selectedWorkshopTags.Insert(filterEntryTag.GetWorkshopTag());
1353 }
1354 // If any tag filter is selected, but not all of them (because it would be same as selecting none)
1355 if (!selectedWorkshopTags.IsEmpty() && selectedWorkshopTags.Count() != catType.GetFilters().Count())
1356 {
1357 array<ref SCR_WorkshopItem> itemsFilteredByTags = {};
1358 foreach (SCR_WorkshopItem item : items)
1359 {
1360 if (item.GetWorkshopItem().HasAnyTag(selectedWorkshopTags))
1361 itemsFilteredByTags.Insert(item);
1362 }
1363 arraysFromCategories.Insert(itemsFilteredByTags);
1364 }
1365
1366 // State
1367 SCR_FilterCategory catState = filterSet.FindFilterCategory("state");
1368 if (!catState)
1369 return {};
1370
1371 if (catState.GetAllSelected() || catState.GetAllDeselected())
1372 {
1373 arraysFromCategories.Insert(items);
1374 }
1375 else
1376 {
1377 EWorkshopItemQuery query = 0;
1378 if (catState.FindFilter("enabled").GetSelected())
1379 query = query | EWorkshopItemQuery.ENABLED;
1380
1381 if (catState.FindFilter("disabled").GetSelected())
1382 query = query | EWorkshopItemQuery.NOT_ENABLED;
1383
1384 array<ref SCR_WorkshopItem> queryResult = SCR_AddonManager.SelectItemsOr(items, query);
1385 arraysFromCategories.Insert(queryResult);
1386 }
1387
1388 // Condition
1389 SCR_FilterCategory catCondition = filterSet.FindFilterCategory("condition");
1390 if (catCondition.GetAllSelected() || catCondition.GetAllDeselected())
1391 {
1392 arraysFromCategories.Insert(items);
1393 }
1394 else
1395 {
1396 EWorkshopItemQuery query = 0;
1397 if (catCondition.FindFilter("ready").GetSelected())
1398 query = query | EWorkshopItemQuery.NO_PROBLEMS;
1399
1400 if (catCondition.FindFilter("update_available").GetSelected())
1401 query = query | EWorkshopItemQuery.UPDATE_AVAILABLE | EWorkshopItemQuery.DEPENDENCY_UPDATE_AVAILABLE;
1402
1403 if (catCondition.FindFilter("repair_required").GetSelected())
1404 query = query | EWorkshopItemQuery.DEPENDENCY_MISSING | EWorkshopItemQuery.ENABLED_AND_DEPENDENCY_DISABLED;
1405
1406 array<ref SCR_WorkshopItem> queryResult = SCR_AddonManager.SelectItemsOr(items, query);
1407 arraysFromCategories.Insert(queryResult);
1408 }
1409
1410 // Find intersection of all arrays
1411 return Filter_IntersectArrays(arraysFromCategories);
1412 }
1413
1414 //------------------------------------------------------------------------------------------------
1416 protected array<ref SCR_WorkshopItem> Filter_IntersectArrays(array<ref array<ref SCR_WorkshopItem>> arrays)
1417 {
1418 array<ref SCR_WorkshopItem> result = {};
1419
1420 // Trivial cace: no arrays
1421 if (arrays.IsEmpty())
1422 return result;
1423
1424 array<ref SCR_WorkshopItem> array0 = arrays[0];
1425
1426 // Trivial case: one array
1427 if (arrays.Count() == 1)
1428 {
1429 result.Resize(array0.Count());
1430
1431 foreach (int i, SCR_WorkshopItem item : array0)
1432 {
1433 result[i] = item;
1434 }
1435
1436 return result;
1437 }
1438
1439 // Non-trivial case: more than one array
1440 bool foundInAll;
1441 foreach (SCR_WorkshopItem item : array0)
1442 {
1443 // This item must be found in all other arrays too
1444 foundInAll = true;
1445 for (int arrayId = 1; arrayId < arrays.Count(); arrayId++)
1446 {
1447
1448 if (!arrays[arrayId].Contains(item))
1449 {
1450 foundInAll = false;
1451 break;
1452 }
1453 }
1454
1455 if (foundInAll)
1456 result.Insert(item);
1457 }
1458
1459 return result;
1460 }
1461
1462 //--- API ---
1463 //------------------------------------------------------------------------------------------------
1467 {
1468 SCR_FilterSet filterSet = m_Widgets.m_FilterPanelComponent.GetFilter();
1469 SCR_FilterCategory otherCategory = filterSet.FindFilterCategory("other");
1470
1471 // Pack search string
1472 string searchStr = m_Widgets.m_FilterPanelComponent.GetWidgets().m_FilterSearchComponent.GetValue();
1473 if (!searchStr.IsEmpty() && !defaultValues)
1474 p.StoreString("search", searchStr); // <- "search": "..."
1475
1476 // Pack sorting and filters
1477 PackSortingData(p);
1478 PackQuickFilterData(p, defaultValues);
1479 PackTagFilterData(p, defaultValues);
1480
1481 // --- Reported ---
1482 // Pack reported
1483 SCR_FilterEntry reportedFilter = otherCategory.FindFilter("reported");
1484 p.StoreBoolean("reported", reportedFilter.GetSelected(defaultValues)); // <- "reported": true
1485 p.StoreBoolean("hidden", reportedFilter.GetSelected(defaultValues));
1486 }
1487
1488 //------------------------------------------------------------------------------------------------
1491 {
1492 // Pack sorting
1493 string orderBy = "name";
1494 string currentSortingItem = m_Widgets.m_SortingHeaderComponent.GetSortElementName();
1495 bool sortAscending = m_Widgets.m_SortingHeaderComponent.GetSortOrderAscending();
1496
1497 const string orderAsc = "asc";
1498 const string orderDesc = "desc";
1499
1500 switch (currentSortingItem)
1501 {
1502 case "popularity":
1503 orderBy = "popularity";
1504 break;
1505 case "rating":
1506 orderBy = "averageRating";
1507 break;
1508 case "recently_added":
1509 orderBy = "createdAt";
1510 sortAscending = !sortAscending; // Invert sorting direction, when ascen
1511 break;
1512 case "name":
1513 orderBy = "name";
1514 break;
1515 case "subscribers":
1516 orderBy = "subscriberCount";
1517 break;
1518 }
1519
1520 string orderDirection = orderAsc;
1521 if (!sortAscending)
1522 orderDirection = orderDesc;
1523
1524 params.StoreString("orderBy", orderBy); // <- "orderBy": "..."
1525 params.StoreString("orderDirection", orderDirection); // <- "orderDirection": "..."
1526 }
1527
1528 //------------------------------------------------------------------------------------------------
1530 {
1531 SCR_FilterSet filterSet = m_Widgets.m_FilterPanelComponent.GetFilter();
1532
1533 // Pack 'quick filters'
1534 SCR_FilterCategory quickCategory = filterSet.FindFilterCategory("quick_filters");
1535 SCR_FilterEntry filterAll = quickCategory.FindFilter("all");
1536 SCR_FilterEntry filterFav = quickCategory.FindFilter("favourite");
1537 //SCR_FilterEntry filterSub = quickCategory.FindFilter("subscribed");
1538 SCR_FilterEntry filterRated = quickCategory.FindFilter("rated");
1539
1540 if (filterFav.GetSelected(defaultValues))
1541 params.StoreBoolean("favorited", true); // <- "favorited": true
1542 //else if (filterSub.GetSelected())
1543 // p.StoreBoolean("subscribed", true); // <- "subscribed": true
1544 else if (filterRated.GetSelected(defaultValues))
1545 {
1546 params.StartObject("averageRating"); // <- "AverageRating" object
1547 params.StoreFloat("gt", 0.75); // <- "gt": 1.23
1548 params.StoreFloat("lt", 1); // <- "lt": 1.23
1549 params.EndObject(); // <- End AverageRating
1550 }
1551 }
1552
1553 //------------------------------------------------------------------------------------------------
1555 {
1556 SCR_FilterSet filterSet = m_Widgets.m_FilterPanelComponent.GetFilter();
1557 SCR_FilterCategory otherCategory = filterSet.FindFilterCategory("other");
1558
1559 // Make arrays with tags to include or exclude
1560 array<ref array<string>> tagArraysInclude = {};
1561 array<string> tagsExclude = {};
1562
1563 // Tags from type filters
1564 SCR_FilterCategory manwCategory = filterSet.FindFilterCategory("manw_winners");
1565 if (manwCategory.GetAnySelected() && !defaultValues)
1566 {
1567 array<string> typeTags = {};
1568 foreach (SCR_FilterEntry filter : manwCategory.GetFilters())
1569 {
1570 if (filter.GetSelected(defaultValues))
1571 typeTags.Insert(filter.m_sInternalName);
1572 }
1573 tagArraysInclude.Insert(typeTags);
1574 }
1575
1576 // Tags from type filters
1577 SCR_FilterCategory typeCategory = filterSet.FindFilterCategory("type");
1578 if (typeCategory.GetAnySelected() && !defaultValues)
1579 {
1580 array<string> typeTags = {};
1581 foreach (SCR_FilterEntry filter : typeCategory.GetFilters())
1582 {
1583 if (filter.GetSelected(defaultValues))
1584 typeTags.Insert(filter.m_sInternalName);
1585 }
1586 tagArraysInclude.Insert(typeTags);
1587 }
1588
1589 // Tags from test mods
1590 SCR_FilterEntry testFilter = otherCategory.FindFilter("TESTDEV");
1591 if (!testFilter.GetSelected(defaultValues))
1592 tagsExclude.Insert(testFilter.m_sInternalName);
1593
1594 // Pack the tags
1595 if (!tagArraysInclude.IsEmpty() || !tagsExclude.IsEmpty())
1596 {
1597 params.StartObject("tags"); // <- Start "tags" object
1598
1599 if (!tagArraysInclude.IsEmpty())
1600 {
1601 params.StartArray("include"); // <- Start "include" array
1602
1603 foreach (array<string> tagArray : tagArraysInclude)
1604 {
1605 params.ItemArray(); // <- Start tags array
1606 foreach (string tag : tagArray)
1607 {
1608 params.ItemString(tag); // <- Array element
1609 }
1610 params.EndArray(); // <- End tags array
1611 }
1612
1613 params.EndArray(); // <- End "include" array
1614 }
1615
1616 if (!tagsExclude.IsEmpty())
1617 {
1618 params.StartArray("exclude"); // <- Start "exclude" array
1619 foreach (string tag : tagsExclude)
1620 {
1621 params.ItemString(tag); // <- Array element
1622 }
1623 params.EndArray(); // <- End "exclude" array
1624 }
1625
1626 params.EndObject(); // <- End "tags" object
1627 }
1628 }
1629
1630 //------------------------------------------------------------------------------------------------
1632 void SetWorkshopItems(array<ref SCR_WorkshopItem> items)
1633 {
1634 if (m_eMode != EContentBrowserAddonsSubMenuMode.MODE_EXTERNAL_ITEM_ARRAY)
1635 return;
1636
1637 m_aExternalItems = items;
1638 RequestPage(0);
1639 }
1640}
1641
1643{
1644 // We will call back to submenu to perform packing of the JSON struct
1645 SCR_ContentBrowser_AddonsSubMenu m_AddonsSubMenu;
1646 protected bool m_bUseDefaultValues;
1647
1648 //------------------------------------------------------------------------------------------------
1649 void SCR_ContentBrowser_GetAssetListParams(SCR_ContentBrowser_AddonsSubMenu subMenu, bool defaultValues = false)
1650 {
1651 m_bUseDefaultValues = defaultValues;
1652 m_AddonsSubMenu = subMenu;
1653 }
1654
1655 //------------------------------------------------------------------------------------------------
1656 override void OnPack()
1657 {
1658 m_AddonsSubMenu.PackGetAssetListParams(this, m_bUseDefaultValues);
1659 }
1660}
1661
1662class SCR_CompareWorkshopItemName : SCR_SortCompare<SCR_WorkshopItem>
1663{
1664 override static int Compare(SCR_WorkshopItem left, SCR_WorkshopItem right)
1665 {
1666 string name1 = left.GetName();
1667 string name2 = right.GetName();
1668
1669 if (name1.Compare(name2) == -1)
1670 return 1;
1671 else
1672 return 0;
1673 }
1674}
1675
1676class SCR_CompareWorkshopItemTimeSinceLastPlay : SCR_SortCompare<SCR_WorkshopItem>
1678 override static int Compare(SCR_WorkshopItem left, SCR_WorkshopItem right)
1679 {
1680 int timeLeft = left.GetTimeSinceLastPlay();
1681 if (timeLeft < 0) // Negative value means we have never played this.
1682 timeLeft = 0x7FFFFFFF; // We set it to max positive int for sorting purpuse.
1683 int timeRight = right.GetTimeSinceLastPlay();
1684 if (timeRight < 0)
1685 timeRight = 0x7FFFFFFF;
1686
1687 return timeLeft < timeRight;
1688 }
1689}
1690
1691class SCR_CompareWorkshopItemTimeSinceFirstDownload : SCR_SortCompare<SCR_WorkshopItem>
1692{
1693 override static int Compare(SCR_WorkshopItem left, SCR_WorkshopItem right)
1694 {
1695 int timeLeft = left.GetTimeSinceFirstDownload();
1696 if (timeLeft < 0) // Negative value means we never downloaded this
1697 timeLeft = 0x7FFFFFFF; // We set it to max positive int for sorting purpuse.
1698 int timeRight = right.GetTimeSinceFirstDownload();
1699 if (timeRight < 0)
1700 timeRight = 0x7FFFFFFF;
1701
1702 return timeLeft < timeRight;
1703 }
1704}
1705
1706// Sort by time since last played
1707class SCR_CompareWorkshopItemTargetSize : SCR_SortCompare<SCR_WorkshopItem>
1708{
1709 override static int Compare(SCR_WorkshopItem left, SCR_WorkshopItem right)
1710 {
1711 float leftSize = left.GetTargetRevisionPatchSize();
1712 float rightSize = right.GetTargetRevisionPatchSize();
1713
1714 return leftSize < rightSize;
1715 }
1716}
ref array< string > tags
EWorkshopTabId
ArmaReforgerScripted GetGame()
Definition game.c:1398
EWorkshopItemQuery
InputManager GetInputManager()
void Callback_OnAddonReportedStateChanged(SCR_WorkshopItem item, bool newReported)
Called by SCR_AddonManager when some addon is reported or unreported.
void OnFilterButton(SCR_ButtonBaseComponent comp)
array< ref SCR_WorkshopItem > Filter_IntersectArrays(array< ref array< ref SCR_WorkshopItem > > arrays)
Returns intersection of all arrays (array of elements which are contained in all arrays).
void OnPrimaryButton(SCR_ButtonBaseComponent comp)
SCR_InputButtonComponent m_NavRepair
array< ref SCR_WorkshopItem > SelectItemsAtPage(array< ref SCR_WorkshopItem > items, int pageId)
Returns array of items within specified page, page ID starting from 0.
SCR_InputButtonComponent m_NavEnable
void OnTileFocusLost(ScriptedWidgetComponent comp)
void OnDetailsButton(SCR_ButtonBaseComponent comp)
WorkshopApi m_WorkshopApi
array< ref SCR_WorkshopItem > Filter_OfflineFilterAndSearch(array< ref SCR_WorkshopItem > itemsIn)
void Callback_OnRequestPageGetAssets()
const string MESSAGE_TAG_NOTHING_DOWNLOADED
void UpdateItemsFoundMessage(int current, int total)
const int MOUSE_WHEEL_SOUND_COOLDOWN
void Callback_OnAddonOfflineStateChanged(SCR_WorkshopItem item, bool newState)
Called by SCR_AddonManager when some addon is downloaded or uninstalled.
void UpdateNavigationButtons()
void OnTileStateChange(ScriptedWidgetComponent comp)
const string MESSAGE_TAG_NO_SAVES_FOUND
void OnRepairButton(SCR_ButtonBaseComponent comp)
string m_sScrollWheelPageTurningSound
SCR_InputButtonComponent m_NavFilter
void OnTileClick(SCR_ScriptedWidgetComponent baseComp)
void SetWorkshopItems(array< ref SCR_WorkshopItem > items)
Sets the item array on which this tab operates. Only possible in external item array mode.
ref BackendCallback m_PageCallback
ref array< SCR_ContentBrowserTileComponent > m_aTileComponents
array< ref SCR_WorkshopItem > SearchItems(array< ref SCR_WorkshopItem > items, string searchStr)
void OnFilterPanelToggled(bool shown)
void PackSortingData(SCR_ContentBrowser_GetAssetListParams params)
Pack filter sorting settings - order type, dirrection.
void Callback_OnRequestPageGetAllAssets(BackendCallback callback)
void OnEnableButton(SCR_ButtonBaseComponent comp)
void InitWidgetEventHandlers()
void OnInputAction(string name, float multiplier)
ref SCR_WorkshopDownloadSequence m_DownloadRequest
void RequestPage(int pageId)
void PackTagFilterData(SCR_ContentBrowser_GetAssetListParams params, bool defaultValues)
void RequestOnlinePageUnfiltered(int pageId)
Requests an unfiltered page from backend to get the total number of items.
void PackQuickFilterData(SCR_ContentBrowser_GetAssetListParams params, bool defaultValues)
void RequestOnlinePage(int pageId)
Requests a filtered page from backend to get the actual items to display.
SCR_InputButtonComponent m_NavDetails
const string MESSAGE_TAG_ERROR_CONNECTION
const string MESSAGE_TAG_NOTHING_FOUND
void OnMouseWheelSoundCooldownEnd()
SCR_InputButtonComponent m_NavFavourite
void Callback_OnRequestPageTimeout_OnDialogTryAgain()
void PackGetAssetListParams(SCR_ContentBrowser_GetAssetListParams p, bool defaultValues=false)
SCR_ContentBrowserTileComponent m_LastFocusedTile
void OnInputDeviceUserChanged(EInputDeviceType oldDevice, EInputDeviceType newDevice)
void OnSortingHeaderChange(SCR_SortHeaderComponent sortHeader)
Called when sorting header changes.
SCR_WorkshopItem GetSelectedItem()
Returns currently selected workshop item.
void GridScreen_Clear()
Tiles are cleared up here. They are not deleted, but hidden.
void ExecuteItemPrimaryAction()
ref SCR_ContentBrowser_GetAssetListParams m_GetAssetListParamsDefault
void OnTileFocus(ScriptedWidgetComponent comp)
void SCR_ContentBrowser_GetAssetListParams(SCR_ContentBrowser_AddonsSubMenu subMenu, bool defaultValues=false)
const int GRID_N_ROWS
void UpdatePagingWidgets()
Updates the widgets related to paging: buttons, text.
ref SCR_ContentBrowser_GetAssetListParams m_GetAssetListParams
const string TEXT_PAGE_INDICATOR
void GridScreen_DisplayItems(array< ref SCR_WorkshopItem > items)
void OnFilterSearchConfirm(SCR_EditBoxComponent comp, string newValue)
Called when user confirms the value in the seacrh string.
const int GRID_N_COLUMNS
void SetPanelsMode(bool showEmptyPanel, bool forceFiltersList=false, string messagePresetTag=string.Empty, bool animateFiltersList=false)
const string MESSAGE_TAG_LOADING
void DisplayOfflineItems(int pageId)
void Callback_OnRequestPageTimeout_OnCloseDialog()
void OnScrollPage(string name)
ref array< ref SCR_WorkshopItem > m_aExternalItems
bool m_bClearCacheAtNextRequest
ref BackendCallback m_PageCallbackItemsTotal
const int LOAD_PAGE_DELAY
void SwitchFocus(SCR_EListMenuWidgetFocus focus)
Switch focus.
const ResourceName LAYOUT_GRID_TILE
void DisplayExternalItems(int pageId)
Selects items from the externally provided array and shows them on grid, only makes sense in external...
void Callback_OnRequestPageTimeout()
SCR_EListMenuWidgetFocus m_eFocusedWidgetState
const string MESSAGE_TAG_NOTHING_DOWNLOADED_2
SCR_InputButtonComponent m_NavPrimary
SCR_MenuActionsComponent m_MenuActionsComponent
void OnFavouriteButton(SCR_ButtonBaseComponent comp)
bool IsWidgetUnderCursor(Widget w)
Returns true if cursor over a given widget.
void OnMenuUpdate(float tDelta)
void OnFilterChange(EEditableEntityState state, set< SCR_EditableEntityComponent > entitiesInsert, set< SCR_EditableEntityComponent > entitiesRemove)
SCR_DestructionSynchronizationComponentClass ScriptComponentClass int index
void OnMenuFocusGained()
Widget m_wRoot
bool m_bShown
ref SCR_HeaderNavigationWidgets m_Widgets
Widget GetRootWidget()
override void OnTabHide()
override void OnTabRemove()
override void OnTabShow()
override void OnTabCreate(Widget menuRoot, ResourceName buttonsLayout, int index)
override void OnMenuShow()
SCR_VONRadialDisplay Compare
EWeaponGroupFireMode m_eMode
static ContentBrowserDetailsMenu OpenForWorkshopItem(SCR_WorkshopItem item)
Opens the menu for a given workshop item.
Definition Math.c:13
SCR_WorkshopItem Register(WorkshopItem item)
Returns a SCR_WorkshopItem. If it's not registered, creates a new one and registers it.
ref ScriptInvoker m_OnAddonOfflineStateChanged
static array< ref SCR_WorkshopItem > SelectItemsBasic(array< ref SCR_WorkshopItem > items, EWorkshopItemQuery query)
ref ScriptInvoker m_OnAddonReportedStateChanged
static array< ref SCR_WorkshopItem > SelectItemsOr(array< ref SCR_WorkshopItem > items, EWorkshopItemQuery query)
static SCR_AddonManager GetInstance()
Base class for any button, regardless its own content.
void SCR_ContentBrowser_GetAssetListParams(SCR_ContentBrowser_AddonsSubMenu subMenu, bool defaultValues=false)
ScriptInvokerScriptedWidgetComponent GetOnFocusLost()
ScriptInvokerScriptedWidgetComponent GetOnFocus()
void SetWorkshopItem(SCR_WorkshopItem workshopItem)
If a null pointer is passed, tile becomes hidden.
ScriptInvokerScriptedWidgetComponent GetOnChange()
static SCR_ContentBrowserTileComponent FindComponent(notnull Widget w)
array< ref SCR_FilterEntry > GetFilters()
Returns array with filter entries.
SCR_FilterEntry FindFilter(string internalName)
Finds a filter by internal name.
bool GetSelected(bool defaultValue=false)
Returns the selected flag.
SCR_FilterCategory FindFilterCategory(string internalName)
Finds a filter category by its internal name.
array< ref SCR_FilterCategory > GetFilterCategories()
static SCR_MenuActionsComponent FindComponent(Widget w)
void LoadAddon(notnull SCR_WorkshopItem item)
void SetFavourite(bool favourite)
bool GetOffline()
True when we have the item on our local storage.
float GetTargetRevisionPatchSize()
bool GetRestricted()
Returns true when item is restricted for any reason (blocked or reported).
static void OnEnableAddonButton(SCR_WorkshopItem item)
static string GetPrimaryActionName(SCR_WorkshopItem item)
Returns name of suggested primary action.
static void ExecutePrimaryAction(notnull SCR_WorkshopItem item, out SCR_WorkshopDownloadSequence dlRequest, Revision revision=null)
static SCR_EAddonPrimaryActionState GetPrimaryActionState(notnull SCR_WorkshopItem item)
static bool IsDownloadingAddonOrDependencies(SCR_WorkshopItem item)
Workshop Api instance.
Definition WorkshopApi.c:14
Workshop Item instance.
SCR_FieldOfViewSettings Attribute
proto bool Contains(T value)