3 using System.Collections.Generic;
5 using System.Threading;
6 using System.Threading.Tasks;
9 using Microsoft.Xna.Framework;
10 using Microsoft.Xna.Framework.Graphics;
15 sealed
partial class MutableWorkshopMenu : WorkshopMenu
17 private string ExtractTitle(ItemOrPackage itemOrPackage)
18 => itemOrPackage.TryGet(out ContentPackage package)
20 : (((Steamworks.Ugc.Item)itemOrPackage).Title ??
"");
22 private void CreateWorkshopItemDetailContainer(
24 out GUIListBox outerContainer,
25 Action<ItemOrPackage, GUIFrame> onSelected,
27 out Action<ItemOrPackage> select,
30 ItemOrPackage? selectedItemOrPackage =
null;
32 GUIListBox outContainer =
new GUIListBox(
new RectTransform(Vector2.One, parent.RectTransform),
36 ScrollBarEnabled =
false,
37 ScrollBarVisible =
false,
40 outerContainer = outContainer;
43 new GUILayoutGroup(
new RectTransform(Vector2.One, outerContainer.Content.RectTransform));
44 var selectedHeaderLayout =
45 new GUILayoutGroup(
new RectTransform((1.0f, 0.05f), selectedLayout.RectTransform),
47 childAnchor:
Anchor.CenterLeft);
51 if (selectedItemOrPackage is
null) {
return; }
52 selectedItemOrPackage =
null;
56 deselect = deselectMethod;
59 new GUIButton(
new RectTransform((0.04f, 1.0f), selectedHeaderLayout.RectTransform),
60 style:
"GUIButtonToggleLeft")
62 OnClicked = (button, o) =>
68 var padding =
new GUIFrame(
new RectTransform((1.0f, 0.005f), selectedLayout.RectTransform), style:
null);
69 var selectedFrame =
new GUIFrame(
new RectTransform((1.0f, 0.945f), selectedLayout.RectTransform),
72 var selectionScroller =
new GUICustomComponent(
73 new RectTransform(Vector2.Zero, outerContainer.Parent.RectTransform),
74 onUpdate: (deltaTime, component) =>
76 float targetScroll = selectedItemOrPackage is null
79 outContainer.ScrollBar.BarScroll
80 = MathUtils.NearlyEqual(targetScroll, outContainer.ScrollBar.BarScroll)
82 : MathHelper.Lerp(outContainer.ScrollBar.BarScroll, targetScroll, 0.3f);
85 select = itemOrPackage =>
90 selectedFrame.ClearChildren();
94 var dropdowns = outContainer.Content.GetAllChildren<GUIDropDown>().ToArray();
95 var allChildren = outContainer.Content.GetAllChildren()
96 .Concat(selectedFrame.GetAllChildren());
97 allChildren.ForEach(c =>
101 c.ClampMouseRectToParent = !(c.Parent?.Parent is GUIDropDown);
105 selectedItemOrPackage = itemOrPackage;
106 onSelected(itemOrPackage, selectedFrame);
110 private void CreateWorkshopItemList(
112 out GUIListBox outerContainer,
113 out GUIListBox workshopItemList,
114 Action<Steamworks.Ugc.Item, GUIFrame> onSelected)
115 => CreateWorkshopItemOrPackageList(
118 out workshopItemList,
119 onSelected: (ItemOrPackage itemOrPackage, GUIFrame frame)
120 => onSelected((Steamworks.Ugc.Item)itemOrPackage, frame));
122 private GUIButton CreateShowInSteamButton(Steamworks.Ugc.Item workshopItem, RectTransform rectT)
125 TextManager.Get(
"WorkshopShowItemInSteam"), style:
"GUIButtonSmall")
127 OnClicked = (button, o) =>
129 SteamManager.OverlayCustomUrl(workshopItem.Url);
134 private GUIButton? CreateShowInSteamButton(ItemOrPackage itemOrPackage)
135 => itemOrPackage.TryGet(out Steamworks.Ugc.Item workshopItem)
136 ? CreateShowInSteamButton(workshopItem)
139 private void CreateWorkshopItemOrPackageList(
141 out GUIListBox outerContainer,
142 out GUIListBox workshopItemList,
143 Action<ItemOrPackage, GUIFrame> onSelected)
145 GUIListBox? itemList =
null;
147 CreateWorkshopItemDetailContainer(
150 onSelected: onSelected,
151 onDeselected: () => itemList?.
Deselect(),
152 out var select, out var deselect);
154 itemList =
new GUIListBox(
new RectTransform(Vector2.One, outerContainer.Content.RectTransform))
156 PlaySoundOnSelect =
true,
158 itemList.RectTransform.SetAsFirstChild();
159 workshopItemList = itemList;
164 itemList.OnSelected = (component, userData) =>
167 if (GUI.MouseOn.Parent != itemList.Content) {
return false; }
169 if (!(userData is ItemOrPackage itemOrPackage)) {
return false; }
171 select(itemOrPackage);
177 private void AddUnpublishedMods(ISet<Steamworks.Ugc.Item> workshopItems)
179 if (!selfModsListOption.TryUnwrap(out var selfModsList)) {
return; }
183 void clearWithMessage(LocalizedString message)
185 selfModsList.ClearChildren();
186 var messageFrame =
new GUIFrame(
new RectTransform(Vector2.One, selfModsList.Content.RectTransform),
191 new GUITextBlock(
new RectTransform((0.5f, 1.0f), messageFrame.RectTransform,
Anchor.Center),
193 textAlignment: Alignment.Center,
195 font: GUIStyle.Font);
198 if (SteamManager.IsFreeWeekend())
200 clearWithMessage(TextManager.Get(
"FreeWeekendCantPublish"));
203 if (SteamManager.IsFamilyShared())
205 clearWithMessage(TextManager.Get(
"FamilySharedCantPublish"));
209 DateTime getEditTime(ContentPackage p)
211 DateTime writeTime = File.GetLastWriteTime(p.Dir);
217 var files = Directory.GetFiles(p.Dir,
"*", System.IO.SearchOption.AllDirectories);
218 foreach (var file
in files)
220 DateTime newTime = File.GetLastWriteTime(file);
221 if (newTime > writeTime) { writeTime = newTime; }
228 (Steamworks.Ugc.Item WorkshopItem, ContentPackage? LocalPackage)[] publishedItems = workshopItems
230 (ContentPackage?)ContentPackageManager.LocalPackages.FirstOrDefault(p
231 => p.TryExtractSteamWorkshopId(out var workshopId) && workshopId.Value == item.Id)))
233 .OrderBy(t => t.Item2 ==
null)
234 .ThenByDescending(t => t.Item2 is { } p ? getEditTime(p) : t.Item1.LatestUpdateTime)
237 int indexOfUserDataInPublishedItemsArray(
object userData)
238 => publishedItems.IndexOf(t
239 => t.WorkshopItem.Id == ((Steamworks.Ugc.Item)(userData as ItemOrPackage)!).Id);
242 var publishedGuiComponents = selfModsList.Content.Children.OrderBy(c => indexOfUserDataInPublishedItemsArray(c.UserData)).ToArray();
245 var unpublishedMods = ContentPackageManager.LocalPackages
247 !p.TryExtractSteamWorkshopId(out var workshopId)
248 || !publishedItems.Any(item => item.WorkshopItem.Id == workshopId.Value))
249 .OrderByDescending(getEditTime).ToArray();
251 if (unpublishedMods.Any())
253 var unpublishedHeader
254 =
new GUITextBlock(
new RectTransform((1.0f, 1.0f / 11.0f), selfModsList.Content.RectTransform),
255 TextManager.Get(
"UnpublishedModsHeader"), font: GUIStyle.SubHeadingFont) { CanBeFocused =
false };
258 foreach (var unpublishedMod
in unpublishedMods)
260 var unpublishedFrame =
new GUIFrame(
261 new RectTransform((1.0f, 1.0f / 5.5f), selfModsList.Content.RectTransform),
262 style:
"ListBoxElement")
264 UserData = (ItemOrPackage)unpublishedMod
266 var unpublishedLayout
267 =
new GUILayoutGroup(
new RectTransform(Vector2.One, unpublishedFrame.RectTransform),
271 RelativeSpacing = 0.02f
273 var unpublishedPadding
275 new RectTransform(Vector2.One, unpublishedLayout.RectTransform,
276 scaleBasis:
ScaleBasis.BothHeight), style:
null)
280 var unpublishedTextBlock
281 =
new GUITextBlock(
new RectTransform(Vector2.One, unpublishedLayout.RectTransform),
282 $
"{unpublishedMod.Name}\n\n" +
283 TextManager.GetWithVariable(
"LastLocalEditTime",
285 getEditTime(unpublishedMod).ToString()),
290 unpublishedLayout.Recalculate();
293 if (publishedGuiComponents.Any())
296 =
new GUITextBlock(
new RectTransform((1.0f, 1.0f / 11.0f), selfModsList.Content.RectTransform),
297 TextManager.Get(
"PublishedModsHeader"), font: GUIStyle.SubHeadingFont) { CanBeFocused =
false };
300 foreach (var c
in publishedGuiComponents)
303 var textBlock = (c.FindChild(b => b is GUITextBlock, recursive:
true) as GUITextBlock)!;
304 textBlock.Text += $
"\n";
306 int index = indexOfUserDataInPublishedItemsArray(c.UserData);
307 (Steamworks.Ugc.Item workshopItem, ContentPackage? localMod) = publishedItems[index];
308 if (localMod !=
null)
310 textBlock.Text += $
"\n" + TextManager.GetWithVariable(
"LastLocalEditTime",
"[datetime]", getEditTime(localMod).ToString());
312 textBlock.Text += $
"\n" + TextManager.GetWithVariable(
"LatestPublishTime",
"[datetime]", workshopItem.LatestUpdateTime.ToLocalTime().ToString());
316 private static (GUIButton Button, GUIFrame Sprite) CreatePaddedButton(RectTransform rectT,
string style,
float spriteScale)
318 var button =
new GUIButton(
322 var sprite =
new GUIFrame(
323 new RectTransform(Vector2.One * spriteScale, button.RectTransform,
Anchor.Center),
329 return (button, sprite);
332 private static void CreateSubscribeButton(Steamworks.Ugc.Item workshopItem, RectTransform rectT,
float spriteScale)
334 const string plusButton =
"GUIPlusButton";
335 const string minusButton =
"GUIMinusButton";
337 LocalizedString subscribeTooltip = TextManager.Get(
"DownloadButton");
338 LocalizedString unsubscribeTooptip = TextManager.Get(
"WorkshopItemUnsubscribe");
340 var (subscribeButton, subscribeButtonSprite) = CreatePaddedButton(rectT, plusButton, spriteScale);
341 subscribeButton.ToolTip = subscribeTooltip;
343 subscribeButton.OnClicked = (button, o) =>
345 if (!SteamManager.IsInitialized) {
return false; }
347 if (!workshopItem.IsSubscribed)
349 workshopItem.Subscribe();
350 TaskPool.Add($
"DownloadSubscribedItem{workshopItem.Id}",
351 SteamManager.Workshop.ForceRedownload(workshopItem),
352 TaskPool.IgnoredCallback);
356 workshopItem.Unsubscribe();
357 SteamManager.Workshop.Uninstall(workshopItem);
363 var buttonStyleUpdater =
new GUICustomComponent(
364 new RectTransform(Vector2.Zero, subscribeButton.RectTransform),
365 onUpdate: (deltaTime, component) =>
367 if (!SteamManager.IsInitialized) { return; }
369 if (subscribeButtonSprite.Style is { Identifier: { } styleId })
371 if (workshopItem.IsSubscribed && styleId != minusButton)
373 subscribeButtonSprite.ApplyStyle(GUIStyle.GetComponentStyle(minusButton));
374 subscribeButton.ToolTip = unsubscribeTooptip;
376 if (!workshopItem.IsSubscribed && styleId != plusButton)
378 subscribeButtonSprite.ApplyStyle(GUIStyle.GetComponentStyle(plusButton));
379 subscribeButton.ToolTip = subscribeTooltip;
384 float displayedDownloadAmount = workshopItem.DownloadAmount;
385 var downloadProgressBar =
new GUICustomComponent(
386 new RectTransform((1.22f, 1.22f), subscribeButtonSprite.RectTransform,
Anchor.Center),
387 onDraw: (spriteBatch, component) =>
389 if (!SteamManager.IsInitialized) { return; }
391 bool visible = workshopItem.IsSubscribed
392 && (workshopItem.IsDownloading
393 || workshopItem.IsDownloadPending
394 || !MathUtils.NearlyEqual(workshopItem.DownloadAmount, displayedDownloadAmount));
395 if (!visible) {
return; }
397 void drawSection(
float amount, Color color,
float thickness)
398 => GUI.DrawDonutSection(
400 component.Rect.Center.ToVector2() + (0, 1),
401 new Range<float>(component.Rect.Width * 0.55f - thickness * 0.5f, component.Rect.Width * 0.55f + thickness * 0.5f),
402 amount * MathF.PI * 2.0f,
405 void drawSectionFuzzy(
float amount, Color color,
float thickness)
407 drawSection(amount, color, thickness);
408 drawSection(amount, color * 0.6f, thickness + 0.5f);
409 drawSection(amount, color * 0.3f, thickness + 1.0f);
412 drawSectionFuzzy(1.0f, Color.Lerp(Color.Black, GUIStyle.Blue, 0.2f), component.Rect.Width * 0.25f);
413 drawSectionFuzzy(1.0f, Color.Black, component.Rect.Width * 0.15f);
414 drawSectionFuzzy(displayedDownloadAmount, GUIStyle.Green, component.Rect.Width * 0.08f);
416 onUpdate: (deltaTime, component) =>
418 if (!SteamManager.IsInitialized) {
return; }
420 displayedDownloadAmount = Math.Min(
421 workshopItem.DownloadAmount,
422 MathHelper.Lerp(displayedDownloadAmount, workshopItem.DownloadAmount, 0.05f));
429 private void PopulateItemList(GUIListBox itemListBox, Task<ISet<Steamworks.Ugc.Item>> items,
bool includeSubscribeButton, Action<ISet<Steamworks.Ugc.Item>>? onFill =
null)
431 itemListBox.ClearChildren();
432 itemListBox.Deselect();
433 itemListBox.ScrollBar.BarScroll = 0.0f;
434 TaskPool.AddIfNotFound(
"PopulateTabWithItemList", items,
437 taskCancelSrc = taskCancelSrc.IsCancellationRequested ?
new CancellationTokenSource() : taskCancelSrc;
438 itemListBox.ClearChildren();
439 var workshopItems = ((Task<ISet<Steamworks.Ugc.Item>>)t).Result;
440 foreach (var workshopItem
in workshopItems)
442 var itemFrame =
new GUIFrame(
443 new RectTransform((1.0f, 1.0f / 5.5f), itemListBox.Content.RectTransform),
444 style:
"ListBoxElement")
446 UserData = (ItemOrPackage)workshopItem
448 var itemLayout =
new GUILayoutGroup(
449 new RectTransform(Vector2.One, itemFrame.RectTransform),
450 isHorizontal:
true, childAnchor:
Anchor.CenterLeft)
455 var thumbnailContainer
456 = CreateThumbnailContainer(itemLayout, Vector2.One,
ScaleBasis.BothHeight);
457 CreateItemThumbnail(workshopItem, taskCancelSrc.Token, thumbnailContainer);
458 thumbnailContainer.CanBeFocused =
false;
459 thumbnailContainer.GetAllChildren().ForEach(c => c.CanBeFocused =
false);
461 var title =
new GUITextBlock(
462 new RectTransform(Vector2.One, itemLayout.RectTransform),
463 workshopItem.Title ??
"", font: GUIStyle.Font)
468 if (includeSubscribeButton)
470 CreateSubscribeButton(workshopItem,
new RectTransform(Vector2.One, itemLayout.RectTransform, scaleBasis:
ScaleBasis.BothHeight), spriteScale: 0.4f);
472 itemLayout.Recalculate();
474 onFill?.Invoke(workshopItems);
478 private GUIFrame CreateThumbnailContainer(
480 Vector2 relativeSize,
481 ScaleBasis scaleBasis)
482 =>
new GUIFrame(
new RectTransform(relativeSize, parent.RectTransform, scaleBasis: scaleBasis),
483 style:
"GUIFrameListBox");
485 private SteamManager.Workshop.ItemThumbnail CreateItemThumbnail(
486 in Steamworks.Ugc.Item workshopItem,
487 CancellationToken cancellationToken,
488 GUIFrame thumbnailContainer)
490 var thumbnail =
new SteamManager.Workshop.ItemThumbnail(workshopItem, cancellationToken);
491 itemThumbnails.Add(thumbnail);
492 CreateAsyncThumbnailComponent(thumbnailContainer, () => thumbnail.Texture, () => thumbnail.Loading);
496 private GUICustomComponent CreateAsyncThumbnailComponent(GUIFrame thumbnailContainer, Func<Texture2D?> textureGetter, Func<bool> throbberEnabled)
498 int randomThrobberOffset = Rand.Range(0, 10, Rand.RandSync.Unsynced);
499 return new GUICustomComponent(
500 new RectTransform(Vector2.One, thumbnailContainer.RectTransform,
Anchor.Center),
501 onDraw: (spriteBatch, component) =>
503 Rectangle rect = component.Rect;
504 Texture2D? texture = textureGetter();
507 rect.Location += (4, 4);
509 Point destinationSizeMaxWidth = (rect.Width, rect.Width * texture.Height / texture.Width);
510 Point destinationSizeMaxHeight = (rect.Height * texture.Width / texture.Height, rect.Height);
511 Point destinationSize = destinationSizeMaxHeight.X > rect.Width
512 ? destinationSizeMaxWidth
513 : destinationSizeMaxHeight;
514 Rectangle destinationRectangle = new Rectangle(
515 rect.Center.X - destinationSize.X / 2,
516 rect.Center.Y - destinationSize.Y / 2,
519 spriteBatch.Draw(texture, destinationRectangle, Color.White);
521 else if (throbberEnabled())
523 var sheet = GUIStyle.GenericThrobber;
524 Vector2 pos = rect.Center.ToVector2() - Vector2.One * rect.Height * 0.4f;
525 sheet.Draw(spriteBatch, ((
int)Math.Floor(Timing.TotalTime * 24.0f) + randomThrobberOffset) % sheet.FrameCount, pos, Color.White,
526 origin: Vector2.Zero, rotate: 0.0f,
527 scale: Vector2.One * component.Rect.Height / sheet.FrameSize.ToVector2() * 0.8f);
532 private GUIListBox CreateTagsList(IEnumerable<Identifier> tags, RectTransform rectT,
bool canBeFocused)
535 =
new GUIListBox(rectT, style:
null, isHorizontal:
false)
537 UseGridLayout =
true,
538 ScrollBarEnabled =
false,
539 ScrollBarVisible =
false,
540 HideChildrenOutsideFrame =
false,
541 Spacing = GUI.IntScale(4)
543 tagsList.Content.ClampMouseRectToParent =
false;
544 foreach (Identifier tag
in tags)
546 var tagBtn =
new GUIButton(
547 new RectTransform(
new Vector2(0.25f, 1.0f / 8.0f), tagsList.Content.RectTransform,
549 TextManager.Get($
"workshop.contenttag.{tag.Value.RemoveWhitespace()}")
550 .Fallback(tag.Value.CapitaliseFirstInvariant()), style:
"GUIButtonRound")
552 CanBeFocused = canBeFocused,
556 tagBtn.RectTransform.NonScaledSize
557 = tagBtn.Font.MeasureString(tagBtn.Text).ToPoint() +
new Point(GUI.IntScale(15), GUI.IntScale(5));
558 tagBtn.RectTransform.IsFixedSize =
true;
559 tagBtn.ClampMouseRectToParent =
false;
565 private void PopulateFrameWithItemInfo(Steamworks.Ugc.Item workshopItem, GUIFrame parentFrame)
567 ViewingItemDetails =
true;
568 taskCancelSrc = taskCancelSrc.IsCancellationRequested ?
new CancellationTokenSource() : taskCancelSrc;
571 = ContentPackageManager.WorkshopPackages.FirstOrDefault(p =>
572 p.TryExtractSteamWorkshopId(out var workshopId)
573 && workshopId.Value == workshopItem.Id);
575 var verticalLayout =
new GUILayoutGroup(
new RectTransform(Vector2.One, parentFrame.RectTransform));
577 var headerLayout =
new GUILayoutGroup(
new RectTransform((1.0f, 0.1f), verticalLayout.RectTransform),
578 isHorizontal:
true) { Stretch =
true };
580 var titleAndAuthorLayout =
new GUILayoutGroup(
new RectTransform(Vector2.One, headerLayout.RectTransform));
583 new GUITextBlock(
new RectTransform((1.0f, 0.5f), titleAndAuthorLayout.RectTransform), workshopItem.Title ??
"",
584 font: GUIStyle.LargeFont);
586 var author = workshopItem.Owner;
587 var authorButton =
new GUIButton(
new RectTransform((1.0f, 0.5f),
588 titleAndAuthorLayout.RectTransform),
590 textAlignment: Alignment.CenterLeft)
593 Font = GUIStyle.SubHeadingFont,
594 TextColor = GUIStyle.TextColorNormal,
595 HoverTextColor = Color.White,
596 SelectedTextColor = GUIStyle.TextColorNormal,
597 OnClicked = (button, o) =>
599 SteamManager.OverlayCustomUrl(
600 $
"https://steamcommunity.com/profiles/{author.Id}/myworkshopfiles/?appid={SteamManager.AppID}");
604 var authorPadding = authorButton.GetChild<GUITextBlock>().Padding;
606 RectTransform rightSideButtonRectT()
607 =>
new RectTransform(Vector2.One, headerLayout.RectTransform, scaleBasis:
ScaleBasis.BothHeight);
609 bool reinstallAction(GUIButton button,
object o)
611 SettingsMenu.Instance?.ApplyInstalledModChanges();
612 int prevIndex = ContentPackageManager.EnabledPackages.Regular.IndexOf(contentPackage);
613 TaskPool.AddIfNotFound($
"Reinstall{workshopItem.Id}",
614 SteamManager.Workshop.Reinstall(workshopItem), t =>
616 ContentPackageManager.WorkshopPackages.Refresh();
617 ContentPackageManager.EnabledPackages.RefreshUpdatedMods();
618 if (SettingsMenu.Instance?.WorkshopMenu is MutableWorkshopMenu mutableWorkshopMenu && !mutableWorkshopMenu.ViewingItemDetails)
620 mutableWorkshopMenu.PopulateInstalledModLists(forceRefreshEnabled: true);
626 var (updateButton, updateSprite) = CreatePaddedButton(
627 rightSideButtonRectT(),
630 updateButton.ToolTip = TextManager.Get(
"WorkshopItemUpdate");
631 updateButton.Visible =
false;
632 updateButton.OnClicked = reinstallAction;
634 if (contentPackage !=
null)
636 TaskPool.AddIfNotFound(
637 $
"DetermineUpdateRequired{contentPackage.UgcId}",
638 contentPackage.IsUpToDate(),
641 if (!t.TryGetResult(out bool isUpToDate)) { return; }
643 updateButton.Visible = !isUpToDate;
647 var (reinstallButton, reinstallSprite) = CreatePaddedButton(
648 rightSideButtonRectT(),
651 reinstallButton.ToolTip = TextManager.Get(
"WorkshopItemReinstall");
652 reinstallButton.OnClicked = reinstallAction;
653 var reinstallButtonUpdater =
new GUICustomComponent(
654 new RectTransform(Vector2.Zero, reinstallButton.RectTransform),
655 onUpdate: (f, component) =>
657 reinstallButton.Visible = workshopItem.IsSubscribed
658 || workshopItem.Owner.Id == SteamManager.GetSteamId().Select(steamId => steamId.Value).Fallback(0);
659 reinstallButton.Enabled = !workshopItem.IsDownloading && !workshopItem.IsDownloadPending
660 && !SteamManager.Workshop.IsInstalling(workshopItem);
662 reinstallSprite.Color = reinstallButton.Enabled
663 ? reinstallSprite.Style.Color
665 updateButton.Enabled = reinstallButton.Enabled && contentPackage != null && ContentPackageManager.WorkshopPackages.Contains(contentPackage);
666 updateSprite.Color = reinstallSprite.Color;
668 if (contentPackage != null
669 && !ContentPackageManager.WorkshopPackages.Contains(contentPackage)
670 && ContentPackageManager.WorkshopPackages.Any(p =>
671 p.TryExtractSteamWorkshopId(out var workshopId)
672 && workshopId.Value == workshopItem.Id))
674 updateButton.Visible = false;
677 CreateSubscribeButton(workshopItem,
678 rightSideButtonRectT(),
681 var padding =
new GUIFrame(
682 new RectTransform((0.15f, 1.0f), headerLayout.RectTransform, scaleBasis:
ScaleBasis.BothHeight),
685 padding =
new GUIFrame(
new RectTransform((1.0f, 0.015f), verticalLayout.RectTransform), style:
null);
687 var horizontalLayout =
new GUILayoutGroup(
new RectTransform((1.0f, 0.45f), verticalLayout.RectTransform),
693 TaskPool.Add($
"Request username for {author.Id}", author.RequestInfoAsync(), (t) =>
695 authorButton.Text = author.Name ??
"";
696 authorButton.RectTransform.NonScaledSize =
697 ((int)(authorButton.Font.MeasureString(author.Name ??
"").X + authorPadding.X + authorPadding.Z),
698 authorButton.RectTransform.NonScaledSize.Y);
701 var thumbnailSuperContainer =
new GUIFrame(
702 new RectTransform(Vector2.One, horizontalLayout.RectTransform, scaleBasis:
ScaleBasis.BothHeight),
704 GUIFrame thumbnailContainer = CreateThumbnailContainer(thumbnailSuperContainer, Vector2.One,
706 CreateItemThumbnail(workshopItem, taskCancelSrc.Token, thumbnailContainer);
707 thumbnailContainer.RectTransform.Anchor =
Anchor.Center;
708 thumbnailContainer.RectTransform.Pivot =
Pivot.Center;
710 var statsBox =
new GUIFrame(
new RectTransform((0.6f, 1.0f), horizontalLayout.RectTransform),
711 style:
"GUIFrameListBox");
714 var statsHorizontalLayout =
new GUILayoutGroup(
new RectTransform(Vector2.One, statsBox.RectTransform), isHorizontal:
true);
716 =
new GUILayoutGroup(
new RectTransform((1.0f, 1.0f), statsHorizontalLayout.RectTransform), childAnchor:
Anchor.TopCenter);
720 var scoreFrame =
new GUIFrame(
new RectTransform((1.0f, 0.12f), statsVertical0.RectTransform), style:
null);
721 var scoreLabel =
new GUITextBlock(
new RectTransform((0.4f, 1.0f), scoreFrame.RectTransform),
722 TextManager.Get(
"WorkshopItemScore"), font: GUIStyle.SubHeadingFont);
723 var scoreStarContainer
724 =
new GUILayoutGroup(
725 new RectTransform((0.6f, 1.0f), scoreFrame.RectTransform,
Anchor.CenterRight),
727 childAnchor:
Anchor.CenterLeft) { Stretch =
true };
728 var starColor = Color.Lerp(
729 Color.Lerp(Color.White, Color.Yellow, Math.Min(workshopItem.Score * 2.0f, 1.0f)),
730 Color.Lime, Math.Max(0.0f, (workshopItem.Score - 0.5f) * 2.0f));
731 for (
int i = 0; i < 5; i++)
733 bool isStarLit = i <= Round(workshopItem.Score * 5.0f);
734 var star =
new GUIFrame(
new RectTransform(Vector2.One, scoreStarContainer.RectTransform, scaleBasis:
ScaleBasis.BothHeight),
735 style: isStarLit ?
"GUIStarIconBright" :
"GUIStarIconDark");
738 star.Color = starColor;
739 star.HoverColor = starColor;
740 star.SelectedColor = starColor;
743 var scoreTextPadding =
new GUIFrame(
new RectTransform((0.5f, 1.0f), scoreStarContainer.RectTransform, scaleBasis:
ScaleBasis.BothHeight),
746 var scoreTextContainer =
new GUIFrame(
new RectTransform(Vector2.One, scoreStarContainer.RectTransform),
749 var scoreVoteCount =
new GUITextBlock(
750 new RectTransform((1.0f, 1.5f), scoreTextContainer.RectTransform,
Anchor.Center),
751 TextManager.GetWithVariable(
"WorkshopItemVotes",
"[VoteCount]",
752 (workshopItem.VotesUp + workshopItem.VotesDown).ToString()), textAlignment: Alignment.BottomLeft)
754 Padding = Vector4.Zero
756 var subscriptionCount =
new GUITextBlock(
757 new RectTransform((1.0f, 1.5f), scoreTextContainer.RectTransform,
Anchor.Center),
758 TextManager.GetWithVariable(
"WorkshopItemSubscriptions",
"[SubscriptionCount]",
759 workshopItem.NumUniqueSubscriptions.ToString()), textAlignment: Alignment.TopLeft)
761 Padding = Vector4.Zero
764 void statFrame(LocalizedString labelText, LocalizedString dataText)
766 var frame =
new GUIFrame(
new RectTransform((1.0f, 0.12f), statsVertical0!.RectTransform), style:
null);
767 var label =
new GUITextBlock(
new RectTransform((0.4f, 1.0f), frame.RectTransform),
768 labelText, font: GUIStyle.SubHeadingFont);
769 var data =
new GUITextBlock(
new RectTransform((0.6f, 1.0f), frame.RectTransform,
Anchor.CenterRight),
770 dataText, font: GUIStyle.Font)
772 Padding = Vector4.Zero
776 statFrame(TextManager.Get(
"WorkshopItemFileSize"), MathUtils.GetBytesReadable(workshopItem.SizeOfFileInBytes));
777 statFrame(TextManager.Get(
"WorkshopItemCreationDate"), workshopItem.Created.ToShortDateString());
778 statFrame(TextManager.Get(
"WorkshopItemModificationDate"), workshopItem.Updated.ToShortDateString());
780 var tagsLabel =
new GUITextBlock(
new RectTransform((1.0f, 0.12f), statsVertical0.RectTransform),
781 TextManager.Get(
"WorkshopItemTags"), font: GUIStyle.SubHeadingFont);
782 CreateTagsList((workshopItem.Tags ?? Array.Empty<
string>()).ToIdentifiers(),
new RectTransform((0.97f, 0.3f), statsVertical0.RectTransform), canBeFocused:
false);
785 var descriptionListBox =
new GUIListBox(
new RectTransform((1.0f, 0.38f), verticalLayout.RectTransform));
786 CreateBBCodeElement(workshopItem, descriptionListBox);
788 var showInSteamContainer
789 =
new GUIFrame(
new RectTransform((1.0f, 0.05f), verticalLayout.RectTransform), style:
null);
790 CreateShowInSteamButton(workshopItem,
new RectTransform((0.2f, 1.0f), showInSteamContainer.RectTransform,
Anchor.CenterRight));