using System;
using System.Collections.Generic;
using UnityEngine;
using Framework.Selection;
using Framework.Services;
using Framework.Support;
namespace Framework.UI {
/// Highlights the selection of an actor
public class SelectionHighlighter : ScriptComponent {
///
/// Selection tracker for which the highlighting system will highlight items
///
public SelectionTracker SelectionTracker;
/// Color in which highlighted objects will be shown
public Color HighlightColor = Color.red;
/// Color in which selected objects will be shown
public Color SelectionColor = Color.white;
/// Called once per visual frame
protected virtual void Update() {
if (this.SelectionTracker != this.usedSelectionTracker) {
if (this.highlightedItems == null) {
createDelegatesAndCollections();
}
if (this.usedSelectionTracker != null) {
detachFromAllEvents();
}
this.usedSelectionTracker = this.SelectionTracker;
}
if (this.usedSelectionTracker != null) {
resyncEventSubscriptions();
}
}
/// Attaches and detaches from change notifications as needed
private void resyncEventSubscriptions() {
if (this.usedSelectionTracker.CanHighlight != this.highlightEventsAttached) {
if (this.usedSelectionTracker.CanHighlight) {
attachToHighlightEvents();
} else {
detachFromHighlightEvents();
}
}
if (this.usedSelectionTracker.CanSelect != this.selectionEventsAttached) {
if (this.usedSelectionTracker.CanSelect) {
attachToSelectionEvents();
} else {
detachFromSelectionEvents();
}
}
}
/// Detaches from all events of the selection tracker
private void detachFromAllEvents() {
if (this.highlightEventsAttached) {
detachFromHighlightEvents();
}
if (this.selectionEventsAttached) {
detachFromSelectionEvents();
}
}
/// Called when the highlighter component gets enabled
protected virtual void OnEnable() {
if (this.usedSelectionTracker != null) {
resyncEventSubscriptions();
}
}
/// Called when the highlighter component gets disabled
protected virtual void OnDisable() {
if (this.usedSelectionTracker != null) {
detachFromAllEvents();
}
}
/// Called when the component gets removed from a game object
protected virtual void OnDestroy() {
// If selection tracker has been destroyed, its overloaded equals operator
// will compare as equal to null. In this case, wo only need to clear out
// any objects still marked as selected or highlighted
if (this.usedSelectionTracker == null) {
selectedItemsClearing();
highlightedItemsClearing();
} else {
detachFromAllEvents();
}
}
/// Subscribes to the notifications for the highlighted object collection
private void attachToHighlightEvents() {
var observableHighlightedSet = (
(IObservableCollection) this.usedSelectionTracker.HighlightedObjects
);
observableHighlightedSet.Clearing += this.highlightedItemsClearingDelegate;
observableHighlightedSet.ItemAdded += this.highlightedItemAddedDelegate;
observableHighlightedSet.ItemRemoved += this.highlightedItemRemovedDelegate;
// If any objects are already in the highlight collection make them highlighted
foreach (Selectable selectable in this.usedSelectionTracker.HighlightedObjects) {
highlightedItemAdded(selectable);
}
this.highlightEventsAttached = true;
}
/// Subscribes to the notifications for the selected object collection
private void attachToSelectionEvents() {
var observableSelectedSet = (
(IObservableCollection)this.usedSelectionTracker.SelectedObjects
);
observableSelectedSet.Clearing += this.selectedItemsClearingDelegate;
observableSelectedSet.ItemAdded += this.selectedItemAddedDelegate;
observableSelectedSet.ItemRemoved += this.selectedItemRemovedDelegate;
// If any bjects are alreadz in the selected collection make them highlighted
foreach(Selectable selectable in this.usedSelectionTracker.SelectedObjects) {
selectedItemAdded(selectable);
}
this.selectionEventsAttached = true;
}
/// Unsubscribes from the notifications of the highlighted object collection
private void detachFromHighlightEvents() {
var observableHighlightedSet = (
(IObservableCollection)this.usedSelectionTracker.HighlightedObjects
);
this.highlightedItemsClearing();
observableHighlightedSet.ItemRemoved -= this.highlightedItemRemovedDelegate;
observableHighlightedSet.ItemAdded -= this.highlightedItemAddedDelegate;
observableHighlightedSet.Clearing -= this.highlightedItemsClearingDelegate;
this.highlightEventsAttached = false;
}
/// Unsubscribes from the notifications of the selected object collection
private void detachFromSelectionEvents() {
var observableSelectedSet = (
(IObservableCollection)this.usedSelectionTracker.SelectedObjects
);
this.selectedItemsClearing();
observableSelectedSet.ItemRemoved -= this.selectedItemRemovedDelegate;
observableSelectedSet.ItemAdded -= this.selectedItemAddedDelegate;
observableSelectedSet.Clearing -= this.selectedItemsClearingDelegate;
this.selectionEventsAttached = true;
}
/// Creates the permanent collections and delegates used by the instance
private void createDelegatesAndCollections() {
this.highlightedItems = new List();
this.selectedItems = new List();
this.highlightedItemsClearingDelegate = new Action(highlightedItemsClearing);
this.highlightedItemAddedDelegate = new Action(highlightedItemAdded);
this.highlightedItemRemovedDelegate = new Action(highlightedItemRemoved);
this.selectedItemsClearingDelegate = new Action(selectedItemsClearing);
this.selectedItemAddedDelegate = new Action(selectedItemAdded);
this.selectedItemRemovedDelegate = new Action(selectedItemRemoved);
this.selectionEventsAttached = false;
}
/// Called when the highlighted items set is being cleared
private void highlightedItemsClearing() {
foreach(Selectable selectable in this.usedSelectionTracker.HighlightedObjects) {
highlightedItemRemoved(selectable);
}
}
/// Called when a new item is added to the highlighted items set
/// Item that has been added to the highlighted items set
private void highlightedItemAdded(Selectable item) {
this.highlightedItems.Add(item);
#if HAVE_HIGHLIGHTING_SYSTEM
HighlightingSystem.Highlighter highlighter;
{
highlighter = item.GetComponent();
if(highlighter == null) {
highlighter = item.gameObject.AddComponent();
}
}
highlighter.ConstantOnImmediate(this.HighlightColor);
#endif // HAVE_HIGHLIGHTING_SYSTEM
}
/// Called when a new item is removed from the highlighted items set
/// Item that has been removed from the highlighted items set
private void highlightedItemRemoved(Selectable item) {
this.highlightedItems.Remove(item);
// The component or its game object may have already been destroyed without
// unregistering. In this case, the equals operators reports equality with null.
if(item == null) {
return;
}
#if HAVE_HIGHLIGHTING_SYSTEM
HighlightingSystem.Highlighter highlighter;
{
highlighter = item.GetComponent();
if(highlighter != null) {
highlighter.ConstantOffImmediate();
if(this.selectedItems.Contains(item)) {
highlighter.ConstantOnImmediate(this.SelectionColor);
}
}
}
#endif // HAVE_HIGHLIGHTING_SYSTEM
}
/// Called when the selected items set is being cleared
private void selectedItemsClearing() {
foreach(Selectable selectable in this.usedSelectionTracker.SelectedObjects) {
selectedItemRemoved(selectable);
}
}
/// Called when a new item is added to the selected items set
/// Item that has been added to the selected items set
private void selectedItemAdded(Selectable item) {
this.selectedItems.Add(item);
#if HAVE_HIGHLIGHTING_SYSTEM
HighlightingSystem.Highlighter highlighter;
{
highlighter = item.GetComponent();
if(highlighter == null) {
highlighter = item.gameObject.AddComponent();
}
}
// Highlight overrides selection
if(!this.highlightedItems.Contains(item)) {
highlighter.ConstantOnImmediate(this.SelectionColor);
}
#endif // HAVE_HIGHLIGHTING_SYSTEM
}
/// Called when a new item is removed from the selected items set
/// Item that has been removed from the selected items set
private void selectedItemRemoved(Selectable item) {
this.selectedItems.Remove(item);
// The component or its game object may have already been destroyed without
// unregistering. In this case, the equals operators reports equality with null.
if(item == null) {
return;
}
#if HAVE_HIGHLIGHTING_SYSTEM
HighlightingSystem.Highlighter highlighter;
{
highlighter = item.GetComponent();
if(highlighter != null) {
if(!this.highlightedItems.Contains(item)) {
highlighter.ConstantOffImmediate();
}
}
}
#endif // HAVE_HIGHLIGHTING_SYSTEM
}
/// Copy of the publicly assigned selection tracker
/// >
/// Theoretically, someone could assign a different selection tracker to this
/// component. Then we have to remove highlighters from all selected objects
/// in the old selection tracker and add highlighters to all selected objects
/// in the new tracker. This field helps us know when we need these measures.
///
private SelectionTracker usedSelectionTracker;
/// Whether the highlighter is currently attached to the tracker
private bool isAttached;
/// Game objects that are currently being displayed as highlighted
private IList highlightedItems;
/// Game objects that are currently being displayed as selected
private IList selectedItems;
/// Whether the higlighting callbacks are current subscribed
private bool highlightEventsAttached;
/// Delegate for the method
private Action highlightedItemsClearingDelegate;
/// Delegate for the method
private Action highlightedItemAddedDelegate;
/// Delegate for the method
private Action highlightedItemRemovedDelegate;
/// Whether the selection callbacks are current subscribed
private bool selectionEventsAttached;
/// Delegate for the method
private Action selectedItemsClearingDelegate;
/// Delegate for the method
private Action selectedItemAddedDelegate;
/// Delegate for the method
private Action selectedItemRemovedDelegate;
}
} // namespace Framework.UI