This commit is contained in:
Javier Suárez Ruiz 2017-11-27 20:13:42 +01:00
commit 6c9e0fcd08
16 changed files with 451 additions and 92 deletions

View File

@ -5,11 +5,14 @@ using Xamarin.Forms;
using Xamarin.Forms.Internals; using Xamarin.Forms.Internals;
[assembly: Dependency (typeof (ResourcesProvider))] [assembly: Dependency (typeof (ResourcesProvider))]
[assembly: ExportRenderer (typeof (ActivityIndicator), typeof (ActivityIndicatorRenderer))]
[assembly: ExportRenderer (typeof (BoxView), typeof (BoxRenderer))] [assembly: ExportRenderer (typeof (BoxView), typeof (BoxRenderer))]
[assembly: ExportRenderer (typeof (Button), typeof (ButtonRenderer))] [assembly: ExportRenderer (typeof (Button), typeof (ButtonRenderer))]
[assembly: ExportRenderer(typeof(Editor), typeof(EditorRenderer))] [assembly: ExportRenderer (typeof (DatePicker), typeof (DatePickerRenderer))]
[assembly: ExportRenderer (typeof (Editor), typeof (EditorRenderer))]
[assembly: ExportRenderer (typeof (Entry), typeof (EntryRenderer))] [assembly: ExportRenderer (typeof (Entry), typeof (EntryRenderer))]
[assembly: ExportRenderer (typeof (Label), typeof (LabelRenderer))] [assembly: ExportRenderer (typeof (Label), typeof (LabelRenderer))]
[assembly: ExportRenderer (typeof (ProgressBar), typeof (ProgressBarRenderer))]
namespace Ooui.Forms namespace Ooui.Forms
{ {

View File

@ -0,0 +1,67 @@
using System;
using System.ComponentModel;
using System.Linq;
using Xamarin.Forms;
namespace Ooui.Forms.Renderers
{
public class ActivityIndicatorRenderer : ViewRenderer<ActivityIndicator, Div>
{
public override SizeRequest GetDesiredSize (double widthConstraint, double heightConstraint)
{
var size = new Size (40, 20);
return new SizeRequest (size, size);
}
protected override void OnElementChanged (ElementChangedEventArgs<ActivityIndicator> e)
{
if (e.NewElement != null) {
if (Control == null) {
var p = new Div { ClassName = "progress" };
var pb = new Div { ClassName = "progress-bar progress-bar-striped" };
pb.SetAttribute ("role", "progressbar");
pb.SetAttribute ("aria-valuenow", "0");
pb.SetAttribute ("aria-valuemin", "0");
pb.SetAttribute ("aria-valuemax", "100");
pb.Style.Width = "0%";
p.AppendChild (pb);
SetNativeControl (p);
}
UpdateColor ();
UpdateIsRunning ();
}
base.OnElementChanged (e);
}
protected override void OnElementPropertyChanged (object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged (sender, e);
if (e.PropertyName == ActivityIndicator.ColorProperty.PropertyName)
UpdateColor ();
else if (e.PropertyName == ActivityIndicator.IsRunningProperty.PropertyName)
UpdateIsRunning ();
}
void UpdateColor ()
{
}
void UpdateIsRunning ()
{
var pb = (Div)Control.Children[0];
if (Element.IsRunning) {
pb.SetAttribute ("aria-valuenow", "100");
pb.Style.Width = "100%";
pb.ClassName = "progress-bar progress-bar-striped active";
}
else {
pb.SetAttribute ("aria-valuenow", "0");
pb.Style.Width = "0%";
pb.ClassName = "progress-bar progress-bar-striped";
}
}
}
}

View File

@ -0,0 +1,111 @@
using System;
using System.ComponentModel;
using Xamarin.Forms;
using Ooui.Forms.Extensions;
namespace Ooui.Forms.Renderers
{
public class DatePickerRenderer : ViewRenderer<DatePicker, Input>
{
bool _disposed;
IElementController ElementController => Element as IElementController;
public override SizeRequest GetDesiredSize (double widthConstraint, double heightConstraint)
{
var size = "00/00/0000".MeasureSize ("", 16.0, FontAttributes.None);
size = new Size (size.Width, size.Height * 1.428 + 14);
return new SizeRequest (size, size);
}
protected override void OnElementChanged (ElementChangedEventArgs<DatePicker> e)
{
base.OnElementChanged (e);
if (e.NewElement == null)
return;
if (Control == null) {
var entry = new Input {
ClassName = "form-control",
Type = InputType.Date,
};
entry.Inputted += OnStarted;
entry.Changed += OnEnded;
SetNativeControl (entry);
}
UpdateDateFromModel (false);
UpdateMaximumDate ();
UpdateMinimumDate ();
UpdateTextColor ();
UpdateFlowDirection ();
}
protected override void OnElementPropertyChanged (object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged (sender, e);
if (e.PropertyName == DatePicker.DateProperty.PropertyName || e.PropertyName == DatePicker.FormatProperty.PropertyName)
UpdateDateFromModel (true);
else if (e.PropertyName == DatePicker.MinimumDateProperty.PropertyName)
UpdateMinimumDate ();
else if (e.PropertyName == DatePicker.MaximumDateProperty.PropertyName)
UpdateMaximumDate ();
else if (e.PropertyName == DatePicker.TextColorProperty.PropertyName || e.PropertyName == VisualElement.IsEnabledProperty.PropertyName)
UpdateTextColor ();
}
void OnEnded (object sender, EventArgs eventArgs)
{
DateTime.TryParseExact (Control.Value, "yyyy-MM-dd", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.AssumeUniversal, out var date);
ElementController?.SetValueFromRenderer (DatePicker.DateProperty, date);
ElementController?.SetValueFromRenderer (VisualElement.IsFocusedPropertyKey, false);
}
void OnStarted (object sender, EventArgs eventArgs)
{
ElementController?.SetValueFromRenderer (VisualElement.IsFocusedPropertyKey, true);
}
void UpdateDateFromModel (bool animate)
{
Control.Value = Element.Date.ToString ("yyyy-MM-dd");
}
void UpdateFlowDirection ()
{
}
void UpdateMaximumDate ()
{
}
void UpdateMinimumDate ()
{
}
void UpdateTextColor ()
{
}
protected override void Dispose (bool disposing)
{
if (_disposed)
return;
_disposed = true;
if (disposing) {
if (Control != null) {
Control.Inputted -= OnStarted;
Control.Changed -= OnEnded;
}
}
base.Dispose (disposing);
}
}
}

View File

@ -1,124 +1,129 @@
using Ooui.Forms.Extensions; using System;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics;
using Xamarin.Forms; using Xamarin.Forms;
using Ooui.Forms.Extensions;
namespace Ooui.Forms.Renderers namespace Ooui.Forms.Renderers
{ {
public class EditorRenderer : ViewRenderer<Editor, Ooui.TextArea> public class EditorRenderer : ViewRenderer<Editor, TextArea>
{ {
private bool _disposed; bool _disposed;
private Ooui.Color _defaultTextColor; IEditorController ElementController => Element;
static Size initialSize = Size.Zero; protected override void Dispose (bool disposing)
protected IElementController ElementController => Element as IElementController;
public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
{
var size = Element.Text.MeasureSize(Element.FontFamily, Element.FontSize, Element.FontAttributes);
size = new Size(size.Width, size.Height * 1.428 + 14);
return new SizeRequest(size, size);
}
protected override void Dispose(bool disposing)
{ {
if (_disposed) if (_disposed)
return; return;
_disposed = true; _disposed = true;
if (disposing) if (disposing) {
{ if (Control != null) {
if (Control != null) Control.Changed -= HandleChanged;
{ //Control.Started -= OnStarted;
Control.Inputted -= OnEditingChanged; //Control.Ended -= OnEnded;
Control.Changed -= OnEditingEnded;
} }
} }
base.Dispose(disposing); base.Dispose (disposing);
} }
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e) protected override void OnElementChanged (ElementChangedEventArgs<Editor> e)
{ {
base.OnElementChanged (e);
if (e.NewElement == null) if (e.NewElement == null)
return; return;
if (Control == null) if (Control == null) {
{ SetNativeControl (new TextArea {
SetNativeControl(new Ooui.TextArea()); ClassName = "form-control"
});
_defaultTextColor = Colors.Black; Control.Changed += HandleChanged;
//Control.Started += OnStarted;
Debug.Assert(Control != null, "Control != null"); //Control.Ended += OnEnded;
Control.Inputted += OnEditingChanged;
Control.Changed += OnEditingEnded;
} }
UpdateText(); UpdateText ();
UpdateTextColor(); UpdateFont ();
UpdateFont(); UpdateTextColor ();
UpdateKeyboard ();
base.OnElementChanged(e); UpdateEditable ();
UpdateTextAlignment ();
} }
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) protected override void OnElementPropertyChanged (object sender, PropertyChangedEventArgs e)
{ {
base.OnElementPropertyChanged(sender, e); base.OnElementPropertyChanged (sender, e);
if (e.PropertyName == Editor.TextProperty.PropertyName) if (e.PropertyName == Editor.TextProperty.PropertyName)
UpdateText(); UpdateText ();
else if (e.PropertyName == Xamarin.Forms.InputView.KeyboardProperty.PropertyName)
UpdateKeyboard ();
else if (e.PropertyName == VisualElement.IsEnabledProperty.PropertyName)
UpdateEditable ();
else if (e.PropertyName == Editor.TextColorProperty.PropertyName) else if (e.PropertyName == Editor.TextColorProperty.PropertyName)
UpdateTextColor(); UpdateTextColor ();
else if (e.PropertyName == Editor.FontAttributesProperty.PropertyName) else if (e.PropertyName == Editor.FontAttributesProperty.PropertyName)
UpdateFont(); UpdateFont ();
else if (e.PropertyName == Editor.FontFamilyProperty.PropertyName) else if (e.PropertyName == Editor.FontFamilyProperty.PropertyName)
UpdateFont(); UpdateFont ();
else if (e.PropertyName == Editor.FontSizeProperty.PropertyName) else if (e.PropertyName == Editor.FontSizeProperty.PropertyName)
UpdateFont(); UpdateFont ();
} }
void UpdateText() void HandleChanged (object sender, EventArgs e)
{ {
if (Control.Value != Element.Text) ElementController.SetValueFromRenderer (Editor.TextProperty, Control.Text);
Control.Value = Element.Text;
} }
void UpdateTextColor() void OnEnded (object sender, EventArgs eventArgs)
{
if (Control.Text != Element.Text)
ElementController.SetValueFromRenderer (Editor.TextProperty, Control.Text);
Element.SetValue (VisualElement.IsFocusedPropertyKey, false);
ElementController.SendCompleted ();
}
void OnStarted (object sender, EventArgs eventArgs)
{
ElementController.SetValueFromRenderer (VisualElement.IsFocusedPropertyKey, true);
}
void UpdateEditable ()
{
Control.IsDisabled = !Element.IsEnabled;
}
void UpdateFont ()
{
Element.SetStyleFont (Element.FontFamily, Element.FontSize, Element.FontAttributes, Control.Style);
}
void UpdateKeyboard ()
{
}
void UpdateText ()
{
if (Control.Text != Element.Text)
Control.Text = Element.Text;
}
void UpdateTextAlignment ()
{
}
void UpdateTextColor ()
{ {
var textColor = Element.TextColor; var textColor = Element.TextColor;
if (textColor.IsDefault || !Element.IsEnabled) if (textColor.IsDefault)
Control.Style.Color = _defaultTextColor; Control.Style.Color = "black";
else else
Control.Style.Color = textColor.ToOouiColor(); Control.Style.Color = textColor.ToOouiColor ();
}
void UpdateFont()
{
if (initialSize == Size.Zero)
{
var testString = "Tj";
initialSize = testString.MeasureSize(Control.Style);
}
Element.SetStyleFont(Element.FontFamily, Element.FontSize, Element.FontAttributes, Control.Style);
}
private void OnEditingChanged(object sender, TargetEventArgs e)
{
ElementController.SetValueFromRenderer(Editor.TextProperty, Control.Value);
}
private void OnEditingEnded(object sender, TargetEventArgs e)
{
if (Control.Text != Element.Text)
{
ElementController.SetValueFromRenderer(Editor.TextProperty, Control.Text);
}
} }
} }
} }

View File

@ -0,0 +1,51 @@
using System;
using System.ComponentModel;
using System.Linq;
using Xamarin.Forms;
namespace Ooui.Forms.Renderers
{
public class ProgressBarRenderer : ViewRenderer<ProgressBar, Div>
{
public override SizeRequest GetDesiredSize (double widthConstraint, double heightConstraint)
{
var size = new Size (80, 20);
return new SizeRequest (size, size);
}
protected override void OnElementChanged (ElementChangedEventArgs<ProgressBar> e)
{
if (e.NewElement != null) {
if (Control == null) {
var p = new Div { ClassName = "progress" };
var pb = new Div { ClassName = "progress-bar progress-bar" };
pb.SetAttribute ("role", "progressbar");
pb.SetAttribute ("aria-valuenow", "0");
pb.SetAttribute ("aria-valuemin", "0");
pb.SetAttribute ("aria-valuemax", "100");
pb.Style.Width = "0%";
p.AppendChild (pb);
SetNativeControl (p);
}
UpdateProgress ();
}
base.OnElementChanged (e);
}
protected override void OnElementPropertyChanged (object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged (sender, e);
if (e.PropertyName == ProgressBar.ProgressProperty.PropertyName)
UpdateProgress ();
}
void UpdateProgress ()
{
var pb = Control?.Children.FirstOrDefault () as Div;
pb.Style.Width = string.Format (System.Globalization.CultureInfo.InvariantCulture, "{0}%", Element.Progress*100);
}
}
}

View File

@ -100,11 +100,8 @@ namespace Ooui.Forms.Renderers
public override void SetControlSize (Size size) public override void SetControlSize (Size size)
{ {
if (Control != null) { if (Control != null) {
//Control.Style.Position = "absolute"; Control.Style.Width = size.Width;
//Control.Style.Left = "0px"; Control.Style.Height = size.Height;
//Control.Style.Top = "0px";
Control.Style.Width = size.Width + "px";
Control.Style.Height = size.Height + "px";
} }
} }

View File

@ -84,7 +84,7 @@ function ooui (rootElementPath) {
if (!resizeTimeout) { if (!resizeTimeout) {
resizeTimeout = setTimeout(function() { resizeTimeout = setTimeout(function() {
resizeTimeout = null; resizeTimeout = null;
actualResizeHandler(); resizeHandler();
}, 100); }, 100);
} }
} }
@ -145,6 +145,17 @@ function msgSet (m) {
if (debug) console.log ("Set", node, parts, value); if (debug) console.log ("Set", node, parts, value);
} }
function msgSetAttr (m) {
const id = m.id;
const node = getNode (id);
if (!node) {
console.error ("Unknown node id", m);
return;
}
node.setAttribute(m.k, m.v);
if (debug) console.log ("SetAttr", node, m.k, m.v);
}
function msgCall (m) { function msgCall (m) {
const id = m.id; const id = m.id;
const node = getNode (id); const node = getNode (id);
@ -203,6 +214,9 @@ function processMessage (m) {
case "set": case "set":
msgSet (m); msgSet (m);
break; break;
case "setAttr":
msgSetAttr (m);
break;
case "call": case "call":
msgCall (m); msgCall (m);
break; break;

View File

@ -102,6 +102,16 @@ namespace Ooui
Style.PropertyChanged += HandleStylePropertyChanged; Style.PropertyChanged += HandleStylePropertyChanged;
} }
public void SetAttribute (string attributeName, string value)
{
Send (new Message {
MessageType = MessageType.SetAttribute,
TargetId = Id,
Key = attributeName,
Value = value,
});
}
void HandleStylePropertyChanged (object sender, PropertyChangedEventArgs e) void HandleStylePropertyChanged (object sender, PropertyChangedEventArgs e)
{ {
SendSet ("style." + Style.GetJsName (e.PropertyName), Style[e.PropertyName]); SendSet ("style." + Style.GetJsName (e.PropertyName), Style[e.PropertyName]);

View File

@ -163,6 +163,12 @@ namespace Ooui
state.Add (message); state.Add (message);
}); });
break; break;
case MessageType.SetAttribute:
UpdateStateMessages (state => {
state.RemoveAll (x => x.MessageType == MessageType.SetAttribute && x.Key == message.Key);
state.Add (message);
});
break;
case MessageType.Listen: case MessageType.Listen:
AddStateMessage (message); AddStateMessage (message);
break; break;

View File

@ -46,6 +46,8 @@ namespace Ooui
Create, Create,
[EnumMember(Value = "set")] [EnumMember(Value = "set")]
Set, Set,
[EnumMember (Value = "setAttr")]
SetAttribute,
[EnumMember(Value = "call")] [EnumMember(Value = "call")]
Call, Call,
[EnumMember(Value = "listen")] [EnumMember(Value = "listen")]

View File

@ -4,8 +4,11 @@
x:Class="Samples.DisplayAlertPage"> x:Class="Samples.DisplayAlertPage">
<ContentPage.Content> <ContentPage.Content>
<StackLayout> <StackLayout>
<Label Text="Welcome to DisplayAlert Sample!" /> <Label Text="Welcome to DisplayAlert Sample!" FontSize="32" FontAttributes="Bold" Margin="10,10,10,50" />
<Button Text="Tap for Display Alert" <ActivityIndicator x:Name="activity" />
<ProgressBar x:Name="progress" />
<DatePicker x:Name="datePicker" />
<Button Text="Tap to Display Alert"
Clicked="OnButtonClicked" /> Clicked="OnButtonClicked" />
</StackLayout> </StackLayout>
</ContentPage.Content> </ContentPage.Content>

View File

@ -16,8 +16,12 @@ namespace Samples
public async void OnButtonClicked(object sender, EventArgs args) public async void OnButtonClicked(object sender, EventArgs args)
{ {
var result = await DisplayAlert("Alert Message", "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. Praesent mauris. Fusce nec tellus sed augue semper porta. Mauris massa.", "YES", "NO"); activity.IsRunning = true;
progress.Progress = 0.5;
var result = await DisplayAlert($"Alert @ {datePicker.Date}", "This is a test of the dialog. Is it working?", "YES", "NO");
await DisplayAlert("Alert Response", $"You selected value: {result}", "OK"); await DisplayAlert("Alert Response", $"You selected value: {result}", "OK");
activity.IsRunning = false;
progress.Progress = 1.0;
} }
} }
} }

View File

@ -18,11 +18,14 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Condition=" '$(EnableDefaultCompileItems)' == 'true' " Update="ButtonXamlPage.xaml.cs"> <Compile Update="ButtonXamlPage.xaml.cs">
<DependentUpon>*.xaml</DependentUpon> <DependentUpon>ButtonXamlPage.xaml</DependentUpon>
</Compile> </Compile>
<Compile Condition=" '$(EnableDefaultCompileItems)' == 'true' " Update="DisplayAlertPage.xaml.cs"> <Compile Update="DisplayAlertPage.xaml.cs">
<DependentUpon>*.xaml</DependentUpon> <DependentUpon>DisplayAlertPage.xaml</DependentUpon>
</Compile>
<Compile Update="XamlPreviewPage.xaml.cs">
<DependentUpon>XamlPreviewPage.xaml</DependentUpon>
</Compile> </Compile>
</ItemGroup> </ItemGroup>
@ -33,6 +36,9 @@
<EmbeddedResource Update="DisplayAlertPage.xaml"> <EmbeddedResource Update="DisplayAlertPage.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator> <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Update="XamlPreviewPage.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
</ItemGroup> </ItemGroup>
<PropertyGroup> <PropertyGroup>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Samples.XamlPreviewPage">
<ContentPage.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Editor x:Name="editor" FontFamily="monospace" Grid.Row="0" Grid.Column="0" />
<ContentView x:Name="results" Grid.Row="0" Grid.Column="1" BackgroundColor="White" />
</Grid>
</ContentPage.Content>
</ContentPage>

View File

@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using Xamarin.Forms;
namespace Samples
{
public partial class XamlPreviewPage : ContentPage
{
public XamlPreviewPage ()
{
InitializeComponent ();
editor.Text = @"<ContentView
xmlns=""http://xamarin.com/schemas/2014/forms""
xmlns:x=""http://schemas.microsoft.com/winfx/2009/xaml"">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height=""*"" />
<RowDefinition Height=""*"" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=""*"" />
<ColumnDefinition Width=""*"" />
</Grid.ColumnDefinitions>
<Label Text=""Top Left"" Grid.Row=""0"" Grid.Column=""0"" />
<Label Text=""Top Right"" Grid.Row=""0"" Grid.Column=""1"" />
<Label Text=""Bottom Left"" Grid.Row=""1"" Grid.Column=""0"" />
<Label Text=""Bottom Right"" Grid.Row=""1"" Grid.Column=""1"" />
</Grid>
</ContentView>";
DisplayXaml ();
}
public void DisplayXaml ()
{
var asm = typeof (Xamarin.Forms.Xaml.Internals.XamlTypeResolver).Assembly;
var xamlLoaderType = asm.GetType ("Xamarin.Forms.Xaml.XamlLoader");
var loadArgTypes = new[] { typeof (object), typeof (string) };
var loadMethod = xamlLoaderType.GetMethod ("Load", System.Reflection.BindingFlags.Static|System.Reflection.BindingFlags.Public, null, System.Reflection.CallingConventions.Any, loadArgTypes, null);
var contentView = new ContentView ();
loadMethod.Invoke (null, new object[] { contentView, editor.Text });
results.Content = contentView;
}
}
}

View File

@ -0,0 +1,17 @@
using System;
using Xamarin.Forms;
namespace Samples
{
public class XamlPreviewPageSample : ISample
{
public string Title => "Xamarin.Forms XAML Editor";
public Ooui.Element CreateElement ()
{
var page = new XamlPreviewPage ();
return page.GetOouiElement ();
}
}
}