зеркало из https://github.com/DeGsoft/maui-linux.git
Merge branch '4.4.0'
This commit is contained in:
Коммит
1b9c22b4b9
|
@ -390,14 +390,22 @@ namespace Xamarin.Forms.Build.Tasks
|
|||
public static TypeReference ResolveGenericParameters(this TypeReference self, TypeReference declaringTypeReference)
|
||||
{
|
||||
var genericdeclType = declaringTypeReference as GenericInstanceType;
|
||||
if (genericdeclType == null)
|
||||
var genericParameterSelf = self as GenericParameter;
|
||||
var genericself = self as GenericInstanceType;
|
||||
|
||||
if (genericdeclType == null && genericParameterSelf == null && genericself == null)
|
||||
return self;
|
||||
|
||||
var genericParameterSelf = self as GenericParameter;
|
||||
if (genericdeclType == null && genericParameterSelf!=null)
|
||||
{
|
||||
var typeDef = declaringTypeReference.Resolve();
|
||||
if (typeDef.BaseType == null || typeDef.BaseType.FullName == "System.Object")
|
||||
return self;
|
||||
return self.ResolveGenericParameters(typeDef.BaseType.ResolveGenericParameters(declaringTypeReference));
|
||||
}
|
||||
if (genericParameterSelf != null)
|
||||
return genericdeclType.GenericArguments[genericParameterSelf.Position];
|
||||
|
||||
var genericself = self as GenericInstanceType;
|
||||
if (genericself != null)
|
||||
return genericself.ResolveGenericParameters(declaringTypeReference);
|
||||
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Text;
|
||||
using Xamarin.Forms.CustomAttributes;
|
||||
using Xamarin.Forms.Internals;
|
||||
using System.Linq;
|
||||
|
||||
|
||||
#if UITEST
|
||||
using Xamarin.UITest;
|
||||
using NUnit.Framework;
|
||||
using Xamarin.Forms.Core.UITests;
|
||||
#endif
|
||||
|
||||
namespace Xamarin.Forms.Controls.Issues
|
||||
{
|
||||
[Preserve(AllMembers = true)]
|
||||
[Issue(IssueTracker.Github, 8262, "[Android] ImageRenderer still being accessed after control destroyed",
|
||||
PlatformAffected.Android)]
|
||||
#if UITEST
|
||||
[NUnit.Framework.Category(UITestCategories.LifeCycle)]
|
||||
[NUnit.Framework.Category(UITestCategories.CollectionView)]
|
||||
#endif
|
||||
public class Issue8262 : TestContentPage
|
||||
{
|
||||
public View WithBounds(View v, double x, double y, double w, double h)
|
||||
{
|
||||
AbsoluteLayout.SetLayoutBounds(v, new Rectangle(x, y, w, h));
|
||||
return v;
|
||||
}
|
||||
|
||||
protected override void Init()
|
||||
{
|
||||
IEnumerable<View> Select((string groupHeader, IEnumerable<int> items) t)
|
||||
{
|
||||
yield return new AbsoluteLayout
|
||||
{
|
||||
Children = {
|
||||
WithBounds(new Label {
|
||||
Text = t.groupHeader, HorizontalTextAlignment = TextAlignment.Center,
|
||||
TextColor = Color.FromUint(0xff5a5a5a), FontSize = 10
|
||||
}, 0, 21.1, 310, AbsoluteLayout.AutoSize) },
|
||||
HorizontalOptions = LayoutOptions.FillAndExpand,
|
||||
VerticalOptions = LayoutOptions.Start,
|
||||
HeightRequest = 46
|
||||
};
|
||||
foreach (var item in t.items)
|
||||
{
|
||||
yield return new AbsoluteLayout
|
||||
{
|
||||
Children = {
|
||||
|
||||
WithBounds(new Image { Source = ImageSource.FromResource("Xamarin.Forms.Controls.GalleryPages.crimson.jpg", System.Reflection.Assembly.GetCallingAssembly()) }, 23.6, 14.5, 14.9, 20.7),
|
||||
|
||||
WithBounds(new Label { Text = item.ToString(), TextColor = Color.FromUint(0xff5a5a5a), FontSize = 10 }, 58, 18.2, AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize)
|
||||
},
|
||||
HorizontalOptions = LayoutOptions.FillAndExpand,
|
||||
VerticalOptions = LayoutOptions.Start,
|
||||
HeightRequest = 49.7
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Content = new AbsoluteLayout
|
||||
{
|
||||
Children = {
|
||||
new CollectionView {
|
||||
ItemsSource =
|
||||
new (string, Func<int, bool>)[] {
|
||||
("odd", i => i % 2 == 1),
|
||||
("even", i => i % 2 == 0),
|
||||
("triple", i => i % 3 == 0),
|
||||
("fives", i => i % 5 == 0) }
|
||||
.Select(t => (t.Item1, Enumerable.Range(1, 100).Where(t.Item2)))
|
||||
.SelectMany(Select),
|
||||
ItemTemplate = new DataTemplate(() => {
|
||||
var template = new ContentView();
|
||||
template.SetBinding(ContentView.ContentProperty, ".");
|
||||
return template;
|
||||
}),
|
||||
ItemsLayout = LinearItemsLayout.Vertical,
|
||||
ItemSizingStrategy = ItemSizingStrategy.MeasureFirstItem,
|
||||
AutomationId = "ScrollMe"
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#if UITEST
|
||||
[Test]
|
||||
public void ScrollingQuicklyOnCollectionViewDoesntCrashOnDestroyedImage()
|
||||
{
|
||||
RunningApp.WaitForElement("ScrollMe");
|
||||
RunningApp.ScrollDown("ScrollMe", ScrollStrategy.Gesture, swipeSpeed: 20000);
|
||||
RunningApp.ScrollUp("ScrollMe", ScrollStrategy.Gesture, swipeSpeed: 20000);
|
||||
RunningApp.ScrollDown("ScrollMe", ScrollStrategy.Gesture, swipeSpeed: 20000);
|
||||
RunningApp.ScrollUp("ScrollMe", ScrollStrategy.Gesture, swipeSpeed: 20000);
|
||||
RunningApp.ScrollDown("ScrollMe", ScrollStrategy.Gesture, swipeSpeed: 20000);
|
||||
RunningApp.WaitForElement("ScrollMe");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
using Xamarin.Forms.CustomAttributes;
|
||||
using Xamarin.Forms.Internals;
|
||||
|
||||
|
||||
#if UITEST
|
||||
using NUnit.Framework;
|
||||
using Xamarin.Forms.Core.UITests;
|
||||
#endif
|
||||
|
||||
namespace Xamarin.Forms.Controls.Issues
|
||||
{
|
||||
[Preserve(AllMembers = true)]
|
||||
[Issue(IssueTracker.Github, 8461, "[Bug] [iOS] [Shell] \"Nav Stack consistency error\"",
|
||||
PlatformAffected.iOS)]
|
||||
#if UITEST
|
||||
[NUnit.Framework.Category(UITestCategories.Shell)]
|
||||
[NUnit.Framework.Category(UITestCategories.Navigation)]
|
||||
#endif
|
||||
public class Issue8461 : TestShell
|
||||
{
|
||||
const string ButtonId = "PageButtonId";
|
||||
const string LayoutId = "LayoutId";
|
||||
|
||||
protected override void Init()
|
||||
{
|
||||
var page1 = CreateContentPage("page 1");
|
||||
var page2 = new ContentPage() { Title = "page 2" };
|
||||
|
||||
var pushPageBtn = new Button();
|
||||
pushPageBtn.Text = "Push Page";
|
||||
pushPageBtn.AutomationId = ButtonId;
|
||||
pushPageBtn.Clicked += (sender, args) =>
|
||||
{
|
||||
Navigation.PushAsync(page2);
|
||||
};
|
||||
|
||||
page1.Content = new StackLayout()
|
||||
{
|
||||
Children =
|
||||
{
|
||||
pushPageBtn
|
||||
}
|
||||
};
|
||||
|
||||
var instructions = new StackLayout()
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label()
|
||||
{
|
||||
Text = "1. Swipe left to dismiss this page, but cancel the gesture before it completes"
|
||||
},
|
||||
new Label()
|
||||
{
|
||||
Text = "2. Swipe left to dismiss this page again, crashes immediately"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Grid.SetColumn(instructions, 1);
|
||||
|
||||
page2.Content = new Grid()
|
||||
{
|
||||
HorizontalOptions = LayoutOptions.FillAndExpand,
|
||||
VerticalOptions = LayoutOptions.FillAndExpand,
|
||||
|
||||
ColumnDefinitions = new ColumnDefinitionCollection()
|
||||
{
|
||||
new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) },
|
||||
new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) },
|
||||
},
|
||||
|
||||
Children =
|
||||
{
|
||||
// Use this BoxView to achor our swipe to left of the screen
|
||||
new BoxView()
|
||||
{
|
||||
AutomationId = LayoutId,
|
||||
HorizontalOptions = LayoutOptions.FillAndExpand,
|
||||
VerticalOptions = LayoutOptions.FillAndExpand,
|
||||
BackgroundColor = Color.Red
|
||||
},
|
||||
instructions
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#if UITEST && __IOS__
|
||||
[Test]
|
||||
public void ShellSwipeToDismiss()
|
||||
{
|
||||
var pushButton = RunningApp.WaitForElement(ButtonId);
|
||||
Assert.AreEqual(1, pushButton.Length);
|
||||
|
||||
RunningApp.Tap(ButtonId);
|
||||
|
||||
var page2Layout = RunningApp.WaitForElement(LayoutId);
|
||||
Assert.AreEqual(1, page2Layout.Length);
|
||||
// Swipe in from left across 1/2 of screen width
|
||||
RunningApp.SwipeLeftToRight(LayoutId, 0.99, 500, false);
|
||||
// Swipe in from left across full screen width
|
||||
RunningApp.SwipeLeftToRight(0.99, 500);
|
||||
|
||||
pushButton = RunningApp.WaitForElement(ButtonId);
|
||||
Assert.AreEqual(1, pushButton.Length);
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
using System.ComponentModel;
|
||||
using Xamarin.Forms.CustomAttributes;
|
||||
using Xamarin.Forms.Internals;
|
||||
|
||||
namespace Xamarin.Forms.Controls.Issues
|
||||
{
|
||||
[Preserve(AllMembers = true)]
|
||||
[Issue(IssueTracker.Github, 8644, "WPF Entry crashes when IsPassword=true", PlatformAffected.WPF)]
|
||||
public class Issue8644 : TestContentPage
|
||||
{
|
||||
public class BinCon : INotifyPropertyChanged
|
||||
{
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
string _title;
|
||||
public string Title
|
||||
{
|
||||
get => _title;
|
||||
set
|
||||
{
|
||||
_title = value?.Length > 4 ? value.Substring(0, 4) : value;
|
||||
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Title)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
protected override void Init()
|
||||
{
|
||||
var bc = new BinCon();
|
||||
var e1 = new Entry
|
||||
{
|
||||
BindingContext = bc,
|
||||
Margin = new Thickness(50),
|
||||
HorizontalOptions = LayoutOptions.FillAndExpand,
|
||||
IsPassword = true,
|
||||
};
|
||||
e1.SetBinding(Entry.TextProperty, nameof(BinCon.Title));
|
||||
|
||||
// Label just to show current Entry text, not needed for test
|
||||
var lbl = new Label { BindingContext = bc };
|
||||
lbl.SetBinding(Label.TextProperty, nameof(BinCon.Title));
|
||||
var stack = new StackLayout
|
||||
{
|
||||
|
||||
Children = {
|
||||
new Label { Text = "Type more than 4 symbols" },
|
||||
e1,
|
||||
lbl,
|
||||
}
|
||||
};
|
||||
|
||||
Content = stack;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
using Xamarin.Forms.CustomAttributes;
|
||||
using Xamarin.Forms.Internals;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System;
|
||||
|
||||
#if UITEST
|
||||
using Xamarin.Forms.Core.UITests;
|
||||
using Xamarin.UITest;
|
||||
using NUnit.Framework;
|
||||
#endif
|
||||
|
||||
namespace Xamarin.Forms.Controls.Issues
|
||||
{
|
||||
#if UITEST
|
||||
[NUnit.Framework.Category(UITestCategories.Shell)]
|
||||
#endif
|
||||
[Preserve(AllMembers = true)]
|
||||
[Issue(IssueTracker.Github, 8741, "[Bug] [Shell] [Android] ToolbarItem Enabled/Disabled behavior does not work for Shell apps", PlatformAffected.Android)]
|
||||
public class Issue8741 : TestShell
|
||||
{
|
||||
protected override void Init()
|
||||
{
|
||||
var page = CreateContentPage();
|
||||
var toolbarItem = new ToolbarItem
|
||||
{
|
||||
Text = "Add",
|
||||
AutomationId = "Add"
|
||||
};
|
||||
|
||||
toolbarItem.SetBinding(MenuItem.CommandProperty, "ToolbarTappedCommand");
|
||||
page.ToolbarItems.Add(toolbarItem);
|
||||
|
||||
var button = new Button
|
||||
{
|
||||
Text = "Toggle Enabled/Disabled",
|
||||
AutomationId = "ToggleEnabled"
|
||||
};
|
||||
|
||||
button.SetBinding(Button.CommandProperty, "ChangeToggleCommand");
|
||||
var label = new Label();
|
||||
label.SetBinding(Label.TextProperty, "EnabledText");
|
||||
|
||||
var clickCount = new Label();
|
||||
clickCount.AutomationId = "ClickCount";
|
||||
clickCount.SetBinding(Label.TextProperty, "ClickCount");
|
||||
|
||||
page.Content =
|
||||
new StackLayout
|
||||
{
|
||||
Children =
|
||||
{
|
||||
label,
|
||||
clickCount,
|
||||
button
|
||||
}
|
||||
};
|
||||
|
||||
BindingContext = new ViewModelIssue8741();
|
||||
}
|
||||
|
||||
#if UITEST
|
||||
[Test]
|
||||
public void Issue8741Test()
|
||||
{
|
||||
RunningApp.WaitForElement("Add");
|
||||
RunningApp.Tap("Add");
|
||||
#if __ANDROID__
|
||||
var toolbarItemColorValue = GetToolbarItemColorValue();
|
||||
int disabledAlpha = GetAlphaValue(toolbarItemColorValue);
|
||||
#endif
|
||||
Assert.AreEqual("0", RunningApp.WaitForElement("ClickCount")[0].ReadText());
|
||||
|
||||
RunningApp.Tap("ToggleEnabled");
|
||||
RunningApp.Tap("Add");
|
||||
#if __ANDROID__
|
||||
toolbarItemColorValue = GetToolbarItemColorValue();
|
||||
int enabledAlpha = GetAlphaValue(toolbarItemColorValue);
|
||||
Assert.Less(disabledAlpha, enabledAlpha);
|
||||
#endif
|
||||
Assert.AreEqual("1", RunningApp.WaitForElement("ClickCount")[0].ReadText());
|
||||
|
||||
RunningApp.Tap("ToggleEnabled");
|
||||
RunningApp.Tap("Add");
|
||||
|
||||
Assert.AreEqual("1", RunningApp.WaitForElement("ClickCount")[0].ReadText());
|
||||
}
|
||||
|
||||
#if __ANDROID__
|
||||
private object GetToolbarItemColorValue()
|
||||
{
|
||||
return RunningApp.Query(x => x.Text("Add").Invoke("getCurrentTextColor"))[0];
|
||||
}
|
||||
|
||||
private int GetAlphaValue(object toolbarItemColorValue)
|
||||
{
|
||||
int color = Convert.ToInt32(toolbarItemColorValue);
|
||||
int a = (color >> 24) & 0xff;
|
||||
return a;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
[Preserve(AllMembers = true)]
|
||||
public class ViewModelIssue8741 : INotifyPropertyChanged
|
||||
{
|
||||
bool _canAddNewItem;
|
||||
int _clickCount;
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
public bool Enabled
|
||||
{
|
||||
get => _canAddNewItem;
|
||||
set
|
||||
{
|
||||
_canAddNewItem = value;
|
||||
OnPropertyChanged(nameof(Enabled));
|
||||
ToolbarTappedCommand.ChangeCanExecute();
|
||||
}
|
||||
}
|
||||
|
||||
public int ClickCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return _clickCount;
|
||||
}
|
||||
set
|
||||
{
|
||||
_clickCount = value;
|
||||
OnPropertyChanged(nameof(ClickCount));
|
||||
}
|
||||
}
|
||||
|
||||
public string EnabledText { get; set; }
|
||||
public Command ChangeToggleCommand { get; set; }
|
||||
public Command ToolbarTappedCommand { get; set; }
|
||||
|
||||
public ViewModelIssue8741()
|
||||
{
|
||||
ChangeToggleCommand = new Command(ChangeToggle);
|
||||
ToolbarTappedCommand = new Command(ToolbarTapped, () => Enabled);
|
||||
EnabledText = Enabled ? "Enabled" : "Disabled";
|
||||
}
|
||||
|
||||
void ToolbarTapped()
|
||||
{
|
||||
ClickCount++;
|
||||
}
|
||||
|
||||
void ChangeToggle()
|
||||
{
|
||||
Enabled = !Enabled;
|
||||
EnabledText = Enabled ? "Enabled" : "Disabled";
|
||||
OnPropertyChanged(nameof(EnabledText));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Xamarin.Forms.CustomAttributes;
|
||||
using Xamarin.Forms.Internals;
|
||||
|
||||
#if UITEST
|
||||
using Xamarin.Forms.Core.UITests;
|
||||
using Xamarin.UITest;
|
||||
using NUnit.Framework;
|
||||
#endif
|
||||
|
||||
namespace Xamarin.Forms.Controls.Issues
|
||||
{
|
||||
#if UITEST
|
||||
[Category(UITestCategories.Image)]
|
||||
#endif
|
||||
[Preserve(AllMembers = true)]
|
||||
[Issue(IssueTracker.Github, 8821, "Animations of downloaded gifs are not playing on Android", PlatformAffected.Android)]
|
||||
public class Issue8821 : TestContentPage
|
||||
{
|
||||
Image _image;
|
||||
|
||||
public Issue8821()
|
||||
{
|
||||
var instructions = new Label
|
||||
{
|
||||
BackgroundColor = Color.Black,
|
||||
TextColor = Color.White,
|
||||
Text = "Press the DownloadFile button and then the Animate button. Verify that the gif is downloaded and animate without problems."
|
||||
};
|
||||
|
||||
var downloadButton = new Button { Text = "DownloadFile" };
|
||||
downloadButton.Clicked += async (sender, args) =>
|
||||
{
|
||||
string nextURL = "https://upload.wikimedia.org/wikipedia/commons/c/c0/An_example_animation_made_with_Pivot.gif";
|
||||
|
||||
await CreateImage(nextURL);
|
||||
};
|
||||
|
||||
_image = new Image { Source = string.Empty };
|
||||
|
||||
var animateButton = new Button { Text = "Animate" };
|
||||
animateButton.Clicked += (sender, args) =>
|
||||
{
|
||||
_image.IsAnimationPlaying = true;
|
||||
};
|
||||
|
||||
Content = new StackLayout
|
||||
{
|
||||
Padding = new Thickness(20, 35, 20, 20),
|
||||
Children =
|
||||
{
|
||||
instructions,
|
||||
downloadButton,
|
||||
_image,
|
||||
animateButton
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public string SecondImageSource { get; set; }
|
||||
|
||||
protected override void Init()
|
||||
{
|
||||
Title = "Issue 8821";
|
||||
}
|
||||
|
||||
async Task CreateImage(string imageUrl)
|
||||
{
|
||||
var bytes = await DownloadImageAsync(imageUrl);
|
||||
|
||||
string path;
|
||||
|
||||
#if WINDOWS_UWP
|
||||
path = Windows.Storage.ApplicationData.Current.LocalFolder.Path;
|
||||
#else
|
||||
path = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
|
||||
#endif
|
||||
SecondImageSource = Path.Combine(path, "Issue8821.gif");
|
||||
File.WriteAllBytes(SecondImageSource, bytes);
|
||||
|
||||
_image.Source = SecondImageSource;
|
||||
OnPropertyChanged(nameof(SecondImageSource));
|
||||
}
|
||||
|
||||
async Task<byte[]> DownloadImageAsync(string imageUrl)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var httpClient = new HttpClient())
|
||||
using (var httpResponse = await httpClient.GetAsync(imageUrl))
|
||||
{
|
||||
if (httpResponse.StatusCode == HttpStatusCode.OK)
|
||||
return await httpResponse.Content.ReadAsByteArrayAsync();
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -693,6 +693,7 @@ namespace Xamarin.Forms.Controls
|
|||
item.Title = shellItemTitle;
|
||||
|
||||
TShellSection shellSection = Activator.CreateInstance<TShellSection>();
|
||||
shellSection.Title = shellItemTitle;
|
||||
|
||||
shellSection.Items.Add(new ShellContent()
|
||||
{
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
<DependentUpon>Issue3228.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Issue8262.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Issue8207.xaml.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Issue6362.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Issue7505.cs" />
|
||||
|
@ -27,6 +28,7 @@
|
|||
<Compile Include="$(MSBuildThisFileDirectory)CollectionViewHeaderFooterView.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)CollectionViewItemsUpdatingScrollMode.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Issue4606.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Issue8644.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Issue8177.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Issue4744.xaml.cs">
|
||||
<DependentUpon>Issue4744.xaml</DependentUpon>
|
||||
|
@ -148,6 +150,7 @@
|
|||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Issue7963.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Issue8741.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)RefreshViewTests.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Issue7338.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ScrollToGroup.cs" />
|
||||
|
@ -1213,9 +1216,11 @@
|
|||
<Compile Include="$(MSBuildThisFileDirectory)Issue8638.xaml.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Issue8392.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Issue8672.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Issue8821.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Issue8326.xaml.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Issue8449.xaml.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Issue7875.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Issue8461.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Bugzilla22229.xaml">
|
||||
|
|
|
@ -329,5 +329,16 @@ namespace Xamarin.Forms.Core.UnitTests
|
|||
{
|
||||
Assert.AreEqual(Color.Default, (Color)System.Drawing.Color.Empty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DefaultColorsMatch()
|
||||
{
|
||||
//This spot-checks a few of the fields in Color
|
||||
Assert.AreEqual(Color.CornflowerBlue, Color.FromRgb(100, 149, 237));
|
||||
Assert.AreEqual(Color.DarkSalmon, Color.FromRgb(233, 150, 122));
|
||||
Assert.AreEqual(Color.Transparent, Color.FromRgba(255, 255, 255, 0));
|
||||
Assert.AreEqual(Color.Wheat, Color.FromRgb(245, 222, 179));
|
||||
Assert.AreEqual(Color.White, Color.FromRgb(255, 255, 255));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
using System;
|
||||
using NUnit.Framework;
|
||||
using Xamarin.Forms.Internals;
|
||||
|
||||
namespace Xamarin.Forms.Core.UnitTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class NumericExtensionsTests
|
||||
{
|
||||
[Test]
|
||||
public void InRange()
|
||||
{
|
||||
Assert.AreEqual(5, 5.Clamp(0, 10));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BelowMin()
|
||||
{
|
||||
Assert.AreEqual(5, 0.Clamp(5, 10));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AboveMax()
|
||||
{
|
||||
Assert.AreEqual(5, 10.Clamp(0, 5));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MinMaxWrong()
|
||||
{
|
||||
Assert.AreEqual(0, 10.Clamp(5, 0));
|
||||
Assert.AreEqual(0, 5.Clamp(10, 0));
|
||||
Assert.AreEqual(5, 0.Clamp(10, 5));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -76,6 +76,7 @@
|
|||
<Compile Include="CommandSourceTests.cs" />
|
||||
<Compile Include="CommandTests.cs" />
|
||||
<Compile Include="DependencyResolutionTests.cs" />
|
||||
<Compile Include="NumericExtensionsTests.cs" />
|
||||
<Compile Include="RefreshViewTests.cs" />
|
||||
<Compile Include="MockDispatcherProvider.cs" />
|
||||
<Compile Include="MockDispatcher.cs" />
|
||||
|
|
|
@ -279,15 +279,16 @@ namespace Xamarin.Forms
|
|||
return pi;
|
||||
}
|
||||
|
||||
//defined on a base class ?
|
||||
if (sourceType.BaseType is Type baseT && GetIndexer(baseT.GetTypeInfo(), indexerName, content) is PropertyInfo p)
|
||||
return p;
|
||||
|
||||
//defined on an interface ?
|
||||
foreach (var face in sourceType.ImplementedInterfaces) {
|
||||
if (GetIndexer(face.GetTypeInfo(), indexerName, content) is PropertyInfo pi)
|
||||
return pi;
|
||||
}
|
||||
|
||||
//defined on a base class ?
|
||||
if (sourceType.BaseType is Type baseT && GetIndexer(baseT.GetTypeInfo(), indexerName, content) is PropertyInfo p)
|
||||
return p;
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -114,6 +114,26 @@ namespace Xamarin.Forms
|
|||
}
|
||||
}
|
||||
|
||||
Color(int r, int g, int b)
|
||||
{
|
||||
_mode = Mode.Rgb;
|
||||
_r = r / 255f;
|
||||
_g = g / 255f;
|
||||
_b = b / 255f;
|
||||
_a = 1;
|
||||
ConvertToHsl(_r, _g, _b, _mode, out _hue, out _saturation, out _luminosity);
|
||||
}
|
||||
|
||||
Color(int r, int g, int b, int a)
|
||||
{
|
||||
_mode = Mode.Rgb;
|
||||
_r = r / 255f;
|
||||
_g = g / 255f;
|
||||
_b = b / 255f;
|
||||
_a = a / 255f;
|
||||
ConvertToHsl(_r, _g, _b, _mode, out _hue, out _saturation, out _luminosity);
|
||||
}
|
||||
|
||||
public Color(double r, double g, double b) : this(r, g, b, 1)
|
||||
{
|
||||
}
|
||||
|
@ -418,150 +438,150 @@ namespace Xamarin.Forms
|
|||
#region Color Definitions
|
||||
|
||||
// matches colors in WPF's System.Windows.Media.Colors
|
||||
public static readonly Color AliceBlue = FromRgb(240, 248, 255);
|
||||
public static readonly Color AntiqueWhite = FromRgb(250, 235, 215);
|
||||
public static readonly Color Aqua = FromRgb(0, 255, 255);
|
||||
public static readonly Color Aquamarine = FromRgb(127, 255, 212);
|
||||
public static readonly Color Azure = FromRgb(240, 255, 255);
|
||||
public static readonly Color Beige = FromRgb(245, 245, 220);
|
||||
public static readonly Color Bisque = FromRgb(255, 228, 196);
|
||||
public static readonly Color Black = FromRgb(0, 0, 0);
|
||||
public static readonly Color BlanchedAlmond = FromRgb(255, 235, 205);
|
||||
public static readonly Color Blue = FromRgb(0, 0, 255);
|
||||
public static readonly Color BlueViolet = FromRgb(138, 43, 226);
|
||||
public static readonly Color Brown = FromRgb(165, 42, 42);
|
||||
public static readonly Color BurlyWood = FromRgb(222, 184, 135);
|
||||
public static readonly Color CadetBlue = FromRgb(95, 158, 160);
|
||||
public static readonly Color Chartreuse = FromRgb(127, 255, 0);
|
||||
public static readonly Color Chocolate = FromRgb(210, 105, 30);
|
||||
public static readonly Color Coral = FromRgb(255, 127, 80);
|
||||
public static readonly Color CornflowerBlue = FromRgb(100, 149, 237);
|
||||
public static readonly Color Cornsilk = FromRgb(255, 248, 220);
|
||||
public static readonly Color Crimson = FromRgb(220, 20, 60);
|
||||
public static readonly Color Cyan = FromRgb(0, 255, 255);
|
||||
public static readonly Color DarkBlue = FromRgb(0, 0, 139);
|
||||
public static readonly Color DarkCyan = FromRgb(0, 139, 139);
|
||||
public static readonly Color DarkGoldenrod = FromRgb(184, 134, 11);
|
||||
public static readonly Color DarkGray = FromRgb(169, 169, 169);
|
||||
public static readonly Color DarkGreen = FromRgb(0, 100, 0);
|
||||
public static readonly Color DarkKhaki = FromRgb(189, 183, 107);
|
||||
public static readonly Color DarkMagenta = FromRgb(139, 0, 139);
|
||||
public static readonly Color DarkOliveGreen = FromRgb(85, 107, 47);
|
||||
public static readonly Color DarkOrange = FromRgb(255, 140, 0);
|
||||
public static readonly Color DarkOrchid = FromRgb(153, 50, 204);
|
||||
public static readonly Color DarkRed = FromRgb(139, 0, 0);
|
||||
public static readonly Color DarkSalmon = FromRgb(233, 150, 122);
|
||||
public static readonly Color DarkSeaGreen = FromRgb(143, 188, 143);
|
||||
public static readonly Color DarkSlateBlue = FromRgb(72, 61, 139);
|
||||
public static readonly Color DarkSlateGray = FromRgb(47, 79, 79);
|
||||
public static readonly Color DarkTurquoise = FromRgb(0, 206, 209);
|
||||
public static readonly Color DarkViolet = FromRgb(148, 0, 211);
|
||||
public static readonly Color DeepPink = FromRgb(255, 20, 147);
|
||||
public static readonly Color DeepSkyBlue = FromRgb(0, 191, 255);
|
||||
public static readonly Color DimGray = FromRgb(105, 105, 105);
|
||||
public static readonly Color DodgerBlue = FromRgb(30, 144, 255);
|
||||
public static readonly Color Firebrick = FromRgb(178, 34, 34);
|
||||
public static readonly Color FloralWhite = FromRgb(255, 250, 240);
|
||||
public static readonly Color ForestGreen = FromRgb(34, 139, 34);
|
||||
public static readonly Color Fuchsia = FromRgb(255, 0, 255);
|
||||
public static readonly Color AliceBlue = new Color(240, 248, 255);
|
||||
public static readonly Color AntiqueWhite = new Color(250, 235, 215);
|
||||
public static readonly Color Aqua = new Color(0, 255, 255);
|
||||
public static readonly Color Aquamarine = new Color(127, 255, 212);
|
||||
public static readonly Color Azure = new Color(240, 255, 255);
|
||||
public static readonly Color Beige = new Color(245, 245, 220);
|
||||
public static readonly Color Bisque = new Color(255, 228, 196);
|
||||
public static readonly Color Black = new Color(0, 0, 0);
|
||||
public static readonly Color BlanchedAlmond = new Color(255, 235, 205);
|
||||
public static readonly Color Blue = new Color(0, 0, 255);
|
||||
public static readonly Color BlueViolet = new Color(138, 43, 226);
|
||||
public static readonly Color Brown = new Color(165, 42, 42);
|
||||
public static readonly Color BurlyWood = new Color(222, 184, 135);
|
||||
public static readonly Color CadetBlue = new Color(95, 158, 160);
|
||||
public static readonly Color Chartreuse = new Color(127, 255, 0);
|
||||
public static readonly Color Chocolate = new Color(210, 105, 30);
|
||||
public static readonly Color Coral = new Color(255, 127, 80);
|
||||
public static readonly Color CornflowerBlue = new Color(100, 149, 237);
|
||||
public static readonly Color Cornsilk = new Color(255, 248, 220);
|
||||
public static readonly Color Crimson = new Color(220, 20, 60);
|
||||
public static readonly Color Cyan = new Color(0, 255, 255);
|
||||
public static readonly Color DarkBlue = new Color(0, 0, 139);
|
||||
public static readonly Color DarkCyan = new Color(0, 139, 139);
|
||||
public static readonly Color DarkGoldenrod = new Color(184, 134, 11);
|
||||
public static readonly Color DarkGray = new Color(169, 169, 169);
|
||||
public static readonly Color DarkGreen = new Color(0, 100, 0);
|
||||
public static readonly Color DarkKhaki = new Color(189, 183, 107);
|
||||
public static readonly Color DarkMagenta = new Color(139, 0, 139);
|
||||
public static readonly Color DarkOliveGreen = new Color(85, 107, 47);
|
||||
public static readonly Color DarkOrange = new Color(255, 140, 0);
|
||||
public static readonly Color DarkOrchid = new Color(153, 50, 204);
|
||||
public static readonly Color DarkRed = new Color(139, 0, 0);
|
||||
public static readonly Color DarkSalmon = new Color(233, 150, 122);
|
||||
public static readonly Color DarkSeaGreen = new Color(143, 188, 143);
|
||||
public static readonly Color DarkSlateBlue = new Color(72, 61, 139);
|
||||
public static readonly Color DarkSlateGray = new Color(47, 79, 79);
|
||||
public static readonly Color DarkTurquoise = new Color(0, 206, 209);
|
||||
public static readonly Color DarkViolet = new Color(148, 0, 211);
|
||||
public static readonly Color DeepPink = new Color(255, 20, 147);
|
||||
public static readonly Color DeepSkyBlue = new Color(0, 191, 255);
|
||||
public static readonly Color DimGray = new Color(105, 105, 105);
|
||||
public static readonly Color DodgerBlue = new Color(30, 144, 255);
|
||||
public static readonly Color Firebrick = new Color(178, 34, 34);
|
||||
public static readonly Color FloralWhite = new Color(255, 250, 240);
|
||||
public static readonly Color ForestGreen = new Color(34, 139, 34);
|
||||
public static readonly Color Fuchsia = new Color(255, 0, 255);
|
||||
[Obsolete("Fuschia is obsolete as of version 1.3.0. Please use Fuchsia instead.")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static readonly Color Fuschia = FromRgb(255, 0, 255);
|
||||
public static readonly Color Gainsboro = FromRgb(220, 220, 220);
|
||||
public static readonly Color GhostWhite = FromRgb(248, 248, 255);
|
||||
public static readonly Color Gold = FromRgb(255, 215, 0);
|
||||
public static readonly Color Goldenrod = FromRgb(218, 165, 32);
|
||||
public static readonly Color Gray = FromRgb(128, 128, 128);
|
||||
public static readonly Color Green = FromRgb(0, 128, 0);
|
||||
public static readonly Color GreenYellow = FromRgb(173, 255, 47);
|
||||
public static readonly Color Honeydew = FromRgb(240, 255, 240);
|
||||
public static readonly Color HotPink = FromRgb(255, 105, 180);
|
||||
public static readonly Color IndianRed = FromRgb(205, 92, 92);
|
||||
public static readonly Color Indigo = FromRgb(75, 0, 130);
|
||||
public static readonly Color Ivory = FromRgb(255, 255, 240);
|
||||
public static readonly Color Khaki = FromRgb(240, 230, 140);
|
||||
public static readonly Color Lavender = FromRgb(230, 230, 250);
|
||||
public static readonly Color LavenderBlush = FromRgb(255, 240, 245);
|
||||
public static readonly Color LawnGreen = FromRgb(124, 252, 0);
|
||||
public static readonly Color LemonChiffon = FromRgb(255, 250, 205);
|
||||
public static readonly Color LightBlue = FromRgb(173, 216, 230);
|
||||
public static readonly Color LightCoral = FromRgb(240, 128, 128);
|
||||
public static readonly Color LightCyan = FromRgb(224, 255, 255);
|
||||
public static readonly Color LightGoldenrodYellow = FromRgb(250, 250, 210);
|
||||
public static readonly Color LightGray = FromRgb(211, 211, 211);
|
||||
public static readonly Color LightGreen = FromRgb(144, 238, 144);
|
||||
public static readonly Color LightPink = FromRgb(255, 182, 193);
|
||||
public static readonly Color LightSalmon = FromRgb(255, 160, 122);
|
||||
public static readonly Color LightSeaGreen = FromRgb(32, 178, 170);
|
||||
public static readonly Color LightSkyBlue = FromRgb(135, 206, 250);
|
||||
public static readonly Color LightSlateGray = FromRgb(119, 136, 153);
|
||||
public static readonly Color LightSteelBlue = FromRgb(176, 196, 222);
|
||||
public static readonly Color LightYellow = FromRgb(255, 255, 224);
|
||||
public static readonly Color Lime = FromRgb(0, 255, 0);
|
||||
public static readonly Color LimeGreen = FromRgb(50, 205, 50);
|
||||
public static readonly Color Linen = FromRgb(250, 240, 230);
|
||||
public static readonly Color Magenta = FromRgb(255, 0, 255);
|
||||
public static readonly Color Maroon = FromRgb(128, 0, 0);
|
||||
public static readonly Color MediumAquamarine = FromRgb(102, 205, 170);
|
||||
public static readonly Color MediumBlue = FromRgb(0, 0, 205);
|
||||
public static readonly Color MediumOrchid = FromRgb(186, 85, 211);
|
||||
public static readonly Color MediumPurple = FromRgb(147, 112, 219);
|
||||
public static readonly Color MediumSeaGreen = FromRgb(60, 179, 113);
|
||||
public static readonly Color MediumSlateBlue = FromRgb(123, 104, 238);
|
||||
public static readonly Color MediumSpringGreen = FromRgb(0, 250, 154);
|
||||
public static readonly Color MediumTurquoise = FromRgb(72, 209, 204);
|
||||
public static readonly Color MediumVioletRed = FromRgb(199, 21, 133);
|
||||
public static readonly Color MidnightBlue = FromRgb(25, 25, 112);
|
||||
public static readonly Color MintCream = FromRgb(245, 255, 250);
|
||||
public static readonly Color MistyRose = FromRgb(255, 228, 225);
|
||||
public static readonly Color Moccasin = FromRgb(255, 228, 181);
|
||||
public static readonly Color NavajoWhite = FromRgb(255, 222, 173);
|
||||
public static readonly Color Navy = FromRgb(0, 0, 128);
|
||||
public static readonly Color OldLace = FromRgb(253, 245, 230);
|
||||
public static readonly Color Olive = FromRgb(128, 128, 0);
|
||||
public static readonly Color OliveDrab = FromRgb(107, 142, 35);
|
||||
public static readonly Color Orange = FromRgb(255, 165, 0);
|
||||
public static readonly Color OrangeRed = FromRgb(255, 69, 0);
|
||||
public static readonly Color Orchid = FromRgb(218, 112, 214);
|
||||
public static readonly Color PaleGoldenrod = FromRgb(238, 232, 170);
|
||||
public static readonly Color PaleGreen = FromRgb(152, 251, 152);
|
||||
public static readonly Color PaleTurquoise = FromRgb(175, 238, 238);
|
||||
public static readonly Color PaleVioletRed = FromRgb(219, 112, 147);
|
||||
public static readonly Color PapayaWhip = FromRgb(255, 239, 213);
|
||||
public static readonly Color PeachPuff = FromRgb(255, 218, 185);
|
||||
public static readonly Color Peru = FromRgb(205, 133, 63);
|
||||
public static readonly Color Pink = FromRgb(255, 192, 203);
|
||||
public static readonly Color Plum = FromRgb(221, 160, 221);
|
||||
public static readonly Color PowderBlue = FromRgb(176, 224, 230);
|
||||
public static readonly Color Purple = FromRgb(128, 0, 128);
|
||||
public static readonly Color Red = FromRgb(255, 0, 0);
|
||||
public static readonly Color RosyBrown = FromRgb(188, 143, 143);
|
||||
public static readonly Color RoyalBlue = FromRgb(65, 105, 225);
|
||||
public static readonly Color SaddleBrown = FromRgb(139, 69, 19);
|
||||
public static readonly Color Salmon = FromRgb(250, 128, 114);
|
||||
public static readonly Color SandyBrown = FromRgb(244, 164, 96);
|
||||
public static readonly Color SeaGreen = FromRgb(46, 139, 87);
|
||||
public static readonly Color SeaShell = FromRgb(255, 245, 238);
|
||||
public static readonly Color Sienna = FromRgb(160, 82, 45);
|
||||
public static readonly Color Silver = FromRgb(192, 192, 192);
|
||||
public static readonly Color SkyBlue = FromRgb(135, 206, 235);
|
||||
public static readonly Color SlateBlue = FromRgb(106, 90, 205);
|
||||
public static readonly Color SlateGray = FromRgb(112, 128, 144);
|
||||
public static readonly Color Snow = FromRgb(255, 250, 250);
|
||||
public static readonly Color SpringGreen = FromRgb(0, 255, 127);
|
||||
public static readonly Color SteelBlue = FromRgb(70, 130, 180);
|
||||
public static readonly Color Tan = FromRgb(210, 180, 140);
|
||||
public static readonly Color Teal = FromRgb(0, 128, 128);
|
||||
public static readonly Color Thistle = FromRgb(216, 191, 216);
|
||||
public static readonly Color Tomato = FromRgb(255, 99, 71);
|
||||
public static readonly Color Transparent = FromRgba(255, 255, 255, 0);
|
||||
public static readonly Color Turquoise = FromRgb(64, 224, 208);
|
||||
public static readonly Color Violet = FromRgb(238, 130, 238);
|
||||
public static readonly Color Wheat = FromRgb(245, 222, 179);
|
||||
public static readonly Color White = FromRgb(255, 255, 255);
|
||||
public static readonly Color WhiteSmoke = FromRgb(245, 245, 245);
|
||||
public static readonly Color Yellow = FromRgb(255, 255, 0);
|
||||
public static readonly Color YellowGreen = FromRgb(154, 205, 50);
|
||||
public static readonly Color Fuschia = new Color(255, 0, 255);
|
||||
public static readonly Color Gainsboro = new Color(220, 220, 220);
|
||||
public static readonly Color GhostWhite = new Color(248, 248, 255);
|
||||
public static readonly Color Gold = new Color(255, 215, 0);
|
||||
public static readonly Color Goldenrod = new Color(218, 165, 32);
|
||||
public static readonly Color Gray = new Color(128, 128, 128);
|
||||
public static readonly Color Green = new Color(0, 128, 0);
|
||||
public static readonly Color GreenYellow = new Color(173, 255, 47);
|
||||
public static readonly Color Honeydew = new Color(240, 255, 240);
|
||||
public static readonly Color HotPink = new Color(255, 105, 180);
|
||||
public static readonly Color IndianRed = new Color(205, 92, 92);
|
||||
public static readonly Color Indigo = new Color(75, 0, 130);
|
||||
public static readonly Color Ivory = new Color(255, 255, 240);
|
||||
public static readonly Color Khaki = new Color(240, 230, 140);
|
||||
public static readonly Color Lavender = new Color(230, 230, 250);
|
||||
public static readonly Color LavenderBlush = new Color(255, 240, 245);
|
||||
public static readonly Color LawnGreen = new Color(124, 252, 0);
|
||||
public static readonly Color LemonChiffon = new Color(255, 250, 205);
|
||||
public static readonly Color LightBlue = new Color(173, 216, 230);
|
||||
public static readonly Color LightCoral = new Color(240, 128, 128);
|
||||
public static readonly Color LightCyan = new Color(224, 255, 255);
|
||||
public static readonly Color LightGoldenrodYellow = new Color(250, 250, 210);
|
||||
public static readonly Color LightGray = new Color(211, 211, 211);
|
||||
public static readonly Color LightGreen = new Color(144, 238, 144);
|
||||
public static readonly Color LightPink = new Color(255, 182, 193);
|
||||
public static readonly Color LightSalmon = new Color(255, 160, 122);
|
||||
public static readonly Color LightSeaGreen = new Color(32, 178, 170);
|
||||
public static readonly Color LightSkyBlue = new Color(135, 206, 250);
|
||||
public static readonly Color LightSlateGray = new Color(119, 136, 153);
|
||||
public static readonly Color LightSteelBlue = new Color(176, 196, 222);
|
||||
public static readonly Color LightYellow = new Color(255, 255, 224);
|
||||
public static readonly Color Lime = new Color(0, 255, 0);
|
||||
public static readonly Color LimeGreen = new Color(50, 205, 50);
|
||||
public static readonly Color Linen = new Color(250, 240, 230);
|
||||
public static readonly Color Magenta = new Color(255, 0, 255);
|
||||
public static readonly Color Maroon = new Color(128, 0, 0);
|
||||
public static readonly Color MediumAquamarine = new Color(102, 205, 170);
|
||||
public static readonly Color MediumBlue = new Color(0, 0, 205);
|
||||
public static readonly Color MediumOrchid = new Color(186, 85, 211);
|
||||
public static readonly Color MediumPurple = new Color(147, 112, 219);
|
||||
public static readonly Color MediumSeaGreen = new Color(60, 179, 113);
|
||||
public static readonly Color MediumSlateBlue = new Color(123, 104, 238);
|
||||
public static readonly Color MediumSpringGreen = new Color(0, 250, 154);
|
||||
public static readonly Color MediumTurquoise = new Color(72, 209, 204);
|
||||
public static readonly Color MediumVioletRed = new Color(199, 21, 133);
|
||||
public static readonly Color MidnightBlue = new Color(25, 25, 112);
|
||||
public static readonly Color MintCream = new Color(245, 255, 250);
|
||||
public static readonly Color MistyRose = new Color(255, 228, 225);
|
||||
public static readonly Color Moccasin = new Color(255, 228, 181);
|
||||
public static readonly Color NavajoWhite = new Color(255, 222, 173);
|
||||
public static readonly Color Navy = new Color(0, 0, 128);
|
||||
public static readonly Color OldLace = new Color(253, 245, 230);
|
||||
public static readonly Color Olive = new Color(128, 128, 0);
|
||||
public static readonly Color OliveDrab = new Color(107, 142, 35);
|
||||
public static readonly Color Orange = new Color(255, 165, 0);
|
||||
public static readonly Color OrangeRed = new Color(255, 69, 0);
|
||||
public static readonly Color Orchid = new Color(218, 112, 214);
|
||||
public static readonly Color PaleGoldenrod = new Color(238, 232, 170);
|
||||
public static readonly Color PaleGreen = new Color(152, 251, 152);
|
||||
public static readonly Color PaleTurquoise = new Color(175, 238, 238);
|
||||
public static readonly Color PaleVioletRed = new Color(219, 112, 147);
|
||||
public static readonly Color PapayaWhip = new Color(255, 239, 213);
|
||||
public static readonly Color PeachPuff = new Color(255, 218, 185);
|
||||
public static readonly Color Peru = new Color(205, 133, 63);
|
||||
public static readonly Color Pink = new Color(255, 192, 203);
|
||||
public static readonly Color Plum = new Color(221, 160, 221);
|
||||
public static readonly Color PowderBlue = new Color(176, 224, 230);
|
||||
public static readonly Color Purple = new Color(128, 0, 128);
|
||||
public static readonly Color Red = new Color(255, 0, 0);
|
||||
public static readonly Color RosyBrown = new Color(188, 143, 143);
|
||||
public static readonly Color RoyalBlue = new Color(65, 105, 225);
|
||||
public static readonly Color SaddleBrown = new Color(139, 69, 19);
|
||||
public static readonly Color Salmon = new Color(250, 128, 114);
|
||||
public static readonly Color SandyBrown = new Color(244, 164, 96);
|
||||
public static readonly Color SeaGreen = new Color(46, 139, 87);
|
||||
public static readonly Color SeaShell = new Color(255, 245, 238);
|
||||
public static readonly Color Sienna = new Color(160, 82, 45);
|
||||
public static readonly Color Silver = new Color(192, 192, 192);
|
||||
public static readonly Color SkyBlue = new Color(135, 206, 235);
|
||||
public static readonly Color SlateBlue = new Color(106, 90, 205);
|
||||
public static readonly Color SlateGray = new Color(112, 128, 144);
|
||||
public static readonly Color Snow = new Color(255, 250, 250);
|
||||
public static readonly Color SpringGreen = new Color(0, 255, 127);
|
||||
public static readonly Color SteelBlue = new Color(70, 130, 180);
|
||||
public static readonly Color Tan = new Color(210, 180, 140);
|
||||
public static readonly Color Teal = new Color(0, 128, 128);
|
||||
public static readonly Color Thistle = new Color(216, 191, 216);
|
||||
public static readonly Color Tomato = new Color(255, 99, 71);
|
||||
public static readonly Color Transparent = new Color(255, 255, 255, 0);
|
||||
public static readonly Color Turquoise = new Color(64, 224, 208);
|
||||
public static readonly Color Violet = new Color(238, 130, 238);
|
||||
public static readonly Color Wheat = new Color(245, 222, 179);
|
||||
public static readonly Color White = new Color(255, 255, 255);
|
||||
public static readonly Color WhiteSmoke = new Color(245, 245, 245);
|
||||
public static readonly Color Yellow = new Color(255, 255, 0);
|
||||
public static readonly Color YellowGreen = new Color(154, 205, 50);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
|
@ -109,33 +109,15 @@ namespace Xamarin.Forms
|
|||
if (s_initialized)
|
||||
return;
|
||||
|
||||
Type targetAttrType = typeof(DependencyAttribute);
|
||||
|
||||
// Don't use LINQ for performance reasons
|
||||
// Naive implementation can easily take over a second to run
|
||||
foreach (Assembly assembly in assemblies)
|
||||
{
|
||||
object[] attributes;
|
||||
try
|
||||
{
|
||||
#if NETSTANDARD2_0
|
||||
attributes = assembly.GetCustomAttributes(targetAttrType, true);
|
||||
#else
|
||||
attributes = assembly.GetCustomAttributes(targetAttrType).ToArray();
|
||||
#endif
|
||||
}
|
||||
catch (System.IO.FileNotFoundException)
|
||||
{
|
||||
// Sometimes the previewer doesn't actually have everything required for these loads to work
|
||||
Log.Warning(nameof(Registrar), "Could not load assembly: {0} for Attibute {1} | Some renderers may not be loaded", assembly.FullName, targetAttrType.FullName);
|
||||
continue;
|
||||
}
|
||||
|
||||
var length = attributes.Length;
|
||||
if (length == 0)
|
||||
object[] attributes = assembly.GetCustomAttributesSafe(typeof(DependencyAttribute));
|
||||
if (attributes == null)
|
||||
continue;
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
for (int i = 0; i < attributes.Length; i++)
|
||||
{
|
||||
DependencyAttribute attribute = (DependencyAttribute)attributes[i];
|
||||
if (!DependencyTypes.Contains(attribute.Implementor))
|
||||
|
|
|
@ -12,6 +12,11 @@ namespace Xamarin.Forms.Internals
|
|||
{
|
||||
int _masterDetails;
|
||||
Page _target;
|
||||
ToolBarItemComparer _toolBarItemComparer;
|
||||
public ToolbarTracker()
|
||||
{
|
||||
_toolBarItemComparer = new ToolBarItemComparer();
|
||||
}
|
||||
|
||||
public IEnumerable<Page> AdditionalTargets { get; set; }
|
||||
|
||||
|
@ -44,12 +49,20 @@ namespace Xamarin.Forms.Internals
|
|||
get
|
||||
{
|
||||
if (Target == null)
|
||||
return Enumerable.Empty<ToolbarItem>();
|
||||
IEnumerable<ToolbarItem> items = GetCurrentToolbarItems(Target);
|
||||
if (AdditionalTargets != null)
|
||||
items = items.Concat(AdditionalTargets.SelectMany(t => t.ToolbarItems));
|
||||
return new ToolbarItem[0];
|
||||
|
||||
return items.OrderBy(ti => ti.Priority);
|
||||
// I realize this is sorting on every single get but we don't have
|
||||
// a mechanism in place currently to invalidate a stored version of this
|
||||
|
||||
List<ToolbarItem> returnValue = GetCurrentToolbarItems(Target);
|
||||
|
||||
if (AdditionalTargets != null)
|
||||
foreach(var item in AdditionalTargets)
|
||||
foreach(var toolbarItem in item.ToolbarItems)
|
||||
returnValue.Add(toolbarItem);
|
||||
|
||||
returnValue.Sort(_toolBarItemComparer);
|
||||
return returnValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,7 +71,7 @@ namespace Xamarin.Forms.Internals
|
|||
void EmitCollectionChanged()
|
||||
=> CollectionChanged?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
IEnumerable<ToolbarItem> GetCurrentToolbarItems(Page page)
|
||||
List<ToolbarItem> GetCurrentToolbarItems(Page page)
|
||||
{
|
||||
var result = new List<ToolbarItem>();
|
||||
result.AddRange(page.ToolbarItems);
|
||||
|
@ -178,5 +191,10 @@ namespace Xamarin.Forms.Internals
|
|||
page.DescendantRemoved -= OnChildRemoved;
|
||||
page.PropertyChanged -= OnPropertyChanged;
|
||||
}
|
||||
|
||||
class ToolBarItemComparer : IComparer<ToolbarItem>
|
||||
{
|
||||
public int Compare(ToolbarItem x, ToolbarItem y) => x.Priority.CompareTo(y.Priority);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +1,48 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Xamarin.Forms.Internals
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static class NumericExtensions
|
||||
{
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double Clamp(this double self, double min, double max)
|
||||
{
|
||||
return Math.Min(max, Math.Max(self, min));
|
||||
if (max < min)
|
||||
{
|
||||
return max;
|
||||
}
|
||||
else if (self < min)
|
||||
{
|
||||
return min;
|
||||
}
|
||||
else if (self > max)
|
||||
{
|
||||
return max;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int Clamp(this int self, int min, int max)
|
||||
{
|
||||
return Math.Min(max, Math.Max(self, min));
|
||||
if (max < min)
|
||||
{
|
||||
return max;
|
||||
}
|
||||
else if (self < min)
|
||||
{
|
||||
return min;
|
||||
}
|
||||
else if (self > max)
|
||||
{
|
||||
return max;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,6 +6,8 @@ namespace Xamarin.Forms.PlatformConfiguration.TizenSpecific
|
|||
{
|
||||
public static readonly BindableProperty BlendColorProperty = BindableProperty.Create("BlendColor", typeof(Color), typeof(FormsElement), Color.Default);
|
||||
|
||||
public static readonly BindableProperty FileProperty = BindableProperty.Create("File", typeof(string), typeof(FormsElement), default(string));
|
||||
|
||||
public static Color GetBlendColor(BindableObject element)
|
||||
{
|
||||
return (Color)element.GetValue(BlendColorProperty);
|
||||
|
@ -26,5 +28,26 @@ namespace Xamarin.Forms.PlatformConfiguration.TizenSpecific
|
|||
SetBlendColor(config.Element, color);
|
||||
return config;
|
||||
}
|
||||
|
||||
public static string GetFile(BindableObject element)
|
||||
{
|
||||
return (string)element.GetValue(FileProperty);
|
||||
}
|
||||
|
||||
public static void SetFile(BindableObject element, string file)
|
||||
{
|
||||
element.SetValue(FileProperty, file);
|
||||
}
|
||||
|
||||
public static string GetFile(this IPlatformElementConfiguration<Tizen, FormsElement> config)
|
||||
{
|
||||
return GetFile(config.Element);
|
||||
}
|
||||
|
||||
public static IPlatformElementConfiguration<Tizen, FormsElement> SetFile(this IPlatformElementConfiguration<Tizen, FormsElement> config, string file)
|
||||
{
|
||||
SetFile(config.Element, file);
|
||||
return config;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,13 +47,12 @@ namespace Xamarin.Forms.Internals
|
|||
|
||||
internal static object[] GetCustomAttributesSafe(this Assembly assembly, Type attrType)
|
||||
{
|
||||
object[] attributes = null;
|
||||
try
|
||||
{
|
||||
#if NETSTANDARD2_0
|
||||
attributes = assembly.GetCustomAttributes(attrType, true);
|
||||
return assembly.GetCustomAttributes(attrType, true);
|
||||
#else
|
||||
attributes = assembly.GetCustomAttributes(attrType).ToArray();
|
||||
return assembly.GetCustomAttributes(attrType).ToArray();
|
||||
#endif
|
||||
}
|
||||
catch (System.IO.FileNotFoundException)
|
||||
|
@ -62,7 +61,7 @@ namespace Xamarin.Forms.Internals
|
|||
Log.Warning(nameof(Registrar), "Could not load assembly: {0} for Attribute {1} | Some renderers may not be loaded", assembly.FullName, attrType.FullName);
|
||||
}
|
||||
|
||||
return attributes;
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Type[] GetExportedTypes(this Assembly assembly)
|
||||
|
|
|
@ -336,19 +336,8 @@ namespace Xamarin.Forms.Internals
|
|||
|
||||
foreach (Type attrType in attrTypes)
|
||||
{
|
||||
object[] attributes;
|
||||
try
|
||||
{
|
||||
#if NETSTANDARD2_0
|
||||
attributes = assembly.GetCustomAttributes(attrType, true);
|
||||
#else
|
||||
attributes = assembly.GetCustomAttributes(attrType).ToArray();
|
||||
#endif
|
||||
}
|
||||
catch (System.IO.FileNotFoundException)
|
||||
{
|
||||
// Sometimes the previewer doesn't actually have everything required for these loads to work
|
||||
Log.Warning(nameof(Registrar), "Could not load assembly: {0} for Attibute {1} | Some renderers may not be loaded", assembly.FullName, attrType.FullName);
|
||||
object[] attributes = assembly.GetCustomAttributesSafe(attrType);
|
||||
if (attributes == null || attributes.Length == 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -369,19 +358,14 @@ namespace Xamarin.Forms.Internals
|
|||
}
|
||||
}
|
||||
|
||||
object[] effectAttributes = assembly.GetCustomAttributesSafe(typeof (ExportEffectAttribute));
|
||||
if (effectAttributes == null || effectAttributes.Length == 0)
|
||||
continue;
|
||||
string resolutionName = assembly.FullName;
|
||||
var resolutionNameAttribute = (ResolutionGroupNameAttribute)assembly.GetCustomAttribute(typeof(ResolutionGroupNameAttribute));
|
||||
if (resolutionNameAttribute != null)
|
||||
resolutionName = resolutionNameAttribute.ShortName;
|
||||
|
||||
#if NETSTANDARD2_0
|
||||
object[] effectAttributes = assembly.GetCustomAttributes(typeof(ExportEffectAttribute), true);
|
||||
#else
|
||||
object[] effectAttributes = assembly.GetCustomAttributes(typeof(ExportEffectAttribute)).ToArray();
|
||||
#endif
|
||||
var typedEffectAttributes = new ExportEffectAttribute[effectAttributes.Length];
|
||||
Array.Copy(effectAttributes, typedEffectAttributes, effectAttributes.Length);
|
||||
RegisterEffects(resolutionName, typedEffectAttributes);
|
||||
RegisterEffects(resolutionName, (ExportEffectAttribute[])effectAttributes);
|
||||
|
||||
Profile.FrameEnd(assemblyName);
|
||||
}
|
||||
|
|
|
@ -220,6 +220,6 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
|
|||
}
|
||||
|
||||
AppCompatButton IButtonLayoutRenderer.View => Control;
|
||||
bool IDisposedState.IsDisposed => _isDisposed;
|
||||
bool IDisposedState.IsDisposed => _isDisposed || !Control.IsAlive();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@ namespace Xamarin.Forms.Platform.Android
|
|||
CompoundButton.IOnCheckedChangeListener
|
||||
{
|
||||
bool _disposed;
|
||||
bool _skipInvalidate;
|
||||
int? _defaultLabelFor;
|
||||
VisualElementTracker _tracker;
|
||||
VisualElementRenderer _visualElementRenderer;
|
||||
|
@ -87,17 +86,6 @@ namespace Xamarin.Forms.Platform.Android
|
|||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
public override void Invalidate()
|
||||
{
|
||||
if (_skipInvalidate)
|
||||
{
|
||||
_skipInvalidate = false;
|
||||
return;
|
||||
}
|
||||
|
||||
base.Invalidate();
|
||||
}
|
||||
|
||||
Size MinimumSize()
|
||||
{
|
||||
return new Size();
|
||||
|
|
|
@ -23,7 +23,6 @@ namespace Xamarin.Forms.Platform.Android
|
|||
ITabStop
|
||||
{
|
||||
bool _disposed;
|
||||
bool _skipInvalidate;
|
||||
int? _defaultLabelFor;
|
||||
VisualElementTracker _tracker;
|
||||
VisualElementRenderer _visualElementRenderer;
|
||||
|
@ -85,17 +84,6 @@ namespace Xamarin.Forms.Platform.Android
|
|||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
public override void Invalidate()
|
||||
{
|
||||
if (_skipInvalidate)
|
||||
{
|
||||
_skipInvalidate = false;
|
||||
return;
|
||||
}
|
||||
|
||||
base.Invalidate();
|
||||
}
|
||||
|
||||
Size MinimumSize()
|
||||
{
|
||||
return Size.Zero;
|
||||
|
|
|
@ -43,7 +43,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
AView IVisualElementRenderer.View => this;
|
||||
ViewGroup IVisualElementRenderer.ViewGroup => null;
|
||||
VisualElementTracker IVisualElementRenderer.Tracker => _tracker;
|
||||
bool IDisposedState.IsDisposed => _disposed;
|
||||
bool IDisposedState.IsDisposed => ((IImageRendererController)this).IsDisposed;
|
||||
|
||||
public ImageButton Element
|
||||
{
|
||||
|
@ -56,7 +56,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
}
|
||||
|
||||
void IImageRendererController.SkipInvalidate() => _skipInvalidate = true;
|
||||
bool IImageRendererController.IsDisposed => _disposed;
|
||||
bool IImageRendererController.IsDisposed => _disposed || !Control.IsAlive();
|
||||
|
||||
AppCompatImageButton Control => this;
|
||||
public ImageButtonRenderer(Context context) : base(context)
|
||||
|
|
|
@ -32,8 +32,6 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
|
|||
{
|
||||
public class NavigationPageRenderer : VisualElementRenderer<NavigationPage>, IManageFragments, IOnClickListener, ILifeCycleState
|
||||
{
|
||||
const int DefaultDisabledToolbarAlpha = 127;
|
||||
|
||||
readonly List<Fragment> _fragmentStack = new List<Fragment>();
|
||||
|
||||
Drawable _backgroundDrawable;
|
||||
|
@ -220,8 +218,7 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
|
|||
{
|
||||
_toolbarTracker.CollectionChanged -= ToolbarTrackerOnCollectionChanged;
|
||||
|
||||
foreach (ToolbarItem item in _toolbarTracker.ToolbarItems)
|
||||
item.PropertyChanged -= OnToolbarItemPropertyChanged;
|
||||
_toolbar.DisposeMenuItems(_toolbarTracker?.ToolbarItems, OnToolbarItemPropertyChanged);
|
||||
|
||||
_toolbarTracker.Target = null;
|
||||
_toolbarTracker = null;
|
||||
|
@ -562,8 +559,7 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
|
|||
|
||||
protected virtual void OnToolbarItemPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == MenuItem.IsEnabledProperty.PropertyName || e.PropertyName == MenuItem.TextProperty.PropertyName || e.PropertyName == MenuItem.IconImageSourceProperty.PropertyName)
|
||||
UpdateMenu();
|
||||
_toolbar.OnToolbarItemPropertyChanged(e, _toolbarTracker?.ToolbarItems, Context, null, OnToolbarItemPropertyChanged);
|
||||
}
|
||||
|
||||
void InsertPageBefore(Page page, Page before)
|
||||
|
@ -909,51 +905,7 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
|
|||
if (_disposed)
|
||||
return;
|
||||
|
||||
AToolbar bar = _toolbar;
|
||||
Context context = Context;
|
||||
IMenu menu = bar.Menu;
|
||||
|
||||
foreach (ToolbarItem item in _toolbarTracker.ToolbarItems)
|
||||
item.PropertyChanged -= OnToolbarItemPropertyChanged;
|
||||
menu.Clear();
|
||||
|
||||
foreach (ToolbarItem item in _toolbarTracker.ToolbarItems)
|
||||
{
|
||||
IMenuItemController controller = item;
|
||||
item.PropertyChanged += OnToolbarItemPropertyChanged;
|
||||
if (item.Order == ToolbarItemOrder.Secondary)
|
||||
{
|
||||
IMenuItem menuItem = menu.Add(item.Text);
|
||||
menuItem.SetEnabled(controller.IsEnabled);
|
||||
menuItem.SetOnMenuItemClickListener(new GenericMenuClickListener(controller.Activate));
|
||||
menuItem.SetTitleOrContentDescription(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
IMenuItem menuItem = menu.Add(item.Text);
|
||||
menuItem.SetEnabled(controller.IsEnabled);
|
||||
UpdateMenuItemIcon(context, menuItem, item);
|
||||
menuItem.SetShowAsAction(ShowAsAction.Always);
|
||||
menuItem.SetOnMenuItemClickListener(new GenericMenuClickListener(controller.Activate));
|
||||
menuItem.SetTitleOrContentDescription(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void UpdateMenuItemIcon(Context context, IMenuItem menuItem, ToolbarItem toolBarItem)
|
||||
{
|
||||
_ = this.ApplyDrawableAsync(toolBarItem, ToolbarItem.IconImageSourceProperty, Context, iconDrawable =>
|
||||
{
|
||||
if (iconDrawable != null)
|
||||
{
|
||||
if (!menuItem.IsEnabled)
|
||||
{
|
||||
iconDrawable.Mutate().SetAlpha(DefaultDisabledToolbarAlpha);
|
||||
}
|
||||
|
||||
menuItem.SetIcon(iconDrawable);
|
||||
}
|
||||
});
|
||||
_toolbar.UpdateMenuItems(_toolbarTracker?.ToolbarItems, Context, null, OnToolbarItemPropertyChanged);
|
||||
}
|
||||
|
||||
void UpdateToolbar()
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
using System.ComponentModel;
|
||||
using Android.Views;
|
||||
using AToolbar = Android.Support.V7.Widget.Toolbar;
|
||||
using ATextView = global::Android.Widget.TextView;
|
||||
using Android.Content;
|
||||
using Android.Graphics;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Xamarin.Forms.Platform.Android
|
||||
{
|
||||
internal static class ToolbarExtensions
|
||||
{
|
||||
const int DefaultDisabledToolbarAlpha = 127;
|
||||
public static void DisposeMenuItems(this AToolbar toolbar, IEnumerable<ToolbarItem> toolbarItems, PropertyChangedEventHandler toolbarItemChanged)
|
||||
{
|
||||
if (toolbarItems == null)
|
||||
return;
|
||||
|
||||
foreach (var item in toolbarItems)
|
||||
item.PropertyChanged -= toolbarItemChanged;
|
||||
}
|
||||
|
||||
public static void UpdateMenuItems(this AToolbar toolbar,
|
||||
IEnumerable<ToolbarItem> toolbarItems,
|
||||
Context context,
|
||||
Color? tintColor,
|
||||
PropertyChangedEventHandler toolbarItemChanged
|
||||
)
|
||||
{
|
||||
if (toolbarItems == null)
|
||||
return;
|
||||
|
||||
var menu = toolbar.Menu;
|
||||
menu.Clear();
|
||||
|
||||
foreach (var item in toolbarItems)
|
||||
{
|
||||
item.PropertyChanged -= toolbarItemChanged;
|
||||
item.PropertyChanged += toolbarItemChanged;
|
||||
|
||||
using (var title = new Java.Lang.String(item.Text))
|
||||
{
|
||||
var menuitem = menu.Add(global::Android.Views.Menu.None, 0, item.Priority, title);
|
||||
menuitem.SetEnabled(item.IsEnabled);
|
||||
menuitem.SetTitleOrContentDescription(item);
|
||||
UpdateMenuItemIcon(context, menuitem, item, tintColor);
|
||||
|
||||
if (item.Order != ToolbarItemOrder.Secondary)
|
||||
menuitem.SetShowAsAction(ShowAsAction.Always);
|
||||
|
||||
menuitem.SetOnMenuItemClickListener(new GenericMenuClickListener(((IMenuItemController)item).Activate));
|
||||
|
||||
if (tintColor != null && tintColor != Color.Default)
|
||||
{
|
||||
var view = toolbar.FindViewById(menuitem.ItemId);
|
||||
if (view is ATextView textView)
|
||||
{
|
||||
if (item.IsEnabled)
|
||||
textView.SetTextColor(tintColor.Value.ToAndroid());
|
||||
else
|
||||
textView.SetTextColor(tintColor.Value.MultiplyAlpha(0.302).ToAndroid());
|
||||
}
|
||||
}
|
||||
|
||||
menuitem.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void UpdateMenuItemIcon(Context context, IMenuItem menuItem, ToolbarItem toolBarItem, Color? tintColor)
|
||||
{
|
||||
_ = context.ApplyDrawableAsync(toolBarItem, ToolbarItem.IconImageSourceProperty, baseDrawable =>
|
||||
{
|
||||
if (baseDrawable != null)
|
||||
{
|
||||
using (var constant = baseDrawable.GetConstantState())
|
||||
using (var newDrawable = constant.NewDrawable())
|
||||
using (var iconDrawable = newDrawable.Mutate())
|
||||
{
|
||||
if(tintColor != null)
|
||||
iconDrawable.SetColorFilter(tintColor.Value.ToAndroid(Color.White), PorterDuff.Mode.SrcAtop);
|
||||
|
||||
if (!menuItem.IsEnabled)
|
||||
{
|
||||
iconDrawable.Mutate().SetAlpha(DefaultDisabledToolbarAlpha);
|
||||
}
|
||||
|
||||
menuItem.SetIcon(iconDrawable);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void OnToolbarItemPropertyChanged(
|
||||
this AToolbar toolbar,
|
||||
PropertyChangedEventArgs e,
|
||||
IEnumerable<ToolbarItem> toolbarItems,
|
||||
Context context,
|
||||
Color? tintColor,
|
||||
PropertyChangedEventHandler toolbarItemChanged)
|
||||
{
|
||||
if (toolbarItems == null)
|
||||
return;
|
||||
|
||||
if (e.IsOneOf(MenuItem.TextProperty, MenuItem.IconImageSourceProperty, MenuItem.IsEnabledProperty))
|
||||
{
|
||||
toolbar.UpdateMenuItems(toolbarItems, context, tintColor, toolbarItemChanged);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -51,13 +51,20 @@ namespace Xamarin.Forms.Platform.Android.FastRenderers
|
|||
var oldImageElementManager = e.OldElement as IImageElement;
|
||||
var rendererController = renderer as IImageRendererController;
|
||||
|
||||
await TryUpdateBitmap(rendererController, view, newImageElementManager, oldImageElementManager);
|
||||
UpdateAspect(rendererController, view, newImageElementManager, oldImageElementManager);
|
||||
if (rendererController.IsDisposed)
|
||||
return;
|
||||
|
||||
if (!rendererController.IsDisposed)
|
||||
{
|
||||
ElevationHelper.SetElevation(view, renderer.Element);
|
||||
}
|
||||
await TryUpdateBitmap(rendererController, view, newImageElementManager, oldImageElementManager);
|
||||
|
||||
if (rendererController.IsDisposed)
|
||||
return;
|
||||
|
||||
UpdateAspect(rendererController, view, newImageElementManager, oldImageElementManager);
|
||||
|
||||
if (rendererController.IsDisposed)
|
||||
return;
|
||||
|
||||
ElevationHelper.SetElevation(view, renderer.Element);
|
||||
}
|
||||
|
||||
async static void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
|
@ -136,6 +143,9 @@ namespace Xamarin.Forms.Platform.Android.FastRenderers
|
|||
imageController.SetIsLoading(false);
|
||||
}
|
||||
|
||||
if (rendererController.IsDisposed)
|
||||
return;
|
||||
|
||||
if (Control.Drawable is FormsAnimationDrawable updatedAnimation)
|
||||
{
|
||||
rendererController.SetFormsAnimationDrawable(updatedAnimation);
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace Xamarin.Forms.Platform.Android.FastRenderers
|
|||
readonly MotionEventHelper _motionEventHelper = new MotionEventHelper();
|
||||
IFormsAnimationDrawable _formsAnimationDrawable;
|
||||
|
||||
bool IImageRendererController.IsDisposed => _disposed;
|
||||
bool IImageRendererController.IsDisposed => _disposed || !Control.IsAlive();
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
|
|
|
@ -249,7 +249,6 @@ namespace Xamarin.Forms.Platform.Android.FastRenderers
|
|||
|
||||
e.NewElement.PropertyChanged += OnElementPropertyChanged;
|
||||
|
||||
SkipNextInvalidate();
|
||||
UpdateText();
|
||||
UpdateLineHeight();
|
||||
UpdateCharacterSpacing();
|
||||
|
|
|
@ -405,14 +405,15 @@ namespace Xamarin.Forms.Platform.Android
|
|||
return;
|
||||
}
|
||||
|
||||
foreach (ToolbarItem item in _toolbarTracker.ToolbarItems)
|
||||
var toolbarItems = _toolbarTracker.ToolbarItems;
|
||||
foreach (ToolbarItem item in toolbarItems)
|
||||
item.PropertyChanged -= HandleToolbarItemPropertyChanged;
|
||||
menu.Clear();
|
||||
|
||||
if (!ShouldShowActionBarTitleArea())
|
||||
return;
|
||||
|
||||
foreach (ToolbarItem item in _toolbarTracker.ToolbarItems)
|
||||
foreach (ToolbarItem item in toolbarItems)
|
||||
{
|
||||
IMenuItemController controller = item;
|
||||
item.PropertyChanged += HandleToolbarItemPropertyChanged;
|
||||
|
|
|
@ -38,6 +38,8 @@ namespace Xamarin.Forms.Platform.Android
|
|||
|
||||
public class FormsAnimationDrawable : AnimationDrawable, IFormsAnimationDrawable
|
||||
{
|
||||
const int DefaultBufferSize = 4096;
|
||||
|
||||
int _repeatCounter = 0;
|
||||
int _frameCount = 0;
|
||||
bool _finished = false;
|
||||
|
@ -192,28 +194,19 @@ namespace Xamarin.Forms.Platform.Android
|
|||
InJustDecodeBounds = true
|
||||
};
|
||||
|
||||
if (!FileImageSourceHandler.DecodeSynchronously)
|
||||
await BitmapFactory.DecodeResourceAsync(context.Resources, ResourceManager.GetDrawableByName(file), options);
|
||||
else
|
||||
BitmapFactory.DecodeResource(context.Resources, ResourceManager.GetDrawableByName(file), options);
|
||||
int drawableIdentifier = ResourceManager.GetDrawableByName(file);
|
||||
|
||||
using (var stream = context.Resources.OpenRawResource(ResourceManager.GetDrawableByName(file)))
|
||||
using (var decoder = new AndroidGIFImageParser(context, options.InDensity, options.InTargetDensity))
|
||||
if (drawableIdentifier != 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!FileImageSourceHandler.DecodeSynchronously)
|
||||
await decoder.ParseAsync(stream).ConfigureAwait(false);
|
||||
else
|
||||
decoder.ParseAsync(stream).Wait();
|
||||
if (!FileImageSourceHandler.DecodeSynchronously)
|
||||
await BitmapFactory.DecodeResourceAsync(context.Resources, drawableIdentifier, options);
|
||||
else
|
||||
BitmapFactory.DecodeResource(context.Resources, drawableIdentifier, options);
|
||||
|
||||
animation = decoder.Animation;
|
||||
}
|
||||
catch (GIFDecoderFormatException)
|
||||
{
|
||||
animation = null;
|
||||
}
|
||||
animation = await GetFormsAnimationDrawableFromResource(drawableIdentifier, context, options);
|
||||
}
|
||||
else
|
||||
animation = await GetFormsAnimationDrawableFromFile(file, context, options);
|
||||
|
||||
if (animation == null)
|
||||
{
|
||||
|
@ -264,6 +257,50 @@ namespace Xamarin.Forms.Platform.Android
|
|||
|
||||
return animation;
|
||||
}
|
||||
|
||||
internal static async Task<FormsAnimationDrawable> GetFormsAnimationDrawableFromResource(int resourceId, Context context, BitmapFactory.Options options)
|
||||
{
|
||||
FormsAnimationDrawable animation = null;
|
||||
|
||||
using (var stream = context.Resources.OpenRawResource(resourceId))
|
||||
animation = await GetFormsAnimationDrawableFromStream(stream, context, options);
|
||||
|
||||
return animation;
|
||||
}
|
||||
|
||||
internal static async Task<FormsAnimationDrawable> GetFormsAnimationDrawableFromFile(string file, Context context, BitmapFactory.Options options)
|
||||
{
|
||||
FormsAnimationDrawable animation = null;
|
||||
|
||||
using (var stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultBufferSize, true))
|
||||
animation = await GetFormsAnimationDrawableFromStream(stream, context, options);
|
||||
|
||||
return animation;
|
||||
}
|
||||
|
||||
internal static async Task<FormsAnimationDrawable> GetFormsAnimationDrawableFromStream(Stream stream, Context context, BitmapFactory.Options options)
|
||||
{
|
||||
FormsAnimationDrawable animation = null;
|
||||
|
||||
using (var decoder = new AndroidGIFImageParser(context, options.InDensity, options.InTargetDensity))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!FileImageSourceHandler.DecodeSynchronously)
|
||||
await decoder.ParseAsync(stream).ConfigureAwait(false);
|
||||
else
|
||||
decoder.ParseAsync(stream).Wait();
|
||||
|
||||
animation = decoder.Animation;
|
||||
}
|
||||
catch (GIFDecoderFormatException)
|
||||
{
|
||||
animation = null;
|
||||
}
|
||||
}
|
||||
|
||||
return animation;
|
||||
}
|
||||
}
|
||||
|
||||
class AndroidGIFImageParser : GIFImageParser, IDisposable
|
||||
|
|
|
@ -39,6 +39,6 @@ namespace Xamarin.Forms.Platform.Android
|
|||
}
|
||||
|
||||
|
||||
bool IImageRendererController.IsDisposed => false;
|
||||
bool IImageRendererController.IsDisposed => false || !this.IsAlive();
|
||||
}
|
||||
}
|
|
@ -8,34 +8,28 @@ namespace Xamarin.Forms.Platform.Android
|
|||
{
|
||||
public class FormsTextView : TextView
|
||||
{
|
||||
bool _skip;
|
||||
|
||||
public FormsTextView(Context context) : base(context)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public FormsTextView(Context context, IAttributeSet attrs) : base(context, attrs)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public FormsTextView(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
protected FormsTextView(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Invalidate()
|
||||
{
|
||||
if (!_skip)
|
||||
base.Invalidate();
|
||||
_skip = false;
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public void SkipNextInvalidate()
|
||||
{
|
||||
_skip = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -124,8 +124,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
UpdateMaxLines();
|
||||
}
|
||||
else
|
||||
{
|
||||
_view.SkipNextInvalidate();
|
||||
{
|
||||
UpdateText();
|
||||
if (e.OldElement.LineBreakMode != e.NewElement.LineBreakMode)
|
||||
UpdateLineBreakMode();
|
||||
|
|
|
@ -19,6 +19,8 @@ using Toolbar = Android.Support.V7.Widget.Toolbar;
|
|||
using ADrawableCompat = Android.Support.V4.Graphics.Drawable.DrawableCompat;
|
||||
using ATextView = global::Android.Widget.TextView;
|
||||
using Android.Support.Design.Widget;
|
||||
using AColor = Android.Graphics.Color;
|
||||
using Xamarin.Forms.Internals;
|
||||
|
||||
namespace Xamarin.Forms.Platform.Android
|
||||
{
|
||||
|
@ -154,11 +156,8 @@ namespace Xamarin.Forms.Platform.Android
|
|||
if (_backButtonBehavior != null)
|
||||
_backButtonBehavior.PropertyChanged -= OnBackButtonBehaviorChanged;
|
||||
|
||||
if(Page?.ToolbarItems?.Count > 0)
|
||||
{
|
||||
foreach (var item in Page.ToolbarItems)
|
||||
item.PropertyChanged -= OnToolbarItemPropertyChanged;
|
||||
}
|
||||
|
||||
_toolbar.DisposeMenuItems(Page?.ToolbarItems, OnToolbarItemPropertyChanged);
|
||||
|
||||
((IShellController)_shellContext?.Shell)?.RemoveFlyoutBehaviorObserver(this);
|
||||
|
||||
|
@ -492,37 +491,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
protected virtual void UpdateToolbarItems(Toolbar toolbar, Page page)
|
||||
{
|
||||
var menu = toolbar.Menu;
|
||||
menu.Clear();
|
||||
|
||||
foreach (var item in page.ToolbarItems)
|
||||
{
|
||||
item.PropertyChanged -= OnToolbarItemPropertyChanged;
|
||||
item.PropertyChanged += OnToolbarItemPropertyChanged;
|
||||
|
||||
using (var title = new Java.Lang.String(item.Text))
|
||||
{
|
||||
var menuitem = menu.Add(title);
|
||||
UpdateMenuItemIcon(_shellContext.AndroidContext, menuitem, item);
|
||||
|
||||
menuitem.SetTitleOrContentDescription(item);
|
||||
menuitem.SetEnabled(item.IsEnabled);
|
||||
|
||||
if (item.Order != ToolbarItemOrder.Secondary)
|
||||
menuitem.SetShowAsAction(ShowAsAction.Always);
|
||||
|
||||
menuitem.SetOnMenuItemClickListener(new GenericMenuClickListener(((IMenuItemController)item).Activate));
|
||||
|
||||
if (TintColor != Color.Default)
|
||||
{
|
||||
var view = toolbar.FindViewById(menuitem.ItemId);
|
||||
if (view is ATextView textView)
|
||||
textView.SetTextColor(TintColor.ToAndroid());
|
||||
}
|
||||
|
||||
menuitem.Dispose();
|
||||
|
||||
}
|
||||
}
|
||||
toolbar.UpdateMenuItems(page.ToolbarItems, _shellContext.AndroidContext, TintColor, OnToolbarItemPropertyChanged);
|
||||
|
||||
SearchHandler = Shell.GetSearchHandler(page);
|
||||
if (SearchHandler != null && SearchHandler.SearchBoxVisibility != SearchBoxVisibility.Hidden)
|
||||
|
@ -585,10 +554,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
|
||||
void OnToolbarItemPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == MenuItem.TextProperty.PropertyName)
|
||||
UpdateToolbarItems();
|
||||
if (e.PropertyName == MenuItem.IconImageSourceProperty.PropertyName)
|
||||
UpdateToolbarItems();
|
||||
_toolbar.OnToolbarItemPropertyChanged(e, Page.ToolbarItems, _shellContext.AndroidContext, TintColor, OnToolbarItemPropertyChanged);
|
||||
}
|
||||
|
||||
void OnSearchViewAttachedToWindow(object sender, AView.ViewAttachedToWindowEventArgs e)
|
||||
|
|
|
@ -143,10 +143,12 @@ namespace Xamarin.Forms.Platform.MacOS
|
|||
AllowsUserCustomization = false,
|
||||
ShowsBaselineSeparator = true,
|
||||
SizeMode = NSToolbarSizeMode.Regular,
|
||||
Delegate = this,
|
||||
CenteredItemIdentifier = TitleGroupIdentifier
|
||||
Delegate = this
|
||||
};
|
||||
|
||||
if (Forms.IsMojaveOrNewer)
|
||||
toolbar.CenteredItemIdentifier = TitleGroupIdentifier;
|
||||
|
||||
return toolbar;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,34 @@
|
|||
using EImage = ElmSharp.Image;
|
||||
|
||||
namespace Xamarin.Forms.Platform.Tizen
|
||||
{
|
||||
internal static class ImageExtensions
|
||||
public static class ImageExtensions
|
||||
{
|
||||
internal static bool IsNullOrEmpty(this ImageSource imageSource) =>
|
||||
public static void ApplyAspect(this EImage image, Aspect aspect)
|
||||
{
|
||||
Aspect _aspect = aspect;
|
||||
|
||||
switch (_aspect)
|
||||
{
|
||||
case Aspect.AspectFit:
|
||||
image.IsFixedAspect = true;
|
||||
image.CanFillOutside = false;
|
||||
break;
|
||||
case Aspect.AspectFill:
|
||||
image.IsFixedAspect = true;
|
||||
image.CanFillOutside = true;
|
||||
break;
|
||||
case Aspect.Fill:
|
||||
image.IsFixedAspect = false;
|
||||
image.CanFillOutside = false;
|
||||
break;
|
||||
default:
|
||||
Log.Warn("Invalid Aspect value: {0}", _aspect);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsNullOrEmpty(this ImageSource imageSource) =>
|
||||
imageSource == null || imageSource.IsEmpty;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,91 +1,222 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using ElmSharp;
|
||||
using Xamarin.Forms.Internals;
|
||||
using EColor = ElmSharp.Color;
|
||||
|
||||
namespace Xamarin.Forms.Platform.Tizen
|
||||
{
|
||||
public class LightweightPlatform : BindableObject, ITizenPlatform
|
||||
public class LightweightPlatform : ITizenPlatform, INavigation, IDisposable
|
||||
{
|
||||
Page _page;
|
||||
EvasObject _rootView;
|
||||
bool _disposed;
|
||||
NavigationModel _navModel = new NavigationModel();
|
||||
Native.Canvas _viewStack;
|
||||
readonly PopupManager _popupManager;
|
||||
bool _hasAlpha;
|
||||
readonly EColor _defaultPlatformColor;
|
||||
|
||||
internal LightweightPlatform(EvasObject parent)
|
||||
public LightweightPlatform(EvasObject parent)
|
||||
{
|
||||
Forms.NativeParent = parent;
|
||||
_defaultPlatformColor = Device.Idiom == TargetIdiom.Phone ? EColor.White : EColor.Transparent;
|
||||
_viewStack = new Native.Canvas(parent)
|
||||
{
|
||||
BackgroundColor = _defaultPlatformColor,
|
||||
};
|
||||
_viewStack.SetAlignment(-1, -1);
|
||||
_viewStack.SetWeight(1.0, 1.0);
|
||||
_viewStack.LayoutUpdated += OnLayout;
|
||||
_viewStack.Show();
|
||||
|
||||
if (Forms.UseMessagingCenter)
|
||||
{
|
||||
_popupManager = new PopupManager(this);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable 0067
|
||||
public event EventHandler<RootNativeViewChangedEventArgs> RootNativeViewChanged;
|
||||
#pragma warning restore 0067
|
||||
|
||||
public bool HasAlpha { get => false; set { } }
|
||||
public bool HasAlpha
|
||||
{
|
||||
get => _hasAlpha;
|
||||
set
|
||||
{
|
||||
_hasAlpha = value;
|
||||
_viewStack.BackgroundColor = _hasAlpha ? EColor.Transparent : _defaultPlatformColor;
|
||||
}
|
||||
}
|
||||
|
||||
IPageController CurrentPageController => _navModel.CurrentPage as IPageController;
|
||||
|
||||
IReadOnlyList<Page> INavigation.ModalStack => _navModel.Modals.ToList();
|
||||
|
||||
IReadOnlyList<Page> INavigation.NavigationStack => new List<Page>();
|
||||
|
||||
public void SetPage(Page page)
|
||||
{
|
||||
ResetChildren();
|
||||
_navModel = new NavigationModel();
|
||||
if (page == null)
|
||||
return;
|
||||
|
||||
_navModel.Push(page, null);
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
page.Platform = this;
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
((Application)page.RealParent).NavigationProxy.Inner = this;
|
||||
|
||||
var renderer = Platform.CreateRenderer(page);
|
||||
renderer.NativeView.Geometry = _viewStack.Geometry;
|
||||
_viewStack.Children.Add(renderer.NativeView);
|
||||
|
||||
CurrentPageController?.SendAppearing();
|
||||
}
|
||||
|
||||
public bool SendBackButtonPressed()
|
||||
{
|
||||
return _navModel?.CurrentPage?.SendBackButtonPressed() ?? false;
|
||||
}
|
||||
|
||||
public EvasObject GetRootNativeView()
|
||||
{
|
||||
return _viewStack;
|
||||
}
|
||||
|
||||
public bool PageIsChildOfPlatform(Page page)
|
||||
{
|
||||
var parent = page.AncestorToRoot();
|
||||
return _navModel.Modals.FirstOrDefault() == page || _navModel.Roots.Contains(parent);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public EvasObject GetRootNativeView() => _rootView;
|
||||
|
||||
public bool SendBackButtonPressed()
|
||||
{
|
||||
if (_page == null) return false;
|
||||
return _page.SendBackButtonPressed();
|
||||
}
|
||||
|
||||
public void SetPage(Page page)
|
||||
{
|
||||
if (_page == page) return;
|
||||
if (_page != null)
|
||||
{
|
||||
var oldRenderer = Platform.GetRenderer(_page);
|
||||
oldRenderer?.Dispose();
|
||||
}
|
||||
|
||||
_page = page;
|
||||
|
||||
if (_page == null) return;
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
// The Platform property is no longer necessary, but we have to set it because some third-party
|
||||
// library might still be retrieving it and using it
|
||||
_page.Platform = this;
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
var renderer = Platform.CreateRenderer(_page);
|
||||
_rootView = renderer.NativeView;
|
||||
RootNativeViewChanged?.Invoke(this, new RootNativeViewChangedEventArgs(_rootView));
|
||||
_rootView.Show();
|
||||
|
||||
Device.StartTimer(TimeSpan.Zero, () =>
|
||||
{
|
||||
_page?.SendAppearing();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
protected override void OnBindingContextChanged()
|
||||
{
|
||||
BindableObject.SetInheritedBindingContext(_page, base.BindingContext);
|
||||
base.OnBindingContextChanged();
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed) return;
|
||||
if (disposing)
|
||||
{
|
||||
SetPage(null);
|
||||
_popupManager?.Dispose();
|
||||
_viewStack?.Unrealize();
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
Task<Page> INavigation.PopModalAsync()
|
||||
{
|
||||
return (this as INavigation).PopModalAsync(true);
|
||||
}
|
||||
|
||||
Task<Page> INavigation.PopModalAsync(bool animated)
|
||||
{
|
||||
Page page = _navModel.PopModal();
|
||||
(page as IPageController)?.SendDisappearing();
|
||||
|
||||
var renderer = Platform.GetRenderer(page);
|
||||
_viewStack.Children.Remove(renderer.NativeView);
|
||||
renderer.Dispose();
|
||||
|
||||
_viewStack.Children.LastOrDefault()?.Show();
|
||||
|
||||
CurrentPageController?.SendAppearing();
|
||||
return Task.FromResult(page);
|
||||
}
|
||||
|
||||
Task INavigation.PushModalAsync(Page modal)
|
||||
{
|
||||
return (this as INavigation).PushModalAsync(modal, true);
|
||||
}
|
||||
|
||||
Task INavigation.PushModalAsync(Page page, bool animated)
|
||||
{
|
||||
var previousPage = CurrentPageController;
|
||||
previousPage?.SendDisappearing();
|
||||
|
||||
_navModel.PushModal(page);
|
||||
|
||||
var lastTop = _viewStack.Children.LastOrDefault();
|
||||
|
||||
var renderer = Platform.GetOrCreateRenderer(page);
|
||||
renderer.NativeView.Geometry = _viewStack.Geometry;
|
||||
|
||||
_viewStack.Children.Add(renderer.NativeView);
|
||||
if (lastTop != null)
|
||||
{
|
||||
lastTop.Hide();
|
||||
renderer.NativeView.StackAbove(lastTop);
|
||||
}
|
||||
|
||||
// Verify that the modal is still on the stack
|
||||
if (_navModel.CurrentPage == page)
|
||||
CurrentPageController.SendAppearing();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
void INavigation.InsertPageBefore(Page page, Page before)
|
||||
{
|
||||
throw new InvalidOperationException("InsertPageBefore is not supported globally on Tizen, please use a NavigationPage.");
|
||||
}
|
||||
|
||||
Task<Page> INavigation.PopAsync()
|
||||
{
|
||||
return ((INavigation)this).PopAsync(true);
|
||||
}
|
||||
|
||||
Task<Page> INavigation.PopAsync(bool animated)
|
||||
{
|
||||
throw new InvalidOperationException("PopAsync is not supported globally on Tizen, please use a NavigationPage.");
|
||||
}
|
||||
|
||||
Task INavigation.PopToRootAsync()
|
||||
{
|
||||
return ((INavigation)this).PopToRootAsync(true);
|
||||
}
|
||||
|
||||
Task INavigation.PopToRootAsync(bool animated)
|
||||
{
|
||||
throw new InvalidOperationException("PopToRootAsync is not supported globally on Tizen, please use a NavigationPage.");
|
||||
}
|
||||
|
||||
Task INavigation.PushAsync(Page root)
|
||||
{
|
||||
return ((INavigation)this).PushAsync(root, true);
|
||||
}
|
||||
|
||||
Task INavigation.PushAsync(Page root, bool animated)
|
||||
{
|
||||
throw new InvalidOperationException("PushAsync is not supported globally on Tizen, please use a NavigationPage.");
|
||||
}
|
||||
|
||||
void INavigation.RemovePage(Page page)
|
||||
{
|
||||
throw new InvalidOperationException("RemovePage is not supported globally on Tizen, please use a NavigationPage.");
|
||||
}
|
||||
|
||||
SizeRequest IPlatform.GetNativeSize(VisualElement view, double widthConstraint, double heightConstraint)
|
||||
{
|
||||
return Platform.GetNativeSize(view, widthConstraint, heightConstraint);
|
||||
}
|
||||
|
||||
void OnLayout(object sender, Native.LayoutEventArgs e)
|
||||
{
|
||||
foreach (var child in _viewStack.Children)
|
||||
{
|
||||
child.Geometry = _viewStack.Geometry;
|
||||
}
|
||||
}
|
||||
|
||||
void ResetChildren()
|
||||
{
|
||||
var children = _viewStack.Children.ToList();
|
||||
_viewStack.Children.Clear();
|
||||
foreach (var child in children)
|
||||
{
|
||||
child.Unrealize();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,39 +10,12 @@ namespace Xamarin.Forms.Platform.Tizen.Native
|
|||
/// </summary>
|
||||
public class Image : EImage, IMeasurable
|
||||
{
|
||||
Aspect _aspect;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Xamarin.Forms.Platform.Tizen.Native.Image"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">The parent EvasObject.</param>
|
||||
public Image(EvasObject parent) : base(parent)
|
||||
{
|
||||
IsScaling = true;
|
||||
CanScaleUp = true;
|
||||
CanScaleDown = true;
|
||||
|
||||
ApplyAspect(Aspect.AspectFit);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the image aspect ratio preserving option.
|
||||
/// </summary>
|
||||
/// <value>The aspect option.</value>
|
||||
public Aspect Aspect
|
||||
{
|
||||
get
|
||||
{
|
||||
return _aspect;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (_aspect != value)
|
||||
{
|
||||
ApplyAspect(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -67,6 +40,15 @@ namespace Xamarin.Forms.Platform.Tizen.Native
|
|||
return isLoadComplate;
|
||||
}
|
||||
|
||||
public bool LoadFromFile(string file)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(file))
|
||||
{
|
||||
return Load(ResourcePath.GetPath(file));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implements the <see cref="Xamarin.Forms.Platform.Tizen.Native.IMeasurable"/> interface.
|
||||
/// </summary>
|
||||
|
@ -75,7 +57,6 @@ namespace Xamarin.Forms.Platform.Tizen.Native
|
|||
public ESize Measure(int availableWidth, int availableHeight)
|
||||
{
|
||||
var imageSize = ObjectSize;
|
||||
|
||||
var size = new ESize()
|
||||
{
|
||||
Width = imageSize.Width,
|
||||
|
@ -96,36 +77,5 @@ namespace Xamarin.Forms.Platform.Tizen.Native
|
|||
|
||||
return size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the <c>IsFixedAspect</c> and <c>CanFillOutside</c> properties according to the given <paramref name="aspect"/>.
|
||||
/// </summary>
|
||||
/// <param name="aspect">The aspect setting to be applied to the image.</param>
|
||||
void ApplyAspect(Aspect aspect)
|
||||
{
|
||||
_aspect = aspect;
|
||||
|
||||
switch (_aspect)
|
||||
{
|
||||
case Aspect.AspectFit:
|
||||
IsFixedAspect = true;
|
||||
CanFillOutside = false;
|
||||
break;
|
||||
|
||||
case Aspect.AspectFill:
|
||||
IsFixedAspect = true;
|
||||
CanFillOutside = true;
|
||||
break;
|
||||
|
||||
case Aspect.Fill:
|
||||
IsFixedAspect = false;
|
||||
CanFillOutside = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
Log.Warn("Invalid Aspect value: {0}", _aspect);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +1,11 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.ComponentModel;
|
||||
using Xamarin.Forms.Internals;
|
||||
using Xamarin.Forms.PlatformConfiguration.TizenSpecific;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using ElmSharp;
|
||||
using EProgressBar = ElmSharp.ProgressBar;
|
||||
using EButton = ElmSharp.Button;
|
||||
using EColor = ElmSharp.Color;
|
||||
using Xamarin.Forms.Internals;
|
||||
|
||||
[assembly: InternalsVisibleTo("Xamarin.Forms.Material")]
|
||||
|
||||
|
@ -85,6 +81,7 @@ namespace Xamarin.Forms.Platform.Tizen
|
|||
EvasObject GetRootNativeView();
|
||||
bool HasAlpha { get; set; }
|
||||
event EventHandler<RootNativeViewChangedEventArgs> RootNativeViewChanged;
|
||||
bool PageIsChildOfPlatform(Page page);
|
||||
}
|
||||
|
||||
public class RootNativeViewChangedEventArgs : EventArgs
|
||||
|
@ -97,9 +94,8 @@ namespace Xamarin.Forms.Platform.Tizen
|
|||
{
|
||||
NavigationModel _navModel = new NavigationModel();
|
||||
bool _disposed;
|
||||
Native.Dialog _pageBusyDialog;
|
||||
int _pageBusyCount;
|
||||
readonly Naviframe _internalNaviframe;
|
||||
readonly PopupManager _popupManager;
|
||||
|
||||
readonly HashSet<EvasObject> _alerts = new HashSet<EvasObject>();
|
||||
|
||||
|
@ -110,14 +106,6 @@ namespace Xamarin.Forms.Platform.Tizen
|
|||
internal DefaultPlatform(EvasObject parent)
|
||||
{
|
||||
Forms.NativeParent = parent;
|
||||
_pageBusyCount = 0;
|
||||
if (Forms.UseMessagingCenter)
|
||||
{
|
||||
MessagingCenter.Subscribe<Page, bool>(this, Page.BusySetSignalName, BusySetSignalNameHandler);
|
||||
MessagingCenter.Subscribe<Page, AlertArguments>(this, Page.AlertSignalName, AlertSignalNameHandler);
|
||||
MessagingCenter.Subscribe<Page, ActionSheetArguments>(this, Page.ActionSheetSignalName, ActionSheetSignalNameHandler);
|
||||
MessagingCenter.Subscribe<Page, PromptArguments>(this, Page.PromptSignalName, OnPromptRequested);
|
||||
}
|
||||
|
||||
_internalNaviframe = new Naviframe(Forms.NativeParent)
|
||||
{
|
||||
|
@ -128,6 +116,11 @@ namespace Xamarin.Forms.Platform.Tizen
|
|||
_internalNaviframe.SetWeight(1.0, 1.0);
|
||||
_internalNaviframe.Show();
|
||||
_internalNaviframe.AnimationFinished += NaviAnimationFinished;
|
||||
|
||||
if (Forms.UseMessagingCenter)
|
||||
{
|
||||
_popupManager = new PopupManager(this);
|
||||
}
|
||||
}
|
||||
|
||||
~DefaultPlatform()
|
||||
|
@ -224,18 +217,18 @@ namespace Xamarin.Forms.Platform.Tizen
|
|||
return _internalNaviframe as EvasObject;
|
||||
}
|
||||
|
||||
public bool PageIsChildOfPlatform(Page page)
|
||||
{
|
||||
var parent = page.AncestorToRoot();
|
||||
return Page == parent || _navModel.Roots.Contains(parent);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed) return;
|
||||
if (disposing)
|
||||
{
|
||||
if (Forms.UseMessagingCenter)
|
||||
{
|
||||
MessagingCenter.Unsubscribe<Page, AlertArguments>(this, Page.AlertSignalName);
|
||||
MessagingCenter.Unsubscribe<Page, bool>(this, Page.BusySetSignalName);
|
||||
MessagingCenter.Unsubscribe<Page, ActionSheetArguments>(this, Page.ActionSheetSignalName);
|
||||
MessagingCenter.Unsubscribe<Page, PromptArguments>(this, Page.PromptSignalName);
|
||||
}
|
||||
_popupManager?.Dispose();
|
||||
SetPage(null);
|
||||
_internalNaviframe.Unrealize();
|
||||
}
|
||||
|
@ -405,263 +398,6 @@ namespace Xamarin.Forms.Platform.Tizen
|
|||
tcs?.SetResult(true);
|
||||
}
|
||||
|
||||
void BusySetSignalNameHandler(Page sender, bool enabled)
|
||||
{
|
||||
// Verify that the page making the request is child of this platform
|
||||
if (!PageIsChildOfPlatform(sender))
|
||||
return;
|
||||
|
||||
if (null == _pageBusyDialog)
|
||||
{
|
||||
_pageBusyDialog = new Native.Dialog(Forms.NativeParent)
|
||||
{
|
||||
Orientation = PopupOrientation.Center,
|
||||
BackgroundColor = EColor.Transparent
|
||||
};
|
||||
|
||||
if (Device.Idiom == TargetIdiom.Phone)
|
||||
{
|
||||
_pageBusyDialog.SetPartColor("bg_title", EColor.Transparent);
|
||||
_pageBusyDialog.SetPartColor("bg_content", EColor.Transparent);
|
||||
}
|
||||
else if (Device.Idiom == TargetIdiom.Watch)
|
||||
{
|
||||
_pageBusyDialog.Style = "circle";
|
||||
}
|
||||
|
||||
var activity = new EProgressBar(_pageBusyDialog)
|
||||
{
|
||||
Style = "process_large",
|
||||
IsPulseMode = true,
|
||||
};
|
||||
activity.PlayPulse();
|
||||
activity.Show();
|
||||
|
||||
_pageBusyDialog.Content = activity;
|
||||
|
||||
}
|
||||
_pageBusyCount = Math.Max(0, enabled ? _pageBusyCount + 1 : _pageBusyCount - 1);
|
||||
if (_pageBusyCount > 0)
|
||||
{
|
||||
_pageBusyDialog.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
_pageBusyDialog.Dismiss();
|
||||
_pageBusyDialog = null;
|
||||
}
|
||||
}
|
||||
|
||||
void AlertSignalNameHandler(Page sender, AlertArguments arguments)
|
||||
{
|
||||
// Verify that the page making the request is child of this platform
|
||||
if (!PageIsChildOfPlatform(sender))
|
||||
return;
|
||||
|
||||
Native.Dialog alert = Native.Dialog.CreateDialog(Forms.NativeParent, (arguments.Accept != null));
|
||||
|
||||
alert.Title = arguments.Title;
|
||||
var message = arguments.Message.Replace("&", "&").Replace("<", "<").Replace(">", ">").Replace(Environment.NewLine, "<br>");
|
||||
alert.Message = message;
|
||||
|
||||
EButton cancel = new EButton(alert) { Text = arguments.Cancel };
|
||||
alert.NegativeButton = cancel;
|
||||
cancel.Clicked += (s, evt) =>
|
||||
{
|
||||
arguments.SetResult(false);
|
||||
alert.Dismiss();
|
||||
};
|
||||
|
||||
if (arguments.Accept != null)
|
||||
{
|
||||
EButton ok = new EButton(alert) { Text = arguments.Accept };
|
||||
alert.NeutralButton = ok;
|
||||
ok.Clicked += (s, evt) =>
|
||||
{
|
||||
arguments.SetResult(true);
|
||||
alert.Dismiss();
|
||||
};
|
||||
}
|
||||
|
||||
alert.BackButtonPressed += (s, evt) =>
|
||||
{
|
||||
arguments.SetResult(false);
|
||||
alert.Dismiss();
|
||||
};
|
||||
|
||||
alert.Show();
|
||||
_alerts.Add(alert);
|
||||
alert.Dismissed += (s, e) => _alerts.Remove(alert);
|
||||
}
|
||||
|
||||
void ActionSheetSignalNameHandler(Page sender, ActionSheetArguments arguments)
|
||||
{
|
||||
// Verify that the page making the request is child of this platform
|
||||
if (!PageIsChildOfPlatform(sender))
|
||||
return;
|
||||
|
||||
Native.Dialog alert = Native.Dialog.CreateDialog(Forms.NativeParent);
|
||||
|
||||
alert.Title = arguments.Title;
|
||||
Box box = new Box(alert);
|
||||
|
||||
if (null != arguments.Destruction)
|
||||
{
|
||||
Native.Button destruction = new Native.Button(alert)
|
||||
{
|
||||
Text = arguments.Destruction,
|
||||
Style = ButtonStyle.Text,
|
||||
TextColor = EColor.Red,
|
||||
AlignmentX = -1
|
||||
};
|
||||
destruction.Clicked += (s, evt) =>
|
||||
{
|
||||
arguments.SetResult(arguments.Destruction);
|
||||
alert.Dismiss();
|
||||
};
|
||||
destruction.Show();
|
||||
box.PackEnd(destruction);
|
||||
}
|
||||
|
||||
foreach (string buttonName in arguments.Buttons)
|
||||
{
|
||||
Native.Button button = new Native.Button(alert)
|
||||
{
|
||||
Text = buttonName,
|
||||
Style = ButtonStyle.Text,
|
||||
AlignmentX = -1
|
||||
};
|
||||
button.Clicked += (s, evt) =>
|
||||
{
|
||||
arguments.SetResult(buttonName);
|
||||
alert.Dismiss();
|
||||
};
|
||||
button.Show();
|
||||
box.PackEnd(button);
|
||||
}
|
||||
|
||||
box.Show();
|
||||
alert.Content = box;
|
||||
|
||||
if (null != arguments.Cancel)
|
||||
{
|
||||
EButton cancel = new EButton(Forms.NativeParent) { Text = arguments.Cancel };
|
||||
alert.NegativeButton = cancel;
|
||||
cancel.Clicked += (s, evt) =>
|
||||
{
|
||||
alert.Dismiss();
|
||||
};
|
||||
}
|
||||
|
||||
alert.BackButtonPressed += (s, evt) =>
|
||||
{
|
||||
alert.Dismiss();
|
||||
};
|
||||
|
||||
alert.Show();
|
||||
|
||||
_alerts.Add(alert);
|
||||
alert.Dismissed += (s, e) => _alerts.Remove(alert);
|
||||
}
|
||||
|
||||
void OnPromptRequested(Page sender, PromptArguments args)
|
||||
{
|
||||
// Verify that the page making the request is child of this platform
|
||||
if (!PageIsChildOfPlatform(sender))
|
||||
return;
|
||||
|
||||
var prompt = Native.Dialog.CreateDialog(Forms.NativeParent, (args.Accept != null));
|
||||
prompt.Title = args.Title;
|
||||
|
||||
var entry = new Entry
|
||||
{
|
||||
MinimumWidthRequest = 200,
|
||||
HorizontalOptions = LayoutOptions.FillAndExpand,
|
||||
BackgroundColor = Color.FromRgb(250, 250, 250),
|
||||
TextColor = Color.Black,
|
||||
Keyboard = args.Keyboard,
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(args.Placeholder))
|
||||
{
|
||||
entry.Placeholder = args.Placeholder;
|
||||
}
|
||||
if (args.MaxLength > 0)
|
||||
{
|
||||
entry.MaxLength = args.MaxLength;
|
||||
}
|
||||
|
||||
var layout = new StackLayout
|
||||
{
|
||||
Spacing = 10,
|
||||
Children =
|
||||
{
|
||||
new Label
|
||||
{
|
||||
LineBreakMode = LineBreakMode.CharacterWrap,
|
||||
TextColor = Device.Idiom == TargetIdiom.Watch ? Color.White : Color.Accent,
|
||||
Text = args.Message,
|
||||
HorizontalOptions = LayoutOptions.FillAndExpand,
|
||||
HorizontalTextAlignment = TextAlignment.Center,
|
||||
FontSize = Device.GetNamedSize(NamedSize.Subtitle, typeof(Label)),
|
||||
},
|
||||
entry,
|
||||
}
|
||||
};
|
||||
|
||||
layout.Parent = sender;
|
||||
var layoutrenderer = Platform.GetOrCreateRenderer(layout);
|
||||
|
||||
var request = layout.Measure(Device.Idiom == TargetIdiom.Watch ? Page.Width * 0.7 : Page.Width, Page.Height);
|
||||
(layoutrenderer as LayoutRenderer).RegisterOnLayoutUpdated();
|
||||
layoutrenderer.NativeView.MinimumHeight = Forms.ConvertToScaledPixel(request.Request.Height);
|
||||
layoutrenderer.NativeView.MinimumWidth = Forms.ConvertToScaledPixel(request.Request.Width);
|
||||
|
||||
prompt.Content = layoutrenderer.NativeView;
|
||||
|
||||
var cancel = new EButton(prompt) { Text = args.Cancel };
|
||||
prompt.NegativeButton = cancel;
|
||||
cancel.Clicked += (s, evt) =>
|
||||
{
|
||||
args.SetResult(null);
|
||||
prompt.Dismiss();
|
||||
};
|
||||
|
||||
if (args.Accept != null)
|
||||
{
|
||||
var ok = new EButton(prompt) { Text = args.Accept };
|
||||
prompt.NeutralButton = ok;
|
||||
ok.Clicked += (s, evt) =>
|
||||
{
|
||||
args.SetResult(entry.Text);
|
||||
prompt.Dismiss();
|
||||
};
|
||||
}
|
||||
|
||||
entry.Completed += (s, e) =>
|
||||
{
|
||||
args.SetResult(entry.Text);
|
||||
prompt.Dismiss();
|
||||
};
|
||||
|
||||
prompt.BackButtonPressed += (s, evt) =>
|
||||
{
|
||||
args.SetResult(null);
|
||||
prompt.Dismiss();
|
||||
};
|
||||
|
||||
prompt.Show();
|
||||
|
||||
_alerts.Add(prompt);
|
||||
prompt.Dismissed += (s, e) => _alerts.Remove(prompt);
|
||||
}
|
||||
|
||||
bool PageIsChildOfPlatform(Page page)
|
||||
{
|
||||
var parent = page.AncestorToRoot();
|
||||
return Page == parent || _navModel.Roots.Contains(parent);
|
||||
}
|
||||
|
||||
SizeRequest IPlatform.GetNativeSize(VisualElement view, double widthConstraint, double heightConstraint)
|
||||
{
|
||||
return Platform.GetNativeSize(view, widthConstraint, heightConstraint);
|
||||
|
|
|
@ -0,0 +1,295 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ElmSharp;
|
||||
using Xamarin.Forms.Internals;
|
||||
using Xamarin.Forms.PlatformConfiguration.TizenSpecific;
|
||||
using EButton = ElmSharp.Button;
|
||||
using EColor = ElmSharp.Color;
|
||||
using EProgressBar = ElmSharp.ProgressBar;
|
||||
|
||||
namespace Xamarin.Forms.Platform.Tizen
|
||||
{
|
||||
public class PopupManager : IDisposable
|
||||
{
|
||||
ITizenPlatform _platform;
|
||||
Native.Dialog _pageBusyDialog;
|
||||
int _pageBusyCount;
|
||||
readonly HashSet<EvasObject> _alerts = new HashSet<EvasObject>();
|
||||
|
||||
public PopupManager(ITizenPlatform platform)
|
||||
{
|
||||
_platform = platform;
|
||||
MessagingCenter.Subscribe<Page, bool>(this, Page.BusySetSignalName, OnBusySetRequest);
|
||||
MessagingCenter.Subscribe<Page, AlertArguments>(this, Page.AlertSignalName, OnAlertRequest);
|
||||
MessagingCenter.Subscribe<Page, ActionSheetArguments>(this, Page.ActionSheetSignalName, OnActionSheetRequest);
|
||||
MessagingCenter.Subscribe<Page, PromptArguments>(this, Page.PromptSignalName, OnPromptRequested);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
MessagingCenter.Unsubscribe<Page, AlertArguments>(this, Page.AlertSignalName);
|
||||
MessagingCenter.Unsubscribe<Page, bool>(this, Page.BusySetSignalName);
|
||||
MessagingCenter.Unsubscribe<Page, ActionSheetArguments>(this, Page.ActionSheetSignalName);
|
||||
MessagingCenter.Unsubscribe<Page, PromptArguments>(this, Page.PromptSignalName);
|
||||
}
|
||||
}
|
||||
|
||||
void OnBusySetRequest(Page sender, bool enabled)
|
||||
{
|
||||
// Verify that the page making the request is child of this platform
|
||||
if (!_platform.PageIsChildOfPlatform(sender))
|
||||
return;
|
||||
|
||||
if (null == _pageBusyDialog)
|
||||
{
|
||||
_pageBusyDialog = new Native.Dialog(Forms.NativeParent)
|
||||
{
|
||||
Orientation = PopupOrientation.Center,
|
||||
BackgroundColor = EColor.Transparent
|
||||
};
|
||||
|
||||
if (Device.Idiom == TargetIdiom.Phone)
|
||||
{
|
||||
_pageBusyDialog.SetPartColor("bg_title", EColor.Transparent);
|
||||
_pageBusyDialog.SetPartColor("bg_content", EColor.Transparent);
|
||||
}
|
||||
else if (Device.Idiom == TargetIdiom.Watch)
|
||||
{
|
||||
_pageBusyDialog.Style = "circle";
|
||||
}
|
||||
|
||||
var activity = new EProgressBar(_pageBusyDialog)
|
||||
{
|
||||
Style = "process_large",
|
||||
IsPulseMode = true,
|
||||
};
|
||||
activity.PlayPulse();
|
||||
activity.Show();
|
||||
|
||||
_pageBusyDialog.Content = activity;
|
||||
|
||||
}
|
||||
_pageBusyCount = Math.Max(0, enabled ? _pageBusyCount + 1 : _pageBusyCount - 1);
|
||||
if (_pageBusyCount > 0)
|
||||
{
|
||||
_pageBusyDialog.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
_pageBusyDialog.Dismiss();
|
||||
_pageBusyDialog = null;
|
||||
}
|
||||
}
|
||||
|
||||
void OnAlertRequest(Page sender, AlertArguments arguments)
|
||||
{
|
||||
// Verify that the page making the request is child of this platform
|
||||
if (!_platform.PageIsChildOfPlatform(sender))
|
||||
return;
|
||||
|
||||
var alert = Native.Dialog.CreateDialog(Forms.NativeParent, (arguments.Accept != null));
|
||||
|
||||
alert.Title = arguments.Title;
|
||||
var message = arguments.Message.Replace("&", "&").Replace("<", "<").Replace(">", ">").Replace(Environment.NewLine, "<br>");
|
||||
alert.Message = message;
|
||||
|
||||
var cancel = new EButton(alert) { Text = arguments.Cancel };
|
||||
alert.NegativeButton = cancel;
|
||||
cancel.Clicked += (s, evt) =>
|
||||
{
|
||||
arguments.SetResult(false);
|
||||
alert.Dismiss();
|
||||
};
|
||||
|
||||
if (arguments.Accept != null)
|
||||
{
|
||||
var ok = new EButton(alert) { Text = arguments.Accept };
|
||||
alert.NeutralButton = ok;
|
||||
ok.Clicked += (s, evt) =>
|
||||
{
|
||||
arguments.SetResult(true);
|
||||
alert.Dismiss();
|
||||
};
|
||||
}
|
||||
|
||||
alert.BackButtonPressed += (s, evt) =>
|
||||
{
|
||||
arguments.SetResult(false);
|
||||
alert.Dismiss();
|
||||
};
|
||||
|
||||
alert.Show();
|
||||
_alerts.Add(alert);
|
||||
alert.Dismissed += (s, e) => _alerts.Remove(alert);
|
||||
}
|
||||
|
||||
void OnActionSheetRequest(Page sender, ActionSheetArguments arguments)
|
||||
{
|
||||
// Verify that the page making the request is child of this platform
|
||||
if (!_platform.PageIsChildOfPlatform(sender))
|
||||
return;
|
||||
|
||||
var alert = Native.Dialog.CreateDialog(Forms.NativeParent);
|
||||
|
||||
alert.Title = arguments.Title;
|
||||
var box = new Box(alert);
|
||||
|
||||
if (null != arguments.Destruction)
|
||||
{
|
||||
var destruction = new Native.Button(alert)
|
||||
{
|
||||
Text = arguments.Destruction,
|
||||
Style = ButtonStyle.Text,
|
||||
TextColor = EColor.Red,
|
||||
AlignmentX = -1
|
||||
};
|
||||
destruction.Clicked += (s, evt) =>
|
||||
{
|
||||
arguments.SetResult(arguments.Destruction);
|
||||
alert.Dismiss();
|
||||
};
|
||||
destruction.Show();
|
||||
box.PackEnd(destruction);
|
||||
}
|
||||
|
||||
foreach (string buttonName in arguments.Buttons)
|
||||
{
|
||||
var button = new Native.Button(alert)
|
||||
{
|
||||
Text = buttonName,
|
||||
Style = ButtonStyle.Text,
|
||||
AlignmentX = -1
|
||||
};
|
||||
button.Clicked += (s, evt) =>
|
||||
{
|
||||
arguments.SetResult(buttonName);
|
||||
alert.Dismiss();
|
||||
};
|
||||
button.Show();
|
||||
box.PackEnd(button);
|
||||
}
|
||||
|
||||
box.Show();
|
||||
alert.Content = box;
|
||||
|
||||
if (null != arguments.Cancel)
|
||||
{
|
||||
var cancel = new EButton(Forms.NativeParent) { Text = arguments.Cancel };
|
||||
alert.NegativeButton = cancel;
|
||||
cancel.Clicked += (s, evt) =>
|
||||
{
|
||||
alert.Dismiss();
|
||||
};
|
||||
}
|
||||
|
||||
alert.BackButtonPressed += (s, evt) =>
|
||||
{
|
||||
alert.Dismiss();
|
||||
};
|
||||
|
||||
alert.Show();
|
||||
|
||||
_alerts.Add(alert);
|
||||
alert.Dismissed += (s, e) => _alerts.Remove(alert);
|
||||
}
|
||||
|
||||
void OnPromptRequested(Page sender, PromptArguments args)
|
||||
{
|
||||
// Verify that the page making the request is child of this platform
|
||||
if (!_platform.PageIsChildOfPlatform(sender))
|
||||
return;
|
||||
|
||||
var prompt = Native.Dialog.CreateDialog(Forms.NativeParent, (args.Accept != null));
|
||||
prompt.Title = args.Title;
|
||||
|
||||
var entry = new Entry
|
||||
{
|
||||
MinimumWidthRequest = 200,
|
||||
HorizontalOptions = LayoutOptions.FillAndExpand,
|
||||
BackgroundColor = Color.FromRgb(250, 250, 250),
|
||||
TextColor = Color.Black,
|
||||
Keyboard = args.Keyboard,
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(args.Placeholder))
|
||||
{
|
||||
entry.Placeholder = args.Placeholder;
|
||||
}
|
||||
if (args.MaxLength > 0)
|
||||
{
|
||||
entry.MaxLength = args.MaxLength;
|
||||
}
|
||||
|
||||
var layout = new StackLayout
|
||||
{
|
||||
Spacing = 10,
|
||||
Children =
|
||||
{
|
||||
new Label
|
||||
{
|
||||
LineBreakMode = LineBreakMode.CharacterWrap,
|
||||
TextColor = Device.Idiom == TargetIdiom.Watch ? Color.White : Color.Accent,
|
||||
Text = args.Message,
|
||||
HorizontalOptions = LayoutOptions.FillAndExpand,
|
||||
HorizontalTextAlignment = TextAlignment.Center,
|
||||
FontSize = Device.GetNamedSize(NamedSize.Subtitle, typeof(Label)),
|
||||
},
|
||||
entry,
|
||||
}
|
||||
};
|
||||
|
||||
layout.Parent = sender;
|
||||
var layoutrenderer = Platform.GetOrCreateRenderer(layout);
|
||||
|
||||
var request = layout.Measure(Device.Idiom == TargetIdiom.Watch ? sender.Width * 0.7 : sender.Width, sender.Height);
|
||||
(layoutrenderer as LayoutRenderer).RegisterOnLayoutUpdated();
|
||||
layoutrenderer.NativeView.MinimumHeight = Forms.ConvertToScaledPixel(request.Request.Height);
|
||||
layoutrenderer.NativeView.MinimumWidth = Forms.ConvertToScaledPixel(request.Request.Width);
|
||||
|
||||
prompt.Content = layoutrenderer.NativeView;
|
||||
|
||||
var cancel = new EButton(prompt) { Text = args.Cancel };
|
||||
prompt.NegativeButton = cancel;
|
||||
cancel.Clicked += (s, evt) =>
|
||||
{
|
||||
args.SetResult(null);
|
||||
prompt.Dismiss();
|
||||
};
|
||||
|
||||
if (args.Accept != null)
|
||||
{
|
||||
var ok = new EButton(prompt) { Text = args.Accept };
|
||||
prompt.NeutralButton = ok;
|
||||
ok.Clicked += (s, evt) =>
|
||||
{
|
||||
args.SetResult(entry.Text);
|
||||
prompt.Dismiss();
|
||||
};
|
||||
}
|
||||
|
||||
entry.Completed += (s, e) =>
|
||||
{
|
||||
args.SetResult(entry.Text);
|
||||
prompt.Dismiss();
|
||||
};
|
||||
|
||||
prompt.BackButtonPressed += (s, evt) =>
|
||||
{
|
||||
prompt.Dismiss();
|
||||
};
|
||||
|
||||
prompt.Show();
|
||||
|
||||
_alerts.Add(prompt);
|
||||
prompt.Dismissed += (s, e) => _alerts.Remove(prompt);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -177,7 +177,7 @@ namespace Xamarin.Forms.Platform.Tizen
|
|||
|
||||
void UpdateAspect()
|
||||
{
|
||||
_image.Aspect = Element.Aspect;
|
||||
_image.ApplyAspect(Element.Aspect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,52 +8,41 @@ namespace Xamarin.Forms.Platform.Tizen
|
|||
{
|
||||
public class ImageRenderer : ViewRenderer<Image, Native.Image>
|
||||
{
|
||||
public ImageRenderer()
|
||||
{
|
||||
RegisterPropertyHandler(Image.SourceProperty, UpdateSource);
|
||||
RegisterPropertyHandler(Image.AspectProperty, UpdateAspect);
|
||||
RegisterPropertyHandler(Image.IsOpaqueProperty, UpdateIsOpaque);
|
||||
RegisterPropertyHandler(Image.IsAnimationPlayingProperty, UpdateIsAnimationPlaying);
|
||||
RegisterPropertyHandler(Specific.BlendColorProperty, UpdateBlendColor);
|
||||
RegisterPropertyHandler(Specific.FileProperty, UpdateFile);
|
||||
}
|
||||
|
||||
protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
|
||||
{
|
||||
if (Control == null)
|
||||
{
|
||||
var image = new Native.Image(Forms.NativeParent);
|
||||
SetNativeControl(image);
|
||||
SetNativeControl(new Native.Image(Forms.NativeParent));
|
||||
}
|
||||
|
||||
UpdateAll();
|
||||
base.OnElementChanged(e);
|
||||
}
|
||||
|
||||
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
async void UpdateSource(bool initialize)
|
||||
{
|
||||
base.OnElementPropertyChanged(sender, e);
|
||||
if (e.PropertyName == Image.SourceProperty.PropertyName)
|
||||
{
|
||||
UpdateSource();
|
||||
}
|
||||
else if (e.PropertyName == Image.AspectProperty.PropertyName)
|
||||
{
|
||||
UpdateAspect();
|
||||
}
|
||||
else if (e.PropertyName == Image.IsOpaqueProperty.PropertyName)
|
||||
{
|
||||
UpdateIsOpaque();
|
||||
}
|
||||
else if (e.PropertyName == Specific.BlendColorProperty.PropertyName)
|
||||
{
|
||||
UpdateBlendColor();
|
||||
}
|
||||
}
|
||||
if (initialize && Element.Source == default(ImageSource))
|
||||
return;
|
||||
|
||||
async void UpdateSource()
|
||||
{
|
||||
ImageSource source = Element.Source;
|
||||
|
||||
((IImageController)Element).SetIsLoading(true);
|
||||
|
||||
if (Control != null)
|
||||
{
|
||||
bool success = await Control.LoadFromImageSourceAsync(source);
|
||||
|
||||
if (!IsDisposed && success)
|
||||
{
|
||||
((IVisualElementController)Element).NativeSizeChanged();
|
||||
UpdateAfterLoading();
|
||||
UpdateAfterLoading(initialize);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,31 +50,61 @@ namespace Xamarin.Forms.Platform.Tizen
|
|||
((IImageController)Element).SetIsLoading(false);
|
||||
}
|
||||
|
||||
protected virtual void UpdateAfterLoading()
|
||||
void UpdateFile(bool initialize)
|
||||
{
|
||||
UpdateIsOpaque();
|
||||
UpdateBlendColor();
|
||||
if (initialize && Specific.GetFile(Element) == default || Element.Source != default(ImageSource))
|
||||
return;
|
||||
|
||||
if (Control != null)
|
||||
{
|
||||
bool success = Control.LoadFromFile(Specific.GetFile(Element));
|
||||
|
||||
if (!IsDisposed && success)
|
||||
{
|
||||
((IVisualElementController)Element).NativeSizeChanged();
|
||||
UpdateAfterLoading(initialize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateAspect()
|
||||
protected virtual void UpdateAfterLoading(bool initialize)
|
||||
{
|
||||
Control.Aspect = Element.Aspect;
|
||||
UpdateIsOpaque(initialize);
|
||||
UpdateBlendColor(initialize);
|
||||
UpdateIsAnimationPlaying(initialize);
|
||||
}
|
||||
|
||||
void UpdateIsOpaque()
|
||||
void UpdateAspect(bool initialize)
|
||||
{
|
||||
if (initialize && Element.Aspect == Aspect.AspectFit)
|
||||
return;
|
||||
|
||||
Control.ApplyAspect(Element.Aspect);
|
||||
}
|
||||
|
||||
void UpdateIsOpaque(bool initialize)
|
||||
{
|
||||
if (initialize && !Element.IsOpaque)
|
||||
return;
|
||||
|
||||
Control.IsOpaque = Element.IsOpaque;
|
||||
}
|
||||
|
||||
void UpdateBlendColor()
|
||||
void UpdateIsAnimationPlaying(bool initialize)
|
||||
{
|
||||
Control.Color = Specific.GetBlendColor(Element).ToNative();
|
||||
if (initialize && !Element.IsAnimationPlaying)
|
||||
return;
|
||||
|
||||
Control.IsAnimated = Element.IsAnimationPlaying;
|
||||
Control.IsAnimationPlaying = Element.IsAnimationPlaying;
|
||||
}
|
||||
|
||||
void UpdateAll()
|
||||
void UpdateBlendColor(bool initialize)
|
||||
{
|
||||
UpdateSource();
|
||||
UpdateAspect();
|
||||
if (initialize && Specific.GetBlendColor(Element).IsDefault)
|
||||
return;
|
||||
|
||||
Control.Color = Specific.GetBlendColor(Element).ToNative();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using ElmSharp;
|
||||
using EColor = ElmSharp.Color;
|
||||
using Xamarin.Forms.Platform.Tizen.Native;
|
||||
using EColor = ElmSharp.Color;
|
||||
using EButton = ElmSharp.Button;
|
||||
using EImage = ElmSharp.Image;
|
||||
|
||||
namespace Xamarin.Forms.Platform.Tizen
|
||||
{
|
||||
public class ShellNavBar : Native.Box
|
||||
{
|
||||
Native.Image _menu = null;
|
||||
EImage _menu = null;
|
||||
EButton _menuButton = null;
|
||||
Native.Label _title = null;
|
||||
Native.SearchBar _nativeSearchHandler = null;
|
||||
EvasObject _nativeTitleView = null;
|
||||
ShellSectionNavigation _shellSectionNavigation = null;
|
||||
|
||||
SearchHandler _searchHandler = null;
|
||||
View _titleView = null;
|
||||
|
@ -29,15 +31,18 @@ namespace Xamarin.Forms.Platform.Tizen
|
|||
|
||||
bool _hasBackButton = false;
|
||||
|
||||
public ShellNavBar(IFlyoutController flyoutController, ShellSectionNavigation shellSectionNavigation) : base(Forms.NativeParent)
|
||||
public ShellNavBar(IFlyoutController flyoutController) : base(Forms.NativeParent)
|
||||
{
|
||||
_flyoutController = flyoutController;
|
||||
_shellSectionNavigation = shellSectionNavigation;
|
||||
|
||||
_menu = new Native.Image(Forms.NativeParent);
|
||||
_menu.Clicked += OnMenuClicked;
|
||||
_menuButton = new EButton(Forms.NativeParent);
|
||||
_menuButton.Clicked += OnMenuClicked;
|
||||
_menu = new EImage(Forms.NativeParent);
|
||||
UpdateMenuIcon();
|
||||
_menu.Show();
|
||||
_menuButton.Show();
|
||||
|
||||
_menuButton.SetPartContent("icon", _menu);
|
||||
|
||||
_title = new Native.Label(Forms.NativeParent)
|
||||
{
|
||||
|
@ -49,7 +54,8 @@ namespace Xamarin.Forms.Platform.Tizen
|
|||
_title.Show();
|
||||
|
||||
BackgroundColor = _backgroudColor;
|
||||
PackEnd(_menu);
|
||||
_menuButton.BackgroundColor = _backgroudColor;
|
||||
PackEnd(_menuButton);
|
||||
PackEnd(_title);
|
||||
LayoutUpdated += OnLayoutUpdated;
|
||||
}
|
||||
|
@ -123,6 +129,7 @@ namespace Xamarin.Forms.Platform.Tizen
|
|||
set
|
||||
{
|
||||
_backgroudColor = value;
|
||||
_menuButton.BackgroundColor = _backgroudColor;
|
||||
base.BackgroundColor = _backgroudColor;
|
||||
}
|
||||
}
|
||||
|
@ -136,7 +143,7 @@ namespace Xamarin.Forms.Platform.Tizen
|
|||
set
|
||||
{
|
||||
_foregroudColor = value;
|
||||
_menu.Color = value;
|
||||
_menuButton.Color = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -164,11 +171,11 @@ namespace Xamarin.Forms.Platform.Tizen
|
|||
}
|
||||
}
|
||||
|
||||
async void UpdateMenuIcon()
|
||||
void UpdateMenuIcon()
|
||||
{
|
||||
string file = _hasBackButton ? _backIcon : _menuIcon;
|
||||
ImageSource source = ImageSource.FromResource(file, typeof(ShellNavBar).GetTypeInfo().Assembly);
|
||||
bool ret = await _menu.LoadFromImageSourceAsync(source);
|
||||
var path = Assembly.GetExecutingAssembly().GetManifestResourceStream(file);
|
||||
_menu.Load(path);
|
||||
}
|
||||
|
||||
void OnMenuClicked(object sender, EventArgs e)
|
||||
|
@ -180,7 +187,7 @@ namespace Xamarin.Forms.Platform.Tizen
|
|||
}
|
||||
else if (_hasBackButton)
|
||||
{
|
||||
_shellSectionNavigation.PopRequest(this, new Internals.NavigationRequestedEventArgs(_page, false));
|
||||
Shell.Current.CurrentItem.Navigation.PopAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -258,8 +265,8 @@ namespace Xamarin.Forms.Platform.Tizen
|
|||
int titleLeftMargin = 40;
|
||||
int titleViewTopMargin = 40;
|
||||
|
||||
_menu.Move(e.Geometry.X + menuMargin, e.Geometry.Y + (e.Geometry.Height - menuSize) / 2);
|
||||
_menu.Resize(menuSize, menuSize);
|
||||
_menuButton.Move(e.Geometry.X + menuMargin, e.Geometry.Y + (e.Geometry.Height - menuSize) / 2);
|
||||
_menuButton.Resize(menuSize, menuSize);
|
||||
|
||||
if (_searchHandler != null)
|
||||
{
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace Xamarin.Forms.Platform.Tizen
|
|||
_section.PropertyChanged += OnSectionPropertyChanged;
|
||||
_rootPage = ((IShellContentController)_section.CurrentItem).GetOrCreateContent();
|
||||
|
||||
_navBar = new ShellNavBar(flyoutController, this);
|
||||
_navBar = new ShellNavBar(flyoutController);
|
||||
_navBar.Show();
|
||||
|
||||
var renderer = CreateShellSection(section);
|
||||
|
|
|
@ -431,7 +431,7 @@ namespace Xamarin.Forms.Platform.UWP
|
|||
|
||||
var toolBarForegroundBinder = GetToolbarProvider() as IToolBarForegroundBinder;
|
||||
|
||||
foreach (ToolbarItem item in _toolbarTracker.ToolbarItems.OrderBy(ti => ti.Priority))
|
||||
foreach (ToolbarItem item in _toolbarTracker.ToolbarItems)
|
||||
{
|
||||
toolBarForegroundBinder?.BindForegroundColor(commandBar);
|
||||
|
||||
|
|
|
@ -26,10 +26,10 @@ namespace Xamarin.Forms.Platform.WPF
|
|||
public static readonly DependencyProperty IsPasswordProperty = DependencyProperty.Register("IsPassword", typeof(bool), typeof(FormsTextBox),
|
||||
new PropertyMetadata(default(bool), OnIsPasswordChanged));
|
||||
|
||||
public new static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(FormsTextBox),
|
||||
public new static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(FormsTextBox),
|
||||
new PropertyMetadata("", TextPropertyChanged));
|
||||
|
||||
protected internal static readonly DependencyProperty DisabledTextProperty = DependencyProperty.Register("DisabledText", typeof(string), typeof(FormsTextBox),
|
||||
protected internal static readonly DependencyProperty DisabledTextProperty = DependencyProperty.Register("DisabledText", typeof(string), typeof(FormsTextBox),
|
||||
new PropertyMetadata(""));
|
||||
|
||||
static InputScope s_passwordInputScope;
|
||||
|
@ -52,8 +52,8 @@ namespace Xamarin.Forms.Platform.WPF
|
|||
|
||||
public string PlaceholderText
|
||||
{
|
||||
get { return (string)GetValue (PlaceholderTextProperty); }
|
||||
set { SetValue (PlaceholderTextProperty, value); }
|
||||
get { return (string)GetValue(PlaceholderTextProperty); }
|
||||
set { SetValue(PlaceholderTextProperty, value); }
|
||||
}
|
||||
|
||||
public Brush PlaceholderForegroundBrush
|
||||
|
@ -88,11 +88,12 @@ namespace Xamarin.Forms.Platform.WPF
|
|||
return s_passwordInputScope;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DelayObfuscation()
|
||||
{
|
||||
int lengthDifference = base.Text.Length - Text.Length;
|
||||
|
||||
var savedSelectionStart = SelectionStart;
|
||||
string updatedRealText = DetermineTextFromPassword(Text, SelectionStart, base.Text);
|
||||
|
||||
if (Text == updatedRealText)
|
||||
|
@ -101,7 +102,9 @@ namespace Xamarin.Forms.Platform.WPF
|
|||
return;
|
||||
}
|
||||
|
||||
_internalChangeFlag = true;
|
||||
Text = updatedRealText;
|
||||
_internalChangeFlag = false;
|
||||
|
||||
// Cancel any pending delayed obfuscation
|
||||
_cts?.Cancel();
|
||||
|
@ -118,10 +121,10 @@ namespace Xamarin.Forms.Platform.WPF
|
|||
else
|
||||
{
|
||||
// Only one character was added; we need to leave it visible for a brief time period
|
||||
// Obfuscate all but the last character for now
|
||||
newText = Obfuscate(Text, true);
|
||||
// Obfuscate all but the character added for now
|
||||
newText = Obfuscate(Text, savedSelectionStart - 1);
|
||||
|
||||
// Leave the last character visible until a new character is added
|
||||
// Leave the added character visible until a new character is added
|
||||
// or sufficient time has passed
|
||||
if (_cts == null)
|
||||
{
|
||||
|
@ -134,19 +137,20 @@ namespace Xamarin.Forms.Platform.WPF
|
|||
_cts.Token.ThrowIfCancellationRequested();
|
||||
await Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
|
||||
{
|
||||
var ss = SelectionStart;
|
||||
var sl = SelectionLength;
|
||||
base.Text = Obfuscate(Text);
|
||||
SelectionStart = base.Text.Length;
|
||||
SelectionStart = ss;
|
||||
SelectionLength = sl;
|
||||
}));
|
||||
}, _cts.Token);
|
||||
}
|
||||
|
||||
if (base.Text == newText)
|
||||
if (base.Text != newText)
|
||||
{
|
||||
return;
|
||||
base.Text = newText;
|
||||
}
|
||||
|
||||
base.Text = newText;
|
||||
SelectionStart = base.Text.Length;
|
||||
SelectionStart = savedSelectionStart;
|
||||
}
|
||||
|
||||
static string DetermineTextFromPassword(string realText, int start, string passwordText)
|
||||
|
@ -164,14 +168,19 @@ namespace Xamarin.Forms.Platform.WPF
|
|||
return sb.ToString();
|
||||
}
|
||||
|
||||
string Obfuscate(string text, bool leaveLastVisible = false)
|
||||
string Obfuscate(string text, int visibleSymbolIndex = -1)
|
||||
{
|
||||
if (!leaveLastVisible)
|
||||
if (visibleSymbolIndex == -1)
|
||||
return new string(ObfuscationCharacter, text?.Length ?? 0);
|
||||
|
||||
return text == null || text.Length == 1
|
||||
? text
|
||||
: new string(ObfuscationCharacter, text.Length - 1) + text.Substring(text.Length - 1, 1);
|
||||
if (text == null || text.Length == 1)
|
||||
return text;
|
||||
var prefix = visibleSymbolIndex > 0 ? new string(ObfuscationCharacter, visibleSymbolIndex) : string.Empty;
|
||||
var suffix = visibleSymbolIndex == text.Length - 1
|
||||
? string.Empty
|
||||
: new string(ObfuscationCharacter, text.Length - visibleSymbolIndex - 1);
|
||||
|
||||
return prefix + text.Substring(visibleSymbolIndex, 1) + suffix;
|
||||
}
|
||||
|
||||
static void OnIsPasswordChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
|
||||
|
@ -198,7 +207,7 @@ namespace Xamarin.Forms.Platform.WPF
|
|||
// The ctrlDown flag is used to track if the Ctrl key is pressed; if it's actively being used and the most recent
|
||||
// key to trigger OnKeyDown, then treat it as handled.
|
||||
var ctrlDown = (e.Key == Key.LeftCtrl || e.Key == Key.RightCtrl) && e.IsDown;
|
||||
|
||||
|
||||
// The shift, tab, and directional (Home/End/PgUp/PgDown included) keys can be used to select text and should otherwise
|
||||
// be ignored.
|
||||
if (
|
||||
|
@ -234,28 +243,22 @@ namespace Xamarin.Forms.Platform.WPF
|
|||
|
||||
base.OnKeyDown(e);
|
||||
if (_cachedSelectionLength > 0 && !ctrlDown)
|
||||
{
|
||||
var savedSelectionStart = SelectionStart;
|
||||
Text = Text.Remove(SelectionStart, _cachedSelectionLength);
|
||||
SelectionStart = savedSelectionStart;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
base.OnKeyDown(e);
|
||||
}
|
||||
|
||||
|
||||
void OnTextChanged(object sender, System.Windows.Controls.TextChangedEventArgs textChangedEventArgs)
|
||||
{
|
||||
if (IsPassword)
|
||||
{
|
||||
string updatedRealText = DetermineTextFromPassword(Text, SelectionStart, base.Text);
|
||||
string updatedText = Obfuscate(updatedRealText);
|
||||
var savedSelectionStart = SelectionStart;
|
||||
|
||||
if (Text != updatedRealText)
|
||||
Text = updatedRealText;
|
||||
|
||||
if (base.Text != updatedText)
|
||||
base.Text = updatedText;
|
||||
|
||||
SelectionStart = savedSelectionStart;
|
||||
DelayObfuscation();
|
||||
}
|
||||
else if (base.Text != Text)
|
||||
{
|
||||
|
@ -272,11 +275,11 @@ namespace Xamarin.Forms.Platform.WPF
|
|||
{
|
||||
if (_internalChangeFlag)
|
||||
return;
|
||||
|
||||
var savedSelectionStart = SelectionStart;
|
||||
base.Text = IsPassword ? Obfuscate(Text) : Text;
|
||||
DisabledText = base.Text;
|
||||
|
||||
SelectionStart = base.Text.Length;
|
||||
var len = base.Text.Length;
|
||||
SelectionStart = savedSelectionStart > len ? len : savedSelectionStart;
|
||||
}
|
||||
|
||||
static void TextPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
|
||||
|
|
|
@ -108,13 +108,17 @@ namespace Xamarin.Forms.Platform.WPF
|
|||
((IElementController)Element).SetValueFromRenderer(Entry.TextProperty, Control.Text);
|
||||
|
||||
// If an Entry.TextChanged handler modified the value of the Entry's text, the values could now be
|
||||
// out-of-sync; re-sync them and force the TextBox cursor to the end of the text
|
||||
// out-of-sync; re-sync them and fix TextBox cursor position
|
||||
string entryText = Element.Text;
|
||||
if (Control.Text != entryText)
|
||||
{
|
||||
Control.Text = entryText;
|
||||
if (Control.Text != null)
|
||||
Control.SelectionStart = Control.Text.Length;
|
||||
{
|
||||
var savedSelectionStart = Control.SelectionStart;
|
||||
var len = Control.Text.Length;
|
||||
Control.SelectionStart = savedSelectionStart > len ? len : savedSelectionStart;
|
||||
}
|
||||
}
|
||||
|
||||
_ignoreTextChange = false;
|
||||
|
|
|
@ -38,9 +38,7 @@ namespace Xamarin.Forms
|
|||
static bool? s_isiOS11OrNewer;
|
||||
static bool? s_isiOS13OrNewer;
|
||||
static bool? s_respondsTosetNeedsUpdateOfHomeIndicatorAutoHidden;
|
||||
#endif
|
||||
|
||||
#if __MOBILE__
|
||||
internal static bool IsiOS9OrNewer
|
||||
{
|
||||
get
|
||||
|
@ -91,6 +89,19 @@ namespace Xamarin.Forms
|
|||
return s_respondsTosetNeedsUpdateOfHomeIndicatorAutoHidden.Value;
|
||||
}
|
||||
}
|
||||
#else
|
||||
static bool? s_isMojaveOrNewer;
|
||||
|
||||
internal static bool IsMojaveOrNewer
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!s_isMojaveOrNewer.HasValue)
|
||||
s_isMojaveOrNewer = NSProcessInfo.ProcessInfo.IsOperatingSystemAtLeastVersion(new NSOperatingSystemVersion(10, 14, 0));
|
||||
return s_isMojaveOrNewer.Value;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static IReadOnlyList<string> s_flags;
|
||||
|
|
|
@ -1211,7 +1211,8 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
|
||||
List<UIBarButtonItem> primaries = null;
|
||||
List<UIBarButtonItem> secondaries = null;
|
||||
foreach (var item in _tracker.ToolbarItems)
|
||||
var toolbarItems = _tracker.ToolbarItems;
|
||||
foreach (var item in toolbarItems)
|
||||
{
|
||||
if (item.Order == ToolbarItemOrder.Secondary)
|
||||
(secondaries = secondaries ?? new List<UIBarButtonItem>()).Add(item.ToUIBarButtonItem(true));
|
||||
|
|
|
@ -197,17 +197,19 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
NavigationItem.RightBarButtonItems[i].Dispose();
|
||||
}
|
||||
|
||||
List<UIBarButtonItem> items = new List<UIBarButtonItem>();
|
||||
if (Page != null)
|
||||
List<UIBarButtonItem> primaries = null;
|
||||
if (Page.ToolbarItems.Count > 0)
|
||||
{
|
||||
foreach (var item in Page.ToolbarItems)
|
||||
foreach (var item in System.Linq.Enumerable.OrderBy(Page.ToolbarItems, x => x.Priority))
|
||||
{
|
||||
items.Add(item.ToUIBarButtonItem(false, true));
|
||||
(primaries = primaries ?? new List<UIBarButtonItem>()).Add(item.ToUIBarButtonItem(false, true));
|
||||
}
|
||||
|
||||
if (primaries != null)
|
||||
primaries.Reverse();
|
||||
}
|
||||
|
||||
items.Reverse();
|
||||
NavigationItem.SetRightBarButtonItems(items.ToArray(), false);
|
||||
NavigationItem.SetRightBarButtonItems(primaries == null ? new UIBarButtonItem[0] : primaries.ToArray(), false);
|
||||
|
||||
var behavior = BackButtonBehavior;
|
||||
|
||||
|
|
|
@ -62,7 +62,6 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
Page _displayedPage;
|
||||
bool _disposed;
|
||||
bool _firstLayoutCompleted;
|
||||
bool _ignorePop;
|
||||
TaskCompletionSource<bool> _popCompletionTask;
|
||||
IShellSectionRootRenderer _renderer;
|
||||
ShellSection _shellSection;
|
||||
|
@ -72,18 +71,7 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
Delegate = new NavDelegate(this);
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public override UIViewController PopViewController(bool animated)
|
||||
{
|
||||
if (!_ignorePop)
|
||||
{
|
||||
_popCompletionTask = new TaskCompletionSource<bool>();
|
||||
SendPoppedOnCompletion(_popCompletionTask.Task);
|
||||
}
|
||||
|
||||
return base.PopViewController(animated);
|
||||
}
|
||||
|
||||
|
||||
[Export("navigationBar:shouldPopItem:")]
|
||||
public bool ShouldPopItem(UINavigationBar navigationBar, UINavigationItem item)
|
||||
{
|
||||
|
@ -96,7 +84,12 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
if (allowPop)
|
||||
{
|
||||
// Do not remove, wonky behavior on some versions of iOS if you dont dispatch
|
||||
CoreFoundation.DispatchQueue.MainQueue.DispatchAsync(() => PopViewController(true));
|
||||
CoreFoundation.DispatchQueue.MainQueue.DispatchAsync(() =>
|
||||
{
|
||||
_popCompletionTask = new TaskCompletionSource<bool>();
|
||||
SendPoppedOnCompletion(_popCompletionTask.Task);
|
||||
PopViewController(true);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -267,9 +260,7 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
_popCompletionTask = new TaskCompletionSource<bool>();
|
||||
e.Task = _popCompletionTask.Task;
|
||||
|
||||
_ignorePop = true;
|
||||
PopViewController(animated);
|
||||
_ignorePop = false;
|
||||
|
||||
await _popCompletionTask.Task;
|
||||
|
||||
|
@ -500,6 +491,20 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
navBarVisible = Shell.GetNavBarIsVisible(element);
|
||||
|
||||
navigationController.SetNavigationBarHidden(!navBarVisible, true);
|
||||
|
||||
var coordinator = viewController.GetTransitionCoordinator();
|
||||
if (coordinator != null)
|
||||
{
|
||||
// handle swipe to dismiss gesture
|
||||
coordinator.NotifyWhenInteractionEndsUsingBlock((context) =>
|
||||
{
|
||||
if (!context.IsCancelled)
|
||||
{
|
||||
_self._popCompletionTask = new TaskCompletionSource<bool>();
|
||||
_self.SendPoppedOnCompletion(_self._popCompletionTask.Task);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ContentPage
|
||||
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:local="using:Xamarin.Forms.Xaml.UnitTests"
|
||||
x:Class="Xamarin.Forms.Xaml.UnitTests.Gh8936"
|
||||
x:DataType="local:Gh8936VM">
|
||||
<Entry x:Name="entry0" Text="{Binding Data[Key]}" />
|
||||
</ContentPage>
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Core.UnitTests;
|
||||
|
||||
namespace Xamarin.Forms.Xaml.UnitTests
|
||||
{
|
||||
public class Dict : Dictionary<string,string> {}
|
||||
public class Gh8936VM
|
||||
{
|
||||
public Dict Data { get; set; } = new Dict { { "Key", "Value" } };
|
||||
}
|
||||
|
||||
public partial class Gh8936 : ContentPage
|
||||
{
|
||||
public Gh8936() => InitializeComponent();
|
||||
public Gh8936(bool useCompiledXaml)
|
||||
{
|
||||
//this stub will be replaced at compile time
|
||||
}
|
||||
|
||||
[TestFixture]
|
||||
class Tests
|
||||
{
|
||||
[SetUp] public void Setup() => Device.PlatformServices = new MockPlatformServices();
|
||||
[TearDown] public void TearDown() => Device.PlatformServices = null;
|
||||
|
||||
[Test]
|
||||
public void IndexerBindingOnSubclasses([Values(false, true)]bool useCompiledXaml)
|
||||
{
|
||||
var layout = new Gh8936(useCompiledXaml) { BindingContext = new Gh8936VM() };
|
||||
Assert.That(layout.entry0.Text, Is.EqualTo("Value"));
|
||||
layout.entry0.Text = "Bar";
|
||||
Assert.That(layout.entry0.Text, Is.EqualTo("Bar"));
|
||||
Assert.That(((Gh8936VM)layout.BindingContext).Data["Key"], Is.EqualTo("Bar"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче