Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

Initial implementation of Segues #2816

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 26 additions & 5 deletions Xamarin.Forms.Core/Application.cs
Original file line number Diff line number Diff line change
Expand Up @@ -369,34 +369,55 @@ public NavigationImpl(Application owner)
_owner = owner;
}

protected override async Task<Page> OnPopModal(bool animated)
protected internal override Task OnSegue(ValueSegue segue, SegueTarget target)
{
switch (segue.Action)
{
case NavigationAction.PopModal:
case NavigationAction.Pop when this.ShouldPopModal():
return OnPopModal(segue);

case NavigationAction.Modal:
return OnPushModal(segue, target);
}
return base.OnSegue(segue, target);
}

async Task<Page> OnPopModal(ValueSegue segue)
{
Page modal = ModalStack[ModalStack.Count - 1];
if (_owner.OnModalPopping(modal))
{
_owner.OnPopCanceled();
return null;
}
Page result = await base.OnPopModal(animated);
Page result = await ((Task<Page>)base.OnSegue(segue, null));
result.Parent = null;
_owner.OnModalPopped(result);
return result;
}

protected override async Task OnPushModal(Page modal, bool animated)
async Task OnPushModal(ValueSegue segue, SegueTarget target)
{
var modal = target.ToPage();

// IMPORTANT! If the target was a template, create a new SegueTarget from
// the instantiated page.
if (target.IsTemplate)
target = (SegueTarget)modal;

_owner.OnModalPushing(modal);

modal.Parent = _owner;

if (modal.NavigationProxy.ModalStack.Count == 0)
{
modal.NavigationProxy.Inner = this;
await base.OnPushModal(modal, animated);
await base.OnSegue(segue, target);
}
else
{
await base.OnPushModal(modal, animated);
await base.OnSegue(segue, target);
modal.NavigationProxy.Inner = this;
}

Expand Down
2 changes: 1 addition & 1 deletion Xamarin.Forms.Core/Button.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
namespace Xamarin.Forms
{
[RenderWith(typeof(_ButtonRenderer))]
public class Button : View, IFontElement, ITextElement, IBorderElement, IButtonController, IElementConfiguration<Button>, IPaddingElement
public class Button : View, IFontElement, ITextElement, IBorderElement, IButtonController, IElementConfiguration<Button>, IPaddingElement, ICommandableElement
{
const double DefaultSpacing = 10;
const int DefaultBorderRadius = 5;
Expand Down
3 changes: 2 additions & 1 deletion Xamarin.Forms.Core/Cells/TextCell.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
using System;
using System.Windows.Input;
using Xamarin.Forms.Internals;

namespace Xamarin.Forms
{
public class TextCell : Cell
public class TextCell : Cell, ICommandableElement
{
public static readonly BindableProperty CommandProperty = BindableProperty.Create("Command", typeof(ICommand), typeof(TextCell), default(ICommand),
propertyChanging: (bindable, oldvalue, newvalue) =>
Expand Down
3 changes: 2 additions & 1 deletion Xamarin.Forms.Core/ClickGestureRecognizer.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.ComponentModel;
using System.Windows.Input;
using Xamarin.Forms.Internals;

namespace Xamarin.Forms
{
Expand All @@ -11,7 +12,7 @@ public enum ButtonsMask
Secondary = 1 << 1
}

public sealed class ClickGestureRecognizer : GestureRecognizer
public sealed class ClickGestureRecognizer : GestureRecognizer, ICommandableElement
{
public static readonly BindableProperty CommandProperty = BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(ClickGestureRecognizer), null);

Expand Down
14 changes: 13 additions & 1 deletion Xamarin.Forms.Core/Entry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
namespace Xamarin.Forms
{
[RenderWith(typeof(_EntryRenderer))]
public class Entry : InputView, IFontElement, ITextElement, ITextAlignmentElement, IEntryController, IElementConfiguration<Entry>
public class Entry : InputView, IFontElement, ITextElement, ITextAlignmentElement, IEntryController, IElementConfiguration<Entry>, ICommandableElement
{
public static readonly BindableProperty ReturnTypeProperty = BindableProperty.Create(nameof(ReturnType), typeof(ReturnType), typeof(Entry), ReturnType.Default);

Expand Down Expand Up @@ -137,6 +137,18 @@ public object ReturnCommandParameter
set => SetValue(ReturnCommandParameterProperty, value);
}

ICommand ICommandableElement.Command
{
get => ReturnCommand;
set => ReturnCommand = value;
}

object ICommandableElement.CommandParameter
{
get => ReturnCommandParameter;
set => ReturnCommandParameter = value;
}

double IFontElement.FontSizeDefaultValueCreator() =>
Device.GetNamedSize(NamedSize.Default, (Entry)this);

Expand Down
13 changes: 13 additions & 0 deletions Xamarin.Forms.Core/ICommandableElement.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System.Windows.Input;
using System.ComponentModel;

namespace Xamarin.Forms.Internals
{
// implementing classes must be castable to Element
[EditorBrowsable(EditorBrowsableState.Never)]
public interface ICommandableElement
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

keep it internal

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, the BP can be extracted in a static helper class (see ITextElement, TextElement)

{
ICommand Command { get; set; }
object CommandParameter { get; set; }
}
}
4 changes: 4 additions & 0 deletions Xamarin.Forms.Core/INavigation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ public interface INavigation
Task PushModalAsync(Page page);
Task PushModalAsync(Page page, bool animated);

Task ShowAsync(Page page);
Task ShowAsync(Page page, bool animated);
Task SegueAsync(Segue segue, SegueTarget target);

void RemovePage(Page page);
}
}
20 changes: 20 additions & 0 deletions Xamarin.Forms.Core/Internals/SegueRequestedEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System.ComponentModel;
using System.Threading.Tasks;

namespace Xamarin.Forms.Internals
{
[EditorBrowsable(EditorBrowsableState.Never)]
public class SegueRequestedEventArgs
{
public ValueSegue Segue { get; }
public SegueTarget Target { get; }
public Task Task { get; set; }
public bool Handled { get; set; }

internal SegueRequestedEventArgs(ValueSegue segue, SegueTarget target)
{
Segue = segue;
Target = target;
}
}
}
3 changes: 2 additions & 1 deletion Xamarin.Forms.Core/MenuItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Input;
using Xamarin.Forms.Internals;

namespace Xamarin.Forms
{

public class MenuItem : BaseMenuItem, IMenuItemController
public class MenuItem : BaseMenuItem, IMenuItemController, ICommandableElement
{
public static readonly BindableProperty AcceleratorProperty = BindableProperty.CreateAttached(nameof(Accelerator), typeof(Accelerator), typeof(MenuItem), null);

Expand Down
105 changes: 93 additions & 12 deletions Xamarin.Forms.Core/NavigationPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,24 @@ public async Task PushAsync(Page page, bool animated)

public event EventHandler<NavigationEventArgs> Pushed;

async Task SegueAsync(ValueSegue segue, SegueTarget target)
{
if (CurrentNavigationTask != null && !CurrentNavigationTask.IsCompleted)
{
var tcs = new TaskCompletionSource<bool>();
Task oldTask = CurrentNavigationTask;
CurrentNavigationTask = tcs.Task;
await oldTask;

await SegueAsyncInner(segue, target);
tcs.SetResult(true);
return;
}

CurrentNavigationTask = SegueAsyncInner(segue, target);
await CurrentNavigationTask;
}

public static void SetBackButtonTitle(BindableObject page, string value)
{
page.SetValue(BackButtonTitleProperty, value);
Expand Down Expand Up @@ -297,6 +315,9 @@ async Task<Page> INavigationPageController.RemoveAsyncInner(Page page, bool anim
[EditorBrowsable(EditorBrowsableState.Never)]
public event EventHandler<NavigationRequestedEventArgs> RemovePageRequested;

[EditorBrowsable(EditorBrowsableState.Never)]
public event EventHandler<SegueRequestedEventArgs> SegueRequested;

void InsertPageBefore(Page page, Page before)
{
if (page == null)
Expand Down Expand Up @@ -381,6 +402,67 @@ void PushPage(Page page)
CurrentPage = page;
}

async Task SegueAsyncInner(ValueSegue seg, SegueTarget target)
{
var exec = seg.Segue as ISegueExecution;
if (exec != null)
{
switch (seg.Action)
{
case NavigationAction.Show:
case NavigationAction.Push:
if (target.IsTemplate)
target = (SegueTarget)target.ToPage();
break;
case NavigationAction.Pop:
case NavigationAction.PopPushed:
var page = (Page)InternalChildren[InternalChildren.Count - 2];
target = (SegueTarget)page;
break;

case NavigationAction.PopToRoot:
target = (SegueTarget)RootPage;
break;
}
if (!await exec.OnBeforeExecute(target))
return;
}

var handled = false;

EventHandler<SegueRequestedEventArgs> requestSegue = SegueRequested;
if (requestSegue != null)
{
var args = new SegueRequestedEventArgs(seg, target);
requestSegue(this, args);

if (args.Task != null)
await args.Task;

handled = args.Handled;
}

if (!handled)
{
switch (seg.Action)
{
case NavigationAction.Show:
case NavigationAction.Push:
await PushAsyncInner(target.ToPage(), seg.IsAnimated);
break;

case NavigationAction.Pop:
case NavigationAction.PopPushed:
await PopAsyncInner(seg.IsAnimated, false);
break;

case NavigationAction.PopToRoot:
await PopToRootAsyncInner(seg.IsAnimated);
break;
}
}
}

void RemovePage(Page page)
{
if (page == null)
Expand Down Expand Up @@ -437,19 +519,18 @@ protected override void OnInsertPageBefore(Page page, Page before)
Owner.InsertPageBefore(page, before);
}

protected override Task<Page> OnPopAsync(bool animated)
{
return Owner.PopAsync(animated);
}

protected override Task OnPopToRootAsync(bool animated)
{
return Owner.PopToRootAsync(animated);
}

protected override Task OnPushAsync(Page root, bool animated)
protected internal override Task OnSegue(ValueSegue seg, SegueTarget target)
{
return Owner.PushAsync(root, animated);
switch (seg.Action)
{
case NavigationAction.Show:
case NavigationAction.Push:
case NavigationAction.Pop:
case NavigationAction.PopPushed:
case NavigationAction.PopToRoot:
return Owner.SegueAsync(seg, target);
}
return base.OnSegue(seg, target);
}

protected override void OnRemovePage(Page page)
Expand Down
Loading