Initial Commit

This commit is contained in:
Sebastian Cabrera 2021-08-02 05:44:37 -04:00
parent 53eb92e9af
commit 270ab7d11f
15341 changed files with 700234 additions and 0 deletions

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 601cd9697b420cb4c9b311c42ea3881a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,19 @@
using System;
using UnityEngine.UIElements;
namespace Unity.Cloud.Collaborate.Views.Adapters {
internal interface IAdapter
{
int Height { get; }
Func<VisualElement> MakeItem { get; }
Action<VisualElement, int> BindItem { get; }
int GetEntryCount();
void RegisterObserver(IAdapterObserver observer);
void DeregisterObserver(IAdapterObserver observer);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 05643fe94e7516e4b86f73e706833e49
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,9 @@
using System;
namespace Unity.Cloud.Collaborate.Views.Adapters
{
internal interface IAdapterObserver
{
void NotifyDataSetChanged();
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6797ef5b31abfd44993cf9870584620e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 664e0259f5e6a0146b29dc2f29a742f6
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using UnityEngine.UIElements;
namespace Unity.Cloud.Collaborate.Views.Adapters.ListAdapters
{
/// <summary>
/// Adapter used to provide entries to the AdapterListView. Allows the data to be kept separately to the layout
/// and visual elements.
/// </summary>
/// <typeparam name="T">Type of list element the adapter provides</typeparam>
internal abstract class BaseListAdapter<T> : IAdapter where T : VisualElement
{
readonly List<IAdapterObserver> m_AdapterObservers = new List<IAdapterObserver>();
#region PrivateInterfaceFields
Func<VisualElement> IAdapter.MakeItem => MakeItem;
Action<VisualElement, int> IAdapter.BindItem => (v, i) => BindItem((T)v, i);
#endregion
#region UserOverrides
/// <summary>
/// Provides the static height for each element.
/// </summary>
public abstract int Height { get; }
/// <summary>
/// Creates and returns the layout for the entry.
/// </summary>
/// <returns>Created entry layout.</returns>
protected abstract T MakeItem();
/// <summary>
/// Binds data to the entry at the given index.
/// </summary>
/// <param name="element">Entry to bind to.</param>
/// <param name="index">Index in the data.</param>
protected abstract void BindItem(T element, int index);
/// <summary>
/// Gets the count of the number of entries in the list.
/// </summary>
/// <returns>The entry count.</returns>
public abstract int GetEntryCount();
#endregion
/// <summary>
/// Register an observer for this adapter.
/// </summary>
/// <param name="observer">Observer to register.</param>
public void RegisterObserver(IAdapterObserver observer)
{
m_AdapterObservers.Add(observer);
}
/// <summary>
/// Deregister an observer for this adapter.
/// </summary>
/// <param name="observer">Observer to deregister.</param>
public void DeregisterObserver(IAdapterObserver observer)
{
m_AdapterObservers.Remove(observer);
}
/// <summary>
/// Notify that the data set in this adapter has changed.
/// </summary>
public void NotifyDataSetChanged()
{
foreach (var observer in m_AdapterObservers)
{
observer.NotifyDataSetChanged();
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6d5f9c23c4be9e644ae25c47f8b2299d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using Unity.Cloud.Collaborate.Assets;
using Unity.Cloud.Collaborate.Components.ChangeListEntries;
using Unity.Cloud.Collaborate.Models.Structures;
using Unity.Cloud.Collaborate.Presenters;
using UnityEngine.Assertions;
namespace Unity.Cloud.Collaborate.Views.Adapters.ListAdapters
{
internal class ConflictedChangeListAdapter : BaseListAdapter<ConflictedChangeListElement>
{
IChangesPresenter m_Presenter;
[CanBeNull]
IReadOnlyList<IChangeEntryData> m_List;
public IReadOnlyList<IChangeEntryData> List
{
set
{
m_List = value;
NotifyDataSetChanged();
}
}
public ConflictedChangeListAdapter([NotNull] IChangesPresenter presenter)
{
m_Presenter = presenter;
}
public override int Height { get; } = UiConstants.ChangesListViewItemHeight;
protected override ConflictedChangeListElement MakeItem()
{
return new ConflictedChangeListElement();
}
protected override void BindItem(ConflictedChangeListElement element, int index)
{
Assert.IsNotNull(m_List, "List should not be null at this point.");
element.ClearData();
var changesEntry = m_List[index];
var path = changesEntry.All ? StringAssets.all : changesEntry.Entry.Path;
element.UpdateFilePath(path);
// Update status icon
element.statusIcon.ClearClassList();
element.statusIcon.AddToClassList(BaseChangeListElement.IconUssClassName);
element.statusIcon.AddToClassList(ToggleableChangeListElement.StatusIconUssClassName);
element.statusIcon.AddToClassList(changesEntry.Entry.StatusToString());
// Wire up buttons
element.showButton.Clicked += () => m_Presenter.RequestShowConflictedDifferences(changesEntry.Entry.Path);
element.chooseMergeButton.Clicked += () => m_Presenter.RequestChooseMerge(changesEntry.Entry.Path);
element.chooseMineButton.Clicked += () => m_Presenter.RequestChooseMine(changesEntry.Entry.Path);
element.chooseRemoteButton.Clicked += () => m_Presenter.RequestChooseRemote(changesEntry.Entry.Path);
}
public override int GetEntryCount()
{
return m_List?.Count ?? 0;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 70522d02d20893a42b7cf30eb8221cce
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using Unity.Cloud.Collaborate.Assets;
using Unity.Cloud.Collaborate.Presenters;
using Unity.Cloud.Collaborate.Components.ChangeListEntries;
using Unity.Cloud.Collaborate.Models.Structures;
namespace Unity.Cloud.Collaborate.Views.Adapters.ListAdapters
{
internal class HistoryEntryChangeListAdapter : BaseListAdapter<HistoryChangeListElement>
{
string m_RevisionId;
IList<IChangeEntry> m_List;
readonly IHistoryPresenter m_Presenter;
public HistoryEntryChangeListAdapter([NotNull] IHistoryPresenter presenter, [NotNull] string revisionId, [NotNull] IList<IChangeEntry> list)
{
m_Presenter = presenter;
m_RevisionId = revisionId;
m_List = list;
}
public override int Height => UiConstants.HistoryListViewItemHeight;
protected override HistoryChangeListElement MakeItem()
{
return new HistoryChangeListElement();
}
protected override void BindItem(HistoryChangeListElement element, int index)
{
element.ClearData();
var entry = m_List[index];
element.UpdateFilePath(entry.Path);
// TODO: make status icon an object to handle this logic
element.statusIcon.ClearClassList();
element.statusIcon.AddToClassList(BaseChangeListElement.IconUssClassName);
element.statusIcon.AddToClassList(HistoryChangeListElement.StatusIconUssClassName);
element.statusIcon.AddToClassList(entry.StatusToString());
if (m_Presenter.SupportsRevert)
{
element.revertButton.Clicked += () => m_Presenter.RequestRevert(m_RevisionId, new List<string> { entry.Path });
}
else
{
element.revertButton.AddToClassList(UiConstants.ussHidden);
}
}
public override int GetEntryCount()
{
return m_List.Count;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 93a892c54d73e8f4ca210cdc2bf67a74
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,132 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using Unity.Cloud.Collaborate.Assets;
using Unity.Cloud.Collaborate.Components.ChangeListEntries;
using Unity.Cloud.Collaborate.Models.Structures;
using Unity.Cloud.Collaborate.Presenters;
using UnityEngine.Assertions;
namespace Unity.Cloud.Collaborate.Views.Adapters.ListAdapters
{
internal class ToggleableChangeListAdapter : BaseListAdapter<ToggleableChangeListElement>
{
[CanBeNull]
IReadOnlyList<IChangeEntryData> m_List;
public IReadOnlyList<IChangeEntryData> List
{
set
{
m_List = value;
NotifyDataSetChanged();
}
}
readonly IChangesPresenter m_Presenter;
int m_LastBoundElementIndex;
public ToggleableChangeListAdapter(IChangesPresenter presenter)
{
m_Presenter = presenter;
}
public override int Height { get; } = UiConstants.ChangesListViewItemHeight;
protected override ToggleableChangeListElement MakeItem()
{
return new ToggleableChangeListElement();
}
protected override void BindItem(ToggleableChangeListElement element, int index)
{
Assert.IsNotNull(m_List, "List should not be null at this point.");
m_LastBoundElementIndex = index;
element.ClearData();
var changesEntry = m_List[index];
var path = changesEntry.All ? StringAssets.all : changesEntry.Entry.Path;
element.UpdateFilePath(path);
// Setup callbacks
element.SetToggleCallback(c => OnItemToggleChanged(index, c));
element.diffButton.Clicked += () => OnDiffClicked(index);
element.discardButton.RemoveFromClassList(UiConstants.ussHidden);
element.discardButton.Clicked += () => OnDiscardClicked(index);
// Update the toggle and tooltips.
if (changesEntry.ToggleReadOnly)
{
element.toggle.SetValueWithoutNotify(true);
element.toggle.SetEnabled(false);
element.toggle.parent.tooltip = StringAssets.includedToPublishByAnotherGitTool;
}
else
{
element.toggle.SetValueWithoutNotify(changesEntry.Toggled);
element.toggle.SetEnabled(true);
element.toggle.parent.tooltip = string.Empty;
}
// Update the visibility of the icon and discard button.
if (changesEntry.All)
{
element.buttons.AddToClassList(UiConstants.ussHidden);
element.statusIcon.AddToClassList(UiConstants.ussHidden);
}
else
{
element.buttons.RemoveFromClassList(UiConstants.ussHidden);
// TODO: make status icon an object to handle this logic
element.statusIcon.ClearClassList();
element.statusIcon.AddToClassList(BaseChangeListElement.IconUssClassName);
element.statusIcon.AddToClassList(ToggleableChangeListElement.StatusIconUssClassName);
element.statusIcon.AddToClassList(changesEntry.Entry.StatusToString());
}
}
public override int GetEntryCount()
{
return m_List?.Count ?? 0;
}
void OnItemToggleChanged(int index, bool toggled)
{
Assert.IsNotNull(m_List, "List should not be null at this point.");
var changeEntry = m_List[index];
var refresh = m_Presenter.UpdateEntryToggle(changeEntry.Entry.Path, toggled);
if (refresh) NotifyDataSetChanged();
}
void OnDiscardClicked(int index)
{
Assert.IsNotNull(m_List, "List should not be null at this point.");
var changeEntry = m_List[index];
m_Presenter.RequestDiscard(changeEntry.Entry);
}
public int GetLastBoundElementIndex()
{
return m_LastBoundElementIndex;
}
public int GetFirstToggledIndex()
{
Assert.IsNotNull(m_List, "List should not be null at this point.");
for (var i=0; i < m_List.Count; i++)
{
if(m_List[i].Toggled)
{
return i;
}
}
return -1;
}
void OnDiffClicked(int index)
{
Assert.IsNotNull(m_List, "List should not be null at this point.");
var changeEntry = m_List[index];
m_Presenter.RequestDiffChanges(changeEntry.Entry.Path);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 928f8025717499a41802f8fe66420488
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,270 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using Unity.Cloud.Collaborate.Assets;
using Unity.Cloud.Collaborate.Views.Adapters.ListAdapters;
using Unity.Cloud.Collaborate.Components;
using Unity.Cloud.Collaborate.Models.Structures;
using Unity.Cloud.Collaborate.Presenters;
using Unity.Cloud.Collaborate.UserInterface;
using UnityEditor;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.UIElements;
namespace Unity.Cloud.Collaborate.Views
{
[UsedImplicitly]
internal class ChangesTabPageView : TabPageComponent, IChangesView
{
[CanBeNull]
IChangesPresenter m_Presenter;
public const string UssClassName = "changes-tab-page-view";
public const string SearchBarUssClassName = UssClassName + "__search-bar";
public const string EntryGroupsUssClassName = UssClassName + "__entry-groups";
public const string PublishButtonUssClassName = UssClassName + "__publish-button";
public const string TextFieldUssClassName = UssClassName + "__text-field";
public const string ListViewUssClassName = UssClassName + "__list-view";
static readonly string k_LayoutPath = $"{CollaborateWindow.LayoutPath}/{nameof(ChangesTabPageView)}.uxml";
static readonly string k_StylePath = $"{CollaborateWindow.StylePath}/{nameof(ChangesTabPageView)}.uss";
readonly IconTextButton m_PublishButton;
readonly BetterTextField m_RevisionSummaryBox;
readonly SearchBar m_SearchBar;
readonly VisualElement m_EntryGroupsContainer;
bool m_Active;
[CanBeNull]
ConflictedChangeListAdapter m_ConflictedChangeListAdapter;
[CanBeNull]
ToggleableChangeListAdapter m_ToggleableChangeListAdapter;
[CanBeNull]
ChangeEntryGroup m_EntryToggleableGroup;
[CanBeNull]
ChangeEntryGroup m_EntryConflictsGroup;
[CanBeNull]
VisualElement m_ActiveGroup;
public ChangesTabPageView()
{
AddToClassList(UssClassName);
AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(k_LayoutPath).CloneTree(this);
styleSheets.Add(AssetDatabase.LoadAssetAtPath<StyleSheet>(k_StylePath));
// Get the components defined in the style / layout files.
m_SearchBar = this.Q<SearchBar>(className: SearchBarUssClassName);
m_RevisionSummaryBox = this.Q<BetterTextField>(className: TextFieldUssClassName);
m_PublishButton = this.Q<IconTextButton>(className: PublishButtonUssClassName);
m_EntryGroupsContainer = this.Q<VisualElement>(className: EntryGroupsUssClassName);
// Initialize the text strings.
m_PublishButton.Text = StringAssets.publishButton;
m_RevisionSummaryBox.Placeholder = StringAssets.publishSummaryPlaceholder;
}
/// <inheritdoc />
public IChangesPresenter Presenter
{
set
{
m_Presenter = value;
SetupEvents();
// If tab active before presenter has been added, call start once we have it.
if (Active)
{
value.Start();
}
}
}
/// <summary>
/// Setup events to communicate with the presenter. Must be called after presenter is set.
/// </summary>
void SetupEvents()
{
Assert.IsNotNull(m_Presenter, "Invalid changes page state.");
// Set up publish invocation.
m_PublishButton.Clicked += m_Presenter.RequestPublish;
// Send text values to the presenter.
m_SearchBar.Search += m_Presenter.SetSearchQuery;
m_RevisionSummaryBox.OnValueChangedHandler += s => m_Presenter.SetRevisionSummary(s);
}
/// <inheritdoc />
public void SetBusyStatus(bool busy)
{
m_EntryGroupsContainer.SetEnabled(!busy);
m_RevisionSummaryBox.SetEnabled(!busy);
}
/// <inheritdoc />
protected override void SetActive()
{
Assert.IsFalse(m_Active, "The view is already active.");
m_Active = true;
m_Presenter?.Start();
}
/// <inheritdoc />
protected override void SetInactive()
{
Assert.IsTrue(m_Active, "The view is already inactive.");
m_Active = false;
m_Presenter?.Stop();
}
/// <inheritdoc />
public void SetSearchQuery(string query)
{
Assert.IsNotNull(m_Presenter, "Invalid state when setting search query.");
m_SearchBar.SetValueWithoutNotify(query);
var isSearching = m_Presenter.Searching;
if (m_EntryConflictsGroup != null) m_EntryConflictsGroup.Searching = isSearching;
if (m_EntryToggleableGroup != null) m_EntryToggleableGroup.Searching = isSearching;
}
/// <inheritdoc />
public void SetRevisionSummary(string message)
{
m_RevisionSummaryBox.SetValueWithoutNotify(message);
}
/// <inheritdoc />
public void SetConflicts(IReadOnlyList<IChangeEntryData> list)
{
Assert.IsNotNull(m_Presenter, "Invalid state while creating conflict list.");
// Initialise conflicts group
if (m_EntryConflictsGroup == null)
{
var conflictsList = new AdapterListView { name = StringAssets.changeListConflictedList, SelectionType = SelectionType.None };
conflictsList.AddToClassList(ListViewUssClassName);
m_ConflictedChangeListAdapter = new ConflictedChangeListAdapter(m_Presenter);
conflictsList.SetAdapter(m_ConflictedChangeListAdapter);
m_EntryConflictsGroup = new ChangeEntryGroup(conflictsList) { Title = StringAssets.changeListConflictedHeader };
m_EntryConflictsGroup.SetOverflowCallback(m_Presenter.OnClickConflictGroupOverflow);
m_EntryConflictsGroup.Searching = m_Presenter.Searching;
}
Assert.IsTrue(m_ConflictedChangeListAdapter != null && m_EntryConflictsGroup != null, "Invalid state while setting conflicted list.");
// Ensure conflict list is displayed
if (m_ActiveGroup != m_EntryConflictsGroup)
{
m_ActiveGroup?.RemoveFromHierarchy();
m_EntryGroupsContainer.Add(m_EntryConflictsGroup);
m_ActiveGroup = m_EntryConflictsGroup;
}
m_ConflictedChangeListAdapter.List = list;
var count = m_Presenter.ConflictedCount;
m_EntryConflictsGroup.NumberMenuItems = m_Presenter.ConflictGroupOverflowEntryCount;
m_EntryConflictsGroup.SelectedEntryCount = count;
m_EntryConflictsGroup.EntryCount = count;
}
/// <inheritdoc />
public void SetSelectedChanges()
{
Assert.IsNotNull(m_Presenter, "Invalid state while setting selected items from toggleable list.");
if(m_ToggleableChangeListAdapter == null)
{
// we might be Selecting partial changes before the view loads the first time,
// so we just ignore it ....
return;
}
Assert.IsTrue(m_ToggleableChangeListAdapter != null && m_EntryToggleableGroup != null, "Invalid state while setting selected items in toggleable list");
var scrollToIndex = m_ToggleableChangeListAdapter.GetFirstToggledIndex();
m_ToggleableChangeListAdapter.NotifyDataSetChanged();
if (scrollToIndex != -1)
{
scrollToIndex = Math.Min(scrollToIndex, m_ToggleableChangeListAdapter.GetEntryCount() - 1);
m_EntryToggleableGroup.ScrollTo(scrollToIndex);
if(m_ToggleableChangeListAdapter.GetLastBoundElementIndex() < scrollToIndex + 3)
{
// the pool of the list is 14 elements .. but the list actually shows only 12 ..
// so the normal scrollTo call of the list view may stop 1 element short of the selected
// index if the scrolled to index is greater than the currently selected index.
m_EntryToggleableGroup.ScrollTo(scrollToIndex + 3);
}
}
}
/// <inheritdoc />
public void SetChanges(IReadOnlyList<IChangeEntryData> list)
{
Assert.IsNotNull(m_Presenter, "Invalid state while creating toggleable list.");
// Initialise the toggleable list if not already initialised.
if (m_EntryToggleableGroup == null)
{
var toggleableListView = new AdapterListView { SelectionType = SelectionType.None };
toggleableListView.AddToClassList(ListViewUssClassName);
m_ToggleableChangeListAdapter = new ToggleableChangeListAdapter(m_Presenter);
toggleableListView.SetAdapter(m_ToggleableChangeListAdapter);
m_EntryToggleableGroup = new ChangeEntryGroup(toggleableListView)
{ Title = StringAssets.changeListFullHeader };
m_EntryToggleableGroup.SetOverflowCallback(m_Presenter.OnClickGroupOverflow);
m_EntryToggleableGroup.Searching = m_Presenter.Searching;
}
Assert.IsTrue(m_ToggleableChangeListAdapter != null && m_EntryToggleableGroup != null, "Invalid state while setting toggleable list");
// Ensure single list is displayed
if (m_ActiveGroup != m_EntryToggleableGroup)
{
m_ActiveGroup?.RemoveFromHierarchy();
m_EntryGroupsContainer.Add(m_EntryToggleableGroup);
m_ActiveGroup = m_EntryToggleableGroup;
}
// Can use list.Count here since searching hides "All".
m_EntryToggleableGroup.EntryCount = m_Presenter.Searching ? list.Count : m_Presenter.TotalCount;
m_ToggleableChangeListAdapter.List = list;
m_EntryToggleableGroup.NumberMenuItems = m_Presenter.GroupOverflowEntryCount;
m_EntryToggleableGroup.SelectedEntryCount = m_Presenter.ToggledCount;
}
/// <inheritdoc />
public void SetToggledCount(int count)
{
if (m_EntryToggleableGroup != null)
{
m_EntryToggleableGroup.SelectedEntryCount = count;
}
}
/// <inheritdoc />
public void SetPublishEnabled(bool enabled, string reason = null)
{
m_PublishButton.SetEnabled(enabled);
// Disabled elements cannot have a tooltip so apply to a empty/dummy parent instead.
m_PublishButton.parent.tooltip = reason;
}
/// <inheritdoc />
public bool DisplayDialogue(string title, string message, string affirmative)
{
return EditorUtility.DisplayDialog(title, message, affirmative);
}
/// <inheritdoc />
public bool DisplayDialogue(string title, string message, string affirmative, string negative)
{
return EditorUtility.DisplayDialog(title, message, affirmative, negative);
}
[UsedImplicitly]
public new class UxmlFactory : UxmlFactory<ChangesTabPageView> { }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: eb25fc80cc8f828459d0f6769416818d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,234 @@
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using Unity.Cloud.Collaborate.Assets;
using Unity.Cloud.Collaborate.Components;
using Unity.Cloud.Collaborate.Models.Structures;
using Unity.Cloud.Collaborate.Presenters;
using Unity.Cloud.Collaborate.UserInterface;
using Unity.Cloud.Collaborate.Utilities;
using Unity.Cloud.Collaborate.Views.Adapters.ListAdapters;
using UnityEditor;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.UIElements;
namespace Unity.Cloud.Collaborate.Views
{
internal class HistoryTabPageView : TabPageComponent, IHistoryView
{
[CanBeNull]
IHistoryPresenter m_Presenter;
public const string UssClassName = "history-page";
public const string PaginatorUssClassName = UssClassName + "__paginator";
public const string ContentUssClassName = UssClassName + "__content";
public const string NoticeUssClassName = UssClassName + "__notice";
static readonly string k_LayoutPath = $"{CollaborateWindow.LayoutPath}/{nameof(HistoryTabPageView)}.uxml";
readonly ScrollView m_Content;
readonly ListNotice m_ListNotice;
readonly Paginator m_Paginator;
bool m_Active;
public HistoryTabPageView()
{
AddToClassList(UssClassName);
AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(k_LayoutPath).CloneTree(this);
m_Paginator = this.Q<Paginator>(className: PaginatorUssClassName);
m_Paginator.AddToClassList(UiConstants.ussHidden);
m_Paginator.ClickedMovePage += OnClickedMovePage;
m_Content = this.Q<ScrollView>(className: ContentUssClassName);
// Add loading notice
m_ListNotice = this.Q<ListNotice>(className: NoticeUssClassName);
m_ListNotice.Text = StringAssets.loadingRevisions;
}
/// <inheritdoc />
public IHistoryPresenter Presenter
{
set
{
m_Presenter = value;
// If tab active before presenter has been added, call start once we have it.
if (Active)
{
m_Presenter.Start();
}
}
}
/// <inheritdoc />
public void SetBusyStatus(bool busy)
{
m_Paginator.SetEnabled(!busy);
m_Content.SetEnabled(!busy);
}
/// <inheritdoc />
public void SetHistoryList(IReadOnlyList<IHistoryEntry> list)
{
// Clear out old content
m_ListNotice.AddToClassList(UiConstants.ussHidden);
m_Content.Clear();
// Show paginator
m_Paginator.RemoveFromClassList(UiConstants.ussHidden);
// Handle empty list case
if (list.Count == 0)
{
m_ListNotice.Text = StringAssets.noticeNoRevisionsToDisplay;
m_Content.RemoveFromClassList(UiConstants.ussHidden);
return;
}
foreach (var entry in list)
{
// Add entry to the list
m_Content.Add(CreateHistoryEntry(entry, false));
}
}
/// <summary>
/// Event handler for receiving page change requests.
/// </summary>
/// <param name="pageChange">
/// Delta to change the page by: Paginator.MoveForwards, Paginator.MoveBackwards. Mapped to +1, -1 respectively.
/// </param>
void OnClickedMovePage(int pageChange)
{
Assert.IsNotNull(m_Presenter, "Invalid state when requesting page change.");
if (pageChange == Paginator.MoveBackwards)
{
m_Presenter.PrevPage();
}
else
{
m_Presenter.NextPage();
}
}
/// <inheritdoc />
public void SetPage(int page, int max)
{
m_Paginator.SetPage(page, max);
}
/// <inheritdoc />
public void SetSelection(IHistoryEntry entry)
{
// Hide paginator
m_Paginator.AddToClassList(UiConstants.ussHidden);
// Clear out old content
m_ListNotice.AddToClassList(UiConstants.ussHidden);
m_Content.Clear();
// Add new content
m_Content.Add(CreateHistoryEntry(entry, true));
}
/// <summary>
/// Takes a IHistoryEntry and binds it to a created HistoryEntryComponent to be used in the history list.
/// </summary>
/// <param name="entry">History entry to bind</param>
/// <param name="expanded">Whether or not to show its list of changed entries.</param>
/// <returns>Inflated and bound component.</returns>
HistoryEntryComponent CreateHistoryEntry([NotNull] IHistoryEntry entry, bool expanded)
{
Assert.IsNotNull(m_Presenter, "Invalid state when creating history entry");
var comp = new HistoryEntryComponent();
// Handle expanded vs compact layout
if (expanded)
{
// Hide fields used for compact view
comp.showFilesButton.AddToClassList(UiConstants.ussHidden);
comp.cloudStatusText.AddToClassList(UiConstants.ussHidden);
comp.changedFilesCount.text = $"Changes ( {entry.Changes.Count} )";
var listAdapter = new HistoryEntryChangeListAdapter(m_Presenter, entry.RevisionId, entry.Changes.ToList());
comp.changedFiles.SetAdapter(listAdapter);
listAdapter.NotifyDataSetChanged();
// Configure button
comp.gotoButton.text = entry.GetGotoText();
comp.gotoButton.clickable.clicked += () => m_Presenter.RequestGoto(entry.RevisionId, entry.Status);
}
else
{
// Hide fields used for expanded view
comp.changedFilesCount.AddToClassList(UiConstants.ussHidden);
comp.changedFiles.AddToClassList(UiConstants.ussHidden);
comp.gotoButton.text = string.Empty;
comp.gotoButton.AddToClassList(UiConstants.ussHidden);
// Setup show button
comp.showFilesButton.text = entry.Changes.Count == 1
? StringAssets.showChange
: string.Format(StringAssets.showChanges, entry.Changes.Count);
comp.showFilesButton.clickable.clicked += () => m_Presenter.SelectedRevisionId = entry.RevisionId;
// TODO: cloud status text
}
// Trim whitespace on either side and grab initial for profile circle
var trimmedAuthorName = entry.AuthorName.Trim();
comp.profileInitial.text = trimmedAuthorName.Substring(0, 1).ToUpper();
comp.authorName.text = trimmedAuthorName;
// Display relative or absolute timestamp. If relative, show absolute as a tooltip.
comp.timestamp.text = TimeStamp.GetTimeStamp(entry.Time);
if (TimeStamp.UseRelativeTimeStamps)
{
comp.timestamp.tooltip = TimeStamp.GetLocalisedTimeStamp(entry.Time);
}
// Display revision id and show full length id as a tooltip
comp.revisionId.text = $"ID: {entry.RevisionId.Substring(0, 10)}";
comp.revisionId.tooltip = entry.RevisionId;
comp.commitMessage.text = entry.Message;
return comp;
}
/// <inheritdoc />
public bool DisplayDialogue(string title, string message, string affirmative)
{
return EditorUtility.DisplayDialog(title, message, affirmative);
}
/// <inheritdoc />
public bool DisplayDialogue(string title, string message, string affirmative, string negative)
{
return EditorUtility.DisplayDialog(title, message, affirmative, negative);
}
/// <inheritdoc />
protected override void SetActive()
{
Assert.IsFalse(m_Active, "The view is already active.");
m_Active = true;
m_Presenter?.Start();
}
/// <inheritdoc />
protected override void SetInactive()
{
Assert.IsTrue(m_Active, "The view is already inactive.");
m_Active = false;
m_Presenter?.Stop();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5359645287a8484499e79d0fca4b40be
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,78 @@
using System.Collections.Generic;
using JetBrains.Annotations;
using Unity.Cloud.Collaborate.Models.Structures;
using Unity.Cloud.Collaborate.Presenters;
namespace Unity.Cloud.Collaborate.Views
{
internal interface IChangesView : IView<IChangesPresenter>
{
/// <summary>
/// Set busy status in the view.
/// </summary>
/// <param name="busy">Whether or not the presenter is busy with a request.</param>
void SetBusyStatus(bool busy);
/// <summary>
/// Set the search query in the view.
/// </summary>
/// <param name="query">Latest search query to set.</param>
void SetSearchQuery([NotNull] string query);
/// <summary>
/// Set the revision summary in the view.
/// </summary>
/// <param name="message">Latest summary to set.</param>
void SetRevisionSummary([NotNull] string message);
/// <summary>
/// Set the conflicts to be displayed.
/// </summary>
/// <param name="list">List of conflicts to display.</param>
void SetConflicts([NotNull] IReadOnlyList<IChangeEntryData> list);
/// <summary>
/// Set the changes to be selected.
/// </summary>
void SetSelectedChanges();
/// <summary>
/// Set the changes to be displayed.
/// </summary>
/// <param name="list">List of changes to be displayed.</param>
void SetChanges([NotNull] IReadOnlyList<IChangeEntryData> list);
/// <summary>
/// Set the count of toggled entries.
/// </summary>
/// <param name="count">Latest toggled count.</param>
void SetToggledCount(int count);
/// <summary>
/// Enable or disable the publish button based on the provided values. The optional reason is to be used as a
/// hint to users about why the functionality is blocked.
/// </summary>
/// <param name="enabled">Whether or not the publish is to be enabled.</param>
/// <param name="reason">Reason for the publish to be disabled.</param>
void SetPublishEnabled(bool enabled, [CanBeNull] string reason = null);
/// <summary>
/// Display a dialogue to the user.
/// </summary>
/// <param name="title">Title for the dialogue.</param>
/// <param name="message">Message inside the dialogue.</param>
/// <param name="affirmative">Affirmative button text.</param>
/// <returns>True if affirmative is clicked.</returns>
bool DisplayDialogue([NotNull] string title, [NotNull] string message, [NotNull] string affirmative);
/// <summary>
/// Display a dialogue to the user.
/// </summary>
/// <param name="title">Title for the dialogue.</param>
/// <param name="message">Message inside the dialogue.</param>
/// <param name="affirmative">Affirmative button text.</param>
/// <param name="negative">Negative button text.</param>
/// <returns>True if affirmative is clicked.</returns>
bool DisplayDialogue([NotNull] string title, [NotNull] string message, [NotNull] string affirmative, [NotNull] string negative);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3f23fc42896142749a24deb3b18150e3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using Unity.Cloud.Collaborate.Models.Structures;
using Unity.Cloud.Collaborate.Presenters;
namespace Unity.Cloud.Collaborate.Views
{
internal interface IHistoryView : IView<IHistoryPresenter>
{
/// <summary>
/// Set busy status in the view.
/// </summary>
/// <param name="busy">Whether or not the presenter is busy with a request.</param>
void SetBusyStatus(bool busy);
/// <summary>
/// Set the history list to be displayed. If the history list is not the current display, the view should switch
/// to it upon receiving this call.
/// </summary>
/// <param name="list">List of entries to display.</param>
void SetHistoryList(IReadOnlyList<IHistoryEntry> list);
/// <summary>
/// Set the current page and max page so that the paginator can be populated.
/// </summary>
/// <param name="page">Current page.</param>
/// <param name="max">Max page.</param>
void SetPage(int page, int max);
/// <summary>
/// Set the single history entry to display the provided entry. If the history single entry view is not the
/// current display, the view should switch to it upon receiving this call.
/// </summary>
/// <param name="entry">Entry to display.</param>
void SetSelection([NotNull] IHistoryEntry entry);
/// <summary>
/// Display a dialogue to the user.
/// </summary>
/// <param name="title">Title for the dialogue.</param>
/// <param name="message">Message inside the dialogue.</param>
/// <param name="affirmative">Affirmative button text.</param>
/// <returns>True if affirmative is clicked.</returns>
bool DisplayDialogue([NotNull] string title, [NotNull] string message, [NotNull] string affirmative);
/// <summary>
/// Display a dialogue to the user.
/// </summary>
/// <param name="title">Title for the dialogue.</param>
/// <param name="message">Message inside the dialogue.</param>
/// <param name="affirmative">Affirmative button text.</param>
/// <param name="negative">Negative button text.</param>
/// <returns>True if affirmative is clicked.</returns>
bool DisplayDialogue([NotNull] string title, [NotNull] string message, [NotNull] string affirmative, [NotNull] string negative);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 18a62dcf4c536564e99bd3b877101596
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,64 @@
using System;
using JetBrains.Annotations;
using Unity.Cloud.Collaborate.Components;
using Unity.Cloud.Collaborate.Presenters;
namespace Unity.Cloud.Collaborate.Views
{
internal interface IMainView : IView<IMainPresenter>
{
/// <summary>
/// Add or update an alert with the provided id.
/// </summary>
/// <param name="id">Id of the alert to add or update.</param>
/// <param name="level">Level of severity.</param>
/// <param name="message">Message for the alert.</param>
/// <param name="button">Optional button with text and a callback.</param>
void AddAlert([NotNull] string id, AlertBox.AlertLevel level, [NotNull] string message, (string text, Action action)? button = null);
/// <summary>
/// Remove alert with the provided id.
/// </summary>
/// <param name="id">Id of the alert to remove.</param>
void RemoveAlert([NotNull] string id);
/// <summary>
/// Switch to the given tab index.
/// </summary>
/// <param name="index">Index of tab to switch to.</param>
void SetTab(int index);
/// <summary>
/// Display progress view. Should only be called once, so only call when progress starts.
/// </summary>
void AddOperationProgress();
/// <summary>
/// Hide progress view. Should only be called once, so only call when progress finishes.
/// </summary>
void RemoveOperationProgress();
/// <summary>
/// Update the progress value displayed in the progress view.
/// </summary>
/// <param name="title">Title of the job in progress.</param>
/// <param name="details">Description/details/status of the job in progress.</param>
/// <param name="percentage">Current percentage completion of the job in progress. Used for percentage display.</param>
/// <param name="completed">Current number of job items completed. Used for discrete display.</param>
/// <param name="total">Total number of job items completed. Used for discrete display.</param>
/// <param name="isPercentage">True if the progress bar uses percentage, false if its discrete completed-of-total.</param>
/// <param name="canCancel">True if the job in progress can be cancelled.</param>
void SetOperationProgress(string title, string details, int percentage, int completed, int total, bool isPercentage, bool canCancel);
/// <summary>
/// Clear the current back navigation.
/// </summary>
void ClearBackNavigation();
/// <summary>
/// Set back navigation to be displayed with the provided text.
/// </summary>
/// <param name="text">Destination of the back navigation</param>
void DisplayBackNavigation([NotNull] string text);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9f48b2bb8da58264985e72257811c8fd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,23 @@
using Unity.Cloud.Collaborate.Presenters;
namespace Unity.Cloud.Collaborate.Views
{
internal interface IStartView : IView<IStartPresenter>
{
/// <summary>
/// Set the text for the view.
/// </summary>
string Text { set; }
/// <summary>
/// Set the text for the button in the view.
/// </summary>
string ButtonText { set; }
/// <summary>
/// Set the visibility of the button.
/// </summary>
/// <param name="isVisible">True if the button should be visible.</param>
void SetButtonVisible(bool isVisible);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6357e3360bc78e34f96e6d748eb03e01
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,19 @@
using System;
using JetBrains.Annotations;
using Unity.Cloud.Collaborate.Presenters;
namespace Unity.Cloud.Collaborate.Views
{
/// <summary>
/// Interface for all views in the UI.
/// </summary>
/// <typeparam name="T">Type of presenter this view takes.</typeparam>
interface IView<in T> where T : IPresenter
{
/// <summary>
/// Presenter for this view.
/// </summary>
[NotNull]
T Presenter { set; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 322ddf9bb156dcd40956b32589edd8f8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,202 @@
using System;
using JetBrains.Annotations;
using Unity.Cloud.Collaborate.Assets;
using Unity.Cloud.Collaborate.Components;
using Unity.Cloud.Collaborate.Presenters;
using Unity.Cloud.Collaborate.UserInterface;
using UnityEditor;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.UIElements;
namespace Unity.Cloud.Collaborate.Views
{
internal class MainPageView : PageComponent, IMainView
{
IMainPresenter m_Presenter;
public const string UssClassName = "main-page-view";
public const string TopBarUssClassName = UssClassName + "__top-bar";
public const string AlertBoxUssClassName = UssClassName + "__alert-box";
public const string TabViewUssClassName = UssClassName + "__tab-view";
public const string ContainerUssClassName = UssClassName + "__container";
// WARNING - These are hard-coded values. If you do anything to change the order
// these tabs are initialized, you'll need to change these
public const int ChangesTabIndex = 0;
public const int HistoryTabIndex = 1;
static readonly string k_LayoutPath = $"{CollaborateWindow.LayoutPath}/{nameof(MainPageView)}.uxml";
static readonly string k_StylePath = $"{CollaborateWindow.StylePath}/{nameof(MainPageView)}.uss";
readonly AlertBox m_AlertBox;
readonly TabView m_TabView;
readonly HistoryTabPageView m_HistoryView;
readonly ChangesTabPageView m_ChangesView;
readonly VisualElement m_Container;
readonly TopBar m_TopBar;
ProgressView m_ProgressView;
DisplayMode m_DisplayMode;
public MainPageView()
{
AddToClassList(UssClassName);
AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(k_LayoutPath).CloneTree(this);
styleSheets.Add(AssetDatabase.LoadAssetAtPath<StyleSheet>(k_StylePath));
m_TopBar = this.Q<TopBar>(className: TopBarUssClassName);
m_AlertBox = this.Q<AlertBox>(className: AlertBoxUssClassName);
m_TabView = this.Q<TabView>(className: TabViewUssClassName);
m_Container = this.Q<VisualElement>(className: ContainerUssClassName);
m_ChangesView = new ChangesTabPageView();
m_HistoryView = new HistoryTabPageView();
m_TabView.AddTab(StringAssets.changes, m_ChangesView);
m_TabView.AddTab(StringAssets.history, m_HistoryView);
// Set the current display mode.
m_DisplayMode = DisplayMode.TabView;
}
/// <inheritdoc />
public IMainPresenter Presenter
{
set
{
m_Presenter = value;
m_Presenter.AssignHistoryPresenter(m_HistoryView);
m_Presenter.AssignChangesPresenter(m_ChangesView);
m_TabView.TabSwitched += OnTabSwitched;
m_TopBar.BackButtonClicked += OnBackButtonClicked;
// If page active before presenter has been added, call start once we have it.
if (Active)
{
m_Presenter.Start();
}
}
}
/// <inheritdoc />
protected override void SetActive()
{
// Set TabView active if it's currently being displayed.
if (m_DisplayMode == DisplayMode.TabView)
{
m_TabView.SetActive();
}
m_Presenter?.Start();
}
/// <inheritdoc />
protected override void SetInactive()
{
// Set TabView inactive if it's current being displayed.
if (m_DisplayMode == DisplayMode.TabView)
{
m_TabView.SetInactive();
}
m_Presenter?.Stop();
}
/// <inheritdoc />
public void AddAlert(string id, AlertBox.AlertLevel level, string message, (string text, Action action)? button = null)
{
m_AlertBox.QueueAlert(id, level, message, button);
}
/// <inheritdoc />
public void RemoveAlert(string id)
{
m_AlertBox.DequeueAlert(id);
}
/// <inheritdoc />
public void SetTab(int index)
{
m_TabView.SwitchTab(index);
}
/// <inheritdoc />
public void AddOperationProgress()
{
SetDisplay(DisplayMode.ProgressView);
}
/// <inheritdoc />
public void RemoveOperationProgress()
{
SetDisplay(DisplayMode.TabView);
}
/// <inheritdoc />
public void SetOperationProgress(string title, string details, int percentage, int completed, int total, bool isPercentage, bool canCancel)
{
Assert.IsNotNull(m_ProgressView);
if (m_ProgressView == null) return;
var progress = isPercentage ? $"{percentage}%" : $"({completed} of {total})";
m_ProgressView.SetText($"{title}\n\n{details}", progress);
m_ProgressView.SetPercentComplete(percentage);
m_ProgressView.SetCancelButtonActive(canCancel);
}
/// <inheritdoc />
public void ClearBackNavigation()
{
m_TopBar.HideBackNavigation();
}
/// <inheritdoc />
public void DisplayBackNavigation(string text)
{
m_TopBar.DisplayBackNavigation(text);
}
void SetDisplay(DisplayMode mode)
{
Assert.AreNotEqual(m_DisplayMode, mode, "Cannot switch to the current display mode.");
m_DisplayMode = mode;
// Switch into tab or progress view.
if (m_DisplayMode == DisplayMode.TabView)
{
m_ProgressView?.AddToClassList(UiConstants.ussHidden);
m_TabView.RemoveFromClassList(UiConstants.ussHidden);
m_TabView.SetActive();
}
else
{
if (m_ProgressView == null)
{
m_ProgressView = new ProgressView();
m_ProgressView.SetCancelCallback(m_Presenter.RequestCancelJob);
m_Container.Add(m_ProgressView);
}
m_ProgressView.RemoveFromClassList(UiConstants.ussHidden);
m_TabView.AddToClassList(UiConstants.ussHidden);
m_TabView.SetInactive();
}
}
void OnTabSwitched(int index)
{
m_Presenter.UpdateTabIndex(index);
}
void OnBackButtonClicked()
{
m_Presenter.NavigateBack();
}
[UsedImplicitly]
public new class UxmlFactory : UxmlFactory<MainPageView> { }
enum DisplayMode
{
TabView,
ProgressView
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e67e7acd2f0e946489c7839b60602d17
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,4 @@
# Views
In this directory, we have all of the interfaces and implementations of the Views in the package's **MVP** architecture.
ListAdapters are also stored under `Adapters/` as they are considered a sub-View.

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 3f95ba73fe5e596409dd5591b959c1ce
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,96 @@
using System;
using JetBrains.Annotations;
using Unity.Cloud.Collaborate.Assets;
using Unity.Cloud.Collaborate.Components;
using Unity.Cloud.Collaborate.Presenters;
using Unity.Cloud.Collaborate.UserInterface;
using UnityEditor;
using UnityEngine.UIElements;
namespace Unity.Cloud.Collaborate.Views
{
internal class StartPageView : PageComponent, IStartView
{
public const string UssClassName = "start-page-view";
public const string UssTitleClassName = UssClassName + "__title";
public const string UssButtonClassName = UssClassName + "__button";
static readonly string k_LayoutPath = $"{CollaborateWindow.LayoutPath}/{nameof(StartPageView)}.uxml";
static readonly string k_StylePath = $"{CollaborateWindow.StylePath}/{nameof(StartPageView)}.uss";
IStartPresenter m_Presenter;
readonly Label m_Text;
readonly Button m_Button;
public StartPageView()
{
AddToClassList(UssClassName);
AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(k_LayoutPath).CloneTree(this);
styleSheets.Add(AssetDatabase.LoadAssetAtPath<StyleSheet>(k_StylePath));
m_Text = this.Q<Label>(className: UssTitleClassName);
m_Button = this.Q<Button>(className: UssButtonClassName);
}
/// <inheritdoc />
public IStartPresenter Presenter
{
set
{
m_Presenter = value;
SetupEvents();
// If page active before presenter has been added, call start once we have it.
if (Active)
{
m_Presenter.Start();
}
}
}
/// <inheritdoc />
protected override void SetActive()
{
m_Presenter?.Start();
}
/// <inheritdoc />
protected override void SetInactive()
{
m_Presenter?.Stop();
}
void SetupEvents()
{
m_Button.clickable.clicked += m_Presenter.RequestStart;
}
/// <inheritdoc />
public string Text
{
set => m_Text.text = value;
}
/// <inheritdoc />
public string ButtonText
{
set => m_Button.text = value;
}
/// <inheritdoc />
public void SetButtonVisible(bool isVisible)
{
if (isVisible)
{
m_Button.RemoveFromClassList(UiConstants.ussHidden);
}
else
{
m_Button.AddToClassList(UiConstants.ussHidden);
}
}
[UsedImplicitly]
public new class UxmlFactory : UxmlFactory<StartPageView> { }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 59ee13971b9c3b049bb9a96886120e0b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: