using System;
using UnityEditor;
using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.Events;
namespace Unity.Tutorials.Core.Editor
{
///
/// A generic event for signaling changes in a tutorial container.
/// Parameters: sender.
///
[Serializable]
public class TutorialContainerEvent : UnityEvent
{
}
///
/// A tutorial container is a collection of tutorial content, and is used to access the actual tutorials in the project.
///
///
/// A tutorial container can be two things:
/// 1. Tutorial project (null Parent): a root container which is the entry point for tutorial content in the project.
/// 2. Tutorial category (non-null Parent): a set of tutorials that are a part of some other container
///
public class TutorialContainer : ScriptableObject
{
///
/// Raised when any TutorialContainer is modified.
///
///
/// Raised before Modified event.
///
public static TutorialContainerEvent TutorialContainerModified = new TutorialContainerEvent();
///
/// Raised when any field of this container is modified.
///
///
/// If 'this' container is parented, we consider modifications to 'this' container also to be modifications of the parent.
///
public TutorialContainerEvent Modified;
///
/// By setting another container as a parent, this container becomes a tutorial category in the parent container.
///
[Tooltip("By setting another container as a parent, this container becomes a tutorial category in the parent container.")]
public TutorialContainer ParentContainer;
///
/// This value determines the position of a container / container card within a container, if this container is shown as a card.
///
[Tooltip("This value determines the position of a container / container card within a container, if this container is shown as a card.")]
public int OrderInView;
///
/// Background texture for the card/header.
///
[FormerlySerializedAs("HeaderBackground")]
public Texture2D BackgroundImage;
///
/// Title shown in the card/header.
///
[Tooltip("Title shown in the card/header.")]
public LocalizableString Title;
///
/// Subtitle shown in the container card and header area.
///
[Tooltip("Subtitle shown in the card/header.")]
public LocalizableString Subtitle;
///
/// Used as the tooltip for the container card.
///
[Tooltip("Used as the tooltip for the card.")]
public LocalizableString Description;
///
/// Can be used to override or disable (the default behavior) the default project layout specified by the Tutorial Framework.
///
[Tooltip("Can be used to override or disable (the default behavior) the default project layout specified by the Tutorial Framework.")]
public UnityEngine.Object ProjectLayout;
///
/// Sections (tutorial or link card) of this container.
///
#if UNITY_2020_2_OR_NEWER
[NonReorderable] // reordering freely would be problematic and is disallowed for now
#endif
public Section[] Sections = {};
///
/// Returns the path for the ProjectLayout, relative to the project folder,
/// or a default tutorial layout path if ProjectLayout not specified.
///
public string ProjectLayoutPath =>
ProjectLayout != null ? AssetDatabase.GetAssetPath(ProjectLayout) : k_DefaultLayoutPath;
// The default layout used when a project is started for the first time, if project layout is used.
internal static readonly string k_DefaultLayoutPath =
"Packages/com.unity.learn.iet-framework/Editor/DefaultAssets/DefaultLayout.wlt";
///
/// A section/card for starting a tutorial or opening a web page.
///
[Serializable]
public class Section
{
///
/// This value determines the position of a section / section card within a container.
///
[Tooltip("This value determines the position of a section / section card within a container.")]
public int OrderInView;
///
/// Title of the card.
///
public LocalizableString Heading;
///
/// Description of the card.
///
public LocalizableString Text;
///
/// Used as content type metadata for external references/URLs
///
[Tooltip("Used as content type metadata for external references/URLs"), FormerlySerializedAs("LinkText")]
public string Metadata;
///
/// The URL of this section.
/// Setting the URL will take precedence and make the card act as a link card instead of a tutorial card
///
[Tooltip("Setting the URL will take precedence and make the card act as a link card instead of a tutorial card")]
public string Url;
///
/// Image for the card.
///
public Texture2D Image;
///
/// The tutorial this container contains
///
public Tutorial Tutorial;
///
/// Does this represent a tutorial?
///
public bool IsTutorial => Url.IsNullOrEmpty();
///
/// The ID of the represented tutorial, if any
///
public string TutorialId => Tutorial?.LessonId.AsEmptyIfNull();
///
/// Starts the tutorial of the section
///
public void StartTutorial()
{
TutorialManager.Instance.StartTutorial(Tutorial);
}
///
/// Opens the URL Of the section, if any
///
public void OpenUrl()
{
// TODO by making a static OpenUrl(string url) utility function we can easily track rich text hyperlink clicks also
TutorialEditorUtils.OpenUrl(Url);
AnalyticsHelper.SendExternalReferenceEvent(Url, Heading.Untranslated, Metadata, Tutorial?.LessonId);
}
// TODO Managing tutorials' completion states feels something that Tutorial and TutorialManager classes should be responsible of.
///
/// Has the tutorial already been completed?
///
internal bool TutorialCompleted { get; set; }
internal string SessionStateKey => $"Unity.Tutorials.Core.Editor.lesson{TutorialId}";
///
/// Loads the state of the section from SessionState.
///
/// returns true if the state was found from EditorPrefs
internal bool LoadState()
{
const string nonexisting = "NONEXISTING";
var state = SessionState.GetString(SessionStateKey, nonexisting);
if (state == "")
{
TutorialCompleted = false;
}
else if (state == "Finished")
{
TutorialCompleted = true;
}
return state != nonexisting;
}
///
/// Saves the state of the section from SessionState.
///
internal void SaveState()
{
SessionState.SetString(SessionStateKey, TutorialCompleted ? "Finished" : "");
}
}
void OnValidate()
{
Title = POFileUtils.SanitizeString(Title);
Subtitle = POFileUtils.SanitizeString(Subtitle);
Description = POFileUtils.SanitizeString(Description);
foreach (var section in Sections)
{
section.Heading = POFileUtils.SanitizeString(section.Heading);
section.Text = POFileUtils.SanitizeString(section.Text);
}
Array.Sort(Sections, (x, y) => x.OrderInView.CompareTo(y.OrderInView));
}
///
/// Loads the tutorial project layout
///
public void LoadTutorialProjectLayout()
{
TutorialManager.LoadWindowLayoutWorkingCopy(ProjectLayoutPath);
}
///
/// Raises the Modified events for this asset.
///
public void RaiseModified()
{
TutorialContainerModified?.Invoke(this);
Modified?.Invoke(this);
}
}
}