internal void Push(IScreen source, IScreen newScreen)
        {
            if (stack.Contains(newScreen))
            {
                throw new ScreenAlreadyEnteredException();
            }

            if (source == null && stack.Count > 0)
            {
                throw new InvalidOperationException($"A source must be provided when pushing to a non-empty {nameof(ScreenStack)}");
            }

            if (newScreen.AsDrawable().RemoveWhenNotAlive)
            {
                throw new ScreenWillBeRemovedOnPushException(newScreen.GetType());
            }

            // Suspend the current screen, if there is one
            if (source != null && source != stack.Peek())
            {
                throw new ScreenNotCurrentException(nameof(Push));
            }

            var newScreenDrawable = newScreen.AsDrawable();

            if (newScreenDrawable.IsLoaded)
            {
                throw new InvalidOperationException("A screen should not be loaded before being pushed.");
            }

            if (suspendImmediately)
            {
                suspend(source, newScreen);
            }

            stack.Push(newScreen);
            ScreenPushed?.Invoke(source, newScreen);

            // this needs to be queued here before the load is begun so it preceed any potential OnSuspending event (also attached to OnLoadComplete).
            newScreenDrawable.OnLoadComplete += _ => newScreen.OnEntering(new ScreenTransitionEvent(source, newScreen));

            if (source == null)
            {
                // this is the first screen to be loaded.
                if (LoadState >= LoadState.Ready)
                {
                    LoadScreen(this, newScreenDrawable, () => finishPush(null, newScreen));
                }
                else
                {
                    log($"scheduling push {getTypeString(newScreen)}");
                    Schedule(() => finishPush(null, newScreen));
                }
            }
            else
            {
                LoadScreen((CompositeDrawable)source, newScreenDrawable, () => finishPush(source, newScreen));
            }
        }
Beispiel #2
0
        internal void Push(IScreen source, IScreen newScreen)
        {
            if (stack.Contains(newScreen))
            {
                throw new ScreenAlreadyEnteredException();
            }

            if (source == null && stack.Count > 0)
            {
                throw new InvalidOperationException($"A source must be provided when pushing to a non-empty {nameof(ScreenStack)}");
            }

            if (newScreen.AsDrawable().RemoveWhenNotAlive)
            {
                throw new ScreenWillBeRemovedOnPushException(newScreen.GetType());
            }

            // Suspend the current screen, if there is one
            if (source != null)
            {
                if (source != stack.Peek())
                {
                    throw new ScreenNotCurrentException(nameof(Push));
                }

                source.OnSuspending(newScreen);
                source.AsDrawable().Expire();
            }

            // Push the new screen
            stack.Push(newScreen);
            ScreenPushed?.Invoke(source, newScreen);

            void finishLoad()
            {
                if (!newScreen.ValidForPush)
                {
                    exitFrom(null);
                    return;
                }

                AddInternal(newScreen.AsDrawable());
                newScreen.OnEntering(source);
            }

            if (source != null)
            {
                LoadScreen((CompositeDrawable)source, newScreen.AsDrawable(), finishLoad);
            }
            else if (LoadState >= LoadState.Ready)
            {
                LoadScreen(this, newScreen.AsDrawable(), finishLoad);
            }
            else
            {
                Schedule(finishLoad);
            }
        }
        internal void Push(IScreen source, IScreen newScreen)
        {
            if (stack.Contains(newScreen))
            {
                throw new ScreenAlreadyEnteredException();
            }

            if (source == null && stack.Count > 0)
            {
                throw new InvalidOperationException($"A source must be provided when pushing to a non-empty {nameof(ScreenStack)}");
            }

            if (newScreen.AsDrawable().RemoveWhenNotAlive)
            {
                throw new ScreenWillBeRemovedOnPushException(newScreen.GetType());
            }

            // Suspend the current screen, if there is one
            if (source != null && source != stack.Peek())
            {
                throw new ScreenNotCurrentException(nameof(Push));
            }

            if (suspendImmediately)
            {
                suspend(source, newScreen);
            }

            stack.Push(newScreen);
            ScreenPushed?.Invoke(source, newScreen);

            var newScreenDrawable = newScreen.AsDrawable();

            if (source == null)
            {
                // this is the first screen to be loaded.
                if (LoadState >= LoadState.Ready)
                {
                    LoadScreen(this, newScreenDrawable, () => finishPush(null, newScreen));
                }
                else
                {
                    Schedule(() => finishPush(null, newScreen));
                }
            }
            else
            {
                LoadScreen((CompositeDrawable)source, newScreenDrawable, () => finishPush(source, newScreen));
            }
        }
        /// <summary>
        /// Complete suspend of a screen in the stack.
        /// </summary>
        /// <param name="from">The screen being suspended.</param>
        /// <param name="to">The screen being entered.</param>
        private void suspend(IScreen from, IScreen to)
        {
            var sourceDrawable = from?.AsDrawable();

            if (sourceDrawable == null)
            {
                return;
            }

            if (sourceDrawable.IsLoaded)
            {
                performSuspend();
            }
            else
            {
                // Screens only receive OnEntering() upon load completion, so OnSuspending() should be delayed until after that
                sourceDrawable.OnLoadComplete += _ => performSuspend();
            }

            void performSuspend()
            {
                from.OnSuspending(to);
                sourceDrawable.Expire();
            }
        }
        private void suspend(IScreen from, IScreen to)
        {
            var sourceDrawable = from.AsDrawable();

            if (sourceDrawable == null)
            {
                return;
            }

            if (sourceDrawable.IsLoaded)
            {
                performSuspend();
            }
            else
            {
                // Screens only receive OnEntering() upon load completion, so OnSuspending() should be delayed until after that
                sourceDrawable.OnLoadComplete += _ => performSuspend();
            }

            void performSuspend()
            {
                log($"suspended {getTypeString(from)} (waiting on {getTypeString(to)})");
                from.OnSuspending(new ScreenTransitionEvent(from, to));
                sourceDrawable.Expire();
            }
        }
Beispiel #6
0
        /// <summary>
        /// Complete push of a loaded screen.
        /// </summary>
        /// <param name="from">The screen to push to.</param>
        /// <param name="to">The new screen being pushed.</param>
        private void push(IScreen from, IScreen to)
        {
            if (!suspendImmediately)
            {
                suspend(from, to);
            }

            if (!to.ValidForPush)
            {
                exitFrom(null, shouldFireEvent: false);
                return;
            }

            AddInternal(to.AsDrawable());
            to.OnEntering(from);
        }
        /// <summary>
        /// Complete push of a loaded screen.
        /// </summary>
        /// <param name="parent">The screen to push to.</param>
        /// <param name="child">The new screen being pushed.</param>
        private void finishPush(IScreen parent, IScreen child)
        {
            if (!child.ValidForPush)
            {
                if (child == CurrentScreen)
                {
                    exitFrom(null, shouldFireExitEvent: false, shouldFireResumeEvent: suspendImmediately);
                }

                return;
            }

            if (!suspendImmediately)
            {
                suspend(parent, child);
            }

            AddInternal(child.AsDrawable());
        }
        private void finishPush(IScreen parent, IScreen child)
        {
            if (!child.ValidForPush)
            {
                if (child == CurrentScreen)
                {
                    exitFrom(null, shouldFireExitEvent: false, shouldFireResumeEvent: suspendImmediately);
                }

                log($"push of {getTypeString(child)} cancelled due to {nameof(child.ValidForPush)} becoming false");
                return;
            }

            if (!suspendImmediately)
            {
                suspend(parent, child);
            }

            AddInternal(child.AsDrawable());
            log($"entered {getTypeString(child)}");
        }