59 changed files with 7153 additions and 2 deletions
@ -0,0 +1,82 @@ |
|||||
|
// Copyright (c) The Perspex Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using Perspex.Android.Platform.Specific; |
||||
|
using Perspex.Platform; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.IO; |
||||
|
using System.Linq; |
||||
|
using System.Reflection; |
||||
|
|
||||
|
namespace Perspex.Android.PlatformSupport |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Loads assets compiled into the application binary.
|
||||
|
/// </summary>
|
||||
|
public class AndroidAssetLoader : IAssetLoader |
||||
|
{ |
||||
|
internal Assembly DefaultAssetAssembly { get; set; } |
||||
|
internal string DefaultResourcePrefix { get; set; } |
||||
|
|
||||
|
public AndroidAssetLoader() |
||||
|
{ |
||||
|
//Assembly.GetEntryAssembly() not working on mono/xamarin for android!!!
|
||||
|
//may be this is better way to have at least the application assembly as default one
|
||||
|
DefaultAssetAssembly = PerspexLocator.Current.GetService<IAndroidActivity>().Activity.GetType().Assembly; |
||||
|
} |
||||
|
|
||||
|
private static readonly Dictionary<string, Assembly> AssemblyNameCache = new Dictionary<string, Assembly>(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Checks if an asset with the specified URI exists.
|
||||
|
/// </summary>
|
||||
|
/// <param name="uri">The URI.</param>
|
||||
|
/// <returns>True if the asset could be found; otherwise false.</returns>
|
||||
|
public bool Exists(Uri uri) |
||||
|
{ |
||||
|
return OpenInternal(uri, false) != null; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Opens the resource with the requested URI.
|
||||
|
/// </summary>
|
||||
|
/// <param name="uri">The URI.</param>
|
||||
|
/// <returns>A stream containing the resource contents.</returns>
|
||||
|
/// <exception cref="FileNotFoundException">
|
||||
|
/// The resource was not found.
|
||||
|
/// </exception>
|
||||
|
public Stream Open(Uri uri) |
||||
|
{ |
||||
|
return OpenInternal(uri, true); |
||||
|
} |
||||
|
|
||||
|
private Stream OpenInternal(Uri uri, bool throwIfNotSuccess) |
||||
|
{ |
||||
|
//uri.AbsolutePath is NotFiniteNumberException escaped adn can cause problems in assembly names with spaces
|
||||
|
bool isAbsolute = uri.IsAbsoluteUri; |
||||
|
var parts = (isAbsolute ? uri.LocalPath : uri.OriginalString).Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); |
||||
|
var asm = parts.Length == 1 ? DefaultAssetAssembly : GetAssembly(parts[0]); |
||||
|
var typeName = parts[parts.Length == 1 ? 0 : 1]; |
||||
|
if (typeName != null && !isAbsolute && !typeName.StartsWith(DefaultResourcePrefix)) |
||||
|
{ |
||||
|
typeName = DefaultResourcePrefix + typeName; |
||||
|
} |
||||
|
|
||||
|
var rv = asm.GetManifestResourceStream(typeName); |
||||
|
if (rv == null && throwIfNotSuccess) |
||||
|
throw new FileNotFoundException($"The resource {uri} could not be found."); |
||||
|
return rv; |
||||
|
} |
||||
|
|
||||
|
private static Assembly GetAssembly(string name) |
||||
|
{ |
||||
|
Assembly rv; |
||||
|
if (!AssemblyNameCache.TryGetValue(name, out rv)) |
||||
|
AssemblyNameCache[name] = rv = |
||||
|
AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.GetName().Name == name) |
||||
|
?? Assembly.Load(name); |
||||
|
return rv; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,221 @@ |
|||||
|
using Android.OS; |
||||
|
using Perspex.Android.CanvasRendering; |
||||
|
using Perspex.Android.Platform; |
||||
|
using Perspex.Android.Platform.CanvasPlatform; |
||||
|
using Perspex.Android.Platform.Input; |
||||
|
using Perspex.Android.Platform.Specific; |
||||
|
using Perspex.Android.Platform.Specific.Helpers; |
||||
|
using Perspex.Android.PlatformSupport; |
||||
|
using Perspex.Animation; |
||||
|
using Perspex.Controls.Platform; |
||||
|
using Perspex.Input; |
||||
|
using Perspex.Input.Platform; |
||||
|
using Perspex.Platform; |
||||
|
using Perspex.Shared.PlatformSupport; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Reactive.Disposables; |
||||
|
using System.Reflection; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Perspex.Android |
||||
|
{ |
||||
|
public class AndroidPlatform : IPlatformThreadingInterface, IPlatformSettings |
||||
|
{ |
||||
|
public static readonly AndroidPlatform Instance = new AndroidPlatform(); |
||||
|
public Size DoubleClickSize => new Size(4, 4); |
||||
|
public TimeSpan DoubleClickTime => TimeSpan.FromMilliseconds(200); |
||||
|
|
||||
|
private HashSet<int> _renderingTask = new HashSet<int>(); |
||||
|
|
||||
|
public void RegisterRenderingTaskId(int taskId) |
||||
|
{ |
||||
|
_renderingTask.Add(taskId); |
||||
|
} |
||||
|
|
||||
|
public void UnRegisterRenderingTaskId(int taskId) |
||||
|
{ |
||||
|
_renderingTask.Add(taskId); |
||||
|
} |
||||
|
|
||||
|
public bool CurrentThreadIsLoopThread |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
bool res = Looper.MainLooper.Thread.Equals(Java.Lang.Thread.CurrentThread()); |
||||
|
if (!res) |
||||
|
{ |
||||
|
if (Task.CurrentId != null) |
||||
|
{ |
||||
|
int id = Task.CurrentId.Value; |
||||
|
res = _renderingTask.Contains(Task.CurrentId.Value); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return res; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void RunLoop(CancellationToken cancellationToken) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
private TimeSpan _animationTick = TimeSpan.FromSeconds(1.0 / Animate.FramesPerSecond); |
||||
|
|
||||
|
public IDisposable StartTimer(TimeSpan interval, Action tick) |
||||
|
{ |
||||
|
//we should be carefull about the smallest amount of time the timer ticks
|
||||
|
//as on android for animations so far there is some issue with 60 fps
|
||||
|
//for now we can get set it to 16 fps with observable timer
|
||||
|
//and still random native exceptions causing app to stop!!!!
|
||||
|
//better not use it
|
||||
|
//return Observable.Timer(interval, interval).Subscribe(_ => tick());
|
||||
|
|
||||
|
//System.Threading.Timer
|
||||
|
//int ms = (int)interval.TotalMilliseconds;
|
||||
|
////regular timer is working perfect
|
||||
|
//return new System.Threading.Timer(_ => tick(), null, ms, ms);
|
||||
|
|
||||
|
//System.Timers.Timer
|
||||
|
if (interval.TotalMilliseconds == 0) |
||||
|
{ |
||||
|
//android ui thread
|
||||
|
PerspexLocator.Current.GetService<IAndroidActivity>().Activity.RunOnUiThread(tick); |
||||
|
return Disposable.Empty; |
||||
|
} |
||||
|
|
||||
|
if (OverrideAnimateFramesPerSecond > 0) |
||||
|
{ |
||||
|
if (_animationTick >= interval) |
||||
|
{ |
||||
|
interval = TimeSpan.FromSeconds(1.0 / OverrideAnimateFramesPerSecond); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//working not very bad with standard timer
|
||||
|
//var timer = new System.Timers.Timer(interval.TotalMilliseconds);
|
||||
|
//System.Timers.ElapsedEventHandler elapsed = (s, e) => tick();
|
||||
|
//timer.Elapsed += elapsed;
|
||||
|
//timer.Start();
|
||||
|
//return Disposable.Create(() =>
|
||||
|
//{
|
||||
|
// timer.Stop();
|
||||
|
// timer.Elapsed -= elapsed;
|
||||
|
// timer.Dispose();
|
||||
|
//});
|
||||
|
|
||||
|
//common ui timer is optimizing performance
|
||||
|
//a lot almost twice, also may be processor usage
|
||||
|
//most probably because it optimizes invalidation calls in animations and using less system resources
|
||||
|
CommonUITimer timer; |
||||
|
if (!_commonTimers.TryGetValue(interval, out timer)) |
||||
|
{ |
||||
|
timer = new CommonUITimer(interval); |
||||
|
_commonTimers.Add(interval, timer); |
||||
|
} |
||||
|
|
||||
|
timer.AddAction(tick); |
||||
|
|
||||
|
return Disposable.Create(() => |
||||
|
{ |
||||
|
timer.RemoveAction(tick); |
||||
|
|
||||
|
if (timer.ActiveActionsCount == 0) |
||||
|
{ |
||||
|
timer.Dispose(); |
||||
|
_commonTimers.Remove(interval); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
private Dictionary<TimeSpan, CommonUITimer> _commonTimers = new Dictionary<TimeSpan, CommonUITimer>(); |
||||
|
|
||||
|
public void Signal() |
||||
|
{ |
||||
|
EnsureInvokeOnMainThread(() => Signaled?.Invoke()); |
||||
|
} |
||||
|
|
||||
|
public event Action Signaled; |
||||
|
|
||||
|
public static void Initialize() |
||||
|
{ |
||||
|
PerspexLocator.CurrentMutable |
||||
|
.Bind<IClipboard>().ToTransient<ClipboardImpl>() |
||||
|
.Bind<IStandardCursorFactory>().ToTransient<CursorFactory>() |
||||
|
.Bind<IKeyboardDevice>().ToSingleton<AndroidKeyboardDevice>() |
||||
|
.Bind<IMouseDevice>().ToSingleton<AndroidMouseDevice>() |
||||
|
.Bind<IPlatformSettings>().ToConstant(Instance) |
||||
|
.Bind<IPlatformThreadingInterface>().ToConstant(Instance) |
||||
|
.Bind<ISystemDialogImpl>().ToTransient<SystemDialogImpl>() |
||||
|
.Bind<IAssetLoader>().ToSingleton<AndroidAssetLoader>() |
||||
|
.Bind<ITopLevelRenderer>().ToTransient<AndroidTopLevelRenderer>() |
||||
|
; |
||||
|
AndroidPlatformRender.Initialize(); |
||||
|
|
||||
|
Instance.RegisterViewDrawType(); |
||||
|
//set defaults for simple resources
|
||||
|
//Assembly.GetEntryAssembly(); //not working on mono for android
|
||||
|
var appType = Application.Current.GetType(); |
||||
|
var entryAssembly = appType.Assembly; |
||||
|
AndroidPlatform.Instance.InitializeAssetsLoader(entryAssembly, appType.Namespace + "."); |
||||
|
// SharedPlatform.Register();
|
||||
|
//we have custom Assetloader so no need to overwrite it
|
||||
|
PerspexLocator.CurrentMutable |
||||
|
.Bind<IPclPlatformWrapper>().ToSingleton<PclPlatformWrapper>(); |
||||
|
} |
||||
|
|
||||
|
public void RegisterViewDrawType() |
||||
|
{ |
||||
|
if (Instance.DefaultViewDrawType == ViewDrawType.SurfaceViewCanvasOnDraw) |
||||
|
{ |
||||
|
PerspexLocator.CurrentMutable.Bind<IWindowImpl>().ToSingleton<Platform.CanvasPlatform.SurfaceMainWindowImpl>(); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
PerspexLocator.CurrentMutable.Bind<IWindowImpl>().ToSingleton<Platform.CanvasPlatform.MainWindowImpl>(); |
||||
|
} |
||||
|
|
||||
|
PerspexLocator.CurrentMutable.Bind<IPopupImpl>().ToTransient<Platform.CanvasPlatform.PopupImpl>(); |
||||
|
} |
||||
|
|
||||
|
public void RegisterViewPointUnits() |
||||
|
{ |
||||
|
PerspexLocator.CurrentMutable.Bind<IPointUnitService>().ToSingleton<PointUnitService>(); |
||||
|
} |
||||
|
|
||||
|
public void InitializeAssetsLoader(Assembly assembly, string defaultResourcePrefix) |
||||
|
{ |
||||
|
var assetLoder = PerspexLocator.Current.GetService<IAssetLoader>() as AndroidAssetLoader; |
||||
|
if (assetLoder != null) |
||||
|
{ |
||||
|
assetLoder.DefaultAssetAssembly = assembly; |
||||
|
assetLoder.DefaultResourcePrefix = defaultResourcePrefix; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void EnsureInvokeOnMainThread(Action action) |
||||
|
{ |
||||
|
var mainHandler = new Handler(global::Android.App.Application.Context.MainLooper); |
||||
|
mainHandler.Post(action); |
||||
|
} |
||||
|
|
||||
|
public bool DrawDebugInfo { get; set; } = false; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Default View DrawType
|
||||
|
/// WindowImpl supports:
|
||||
|
/// ViewDrawType.CanvasOnDraw
|
||||
|
/// ViewDrawType.BitmapOnPreDraw
|
||||
|
/// BitmapBackgroundRender
|
||||
|
/// by default it is ViewDrawType.CanvasOnDraw simples way of drawing view
|
||||
|
/// SurfaceWindowImpl supports:
|
||||
|
/// SurfaceViewCanvasOnDraw
|
||||
|
/// </summary>
|
||||
|
public ViewDrawType DefaultViewDrawType { get; set; } = ViewDrawType.SurfaceViewCanvasOnDraw; |
||||
|
|
||||
|
public PointUnit DefaultPointUnit { get; set; } = PointUnit.DP; |
||||
|
|
||||
|
public int OverrideAnimateFramesPerSecond { get; set; } = -1; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,246 @@ |
|||||
|
using Perspex.Android.Platform.CanvasPlatform; |
||||
|
using Perspex.Android.Platform.Specific; |
||||
|
using Perspex.Media; |
||||
|
using System; |
||||
|
using AAllignment = Android.Text.Layout.Alignment; |
||||
|
using ACap = Android.Graphics.Paint.Cap; |
||||
|
using AColor = Android.Graphics.Color; |
||||
|
using AJoin = Android.Graphics.Paint.Join; |
||||
|
using AMatrix = Android.Graphics.Matrix; |
||||
|
using APoint = Android.Graphics.PointF; |
||||
|
using ARect = Android.Graphics.Rect; |
||||
|
using ARectF = Android.Graphics.RectF; |
||||
|
using AStyle = Android.Graphics.Paint.Style; |
||||
|
using ATextAlign = Android.Graphics.Paint.Align; |
||||
|
using ATileMode = Android.Graphics.Shader.TileMode; |
||||
|
using ATypeface = Android.Graphics.Typeface; |
||||
|
using ATypeStyle = Android.Graphics.TypefaceStyle; |
||||
|
|
||||
|
namespace Perspex.Android.CanvasRendering |
||||
|
{ |
||||
|
public static class AndroidGraphicsExtensions |
||||
|
{ |
||||
|
public static AColor ToAndroidGraphics(this Color c) |
||||
|
{ |
||||
|
return new AColor(c.R, c.G, c.B, c.A); |
||||
|
} |
||||
|
|
||||
|
public static AMatrix ToAndroidGraphics(this Matrix matrix) |
||||
|
{ |
||||
|
var ps = PointUnitService.Instance; |
||||
|
//TODO: !!!
|
||||
|
var transformValues = new float[9]; |
||||
|
transformValues[0] = (float)matrix.M11; |
||||
|
transformValues[1] = (float)matrix.M21; |
||||
|
transformValues[2] = ps.PerspexToNativeXF(matrix.M31); |
||||
|
//transformValues[2] = (float)matrix.M31;
|
||||
|
|
||||
|
transformValues[3] = (float)matrix.M12; |
||||
|
transformValues[4] = (float)matrix.M22; |
||||
|
transformValues[5] = ps.PerspexToNativeYF(matrix.M32); |
||||
|
//transformValues[5] = (float)matrix.M32;
|
||||
|
|
||||
|
transformValues[6] = 0; |
||||
|
transformValues[7] = 0; |
||||
|
transformValues[8] = 1; |
||||
|
var am = new AMatrix(); |
||||
|
am.SetValues(transformValues); |
||||
|
return am; |
||||
|
} |
||||
|
|
||||
|
//public static Matrix ToPerspex(this AMatrix m)
|
||||
|
//{
|
||||
|
// var v = new float[9];
|
||||
|
// m.GetValues(v);
|
||||
|
// return new Matrix(v[0], v[3], v[1], v[4], v[2], v[5]);
|
||||
|
//}
|
||||
|
|
||||
|
public static APoint ToAndroidGraphics(this Point p, bool notransform = false) |
||||
|
{ |
||||
|
if (notransform) new APoint((float)p.X, (float)p.Y); |
||||
|
var ps = PointUnitService.Instance; |
||||
|
return new APoint(ps.PerspexToNativeXF(p.X), ps.PerspexToNativeYF(p.Y)); |
||||
|
} |
||||
|
|
||||
|
//public static Point ToPerspex(this APoint p)
|
||||
|
//{
|
||||
|
// return new Point(p.X, p.Y);
|
||||
|
//}
|
||||
|
|
||||
|
public static ARect ToAndroidGraphics(this Rect r, bool notransform = false) |
||||
|
{ |
||||
|
if (notransform) return new ARect((int)r.X, (int)r.Y, (int)r.Right, (int)r.Bottom); |
||||
|
var ps = PointUnitService.Instance; |
||||
|
return new ARect(ps.PerspexToNativeXInt(r.X), ps.PerspexToNativeYInt(r.Y), ps.PerspexToNativeXInt(r.Right), ps.PerspexToNativeYInt(r.Bottom)); |
||||
|
} |
||||
|
|
||||
|
public static ARectF ToAndroidGraphicsF(this Rect r) |
||||
|
{ |
||||
|
var ps = PointUnitService.Instance; |
||||
|
return new ARectF(ps.PerspexToNativeXF(r.X), ps.PerspexToNativeYF(r.Y), ps.PerspexToNativeXF(r.Right), ps.PerspexToNativeYF(r.Bottom)); |
||||
|
} |
||||
|
|
||||
|
//public static Rect ToPerspex(this ARect r)
|
||||
|
//{
|
||||
|
// return new Rect(r.Left, r.Top, r.Width(), r.Height());
|
||||
|
//}
|
||||
|
|
||||
|
public static Rect ToPerspex(this ARectF r) |
||||
|
{ |
||||
|
var ps = PointUnitService.Instance; |
||||
|
return new Rect(ps.NativeToPerspexX(r.Left), ps.NativeToPerspexY(r.Top), ps.NativeToPerspexX(r.Width()), ps.NativeToPerspexY(r.Height())); |
||||
|
} |
||||
|
|
||||
|
public static AJoin ToAndroidGraphics(this PenLineJoin plj) |
||||
|
{ |
||||
|
switch (plj) |
||||
|
{ |
||||
|
default: |
||||
|
case PenLineJoin.Bevel: |
||||
|
return AJoin.Bevel; |
||||
|
|
||||
|
case PenLineJoin.Miter: |
||||
|
return AJoin.Miter; |
||||
|
|
||||
|
case PenLineJoin.Round: |
||||
|
return AJoin.Round; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static ACap ToAndroidGraphics(this PenLineCap plc) |
||||
|
{ |
||||
|
switch (plc) |
||||
|
{ |
||||
|
default: |
||||
|
case PenLineCap.Flat: |
||||
|
return ACap.Butt; |
||||
|
|
||||
|
case PenLineCap.Round: |
||||
|
return ACap.Round; |
||||
|
|
||||
|
case PenLineCap.Square: |
||||
|
return ACap.Square; |
||||
|
//Triangle not supported
|
||||
|
case PenLineCap.Triangle: |
||||
|
goto default; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static AStyle ToAndroidGraphics(this BrushUsage bu) |
||||
|
{ |
||||
|
switch (bu) |
||||
|
{ |
||||
|
default: |
||||
|
case BrushUsage.Fill: |
||||
|
return AStyle.Fill; |
||||
|
|
||||
|
case BrushUsage.Stroke: |
||||
|
return AStyle.Stroke; |
||||
|
|
||||
|
case BrushUsage.Both: |
||||
|
return AStyle.FillAndStroke; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static ATypeface ToAndroidGraphicsTypeface(this FontStyle s, FontWeight fontWeight) |
||||
|
{ |
||||
|
ATypeface result = ATypeface.Default; |
||||
|
|
||||
|
if (fontWeight >= FontWeight.Bold) |
||||
|
{ |
||||
|
result = ATypeface.DefaultBold; |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
public static ATypeStyle ToAndroidGraphicsTypefaceStyle(this FontStyle s, FontWeight fontWeight) |
||||
|
{ |
||||
|
ATypeStyle result; |
||||
|
switch (s) |
||||
|
{ |
||||
|
default: |
||||
|
case FontStyle.Normal: |
||||
|
result = ATypeStyle.Normal; |
||||
|
break; |
||||
|
|
||||
|
case FontStyle.Italic: |
||||
|
result = ATypeStyle.Italic; |
||||
|
break; |
||||
|
//Oblique not supported, return normal
|
||||
|
case FontStyle.Oblique: |
||||
|
goto default; |
||||
|
} |
||||
|
|
||||
|
if (fontWeight >= FontWeight.Bold) |
||||
|
{ |
||||
|
result = result == ATypeStyle.Italic ? ATypeStyle.BoldItalic : ATypeStyle.Bold; |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
public static ATextAlign ToAndroidGraphics(this TextAlignment ta) |
||||
|
{ |
||||
|
switch (ta) |
||||
|
{ |
||||
|
default: |
||||
|
case TextAlignment.Left: |
||||
|
return ATextAlign.Left; |
||||
|
|
||||
|
case TextAlignment.Center: |
||||
|
return ATextAlign.Center; |
||||
|
|
||||
|
case TextAlignment.Right: |
||||
|
return ATextAlign.Right; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static AAllignment ToAndroidGraphicsLayoutAligment(this TextAlignment ta) |
||||
|
{ |
||||
|
switch (ta) |
||||
|
{ |
||||
|
default: |
||||
|
case TextAlignment.Left: |
||||
|
return AAllignment.AlignNormal; |
||||
|
|
||||
|
case TextAlignment.Center: |
||||
|
return AAllignment.AlignCenter; |
||||
|
|
||||
|
case TextAlignment.Right: |
||||
|
return AAllignment.AlignOpposite; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static ATileMode ToAndroidGraphicsX(this TileMode tileMode) |
||||
|
{ |
||||
|
switch (tileMode) |
||||
|
{ |
||||
|
case TileMode.Tile: return ATileMode.Repeat; |
||||
|
case TileMode.FlipX: return ATileMode.Mirror; |
||||
|
case TileMode.FlipXY: return ATileMode.Mirror; |
||||
|
case TileMode.FlipY: return ATileMode.Clamp; |
||||
|
case TileMode.None: return ATileMode.Clamp; |
||||
|
default: return ATileMode.Clamp; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static ATileMode ToAndroidGraphicsY(this TileMode tileMode) |
||||
|
{ |
||||
|
switch (tileMode) |
||||
|
{ |
||||
|
case TileMode.Tile: return ATileMode.Repeat; |
||||
|
case TileMode.FlipX: return ATileMode.Clamp; |
||||
|
case TileMode.FlipXY: return ATileMode.Mirror; |
||||
|
case TileMode.FlipY: return ATileMode.Mirror; |
||||
|
case TileMode.None: return ATileMode.Clamp; |
||||
|
default: return ATileMode.Clamp; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static int OpacityToAndroidAlfa(double opacity) |
||||
|
{ |
||||
|
return Convert.ToInt32(opacity * 255); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,65 @@ |
|||||
|
using Perspex.Android.Platform.Specific; |
||||
|
using Perspex.Media; |
||||
|
using Perspex.Platform; |
||||
|
using System.IO; |
||||
|
using AG = Android.Graphics; |
||||
|
using System; |
||||
|
using Perspex.Android.Platform.CanvasPlatform; |
||||
|
|
||||
|
namespace Perspex.Android.CanvasRendering |
||||
|
{ |
||||
|
public class AndroidPlatformRender : IPlatformRenderInterface |
||||
|
{ |
||||
|
private static readonly AndroidPlatformRender instance = new AndroidPlatformRender(); |
||||
|
|
||||
|
public IFormattedTextImpl CreateFormattedText(string text, string fontFamilyName, double fontSize, |
||||
|
FontStyle fontStyle, |
||||
|
TextAlignment textAlignment, FontWeight fontWeight) |
||||
|
{ |
||||
|
return new FormattedTextImpl(text, fontFamilyName, fontSize, fontStyle, textAlignment, fontWeight); |
||||
|
} |
||||
|
|
||||
|
public IStreamGeometryImpl CreateStreamGeometry() |
||||
|
{ |
||||
|
return new StreamGeometryImpl(); |
||||
|
} |
||||
|
|
||||
|
public IRenderTarget CreateRenderer(IPlatformHandle handle) |
||||
|
{ |
||||
|
return new RenderTarget((IAndroidCanvasView)handle); |
||||
|
} |
||||
|
|
||||
|
public IRenderTargetBitmapImpl CreateRenderTargetBitmap(int pwidth, int pheight) |
||||
|
{ |
||||
|
int width = PointUnitService.Instance.PerspexToNativeXInt(pwidth); |
||||
|
int height = PointUnitService.Instance.PerspexToNativeYInt(pwidth); |
||||
|
|
||||
|
return new RenderTargetBitmapImpl(CreateNativeBitmap(width, height)); |
||||
|
} |
||||
|
|
||||
|
public IBitmapImpl CreateBitmap(int pwidth, int pheight) |
||||
|
{ |
||||
|
int width = PointUnitService.Instance.PerspexToNativeXInt(pwidth); |
||||
|
int height = PointUnitService.Instance.PerspexToNativeYInt(pwidth); |
||||
|
return new BitmapImpl(CreateNativeBitmap(width, height)); |
||||
|
} |
||||
|
|
||||
|
public IBitmapImpl LoadBitmap(string fileName) |
||||
|
{ |
||||
|
return new BitmapImpl(AG.BitmapFactory.DecodeFile(fileName)); |
||||
|
} |
||||
|
|
||||
|
public IBitmapImpl LoadBitmap(Stream stream) |
||||
|
{ |
||||
|
return new BitmapImpl(AG.BitmapFactory.DecodeStream(stream)); |
||||
|
} |
||||
|
|
||||
|
public static void Initialize() |
||||
|
=> PerspexLocator.CurrentMutable.Bind<IPlatformRenderInterface>().ToConstant(instance); |
||||
|
|
||||
|
private AG.Bitmap CreateNativeBitmap(int width, int height) |
||||
|
{ |
||||
|
return AG.Bitmap.CreateBitmap(width, height, AG.Bitmap.Config.Argb8888); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,59 @@ |
|||||
|
using Perspex.Controls; |
||||
|
using Perspex.Controls.Platform; |
||||
|
using Perspex.Platform; |
||||
|
using Perspex.Rendering; |
||||
|
using Perspex.Threading; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace Perspex.Android.CanvasRendering |
||||
|
{ |
||||
|
internal class AndroidTopLevelRenderer : ITopLevelRenderer |
||||
|
{ |
||||
|
public void Attach(TopLevel topLevel) |
||||
|
{ |
||||
|
var resources = new List<IDisposable>(); |
||||
|
var initialClientSize = topLevel.PlatformImpl.ClientSize; |
||||
|
|
||||
|
|
||||
|
var queueManager = ((IRenderRoot)topLevel).RenderQueueManager; |
||||
|
|
||||
|
if (queueManager == null) |
||||
|
return; |
||||
|
|
||||
|
|
||||
|
var viewport = PlatformManager.CreateRenderTarget(topLevel.PlatformImpl); |
||||
|
resources.Add(viewport); |
||||
|
//resources.Add(queueManager.RenderNeeded.Subscribe(_
|
||||
|
// =>
|
||||
|
// Dispatcher.UIThread.InvokeAsync(() => topLevel.PlatformImpl.Invalidate(new Rect(topLevel.ClientSize)))));
|
||||
|
Action pendingInvalidation = null; |
||||
|
resources.Add(queueManager.RenderNeeded.Subscribe(_ => |
||||
|
{ |
||||
|
if (pendingInvalidation == null) |
||||
|
{ |
||||
|
pendingInvalidation = () => |
||||
|
{ |
||||
|
topLevel.PlatformImpl.Invalidate(new Rect(topLevel.ClientSize)); |
||||
|
pendingInvalidation = null; |
||||
|
}; |
||||
|
Dispatcher.UIThread.InvokeAsync(pendingInvalidation); |
||||
|
} |
||||
|
} |
||||
|
)); |
||||
|
|
||||
|
topLevel.PlatformImpl.Paint = rect => |
||||
|
{ |
||||
|
viewport.Render(topLevel); |
||||
|
queueManager.RenderFinished(); |
||||
|
}; |
||||
|
|
||||
|
topLevel.Closed += delegate |
||||
|
{ |
||||
|
foreach (var disposable in resources) |
||||
|
disposable.Dispose(); |
||||
|
resources.Clear(); |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
// Copyright (c) The Perspex Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using Perspex.Platform; |
||||
|
using System.IO; |
||||
|
using AG = Android.Graphics; |
||||
|
|
||||
|
namespace Perspex.Android.CanvasRendering |
||||
|
{ |
||||
|
public class BitmapImpl : IBitmapImpl |
||||
|
{ |
||||
|
public BitmapImpl(AG.Bitmap bitmap) |
||||
|
{ |
||||
|
PlatformBitmap = bitmap; |
||||
|
} |
||||
|
|
||||
|
public int PixelWidth => PlatformBitmap.Width; |
||||
|
|
||||
|
public int PixelHeight => PlatformBitmap.Height; |
||||
|
|
||||
|
public AG.Bitmap PlatformBitmap |
||||
|
{ |
||||
|
get; |
||||
|
} |
||||
|
|
||||
|
public void Save(string fileName) |
||||
|
{ |
||||
|
using (var f = File.OpenWrite(fileName)) |
||||
|
{ |
||||
|
PlatformBitmap.Compress(AG.Bitmap.CompressFormat.Png, 100, f); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,363 @@ |
|||||
|
using Android.Graphics; |
||||
|
using Android.Util; |
||||
|
using Perspex.Android.Platform.CanvasPlatform; |
||||
|
using Perspex.Android.Platform.Specific; |
||||
|
using Perspex.Media; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using AColor = Android.Graphics.Color; |
||||
|
|
||||
|
namespace Perspex.Android.CanvasRendering |
||||
|
{ |
||||
|
public enum BrushUsage |
||||
|
{ |
||||
|
Fill, |
||||
|
Stroke, |
||||
|
Both |
||||
|
}; |
||||
|
|
||||
|
public interface INativePaintWrapper : IDisposable |
||||
|
{ |
||||
|
Paint NativePaint { get; } |
||||
|
|
||||
|
void PushOpacity(double value); |
||||
|
|
||||
|
void PopOpacity(); |
||||
|
} |
||||
|
|
||||
|
public abstract class BrushImpl : IDisposable, INativePaintWrapper |
||||
|
{ |
||||
|
public BrushImpl(Brush brush) |
||||
|
{ |
||||
|
Brush = brush; |
||||
|
Brush.PropertyChanged += Brush_PropertyChanged; |
||||
|
Init(); |
||||
|
} |
||||
|
|
||||
|
protected virtual void Init() |
||||
|
{ |
||||
|
Opacity = Brush.Opacity; |
||||
|
} |
||||
|
|
||||
|
private void Brush_PropertyChanged(object sender, PerspexPropertyChangedEventArgs e) |
||||
|
{ |
||||
|
_isDirty = true; |
||||
|
} |
||||
|
|
||||
|
protected bool _isDirty = false; |
||||
|
|
||||
|
public Brush Brush { get; private set; } |
||||
|
|
||||
|
private double _oldOpacity = 1; |
||||
|
|
||||
|
public double Opacity { get; private set; } |
||||
|
|
||||
|
public virtual void Dispose() |
||||
|
{ |
||||
|
Brush.PropertyChanged -= Brush_PropertyChanged; |
||||
|
Brush = null; |
||||
|
NativePaint?.Dispose(); |
||||
|
} |
||||
|
|
||||
|
private Paint _nativeBrush; |
||||
|
|
||||
|
public Paint NativePaint |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
if (_nativeBrush == null) |
||||
|
{ |
||||
|
_nativeBrush = new Paint(PaintFlags.AntiAlias); |
||||
|
_isDirty = true; |
||||
|
} |
||||
|
|
||||
|
if (_isDirty) |
||||
|
{ |
||||
|
Apply(); |
||||
|
} |
||||
|
|
||||
|
return _nativeBrush; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public Paint.Style Style { get; set; } |
||||
|
|
||||
|
public virtual void ApplyTo(Paint paint) |
||||
|
{ |
||||
|
paint.Alpha = AndroidGraphicsExtensions.OpacityToAndroidAlfa(Opacity); |
||||
|
paint.SetStyle(Style); |
||||
|
} |
||||
|
|
||||
|
private void Apply() |
||||
|
{ |
||||
|
_isDirty = false; |
||||
|
ApplyTo(NativePaint); |
||||
|
} |
||||
|
|
||||
|
private static readonly BrushImpl FallbackBrush = new SolidColorBrushImpl(new SolidColorBrush(Colors.Transparent)); |
||||
|
|
||||
|
public static BrushImpl Create(Brush brush, Size dstRect, Paint.Style style) |
||||
|
{ |
||||
|
//Size dstRect = PointService.Instance.PerspexToNative(pdstRect);
|
||||
|
BrushImpl impl = null; |
||||
|
|
||||
|
if (brush is SolidColorBrush) |
||||
|
{ |
||||
|
impl = new SolidColorBrushImpl(brush as SolidColorBrush); |
||||
|
} |
||||
|
else if (brush is LinearGradientBrush) |
||||
|
{ |
||||
|
impl = new LinearGradientBrushImpl(brush as LinearGradientBrush, dstRect); |
||||
|
} |
||||
|
else if (brush is RadialGradientBrush) |
||||
|
{ |
||||
|
impl = new RadialGradientBrushImpl(brush as RadialGradientBrush, dstRect); |
||||
|
} |
||||
|
else if (brush is ImageBrush) |
||||
|
{ |
||||
|
impl = new ImageBrushImpl(brush as ImageBrush); |
||||
|
} |
||||
|
else if (brush is VisualBrush) |
||||
|
{ |
||||
|
// TODO: Implement me
|
||||
|
Log.Debug("REND", "VisualBrush not implemented"); |
||||
|
impl = FallbackBrush; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
impl = new SolidColorBrushImpl(null); |
||||
|
} |
||||
|
|
||||
|
impl.Style = style; |
||||
|
|
||||
|
return impl; |
||||
|
} |
||||
|
|
||||
|
private Stack<double> _opacityStack = new Stack<double>(); |
||||
|
|
||||
|
public void PushOpacity(double value) |
||||
|
{ |
||||
|
_opacityStack.Push(this.Opacity); |
||||
|
Opacity = Opacity * value; |
||||
|
_isDirty = value != 1 ? true : false; |
||||
|
} |
||||
|
|
||||
|
public void PopOpacity() |
||||
|
{ |
||||
|
double newOpacity = _opacityStack.Pop(); |
||||
|
if (newOpacity != Opacity) |
||||
|
{ |
||||
|
Opacity = newOpacity; |
||||
|
_isDirty = true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class PenImpl : IDisposable, INativePaintWrapper |
||||
|
{ |
||||
|
public PenImpl(Pen pen, Size dstRect, Paint.Style style) |
||||
|
{ |
||||
|
this.Pen = pen; |
||||
|
if (this.Pen.Brush != null) |
||||
|
{ |
||||
|
this.Brush = BrushImpl.Create(this.Pen.Brush, dstRect, style); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private BrushImpl Brush { get; set; } |
||||
|
|
||||
|
public Pen Pen { get; } |
||||
|
|
||||
|
private Paint _nativePaint; |
||||
|
|
||||
|
public Paint NativePaint |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
if (_nativePaint == null) |
||||
|
{ |
||||
|
_nativePaint = new Paint(PaintFlags.AntiAlias); |
||||
|
Apply(); |
||||
|
} |
||||
|
|
||||
|
return _nativePaint; |
||||
|
} |
||||
|
set |
||||
|
{ |
||||
|
this._nativePaint = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void PushOpacity(double opacity) |
||||
|
{ |
||||
|
Brush?.PushOpacity(opacity); |
||||
|
} |
||||
|
|
||||
|
public void PopOpacity() |
||||
|
{ |
||||
|
Brush?.PopOpacity(); |
||||
|
} |
||||
|
|
||||
|
private void Apply() |
||||
|
{ |
||||
|
if (Pen.DashStyle?.Dashes != null && Pen.DashStyle.Dashes.Count > 0) |
||||
|
{ |
||||
|
var cray = Pen.DashStyle.Dashes.Select(d => (float)d).ToArray(); |
||||
|
NativePaint.SetPathEffect(new DashPathEffect(cray, (float)Pen.DashStyle.Offset)); |
||||
|
} |
||||
|
|
||||
|
NativePaint.StrokeWidth = PointUnitService.Instance.PerspexToNativeXF(Pen.Thickness); |
||||
|
NativePaint.StrokeMiter = (float)Pen.MiterLimit; |
||||
|
|
||||
|
NativePaint.StrokeJoin = Pen.LineJoin.ToAndroidGraphics(); |
||||
|
NativePaint.StrokeCap = Pen.StartLineCap.ToAndroidGraphics(); |
||||
|
|
||||
|
if (Brush != null) |
||||
|
{ |
||||
|
Brush.ApplyTo(NativePaint); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
Brush?.Dispose(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class SolidColorBrushImpl : BrushImpl |
||||
|
{ |
||||
|
private AColor _nativeColor; |
||||
|
|
||||
|
public SolidColorBrushImpl(SolidColorBrush brush) : base(brush) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public override void ApplyTo(Paint paint) |
||||
|
{ |
||||
|
paint.Color = _nativeColor; |
||||
|
base.ApplyTo(paint); |
||||
|
} |
||||
|
|
||||
|
protected override void Init() |
||||
|
{ |
||||
|
base.Init(); |
||||
|
var brush = Brush as SolidColorBrush; |
||||
|
|
||||
|
_nativeColor = brush.Color.ToAndroidGraphics(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class ShaderBrushImpl : BrushImpl |
||||
|
{ |
||||
|
protected Shader Shader { get; set; } |
||||
|
|
||||
|
protected Size DestinationSize { get; } |
||||
|
|
||||
|
public ShaderBrushImpl(Brush brush, Size destinationSize) : base(brush) |
||||
|
{ |
||||
|
DestinationSize = destinationSize; |
||||
|
} |
||||
|
|
||||
|
public override void ApplyTo(Paint paint) |
||||
|
{ |
||||
|
base.ApplyTo(paint); |
||||
|
|
||||
|
if (Shader != null) |
||||
|
{ |
||||
|
paint.SetShader(Shader); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
//we had some problem better set something
|
||||
|
paint.Color = new AColor(0, 0, 0, 255); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public override void Dispose() |
||||
|
{ |
||||
|
Shader?.Dispose(); |
||||
|
base.Dispose(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class LinearGradientBrushImpl : ShaderBrushImpl |
||||
|
{ |
||||
|
public LinearGradientBrushImpl(LinearGradientBrush brush, Size destinationSize) : base(brush, destinationSize) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
protected override void Init() |
||||
|
{ |
||||
|
base.Init(); |
||||
|
var brush = Brush as LinearGradientBrush; |
||||
|
int n = brush.GradientStops.Count; |
||||
|
var offsets = new float[n]; |
||||
|
var colors = new int[n]; |
||||
|
for (var i = 0; i < n; i++) |
||||
|
{ |
||||
|
var s = brush.GradientStops[i]; |
||||
|
offsets[i] = (float)s.Offset; |
||||
|
colors[i] = (int)s.Color.ToUint32(); |
||||
|
} |
||||
|
|
||||
|
var startPoint = brush.StartPoint.ToPixels(DestinationSize); |
||||
|
var endPoint = brush.EndPoint.ToPixels(DestinationSize); |
||||
|
|
||||
|
this.Shader = new LinearGradient( |
||||
|
(float)startPoint.X, (float)startPoint.Y, |
||||
|
(float)endPoint.X, (float)endPoint.Y, |
||||
|
colors, |
||||
|
offsets, |
||||
|
Shader.TileMode.Clamp); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class RadialGradientBrushImpl : ShaderBrushImpl |
||||
|
{ |
||||
|
public RadialGradientBrushImpl(RadialGradientBrush brush, Size destinationSize) : base(brush, destinationSize) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
protected override void Init() |
||||
|
{ |
||||
|
base.Init(); |
||||
|
var brush = Brush as RadialGradientBrush; |
||||
|
int n = brush.GradientStops.Count; |
||||
|
var offsets = new float[n]; |
||||
|
var colors = new int[n]; |
||||
|
|
||||
|
for (var i = 0; i < n; i++) |
||||
|
{ |
||||
|
var s = brush.GradientStops[i]; |
||||
|
offsets[i] = (float)s.Offset; |
||||
|
colors[i] = (int)s.Color.ToUint32(); |
||||
|
} |
||||
|
|
||||
|
var centerPoint = brush.Center.ToPixels(DestinationSize); |
||||
|
double radius = brush.Center.Unit == RelativeUnit.Absolute ? brush.Radius : Math.Max(brush.Radius * DestinationSize.Width, brush.Radius * DestinationSize.Height); |
||||
|
|
||||
|
Shader = new RadialGradient( |
||||
|
(float)centerPoint.X, (float)centerPoint.Y, |
||||
|
(float)brush.Radius, |
||||
|
colors, |
||||
|
offsets, |
||||
|
Shader.TileMode.Clamp); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class ImageBrushImpl : ShaderBrushImpl |
||||
|
{ |
||||
|
public ImageBrushImpl(ImageBrush brush) : base(brush, default(Size)) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
protected override void Init() |
||||
|
{ |
||||
|
base.Init(); |
||||
|
var brush = Brush as ImageBrush; |
||||
|
var bmp = brush.Source.PlatformImpl as BitmapImpl; |
||||
|
Shader = new BitmapShader(bmp.PlatformBitmap, brush.TileMode.ToAndroidGraphicsX(), brush.TileMode.ToAndroidGraphicsY()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,248 @@ |
|||||
|
using Android.Graphics; |
||||
|
using Perspex.Android.Platform.CanvasPlatform; |
||||
|
using Perspex.Android.Platform.Specific; |
||||
|
using Perspex.Media; |
||||
|
using Perspex.Media.Imaging; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Reactive.Disposables; |
||||
|
|
||||
|
namespace Perspex.Android.CanvasRendering |
||||
|
{ |
||||
|
public class DrawingContextImpl : IDrawingContextImpl |
||||
|
{ |
||||
|
private double _currentOpacity = 1.0f; |
||||
|
|
||||
|
private double CurrentOpacity |
||||
|
{ |
||||
|
get { return _currentOpacity; } |
||||
|
set |
||||
|
{ |
||||
|
_currentOpacity = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public Canvas _canvas; |
||||
|
private Dictionary<object, IDisposable> _visualCaches; |
||||
|
private Dictionary<object, IDisposable> _cache; |
||||
|
|
||||
|
public DrawingContextImpl(Canvas canvas, Dictionary<object, IDisposable> visualCaches) |
||||
|
{ |
||||
|
_canvas = canvas; |
||||
|
_visualCaches = visualCaches; |
||||
|
_cache = visualCaches ?? new Dictionary<object, IDisposable>(); |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
_canvas = null; |
||||
|
if (_visualCaches == null) |
||||
|
{ |
||||
|
DisposeCaches(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void DisposeCaches() |
||||
|
{ |
||||
|
foreach (var d in _cache) |
||||
|
{ |
||||
|
d.Value.Dispose(); |
||||
|
} |
||||
|
_cache.Clear(); |
||||
|
} |
||||
|
|
||||
|
private Matrix _currentTransform = Matrix.Identity; |
||||
|
|
||||
|
public Matrix Transform |
||||
|
{ |
||||
|
get { return _currentTransform; } |
||||
|
set |
||||
|
{ |
||||
|
if (_currentTransform == value) |
||||
|
return; |
||||
|
_currentTransform = value; |
||||
|
_canvas.Matrix = value.ToAndroidGraphics(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void DrawImage(IBitmap source, double opacity, Rect sourceRect, Rect destRect) |
||||
|
{ |
||||
|
var nativeBitmap = (source.PlatformImpl as BitmapImpl).PlatformBitmap; |
||||
|
_canvas.DrawBitmap(nativeBitmap, sourceRect.ToAndroidGraphics(true), destRect.ToAndroidGraphicsF(), new Paint() { Alpha = AndroidGraphicsExtensions.OpacityToAndroidAlfa(opacity * CurrentOpacity) }); |
||||
|
} |
||||
|
|
||||
|
public void DrawLine(Pen pen, Point pp1, Point pp2) |
||||
|
{ |
||||
|
var p1 = PointUnitService.Instance.PerspexToNative(pp1); |
||||
|
var p2 = PointUnitService.Instance.PerspexToNative(pp2); |
||||
|
|
||||
|
var Rect = new Rect(p1.X, p1.Y, p2.X, p2.Y); |
||||
|
PenImpl np; |
||||
|
using ((GetPen(pen, new Size(Rect.Width, Rect.Height), out np))) |
||||
|
{ |
||||
|
_canvas.DrawLine((float)p1.X, (float)p1.Y, (float)p2.X, (float)p2.Y, np.NativePaint); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void DrawGeometry(Brush brush, Pen pen, Geometry geometry) |
||||
|
{ |
||||
|
var impl = geometry.PlatformImpl as StreamGeometryImpl; |
||||
|
|
||||
|
if (brush != null) |
||||
|
{ |
||||
|
BrushImpl nb; |
||||
|
using (var b = GetBrush(brush, geometry.Bounds.Size, out nb)) |
||||
|
{ |
||||
|
_canvas.DrawPath(impl.Path, nb.NativePaint); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//if the pen brush is null and thickness null on android
|
||||
|
//the shape is filled with black !! better not draw the stroke!!!
|
||||
|
if (pen?.Brush != null && pen?.Thickness > 0) |
||||
|
{ |
||||
|
PenImpl np; |
||||
|
using (var p = GetPen(pen, geometry.Bounds.Size, out np)) |
||||
|
{ |
||||
|
_canvas.DrawPath(impl.Path, np.NativePaint); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void DrawRectangle(Pen pen, Rect rect, float cornerRadius = 0) |
||||
|
{ |
||||
|
PenImpl np; |
||||
|
using (GetPen(pen, rect.Size, out np)) |
||||
|
{ |
||||
|
if (cornerRadius == 0) |
||||
|
{ |
||||
|
_canvas.DrawRect(rect.ToAndroidGraphicsF(), np.NativePaint); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
_canvas.DrawRoundRect(rect.ToAndroidGraphicsF(), cornerRadius, cornerRadius, np.NativePaint); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void DrawText(Brush foreground, Point origin, FormattedText text) |
||||
|
{ |
||||
|
if (!string.IsNullOrEmpty(text.Text)) |
||||
|
{ |
||||
|
var ap = origin.ToAndroidGraphics(); |
||||
|
var impl = (FormattedTextImpl)text.PlatformImpl; |
||||
|
_canvas.Save(); |
||||
|
BrushImpl nb; |
||||
|
using (var b = GetBrush(foreground, impl.Measure(), out nb)) |
||||
|
{ |
||||
|
nb.ApplyTo(impl.TextPaint); |
||||
|
_canvas.Translate((float)ap.X, (float)ap.Y); |
||||
|
impl.TextLayout.Draw(_canvas); |
||||
|
} |
||||
|
_canvas.Restore(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void FillRectangle(Brush brush, Rect rect, float cornerRadius = 0) |
||||
|
{ |
||||
|
BrushImpl np; |
||||
|
using (var b = GetBrush(brush, rect.Size, out np)) |
||||
|
{ |
||||
|
var ar = rect.ToAndroidGraphicsF(); |
||||
|
if (cornerRadius == 0) |
||||
|
{ |
||||
|
_canvas.DrawRect(ar, np.NativePaint); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
var cr = PointUnitService.Instance.PerspexToNativeXF(cornerRadius); |
||||
|
_canvas.DrawRoundRect(ar, cr, cr, np.NativePaint); |
||||
|
//Canvas.DrawPath(RoundRectPath(rect, cornerRadius), nb.NativePaint);
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private Rect _clip; |
||||
|
|
||||
|
public void PushClip(Rect clip) |
||||
|
{ |
||||
|
_canvas.Save(); |
||||
|
_canvas.ClipRect(clip.ToAndroidGraphics()); |
||||
|
_clip = clip; |
||||
|
} |
||||
|
|
||||
|
public void PopClip() => _canvas.Restore(); |
||||
|
|
||||
|
private Stack<double> _opacityStack = new Stack<double>(); |
||||
|
|
||||
|
public void PushOpacity(double opacity) |
||||
|
{ |
||||
|
_opacityStack.Push(CurrentOpacity); |
||||
|
CurrentOpacity = CurrentOpacity * opacity; |
||||
|
} |
||||
|
|
||||
|
public void PopOpacity() => CurrentOpacity = _opacityStack.Pop(); |
||||
|
|
||||
|
//private Path RoundRectPath(Rect rc, float radius)
|
||||
|
//{
|
||||
|
// var x = (float)rc.TopLeft.X;
|
||||
|
// var y = (float)rc.TopLeft.Y;
|
||||
|
// var width = (float)rc.Width;
|
||||
|
// var height = (float)rc.Height;
|
||||
|
// var rx = radius;
|
||||
|
// var ry = radius;
|
||||
|
// var path = new Path();
|
||||
|
// path.MoveTo(x + rx, y);
|
||||
|
// path.LineTo(x + width - rx, y + 0);
|
||||
|
// path.QuadTo(x + width, y, x + width, y + ry);
|
||||
|
// path.LineTo(x + width, y + height - ry);
|
||||
|
// path.QuadTo(x + width, y + height, x + width - rx, y + height);
|
||||
|
// path.LineTo(x + rx, y + height);
|
||||
|
// path.QuadTo(x, y + height, x, y + height - ry);
|
||||
|
// path.LineTo(x, y + ry);
|
||||
|
// path.QuadTo(x, y, x + rx, y);
|
||||
|
// return path;
|
||||
|
//}
|
||||
|
|
||||
|
private IDisposable GetPen(Pen pen, Size dstRect, out PenImpl result) |
||||
|
{ |
||||
|
var penImpl = GetOrCreatePaintWrapper<PenImpl>(pen, () => new PenImpl(pen, dstRect, Paint.Style.Stroke)); |
||||
|
|
||||
|
penImpl.PushOpacity(CurrentOpacity); |
||||
|
|
||||
|
result = penImpl; |
||||
|
|
||||
|
return Disposable.Create(() => penImpl.PopOpacity()); |
||||
|
} |
||||
|
|
||||
|
private IDisposable GetBrush(Brush brush, Size dstRect, out BrushImpl result) |
||||
|
{ |
||||
|
var brushImpl = GetOrCreatePaintWrapper<BrushImpl>(brush, () => BrushImpl.Create(brush, dstRect, Paint.Style.Fill)); |
||||
|
|
||||
|
brushImpl.PushOpacity(CurrentOpacity); |
||||
|
|
||||
|
result = brushImpl; |
||||
|
|
||||
|
return Disposable.Create(() => brushImpl.PopOpacity()); |
||||
|
} |
||||
|
|
||||
|
private T GetOrCreatePaintWrapper<T>(object key, Func<T> createFunc) where T : INativePaintWrapper |
||||
|
{ |
||||
|
IDisposable cachedVisual; |
||||
|
T result; |
||||
|
|
||||
|
if (!_cache.TryGetValue(key, out cachedVisual)) |
||||
|
{ |
||||
|
result = createFunc(); |
||||
|
|
||||
|
_cache[key] = result; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
result = (T)cachedVisual; |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,205 @@ |
|||||
|
using Android.Graphics; |
||||
|
using Android.Text; |
||||
|
using Perspex.Android.Platform.CanvasPlatform; |
||||
|
using Perspex.Android.Platform.Specific; |
||||
|
using Perspex.Media; |
||||
|
using Perspex.Platform; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using AAllignment = Android.Text.Layout.Alignment; |
||||
|
using ARect = Android.Graphics.Rect; |
||||
|
using ATextPaint = Android.Text.TextPaint; |
||||
|
|
||||
|
namespace Perspex.Android.CanvasRendering |
||||
|
{ |
||||
|
public class FormattedTextImpl : IFormattedTextImpl |
||||
|
{ |
||||
|
public FormattedTextImpl( |
||||
|
string text, |
||||
|
string fontFamily, |
||||
|
double fontSize, |
||||
|
FontStyle fontStyle, |
||||
|
TextAlignment textAlignment, |
||||
|
FontWeight fontWeight) |
||||
|
{ |
||||
|
String = text; |
||||
|
TextPaint = new ATextPaint { TextAlign = textAlignment.ToAndroidGraphics() }; |
||||
|
var typefaceStyle = fontStyle.ToAndroidGraphicsTypefaceStyle(fontWeight); |
||||
|
var typeface = fontStyle.ToAndroidGraphicsTypeface(fontWeight); |
||||
|
TextPaint.SetTypeface(typeface); |
||||
|
TextPaint.TextSize = PointUnitService.Instance.PerspexToNativeFontSize(fontSize); |
||||
|
|
||||
|
Aligment = textAlignment.ToAndroidGraphicsLayoutAligment(); |
||||
|
|
||||
|
var rect = new ARect(); |
||||
|
TextPaint.GetTextBounds(String, 0, String.Length, rect); |
||||
|
//or use measure text to give us other opinion for the text ???
|
||||
|
//double tw = TextFormatting.MeasureText(String, 0, String.Length);
|
||||
|
|
||||
|
//give few pixels in advance just in case
|
||||
|
_initialSize = PointUnitService.Instance.NativeToPerspex(new Size(rect.Right, rect.Bottom)); |
||||
|
Constraint = _initialSize; |
||||
|
} |
||||
|
|
||||
|
private AAllignment Aligment; |
||||
|
|
||||
|
public string String { get; } |
||||
|
|
||||
|
public ATextPaint TextPaint { get; } |
||||
|
|
||||
|
private Size _initialSize; |
||||
|
|
||||
|
public Size Constraint { get; set; } |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
private StaticLayout _layout = null; |
||||
|
private double _layoutBuildWidth = 0; |
||||
|
|
||||
|
private Dictionary<string, KeyValuePair<object, object>> _localCachedValues = new Dictionary<string, KeyValuePair<object, object>>(); |
||||
|
|
||||
|
private T GetLocalCachedValue<T>(string name, object forKey, Func<T> getFunc) |
||||
|
{ |
||||
|
KeyValuePair<object, object> kvp = default(KeyValuePair<object, object>); |
||||
|
|
||||
|
bool create = true; |
||||
|
if (_localCachedValues.ContainsKey(name)) |
||||
|
{ |
||||
|
kvp = _localCachedValues[name]; |
||||
|
create = kvp.Key != forKey; |
||||
|
} |
||||
|
|
||||
|
if (create) |
||||
|
{ |
||||
|
kvp = new KeyValuePair<object, object>(forKey, getFunc()); |
||||
|
_localCachedValues[name] = kvp; |
||||
|
} |
||||
|
|
||||
|
return (T)kvp.Value; |
||||
|
} |
||||
|
|
||||
|
public StaticLayout TextLayout |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
if (_layout == null || Math.Abs(_layoutBuildWidth - Constraint.Width) > 1) |
||||
|
{ |
||||
|
var newWidth = (Constraint.Width > int.MaxValue ? _initialSize.Width : Constraint.Width) + 10; |
||||
|
|
||||
|
double widthDiff = _layoutBuildWidth - newWidth; |
||||
|
|
||||
|
if (_layout == null || widthDiff < 0) |
||||
|
{ |
||||
|
_layoutBuildWidth = newWidth; |
||||
|
_layout = new StaticLayout(String, TextPaint, PointUnitService.Instance.PerspexToNativeXInt(_layoutBuildWidth), Aligment, 1.0f, 0.0f, false); |
||||
|
_localCachedValues.Clear(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return _layout; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public IEnumerable<FormattedTextLine> GetLines() |
||||
|
{ |
||||
|
return GetLocalCachedValue("GetLines", "GetLines", () => |
||||
|
{ |
||||
|
List<FormattedTextLine> result = new List<FormattedTextLine>(); |
||||
|
for (int i = 0; i < TextLayout.LineCount; i++) |
||||
|
{ |
||||
|
int start = TextLayout.GetLineStart(i); |
||||
|
int end = TextLayout.GetLineEnd(i); |
||||
|
//string lineContent = String.Substring(start, end - start);
|
||||
|
result.Add(new FormattedTextLine(end - start, PointUnitService.Instance.NativeToPerspexY(TextLayout.GetLineTop(i) - TextLayout.GetLineBottom(i)))); |
||||
|
} |
||||
|
return result; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public TextHitTestResult HitTestPoint(Point ppoint) |
||||
|
{ |
||||
|
return GetLocalCachedValue("HitTestPoint", ppoint, () => |
||||
|
{ |
||||
|
var point = PointUnitService.Instance.PerspexToNative(ppoint); |
||||
|
|
||||
|
bool inside = point.X >= 0 && point.X <= TextLayout.Width && point.Y >= 0 && point.Y <= TextLayout.Height; |
||||
|
int index = -1; |
||||
|
|
||||
|
if (inside) |
||||
|
{ |
||||
|
int line = this.TextLayout.GetLineForVertical(Convert.ToInt32(point.Y)); |
||||
|
index = this.TextLayout.GetOffsetForHorizontal(line, (float)point.X); |
||||
|
} |
||||
|
|
||||
|
return new TextHitTestResult { IsInside = inside, IsTrailing = false, TextPosition = index }; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public Rect HitTestTextPosition(int index) |
||||
|
{ |
||||
|
return |
||||
|
GetLocalCachedValue("HitTestTextPosition", index, () => |
||||
|
{ |
||||
|
int line = TextLayout.GetLineForOffset(index); |
||||
|
int top = TextLayout.GetLineTop(line); |
||||
|
int bottom = TextLayout.GetLineBottom(line); |
||||
|
double left = TextLayout.GetPrimaryHorizontal(index); |
||||
|
|
||||
|
double right = String.Length > index + 1 ? TextLayout.GetPrimaryHorizontal(index + 1) : left; |
||||
|
|
||||
|
return PointUnitService.Instance.NativeToPerspex(new Rect(left, top, right - left, bottom - top)); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public IEnumerable<Rect> HitTestTextRange(int index, int length) |
||||
|
{ |
||||
|
int startLine = TextLayout.GetLineForOffset(index); |
||||
|
int endIndex = index + length; |
||||
|
int endLine = TextLayout.GetLineForOffset(endIndex); |
||||
|
|
||||
|
for (int i = startLine; i <= endLine; i++) |
||||
|
{ |
||||
|
int top = TextLayout.GetLineTop(i); |
||||
|
int bottom = TextLayout.GetLineBottom(i); |
||||
|
|
||||
|
double left = 0, right = 0; |
||||
|
|
||||
|
if (startLine == i) |
||||
|
{ |
||||
|
left = TextLayout.GetPrimaryHorizontal(index); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
left = TextLayout.GetLineLeft(i); |
||||
|
} |
||||
|
|
||||
|
if (endLine == i) |
||||
|
{ |
||||
|
right = TextLayout.GetPrimaryHorizontal(endIndex); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
right = TextLayout.GetLineRight(i); |
||||
|
} |
||||
|
|
||||
|
yield return PointUnitService.Instance.NativeToPerspex(new Rect(left, top, right - left, bottom - top)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public Size Measure() |
||||
|
{ |
||||
|
return PointUnitService.Instance.NativeToPerspex(new Size(this.TextLayout.Width, this.TextLayout.Height)); |
||||
|
} |
||||
|
|
||||
|
public void SetForegroundBrush(Brush brush, int startIndex, int length) |
||||
|
{ |
||||
|
var scb = brush as SolidColorBrush; |
||||
|
if (scb != null) |
||||
|
{ |
||||
|
TextPaint.Color = scb.Color.ToAndroidGraphics(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,63 @@ |
|||||
|
// Copyright (c) The Perspex Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using Perspex.Android.Platform.CanvasPlatform; |
||||
|
using Perspex.Android.Platform.Specific; |
||||
|
using Perspex.Platform; |
||||
|
using AG = Android.Graphics; |
||||
|
|
||||
|
namespace Perspex.Android.CanvasRendering |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// A android render target.
|
||||
|
/// </summary>
|
||||
|
public class RenderTarget : IRenderTarget |
||||
|
{ |
||||
|
private readonly AG.Bitmap _nativeBitmap; |
||||
|
private IAndroidCanvasView _window; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="RenderTarget"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="window">The window.</param>
|
||||
|
/// <param name="width">The width of the window.</param>
|
||||
|
/// <param name="height">The height of the window.</param>
|
||||
|
public RenderTarget(IAndroidCanvasView window) |
||||
|
{ |
||||
|
_window = window; |
||||
|
} |
||||
|
|
||||
|
public RenderTarget(AG.Bitmap surface) |
||||
|
{ |
||||
|
_nativeBitmap = surface; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Resizes the renderer.
|
||||
|
/// </summary>
|
||||
|
/// <param name="width">The new width.</param>
|
||||
|
/// <param name="height">The new height.</param>
|
||||
|
public void Resize(int width, int height) |
||||
|
{ |
||||
|
if (_nativeBitmap != null) |
||||
|
{ |
||||
|
this._nativeBitmap.Reconfigure(width, height, AG.Bitmap.Config.Argb8888); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Creates a DrawingContext that targets a platform-specific resource.
|
||||
|
/// </summary>
|
||||
|
/// <returns>A surface wrapped in an <see cref="Perspex.Media.DrawingContext"/>.</returns>
|
||||
|
public Perspex.Media.DrawingContext CreateDrawingContext() |
||||
|
{ |
||||
|
var canvas = _nativeBitmap != null ? new AG.Canvas(this._nativeBitmap) : _window.Canvas; |
||||
|
return new Perspex.Media.DrawingContext(new DrawingContextImpl(canvas, _window != null ? _window.VisualCaches : null)); |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
this._nativeBitmap?.Dispose(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,49 @@ |
|||||
|
// Copyright (c) The Perspex Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using Perspex.Platform; |
||||
|
using System.IO; |
||||
|
using AG = Android.Graphics; |
||||
|
|
||||
|
namespace Perspex.Android.CanvasRendering |
||||
|
{ |
||||
|
public class RenderTargetBitmapImpl : IRenderTargetBitmapImpl |
||||
|
{ |
||||
|
private readonly RenderTarget _renderTarget; |
||||
|
|
||||
|
public RenderTargetBitmapImpl(AG.Bitmap surface) |
||||
|
{ |
||||
|
Surface = surface; |
||||
|
_renderTarget = new RenderTarget(Surface); |
||||
|
} |
||||
|
|
||||
|
public int PixelWidth => Surface.Width; |
||||
|
|
||||
|
public int PixelHeight => Surface.Height; |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
_renderTarget.Dispose(); |
||||
|
} |
||||
|
|
||||
|
public AG.Bitmap Surface { get; } |
||||
|
|
||||
|
public void Save(string fileName) |
||||
|
{ |
||||
|
using (var f = File.OpenWrite(fileName)) |
||||
|
{ |
||||
|
Surface.Compress(AG.Bitmap.CompressFormat.Png, 100, f); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public Perspex.Media.DrawingContext CreateDrawingContext() |
||||
|
{ |
||||
|
return _renderTarget.CreateDrawingContext(); |
||||
|
} |
||||
|
|
||||
|
//void IRenderTarget.Resize(int width, int height)
|
||||
|
//{
|
||||
|
// this._renderTarget.Resize(width, height);
|
||||
|
//}
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,158 @@ |
|||||
|
using Android.Graphics; |
||||
|
using Perspex.Media; |
||||
|
using Perspex.Platform; |
||||
|
using System; |
||||
|
using System.Linq; |
||||
|
using APath = Android.Graphics.Path; |
||||
|
using ARect = Android.Graphics.RectF; |
||||
|
|
||||
|
namespace Perspex.Android.CanvasRendering |
||||
|
{ |
||||
|
public class StreamGeometryImpl : IStreamGeometryImpl |
||||
|
{ |
||||
|
private readonly StreamGeometryContextImpl _impl; |
||||
|
private Matrix _transform = Matrix.Identity; |
||||
|
|
||||
|
public StreamGeometryImpl() |
||||
|
{ |
||||
|
_impl = new StreamGeometryContextImpl(null); |
||||
|
} |
||||
|
|
||||
|
public StreamGeometryImpl(StreamGeometryContextImpl impl) |
||||
|
{ |
||||
|
_impl = impl; |
||||
|
} |
||||
|
|
||||
|
public APath Path => _impl.Path; |
||||
|
|
||||
|
public Rect Bounds => _impl.CalculatedBounds; |
||||
|
|
||||
|
public Matrix Transform |
||||
|
{ |
||||
|
get { return _transform; } |
||||
|
set |
||||
|
{ |
||||
|
if (value == Transform) return; |
||||
|
if (!value.IsIdentity) |
||||
|
{ |
||||
|
_transform = value; |
||||
|
|
||||
|
Path.Transform(_transform.ToAndroidGraphics()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public Rect GetRenderBounds(double strokeThickness) |
||||
|
{ |
||||
|
return Bounds.Inflate(strokeThickness); |
||||
|
} |
||||
|
|
||||
|
public IStreamGeometryImpl Clone() |
||||
|
{ |
||||
|
return new StreamGeometryImpl(_impl); |
||||
|
} |
||||
|
|
||||
|
public IStreamGeometryContextImpl Open() |
||||
|
{ |
||||
|
return _impl; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class StreamGeometryContextImpl : IStreamGeometryContextImpl |
||||
|
{ |
||||
|
private double _top = double.MaxValue; |
||||
|
private double _left = double.MaxValue; |
||||
|
private double _bottom = 1; |
||||
|
private double _right = 1; |
||||
|
|
||||
|
public StreamGeometryContextImpl(APath path = null) |
||||
|
{ |
||||
|
Path = path != null ? new APath(path) : new APath(); |
||||
|
Path.AddRect(new ARect(), APath.Direction.Cw); |
||||
|
} |
||||
|
|
||||
|
public APath Path { get; } |
||||
|
|
||||
|
public Rect PathBounds |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
var _bounds = new ARect(); |
||||
|
|
||||
|
Path.ComputeBounds(_bounds, true); |
||||
|
return _bounds.ToPerspex(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public Rect CalculatedBounds |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return new Rect(_left, _top, _right - _left, _bottom - _top); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
private void ProcessBounds(params Point[] points) |
||||
|
{ |
||||
|
_top = Math.Min(_top, points.Min(p => p.Y)); |
||||
|
_left = Math.Min(_left, points.Min(p => p.X)); |
||||
|
_bottom = Math.Max(_bottom, points.Max(p => p.Y)); |
||||
|
_right = Math.Max(_right, points.Max(p => p.X)); |
||||
|
} |
||||
|
|
||||
|
public void ArcTo(Point point, Size size, double rotationAngle, bool isLargeArc, SweepDirection sweepDirection) |
||||
|
{ |
||||
|
ProcessBounds(point); |
||||
|
|
||||
|
Path.ArcTo(new Rect(point, size).ToAndroidGraphicsF(), 0, (float)rotationAngle); |
||||
|
} |
||||
|
|
||||
|
public void BeginFigure(Point pstartPoint, bool isFilled) |
||||
|
{ |
||||
|
ProcessBounds(pstartPoint); |
||||
|
var sp = pstartPoint.ToAndroidGraphics(); |
||||
|
|
||||
|
Path.MoveTo(sp.X, sp.Y); |
||||
|
} |
||||
|
|
||||
|
public void BezierTo(Point ppoint1, Point ppoint2, Point ppoint3) |
||||
|
{ |
||||
|
ProcessBounds(ppoint1, ppoint2, ppoint3); |
||||
|
var point1 = ppoint1.ToAndroidGraphics(); |
||||
|
var point2 = ppoint2.ToAndroidGraphics(); |
||||
|
var point3 = ppoint3.ToAndroidGraphics(); |
||||
|
|
||||
|
Path.CubicTo(point1.X, point1.Y, |
||||
|
point2.X, point2.Y, |
||||
|
point3.X, point3.Y); |
||||
|
} |
||||
|
|
||||
|
public void LineTo(Point ppoint) |
||||
|
{ |
||||
|
ProcessBounds(ppoint); |
||||
|
var point = ppoint.ToAndroidGraphics(); |
||||
|
|
||||
|
Path.LineTo(point.X, point.Y); |
||||
|
} |
||||
|
|
||||
|
public void EndFigure(bool isClosed) |
||||
|
{ |
||||
|
if (Path != null) |
||||
|
if (isClosed) |
||||
|
Path.Close(); |
||||
|
} |
||||
|
|
||||
|
public void QuadTo(Point ppoint1, Point ppoint2) |
||||
|
{ |
||||
|
ProcessBounds(ppoint1, ppoint2); |
||||
|
var point1 = ppoint1.ToAndroidGraphics(); |
||||
|
var point2 = ppoint2.ToAndroidGraphics(); |
||||
|
|
||||
|
Path.QuadTo(point1.X, point1.Y, point2.X, point2.Y); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
using System; |
||||
|
using Perspex.Input; |
||||
|
using Perspex.Platform; |
||||
|
|
||||
|
namespace Perspex.Android |
||||
|
{ |
||||
|
internal class CursorFactory : IStandardCursorFactory |
||||
|
{ |
||||
|
public IPlatformHandle GetCursor(StandardCursorType cursorType) |
||||
|
=> new PlatformHandle(IntPtr.Zero, "ZeroCursor"); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,171 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<PropertyGroup> |
||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> |
||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> |
||||
|
<ProductVersion>8.0.30703</ProductVersion> |
||||
|
<SchemaVersion>2.0</SchemaVersion> |
||||
|
<ProjectGuid>{7B92AF71-6287-4693-9DCB-BD5B6E927E23}</ProjectGuid> |
||||
|
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> |
||||
|
<OutputType>Library</OutputType> |
||||
|
<AppDesignerFolder>Properties</AppDesignerFolder> |
||||
|
<RootNamespace>Perspex.Android</RootNamespace> |
||||
|
<AssemblyName>Perspex.Android</AssemblyName> |
||||
|
<FileAlignment>512</FileAlignment> |
||||
|
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile> |
||||
|
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies> |
||||
|
<AndroidUseLatestPlatformSdk>False</AndroidUseLatestPlatformSdk> |
||||
|
<TargetFrameworkVersion>v4.4</TargetFrameworkVersion> |
||||
|
</PropertyGroup> |
||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> |
||||
|
<DebugSymbols>true</DebugSymbols> |
||||
|
<DebugType>full</DebugType> |
||||
|
<Optimize>false</Optimize> |
||||
|
<OutputPath>bin\Debug\</OutputPath> |
||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants> |
||||
|
<ErrorReport>prompt</ErrorReport> |
||||
|
<WarningLevel>4</WarningLevel> |
||||
|
</PropertyGroup> |
||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> |
||||
|
<DebugType>pdbonly</DebugType> |
||||
|
<Optimize>true</Optimize> |
||||
|
<OutputPath>bin\Release\</OutputPath> |
||||
|
<DefineConstants>TRACE</DefineConstants> |
||||
|
<ErrorReport>prompt</ErrorReport> |
||||
|
<WarningLevel>4</WarningLevel> |
||||
|
</PropertyGroup> |
||||
|
<ItemGroup> |
||||
|
<Reference Include="Mono.Android" /> |
||||
|
<Reference Include="mscorlib" /> |
||||
|
<Reference Include="System" /> |
||||
|
<Reference Include="System.Collections" /> |
||||
|
<Reference Include="System.Core" /> |
||||
|
<Reference Include="System.IO" /> |
||||
|
<Reference Include="System.Reflection" /> |
||||
|
<Reference Include="System.ObjectModel" /> |
||||
|
<Reference Include="System.Threading.Tasks" /> |
||||
|
<Reference Include="System.Reactive.Core, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> |
||||
|
<HintPath>..\..\..\packages\Rx-Core.2.2.5\lib\portable-windows8+net45+wp8\System.Reactive.Core.dll</HintPath> |
||||
|
<Private>True</Private> |
||||
|
</Reference> |
||||
|
<Reference Include="System.Reactive.Interfaces, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> |
||||
|
<HintPath>..\..\..\packages\Rx-Interfaces.2.2.5\lib\portable-windows8+net45+wp8\System.Reactive.Interfaces.dll</HintPath> |
||||
|
<Private>True</Private> |
||||
|
</Reference> |
||||
|
<Reference Include="System.Reactive.Linq, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> |
||||
|
<HintPath>..\..\..\packages\Rx-Linq.2.2.5\lib\portable-windows8+net45+wp8\System.Reactive.Linq.dll</HintPath> |
||||
|
<Private>True</Private> |
||||
|
</Reference> |
||||
|
<Reference Include="System.Reactive.PlatformServices, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> |
||||
|
<HintPath>..\..\..\packages\Rx-PlatformServices.2.2.5\lib\portable-windows8+net45+wp8\System.Reactive.PlatformServices.dll</HintPath> |
||||
|
<Private>True</Private> |
||||
|
</Reference> |
||||
|
<Reference Include="System.Runtime" /> |
||||
|
<Reference Include="System.Xml.Linq" /> |
||||
|
<Reference Include="System.Xml" /> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<Compile Include="AndroidPlatform.cs" /> |
||||
|
<Compile Include="AndroidAssetLoader.cs" /> |
||||
|
<Compile Include="Platform\CanvasPlatform\IAndroidCanvasView.cs" /> |
||||
|
<Compile Include="Platform\CanvasPlatform\IAndroidViewSupportRender.cs" /> |
||||
|
<Compile Include="Platform\ClipboardImpl.cs" /> |
||||
|
<Compile Include="CursorFactory.cs" /> |
||||
|
<Compile Include="Platform\Input\AndroidKeyboardDevice.cs" /> |
||||
|
<Compile Include="Platform\Input\AndroidMouseDevice.cs" /> |
||||
|
<Compile Include="Platform\Specific\CommonUITimer.cs" /> |
||||
|
<Compile Include="Platform\CanvasPlatform\SurfaceMainWindowImpl.cs" /> |
||||
|
<Compile Include="Platform\CanvasPlatform\Specific\Helpers\AndroidViewRenderHelpers.cs" /> |
||||
|
<Compile Include="Platform\Specific\Helpers\ViewDrawType.cs" /> |
||||
|
<Compile Include="Platform\Specific\IAndroidView.cs" /> |
||||
|
<Compile Include="Platform\Specific\IAndroidActivity.cs" /> |
||||
|
<Compile Include="Platform\CanvasPlatform\PointUnitService.cs" /> |
||||
|
<Compile Include="Platform\Specific\PerspexActivity.cs" /> |
||||
|
<Compile Include="CanvasRendering\AndroidPlatformRender.cs" /> |
||||
|
<Compile Include="Platform\Specific\Helpers\AndroidTouchEventsHelper.cs" /> |
||||
|
<Compile Include="Platform\Specific\Helpers\AndroidKeyboardEventsHelper.cs" /> |
||||
|
<Compile Include="CanvasRendering\BitmapImpl.cs" /> |
||||
|
<Compile Include="CanvasRendering\BrushImpl.cs" /> |
||||
|
<Compile Include="CanvasRendering\DrawingContext.cs" /> |
||||
|
<Compile Include="CanvasRendering\FormattedTextImpl.cs" /> |
||||
|
<Compile Include="CanvasRendering\AndroidTopLevelRenderer.cs" /> |
||||
|
<Compile Include="Platform\CanvasPlatform\MainWindowImpl.cs" /> |
||||
|
<Compile Include="Platform\CanvasPlatform\SurfaceWindowImpl.cs" /> |
||||
|
<Compile Include="Platform\CanvasPlatform\WindowImpl.cs" /> |
||||
|
<Compile Include="CanvasRendering\AndroidGraphicsExtensions.cs" /> |
||||
|
<Compile Include="Platform\CanvasPlatform\PopupImpl.cs" /> |
||||
|
<Compile Include="CanvasRendering\RenderTarget.cs" /> |
||||
|
<Compile Include="CanvasRendering\RenderTargetBitmapImpl.cs" /> |
||||
|
<Compile Include="CanvasRendering\StreamGeometryImpl.cs" /> |
||||
|
<Compile Include="Resources\Resource.Designer.cs" /> |
||||
|
<Compile Include="Properties\AssemblyInfo.cs" /> |
||||
|
<Compile Include="SystemDialogImpl.cs" /> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<None Include="packages.config" /> |
||||
|
<None Include="Resources\AboutResources.txt" /> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<AndroidResource Include="Resources\Values\Strings.xml" /> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\..\Markup\Perspex.Markup.Xaml\Perspex.Markup.Xaml.csproj"> |
||||
|
<Project>{3e53a01a-b331-47f3-b828-4a5717e77a24}</Project> |
||||
|
<Name>Perspex.Markup.Xaml</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\..\Perspex.Animation\Perspex.Animation.csproj"> |
||||
|
<Project>{d211e587-d8bc-45b9-95a4-f297c8fa5200}</Project> |
||||
|
<Name>Perspex.Animation</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\..\Perspex.Application\Perspex.Application.csproj"> |
||||
|
<Project>{799a7bb5-3c2c-48b6-85a7-406a12c420da}</Project> |
||||
|
<Name>Perspex.Application</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\..\Perspex.Base\Perspex.Base.csproj"> |
||||
|
<Project>{b09b78d8-9b26-48b0-9149-d64a2f120f3f}</Project> |
||||
|
<Name>Perspex.Base</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\..\Perspex.Controls\Perspex.Controls.csproj"> |
||||
|
<Project>{d2221c82-4a25-4583-9b43-d791e3f6820c}</Project> |
||||
|
<Name>Perspex.Controls</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\..\Perspex.Diagnostics\Perspex.Diagnostics.csproj"> |
||||
|
<Project>{7062ae20-5dcc-4442-9645-8195bdece63e}</Project> |
||||
|
<Name>Perspex.Diagnostics</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\..\Perspex.Input\Perspex.Input.csproj"> |
||||
|
<Project>{62024b2d-53eb-4638-b26b-85eeaa54866e}</Project> |
||||
|
<Name>Perspex.Input</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\..\Perspex.Interactivity\Perspex.Interactivity.csproj"> |
||||
|
<Project>{6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b}</Project> |
||||
|
<Name>Perspex.Interactivity</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\..\Perspex.Layout\Perspex.Layout.csproj"> |
||||
|
<Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project> |
||||
|
<Name>Perspex.Layout</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\..\Perspex.ReactiveUI\Perspex.ReactiveUI.csproj"> |
||||
|
<Project>{6417b24e-49c2-4985-8db2-3ab9d898ec91}</Project> |
||||
|
<Name>Perspex.ReactiveUI</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\..\Perspex.SceneGraph\Perspex.SceneGraph.csproj"> |
||||
|
<Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project> |
||||
|
<Name>Perspex.SceneGraph</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\..\Perspex.Styling\Perspex.Styling.csproj"> |
||||
|
<Project>{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}</Project> |
||||
|
<Name>Perspex.Styling</Name> |
||||
|
</ProjectReference> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup /> |
||||
|
<Import Project="..\..\Shared\PlatformSupport\PlatformSupport.projitems" Label="Shared" /> |
||||
|
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" /> |
||||
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. |
||||
|
Other similar extension points exist, see Microsoft.Common.targets. |
||||
|
<Target Name="BeforeBuild"> |
||||
|
</Target> |
||||
|
<Target Name="AfterBuild"> |
||||
|
</Target> |
||||
|
--> |
||||
|
</Project> |
||||
@ -0,0 +1,14 @@ |
|||||
|
using Android.Graphics; |
||||
|
using Perspex.Android.Platform.Specific; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace Perspex.Android.Platform.CanvasPlatform |
||||
|
{ |
||||
|
public interface IAndroidCanvasView : IAndroidView |
||||
|
{ |
||||
|
Canvas Canvas { get; } |
||||
|
|
||||
|
Dictionary<object, IDisposable> VisualCaches { get; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
using Android.Graphics; |
||||
|
|
||||
|
namespace Perspex.Android.Platform.CanvasPlatform |
||||
|
{ |
||||
|
public interface IAndroidViewSupportRender |
||||
|
{ |
||||
|
void PreRender(Canvas canvas, Rect rect); |
||||
|
|
||||
|
void Render(Canvas canvas, Rect rect); |
||||
|
|
||||
|
void PostRender(Canvas canvas, Rect rect); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,71 @@ |
|||||
|
using Android.Views; |
||||
|
using Perspex.Android.Platform.Specific; |
||||
|
using Perspex.Android.Platform.Specific.Helpers; |
||||
|
using Perspex.Input; |
||||
|
using Perspex.Platform; |
||||
|
using AG = Android.Graphics; |
||||
|
|
||||
|
namespace Perspex.Android.Platform.CanvasPlatform |
||||
|
{ |
||||
|
public class MainWindowImpl : |
||||
|
//SurfaceWindowImpl
|
||||
|
WindowImpl |
||||
|
, IWindowImpl |
||||
|
{ |
||||
|
private IPointUnitService _pointService = PointUnitService.Instance; |
||||
|
|
||||
|
protected override void Init() |
||||
|
{ |
||||
|
base.Init(); |
||||
|
|
||||
|
HandleEvents = true; |
||||
|
_keyboardHelper.ActivateAutoShowKeybord(); |
||||
|
} |
||||
|
|
||||
|
private int _statusbarHeight = -1; |
||||
|
|
||||
|
private int StatusBarHeight |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
if (_statusbarHeight < 0) |
||||
|
{ |
||||
|
var a = PerspexLocator.Current.GetService<IAndroidActivity>().Activity; |
||||
|
AG.Rect rectangle = new AG.Rect(); |
||||
|
Window window = a.Window; |
||||
|
window.DecorView.GetWindowVisibleDisplayFrame(rectangle); |
||||
|
int statusBarHeight = rectangle.Top; |
||||
|
//int contentViewTop =
|
||||
|
// window.FindViewById(Window.IdAndroidContent).Top;
|
||||
|
//int titleBarHeight = contentViewTop - statusBarHeight;
|
||||
|
_statusbarHeight = statusBarHeight; |
||||
|
} |
||||
|
return _statusbarHeight; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public override Point GetPerspexPointFromEvent(MotionEvent e) |
||||
|
{ |
||||
|
//toolbar is on top of android screen and it's height is 50 ???
|
||||
|
//may be dependent from dpi ???
|
||||
|
//return base.GetPerspexPointFromEvent(e);
|
||||
|
//return new Point(e.GetX(0), e.GetY(0) + StatusBarHeight);
|
||||
|
|
||||
|
var point = new Point(e.GetX(0), e.GetY(0) + (DrawType == ViewDrawType.CanvasOnDraw ? StatusBarHeight : 0)); |
||||
|
return _pointService.NativeToPerspex(point); |
||||
|
} |
||||
|
|
||||
|
void ITopLevelImpl.Show() |
||||
|
{ |
||||
|
(Parent as ViewGroup)?.RemoveAllViews(); |
||||
|
PerspexLocator.Current.GetService<IAndroidActivity>().ContentView = this; |
||||
|
this.Visibility = ViewStates.Visible; |
||||
|
} |
||||
|
|
||||
|
void ITopLevelImpl.SetInputRoot(IInputRoot inputRoot) |
||||
|
{ |
||||
|
base.SetInputRoot(inputRoot); |
||||
|
_keyboardHelper.UpdateKeyboardState(inputRoot); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,184 @@ |
|||||
|
using Perspex.Android.Platform.Specific; |
||||
|
using System; |
||||
|
|
||||
|
namespace Perspex.Android.Platform.CanvasPlatform |
||||
|
{ |
||||
|
public enum PointUnit |
||||
|
{ |
||||
|
Pixel, |
||||
|
DP, //device independent pixel
|
||||
|
Custom // CustomScale
|
||||
|
} |
||||
|
|
||||
|
public interface IPointUnitService |
||||
|
{ |
||||
|
double ScaleX { get; } |
||||
|
double ScaleY { get; } |
||||
|
double FontScale { get; } |
||||
|
|
||||
|
double NativeToPerspexX(double x); |
||||
|
|
||||
|
double NativeToPerspexY(double y); |
||||
|
|
||||
|
double PerspexToNativeX(double x); |
||||
|
|
||||
|
double PerspexToNativeY(double y); |
||||
|
|
||||
|
float PerspexToNativeFontSize(double fontSize); |
||||
|
} |
||||
|
|
||||
|
public class PointUnitService : IPointUnitService |
||||
|
{ |
||||
|
public static IPointUnitService Instance => PerspexLocator.Current.GetService<IPointUnitService>() ?? new PointUnitService(); |
||||
|
|
||||
|
private PointUnit _pointUnit; |
||||
|
|
||||
|
public PointUnit PointUnit |
||||
|
{ |
||||
|
get { return _pointUnit; } |
||||
|
set |
||||
|
{ |
||||
|
_pointUnit = value; |
||||
|
OnPointUnitChanged(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public double ScaleX { get; set; } |
||||
|
|
||||
|
public double ScaleY { get; set; } |
||||
|
|
||||
|
public double FontScale { get; set; } |
||||
|
|
||||
|
public PointUnitService() |
||||
|
{ |
||||
|
PointUnit = AndroidPlatform.Instance.DefaultPointUnit; |
||||
|
//ScaleX = 1;
|
||||
|
//ScaleY = 1;
|
||||
|
////FontScale = 1;
|
||||
|
//ScaleX = 2;
|
||||
|
//ScaleY = 2;
|
||||
|
//FontScale = 2;
|
||||
|
} |
||||
|
|
||||
|
private void OnPointUnitChanged() |
||||
|
{ |
||||
|
switch (PointUnit) |
||||
|
{ |
||||
|
case PointUnit.Pixel: |
||||
|
ScaleX = 1; |
||||
|
ScaleY = 1; |
||||
|
FontScale = 1; |
||||
|
break; |
||||
|
|
||||
|
case PointUnit.DP: |
||||
|
{ |
||||
|
//get android metrics for device pixel format
|
||||
|
var activity = PerspexLocator.Current.GetService<IAndroidActivity>().Activity; |
||||
|
var dm = activity.Resources.DisplayMetrics; |
||||
|
double scale = Math.Round((double)dm.ScaledDensity, 2); |
||||
|
ScaleX = scale; |
||||
|
ScaleY = scale; |
||||
|
FontScale = scale; |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case PointUnit.Custom: |
||||
|
{ |
||||
|
double scale = 2; |
||||
|
ScaleX = scale; |
||||
|
ScaleY = scale; |
||||
|
FontScale = scale; |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public double NativeToPerspexX(double x) => ScaleX == 1 ? x : RoundToPerspex(x / ScaleX); |
||||
|
|
||||
|
public double NativeToPerspexY(double y) => ScaleY == 1 ? y : RoundToPerspex(y / ScaleY); |
||||
|
|
||||
|
public double PerspexToNativeX(double x) => ScaleX == 1 ? x : RoundToNative(ScaleX * x); |
||||
|
|
||||
|
public double PerspexToNativeY(double y) => ScaleY == 1 ? y : RoundToNative(ScaleY * y); |
||||
|
|
||||
|
public float PerspexToNativeFontSize(double fontSize) |
||||
|
{ |
||||
|
if (FontScale == 1) return (float)fontSize; |
||||
|
return (float)Math.Round(FontScale * fontSize, 1); |
||||
|
} |
||||
|
|
||||
|
private static double RoundToPerspex(double v) |
||||
|
{ |
||||
|
// return Math.Ceiling(v);
|
||||
|
return Math.Round(v, 5); |
||||
|
} |
||||
|
|
||||
|
private static double RoundToNative(double v) |
||||
|
{ |
||||
|
//return Math.Ceiling(v);
|
||||
|
return Math.Round(v, 2); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static class PointServiceExtensions |
||||
|
{ |
||||
|
//public static Matrix PerspexToNative(this IPointService ps, Matrix m)
|
||||
|
//{
|
||||
|
// if (ps.ScaleX == ps.ScaleY && ps.ScaleX == 1) return m;
|
||||
|
|
||||
|
// return new Matrix(m.M11, m.M12, m.M21, m.M22, m.M31 * ps.ScaleX, m.M32 * ps.ScaleY);
|
||||
|
//}
|
||||
|
|
||||
|
//public static double PerspexToNative(this IPointService service, double x)
|
||||
|
//{
|
||||
|
// if (service.ScaleX == 1) return x;
|
||||
|
// return x * service.ScaleX;
|
||||
|
//}
|
||||
|
|
||||
|
private static int RoundToInt(double v) |
||||
|
{ |
||||
|
return (int)Math.Round(v, 0); |
||||
|
} |
||||
|
|
||||
|
public static double NativeToPerspex(this IPointUnitService ps, double x) => ps.NativeToPerspexX(x); |
||||
|
|
||||
|
public static float PerspexToNativeXF(this IPointUnitService ps, double x) => (float)ps.PerspexToNativeX(x); |
||||
|
|
||||
|
public static float PerspexToNativeYF(this IPointUnitService ps, double y) => (float)ps.PerspexToNativeY(y); |
||||
|
|
||||
|
public static int PerspexToNativeXInt(this IPointUnitService ps, double x) => RoundToInt(ps.PerspexToNativeX(x)); |
||||
|
|
||||
|
public static int PerspexToNativeYInt(this IPointUnitService ps, double y) => RoundToInt(ps.PerspexToNativeX(y)); |
||||
|
|
||||
|
public static Point NativeToPerspex(this IPointUnitService ps, Point point) => (ps.ScaleY == 1 && ps.ScaleX == 1) ? point : new Point(ps.NativeToPerspexX(point.X), ps.NativeToPerspexY(point.Y)); |
||||
|
|
||||
|
public static Point PerspexToNative(this IPointUnitService ps, Point point) => (ps.ScaleY == 1 && ps.ScaleX == 1) ? point : new Point(ps.PerspexToNativeX(point.X), ps.PerspexToNativeY(point.Y)); |
||||
|
|
||||
|
public static Size NativeToPerspex(this IPointUnitService service, Size size) |
||||
|
{ |
||||
|
if (service.ScaleX == service.ScaleY && service.ScaleX == 1) return size; |
||||
|
|
||||
|
return new Size(size.Width / service.ScaleX, size.Height / service.ScaleY); |
||||
|
} |
||||
|
|
||||
|
public static Size PerspexToNative(this IPointUnitService service, Size size) |
||||
|
{ |
||||
|
if (service.ScaleX == service.ScaleY && service.ScaleX == 1) return size; |
||||
|
return new Size(size.Width * service.ScaleX, size.Height * service.ScaleY); |
||||
|
} |
||||
|
|
||||
|
public static Rect NativeToPerspex(this IPointUnitService service, Rect rect) |
||||
|
{ |
||||
|
if (service.ScaleX == service.ScaleY && service.ScaleX == 1) return rect; |
||||
|
|
||||
|
return new Rect(rect.X / service.ScaleX, rect.Y / service.ScaleY, rect.Width / service.ScaleX, rect.Height / service.ScaleY); |
||||
|
} |
||||
|
|
||||
|
public static Rect PerspexToNative(this IPointUnitService service, Rect rect) |
||||
|
{ |
||||
|
if (service.ScaleX == service.ScaleY && service.ScaleX == 1) return rect; |
||||
|
|
||||
|
return new Rect(rect.X * service.ScaleX, rect.Y * service.ScaleY, rect.Width * service.ScaleX, rect.Height * service.ScaleY); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,137 @@ |
|||||
|
// Copyright (c) The Perspex Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using Android.Content; |
||||
|
using Android.Views; |
||||
|
using Android.Widget; |
||||
|
using Perspex.Android.Platform.Specific; |
||||
|
using Perspex.Input; |
||||
|
using Perspex.Input.Raw; |
||||
|
using Perspex.Platform; |
||||
|
using System; |
||||
|
using AG = Android.Graphics; |
||||
|
|
||||
|
namespace Perspex.Android.Platform.CanvasPlatform |
||||
|
{ |
||||
|
public class PopupImpl : PopupWindow, IPopupImpl, IPlatformHandle |
||||
|
{ |
||||
|
private class PopupPerspexViewContent : WindowImpl |
||||
|
{ |
||||
|
private Func<MotionEvent, bool?> _funcDispatchTouchEvent; |
||||
|
|
||||
|
public PopupPerspexViewContent(Context context, Func<MotionEvent, bool?> funcDispatchTouchEvent) : base(context) |
||||
|
{ |
||||
|
this._funcDispatchTouchEvent = funcDispatchTouchEvent; |
||||
|
} |
||||
|
|
||||
|
public override bool DispatchTouchEvent(MotionEvent e) |
||||
|
{ |
||||
|
bool? res = _funcDispatchTouchEvent(e); |
||||
|
|
||||
|
return res == null ? base.DispatchTouchEvent(e) : res.Value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private PopupPerspexViewContent content; |
||||
|
|
||||
|
private View mainView; |
||||
|
|
||||
|
//public PopupImpl(Context context) : base(context)
|
||||
|
public PopupImpl() |
||||
|
{ |
||||
|
ContentView = content = new PopupPerspexViewContent(PerspexLocator.Current.GetService<IAndroidActivity>().Activity, DispatchTouchEvent); |
||||
|
ContentView.Background = new AG.Drawables.ColorDrawable(AG.Color.Transparent); |
||||
|
mainView = PerspexLocator.Current.GetService<IAndroidActivity>().ContentView.View; |
||||
|
//Background = new AG.Drawables.ColorDrawable(AG.Color.);
|
||||
|
} |
||||
|
|
||||
|
//public PopupImpl() : this(PerspexLocator.Current.GetService<IAndroidActivity>().Activity)
|
||||
|
//{
|
||||
|
//}
|
||||
|
|
||||
|
private bool? DispatchTouchEvent(MotionEvent e) |
||||
|
{ |
||||
|
var p = content.GetPerspexPointFromEvent(e); |
||||
|
|
||||
|
Rect clientBounds = new Rect(ClientSize); |
||||
|
|
||||
|
if (clientBounds.Contains(p)) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
return mainView.DispatchTouchEvent(e); |
||||
|
} |
||||
|
|
||||
|
public Size MaxClientSize => content.MaxClientSize; |
||||
|
|
||||
|
public Action Activated { get { return (content as ITopLevelImpl).Activated; } set { (content as ITopLevelImpl).Activated = value; } } |
||||
|
|
||||
|
public Size ClientSize { get { return content.ClientSize; } set { content.ClientSize = value; } } |
||||
|
|
||||
|
public Action Closed { get { return content.Closed; } set { content.Closed = value; } } |
||||
|
|
||||
|
public Action Deactivated { get { return content.Deactivated; } set { content.Deactivated = value; } } |
||||
|
|
||||
|
public Action<RawInputEventArgs> Input { get { return content.Input; } set { content.Input = value; } } |
||||
|
|
||||
|
public Action<Rect> Paint { get { return content.Paint; } set { content.Paint = value; } } |
||||
|
|
||||
|
public Action<Size> Resized { get { return content.Resized; } set { content.Resized = value; } } |
||||
|
|
||||
|
IntPtr IPlatformHandle.Handle => content.Handle; |
||||
|
|
||||
|
IPlatformHandle ITopLevelImpl.Handle => content; |
||||
|
|
||||
|
public string HandleDescriptor => "PopupImpl"; |
||||
|
|
||||
|
public void Activate() => content.Activate(); |
||||
|
|
||||
|
public void Invalidate(Rect rect) => content.Invalidate(rect); |
||||
|
|
||||
|
public Point PointToScreen(Point point) |
||||
|
{ |
||||
|
return content.PointToScreen(point); |
||||
|
} |
||||
|
|
||||
|
public void SetCursor(IPlatformHandle cursor) |
||||
|
{ |
||||
|
content.SetCursor(cursor); |
||||
|
} |
||||
|
|
||||
|
public void SetInputRoot(IInputRoot inputRoot) |
||||
|
{ |
||||
|
content.SetInputRoot(inputRoot); |
||||
|
} |
||||
|
|
||||
|
private Point position; |
||||
|
|
||||
|
public void SetPosition(Point p) |
||||
|
{ |
||||
|
position = p; |
||||
|
} |
||||
|
|
||||
|
public void Show() |
||||
|
{ |
||||
|
content.HandleEvents = true; |
||||
|
var ps = PointUnitService.Instance; |
||||
|
//content.Activate();
|
||||
|
Width = ps.PerspexToNativeXInt(MaxClientSize.Width - position.X); |
||||
|
Height = ps.PerspexToNativeYInt(MaxClientSize.Height - position.Y); |
||||
|
content.LayoutParameters = new ViewGroup.LayoutParams(Width, Height); |
||||
|
// content.ClientSize = new Size(MaxClientSize.Width - position.X, MaxClientSize.Height - position.Y);
|
||||
|
//Width = Convert.ToInt32(ClientSize.Width);
|
||||
|
//Height = Convert.ToInt32(ClientSize.Height);
|
||||
|
|
||||
|
base.ShowAtLocation(mainView, GravityFlags.Top | GravityFlags.Left, ps.PerspexToNativeXInt(position.X), ps.PerspexToNativeYInt(position.Y)); |
||||
|
content.Invalidate(); |
||||
|
} |
||||
|
|
||||
|
public void Hide() |
||||
|
{ |
||||
|
content.HandleEvents = false; |
||||
|
//content.Hide();
|
||||
|
base.Dismiss(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,688 @@ |
|||||
|
using Android.Graphics; |
||||
|
using Android.Runtime; |
||||
|
using Android.Text; |
||||
|
using Android.Util; |
||||
|
using Android.Views; |
||||
|
using Perspex.Android.CanvasRendering; |
||||
|
using Perspex.Android.Platform.Specific.Helpers; |
||||
|
using System; |
||||
|
using System.Collections.Concurrent; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using P = Perspex.Platform; |
||||
|
|
||||
|
namespace Perspex.Android.Platform.CanvasPlatform.Specific.Helpers |
||||
|
{ |
||||
|
public abstract class AndroidViewRenderHelper<TView> : Java.Lang.Object, IDisposable where TView : View, P.IWindowImpl, IAndroidViewSupportRender |
||||
|
{ |
||||
|
private static bool _debug = false; |
||||
|
|
||||
|
protected AndroidViewRenderHelper(TView view) |
||||
|
{ |
||||
|
this.View = view; |
||||
|
VisualCaches = null; |
||||
|
} |
||||
|
|
||||
|
public Dictionary<object, IDisposable> VisualCaches { get; protected set; } |
||||
|
|
||||
|
public abstract void OnDraw(Canvas canvas); |
||||
|
|
||||
|
public abstract void Invalidate(Rect rect); |
||||
|
|
||||
|
public TView View { get; private set; } |
||||
|
|
||||
|
public ViewDrawType DrawType { get; private set; } |
||||
|
|
||||
|
public static AndroidViewRenderHelper<TView> Create(TView view, ViewDrawType drawType) |
||||
|
{ |
||||
|
AndroidViewRenderHelper<TView> result; |
||||
|
switch (drawType) |
||||
|
{ |
||||
|
case ViewDrawType.BitmapBackgroundRender: |
||||
|
result = new BitmapBackgroundRenderHelper<TView>(view); |
||||
|
break; |
||||
|
|
||||
|
case ViewDrawType.BitmapOnPreDraw: |
||||
|
result = new BitmapPreDrawRenderHelper<TView>(view); |
||||
|
break; |
||||
|
|
||||
|
case ViewDrawType.CanvasOnDraw: |
||||
|
result = new CanvasOnDrawAndroidViewRenderHelper<TView>(view); |
||||
|
break; |
||||
|
|
||||
|
default: |
||||
|
throw new NotSupportedException(); |
||||
|
} |
||||
|
|
||||
|
Init(result, drawType); |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
protected static void Init(AndroidViewRenderHelper<TView> helper, ViewDrawType drawType) |
||||
|
{ |
||||
|
helper.DrawType = drawType; |
||||
|
helper.SetDebugInfo("DrawType", drawType.ToString()); |
||||
|
} |
||||
|
|
||||
|
public virtual new void Dispose() |
||||
|
{ |
||||
|
View = null; |
||||
|
_debugInfoPaint?.Dispose(); |
||||
|
} |
||||
|
|
||||
|
public Dictionary<string, string> DebugInfo { get; } = new Dictionary<string, string>(); |
||||
|
|
||||
|
protected void SetDebugInfo(string key, string value) => DebugInfo[key] = value; |
||||
|
|
||||
|
private const int _avgValuesForStat = 50; |
||||
|
|
||||
|
private Dictionary<string, Queue<double>> _renderStats = new Dictionary<string, Queue<double>>(); |
||||
|
|
||||
|
protected void UpdateRenderStat(string key, double ms) |
||||
|
{ |
||||
|
Queue<double> statsQueue; |
||||
|
if (!_renderStats.TryGetValue(key, out statsQueue)) |
||||
|
{ |
||||
|
statsQueue = new Queue<double>(); |
||||
|
_renderStats[key] = statsQueue; |
||||
|
} |
||||
|
|
||||
|
statsQueue.Enqueue(ms); |
||||
|
if (statsQueue.Count > _avgValuesForStat) statsQueue.Dequeue(); |
||||
|
|
||||
|
SetDebugInfo(key, ms.ToString("0.0")); |
||||
|
SetDebugInfo($"a{key}", (statsQueue.Average()).ToString("0.0")); |
||||
|
} |
||||
|
|
||||
|
protected void RenderView(Canvas canvas, Rect rect) |
||||
|
{ |
||||
|
DateTime begin = DateTime.Now; |
||||
|
|
||||
|
View.PreRender(canvas, rect); |
||||
|
View.Render(canvas, rect); |
||||
|
View.PostRender(canvas, rect); |
||||
|
|
||||
|
TimeSpan duration = DateTime.Now - begin; |
||||
|
|
||||
|
if (_debug) Log.Debug("AndroidViewRenderHelper", $"RenderView {duration}"); |
||||
|
|
||||
|
UpdateRenderStat("RenderView", duration.TotalMilliseconds); |
||||
|
UpdateRenderStat("mfps", 1000 / duration.TotalMilliseconds); |
||||
|
} |
||||
|
|
||||
|
private Paint _debugInfoPaint = null; |
||||
|
|
||||
|
public void DrawDebugInfoToCanvas(Canvas canvas) |
||||
|
{ |
||||
|
if (!AndroidPlatform.Instance.DrawDebugInfo) return; |
||||
|
|
||||
|
if (DebugInfo.Count == 0) return; |
||||
|
|
||||
|
if (_debugInfoPaint == null) _debugInfoPaint = new TextPaint(PaintFlags.AntiAlias) { Color = Color.Red, TextAlign = Paint.Align.Left, TextSize = 20 }; |
||||
|
|
||||
|
var sb = new StringBuilder(); |
||||
|
foreach (var o in DebugInfo) |
||||
|
{ |
||||
|
sb.Append($"{o.Key} = {o.Value}; "); |
||||
|
} |
||||
|
string sdb = sb.ToString(); |
||||
|
var r = new global::Android.Graphics.Rect(); |
||||
|
_debugInfoPaint.GetTextBounds(sdb, 0, sdb.Length, r); |
||||
|
|
||||
|
canvas.DrawText(sdb, View.Width - r.Right, View.Height - r.Bottom, _debugInfoPaint); |
||||
|
|
||||
|
if (_debug) Log.Debug("AndroidViewRenderHelper", sdb); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class CanvasOnDrawAndroidViewRenderHelper<TView> : AndroidViewRenderHelper<TView> where TView : View, P.IWindowImpl, IAndroidViewSupportRender |
||||
|
{ |
||||
|
private Rect _rect; |
||||
|
|
||||
|
public CanvasOnDrawAndroidViewRenderHelper(TView view) : base(view) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public override void Invalidate(Rect rect) |
||||
|
{ |
||||
|
_rect = rect; |
||||
|
View.Invalidate(rect.ToAndroidGraphics()); |
||||
|
} |
||||
|
|
||||
|
public override void OnDraw(Canvas canvas) |
||||
|
{ |
||||
|
RenderView(canvas, _rect); |
||||
|
} |
||||
|
|
||||
|
public override void Dispose() |
||||
|
{ |
||||
|
base.Dispose(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class BitmapBackgroundRenderHelper<TView> : AndroidViewRenderHelper<TView> where TView : View, P.IWindowImpl, IAndroidViewSupportRender |
||||
|
{ |
||||
|
private bool _debug = WindowImpl._debug; |
||||
|
private Bitmap _bitmap; |
||||
|
private Rect _rect; |
||||
|
private Paint _paint = new Paint(); |
||||
|
private ConcurrentStack<Rect> _invalidateRects = new ConcurrentStack<Rect>(); |
||||
|
private object _invalidateRectsSync = new object(); |
||||
|
private object _drawSync = new object(); |
||||
|
private Stack<Bitmap> _renderingBitmapsCache = new Stack<Bitmap>(); |
||||
|
private Dictionary<Bitmap, Canvas> _bmCanvases = new Dictionary<Bitmap, Canvas>(); |
||||
|
private CancellationTokenSource _cancelationTokenSource; |
||||
|
|
||||
|
public BitmapBackgroundRenderHelper(TView view) : base(view) |
||||
|
{ |
||||
|
_cancelationTokenSource = new CancellationTokenSource(); |
||||
|
VisualCaches = new Dictionary<object, IDisposable>(); |
||||
|
Task.Factory.StartNew(RenderingLoop, _cancelationTokenSource.Token, _cancelationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Current); |
||||
|
} |
||||
|
|
||||
|
public override void Invalidate(Rect rect) |
||||
|
{ |
||||
|
_rect = rect; |
||||
|
_invalidateRects.Push(rect); |
||||
|
lock (_invalidateRectsSync) |
||||
|
{ |
||||
|
Monitor.Pulse(_invalidateRectsSync); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public override void OnDraw(Canvas canvas) |
||||
|
{ |
||||
|
var begin = DateTime.Now; |
||||
|
lock (_drawSync) |
||||
|
{ |
||||
|
if (_bitmap != null) |
||||
|
{ |
||||
|
canvas.DrawBitmap(_bitmap, 0, 0, _paint); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
UpdateRenderStat("OnDraw", (DateTime.Now - begin).TotalMilliseconds); |
||||
|
} |
||||
|
|
||||
|
private void RenderingLoop(object obj) |
||||
|
{ |
||||
|
CancellationToken token = (CancellationToken)obj; |
||||
|
if (_debug) Log.Debug("render", "RenderingLoop Start ..."); |
||||
|
|
||||
|
AndroidPlatform.Instance.RegisterRenderingTaskId(Task.CurrentId.Value); |
||||
|
|
||||
|
while (!token.IsCancellationRequested) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
Rect rect = default(Rect); |
||||
|
bool hasRect = false; |
||||
|
|
||||
|
hasRect = _invalidateRects.Count > 0; ; |
||||
|
if (!hasRect) |
||||
|
{ |
||||
|
lock (_invalidateRectsSync) |
||||
|
{ |
||||
|
hasRect = Monitor.Wait(_invalidateRectsSync, 1000); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (!hasRect) |
||||
|
{ |
||||
|
DisposeVisualCaches(); |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
if (_invalidateRects.Count < 1) continue; |
||||
|
int rCnt = _invalidateRects.Count; |
||||
|
|
||||
|
Rect[] rects = new Rect[rCnt > 1 ? (rCnt - 1) : 1]; |
||||
|
int cnt = _invalidateRects.TryPopRange(rects, 0, rects.Length); |
||||
|
if (_debug) Log.Debug("render", $"RenderingLoop processing {cnt} rects"); |
||||
|
if (cnt == 1) |
||||
|
{ |
||||
|
rect = rects[0]; |
||||
|
//assuming this is the last frame so we dispose the caches
|
||||
|
DisposeVisualCaches(); |
||||
|
} |
||||
|
else if (cnt > 1) |
||||
|
{ |
||||
|
//for now all the rects are the same but may be in the future this can change
|
||||
|
var minX = rects.Min(r => r.X); |
||||
|
var minY = rects.Min(r => r.Y); |
||||
|
var maxRight = rects.Max(r => r.Right); |
||||
|
var maxBottom = rects.Max(r => r.Bottom); |
||||
|
rect = new Rect(minX, minY, maxRight - minX, maxBottom - minY); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
//Should never happend
|
||||
|
hasRect = false; |
||||
|
} |
||||
|
|
||||
|
if (hasRect) |
||||
|
{ |
||||
|
Render(rect); |
||||
|
} |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
Log.Debug("render", "RenderingLoop Exception {0} ...", e); |
||||
|
} |
||||
|
} |
||||
|
AndroidPlatform.Instance.UnRegisterRenderingTaskId(Task.CurrentId.Value); |
||||
|
Log.Debug("render", "RenderingLoop End ..."); |
||||
|
} |
||||
|
|
||||
|
private void Render(Rect rect) |
||||
|
{ |
||||
|
DateTime begin = DateTime.Now; |
||||
|
if (_debug) Log.Debug("render", $"RenderingLoop new invalidation ... {rect} {begin}"); |
||||
|
Bitmap bitmap; |
||||
|
|
||||
|
int w = View.Width; |
||||
|
int h = View.Height; |
||||
|
|
||||
|
if (w == 0) |
||||
|
{ |
||||
|
var s = PointUnitService.Instance.PerspexToNative(View.ClientSize); |
||||
|
w = (int)s.Width; |
||||
|
h = (int)s.Height; |
||||
|
} |
||||
|
|
||||
|
if (_renderingBitmapsCache.Count > 0) |
||||
|
{ |
||||
|
// _renderingBitmapsCache.TryPop(out bitmap);
|
||||
|
bitmap = _renderingBitmapsCache.Pop(); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
bitmap = Bitmap.CreateBitmap(w, h, Bitmap.Config.Argb8888); |
||||
|
} |
||||
|
|
||||
|
if (bitmap.Width != w || bitmap.Height != h) |
||||
|
{ |
||||
|
bitmap.Dispose(); |
||||
|
bitmap = Bitmap.CreateBitmap(w, h, Bitmap.Config.Argb8888); |
||||
|
foreach (var c in _bmCanvases) |
||||
|
{ |
||||
|
c.Value.Dispose(); |
||||
|
} |
||||
|
_bmCanvases.Clear(); |
||||
|
} |
||||
|
|
||||
|
Canvas canvas; |
||||
|
if (!_bmCanvases.TryGetValue(bitmap, out canvas)) |
||||
|
{ |
||||
|
canvas = new Canvas(bitmap); |
||||
|
_bmCanvases.Add(bitmap, canvas); |
||||
|
} |
||||
|
|
||||
|
RenderView(canvas, _rect); |
||||
|
|
||||
|
//???no need dispose canvas is cached
|
||||
|
//canvas.Dispose();
|
||||
|
lock (_drawSync) |
||||
|
{ |
||||
|
if (_bitmap != null) _renderingBitmapsCache.Push(_bitmap); |
||||
|
_bitmap = bitmap; |
||||
|
} |
||||
|
var nr = rect.ToAndroidGraphics(); |
||||
|
|
||||
|
View.PostInvalidate(nr.Left, nr.Top, nr.Right, nr.Bottom); |
||||
|
//View.PostInvalidateDelayed(1, nr.Left, nr.Top, nr.Right, nr.Bottom);
|
||||
|
} |
||||
|
|
||||
|
private void DisposeVisualCaches() |
||||
|
{ |
||||
|
lock (_drawSync) |
||||
|
{ |
||||
|
if (_debug) Log.Debug("render", $"RenderingLoop disposing {VisualCaches.Count} visual caches"); |
||||
|
foreach (var vc in VisualCaches) |
||||
|
{ |
||||
|
vc.Value.Dispose(); |
||||
|
} |
||||
|
|
||||
|
VisualCaches.Clear(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public override void Dispose() |
||||
|
{ |
||||
|
base.Dispose(); |
||||
|
_cancelationTokenSource.Cancel(); |
||||
|
_cancelationTokenSource = null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class BitmapPreDrawRenderHelper<TView> : AndroidViewRenderHelper<TView> where TView : View, P.IWindowImpl, IAndroidViewSupportRender |
||||
|
{ |
||||
|
private class OnPreDrawListener : Java.Lang.Object, ViewTreeObserver.IOnPreDrawListener |
||||
|
{ |
||||
|
private Func<bool> _preDraw; |
||||
|
|
||||
|
public OnPreDrawListener(Func<bool> preDraw) |
||||
|
{ |
||||
|
_preDraw = preDraw; |
||||
|
} |
||||
|
|
||||
|
public bool OnPreDraw() |
||||
|
{ |
||||
|
return _preDraw(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private Bitmap _bitmap; |
||||
|
private Rect _rect; |
||||
|
private Paint _paint = new Paint(); |
||||
|
private OnPreDrawListener _listener; |
||||
|
|
||||
|
public BitmapPreDrawRenderHelper(TView view) : base(view) |
||||
|
{ |
||||
|
_listener = new OnPreDrawListener(OnPreDraw); |
||||
|
View.ViewTreeObserver.AddOnPreDrawListener(_listener); |
||||
|
} |
||||
|
|
||||
|
public override void Dispose() |
||||
|
{ |
||||
|
base.Dispose(); |
||||
|
View.ViewTreeObserver.RemoveOnPreDrawListener(_listener); |
||||
|
_listener.Dispose(); |
||||
|
_bitmap.Dispose(); |
||||
|
_paint.Dispose(); |
||||
|
} |
||||
|
|
||||
|
public override void Invalidate(Rect rect) |
||||
|
{ |
||||
|
_rect = rect; |
||||
|
View.Invalidate(rect.ToAndroidGraphics()); |
||||
|
} |
||||
|
|
||||
|
public override void OnDraw(Canvas canvas) |
||||
|
{ |
||||
|
var begin = DateTime.Now; |
||||
|
|
||||
|
if (_bitmap != null) |
||||
|
{ |
||||
|
canvas.DrawBitmap(_bitmap, 0, 0, _paint); |
||||
|
} |
||||
|
|
||||
|
UpdateRenderStat("OnDraw", (DateTime.Now - begin).TotalMilliseconds); |
||||
|
} |
||||
|
|
||||
|
public bool OnPreDraw() |
||||
|
{ |
||||
|
int w = View.Width; |
||||
|
int h = View.Height; |
||||
|
Bitmap bitmap = _bitmap; |
||||
|
if (bitmap == null) |
||||
|
{ |
||||
|
bitmap = Bitmap.CreateBitmap(w, h, Bitmap.Config.Argb8888); |
||||
|
} |
||||
|
else if (bitmap.Width != w || bitmap.Height != h) |
||||
|
{ |
||||
|
bitmap.Dispose(); |
||||
|
bitmap = Bitmap.CreateBitmap(w, h, Bitmap.Config.Argb8888); |
||||
|
} |
||||
|
using (var canvas = new Canvas(bitmap)) |
||||
|
{ |
||||
|
RenderView(canvas, _rect); |
||||
|
} |
||||
|
_bitmap = bitmap; |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class SurfaceViewRenderHelper<TView> : AndroidViewRenderHelper<TView> where TView : SurfaceView, P.IWindowImpl, IAndroidViewSupportRender |
||||
|
{ |
||||
|
private class SurfaceHolderCallback : Java.Lang.Object, ISurfaceHolderCallback//, ISurfaceHolderCallback2
|
||||
|
{ |
||||
|
private SurfaceViewRenderHelper<TView> _parent; |
||||
|
|
||||
|
public SurfaceHolderCallback(SurfaceViewRenderHelper<TView> parent) |
||||
|
{ |
||||
|
_parent = parent; |
||||
|
} |
||||
|
|
||||
|
public void SurfaceChanged(ISurfaceHolder holder, [GeneratedEnum] Format format, int width, int height) |
||||
|
{ |
||||
|
_parent.SurfaceChanged(holder, format, width, height); |
||||
|
} |
||||
|
|
||||
|
public void SurfaceCreated(ISurfaceHolder holder) |
||||
|
{ |
||||
|
_parent.SurfaceCreated(holder); |
||||
|
} |
||||
|
|
||||
|
public void SurfaceDestroyed(ISurfaceHolder holder) |
||||
|
{ |
||||
|
_parent.SurfaceDestroyed(holder); |
||||
|
} |
||||
|
|
||||
|
public void SurfaceRedrawNeeded(ISurfaceHolder holder) |
||||
|
{ |
||||
|
_parent.SurfaceRedrawNeeded(holder); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public int DelayBetweenDrawms { get; set; } |
||||
|
protected ISurfaceHolder _holder; |
||||
|
private Task _task; |
||||
|
private CancellationTokenSource _cancelationTokenSource; |
||||
|
private CancellationToken _cancelationToken; |
||||
|
|
||||
|
private ConcurrentStack<Rect> _pendingInvalidations = new ConcurrentStack<Rect>(); |
||||
|
private object _invalidationRectsSync = new object(); |
||||
|
|
||||
|
private Format _surfaceFormat = Format.Rgba8888; |
||||
|
private Bitmap.Config _bitmapFormat = Bitmap.Config.Argb8888; |
||||
|
|
||||
|
public static new SurfaceViewRenderHelper<TView> Create(TView view, ViewDrawType drawType) |
||||
|
{ |
||||
|
SurfaceViewRenderHelper<TView> result; |
||||
|
|
||||
|
switch (drawType) |
||||
|
{ |
||||
|
case ViewDrawType.SurfaceViewCanvasOnDraw: |
||||
|
result = new SurfaceViewRenderHelper<TView>(view); |
||||
|
break; |
||||
|
|
||||
|
default: throw new NotSupportedException(); |
||||
|
} |
||||
|
|
||||
|
Init(result, drawType); |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
public SurfaceViewRenderHelper(TView view) : base(view) |
||||
|
{ |
||||
|
//max 16fps -60ms, 25fps - 40ms, max 30fps 30ms, max 60 fps-17ms
|
||||
|
DelayBetweenDrawms = 40; |
||||
|
view.Holder.AddCallback(new SurfaceHolderCallback(this)); |
||||
|
} |
||||
|
|
||||
|
public override void Invalidate(Rect rect) |
||||
|
{ |
||||
|
lock (_invalidationRectsSync) |
||||
|
{ |
||||
|
_pendingInvalidations.Push(rect); |
||||
|
Monitor.Pulse(_invalidationRectsSync); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public override void OnDraw(Canvas canvas) |
||||
|
{ |
||||
|
//do nothing here we are drawing on another thread
|
||||
|
} |
||||
|
|
||||
|
public void SurfaceChanged(ISurfaceHolder holder, [GeneratedEnum] Format format, int width, int height) |
||||
|
{ |
||||
|
Log.Debug("SurfaceViewRenderAdapter", $"SurfaceChanged format={format} width ={width} height={height}..."); |
||||
|
|
||||
|
var ps = PointUnitService.Instance; |
||||
|
Size newSize = new Size(ps.NativeToPerspexX(width), ps.NativeToPerspexY(height)); |
||||
|
if (View.ClientSize != newSize) |
||||
|
{ |
||||
|
View.ClientSize = newSize; |
||||
|
View.Resized?.Invoke(View.ClientSize); |
||||
|
} |
||||
|
|
||||
|
ScheduleRender(); |
||||
|
} |
||||
|
|
||||
|
public void SurfaceCreated(ISurfaceHolder holder) |
||||
|
{ |
||||
|
Log.Debug("SurfaceViewRenderAdapter", "SurfaceCreated ..."); |
||||
|
|
||||
|
_holder = holder; |
||||
|
_holder.SetFormat(_surfaceFormat); |
||||
|
|
||||
|
if (_task != null) |
||||
|
{ |
||||
|
StopTask(); |
||||
|
} |
||||
|
|
||||
|
if (_task == null) StartTask(); |
||||
|
|
||||
|
ScheduleRender(); |
||||
|
} |
||||
|
|
||||
|
private void SurfaceRedrawNeeded(ISurfaceHolder holder) |
||||
|
{ |
||||
|
Log.Debug("SurfaceViewRenderAdapter", "SurfaceRedrawNeeded ..."); |
||||
|
ScheduleRender(); |
||||
|
} |
||||
|
|
||||
|
private void ScheduleRender() |
||||
|
{ |
||||
|
if (_pendingInvalidations.Count > 0) |
||||
|
{ |
||||
|
Invalidate(new Rect(0, 0, View.ClientSize.Width, View.ClientSize.Height)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void RenderingLoop(object obj) |
||||
|
{ |
||||
|
AndroidPlatform.Instance.RegisterRenderingTaskId(Task.CurrentId.Value); |
||||
|
Log.Debug("SurfaceViewRenderAdapter", $"DrawingTask Enter Id={Task.CurrentId.Value}... "); |
||||
|
|
||||
|
//sometime the ui is not ready for other thread rendering
|
||||
|
Thread.Sleep(10 * DelayBetweenDrawms); |
||||
|
|
||||
|
var cancelationToken = (CancellationToken)obj; |
||||
|
while (!cancelationToken.IsCancellationRequested) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
bool waitResult = false; |
||||
|
//wait few miliseconds so we are not heating the processor too much
|
||||
|
Thread.Sleep(DelayBetweenDrawms); |
||||
|
|
||||
|
if (_pendingInvalidations.Count < 1) |
||||
|
{ |
||||
|
lock (_invalidationRectsSync) |
||||
|
{ |
||||
|
waitResult = Monitor.Wait(_invalidationRectsSync, 1000); |
||||
|
Thread.Sleep(0); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (cancelationToken.IsCancellationRequested) break; |
||||
|
|
||||
|
//if invalidations are more than one render one time after this
|
||||
|
//so we are sure we have correct state
|
||||
|
int requestCnt = _pendingInvalidations.Count > 1 ? _pendingInvalidations.Count - 1 : _pendingInvalidations.Count; |
||||
|
|
||||
|
if (requestCnt < 1) |
||||
|
{ |
||||
|
//we can not continue with drawing
|
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
var rects = new Rect[requestCnt]; |
||||
|
int rectsCount = _pendingInvalidations.TryPopRange(rects); |
||||
|
if (rectsCount < 1) |
||||
|
{ |
||||
|
//should never happen but who knows
|
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
Rect rect; |
||||
|
if (rectsCount == 1) |
||||
|
{ |
||||
|
rect = rects[0]; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
double minX = rects.Min(r => r.X); |
||||
|
double minY = rects.Min(r => r.Y); |
||||
|
double maxRight = rects.Max(r => r.Right); |
||||
|
double maxBottom = rects.Max(r => r.Bottom); |
||||
|
rect = new Rect(minX, minY, maxRight - minX, maxBottom - minY); |
||||
|
} |
||||
|
|
||||
|
RenderRect(rect); |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
Log.Debug("SurfaceViewRenderAdapter", $"Exception in DrawingTask {e}"); |
||||
|
} |
||||
|
} |
||||
|
Log.Debug("SurfaceViewRenderAdapter", $"DrawingTask Exit Id={Task.CurrentId.Value}..."); |
||||
|
} |
||||
|
|
||||
|
private void RenderRect(Rect rect) |
||||
|
{ |
||||
|
//Log.Debug("SurfaceViewRenderAdapter", $"RenderRect Enter rect={rect}... ");
|
||||
|
var surfaceCanvas = _holder.LockCanvas(); |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
lock (_holder) |
||||
|
{ |
||||
|
RenderView(surfaceCanvas, rect); |
||||
|
} |
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
_holder.UnlockCanvasAndPost(surfaceCanvas); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void SurfaceDestroyed(ISurfaceHolder holder) |
||||
|
{ |
||||
|
StopTask(); |
||||
|
} |
||||
|
|
||||
|
private void StartTask() |
||||
|
{ |
||||
|
_cancelationTokenSource = new CancellationTokenSource(); |
||||
|
_cancelationToken = _cancelationTokenSource.Token; |
||||
|
_task = Task.Factory.StartNew(RenderingLoop, _cancelationToken, _cancelationToken); |
||||
|
} |
||||
|
|
||||
|
private void StopTask() |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
Log.Debug("SurfaceViewRenderAdapter", $"StopTask _cancelationTokenSource != null {_cancelationTokenSource != null}"); |
||||
|
if (_cancelationTokenSource != null) |
||||
|
{ |
||||
|
_cancelationTokenSource.Cancel(); |
||||
|
Task.WaitAll(_task); |
||||
|
_cancelationTokenSource = null; |
||||
|
_task = null; |
||||
|
} |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
Log.Debug("SurfaceViewRenderAdapter", $"Exception in StopTask DrawingTask.Cancel {e}"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
using Android.Views; |
||||
|
using Perspex.Android.Platform.Specific; |
||||
|
using Perspex.Input; |
||||
|
using Perspex.Platform; |
||||
|
|
||||
|
namespace Perspex.Android.Platform.CanvasPlatform |
||||
|
{ |
||||
|
public class SurfaceMainWindowImpl : SurfaceWindowImpl, IWindowImpl |
||||
|
{ |
||||
|
private IPointUnitService _pointService = PointUnitService.Instance; |
||||
|
|
||||
|
protected override void Init() |
||||
|
{ |
||||
|
base.Init(); |
||||
|
|
||||
|
HandleEvents = true; |
||||
|
_keyboardHelper.ActivateAutoShowKeybord(); |
||||
|
} |
||||
|
|
||||
|
void ITopLevelImpl.Show() |
||||
|
{ |
||||
|
(Parent as ViewGroup)?.RemoveAllViews(); |
||||
|
PerspexLocator.Current.GetService<IAndroidActivity>().ContentView = this; |
||||
|
Visibility = ViewStates.Visible; |
||||
|
} |
||||
|
|
||||
|
void ITopLevelImpl.SetInputRoot(IInputRoot inputRoot) |
||||
|
{ |
||||
|
base.SetInputRoot(inputRoot); |
||||
|
_keyboardHelper.UpdateKeyboardState(inputRoot); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,194 @@ |
|||||
|
using Android.Content; |
||||
|
using Android.Graphics; |
||||
|
using Android.Util; |
||||
|
using Android.Views; |
||||
|
using Perspex.Android.Platform.CanvasPlatform.Specific.Helpers; |
||||
|
using Perspex.Android.Platform.Specific; |
||||
|
using Perspex.Android.Platform.Specific.Helpers; |
||||
|
using Perspex.Input; |
||||
|
using Perspex.Input.Raw; |
||||
|
using Perspex.Platform; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
using AG = Android.Graphics; |
||||
|
|
||||
|
namespace Perspex.Android.Platform.CanvasPlatform |
||||
|
{ |
||||
|
public class SurfaceWindowImpl : SurfaceView, IWindowImpl, IPlatformHandle, IAndroidCanvasView, IAndroidViewSupportRender |
||||
|
{ |
||||
|
public SurfaceWindowImpl(Context context) : base(context) |
||||
|
{ |
||||
|
DrawType = ViewDrawType.SurfaceViewCanvasOnDraw; |
||||
|
|
||||
|
_keyboardHelper = new AndroidKeyboardEventsHelper<SurfaceWindowImpl>(this); |
||||
|
_touchHelper = new AndroidTouchEventsHelper<SurfaceWindowImpl>(this, () => InputRoot, GetPerspexPointFromEvent); |
||||
|
|
||||
|
ClientSize = MaxClientSize; |
||||
|
Resized = size => Invalidate(new Rect(size)); |
||||
|
Init(); |
||||
|
Background = new AG.Drawables.ColorDrawable(AG.Color.Transparent); |
||||
|
} |
||||
|
|
||||
|
public SurfaceWindowImpl() : this(PerspexLocator.Current.GetService<IAndroidActivity>().Activity) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public void SurfaceRedrawNeeded(ISurfaceHolder holder) |
||||
|
{ |
||||
|
Log.Debug("SurfaceView", "SurfaceRedrawNeeded..."); |
||||
|
//TryDraw(holder);
|
||||
|
} |
||||
|
|
||||
|
protected override void OnDraw(Canvas canvas) |
||||
|
{ |
||||
|
//base.OnDraw(canvas);
|
||||
|
this._renderHelper.OnDraw(canvas); |
||||
|
} |
||||
|
|
||||
|
private IPointUnitService _pointService = PointUnitService.Instance; |
||||
|
|
||||
|
public ViewDrawType DrawType { get; } |
||||
|
|
||||
|
private SurfaceViewRenderHelper<SurfaceWindowImpl> _renderHelper; |
||||
|
protected AndroidKeyboardEventsHelper<SurfaceWindowImpl> _keyboardHelper; |
||||
|
protected AndroidTouchEventsHelper<SurfaceWindowImpl> _touchHelper; |
||||
|
|
||||
|
private bool _handleEvents; |
||||
|
|
||||
|
public bool HandleEvents |
||||
|
{ |
||||
|
get { return _handleEvents; } |
||||
|
set |
||||
|
{ |
||||
|
_handleEvents = value; |
||||
|
_keyboardHelper.HandleEvents = _handleEvents; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected virtual void Init() |
||||
|
{ |
||||
|
//SetLayerType(LayerType.Hardware, null);
|
||||
|
_renderHelper = SurfaceViewRenderHelper<SurfaceWindowImpl>.Create(this, DrawType); |
||||
|
|
||||
|
HandleEvents = false; |
||||
|
|
||||
|
//if the background is not transparent nothing is visible!!!
|
||||
|
//this is the case when inherit from GridLayout, not from View
|
||||
|
//this.Background = new AG.Drawables.ColorDrawable(AG.Color.Transparent);
|
||||
|
} |
||||
|
|
||||
|
public Rect Bounds { get; set; } |
||||
|
|
||||
|
public IInputRoot InputRoot { get; private set; } |
||||
|
IntPtr IPlatformHandle.Handle => Handle; |
||||
|
string IPlatformHandle.HandleDescriptor => "Perspex SurfaceView"; |
||||
|
|
||||
|
public void Resize(double width, double height) |
||||
|
{ |
||||
|
if (ClientSize == new Size(width, height)) return; |
||||
|
ClientSize = new Size(width, height); |
||||
|
Resized(ClientSize); |
||||
|
} |
||||
|
|
||||
|
public Size ClientSize { get; set; } |
||||
|
IPlatformHandle ITopLevelImpl.Handle => this; |
||||
|
Action ITopLevelImpl.Activated { get; set; } |
||||
|
public Action Closed { get; set; } |
||||
|
public Action Deactivated { get; set; } |
||||
|
public Action<RawInputEventArgs> Input { get; set; } |
||||
|
public Action<Rect> Paint { get; set; } |
||||
|
public Action<Size> Resized { get; set; } |
||||
|
|
||||
|
public void Activate() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public void SetInputRoot(IInputRoot inputRoot) |
||||
|
{ |
||||
|
InputRoot = inputRoot; |
||||
|
} |
||||
|
|
||||
|
public Point PointToScreen(Point point) |
||||
|
{ |
||||
|
return point; |
||||
|
} |
||||
|
|
||||
|
public void SetCursor(IPlatformHandle cursor) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public Size MaxClientSize => _pointService.NativeToPerspex(new Size(Resources.DisplayMetrics.WidthPixels, Resources.DisplayMetrics.HeightPixels)); |
||||
|
|
||||
|
public View View => this; |
||||
|
|
||||
|
public void SetTitle(string title) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
void ITopLevelImpl.Show() |
||||
|
{ |
||||
|
this.Visibility = ViewStates.Visible; |
||||
|
} |
||||
|
|
||||
|
public IDisposable ShowDialog() |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
public void Hide() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public virtual Point GetPerspexPointFromEvent(MotionEvent e) => _pointService.NativeToPerspex(new Point(e.GetX(), e.GetY())); |
||||
|
|
||||
|
public override bool DispatchTouchEvent(MotionEvent e) |
||||
|
{ |
||||
|
bool callBase; |
||||
|
bool? result = _touchHelper.DispatchTouchEvent(e, out callBase); |
||||
|
bool baseResult = callBase ? base.DispatchTouchEvent(e) : false; |
||||
|
|
||||
|
return result != null ? result.Value : baseResult; |
||||
|
} |
||||
|
|
||||
|
public override bool DispatchKeyEvent(KeyEvent e) |
||||
|
{ |
||||
|
bool callBase; |
||||
|
bool? res = _keyboardHelper.DispatchKeyEvent(e, out callBase); |
||||
|
bool baseResult = callBase ? base.DispatchKeyEvent(e) : false; |
||||
|
|
||||
|
return res != null ? res.Value : baseResult; |
||||
|
} |
||||
|
|
||||
|
public void Invalidate(Rect rect) |
||||
|
{ |
||||
|
this._renderHelper.Invalidate(rect); |
||||
|
} |
||||
|
|
||||
|
public virtual void PreRender(Canvas canvas, Rect rect) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public virtual void Render(Canvas canvas, Rect rect) |
||||
|
{ |
||||
|
Canvas = canvas; |
||||
|
Paint?.Invoke(rect); |
||||
|
} |
||||
|
|
||||
|
public virtual void PostRender(Canvas canvas, Rect rect) |
||||
|
{ |
||||
|
_renderHelper.DrawDebugInfoToCanvas(canvas); |
||||
|
_touchHelper.DrawLastMousePoint(canvas); |
||||
|
} |
||||
|
|
||||
|
public AG.Canvas Canvas { get; private set; } |
||||
|
|
||||
|
public Dictionary<object, IDisposable> VisualCaches => _renderHelper.VisualCaches; |
||||
|
|
||||
|
protected override void OnSizeChanged(int w, int h, int oldw, int oldh) |
||||
|
{ |
||||
|
base.OnSizeChanged(w, h, oldw, oldh); |
||||
|
Resize(MaxClientSize.Width, MaxClientSize.Height); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,207 @@ |
|||||
|
using Android.Content; |
||||
|
using Android.Views; |
||||
|
using Perspex.Android.Platform.CanvasPlatform.Specific.Helpers; |
||||
|
using Perspex.Android.Platform.Specific; |
||||
|
using Perspex.Android.Platform.Specific.Helpers; |
||||
|
using Perspex.Input; |
||||
|
using Perspex.Input.Raw; |
||||
|
using Perspex.Platform; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using AG = Android.Graphics; |
||||
|
|
||||
|
namespace Perspex.Android.Platform.CanvasPlatform |
||||
|
{ |
||||
|
public class WindowImpl : View, |
||||
|
//GridLayout, //may be GridLayout can be better if int he future we want to embed native views or controls
|
||||
|
IWindowImpl, IPlatformHandle, IAndroidCanvasView, IAndroidViewSupportRender, IDisposable |
||||
|
{ |
||||
|
private IPointUnitService _pointService = PointUnitService.Instance; |
||||
|
|
||||
|
public ViewDrawType DrawType { get; } |
||||
|
|
||||
|
private AndroidViewRenderHelper<WindowImpl> _renderHelper; |
||||
|
protected AndroidKeyboardEventsHelper<WindowImpl> _keyboardHelper; |
||||
|
protected AndroidTouchEventsHelper<WindowImpl> _touchHelper; |
||||
|
|
||||
|
private bool _handleEvents; |
||||
|
|
||||
|
public bool HandleEvents |
||||
|
{ |
||||
|
get { return _handleEvents; } |
||||
|
set |
||||
|
{ |
||||
|
_handleEvents = value; |
||||
|
_keyboardHelper.HandleEvents = _handleEvents; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
internal static bool _debug = false; |
||||
|
|
||||
|
public WindowImpl(Context context) : base(context) |
||||
|
{ |
||||
|
DrawType = AndroidPlatform.Instance.DefaultViewDrawType == ViewDrawType.SurfaceViewCanvasOnDraw ? ViewDrawType.CanvasOnDraw : AndroidPlatform.Instance.DefaultViewDrawType; |
||||
|
|
||||
|
_keyboardHelper = new AndroidKeyboardEventsHelper<WindowImpl>(this); |
||||
|
_touchHelper = new AndroidTouchEventsHelper<WindowImpl>(this, () => InputRoot, GetPerspexPointFromEvent); |
||||
|
_renderHelper = AndroidViewRenderHelper<WindowImpl>.Create(this, DrawType); |
||||
|
|
||||
|
//LayerType = LayerType.Hardware;
|
||||
|
//SetLayerType(LayerType.Hardware, null);
|
||||
|
|
||||
|
ClientSize = MaxClientSize; |
||||
|
Resized = size => Invalidate(new Rect(size)); |
||||
|
this.Init(); |
||||
|
} |
||||
|
|
||||
|
public WindowImpl() : this(PerspexLocator.Current.GetService<IAndroidActivity>().Activity) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
protected virtual void Init() |
||||
|
{ |
||||
|
HandleEvents = false; |
||||
|
|
||||
|
//if the background is not transparent nothing is visible!!!
|
||||
|
//this is the case when inherit from GridLayout, not from View
|
||||
|
//this.Background = new AG.Drawables.ColorDrawable(AG.Color.Transparent);
|
||||
|
} |
||||
|
|
||||
|
public Rect Bounds { get; set; } |
||||
|
|
||||
|
public IInputRoot InputRoot { get; private set; } |
||||
|
IntPtr IPlatformHandle.Handle => Handle; |
||||
|
string IPlatformHandle.HandleDescriptor => "Perspex View"; |
||||
|
|
||||
|
public void Resize(double width, double height) |
||||
|
{ |
||||
|
if (ClientSize == new Size(width, height)) return; |
||||
|
ClientSize = new Size(width, height); |
||||
|
Resized(ClientSize); |
||||
|
} |
||||
|
|
||||
|
public Size ClientSize { get; set; } |
||||
|
IPlatformHandle ITopLevelImpl.Handle => this; |
||||
|
Action ITopLevelImpl.Activated { get; set; } |
||||
|
public Action Closed { get; set; } |
||||
|
public Action Deactivated { get; set; } |
||||
|
public Action<RawInputEventArgs> Input { get; set; } |
||||
|
public Action<Rect> Paint { get; set; } |
||||
|
public Action<Size> Resized { get; set; } |
||||
|
|
||||
|
public void Activate() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public void SetInputRoot(IInputRoot inputRoot) |
||||
|
{ |
||||
|
InputRoot = inputRoot; |
||||
|
} |
||||
|
|
||||
|
public Point PointToScreen(Point point) |
||||
|
{ |
||||
|
return point; |
||||
|
} |
||||
|
|
||||
|
public void SetCursor(IPlatformHandle cursor) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public Size MaxClientSize => _pointService.NativeToPerspex(new Size(Resources.DisplayMetrics.WidthPixels, Resources.DisplayMetrics.HeightPixels)); |
||||
|
|
||||
|
public View View => this; |
||||
|
|
||||
|
public void SetTitle(string title) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
void ITopLevelImpl.Show() |
||||
|
{ |
||||
|
this.Visibility = ViewStates.Visible; |
||||
|
} |
||||
|
|
||||
|
public IDisposable ShowDialog() |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
public void Hide() |
||||
|
{ |
||||
|
this.Visibility = ViewStates.Invisible; |
||||
|
} |
||||
|
|
||||
|
public virtual Point GetPerspexPointFromEvent(MotionEvent e) => _pointService.NativeToPerspex(new Point(e.GetX(), e.GetY())); |
||||
|
|
||||
|
public override bool DispatchTouchEvent(MotionEvent e) |
||||
|
{ |
||||
|
bool callBase; |
||||
|
bool? result = _touchHelper.DispatchTouchEvent(e, out callBase); |
||||
|
bool baseResult = callBase ? base.DispatchTouchEvent(e) : false; |
||||
|
|
||||
|
return result != null ? result.Value : baseResult; |
||||
|
} |
||||
|
|
||||
|
public override bool DispatchKeyEvent(KeyEvent e) |
||||
|
{ |
||||
|
bool callBase; |
||||
|
bool? res = _keyboardHelper.DispatchKeyEvent(e, out callBase); |
||||
|
bool baseResult = callBase ? base.DispatchKeyEvent(e) : false; |
||||
|
|
||||
|
return res != null ? res.Value : baseResult; |
||||
|
} |
||||
|
|
||||
|
public void Invalidate(Rect rect) |
||||
|
{ |
||||
|
this._renderHelper.Invalidate(rect); |
||||
|
} |
||||
|
|
||||
|
public AG.Canvas Canvas { get; private set; } |
||||
|
|
||||
|
public Dictionary<object, IDisposable> VisualCaches |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return _renderHelper.VisualCaches; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected TimeSpan _onDrawDuration; |
||||
|
|
||||
|
private DateTime _beginDraw; |
||||
|
|
||||
|
public virtual void PreRender(AG.Canvas canvas, Rect rect) |
||||
|
{ |
||||
|
_beginDraw = DateTime.Now; |
||||
|
} |
||||
|
|
||||
|
public virtual void Render(AG.Canvas canvas, Rect rect) |
||||
|
{ |
||||
|
Canvas = canvas; |
||||
|
Paint?.Invoke(rect); |
||||
|
} |
||||
|
|
||||
|
public virtual void PostRender(AG.Canvas canvas, Rect rect) |
||||
|
{ |
||||
|
_renderHelper.DrawDebugInfoToCanvas(canvas); |
||||
|
_touchHelper.DrawLastMousePoint(canvas); |
||||
|
} |
||||
|
|
||||
|
protected override void OnDraw(AG.Canvas canvas) |
||||
|
{ |
||||
|
_renderHelper.OnDraw(canvas); |
||||
|
} |
||||
|
|
||||
|
void IDisposable.Dispose() |
||||
|
{ |
||||
|
_renderHelper.Dispose(); |
||||
|
_touchHelper.Dispose(); |
||||
|
_keyboardHelper.Dispose(); |
||||
|
} |
||||
|
|
||||
|
protected override void OnSizeChanged(int w, int h, int oldw, int oldh) |
||||
|
{ |
||||
|
base.OnSizeChanged(w, h, oldw, oldh); |
||||
|
Resize(MaxClientSize.Width, MaxClientSize.Height); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,47 @@ |
|||||
|
using Android.Content; |
||||
|
using Android.Runtime; |
||||
|
using Android.Views; |
||||
|
using Perspex.Input.Platform; |
||||
|
using Perspex.Platform; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Perspex.Android.Platform |
||||
|
{ |
||||
|
internal class ClipboardImpl : IClipboard |
||||
|
{ |
||||
|
private Context context = (PerspexLocator.Current.GetService<IWindowImpl>() as View).Context; |
||||
|
|
||||
|
private ClipboardManager ClipboardManager |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return this.context.GetSystemService(Context.ClipboardService).JavaCast<ClipboardManager>(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public Task<string> GetTextAsync() |
||||
|
{ |
||||
|
if (ClipboardManager.HasPrimaryClip) |
||||
|
{ |
||||
|
return Task.FromResult<string>(ClipboardManager.PrimaryClip.GetItemAt(0).Text); |
||||
|
} |
||||
|
|
||||
|
return Task.FromResult<string>(null); |
||||
|
} |
||||
|
|
||||
|
public Task SetTextAsync(string text) |
||||
|
{ |
||||
|
ClipData clip = ClipData.NewPlainText("text", text); |
||||
|
ClipboardManager.PrimaryClip = clip; |
||||
|
|
||||
|
return Task.FromResult<object>(null); |
||||
|
} |
||||
|
|
||||
|
public Task ClearAsync() |
||||
|
{ |
||||
|
ClipboardManager.PrimaryClip = null; |
||||
|
|
||||
|
return Task.FromResult<object>(null); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,217 @@ |
|||||
|
using System; |
||||
|
using Perspex.Input; |
||||
|
//using Android.InputMethodServices;
|
||||
|
using System.Collections.Generic; |
||||
|
using Android.Views; |
||||
|
|
||||
|
namespace Perspex.Android.Platform.Input |
||||
|
{ |
||||
|
public class AndroidKeyboardDevice : KeyboardDevice, IKeyboardDevice { |
||||
|
private static readonly Dictionary<Keycode, Key> KeyDic = new Dictionary<Keycode, Key> |
||||
|
{ |
||||
|
// { Keycode.Cancel?, Key.Cancel },
|
||||
|
{ Keycode.Del, Key.Back }, |
||||
|
{ Keycode.Tab, Key.Tab }, |
||||
|
// { Keycode.Linefeed?, Key.LineFeed },
|
||||
|
{ Keycode.Clear, Key.Clear }, |
||||
|
{ Keycode.Enter, Key.Return }, |
||||
|
{ Keycode.MediaPause, Key.Pause }, |
||||
|
//{ Keycode.?, Key.CapsLock }
|
||||
|
//{ Keycode.?, Key.HangulMode }
|
||||
|
//{ Keycode.?, Key.JunjaMode }
|
||||
|
//{ Keycode.?, Key.FinalMode }
|
||||
|
//{ Keycode.?, Key.KanjiMode }
|
||||
|
{ Keycode.Escape, Key.Escape }, |
||||
|
//{ Keycode.?, Key.ImeConvert }
|
||||
|
//{ Keycode.?, Key.ImeNonConvert }
|
||||
|
//{ Keycode.?, Key.ImeAccept }
|
||||
|
//{ Keycode.?, Key.ImeModeChange }
|
||||
|
{ Keycode.Space, Key.Space }, |
||||
|
{ Keycode.PageUp, Key.Prior }, |
||||
|
{ Keycode.PageDown, Key.PageDown }, |
||||
|
// { Keycode.end?, Key.End },
|
||||
|
{ Keycode.Home, Key.Home }, |
||||
|
{ Keycode.DpadLeft, Key.Left }, |
||||
|
{ Keycode.DpadUp, Key.Up }, |
||||
|
{ Keycode.DpadRight, Key.Right }, |
||||
|
{ Keycode.DpadDown, Key.Down }, |
||||
|
// { Keycode.ButtonSelect?, Key.Select },
|
||||
|
// { Keycode.print?, Key.Print },
|
||||
|
//{ Keycode.execute?, Key.Execute },
|
||||
|
// { Keycode.snap, Key.Snapshot }
|
||||
|
{ Keycode.Insert, Key.Insert }, |
||||
|
{ Keycode.ForwardDel, Key.Delete }, |
||||
|
//{ Keycode.help, Key.Help },
|
||||
|
//{ Keycode.?, Key.D0 }
|
||||
|
//{ Keycode.?, Key.D1 }
|
||||
|
//{ Keycode.?, Key.D2 }
|
||||
|
//{ Keycode.?, Key.D3 }
|
||||
|
//{ Keycode.?, Key.D4 }
|
||||
|
//{ Keycode.?, Key.D5 }
|
||||
|
//{ Keycode.?, Key.D6 }
|
||||
|
//{ Keycode.?, Key.D7 }
|
||||
|
//{ Keycode.?, Key.D8 }
|
||||
|
//{ Keycode.?, Key.D9 }
|
||||
|
{ Keycode.A, Key.A }, |
||||
|
{ Keycode.B, Key.B }, |
||||
|
{ Keycode.C, Key.C }, |
||||
|
{ Keycode.D, Key.D }, |
||||
|
{ Keycode.E, Key.E }, |
||||
|
{ Keycode.F, Key.F }, |
||||
|
{ Keycode.G, Key.G }, |
||||
|
{ Keycode.H, Key.H }, |
||||
|
{ Keycode.I, Key.I }, |
||||
|
{ Keycode.J, Key.J }, |
||||
|
{ Keycode.K, Key.K }, |
||||
|
{ Keycode.L, Key.L }, |
||||
|
{ Keycode.M, Key.M }, |
||||
|
{ Keycode.N, Key.N }, |
||||
|
{ Keycode.O, Key.O }, |
||||
|
{ Keycode.P, Key.P }, |
||||
|
{ Keycode.Q, Key.Q }, |
||||
|
{ Keycode.R, Key.R }, |
||||
|
{ Keycode.S, Key.S }, |
||||
|
{ Keycode.T, Key.T }, |
||||
|
{ Keycode.U, Key.U }, |
||||
|
{ Keycode.V, Key.V }, |
||||
|
{ Keycode.W, Key.W }, |
||||
|
{ Keycode.X, Key.X }, |
||||
|
{ Keycode.Y, Key.Y }, |
||||
|
{ Keycode.Z, Key.Z }, |
||||
|
//{ Keycode.a, Key.A },
|
||||
|
//{ Keycode.b, Key.B },
|
||||
|
//{ Keycode.c, Key.C },
|
||||
|
//{ Keycode.d, Key.D },
|
||||
|
//{ Keycode.e, Key.E },
|
||||
|
//{ Keycode.f, Key.F },
|
||||
|
//{ Keycode.g, Key.G },
|
||||
|
//{ Keycode.h, Key.H },
|
||||
|
//{ Keycode.i, Key.I },
|
||||
|
//{ Keycode.j, Key.J },
|
||||
|
//{ Keycode.k, Key.K },
|
||||
|
//{ Keycode.l, Key.L },
|
||||
|
//{ Keycode.m, Key.M },
|
||||
|
//{ Keycode.n, Key.N },
|
||||
|
//{ Keycode.o, Key.O },
|
||||
|
//{ Keycode.p, Key.P },
|
||||
|
//{ Keycode.q, Key.Q },
|
||||
|
//{ Keycode.r, Key.R },
|
||||
|
//{ Keycode.s, Key.S },
|
||||
|
//{ Keycode.t, Key.T },
|
||||
|
//{ Keycode.u, Key.U },
|
||||
|
//{ Keycode.v, Key.V },
|
||||
|
//{ Keycode.w, Key.W },
|
||||
|
//{ Keycode.x, Key.X },
|
||||
|
//{ Keycode.y, Key.Y },
|
||||
|
//{ Keycode.z, Key.Z },
|
||||
|
//{ Keycode.?, Key.LWin }
|
||||
|
//{ Keycode.?, Key.RWin }
|
||||
|
//{ Keycode.?, Key.Apps }
|
||||
|
//{ Keycode.?, Key.Sleep }
|
||||
|
//{ Keycode.?, Key.NumPad0 }
|
||||
|
//{ Keycode.?, Key.NumPad1 }
|
||||
|
//{ Keycode.?, Key.NumPad2 }
|
||||
|
//{ Keycode.?, Key.NumPad3 }
|
||||
|
//{ Keycode.?, Key.NumPad4 }
|
||||
|
//{ Keycode.?, Key.NumPad5 }
|
||||
|
//{ Keycode.?, Key.NumPad6 }
|
||||
|
//{ Keycode.?, Key.NumPad7 }
|
||||
|
//{ Keycode.?, Key.NumPad8 }
|
||||
|
//{ Keycode.?, Key.NumPad9 }
|
||||
|
{ Keycode.NumpadMultiply, Key.Multiply }, |
||||
|
{ Keycode.NumpadAdd, Key.Add }, |
||||
|
{ Keycode.NumpadComma, Key.Separator }, |
||||
|
{ Keycode.NumpadSubtract, Key.Subtract }, |
||||
|
//{ Keycode.numpaddecimal?, Key.Decimal }
|
||||
|
{ Keycode.NumpadDivide, Key.Divide }, |
||||
|
{ Keycode.F1, Key.F1 }, |
||||
|
{ Keycode.F2, Key.F2 }, |
||||
|
{ Keycode.F3, Key.F3 }, |
||||
|
{ Keycode.F4, Key.F4 }, |
||||
|
{ Keycode.F5, Key.F5 }, |
||||
|
{ Keycode.F6, Key.F6 }, |
||||
|
{ Keycode.F7, Key.F7 }, |
||||
|
{ Keycode.F8, Key.F8 }, |
||||
|
{ Keycode.F9, Key.F9 }, |
||||
|
{ Keycode.F10, Key.F10 }, |
||||
|
{ Keycode.F11, Key.F11 }, |
||||
|
{ Keycode.F12, Key.F12 }, |
||||
|
//{ Keycode.f13, Key.F13 },
|
||||
|
//{ Keycode.F14, Key.F14 },
|
||||
|
//{ Keycode.L5, Key.F15 },
|
||||
|
//{ Keycode.F16, Key.F16 },
|
||||
|
//{ Keycode.F17, Key.F17 },
|
||||
|
//{ Keycode.L8, Key.F18 },
|
||||
|
//{ Keycode.L9, Key.F19 },
|
||||
|
//{ Keycode.L10, Key.F20 },
|
||||
|
//{ Keycode.R1, Key.F21 },
|
||||
|
//{ Keycode.R2, Key.F22 },
|
||||
|
//{ Keycode.F23, Key.F23 },
|
||||
|
//{ Keycode.R4, Key.F24 },
|
||||
|
// { Keycode.numpad, Key.NumLock }
|
||||
|
{ Keycode.ScrollLock, Key.Scroll }, |
||||
|
{ Keycode.ShiftLeft, Key.LeftShift }, |
||||
|
//{ Keycode.?, Key.RightShift }
|
||||
|
//{ Keycode.?, Key.LeftCtrl }
|
||||
|
//{ Keycode.?, Key.RightCtrl }
|
||||
|
//{ Keycode.?, Key.LeftAlt }
|
||||
|
//{ Keycode.?, Key.RightAlt }
|
||||
|
//{ Keycode.?, Key.BrowserBack }
|
||||
|
//{ Keycode.?, Key.BrowserForward }
|
||||
|
//{ Keycode.?, Key.BrowserRefresh }
|
||||
|
//{ Keycode.?, Key.BrowserStop }
|
||||
|
//{ Keycode.?, Key.BrowserSearch }
|
||||
|
//{ Keycode.?, Key.BrowserFavorites }
|
||||
|
//{ Keycode.?, Key.BrowserHome }
|
||||
|
//{ Keycode.?, Key.VolumeMute }
|
||||
|
//{ Keycode.?, Key.VolumeDown }
|
||||
|
//{ Keycode.?, Key.VolumeUp }
|
||||
|
//{ Keycode.?, Key.MediaNextTrack }
|
||||
|
//{ Keycode.?, Key.MediaPreviousTrack }
|
||||
|
//{ Keycode.?, Key.MediaStop }
|
||||
|
//{ Keycode.?, Key.MediaPlayPause }
|
||||
|
//{ Keycode.?, Key.LaunchMail }
|
||||
|
//{ Keycode.?, Key.SelectMedia }
|
||||
|
//{ Keycode.?, Key.LaunchApplication1 }
|
||||
|
//{ Keycode.?, Key.LaunchApplication2 }
|
||||
|
//{ Keycode.?, Key.OemSemicolon }
|
||||
|
//{ Keycode.?, Key.OemPlus }
|
||||
|
//{ Keycode.?, Key.OemComma }
|
||||
|
//{ Keycode.?, Key.OemMinus }
|
||||
|
//{ Keycode.?, Key.OemPeriod }
|
||||
|
//{ Keycode.?, Key.Oem2 }
|
||||
|
//{ Keycode.?, Key.OemTilde }
|
||||
|
//{ Keycode.?, Key.AbntC1 }
|
||||
|
//{ Keycode.?, Key.AbntC2 }
|
||||
|
//{ Keycode.?, Key.Oem4 }
|
||||
|
//{ Keycode.?, Key.OemPipe }
|
||||
|
//{ Keycode.?, Key.OemCloseBrackets }
|
||||
|
//{ Keycode.?, Key.Oem7 }
|
||||
|
//{ Keycode.?, Key.Oem8 }
|
||||
|
//{ Keycode.?, Key.Oem102 }
|
||||
|
//{ Keycode.?, Key.ImeProcessed }
|
||||
|
//{ Keycode.?, Key.System }
|
||||
|
//{ Keycode.?, Key.OemAttn }
|
||||
|
//{ Keycode.?, Key.OemFinish }
|
||||
|
//{ Keycode.?, Key.DbeHiragana }
|
||||
|
//{ Keycode.?, Key.OemAuto }
|
||||
|
//{ Keycode.?, Key.DbeDbcsChar }
|
||||
|
//{ Keycode.?, Key.OemBackTab }
|
||||
|
//{ Keycode.?, Key.Attn }
|
||||
|
//{ Keycode.?, Key.DbeEnterWordRegisterMode }
|
||||
|
//{ Keycode.?, Key.DbeEnterImeConfigureMode }
|
||||
|
//{ Keycode.?, Key.EraseEof }
|
||||
|
//{ Keycode.?, Key.Play }
|
||||
|
//{ Keycode.?, Key.Zoom }
|
||||
|
//{ Keycode.?, Key.NoName }
|
||||
|
//{ Keycode.?, Key.DbeEnterDialogConversionMode }
|
||||
|
//{ Keycode.?, Key.OemClear }
|
||||
|
//{ Keycode.?, Key.DeadCharProcessed }
|
||||
|
}; |
||||
|
|
||||
|
internal static Key ConvertKey(Keycode key) { |
||||
|
Key result; |
||||
|
return KeyDic.TryGetValue(key, out result) ? result : Key.None; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
using Perspex.Input; |
||||
|
|
||||
|
namespace Perspex.Android.Platform.Input |
||||
|
{ |
||||
|
public class AndroidMouseDevice : MouseDevice |
||||
|
{ |
||||
|
public AndroidMouseDevice() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,50 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace Perspex.Android.Platform.Specific |
||||
|
{ |
||||
|
public class CommonUITimer : IDisposable |
||||
|
{ |
||||
|
private System.Timers.Timer _timer; |
||||
|
private TimeSpan _interval; |
||||
|
private Dictionary<Action, DateTime> _actions = new Dictionary<Action, DateTime>(); |
||||
|
|
||||
|
public CommonUITimer(TimeSpan interval) |
||||
|
{ |
||||
|
_timer = new System.Timers.Timer(interval.TotalMilliseconds); |
||||
|
_interval = interval; |
||||
|
_timer.Elapsed += _timer_Elapsed; |
||||
|
_timer.Start(); |
||||
|
} |
||||
|
|
||||
|
private void _timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) |
||||
|
{ |
||||
|
DateTime now = DateTime.Now; |
||||
|
foreach (var kvp in _actions) |
||||
|
{ |
||||
|
if ((now - kvp.Value) >= _interval) |
||||
|
{ |
||||
|
kvp.Key(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void AddAction(Action action) |
||||
|
{ |
||||
|
_actions[action] = DateTime.Now; |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
_timer.Elapsed -= _timer_Elapsed; |
||||
|
_timer?.Stop(); |
||||
|
} |
||||
|
|
||||
|
public void RemoveAction(Action action) |
||||
|
{ |
||||
|
_actions.Remove(action); |
||||
|
} |
||||
|
|
||||
|
public int ActiveActionsCount => _actions.Count; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,145 @@ |
|||||
|
using Android.Content; |
||||
|
using Android.Runtime; |
||||
|
using Android.Views; |
||||
|
using Android.Views.InputMethods; |
||||
|
using Perspex.Android.Platform.Input; |
||||
|
using Perspex.Controls; |
||||
|
using Perspex.Input; |
||||
|
using Perspex.Input.Raw; |
||||
|
using Perspex.Platform; |
||||
|
using System; |
||||
|
using System.ComponentModel; |
||||
|
|
||||
|
namespace Perspex.Android.Platform.Specific.Helpers |
||||
|
{ |
||||
|
public class AndroidKeyboardEventsHelper<TView> : IDisposable where TView : View, IWindowImpl, IAndroidView |
||||
|
{ |
||||
|
private TView _view; |
||||
|
private IInputElement _lastFocusedElement; |
||||
|
|
||||
|
public bool HandleEvents { get; set; } |
||||
|
|
||||
|
public AndroidKeyboardEventsHelper(TView view) |
||||
|
{ |
||||
|
this._view = view; |
||||
|
HandleEvents = true; |
||||
|
} |
||||
|
|
||||
|
public bool? DispatchKeyEvent(KeyEvent e, out bool callBase) |
||||
|
{ |
||||
|
if (!HandleEvents) |
||||
|
{ |
||||
|
callBase = true; |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
return DispatchKeyEventInternal(e, out callBase); |
||||
|
} |
||||
|
|
||||
|
private bool? DispatchKeyEventInternal(KeyEvent e, out bool callBase) |
||||
|
{ |
||||
|
if (e.Action == KeyEventActions.Multiple) |
||||
|
{ |
||||
|
callBase = true; |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
var rawKeyEvent = new RawKeyEventArgs( |
||||
|
AndroidKeyboardDevice.Instance, |
||||
|
Convert.ToUInt32(e.EventTime), |
||||
|
e.Action == KeyEventActions.Down ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp, |
||||
|
AndroidKeyboardDevice.ConvertKey(e.KeyCode), GetModifierKeys(e)); |
||||
|
_view.Input(rawKeyEvent); |
||||
|
|
||||
|
if (e.Action == KeyEventActions.Down && e.UnicodeChar >= 32) |
||||
|
{ |
||||
|
var rawTextEvent = new RawTextInputEventArgs( |
||||
|
AndroidKeyboardDevice.Instance, |
||||
|
Convert.ToUInt32(e.EventTime), |
||||
|
Convert.ToChar(e.UnicodeChar).ToString() |
||||
|
); |
||||
|
_view.Input(rawTextEvent); |
||||
|
} |
||||
|
|
||||
|
if (e.Action == KeyEventActions.Up) |
||||
|
{ |
||||
|
//nothing to do here more call base no need of more events
|
||||
|
callBase = true; |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
callBase = false; |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
private static InputModifiers GetModifierKeys(KeyEvent e) |
||||
|
{ |
||||
|
var rv = InputModifiers.None; |
||||
|
|
||||
|
if (e.IsCtrlPressed) rv |= InputModifiers.Control; |
||||
|
if (e.IsShiftPressed) rv |= InputModifiers.Shift; |
||||
|
|
||||
|
return rv; |
||||
|
} |
||||
|
|
||||
|
private bool NeedsKeyboard(IInputElement element) |
||||
|
{ |
||||
|
//may be some other elements
|
||||
|
return element is TextBox; |
||||
|
} |
||||
|
|
||||
|
private void TryShowHideKeyboard(IInputElement element, bool value) |
||||
|
{ |
||||
|
var input = _view.Context.GetSystemService(Context.InputMethodService).JavaCast<InputMethodManager>(); |
||||
|
|
||||
|
if (value) |
||||
|
{ |
||||
|
//show keyboard
|
||||
|
//may be in the future different keyboards support e.g. normal, only digits etc.
|
||||
|
//Android.Text.InputTypes
|
||||
|
input.ToggleSoftInput(ShowFlags.Forced, HideSoftInputFlags.ImplicitOnly); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
//hide keyboard
|
||||
|
input.HideSoftInputFromWindow(_view.WindowToken, HideSoftInputFlags.None); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void UpdateKeyboardState(IInputElement element) |
||||
|
{ |
||||
|
var focusedElement = element; |
||||
|
bool oldValue = NeedsKeyboard(_lastFocusedElement); |
||||
|
bool newValue = NeedsKeyboard(focusedElement); |
||||
|
|
||||
|
if (newValue != oldValue || newValue) |
||||
|
{ |
||||
|
TryShowHideKeyboard(focusedElement, newValue); |
||||
|
} |
||||
|
|
||||
|
_lastFocusedElement = element; |
||||
|
} |
||||
|
|
||||
|
public void ActivateAutoShowKeybord() |
||||
|
{ |
||||
|
var kbDevice = (KeyboardDevice.Instance as INotifyPropertyChanged); |
||||
|
|
||||
|
//just in case we've called more than once the method
|
||||
|
kbDevice.PropertyChanged -= KeyboardDevice_PropertyChanged; |
||||
|
kbDevice.PropertyChanged += KeyboardDevice_PropertyChanged; |
||||
|
} |
||||
|
|
||||
|
private void KeyboardDevice_PropertyChanged(object sender, PropertyChangedEventArgs e) |
||||
|
{ |
||||
|
if (e.PropertyName == nameof(KeyboardDevice.FocusedElement)) |
||||
|
{ |
||||
|
UpdateKeyboardState(KeyboardDevice.Instance.FocusedElement); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
HandleEvents = false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,155 @@ |
|||||
|
using Android.Graphics; |
||||
|
using Android.Views; |
||||
|
using Perspex.Android.Platform.CanvasPlatform; |
||||
|
using Perspex.Input; |
||||
|
using Perspex.Input.Raw; |
||||
|
using Perspex.Platform; |
||||
|
using System; |
||||
|
|
||||
|
namespace Perspex.Android.Platform.Specific.Helpers |
||||
|
{ |
||||
|
public class AndroidTouchEventsHelper<TView> : IDisposable where TView : View, IWindowImpl, IAndroidView |
||||
|
{ |
||||
|
private TView _view; |
||||
|
public bool HandleEvents { get; set; } |
||||
|
|
||||
|
public AndroidTouchEventsHelper(TView view, Func<IInputRoot> getInputRoot, Func<MotionEvent, Point> getPointfunc) |
||||
|
{ |
||||
|
this._view = view; |
||||
|
HandleEvents = true; |
||||
|
_getPointFunc = getPointfunc; |
||||
|
_getInputRoot = getInputRoot; |
||||
|
} |
||||
|
|
||||
|
private DateTime _lastTouchMoveEventTime = DateTime.Now; |
||||
|
private Point? _lastTouchMovePoint; |
||||
|
private Func<MotionEvent, Point> _getPointFunc; |
||||
|
private Func<IInputRoot> _getInputRoot; |
||||
|
private Point _point; |
||||
|
|
||||
|
public bool? DispatchTouchEvent(MotionEvent e, out bool callBase) |
||||
|
{ |
||||
|
if (!HandleEvents) |
||||
|
{ |
||||
|
callBase = true; |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
RawMouseEventType? mouseEventType = null; |
||||
|
var eventTime = DateTime.Now; |
||||
|
//Basic touch support
|
||||
|
switch (e.Action) |
||||
|
{ |
||||
|
case MotionEventActions.Move: |
||||
|
//may be bot flood the evnt system with too many event especially on not so powerfull mobile devices
|
||||
|
if ((eventTime - _lastTouchMoveEventTime).TotalMilliseconds > 10) |
||||
|
{ |
||||
|
mouseEventType = RawMouseEventType.Move; |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
case MotionEventActions.Down: |
||||
|
mouseEventType = RawMouseEventType.LeftButtonDown; |
||||
|
|
||||
|
break; |
||||
|
|
||||
|
case MotionEventActions.Up: |
||||
|
mouseEventType = RawMouseEventType.LeftButtonUp; |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
if (mouseEventType != null) |
||||
|
{ |
||||
|
//if point is in view otherwise it's possible perspex not to find the proper window to dispatch the event
|
||||
|
_point = _getPointFunc(e); |
||||
|
|
||||
|
double x = _view.GetX(); |
||||
|
double y = _view.GetY(); |
||||
|
double r = x + _view.Width; |
||||
|
double b = y + _view.Height; |
||||
|
|
||||
|
if (x <= _point.X && r >= _point.X && y <= _point.Y && b >= _point.Y) |
||||
|
{ |
||||
|
var inputRoot = _getInputRoot(); |
||||
|
var mouseDevice = MouseDevice.Instance; |
||||
|
|
||||
|
//in order the controls to work in a predictable way
|
||||
|
//we need to generate mouse move before first mouse down event
|
||||
|
//as this is the way buttons are working every time
|
||||
|
//otherwise there is a problem sometimes
|
||||
|
if (mouseEventType == RawMouseEventType.LeftButtonDown) |
||||
|
{ |
||||
|
var me = new RawMouseEventArgs(mouseDevice, (uint)eventTime.Ticks, inputRoot, |
||||
|
RawMouseEventType.Move, _point, InputModifiers.None); |
||||
|
_view.Input(me); |
||||
|
} |
||||
|
|
||||
|
var mouseEvent = new RawMouseEventArgs(mouseDevice, (uint)eventTime.Ticks, inputRoot, |
||||
|
mouseEventType.Value, _point, InputModifiers.LeftMouseButton); |
||||
|
_view.Input(mouseEvent); |
||||
|
|
||||
|
if (e.Action == MotionEventActions.Move && mouseDevice.Captured == null) |
||||
|
{ |
||||
|
if (_lastTouchMovePoint != null) |
||||
|
{ |
||||
|
//raise mouse scroll event so the scrollers
|
||||
|
//are moving with the cursor
|
||||
|
double vectorX = _point.X - _lastTouchMovePoint.Value.X; |
||||
|
double vectorY = _point.Y - _lastTouchMovePoint.Value.Y; |
||||
|
//based on test correction of 0.02 is working perfect
|
||||
|
double correction = 0.02; |
||||
|
var mouseWheelEvent = new RawMouseWheelEventArgs( |
||||
|
mouseDevice, |
||||
|
(uint)eventTime.Ticks, |
||||
|
inputRoot, |
||||
|
_point, |
||||
|
new Vector(vectorX * correction, vectorY * correction), InputModifiers.LeftMouseButton); |
||||
|
_view.Input(mouseWheelEvent); |
||||
|
} |
||||
|
_lastTouchMovePoint = _point; |
||||
|
_lastTouchMoveEventTime = eventTime; |
||||
|
} |
||||
|
else if (e.Action == MotionEventActions.Down) |
||||
|
{ |
||||
|
_lastTouchMovePoint = _point; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
_lastTouchMovePoint = null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
callBase = true; |
||||
|
//if return false events for move and up are not received!!!
|
||||
|
return e.Action != MotionEventActions.Up; |
||||
|
} |
||||
|
|
||||
|
private Paint _paint; |
||||
|
|
||||
|
public void DrawLastMousePoint(Canvas canvas) |
||||
|
{ |
||||
|
if (!AndroidPlatform.Instance.DrawDebugInfo) return; |
||||
|
|
||||
|
if (_paint == null) |
||||
|
{ |
||||
|
_paint = new Paint() { Color = Color.Red, }; |
||||
|
_paint.SetStyle(Paint.Style.Fill); |
||||
|
} |
||||
|
|
||||
|
if(AndroidPlatform.Instance.DefaultViewDrawType != ViewDrawType.Skia) |
||||
|
{ |
||||
|
canvas.DrawCircle(PointUnitService.Instance.PerspexToNativeXF(_point.X), |
||||
|
PointUnitService.Instance.PerspexToNativeYF(_point.Y), |
||||
|
(float)AndroidPlatform.Instance.DoubleClickSize.Width, |
||||
|
_paint); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
HandleEvents = false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
|
||||
|
using Android.App; |
||||
|
using Android.Content; |
||||
|
using Android.OS; |
||||
|
using Android.Runtime; |
||||
|
using Android.Views; |
||||
|
using Android.Widget; |
||||
|
|
||||
|
namespace Perspex.Android.Platform.Specific.Helpers |
||||
|
{ |
||||
|
public enum ViewDrawType |
||||
|
{ |
||||
|
Skia, |
||||
|
CanvasOnDraw, |
||||
|
BitmapOnPreDraw, |
||||
|
BitmapBackgroundRender, |
||||
|
SurfaceViewCanvasOnDraw |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
using Android.App; |
||||
|
using Android.Views; |
||||
|
|
||||
|
namespace Perspex.Android.Platform.Specific |
||||
|
{ |
||||
|
public interface IAndroidActivity |
||||
|
{ |
||||
|
Activity Activity { get; } |
||||
|
|
||||
|
IAndroidView ContentView { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
using Android.Graphics; |
||||
|
using Android.Views; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace Perspex.Android.Platform.Specific |
||||
|
{ |
||||
|
public interface IAndroidView |
||||
|
{ |
||||
|
|
||||
|
|
||||
|
View View { get; } |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
@ -0,0 +1,54 @@ |
|||||
|
using Android.App; |
||||
|
using Android.OS; |
||||
|
using Android.Views; |
||||
|
using Android.Widget; |
||||
|
|
||||
|
namespace Perspex.Android.Platform.Specific |
||||
|
{ |
||||
|
public class PerspexActivity : Activity, IAndroidActivity |
||||
|
{ |
||||
|
private IAndroidView _contentView; |
||||
|
|
||||
|
public Activity Activity => this; |
||||
|
|
||||
|
public IAndroidView ContentView |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return this._contentView; |
||||
|
} |
||||
|
|
||||
|
set |
||||
|
{ |
||||
|
this._contentView = value; |
||||
|
var fl = new FrameLayout(this); |
||||
|
fl.AddView(this._contentView.View); |
||||
|
//this.SetContentView(value.View);
|
||||
|
this.SetContentView(fl); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected override void OnCreate(Bundle savedInstanceState) |
||||
|
{ |
||||
|
PerspexLocator.CurrentMutable.Bind<IAndroidActivity>().ToConstant(this); |
||||
|
RequestWindowFeature(WindowFeatures.NoTitle); |
||||
|
base.OnCreate(savedInstanceState); |
||||
|
} |
||||
|
|
||||
|
public override void SetContentView(View view) |
||||
|
{ |
||||
|
base.SetContentView(view); |
||||
|
TakeKeyEvents(true); |
||||
|
} |
||||
|
|
||||
|
public override bool DispatchKeyEvent(KeyEvent e) |
||||
|
{ |
||||
|
if (_contentView != null) |
||||
|
{ |
||||
|
_contentView.View.DispatchKeyEvent(e); |
||||
|
} |
||||
|
|
||||
|
return base.DispatchKeyEvent(e); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
using System.Reflection; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
// General Information about an assembly is controlled through the following
|
||||
|
// set of attributes. Change these attribute values to modify the information
|
||||
|
// associated with an assembly.
|
||||
|
|
||||
|
[assembly: AssemblyTitle("Perspex.Android")] |
||||
|
[assembly: AssemblyDescription("")] |
||||
|
[assembly: AssemblyConfiguration("")] |
||||
|
[assembly: AssemblyCompany("")] |
||||
|
[assembly: AssemblyProduct("Perspex.Android")] |
||||
|
[assembly: AssemblyCopyright("Copyright © 2015")] |
||||
|
[assembly: AssemblyTrademark("")] |
||||
|
[assembly: AssemblyCulture("")] |
||||
|
[assembly: ComVisible(false)] |
||||
|
|
||||
|
// Version information for an assembly consists of the following four values:
|
||||
|
//
|
||||
|
// Major Version
|
||||
|
// Minor Version
|
||||
|
// Build Number
|
||||
|
// Revision
|
||||
|
//
|
||||
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
|
// by using the '*' as shown below:
|
||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||
|
|
||||
|
[assembly: AssemblyVersion("1.0.0.0")] |
||||
|
[assembly: AssemblyFileVersion("1.0.0.0")] |
||||
@ -0,0 +1,50 @@ |
|||||
|
Images, layout descriptions, binary blobs and string dictionaries can be included |
||||
|
in your application as resource files. Various Android APIs are designed to |
||||
|
operate on the resource IDs instead of dealing with images, strings or binary blobs |
||||
|
directly. |
||||
|
|
||||
|
For example, a sample Android app that contains a user interface layout (main.xml), |
||||
|
an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) |
||||
|
would keep its resources in the "Resources" directory of the application: |
||||
|
|
||||
|
Resources/ |
||||
|
drawable-hdpi/ |
||||
|
icon.png |
||||
|
|
||||
|
drawable-ldpi/ |
||||
|
icon.png |
||||
|
|
||||
|
drawable-mdpi/ |
||||
|
icon.png |
||||
|
|
||||
|
layout/ |
||||
|
main.xml |
||||
|
|
||||
|
values/ |
||||
|
strings.xml |
||||
|
|
||||
|
In order to get the build system to recognize Android resources, set the build action to |
||||
|
"AndroidResource". The native Android APIs do not operate directly with filenames, but |
||||
|
instead operate on resource IDs. When you compile an Android application that uses resources, |
||||
|
the build system will package the resources for distribution and generate a class called |
||||
|
"Resource" that contains the tokens for each one of the resources included. For example, |
||||
|
for the above Resources layout, this is what the Resource class would expose: |
||||
|
|
||||
|
public class Resource { |
||||
|
public class drawable { |
||||
|
public const int icon = 0x123; |
||||
|
} |
||||
|
|
||||
|
public class layout { |
||||
|
public const int main = 0x456; |
||||
|
} |
||||
|
|
||||
|
public class strings { |
||||
|
public const int first_string = 0xabc; |
||||
|
public const int second_string = 0xbcd; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
You would then use R.drawable.icon to reference the drawable/icon.png file, or Resource.layout.main |
||||
|
to reference the layout/main.xml file, or Resource.strings.first_string to reference the first |
||||
|
string in the dictionary file values/strings.xml. |
||||
@ -0,0 +1,60 @@ |
|||||
|
#pragma warning disable 1591
|
||||
|
//------------------------------------------------------------------------------
|
||||
|
// <auto-generated>
|
||||
|
// This code was generated by a tool.
|
||||
|
// Runtime Version:4.0.30319.42000
|
||||
|
//
|
||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
|
// the code is regenerated.
|
||||
|
// </auto-generated>
|
||||
|
//------------------------------------------------------------------------------
|
||||
|
|
||||
|
[assembly: global::Android.Runtime.ResourceDesignerAttribute("Perspex.Android.Resource", IsApplication=false)] |
||||
|
|
||||
|
namespace Perspex.Android |
||||
|
{ |
||||
|
|
||||
|
|
||||
|
[System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] |
||||
|
public partial class Resource |
||||
|
{ |
||||
|
|
||||
|
static Resource() |
||||
|
{ |
||||
|
global::Android.Runtime.ResourceIdManager.UpdateIdValues(); |
||||
|
} |
||||
|
|
||||
|
public partial class Attribute |
||||
|
{ |
||||
|
|
||||
|
static Attribute() |
||||
|
{ |
||||
|
global::Android.Runtime.ResourceIdManager.UpdateIdValues(); |
||||
|
} |
||||
|
|
||||
|
private Attribute() |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public partial class String |
||||
|
{ |
||||
|
|
||||
|
// aapt resource value: 0x7f020001
|
||||
|
public static int ApplicationName = 2130837505; |
||||
|
|
||||
|
// aapt resource value: 0x7f020000
|
||||
|
public static int Hello = 2130837504; |
||||
|
|
||||
|
static String() |
||||
|
{ |
||||
|
global::Android.Runtime.ResourceIdManager.UpdateIdValues(); |
||||
|
} |
||||
|
|
||||
|
private String() |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
#pragma warning restore 1591
|
||||
@ -0,0 +1,6 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
|
||||
|
<resources> |
||||
|
<string name="Hello">Hello World, Click Me!</string> |
||||
|
<string name="ApplicationName">$projectname$</string> |
||||
|
</resources> |
||||
@ -0,0 +1,16 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Perspex.Controls; |
||||
|
using Perspex.Controls.Platform; |
||||
|
using Perspex.Platform; |
||||
|
|
||||
|
namespace Perspex.Android |
||||
|
{ |
||||
|
internal class SystemDialogImpl : ISystemDialogImpl |
||||
|
{ |
||||
|
public Task<string[]> ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent) |
||||
|
{ |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
|
||||
|
<packages> |
||||
|
<package id="Rx-Core" version="2.2.5" targetFramework="monoandroid51" /> |
||||
|
<package id="Rx-Interfaces" version="2.2.5" targetFramework="monoandroid51" /> |
||||
|
<package id="Rx-Linq" version="2.2.5" targetFramework="monoandroid51" /> |
||||
|
<package id="Rx-Main" version="2.2.5" targetFramework="monoandroid51" /> |
||||
|
<package id="Rx-PlatformServices" version="2.2.5" targetFramework="monoandroid51" /> |
||||
|
</packages> |
||||
@ -0,0 +1,372 @@ |
|||||
|
using Perspex.Android; |
||||
|
using Perspex.Controls; |
||||
|
using Perspex.Controls.Shapes; |
||||
|
using Perspex.Interactivity; |
||||
|
using Perspex.Layout; |
||||
|
using Perspex.Media; |
||||
|
using Perspex.Media.Imaging; |
||||
|
using Perspex.Platform; |
||||
|
using Perspex.Themes.Default; |
||||
|
using System; |
||||
|
|
||||
|
namespace Perspex.AndroidTestApplication |
||||
|
{ |
||||
|
public class App : Application |
||||
|
{ |
||||
|
public App() |
||||
|
{ |
||||
|
RegisterServices(); |
||||
|
InitializePlatform(); |
||||
|
|
||||
|
Styles = new DefaultTheme(); |
||||
|
} |
||||
|
|
||||
|
protected void InitializePlatform() |
||||
|
{ |
||||
|
AndroidPlatform.Initialize(); |
||||
|
//not needed already default assembly is set to Activity that inherits PerspexActivity
|
||||
|
//AndroidPlatform.Instance.SetDefaultAssetAssembly(typeof(App).Assembly);
|
||||
|
} |
||||
|
|
||||
|
public Window BuildSimpleTextUI() |
||||
|
{ |
||||
|
var window = new Window |
||||
|
{ |
||||
|
Title = "Perspex Test Application", |
||||
|
Background = Brushes.Green, |
||||
|
Content = new Grid |
||||
|
{ |
||||
|
VerticalAlignment = VerticalAlignment.Top, |
||||
|
HorizontalAlignment = HorizontalAlignment.Left, |
||||
|
Margin = new Thickness(10, 10, 0, 0), |
||||
|
Children = new Controls.Controls |
||||
|
{ |
||||
|
new TextBlock |
||||
|
{ |
||||
|
Foreground = Brushes.GhostWhite, |
||||
|
Background = Brushes.BlueViolet, |
||||
|
VerticalAlignment = VerticalAlignment.Top, |
||||
|
HorizontalAlignment = HorizontalAlignment.Left, |
||||
|
Text = |
||||
|
"Hello! This is a test of text wrapping and me actually getting something to render for the first time in life. Im and happy to say this was by no means all me. I want to thank everyone who helped me this far.", |
||||
|
FontSize = 80, |
||||
|
Margin = new Thickness(10), |
||||
|
TextWrapping = TextWrapping.Wrap |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
return window; |
||||
|
} |
||||
|
|
||||
|
public Window BuildStackPanelUI() |
||||
|
{ |
||||
|
var window = new Window |
||||
|
{ |
||||
|
Title = "Perspex Test Application", |
||||
|
Background = Brushes.Green, |
||||
|
Content = new Grid |
||||
|
{ |
||||
|
VerticalAlignment = VerticalAlignment.Top, |
||||
|
HorizontalAlignment = HorizontalAlignment.Left, |
||||
|
Margin = new Thickness(10, 10, 0, 0), |
||||
|
Children = new Controls.Controls |
||||
|
{ |
||||
|
new StackPanel |
||||
|
{ |
||||
|
Orientation = Orientation.Horizontal, |
||||
|
[Grid.RowProperty] = 1, |
||||
|
[Grid.ColumnSpanProperty] = 2, |
||||
|
// Background = SolidColorBrush.Parse("#000000"),
|
||||
|
Children = new Controls.Controls |
||||
|
{ |
||||
|
new Button |
||||
|
{ |
||||
|
Content = "Button 1", |
||||
|
Width = 100, |
||||
|
Foreground = Brushes.Blue, |
||||
|
Margin = new Thickness(5) |
||||
|
}, |
||||
|
new Button |
||||
|
{ |
||||
|
Content = "Button 2", |
||||
|
Width = 100, |
||||
|
Margin = new Thickness(5) |
||||
|
}, |
||||
|
new Button |
||||
|
{ |
||||
|
Content = "Button 3", |
||||
|
Width = 100, |
||||
|
Margin = new Thickness(5) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
return window; |
||||
|
} |
||||
|
|
||||
|
public Window BuildGridWithSomeButtonsAndStuff() |
||||
|
{ |
||||
|
var buttonToClick = new Button |
||||
|
{ |
||||
|
Content = "Tap Me", |
||||
|
Width = 200, |
||||
|
Foreground = Brushes.White, |
||||
|
Margin = new Thickness(5), |
||||
|
Background = Brushes.Blue, |
||||
|
FontSize = 50 |
||||
|
}; |
||||
|
|
||||
|
var clickCount = 0; |
||||
|
buttonToClick.Click += (object sender, RoutedEventArgs e) => |
||||
|
{ |
||||
|
Console.WriteLine("You clicked it :)"); |
||||
|
buttonToClick.Content = $"Tap x{clickCount}"; |
||||
|
clickCount++; |
||||
|
}; |
||||
|
|
||||
|
var window = new Window |
||||
|
{ |
||||
|
Title = "Perspex Test Application", |
||||
|
Background = Brushes.DarkTurquoise, |
||||
|
Content = new Grid |
||||
|
{ |
||||
|
Margin = new Thickness(0, 0, 0, 0), // skip the status bar area on iOS
|
||||
|
ColumnDefinitions = new ColumnDefinitions |
||||
|
{ |
||||
|
new ColumnDefinition(1, GridUnitType.Star), |
||||
|
new ColumnDefinition(1, GridUnitType.Star) |
||||
|
}, |
||||
|
RowDefinitions = new RowDefinitions |
||||
|
{ |
||||
|
new RowDefinition(80, GridUnitType.Pixel), |
||||
|
new RowDefinition(160, GridUnitType.Pixel), |
||||
|
new RowDefinition(1, GridUnitType.Star), |
||||
|
new RowDefinition(1, GridUnitType.Star) |
||||
|
}, |
||||
|
Children = new Controls.Controls |
||||
|
{ |
||||
|
new TextBlock |
||||
|
{ |
||||
|
[Grid.RowProperty] = 0, |
||||
|
[Grid.ColumnSpanProperty] = 2, |
||||
|
Text = "Welcome to Perspex on Android !!!", |
||||
|
Foreground = Brushes.SteelBlue, |
||||
|
Background = Brushes.White, |
||||
|
FontSize = 50, |
||||
|
Margin = new Thickness(5, 7, 5, 5), |
||||
|
TextAlignment = TextAlignment.Left |
||||
|
}, |
||||
|
////
|
||||
|
new StackPanel |
||||
|
{ |
||||
|
Orientation = Orientation.Horizontal, |
||||
|
[Grid.RowProperty] = 1, |
||||
|
[Grid.ColumnSpanProperty] = 2, |
||||
|
Background = Brush.Parse("#000000"), |
||||
|
Children = new Controls.Controls |
||||
|
{ |
||||
|
buttonToClick, |
||||
|
new Button |
||||
|
{ |
||||
|
Content = "Button 2", |
||||
|
Width = 250, |
||||
|
FontSize = 50, |
||||
|
Margin = new Thickness(5) |
||||
|
}, |
||||
|
new Button |
||||
|
{ |
||||
|
Content = "Button 3", |
||||
|
Width = 250, |
||||
|
FontSize = 50, |
||||
|
Margin = new Thickness(5) |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
////
|
||||
|
new Image |
||||
|
{ |
||||
|
[Grid.RowProperty] = 2, |
||||
|
[Grid.ColumnProperty] = 0, |
||||
|
Margin = new Thickness(20), |
||||
|
// Source = new Bitmap("github_icon.png"),
|
||||
|
Opacity = 0.4 |
||||
|
}, |
||||
|
////
|
||||
|
new Ellipse |
||||
|
{ |
||||
|
[Grid.RowProperty] = 3, |
||||
|
[Grid.ColumnProperty] = 0, |
||||
|
Margin = new Thickness(20), |
||||
|
Fill = Brushes.Blue |
||||
|
}, |
||||
|
////
|
||||
|
new TextBlock |
||||
|
{ |
||||
|
[Grid.RowProperty] = 2, |
||||
|
[Grid.ColumnProperty] = 1, |
||||
|
Text = |
||||
|
"How does this look? Is this text going to wrap and then look nice within the bounds of this widget? If not I will be extremely disappointed!\n\nWill we start a new paragraph here? If not there will be hell to pay!!!!", |
||||
|
Foreground = Brushes.Purple, |
||||
|
Background = Brushes.Transparent, |
||||
|
FontSize = 28, |
||||
|
Margin = new Thickness(10, 30, 10, 10) |
||||
|
}, |
||||
|
new Path |
||||
|
{ |
||||
|
Data = |
||||
|
StreamGeometry.Parse( |
||||
|
"M 50,50 l 15,0 l 5,-15 l 5,15 l 15,0 l -10,10 l 4,15 l -15,-9 l -15,9 l 7,-15 Z"), |
||||
|
[Grid.RowProperty] = 3, |
||||
|
[Grid.ColumnProperty] = 1, |
||||
|
Margin = new Thickness(20), |
||||
|
Fill = Brushes.White, |
||||
|
Stroke = Brushes.Blue, |
||||
|
StrokeThickness = 4 |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
return window; |
||||
|
} |
||||
|
|
||||
|
public Window BuildGridWithSomeButtonsAndStuff2() |
||||
|
{ |
||||
|
var buttonToClick = new Button |
||||
|
{ |
||||
|
Content = "Tap Me", |
||||
|
Width = 200, |
||||
|
Foreground = Brushes.White, |
||||
|
Margin = new Thickness(5), |
||||
|
Background = Brushes.Blue, |
||||
|
FontSize = 50 |
||||
|
}; |
||||
|
|
||||
|
var clickCount = 0; |
||||
|
buttonToClick.Click += (object sender, RoutedEventArgs e) => |
||||
|
{ |
||||
|
Console.WriteLine("You clicked it :)"); |
||||
|
buttonToClick.Content = $"Tap x{clickCount}"; |
||||
|
clickCount++; |
||||
|
}; |
||||
|
|
||||
|
var window = new Window |
||||
|
{ |
||||
|
Title = "Perspex Test Application", |
||||
|
Background = Brushes.DarkTurquoise, |
||||
|
Content = new Grid |
||||
|
{ |
||||
|
Margin = new Thickness(0, 50, 0, 0), // skip the status bar area on iOS
|
||||
|
ColumnDefinitions = new ColumnDefinitions |
||||
|
{ |
||||
|
new ColumnDefinition(1, GridUnitType.Star), |
||||
|
new ColumnDefinition(1, GridUnitType.Star) |
||||
|
}, |
||||
|
RowDefinitions = new RowDefinitions |
||||
|
{ |
||||
|
new RowDefinition(80, GridUnitType.Pixel), |
||||
|
new RowDefinition(160, GridUnitType.Pixel), |
||||
|
new RowDefinition(1, GridUnitType.Star), |
||||
|
new RowDefinition(1, GridUnitType.Star) |
||||
|
}, |
||||
|
Children = new Controls.Controls |
||||
|
{ |
||||
|
new TextBox |
||||
|
{ |
||||
|
[Grid.RowProperty] = 0, |
||||
|
[Grid.ColumnSpanProperty] = 2, |
||||
|
Text = "Welcome to Perspex on Android !!!", |
||||
|
Foreground = Brushes.SteelBlue, |
||||
|
Background = Brushes.White, |
||||
|
FontSize = 50, |
||||
|
//Margin = new Thickness(5, 7, 5, 5),
|
||||
|
Margin = new Thickness(5, 7, 5, 5), |
||||
|
//VerticalAlignment = VerticalAlignment.Bottom
|
||||
|
//TextAlignment = TextAlignment.Left
|
||||
|
}, |
||||
|
////
|
||||
|
new StackPanel |
||||
|
{ |
||||
|
Orientation = Orientation.Horizontal, |
||||
|
[Grid.RowProperty] = 1, |
||||
|
[Grid.ColumnSpanProperty] = 2, |
||||
|
Background = Brush.Parse("#000000"), |
||||
|
Children = new Controls.Controls |
||||
|
{ |
||||
|
buttonToClick, |
||||
|
new Button |
||||
|
{ |
||||
|
Content = "Button 2", |
||||
|
Width = 250, |
||||
|
FontSize = 50, |
||||
|
Margin = new Thickness(5) |
||||
|
}, |
||||
|
new Button |
||||
|
{ |
||||
|
Content = "Button 3", |
||||
|
Width = 250, |
||||
|
FontSize = 50, |
||||
|
Margin = new Thickness(5) |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
////
|
||||
|
new Image |
||||
|
{ |
||||
|
[Grid.RowProperty] = 2, |
||||
|
[Grid.ColumnProperty] = 0, |
||||
|
Margin = new Thickness(20), |
||||
|
//Stretch=Stretch.Uniform,
|
||||
|
Width=400, |
||||
|
Height=400, |
||||
|
// Source = new Bitmap("github_icon.png"),
|
||||
|
Source = new Bitmap(PerspexLocator.Current.GetService<IAssetLoader>().Open(new Uri("res://component/Perspex Test Application/Perspex.AndroidTestApplication.github_icon.png"))), |
||||
|
Opacity = 0.4 |
||||
|
}, |
||||
|
////
|
||||
|
new Ellipse |
||||
|
{ |
||||
|
[Grid.RowProperty] = 3, |
||||
|
[Grid.ColumnProperty] = 0, |
||||
|
Margin = new Thickness(20), |
||||
|
Fill = Brushes.Blue, |
||||
|
//Stroke = Brushes.Red,
|
||||
|
//StrokeThickness = 4
|
||||
|
}, |
||||
|
////
|
||||
|
new TextBlock |
||||
|
{ |
||||
|
[Grid.RowProperty] = 2, |
||||
|
[Grid.ColumnProperty] = 1, |
||||
|
Text = |
||||
|
"How does this look? Is this text going to wrap and then look nice within the bounds of this widget? If not I will be extremely disappointed!\n\nWill we start a new paragraph here? If not there will be hell to pay!!!!", |
||||
|
Foreground = Brushes.Purple, |
||||
|
Background = Brushes.Transparent, |
||||
|
FontSize = 28, |
||||
|
Margin = new Thickness(10, 30, 10, 10) |
||||
|
}, |
||||
|
new Path |
||||
|
{ |
||||
|
Data = |
||||
|
StreamGeometry.Parse( |
||||
|
"M 50,50 l 15,0 l 5,-15 l 5,15 l 15,0 l -10,10 l 4,15 l -15,-9 l -15,9 l 7,-15 Z"), |
||||
|
[Grid.RowProperty] = 3, |
||||
|
[Grid.ColumnProperty] = 1, |
||||
|
Margin = new Thickness(20), |
||||
|
Fill = Brushes.White, |
||||
|
Stroke = Brushes.Blue, |
||||
|
StrokeThickness = 4 |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
return window; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
Any raw assets you want to be deployed with your application can be placed in |
||||
|
this directory (and child directories) and given a Build Action of "AndroidAsset". |
||||
|
|
||||
|
These files will be deployed with you package and will be accessible using Android's |
||||
|
AssetManager, like this: |
||||
|
|
||||
|
public class ReadAsset : Activity |
||||
|
{ |
||||
|
protected override void OnCreate (Bundle bundle) |
||||
|
{ |
||||
|
base.OnCreate (bundle); |
||||
|
|
||||
|
InputStream input = Assets.Open ("my_asset.txt"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Additionally, some Android functions will automatically load asset files: |
||||
|
|
||||
|
Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf"); |
||||
@ -0,0 +1,4 @@ |
|||||
|
<GettingStarted> |
||||
|
<LocalContent>GS\Android\CS\AndroidApp\GettingStarted.html</LocalContent> |
||||
|
<EmbeddedNavigation>false</EmbeddedNavigation> |
||||
|
</GettingStarted> |
||||
@ -0,0 +1,67 @@ |
|||||
|
using Android.App; |
||||
|
using Android.Content.PM; |
||||
|
using Android.OS; |
||||
|
using Perspex.Android; |
||||
|
using Perspex.Android.Platform.Specific; |
||||
|
using Perspex.Android.Platform.Specific.Helpers; |
||||
|
using Perspex.Controls; |
||||
|
using Perspex.Controls.Presenters; |
||||
|
using Perspex.Controls.Primitives; |
||||
|
using Perspex.Controls.Templates; |
||||
|
using Perspex.Markup.Xaml.Data; |
||||
|
using Perspex.Platform; |
||||
|
using Perspex.Styling; |
||||
|
using ReactiveUI; |
||||
|
using System; |
||||
|
using System.Reactive.Linq; |
||||
|
using System.Windows.Input; |
||||
|
|
||||
|
namespace Perspex.AndroidTestApplication |
||||
|
{ |
||||
|
[Activity(Label = "Main", |
||||
|
MainLauncher = true, |
||||
|
Icon = "@drawable/icon", |
||||
|
LaunchMode = LaunchMode.SingleInstance/*, |
||||
|
ScreenOrientation = ScreenOrientation.Landscape*/)] |
||||
|
public class MainBaseActivity : PerspexActivity |
||||
|
{ |
||||
|
protected override void OnCreate(Bundle savedInstanceState) |
||||
|
{ |
||||
|
base.OnCreate(savedInstanceState); |
||||
|
|
||||
|
//set some parameters to android platform
|
||||
|
AndroidPlatform.Instance.DrawDebugInfo = true; |
||||
|
//AndroidPlatform.Instance.DefaultViewDrawType = ViewDrawType.BitmapOnPreDraw;
|
||||
|
AndroidPlatform.Instance.DefaultViewDrawType = ViewDrawType.CanvasOnDraw; |
||||
|
//AndroidPlatform.Instance.DefaultViewDrawType = ViewDrawType.BitmapBackgroundRender;
|
||||
|
//AndroidPlatform.Instance.DefaultViewDrawType = ViewDrawType.SurfaceViewCanvasOnDraw;
|
||||
|
AndroidPlatform.Instance.DefaultPointUnit = Android.Platform.CanvasPlatform.PointUnit.DP; |
||||
|
//AndroidPlatform.Instance.DefaultPointUnit = Android.Platform.CanvasPlatform.PointUnit.Pixel;
|
||||
|
AndroidPlatform.Instance.OverrideAnimateFramesPerSecond = 16; |
||||
|
|
||||
|
App app; |
||||
|
if (Perspex.Application.Current != null) |
||||
|
app = (App)Perspex.Application.Current; |
||||
|
else |
||||
|
app = new App(); |
||||
|
|
||||
|
var window = TestUI.TestUIBuilder.BuildTestUI(); |
||||
|
//var window = BuildPlatforSetup();
|
||||
|
|
||||
|
window.Show(); |
||||
|
app.Run(window); |
||||
|
} |
||||
|
public static void StartAppFromSetup() |
||||
|
{ |
||||
|
PerspexLocator.Current.GetService<IWindowImpl>().Dispose(); |
||||
|
|
||||
|
AndroidPlatform.Instance.RegisterViewDrawType(); |
||||
|
AndroidPlatform.Instance.RegisterViewPointUnits(); |
||||
|
|
||||
|
var window = TestUI.TestUIBuilder.BuildTestUI(); |
||||
|
|
||||
|
window.Show(); |
||||
|
Perspex.Application.Current.Run(window); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,215 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
<PropertyGroup> |
||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> |
||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> |
||||
|
<ProductVersion>8.0.30703</ProductVersion> |
||||
|
<SchemaVersion>2.0</SchemaVersion> |
||||
|
<ProjectGuid>{FF69B927-C545-49AE-8E16-3D14D621AA12}</ProjectGuid> |
||||
|
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> |
||||
|
<OutputType>Library</OutputType> |
||||
|
<AppDesignerFolder>Properties</AppDesignerFolder> |
||||
|
<RootNamespace>Perspex.AndroidTestApplication</RootNamespace> |
||||
|
<AssemblyName>Perspex Test Application</AssemblyName> |
||||
|
<FileAlignment>512</FileAlignment> |
||||
|
<AndroidApplication>true</AndroidApplication> |
||||
|
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile> |
||||
|
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies> |
||||
|
<AndroidUseLatestPlatformSdk>False</AndroidUseLatestPlatformSdk> |
||||
|
<TargetFrameworkVersion>v4.4</TargetFrameworkVersion> |
||||
|
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest> |
||||
|
</PropertyGroup> |
||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> |
||||
|
<DebugSymbols>true</DebugSymbols> |
||||
|
<DebugType>full</DebugType> |
||||
|
<Optimize>false</Optimize> |
||||
|
<OutputPath>bin\Debug\</OutputPath> |
||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants> |
||||
|
<ErrorReport>prompt</ErrorReport> |
||||
|
<WarningLevel>4</WarningLevel> |
||||
|
<AndroidUseSharedRuntime>True</AndroidUseSharedRuntime> |
||||
|
<AndroidLinkMode>None</AndroidLinkMode> |
||||
|
<AndroidLinkSkip /> |
||||
|
<EmbedAssembliesIntoApk>False</EmbedAssembliesIntoApk> |
||||
|
<BundleAssemblies>False</BundleAssemblies> |
||||
|
<AndroidCreatePackagePerAbi>False</AndroidCreatePackagePerAbi> |
||||
|
<AndroidSupportedAbis>armeabi,armeabi-v7a,x86</AndroidSupportedAbis> |
||||
|
<AndroidStoreUncompressedFileExtensions /> |
||||
|
<MandroidI18n /> |
||||
|
<Debugger>Xamarin</Debugger> |
||||
|
<AndroidEnableMultiDex>False</AndroidEnableMultiDex> |
||||
|
<DevInstrumentationEnabled>True</DevInstrumentationEnabled> |
||||
|
</PropertyGroup> |
||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> |
||||
|
<DebugType>pdbonly</DebugType> |
||||
|
<Optimize>true</Optimize> |
||||
|
<OutputPath>bin\Release\</OutputPath> |
||||
|
<DefineConstants>TRACE</DefineConstants> |
||||
|
<ErrorReport>prompt</ErrorReport> |
||||
|
<WarningLevel>4</WarningLevel> |
||||
|
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime> |
||||
|
<AndroidLinkMode>SdkOnly</AndroidLinkMode> |
||||
|
<AndroidLinkSkip /> |
||||
|
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk> |
||||
|
<BundleAssemblies>False</BundleAssemblies> |
||||
|
<AndroidCreatePackagePerAbi>False</AndroidCreatePackagePerAbi> |
||||
|
<AndroidSupportedAbis>armeabi,armeabi-v7a,x86</AndroidSupportedAbis> |
||||
|
<AndroidStoreUncompressedFileExtensions /> |
||||
|
<MandroidI18n /> |
||||
|
<Debugger>Xamarin</Debugger> |
||||
|
<AotAssemblies>False</AotAssemblies> |
||||
|
<EnableLLVM>False</EnableLLVM> |
||||
|
<AndroidEnableMultiDex>False</AndroidEnableMultiDex> |
||||
|
<EnableProguard>False</EnableProguard> |
||||
|
<DevInstrumentationEnabled>False</DevInstrumentationEnabled> |
||||
|
</PropertyGroup> |
||||
|
<ItemGroup> |
||||
|
<Reference Include="Mono.Android" /> |
||||
|
<Reference Include="mscorlib" /> |
||||
|
<Reference Include="Serilog, Version=1.5.0.0, Culture=neutral, PublicKeyToken=24c2f752a8e58a10, processorArchitecture=MSIL"> |
||||
|
<HintPath>..\..\..\packages\Serilog.1.5.9\lib\portable-net45+win+wpa81+wp80+MonoAndroid10+MonoTouch10\Serilog.dll</HintPath> |
||||
|
<Private>True</Private> |
||||
|
</Reference> |
||||
|
<Reference Include="Splat, Version=1.6.2.0, Culture=neutral, processorArchitecture=MSIL"> |
||||
|
<HintPath>..\..\..\packages\Splat.1.6.2\lib\monoandroid\Splat.dll</HintPath> |
||||
|
<Private>True</Private> |
||||
|
</Reference> |
||||
|
<Reference Include="Sprache, Version=2.0.0.47, Culture=neutral, processorArchitecture=MSIL"> |
||||
|
<HintPath>..\..\..\packages\Sprache.2.0.0.47\lib\portable-net4+netcore45+win8+wp8+sl5+MonoAndroid1+MonoTouch1\Sprache.dll</HintPath> |
||||
|
<Private>True</Private> |
||||
|
</Reference> |
||||
|
<Reference Include="System" /> |
||||
|
<Reference Include="System.Core" /> |
||||
|
<Reference Include="System.IO" /> |
||||
|
<Reference Include="System.Linq.Expressions" /> |
||||
|
<Reference Include="System.ObjectModel" /> |
||||
|
<Reference Include="System.Reactive.Core, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> |
||||
|
<HintPath>..\..\..\packages\Rx-Core.2.2.5\lib\portable-windows8+net45+wp8\System.Reactive.Core.dll</HintPath> |
||||
|
<Private>True</Private> |
||||
|
</Reference> |
||||
|
<Reference Include="System.Reactive.Interfaces, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> |
||||
|
<HintPath>..\..\..\packages\Rx-Interfaces.2.2.5\lib\portable-windows8+net45+wp8\System.Reactive.Interfaces.dll</HintPath> |
||||
|
<Private>True</Private> |
||||
|
</Reference> |
||||
|
<Reference Include="System.Reactive.Linq, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> |
||||
|
<HintPath>..\..\..\packages\Rx-Linq.2.2.5\lib\portable-windows8+net45+wp8\System.Reactive.Linq.dll</HintPath> |
||||
|
<Private>True</Private> |
||||
|
</Reference> |
||||
|
<Reference Include="System.Reactive.PlatformServices, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> |
||||
|
<HintPath>..\..\..\packages\Rx-PlatformServices.2.2.5\lib\portable-windows8+net45+wp8\System.Reactive.PlatformServices.dll</HintPath> |
||||
|
<Private>True</Private> |
||||
|
</Reference> |
||||
|
<Reference Include="System.Runtime" /> |
||||
|
<Reference Include="System.Runtime.Extensions" /> |
||||
|
<Reference Include="System.Xml.Linq" /> |
||||
|
<Reference Include="System.Xml" /> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<Compile Include="App.cs" /> |
||||
|
<Compile Include="MainActivity.cs" /> |
||||
|
<Compile Include="Resources\Resource.Designer.cs" /> |
||||
|
<Compile Include="Properties\AssemblyInfo.cs" /> |
||||
|
<Compile Include="SetupTestUI.cs" /> |
||||
|
<Compile Include="TestUI.cs" /> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<None Include="GettingStarted.Xamarin" /> |
||||
|
<None Include="packages.config" /> |
||||
|
<None Include="Resources\AboutResources.txt" /> |
||||
|
<None Include="Assets\AboutAssets.txt" /> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<AndroidResource Include="Resources\layout\Main.axml" /> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<AndroidResource Include="Resources\values\Strings.xml"> |
||||
|
<SubType>Designer</SubType> |
||||
|
</AndroidResource> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<AndroidResource Include="Resources\drawable\Icon.png" /> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<None Include="Properties\AndroidManifest.xml" /> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\..\..\src\Android\Perspex.Android\Perspex.Android.csproj"> |
||||
|
<Project>{7b92af71-6287-4693-9dcb-bd5b6e927e23}</Project> |
||||
|
<Name>Perspex.Android</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\..\..\src\Markup\Perspex.Markup.Xaml\Perspex.Markup.Xaml.csproj"> |
||||
|
<Project>{3e53a01a-b331-47f3-b828-4a5717e77a24}</Project> |
||||
|
<Name>Perspex.Markup.Xaml</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\..\..\src\Perspex.Animation\Perspex.Animation.csproj"> |
||||
|
<Project>{d211e587-d8bc-45b9-95a4-f297c8fa5200}</Project> |
||||
|
<Name>Perspex.Animation</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\..\..\src\Perspex.Application\Perspex.Application.csproj"> |
||||
|
<Project>{799a7bb5-3c2c-48b6-85a7-406a12c420da}</Project> |
||||
|
<Name>Perspex.Application</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\..\..\src\Perspex.Base\Perspex.Base.csproj"> |
||||
|
<Project>{b09b78d8-9b26-48b0-9149-d64a2f120f3f}</Project> |
||||
|
<Name>Perspex.Base</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\..\..\src\Perspex.Controls\Perspex.Controls.csproj"> |
||||
|
<Project>{d2221c82-4a25-4583-9b43-d791e3f6820c}</Project> |
||||
|
<Name>Perspex.Controls</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\..\..\src\Perspex.Diagnostics\Perspex.Diagnostics.csproj"> |
||||
|
<Project>{7062ae20-5dcc-4442-9645-8195bdece63e}</Project> |
||||
|
<Name>Perspex.Diagnostics</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\..\..\src\Perspex.Input\Perspex.Input.csproj"> |
||||
|
<Project>{62024b2d-53eb-4638-b26b-85eeaa54866e}</Project> |
||||
|
<Name>Perspex.Input</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\..\..\src\Perspex.Interactivity\Perspex.Interactivity.csproj"> |
||||
|
<Project>{6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b}</Project> |
||||
|
<Name>Perspex.Interactivity</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\..\..\src\Perspex.Layout\Perspex.Layout.csproj"> |
||||
|
<Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project> |
||||
|
<Name>Perspex.Layout</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\..\..\src\Perspex.ReactiveUI\Perspex.ReactiveUI.csproj"> |
||||
|
<Project>{6417b24e-49c2-4985-8db2-3ab9d898ec91}</Project> |
||||
|
<Name>Perspex.ReactiveUI</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\..\..\src\Perspex.SceneGraph\Perspex.SceneGraph.csproj"> |
||||
|
<Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project> |
||||
|
<Name>Perspex.SceneGraph</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\..\..\src\Perspex.Styling\Perspex.Styling.csproj"> |
||||
|
<Project>{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}</Project> |
||||
|
<Name>Perspex.Styling</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\..\..\src\Perspex.Themes.Default\Perspex.Themes.Default.csproj"> |
||||
|
<Project>{3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f}</Project> |
||||
|
<Name>Perspex.Themes.Default</Name> |
||||
|
</ProjectReference> |
||||
|
<ProjectReference Include="..\..\Perspex.HtmlRenderer\Perspex.HtmlRenderer.csproj"> |
||||
|
<Project>{5fb2b005-0a7f-4dad-add4-3ed01444e63d}</Project> |
||||
|
<Name>Perspex.HtmlRenderer</Name> |
||||
|
</ProjectReference> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<EmbeddedResource Include="github_icon.png"> |
||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory> |
||||
|
</EmbeddedResource> |
||||
|
</ItemGroup> |
||||
|
<ItemGroup> |
||||
|
<EmbeddedResource Include="pattern.jpg"> |
||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> |
||||
|
</EmbeddedResource> |
||||
|
</ItemGroup> |
||||
|
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" /> |
||||
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. |
||||
|
Other similar extension points exist, see Microsoft.Common.targets. |
||||
|
<Target Name="BeforeBuild"> |
||||
|
</Target> |
||||
|
<Target Name="AfterBuild"> |
||||
|
</Target> |
||||
|
--> |
||||
|
</Project> |
||||
@ -0,0 +1,6 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="Perspex.AndroidTestApplication" android:versionCode="1" android:versionName="1.0" android:installLocation="auto"> |
||||
|
<uses-sdk /> |
||||
|
<application android:label="Perspex.AndroidTestApplication" android:icon="@drawable/Icon" android:hardwareAccelerated="true"></application> |
||||
|
<uses-permission android:name="android.permission.INTERNET" /> |
||||
|
</manifest> |
||||
@ -0,0 +1,30 @@ |
|||||
|
using System.Reflection; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
// General Information about an assembly is controlled through the following
|
||||
|
// set of attributes. Change these attribute values to modify the information
|
||||
|
// associated with an assembly.
|
||||
|
|
||||
|
[assembly: AssemblyTitle("Perspex.AndroidTestApplication")] |
||||
|
[assembly: AssemblyDescription("")] |
||||
|
[assembly: AssemblyConfiguration("")] |
||||
|
[assembly: AssemblyCompany("")] |
||||
|
[assembly: AssemblyProduct("Perspex.AndroidTestApplication")] |
||||
|
[assembly: AssemblyCopyright("Copyright © 2015")] |
||||
|
[assembly: AssemblyTrademark("")] |
||||
|
[assembly: AssemblyCulture("")] |
||||
|
[assembly: ComVisible(false)] |
||||
|
|
||||
|
// Version information for an assembly consists of the following four values:
|
||||
|
//
|
||||
|
// Major Version
|
||||
|
// Minor Version
|
||||
|
// Build Number
|
||||
|
// Revision
|
||||
|
//
|
||||
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
|
// by using the '*' as shown below:
|
||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||
|
|
||||
|
[assembly: AssemblyVersion("1.0.0.0")] |
||||
|
[assembly: AssemblyFileVersion("1.0.0.0")] |
||||
@ -0,0 +1,50 @@ |
|||||
|
Images, layout descriptions, binary blobs and string dictionaries can be included |
||||
|
in your application as resource files. Various Android APIs are designed to |
||||
|
operate on the resource IDs instead of dealing with images, strings or binary blobs |
||||
|
directly. |
||||
|
|
||||
|
For example, a sample Android app that contains a user interface layout (main.xml), |
||||
|
an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) |
||||
|
would keep its resources in the "Resources" directory of the application: |
||||
|
|
||||
|
Resources/ |
||||
|
drawable-hdpi/ |
||||
|
icon.png |
||||
|
|
||||
|
drawable-ldpi/ |
||||
|
icon.png |
||||
|
|
||||
|
drawable-mdpi/ |
||||
|
icon.png |
||||
|
|
||||
|
layout/ |
||||
|
main.xml |
||||
|
|
||||
|
values/ |
||||
|
strings.xml |
||||
|
|
||||
|
In order to get the build system to recognize Android resources, set the build action to |
||||
|
"AndroidResource". The native Android APIs do not operate directly with filenames, but |
||||
|
instead operate on resource IDs. When you compile an Android application that uses resources, |
||||
|
the build system will package the resources for distribution and generate a class called |
||||
|
"Resource" that contains the tokens for each one of the resources included. For example, |
||||
|
for the above Resources layout, this is what the Resource class would expose: |
||||
|
|
||||
|
public class Resource { |
||||
|
public class drawable { |
||||
|
public const int icon = 0x123; |
||||
|
} |
||||
|
|
||||
|
public class layout { |
||||
|
public const int main = 0x456; |
||||
|
} |
||||
|
|
||||
|
public class strings { |
||||
|
public const int first_string = 0xabc; |
||||
|
public const int second_string = 0xbcd; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
You would then use R.drawable.icon to reference the drawable/icon.png file, or Resource.layout.main |
||||
|
to reference the layout/main.xml file, or Resource.strings.first_string to reference the first |
||||
|
string in the dictionary file values/strings.xml. |
||||
@ -0,0 +1,102 @@ |
|||||
|
#pragma warning disable 1591
|
||||
|
//------------------------------------------------------------------------------
|
||||
|
// <auto-generated>
|
||||
|
// This code was generated by a tool.
|
||||
|
// Runtime Version:4.0.30319.42000
|
||||
|
//
|
||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
|
// the code is regenerated.
|
||||
|
// </auto-generated>
|
||||
|
//------------------------------------------------------------------------------
|
||||
|
|
||||
|
[assembly: global::Android.Runtime.ResourceDesignerAttribute("Perspex.AndroidTestApplication.Resource", IsApplication=true)] |
||||
|
|
||||
|
namespace Perspex.AndroidTestApplication |
||||
|
{ |
||||
|
|
||||
|
|
||||
|
[System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] |
||||
|
public partial class Resource |
||||
|
{ |
||||
|
|
||||
|
static Resource() |
||||
|
{ |
||||
|
global::Android.Runtime.ResourceIdManager.UpdateIdValues(); |
||||
|
} |
||||
|
|
||||
|
public static void UpdateIdValues() |
||||
|
{ |
||||
|
global::Perspex.Android.Resource.String.ApplicationName = global::Perspex.AndroidTestApplication.Resource.String.ApplicationName; |
||||
|
global::Perspex.Android.Resource.String.Hello = global::Perspex.AndroidTestApplication.Resource.String.Hello; |
||||
|
global::Splat.Resource.String.library_name = global::Perspex.AndroidTestApplication.Resource.String.library_name; |
||||
|
} |
||||
|
|
||||
|
public partial class Attribute |
||||
|
{ |
||||
|
|
||||
|
static Attribute() |
||||
|
{ |
||||
|
global::Android.Runtime.ResourceIdManager.UpdateIdValues(); |
||||
|
} |
||||
|
|
||||
|
private Attribute() |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public partial class Drawable |
||||
|
{ |
||||
|
|
||||
|
// aapt resource value: 0x7f020000
|
||||
|
public const int Icon = 2130837504; |
||||
|
|
||||
|
static Drawable() |
||||
|
{ |
||||
|
global::Android.Runtime.ResourceIdManager.UpdateIdValues(); |
||||
|
} |
||||
|
|
||||
|
private Drawable() |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public partial class Layout |
||||
|
{ |
||||
|
|
||||
|
// aapt resource value: 0x7f030000
|
||||
|
public const int Main = 2130903040; |
||||
|
|
||||
|
static Layout() |
||||
|
{ |
||||
|
global::Android.Runtime.ResourceIdManager.UpdateIdValues(); |
||||
|
} |
||||
|
|
||||
|
private Layout() |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public partial class String |
||||
|
{ |
||||
|
|
||||
|
// aapt resource value: 0x7f040002
|
||||
|
public const int ApplicationName = 2130968578; |
||||
|
|
||||
|
// aapt resource value: 0x7f040001
|
||||
|
public const int Hello = 2130968577; |
||||
|
|
||||
|
// aapt resource value: 0x7f040000
|
||||
|
public const int library_name = 2130968576; |
||||
|
|
||||
|
static String() |
||||
|
{ |
||||
|
global::Android.Runtime.ResourceIdManager.UpdateIdValues(); |
||||
|
} |
||||
|
|
||||
|
private String() |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
#pragma warning restore 1591
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
@ -0,0 +1,9 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
android:orientation="vertical" |
||||
|
android:layout_width="fill_parent" |
||||
|
android:layout_height="fill_parent"> |
||||
|
<SurfaceView |
||||
|
android:layout_height="fill_parent" |
||||
|
android:layout_width="fill_parent" /> |
||||
|
</LinearLayout> |
||||
@ -0,0 +1,6 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
|
||||
|
<resources> |
||||
|
<string name="Hello">Hello World, Click Me!</string> |
||||
|
<string name="ApplicationName">Perspex.AndroidTestApplication</string> |
||||
|
</resources> |
||||
@ -0,0 +1,162 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Windows.Input; |
||||
|
using ReactiveUI; |
||||
|
using Perspex.Android.Platform.CanvasPlatform; |
||||
|
using Perspex.Android.Platform.Specific.Helpers; |
||||
|
using Perspex.Android; |
||||
|
using Perspex.Styling; |
||||
|
using Perspex.Controls.Primitives; |
||||
|
using Perspex.Controls.Templates; |
||||
|
using Perspex.Controls; |
||||
|
using Perspex.Markup.Xaml.Data; |
||||
|
using Perspex.Controls.Presenters; |
||||
|
|
||||
|
namespace Perspex.AndroidTestApplication |
||||
|
{ |
||||
|
public class SetupTestUI |
||||
|
{ |
||||
|
private static Style HeaderedContentControlStyle() |
||||
|
{ |
||||
|
return new Style(x => x.OfType<HeaderedContentControl>()) |
||||
|
{ |
||||
|
Setters = new[] |
||||
|
{ |
||||
|
new Setter(TemplatedControl.TemplateProperty, new FuncControlTemplate<HeaderedContentControl>(control=> { |
||||
|
return new Border() |
||||
|
{ |
||||
|
[~Border.BackgroundProperty] = control[~TemplatedControl.BackgroundProperty], |
||||
|
Child = new StackPanel() |
||||
|
{ |
||||
|
Children = new Controls.Controls() |
||||
|
{ |
||||
|
new ContentPresenter |
||||
|
{ |
||||
|
Name = "headercontentPresenter", |
||||
|
[~ContentPresenter.ContentProperty] = control[~HeaderedContentControl.HeaderProperty], |
||||
|
}, |
||||
|
new ContentPresenter |
||||
|
{ |
||||
|
Name = "contentPresenter", |
||||
|
[~ContentPresenter.ContentProperty] = control[~ContentControl.ContentProperty], |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
})), |
||||
|
}, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
private Window BuildPlatforSetup() |
||||
|
{ |
||||
|
Perspex.Application.Current.Styles.Add(HeaderedContentControlStyle()); |
||||
|
var vm = new PlatformSetupViewModel(); |
||||
|
vm.PointUnit = AndroidPlatform.Instance.DefaultPointUnit.ToString(); |
||||
|
vm.ViewDrawType = AndroidPlatform.Instance.DefaultViewDrawType.ToString(); |
||||
|
var lbPointUnit = new ListBox() |
||||
|
{ |
||||
|
Name = "PointUnit", |
||||
|
Items = vm.PointUnits, |
||||
|
[ListBox.SelectedItemProperty] = vm.PointUnit |
||||
|
}; |
||||
|
var lbViewDrawType = new ListBox() |
||||
|
{ |
||||
|
Name = "ViewDrawType", |
||||
|
Items = vm.ViewDrawTypes, |
||||
|
[ListBox.SelectedItemProperty] = vm.ViewDrawType |
||||
|
}; |
||||
|
var window = new Window() |
||||
|
{ |
||||
|
DataContext = vm, |
||||
|
Content = new Grid() |
||||
|
{ |
||||
|
Margin = new Thickness(20, 100, 0, 0), |
||||
|
RowDefinitions = new RowDefinitions() { |
||||
|
new RowDefinition() { Height = GridLength.Auto }, |
||||
|
new RowDefinition() |
||||
|
}, |
||||
|
Children = new Controls.Controls() |
||||
|
{ |
||||
|
new Button() |
||||
|
{ |
||||
|
[Grid.RowProperty] = 1, |
||||
|
HorizontalAlignment = Layout.HorizontalAlignment.Center, |
||||
|
VerticalAlignment = Layout.VerticalAlignment.Center, |
||||
|
Content = "Start application", |
||||
|
Command = vm.Command |
||||
|
}, |
||||
|
new StackPanel() |
||||
|
{ |
||||
|
[Grid.RowProperty] = 0, |
||||
|
Orientation = Orientation.Horizontal, |
||||
|
Gap=10, |
||||
|
Children= new Controls.Controls |
||||
|
{ |
||||
|
new HeaderedContentControl() {Content = lbPointUnit, Header="Point Units Type" }, |
||||
|
new HeaderedContentControl() { Content = lbViewDrawType, Header ="Draw View type" }, |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
var b = new Binding(); |
||||
|
b.SourcePropertyPath = "PointUnit"; |
||||
|
b.Mode = BindingMode.TwoWay; |
||||
|
b.Bind(lbPointUnit, ListBox.SelectedItemProperty); |
||||
|
b = new Binding(); |
||||
|
b.SourcePropertyPath = "ViewDrawType"; |
||||
|
b.Mode = BindingMode.TwoWay; |
||||
|
b.Bind(lbViewDrawType, ListBox.SelectedItemProperty); |
||||
|
|
||||
|
return window; |
||||
|
} |
||||
|
|
||||
|
public class PlatformSetupViewModel : ReactiveObject |
||||
|
{ |
||||
|
public PlatformSetupViewModel() |
||||
|
{ |
||||
|
ViewDrawTypes = Enum.GetNames(typeof(ViewDrawType)); |
||||
|
PointUnits = Enum.GetNames(typeof(PointUnit)); |
||||
|
var cmd = ReactiveCommand.Create(); |
||||
|
|
||||
|
Command = cmd; |
||||
|
cmd.Subscribe(onNext: o => |
||||
|
{ |
||||
|
var pu = (PointUnit)Enum.Parse(typeof(PointUnit), PointUnit); |
||||
|
var vdt = (ViewDrawType)Enum.Parse(typeof(ViewDrawType), ViewDrawType); |
||||
|
AndroidPlatform.Instance.DefaultPointUnit = pu; |
||||
|
AndroidPlatform.Instance.DefaultViewDrawType = vdt; |
||||
|
MainBaseActivity.StartAppFromSetup(); |
||||
|
//restart app ;
|
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public string[] ViewDrawTypes { get; set; } |
||||
|
public string[] PointUnits { get; set; } |
||||
|
|
||||
|
private string _viewDrawType; |
||||
|
|
||||
|
public string ViewDrawType |
||||
|
{ |
||||
|
get { return _viewDrawType; } |
||||
|
set { this.RaiseAndSetIfChanged(ref _viewDrawType, value); } |
||||
|
} |
||||
|
|
||||
|
private string _pointUnit; |
||||
|
|
||||
|
public string PointUnit |
||||
|
{ |
||||
|
get { return _pointUnit; } |
||||
|
set { this.RaiseAndSetIfChanged(ref _pointUnit, value); } |
||||
|
} |
||||
|
|
||||
|
public ICommand Command { get; set; } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
File diff suppressed because it is too large
|
After Width: | Height: | Size: 17 KiB |
@ -0,0 +1,12 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
|
||||
|
<packages> |
||||
|
<package id="Rx-Core" version="2.2.5" targetFramework="monoandroid51" /> |
||||
|
<package id="Rx-Interfaces" version="2.2.5" targetFramework="monoandroid51" /> |
||||
|
<package id="Rx-Linq" version="2.2.5" targetFramework="monoandroid51" /> |
||||
|
<package id="Rx-Main" version="2.2.5" targetFramework="monoandroid51" /> |
||||
|
<package id="Rx-PlatformServices" version="2.2.5" targetFramework="monoandroid51" /> |
||||
|
<package id="Serilog" version="1.5.9" targetFramework="monoandroid51" /> |
||||
|
<package id="Splat" version="1.6.2" targetFramework="monoandroid51" /> |
||||
|
<package id="Sprache" version="2.0.0.47" targetFramework="monoandroid51" /> |
||||
|
</packages> |
||||
|
After Width: | Height: | Size: 133 KiB |
Loading…
Reference in new issue