Skip to content

Commit a6d0fa2

Browse files
authored
Merge pull request #10 from PurrNet/dev
Release
2 parents 3992396 + ce70784 commit a6d0fa2

4 files changed

Lines changed: 190 additions & 10 deletions

File tree

Assets/PurrUI/CHANGELOG.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,45 @@
1+
# [1.4.0-beta.3](https://github.com/PurrNet/PurrUI/compare/v1.4.0-beta.2...v1.4.0-beta.3) (2026-04-13)
2+
3+
4+
### Bug Fixes
5+
6+
* make Clear method "cheaper", no need to put everything to foreground etc ([a63c677](https://github.com/PurrNet/PurrUI/commit/a63c6772b868064c7ea0c2facbf58047c7159472))
7+
8+
# [1.4.0-beta.2](https://github.com/PurrNet/PurrUI/compare/v1.4.0-beta.1...v1.4.0-beta.2) (2026-04-13)
9+
10+
11+
### Features
12+
13+
* add OnDestroy method to clear view stack ([8d088fc](https://github.com/PurrNet/PurrUI/commit/8d088fc5168ff1b07178f67c51f7c4f2f19c4436))
14+
15+
# [1.4.0-beta.1](https://github.com/PurrNet/PurrUI/compare/v1.3.1-beta.3...v1.4.0-beta.1) (2026-03-18)
16+
17+
18+
### Features
19+
20+
* ReplaceOrPush ([71e09f6](https://github.com/PurrNet/PurrUI/commit/71e09f6dd7729fd10ed1ad55cffd01d39fd8dba0))
21+
22+
## [1.3.1-beta.3](https://github.com/PurrNet/PurrUI/compare/v1.3.1-beta.2...v1.3.1-beta.3) (2026-03-18)
23+
24+
25+
### Bug Fixes
26+
27+
* allow to replace a specific view instance ([344f199](https://github.com/PurrNet/PurrUI/commit/344f1997ca32aba5adfa88ccd6a8d385f2f31314))
28+
29+
## [1.3.1-beta.2](https://github.com/PurrNet/PurrUI/compare/v1.3.1-beta.1...v1.3.1-beta.2) (2026-03-18)
30+
31+
32+
### Bug Fixes
33+
34+
* reset parentStack when not part of a viewStack ([b71a09f](https://github.com/PurrNet/PurrUI/commit/b71a09f8efa8a35ac1fe70e4473a34f437bd9c73))
35+
36+
## [1.3.1-beta.1](https://github.com/PurrNet/PurrUI/compare/v1.3.0...v1.3.1-beta.1) (2026-03-13)
37+
38+
39+
### Bug Fixes
40+
41+
* dont push/pop/replace outside of play mode, just a guardrail for users ([558d71a](https://github.com/PurrNet/PurrUI/commit/558d71a8f2b30411b1c5a06fd149305a2a02d98c))
42+
143
# [1.3.0](https://github.com/PurrNet/PurrUI/compare/v1.2.0...v1.3.0) (2026-03-12)
244

345

Assets/PurrUI/Runtime/MonoView.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,17 @@ public class MonoView : MonoBehaviour
1212

1313
public CanvasGroup canvasGroup { get; private set; }
1414

15-
public ViewStack parentStack { get; private set; }
15+
public ViewStack parentStack { get; internal set; }
1616

1717
public bool cullWindowsBehind => _cullWindowsBehind;
1818

1919
public bool isTopMost => parentStack && parentStack.top == this;
2020

21+
/// <summary>
22+
/// Returns true if this view is currently part of a ViewStack.
23+
/// </summary>
24+
public bool isInStack => parentStack;
25+
2126
public Canvas canvas { get; private set; }
2227

2328
/// <summary>

Assets/PurrUI/Runtime/ViewStack.cs

Lines changed: 141 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ public T FindWindow<T>() where T : MonoView
102102
/// </summary>
103103
public MonoView Push(MonoView prefab)
104104
{
105+
if (!Application.isPlaying)
106+
return null;
107+
105108
if (prefab == null)
106109
{
107110
Debug.LogError("[WindowStack] Provided prefab is null.", this);
@@ -226,6 +229,9 @@ public T Push<T>() where T : MonoView
226229
/// </summary>
227230
public void Pop()
228231
{
232+
if (!Application.isPlaying)
233+
return;
234+
229235
if (_stack.Count == 0)
230236
{
231237
Debug.LogError("[WindowStack] No windows to pop.", this);
@@ -238,9 +244,12 @@ public void Pop()
238244
{
239245
var newTopIdx = _stack.Count - 1;
240246
var newTop = _stack[newTopIdx];
241-
newTop.transform.SetAsLastSibling();
242-
newTop.MoveToForeground();
243-
newTop.UpdateOrder(newTopIdx + _orderOffset);
247+
if (newTop)
248+
{
249+
newTop.transform.SetAsLastSibling();
250+
newTop.MoveToForeground();
251+
newTop.UpdateOrder(newTopIdx + _orderOffset);
252+
}
244253
}
245254
UpdateVisibility();
246255
}
@@ -250,8 +259,13 @@ public void Pop()
250259
/// </summary>
251260
public void Clear()
252261
{
253-
while (_stack.Count > 0)
254-
Pop();
262+
for (int i = _stack.Count - 1; i >= 0; i--)
263+
{
264+
var view = _stack[i];
265+
view.OnPopped();
266+
view.parentStack = null;
267+
}
268+
_stack.Clear();
255269
}
256270

257271
/// <summary>
@@ -260,6 +274,9 @@ public void Clear()
260274
/// <param name="instance"></param>
261275
public void Pop(MonoView instance)
262276
{
277+
if (!Application.isPlaying)
278+
return;
279+
263280
int idx = _stack.IndexOf(instance);
264281

265282
if (idx == -1)
@@ -277,6 +294,7 @@ public void Pop(MonoView instance)
277294

278295
_stack.RemoveAt(idx);
279296
instance.OnPopped();
297+
instance.parentStack = null;
280298

281299
var transition = instance.ExitTransition();
282300
if (transition != null)
@@ -300,6 +318,9 @@ public void Pop(MonoView instance)
300318
/// </summary>
301319
public MonoView Replace(MonoView prefab)
302320
{
321+
if (!Application.isPlaying)
322+
return null;
323+
303324
if (prefab == null)
304325
{
305326
Debug.LogError("[WindowStack] Provided prefab is null.", this);
@@ -328,6 +349,108 @@ public T Replace<T>() where T : MonoView
328349
return (T)Replace(prefab);
329350
}
330351

352+
/// <summary>
353+
/// Replaces the specified instance in the stack with a new window instantiated from the provided prefab.
354+
/// The old instance plays its exit transition while the new one plays its enter transition.
355+
/// The new window takes the same position in the stack as the old one.
356+
/// </summary>
357+
public MonoView Replace(MonoView instance, MonoView prefab)
358+
{
359+
if (!Application.isPlaying)
360+
return null;
361+
362+
if (prefab == null)
363+
{
364+
Debug.LogError("[WindowStack] Provided prefab is null.", this);
365+
return null;
366+
}
367+
368+
int idx = _stack.IndexOf(instance);
369+
370+
if (idx == -1)
371+
{
372+
Debug.LogError("[WindowStack] The provided window instance is not in the stack.", this);
373+
return null;
374+
}
375+
376+
// If it's the top window, use the regular Replace method
377+
if (idx == _stack.Count - 1)
378+
return Replace(prefab);
379+
380+
// Remove the instance at its position
381+
_stack.RemoveAt(idx);
382+
instance.OnPopped();
383+
instance.parentStack = null;
384+
385+
var exitTransition = instance.ExitTransition();
386+
if (exitTransition != null)
387+
{
388+
instance.canvasGroup.blocksRaycasts = false;
389+
StartCoroutine(RunExitTransition(instance, exitTransition));
390+
}
391+
else
392+
{
393+
instance.DestroyMe();
394+
}
395+
396+
// Insert new instance at the same position
397+
return InsertIntoStack(prefab, idx);
398+
}
399+
400+
/// <summary>
401+
/// Replaces the specified instance in the stack with a new window of the specified type.
402+
/// The old instance plays its exit transition while the new one plays its enter transition.
403+
/// The new window takes the same position in the stack as the old one.
404+
/// </summary>
405+
public T Replace<T>(MonoView instance) where T : MonoView
406+
{
407+
if (!TryGet<T>(out var prefab))
408+
{
409+
Debug.LogError($"[WindowStack] No window prefab of type `{typeof(T)}` found in WindowPrefabs.", this);
410+
return null;
411+
}
412+
413+
return (T)Replace(instance, prefab);
414+
}
415+
416+
/// <summary>
417+
/// Replaces the specified instance in the stack with a new window instantiated from the provided prefab.
418+
/// If the instance is no longer part of the stack, pushes the new window instead.
419+
/// </summary>
420+
public MonoView ReplaceOrPush(MonoView instance, MonoView prefab)
421+
{
422+
if (!Application.isPlaying)
423+
return null;
424+
425+
if (prefab == null)
426+
{
427+
Debug.LogError("[WindowStack] Provided prefab is null.", this);
428+
return null;
429+
}
430+
431+
int idx = _stack.IndexOf(instance);
432+
433+
if (idx == -1)
434+
return Push(prefab);
435+
436+
return Replace(instance, prefab);
437+
}
438+
439+
/// <summary>
440+
/// Replaces the specified instance in the stack with a new window of the specified type.
441+
/// If the instance is no longer part of the stack, pushes the new window instead.
442+
/// </summary>
443+
public T ReplaceOrPush<T>(MonoView instance) where T : MonoView
444+
{
445+
if (!TryGet<T>(out var prefab))
446+
{
447+
Debug.LogError($"[WindowStack] No window prefab of type `{typeof(T)}` found in WindowPrefabs.", this);
448+
return null;
449+
}
450+
451+
return (T)ReplaceOrPush(instance, prefab);
452+
}
453+
331454
/// <summary>
332455
/// Moves the specified instance to the top of the stack. If the instance is not in the stack, does nothing.
333456
/// </summary>
@@ -372,6 +495,7 @@ private void RemoveTop()
372495
var topView = _stack[^1];
373496
_stack.RemoveAt(_stack.Count - 1);
374497
topView.OnPopped();
498+
topView.parentStack = null;
375499

376500
var transition = topView.ExitTransition();
377501
if (transition != null)
@@ -388,11 +512,15 @@ private void RemoveTop()
388512

389513
private MonoView AddToStack(MonoView prefab)
390514
{
391-
var idx = _stack.Count;
515+
return InsertIntoStack(prefab, _stack.Count);
516+
}
517+
518+
private MonoView InsertIntoStack(MonoView prefab, int idx)
519+
{
392520
var instance = Instantiate(prefab, _parent);
393-
_stack.Add(instance);
521+
_stack.Insert(idx, instance);
394522
instance.Initialize(this);
395-
instance.UpdateOrder(idx + _orderOffset);
523+
UpdateOrder(idx);
396524
instance.OnPushed();
397525

398526
var transition = instance.EnterTransition();
@@ -427,5 +555,10 @@ private static IEnumerator RunExitTransition(MonoView view, IEnumerator transiti
427555
yield return transition;
428556
if (view) view.DestroyMe();
429557
}
558+
559+
private void OnDestroy()
560+
{
561+
Clear();
562+
}
430563
}
431564
}

Assets/PurrUI/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "dev.purrnet.ui",
3-
"version": "1.3.0",
3+
"version": "1.4.0-beta.3",
44
"author": {
55
"name": "PurrNet Team",
66
"url": "https://purrnet.dev/"

0 commit comments

Comments
 (0)