Skip to content

Commit

Permalink
0.1.75
Browse files Browse the repository at this point in the history
  • Loading branch information
neilsarkar committed Feb 21, 2021
1 parent 9bc1731 commit 4b0f5b5
Show file tree
Hide file tree
Showing 15 changed files with 325 additions and 79 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
## [0.1.75] - 2020-02-21

### Added
- IRepeatView
- X button to discard edits on a menu
- Navigator.GoBack
- Navigator.DebugVisit
- VioletButton.showModal
- VioletButton.closeModal
- VioletScreen.isEditing


## [0.1.74] - 2020-11-10

### Added
Expand Down
46 changes: 46 additions & 0 deletions Editor/PrefabListener.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using Stopwatch = System.Diagnostics.Stopwatch;

namespace VioletUI {
public class PrefabListener : UnityEditor.AssetModificationProcessor {
static string[] OnWillSaveAssets(string[] paths) {
foreach (var path in paths) {
if (path.Contains(".prefab")) {
UpdateRepeatViews(path);
}
}
return paths;
}

static void UpdateRepeatViews(string prefabPath) {
IRepeatView repeatView;
HashSet<GameObject> processed = new HashSet<GameObject>();

// https://docs.unity3d.com/ScriptReference/Resources.FindObjectsOfTypeAll.html?_ga=2.70405528.968528738.1611110066-121275311.1591173442
foreach (var go in Resources.FindObjectsOfTypeAll<TidyBehaviour>()) {
// check hideFlags
try {
if (go.hideFlags == HideFlags.NotEditable || go.hideFlags == HideFlags.HideAndDontSave) {
continue;
}
} catch(MissingReferenceException) { continue; }
// ignore unity things that aren't in a scene
if (EditorUtility.IsPersistent(go.transform.root.gameObject)) { continue; }
// ignore TidyBehaviours without RepeatViews and RepeatViews without ViewPrefabs
if ((repeatView = go.GetComponent<IRepeatView>())?.ViewPrefab == null) { continue; }
// ignore gameObjects that aren't in a scene
if (!go.gameObject.scene.path.Contains(".unity")) { continue; }
// ignore gameObjects we've already processed
if (processed.Contains(go.gameObject)) { continue; }

if (PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(repeatView.ViewPrefab) == prefabPath) {
repeatView.RegenerateChildren();
processed.Add(go.gameObject);
Violet.Log($"Updated RepeatView on {go.gameObject.name}");
}
}
}
}
}
11 changes: 11 additions & 0 deletions Editor/PrefabListener.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 18 additions & 8 deletions Editor/ScreenEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ namespace VioletUI {
[InitializeOnLoad]
public static class ScreenEditor {

public static Color RedHue = new Color(1f, 0.2f, 0.2f);
public static Color GreenHue = new Color(0.2f, 1f, 0.2f);

//where to show the buttons
private static GUIStyle singleStyle, leftStyle, rightStyle;

Expand All @@ -25,6 +28,7 @@ static void DrawHierarchyItem(int instanceID, Rect rect) {
if (Application.isPlaying) { return; }
var gameObject = EditorUtility.InstanceIDToObject(instanceID) as GameObject;
if (gameObject == null) { return; }
if (gameObject.scene == null) { return; }

navigator = gameObject.GetComponent<Navigator>();
if (navigator != null) {
Expand All @@ -41,13 +45,9 @@ static void DrawHierarchyItem(int instanceID, Rect rect) {

static void DrawNavigator(Navigator navigator, Rect rect) {
if (navigator.EditingScreen == null) {
if (Button(rect, "Add")) {
if (Button(rect, 36, "New")) {
navigator.AddScreen();
}
} else if (navigator.transform.childCount > 1) {
if (Button(rect, "Lose", true, Color.red)) {
navigator.DiscardEdits();
}
}
}

Expand All @@ -62,7 +62,7 @@ static void DrawScreen(VioletScreen screen, Rect rect) {

if (navigator.EditingScreen != null && screen != navigator.EditingScreen) { return; }

if (Button(rect, screen.isActiveAndEnabled ? "Save" : "Edit", screen.isActiveAndEnabled)) {
if (Button(rect, 36, screen.isActiveAndEnabled ? "Save" : "Edit", screen.isActiveAndEnabled)) {
if (navigator == null) {
throw new VioletException($"Tried to edit {screen.name} without a Navigator. Try adding a Navigator component to {screen.transform.parent.name}");
}
Expand All @@ -72,12 +72,21 @@ static void DrawScreen(VioletScreen screen, Rect rect) {
navigator.Edit(screen);
}
}
if (screen.isActiveAndEnabled) {
if (Button(rect, 18, "X", screen.isActiveAndEnabled, RedHue, -50)) {
if (navigator == null) {
throw new VioletException($"Can't find navigator in scene");
}
navigator.DiscardEdits();
}
}

}

static bool Button(Rect rect, string label, bool isActive = false, Color highlightColor = default) {
static bool Button(Rect rect, int buttonWidth, string label, bool isActive = false, Color highlightColor = default, int xOffset = 0) {
// by default the button is 100% width
// we move the left edge to make button fixed width, right aligned
var buttonWidth = 36;
rect.xMax = rect.xMax + xOffset;
rect.xMin = rect.xMax - buttonWidth;

// extend unity mini button style with small tweaks
Expand All @@ -96,5 +105,6 @@ static bool Button(Rect rect, string label, bool isActive = false, Color highlig

return response;
}

}
}
2 changes: 2 additions & 0 deletions Editor/VioletButtonEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ public override void OnInspectorGUI() {
base.OnInspectorGUI();
VioletButton button = (VioletButton)target;
button.visitScreen = (ScreenId)EditorGUILayout.EnumPopup("Visit Screen", button.visitScreen);
button.showModal = (ScreenId)EditorGUILayout.EnumPopup("Show Modal", button.showModal);
button.closeModal = (bool)EditorGUILayout.Toggle("Close Modal", button.closeModal);
}
}
}
6 changes: 6 additions & 0 deletions Runtime/IRepeatView.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
using UnityEngine;

public interface IRepeatView {
void RegenerateChildren();
GameObject ViewPrefab { get; }
}
11 changes: 11 additions & 0 deletions Runtime/IRepeatView.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

91 changes: 77 additions & 14 deletions Runtime/Navigation/Navigator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using UnityEngine;
using Sirenix.OdinInspector;
using System.Threading;
using UniRx.Async;
using Cysharp.Threading.Tasks;
using UnityEngine.UI;
#if UNITY_EDITOR
using UnityEditor;
Expand Down Expand Up @@ -35,9 +35,14 @@ public class Navigator : TidyBehaviour {

public Action<ScreenId> OnModalShow;
public Action<ScreenId> OnModalHide;

public Action<ScreenId> OnEditStart;
public Action<ScreenId> OnEditFinish;
#endregion

#region local
[NonSerialized] protected Stack<ScreenId> lastScreens = new Stack<ScreenId>();
[NonSerialized] protected Stack<ScreenId> modals = new Stack<ScreenId>();
[NonSerialized] protected Dictionary<ScreenId, VioletScreen> screens = new Dictionary<ScreenId, VioletScreen>();
[NonSerialized] protected ScreenId lastScreen = ScreenId.None;
[NonSerialized] protected ScreenId currentScreen = ScreenId.None;
Expand All @@ -59,11 +64,34 @@ void OnDisable() {
canceler = null;
}

/// <summary>
/// GoBack must be called after at least one Visit call and will revisit the previous screen
/// </summary>
/// <returns>visit success status</returns>
public async UniTask<bool> GoBack() {
if (lastScreens.Count == 0) {
UnityEngine.Debug.LogError($"Tried to go back but we haven't visited a screen yet.");
return false;
}

var screen = lastScreens.Pop();
return await Visit(lastScreen, false);
}

public async UniTask<bool> DebugVisit(ScreenId screenId) {
if (currentScreen != ScreenId.None) {
screens[currentScreen].gameObject.SetActive(false);
}
await UniTask.DelayFrame(1);
screens[screenId].gameObject.SetActive(true);
return true;
}

/// <summary>
/// Visit takes a <see cref="ScreenId"/> and transitions the menu to that scene.
/// </summary>
/// <param name="screenId"></param>
public async UniTask<bool> Visit(ScreenId screenId) {
public async UniTask<bool> Visit(ScreenId screenId, bool isForward = true) {
Violet.LogVerbose($"Visiting {screenId}");
if (!screens.ContainsKey(screenId)) {
throw new VioletException($"Tried to visit {screenId} but it doesn't exist in the current scene. You'll want to add the {screenId} prefab to this scene or to the MenuBuilder prefab. Or change the Home Screen to the screen you want.");
Expand All @@ -85,6 +113,9 @@ public async UniTask<bool> Visit(ScreenId screenId) {
hidePromise = screens[lastScreen].Hide(canceler.Token).ContinueWith((x) => {
ok = x;
OnDidLeave?.Invoke(lastScreen);
if (isForward) {
lastScreens.Push(lastScreen);
}
});
}
await hidePromise;
Expand All @@ -104,45 +135,69 @@ public async UniTask<bool> Visit(ScreenId screenId) {
}

/// <summary>
/// Show another screen in addition to the current screen.
/// Show another screen in addition to the current screen and disable input listeners on the current screen.
///
/// It fires <see cref="OnModalShow" /> prior to setting the screen to active
/// It fires <see cref="OnModalShow" /> prior to setting the screen to active.
/// </summary>
/// <param name="screenId">Auto-generated id of screen to show as modal</param>
public async void ShowModal(ScreenId screenId) {
// we have to call this before setting things to active because
// it causes all input listeners to unsubscribe
OnModalShow?.Invoke(screenId);
await screens[screenId].Show(default);
modals.Push(screenId);
currentModal = screenId;
}

/// <summary>
/// Hide the currently shown modal.
/// Hide the currently shown modal and re-enables input listeners on the current screen
///
/// It fires <see cref="OnModalHide" /> after setting the modal to inactive.
/// </summary>
public async void HideModal() {
if (currentModal == ScreenId.None) { Violet.LogWarning("Called HideModal but there is no current modal - check if HideModal is called twice or called before ShowModal"); return; }
await screens[currentModal].Hide();
OnModalHide?.Invoke(currentModal);
currentModal = ScreenId.None;
public async virtual UniTask HideModal() {
if (currentModal == ScreenId.None) { return; }
var screenId = modals.Pop();
await screens[screenId].Hide();
currentModal = modals.Count > 0 ? modals.Peek() : ScreenId.None;
OnModalHide?.Invoke(screenId);
}

public async virtual UniTask CloseAllModals() {
int attempts = 10;
while (currentModal != ScreenId.None) {
if (attempts-- < 0) {
throw new Exception($"Couldn't CloseAllModals. Stuck on {currentModal}");
}
await HideModal();
}
}

/// <summary>
/// Show an overlay screen in addition to the current screen. Triggers no events.
/// Show an overlay screen in addition to the current screen. Triggers no events and allows input on main screen.
/// </summary>
/// <param name="screenId">id of screen to set active</param>
public void ShowOverlay(ScreenId screenId) {
screens[screenId].gameObject.SetActive(true);
try {
screens[screenId].gameObject.SetActive(true);
} catch(KeyNotFoundException) {
// when editing screens in editor the screens dictionary may be empty
LoadScreens();
screens[screenId].gameObject.SetActive(true);
}
}

/// <summary>
/// Show an overlay screen in addition to the current screen. Triggers no events.
/// </summary>
/// <param name="screenId">id of screen to set inactive</param>
public void HideOverlay(ScreenId screenId) {
screens[screenId].gameObject.SetActive(false);
try {
screens[screenId].gameObject.SetActive(false);
} catch(KeyNotFoundException) {
// when editing screens in editor the screens dictionary may be empty
LoadScreens();
screens[screenId].gameObject.SetActive(false);
}
}

// Sigh.
Expand Down Expand Up @@ -220,7 +275,12 @@ void LoadScreens() {
}

screens[screenId] = screen;

#if UNITY_EDITOR
screen.gameObject.SetActive(screen == EditingScreen);
#else
screen.gameObject.SetActive(false);
#endif
}

OnReady?.Invoke();
Expand All @@ -246,6 +306,7 @@ public void Edit(VioletScreen screen) {
screen.gameObject.SetActive(true);
EditingScreen = screen;
currentScreen = ScreenToScreenId(screen);
OnEditStart?.Invoke(currentScreen);
}

public void FinishEditing(VioletScreen screen = null) {
Expand All @@ -255,12 +316,14 @@ public void FinishEditing(VioletScreen screen = null) {
screen.gameObject.SetActive(false);
if (screen == EditingScreen) { EditingScreen = null; };
homeScreen = originalHomeScreen;
OnEditFinish?.Invoke(ScreenToScreenId(screen));
}

public void DiscardEdits() {
if (EditingScreen == null) { EditingScreen = gameObject.GetComponentInChildren<VioletScreen>(); }
EditingScreen.RevertPrefab();
OnEditFinish?.Invoke(ScreenToScreenId(EditingScreen));
EditingScreen.gameObject.SetActive(false);
EditingScreen.RevertPrefab();
EditingScreen = null;
homeScreen = originalHomeScreen;
}
Expand Down
11 changes: 6 additions & 5 deletions Runtime/Navigation/ScreenId.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Names are automatically added through ScreenIdGenerator.cs, deletions are done manually :)
namespace VioletUI {
public enum ScreenId {
None = 0,
}
//Names are automatically added through ScreenIdGenerator.cs, deletions are done manually in Assets/Plugins/VioletUI/ScreenIds.json :)
public enum ScreenId {
None = 0,
ScreenA = 1,
ScreenB = 2,
Showhide = 3,
}
Loading

0 comments on commit 4b0f5b5

Please sign in to comment.