Editor/Window/Templates/DeploymentStepTemplate.cs (182 lines of code) (raw):

// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 using UnityEngine; using UnityEngine.UIElements; using System; using Castle.Core.Internal; namespace AmazonGameLift.Editor { /** * Wrapper of points of interest in a deployment step based on the 'DeploymentStep.uxml' * Recommendation is to use the Builder and the uxml template together. */ public class DeploymentStepTemplate : StatefulInput { public const string BaseButtonProceed = "ProceedButton"; public const string BaseButtonTryAgain = "TryAgainButton"; public const string BaseButtonViewLogs = "ViewLogButton"; private const string LinkUxmlTemplate = "EditorWindow/Templates/DeploymentStepHelpLink"; private const string DividerClassName = "divider--vertical"; public StatusBox StatusBox { get; private set; } /** * Container that stores the template buttons if the feature was enabled by the Builder. * If neither 'WithBaseButtons' or 'WithoutBaseButtons' was used, this will be 'null'. */ public VisualElement ButtonContainer { get; private set; } /** * Container that stores the child content of the template. */ public VisualElement ContentContainer { get; private set; } private VisualElement _container; internal DeploymentStepTemplate(StatusBox statusBox, VisualElement buttonContainer, VisualElement contentContainer, VisualElement container) { StatusBox = statusBox; ButtonContainer = buttonContainer; ContentContainer = contentContainer; _container = container; } public void UpdateTitle(string titleKey) { var l = new ElementLocalizer(_container); l.SetElementText("StepTitle", titleKey); } private static void InitializeHelpLinks(VisualElement linkCollection, DeploymentStepTemplateLink[] links) { if (links.Length == 0) { Hide(linkCollection); return; } linkCollection.Clear(); Show(linkCollection); var linkTemplate = Resources.Load<VisualTreeAsset>(LinkUxmlTemplate); bool first = true; foreach (var link in links) { if (!first) { var divider = new VisualElement(); divider.AddToClassList(DividerClassName); linkCollection.Add(divider); } var linkUxml = linkTemplate.Instantiate(); var l = new ElementLocalizer(linkUxml); linkUxml.RegisterCallback<ClickEvent>(_ => Application.OpenURL(link._linkUrl)); l.SetElementText("LinkLabel", link._linkLabelKey); linkCollection.Add(linkUxml); first = false; } } private static T TryGetElement<T>(VisualElement container, string elementName, string feature = "This feature") where T : VisualElement { var element = container.Q<T>(elementName); if (element == null) { throw new InvalidOperationException($"{feature} is not supported because could not find '{elementName}' element."); } return element; } private static void EnsureClear(VisualElement container, string elementName) { var element = container.Q<VisualElement>(elementName); if (element != null) { Hide(element); element.Clear(); } } protected override void UpdateGUI() { throw new System.NotImplementedException(); } /** * Provides integration for the features setup in the 'DeploymentStep.uxml' template file. */ public class Builder { private const string HelpLinksFeature = "Help links template feature"; private const string ButtonsFeature = "Action buttons template feature"; private const string ElementNameLinkCollection = "LinkCollection"; private const string ElementNameButtonCollection = "ButtonCollection"; private const string ElementNameStepContent = "StepContent"; private string _titleKey; private string _descriptionKey; private bool _hasContent = true; private DeploymentStepTemplateLink[] _helpLinks; private ButtonUsage _buttonUsage = ButtonUsage.Unsupported; private enum ButtonUsage { WithBaseButtons, WithNoButtons, Unsupported, } public Builder(string titleKey, string descriptionKey) { _titleKey = titleKey; _descriptionKey = descriptionKey; } /** * Hides the content container in the template for use cases where no additional content is needed. * Without this, there will be extra spacing as the VisualElement will be visible but empty. * Note: This clears out the content container as well as hiding it. */ public Builder WithNoContent() { _hasContent = false; return this; } /** * Enables support for the template's button container and leaves it populated with some commonly * used buttons: Proceed, Try again, View logs. * Note: These buttons will be visible initially. * Note: Conflicts with 'WithoutBaseButtons'. Whichever is called last takes precendence. */ public Builder WithBaseButtons() { _buttonUsage = ButtonUsage.WithBaseButtons; return this; } /** * Enables support for the template's button container, but DOES NOT populate with any buttons. * Use this when the base buttons aren't needed, but some custom buttons will be. * Note: This also hides the button container so that it doesn't introduce additional spacing. * Note: Conflicts with 'WithBaseButtons'. Whichever is called last takes precendence. */ public Builder WithoutBaseButtons() { _buttonUsage = ButtonUsage.WithNoButtons; return this; } /** * Populates the help link section in the template. The links are inserted in the order provided. */ public Builder WithHelpLinks(params DeploymentStepTemplateLink[] links) { _helpLinks = links; return this; } /** * Builds the current set of features into the provided container and provides a wrapper with * properties to access useful pieces of that container. * Throws exceptions when features are enabled but their containers are missing. */ public DeploymentStepTemplate Build(VisualElement container) { if (!_hasContent) { EnsureClear(container, ElementNameStepContent); } LocalizeText(container); BuildLinks(container); var buttonContainer = PrepareButtonContainer(container); var statusBox = container.Q<StatusBox>(); var contentContainer = container.Q<VisualElement>(ElementNameStepContent); return new DeploymentStepTemplate(statusBox, buttonContainer, contentContainer, container); } private void LocalizeText(VisualElement container) { var l = new ElementLocalizer(container); l.SetElementText("StepTitle", _titleKey); if (!_descriptionKey.IsNullOrEmpty()) { l.SetElementText("StepDescription", _descriptionKey); } else { Hide(container.Q("StepDescription")); } } private void BuildLinks(VisualElement container) { if (_helpLinks == null || _helpLinks.Length == 0) { EnsureClear(container, ElementNameLinkCollection); return; } var linkCollection = TryGetElement<VisualElement>(container, ElementNameLinkCollection, HelpLinksFeature); InitializeHelpLinks(linkCollection, _helpLinks); } private VisualElement PrepareButtonContainer(VisualElement container) { if (_buttonUsage == ButtonUsage.Unsupported) { return null; } if (_buttonUsage == ButtonUsage.WithNoButtons) { EnsureClear(container, ElementNameButtonCollection); } return TryGetElement<VisualElement>(container, ElementNameButtonCollection, ButtonsFeature); } } } public struct DeploymentStepTemplateLink { internal string _linkUrl; internal string _linkLabelKey; public DeploymentStepTemplateLink(string linkUrl, string linkLabelKey) { _linkUrl = linkUrl; _linkLabelKey = linkLabelKey; } } }