using System; using System.Collections; using System.Collections.Generic; using System.Linq; using Unity.EditorCoroutines.Editor; using UnityEngine.UIElements; using static Unity.Tutorials.Core.Editor.TutorialContainer; namespace Unity.Tutorials.Core.Editor { internal class TableOfContentView : View { internal const string k_Name = "TableOfContent"; internal override string Name => k_Name; VisualElement m_Root; VisualElement m_TutorialsContainer; internal int CategoriesOrTutorialsCurrentlyVisibile => m_TutorialsContainer.childCount; TableOfContentModel Model => Application.Model.TableOfContent; List> m_ActiveSections; bool m_SectionsInitialized = false; EditorCoroutine m_CheckmarksUpdateRoutine; private EditorCoroutine m_CategoryStateLoadingRoutine; private bool m_CategoriesInitialized; List> m_CategoryAwaitingStateUpdate; public TableOfContentView() : base() { } internal void Initialize(VisualElement root) { m_Root = root; m_TutorialsContainer = m_Root.Q("lstTutorials"); m_TutorialsContainer.style.alignItems = Align.Center; m_ActiveSections = new List>(); Refresh(); } internal void Refresh() { m_TutorialsContainer.Clear(); LoadHeader(); LoadCategories(); LoadTutorialsAndLinks(); } void GoBackInContainerHierachy() { Application.Broadcast(new BackButtonClickedEvent()); } void LoadHeader() { VisualElement imgTitleHeader = m_Root.Q("imgTitleHeader"); TutorialContainer currentCategory = Model.CurrentCategory; string subtitle = string.Empty; string title = string.Empty; if (currentCategory) { subtitle = currentCategory.Subtitle.Value; title = currentCategory.Title.Value; imgTitleHeader.style.backgroundImage = currentCategory.BackgroundImage; } else { title = Localization.Tr(LocalizationKeys.k_TOCLabelTitle); subtitle = Localization.Tr(LocalizationKeys.k_TOCLabelSubtitle); imgTitleHeader.style.backgroundImage = null; } UIElementsUtils.SetupLabel("lblTitle", title, imgTitleHeader, false); UIElementsUtils.SetupLabel("lblSubtitle", subtitle, imgTitleHeader, false); bool enableBackButton = Model.CurrentCategory && (Model.CurrentCategory.ParentContainer || Model.RootCategoriesOfProject.Count() > 1); UIElementsUtils.SetupButton("btnExitCategory", GoBackInContainerHierachy, enableBackButton, imgTitleHeader, string.Empty, Localization.Tr(LocalizationKeys.k_TOCButtonBackTooltip)); } void LoadCategories() { IEnumerable categoriesToLoad = Model.CurrentCategory == null ? Model.RootCategoriesOfProject : Model.CurrentCategory.FindSubCategories(); if (categoriesToLoad == null) { return; } //sorting category by order in view categoriesToLoad = categoriesToLoad.OrderBy(container => container.OrderInView); m_CategoriesInitialized = false; m_CategoryAwaitingStateUpdate = new(); Application.StopAndNullifyEditorCoroutine(ref m_CategoryStateLoadingRoutine); m_CategoryStateLoadingRoutine = EditorCoroutineUtility.StartCoroutine(UpdateTutorialsStateFetched(), Application); VisualTreeAsset tutorialCategoryUIPrefab = UIElementsUtils.LoadUXML("TutorialCategoryUI"); VisualElement categoryUI; foreach (var category in categoriesToLoad) { categoryUI = tutorialCategoryUIPrefab.CloneTree(); SetupCategoryUI(categoryUI, category); m_TutorialsContainer.Add(categoryUI); } m_CategoriesInitialized = true; } internal void SetupSectionUI(VisualElement sectionUI, Section data) { UIElementsUtils.SetupLabel("lblName", data.Heading, sectionUI, false); UIElementsUtils.SetupLabel("lblDescription", data.Text, sectionUI, false); if (data.Image != null) { sectionUI.Q("TutorialImage").style.backgroundImage = Background.FromTexture2D(data.Image); } sectionUI.UnregisterCallback(OnSectionClicked); UIElementsUtils.ShowOrHide("imgErrorCheckmark", sectionUI, !data.IsConfiguredCorrectly); if (data.IsConfiguredCorrectly) { sectionUI.tooltip = Localization.Tr(LocalizationKeys.k_TutorialSectionTooltip) + data.Text; if (data.IsTutorial) { UIElementsUtils.Show("lblCompletionStatus", sectionUI); UpdateCheckmark(sectionUI, data); } else { UIElementsUtils.Hide("lblCompletionStatus", sectionUI); UIElementsUtils.Hide("imgCheckmark", sectionUI); } sectionUI.RegisterCallback(OnSectionClicked, data); return; } sectionUI.tooltip = Localization.Tr(LocalizationKeys.k_TutorialLabelParseError); UIElementsUtils.Hide("lblCompletionStatus", sectionUI); UIElementsUtils.Hide("imgCheckmark", sectionUI); } internal void SetupCategoryUI(VisualElement categoryUI, TutorialContainer data) { UIElementsUtils.SetupLabel("lblName", data.Title, categoryUI, false); UIElementsUtils.SetupLabel("lblDescription", data.Subtitle, categoryUI, false); InitCompletionUI(categoryUI, data); m_CategoryAwaitingStateUpdate.Add(new Tuple(categoryUI, data)); categoryUI.tooltip = data.Description; if (data.BackgroundImage != null) { categoryUI.Q("TutorialImage").style.backgroundImage = Background.FromTexture2D(data.BackgroundImage); } categoryUI.RegisterCallback((MouseUpEvent evt) => OnCategoryClicked(evt, data)); } void InitCompletionUI(VisualElement categoryUI, TutorialContainer container) { var label = categoryUI.Q