diff --git a/src/Tizen.UIExtensions.Common/Animation/Animation.cs b/src/Tizen.UIExtensions.Common/Animation/Animation.cs new file mode 100644 index 0000000..ea348ed --- /dev/null +++ b/src/Tizen.UIExtensions.Common/Animation/Animation.cs @@ -0,0 +1,144 @@ +// +// Tweener.cs +// +// Author: +// Jason Smith +// +// Copyright (c) 2012 Xamarin Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Tizen.UIExtensions.Common.Internal +{ + public class Animation : IEnumerable + { + readonly List _children; + readonly Easing _easing; + readonly Action? _finished; + readonly Action _step; + double _beginAt; + double _finishAt; + bool _finishedTriggered; + + public Animation() + { + _children = new List(); + _easing = Easing.Linear; + _step = f => { }; + } + + public Animation(Action callback, double start = 0.0f, double end = 1.0f, Easing? easing = null, Action? finished = null) + { + _children = new List(); + _easing = easing ?? Easing.Linear; + _finished = finished; + + Func transform = AnimationExtensions.Interpolate(start, end); + _step = f => callback(transform(f)); + } + + public IEnumerator GetEnumerator() + { + return _children.GetEnumerator(); + } + + public void Add(double beginAt, double finishAt, Animation animation) + { + if (beginAt < 0 || beginAt > 1) + throw new ArgumentOutOfRangeException("beginAt"); + + if (finishAt < 0 || finishAt > 1) + throw new ArgumentOutOfRangeException("finishAt"); + + if (finishAt <= beginAt) + throw new ArgumentException("finishAt must be greater than beginAt"); + + animation._beginAt = beginAt; + animation._finishAt = finishAt; + _children.Add(animation); + } + + public void Commit(IAnimatable owner, string name, uint rate = 16, uint length = 250, Easing? easing = null, Action? finished = null, Func? repeat = null) + { + owner.Animate(name, this, rate, length, easing, finished, repeat); + } + + public Action GetCallback() + { + Action result = f => + { + _step(_easing.Ease(f)); + foreach (Animation animation in _children) + { + if (animation._finishedTriggered) + continue; + + double val = Math.Max(0.0f, Math.Min(1.0f, (f - animation._beginAt) / (animation._finishAt - animation._beginAt))); + + if (val <= 0.0f) // not ready to process yet + continue; + + Action callback = animation.GetCallback(); + callback(val); + + if (val >= 1.0f) + { + animation._finishedTriggered = true; + if (animation._finished != null) + animation._finished(); + } + } + }; + return result; + } + + internal void ResetChildren() + { + foreach (var anim in _children) + anim._finishedTriggered = false; + } + + public Animation Insert(double beginAt, double finishAt, Animation animation) + { + Add(beginAt, finishAt, animation); + return this; + } + + public Animation WithConcurrent(Animation animation, double beginAt = 0.0f, double finishAt = 1.0f) + { + animation._beginAt = beginAt; + animation._finishAt = finishAt; + _children.Add(animation); + return this; + } + + public Animation WithConcurrent(Action callback, double start = 0.0f, double end = 1.0f, Easing? easing = null, double beginAt = 0.0f, double finishAt = 1.0f) + { + var child = new Animation(callback, start, end, easing); + child._beginAt = beginAt; + child._finishAt = finishAt; + _children.Add(child); + return this; + } + } +} \ No newline at end of file diff --git a/src/Tizen.UIExtensions.Common/Animation/AnimationExtensions.cs b/src/Tizen.UIExtensions.Common/Animation/AnimationExtensions.cs new file mode 100644 index 0000000..2943529 --- /dev/null +++ b/src/Tizen.UIExtensions.Common/Animation/AnimationExtensions.cs @@ -0,0 +1,409 @@ +// +// Tweener.cs +// +// Author: +// Jason Smith +// +// Copyright (c) 2012 Xamarin Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Collections.Generic; +using Tizen.Applications; + +namespace Tizen.UIExtensions.Common.Internal +{ + public interface IAnimatable + { + void BatchBegin(); + void BatchCommit(); + } + + internal class AnimatableKey + { + public AnimatableKey(IAnimatable animatable, string handle) + { + if (animatable == null) + { + throw new ArgumentNullException(nameof(animatable)); + } + + if (string.IsNullOrEmpty(handle)) + { + throw new ArgumentException("Argument is null or empty", nameof(handle)); + } + + Animatable = new WeakReference(animatable); + Handle = handle; + } + + public WeakReference Animatable { get; } + + public string Handle { get; } + + public override bool Equals(object? obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + if (ReferenceEquals(this, obj)) + { + return true; + } + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((AnimatableKey)obj); + } + + public override int GetHashCode() + { + unchecked + { + IAnimatable? target; + if (!Animatable.TryGetTarget(out target)) + { + return Handle?.GetHashCode() ?? 0; + } + + return ((target?.GetHashCode() ?? 0) * 397) ^ (Handle?.GetHashCode() ?? 0); + } + } + + protected bool Equals(AnimatableKey other) + { + if (!string.Equals(Handle, other.Handle)) + { + return false; + } + + IAnimatable? thisAnimatable; + + if (!Animatable.TryGetTarget(out thisAnimatable)) + { + return false; + } + + IAnimatable? thatAnimatable; + + if (!other.Animatable.TryGetTarget(out thatAnimatable)) + { + return false; + } + + return Equals(thisAnimatable, thatAnimatable); + } + } + + public static class AnimationExtensions + { + static readonly Dictionary s_animations; + static readonly Dictionary s_kinetics; + + static AnimationExtensions() + { + s_animations = new Dictionary(); + s_kinetics = new Dictionary(); + } + + public static bool AbortAnimation(this IAnimatable self, string handle) + { + var key = new AnimatableKey(self, handle); + + if (!s_animations.ContainsKey(key) && !s_kinetics.ContainsKey(key)) + { + return false; + } + + Action abort = () => + { + AbortAnimation(key); + AbortKinetic(key); + }; + + DoAction(self, abort); + + return true; + } + + public static void Animate(this IAnimatable self, string name, Animation animation, uint rate = 16, uint length = 250, Easing? easing = null, Action? finished = null, + Func? repeat = null) + { + if (repeat == null) + self.Animate(name, animation.GetCallback(), rate, length, easing, finished, null); + else + { + Func r = () => + { + var val = repeat(); + if (val) + animation.ResetChildren(); + return val; + }; + self.Animate(name, animation.GetCallback(), rate, length, easing, finished, r); + } + } + + public static void Animate(this IAnimatable self, string name, Action callback, double start, double end, uint rate = 16, uint length = 250, Easing? easing = null, + Action? finished = null, Func? repeat = null) + { + self.Animate(name, Interpolate(start, end), callback, rate, length, easing, finished, repeat); + } + + public static void Animate(this IAnimatable self, string name, Action callback, uint rate = 16, uint length = 250, Easing? easing = null, Action? finished = null, + Func? repeat = null) + { + self.Animate(name, x => x, callback, rate, length, easing, finished, repeat); + } + + public static void Animate(this IAnimatable self, string name, Func transform, Action callback, + uint rate = 16, uint length = 250, Easing? easing = null, + Action? finished = null, Func? repeat = null) + { + if (transform == null) + throw new ArgumentNullException(nameof(transform)); + if (callback == null) + throw new ArgumentNullException(nameof(callback)); + if (self == null) + throw new ArgumentNullException(nameof(self)); + + Action animate = () => AnimateInternal(self, name, transform, callback, rate, length, easing, finished, repeat); + DoAction(self, animate); + } + + + public static void AnimateKinetic(this IAnimatable self, string name, Func callback, double velocity, double drag, Action? finished = null) + { + Action animate = () => AnimateKineticInternal(self, name, callback, velocity, drag, finished); + DoAction(self, animate); + } + + public static bool AnimationIsRunning(this IAnimatable self, string handle) + { + var key = new AnimatableKey(self, handle); + return s_animations.ContainsKey(key); + } + + public static Func Interpolate(double start, double end = 1.0f, double reverseVal = 0.0f, bool reverse = false) + { + double target = reverse ? reverseVal : end; + return x => start + (target - start) * x; + } + + public static IDisposable Batch(this IAnimatable self) => new BatchObject(self); + + static void AbortAnimation(AnimatableKey key) + { + // If multiple animations on the same view with the same name (IOW, the same AnimatableKey) are invoked + // asynchronously (e.g., from the `[Animate]To` methods in `ViewExtensions`), it's possible to get into + // a situation where after invoking the `Finished` handler below `s_animations` will have a new `Info` + // object in it with the same AnimatableKey. We need to continue cancelling animations until that is no + // longer the case; thus, the `while` loop. + + // If we don't cancel all of the animations popping in with this key, `AnimateInternal` will overwrite one + // of them with the new `Info` object, and the overwritten animation will never complete; any `await` for + // it will never return. + + while (s_animations.ContainsKey(key)) + { + Info info = s_animations[key]; + + s_animations.Remove(key); + + info.Tweener.ValueUpdated -= HandleTweenerUpdated; + info.Tweener.Finished -= HandleTweenerFinished; + info.Tweener.Stop(); + info.Finished?.Invoke(1.0f, true); + } + } + + static void AbortKinetic(AnimatableKey key) + { + if (!s_kinetics.ContainsKey(key)) + { + return; + } + + Ticker.Default.Remove(s_kinetics[key]); + s_kinetics.Remove(key); + } + + static void AnimateInternal(IAnimatable self, string name, Func transform, Action callback, + uint rate, uint length, Easing? easing, Action? finished, Func? repeat) + { + var key = new AnimatableKey(self, name); + + AbortAnimation(key); + + Action step = f => callback(transform(f)); + Action? final = null; + if (finished != null) + final = (f, b) => finished(transform(f), b); + + + var tweener = new Tweener(length, rate); + tweener.Handle = key; + tweener.ValueUpdated += HandleTweenerUpdated; + tweener.Finished += HandleTweenerFinished; + + var info = new Info(rate, length, easing ?? Easing.Linear, tweener, step, new WeakReference(self)); + + info.Finished = final; + info.Repeat = repeat; + s_animations[key] = info; + + info.Callback(0.0f); + tweener.Start(); + } + + static void AnimateKineticInternal(IAnimatable self, string name, Func callback, double velocity, double drag, Action? finished = null) + { + var key = new AnimatableKey(self, name); + + AbortKinetic(key); + + double sign = velocity / Math.Abs(velocity); + velocity = Math.Abs(velocity); + + int tick = Ticker.Default.Insert(step => + { + long ms = step; + + velocity -= drag * ms; + velocity = Math.Max(0, velocity); + + var result = false; + if (velocity > 0) + { + result = callback(sign * velocity * ms, velocity); + } + + if (!result) + { + finished?.Invoke(); + s_kinetics.Remove(key); + } + return result; + }); + + s_kinetics[key] = tick; + } + + static void HandleTweenerFinished(object? o, EventArgs args) + { + var tweener = o as Tweener; + + Info? info; + if (tweener != null && tweener.Handle != null && s_animations.TryGetValue(tweener.Handle, out info)) + { + IAnimatable? owner; + if (info.Owner.TryGetTarget(out owner)) + owner.BatchBegin(); + info.Callback(tweener.Value); + + var repeat = false; + + + if (info.Repeat != null) + repeat = info.Repeat(); + + if (!repeat) + { + s_animations.Remove(tweener.Handle); + tweener.ValueUpdated -= HandleTweenerUpdated; + tweener.Finished -= HandleTweenerFinished; + } + + info.Finished?.Invoke(tweener.Value, false); + + if (info.Owner.TryGetTarget(out owner)) + owner.BatchCommit(); + + if (repeat) + { + tweener.Start(); + } + } + } + + static void HandleTweenerUpdated(object? o, EventArgs args) + { + var tweener = o as Tweener; + Info? info; + IAnimatable? owner; + if (tweener != null && tweener.Handle != null && s_animations.TryGetValue(tweener.Handle, out info) && info.Owner.TryGetTarget(out owner)) + { + owner.BatchBegin(); + info.Callback(info.Easing.Ease(tweener.Value)); + owner.BatchCommit(); + } + } + + static void DoAction(IAnimatable self, Action action) + { + action(); + } + + class Info + { + public Action Callback; + public Action? Finished; + public Func? Repeat; + public Tweener Tweener; + + public Info(uint rate, uint length, Easing easing, Tweener tweener, Action callback, WeakReference owner) + { + Rate = rate; + Length = length; + Easing = easing; + Tweener = tweener; + Callback = callback; + Owner = owner; + } + + public Easing Easing { get; set; } + + public uint Length { get; set; } + + public WeakReference Owner { get; set; } + + public uint Rate { get; set; } + } + + sealed class BatchObject : IDisposable + { + IAnimatable? _animatable; + + public BatchObject(IAnimatable animatable) + { + _animatable = animatable; + _animatable?.BatchBegin(); + } + + public void Dispose() + { + _animatable?.BatchCommit(); + _animatable = null; + } + } + } +} \ No newline at end of file diff --git a/src/Tizen.UIExtensions.Common/Animation/Easing.cs b/src/Tizen.UIExtensions.Common/Animation/Easing.cs new file mode 100644 index 0000000..a526a86 --- /dev/null +++ b/src/Tizen.UIExtensions.Common/Animation/Easing.cs @@ -0,0 +1,98 @@ +// +// Tweener.cs +// +// Author: +// Jason Smith +// +// Copyright (c) 2012 Xamarin Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; + +namespace Tizen.UIExtensions.Common.Internal +{ + public class Easing + { + public static readonly Easing Linear = new Easing(x => x); + + public static readonly Easing SinOut = new Easing(x => Math.Sin(x * Math.PI * 0.5f)); + public static readonly Easing SinIn = new Easing(x => 1.0f - Math.Cos(x * Math.PI * 0.5f)); + public static readonly Easing SinInOut = new Easing(x => -Math.Cos(Math.PI * x) / 2.0f + 0.5f); + + public static readonly Easing CubicIn = new Easing(x => x * x * x); + public static readonly Easing CubicOut = new Easing(x => Math.Pow(x - 1.0f, 3.0f) + 1.0f); + + public static readonly Easing CubicInOut = new Easing(x => x < 0.5f ? Math.Pow(x * 2.0f, 3.0f) / 2.0f : (Math.Pow((x - 1) * 2.0f, 3.0f) + 2.0f) / 2.0f); + + public static readonly Easing BounceOut; + public static readonly Easing BounceIn; + + public static readonly Easing SpringIn = new Easing(x => x * x * ((1.70158f + 1) * x - 1.70158f)); + public static readonly Easing SpringOut = new Easing(x => (x - 1) * (x - 1) * ((1.70158f + 1) * (x - 1) + 1.70158f) + 1); + + readonly Func _easingFunc; + + static Easing() + { + BounceOut = new Easing(p => + { + if (p < 1 / 2.75f) + { + return 7.5625f * p * p; + } + if (p < 2 / 2.75f) + { + p -= 1.5f / 2.75f; + + return 7.5625f * p * p + .75f; + } + if (p < 2.5f / 2.75f) + { + p -= 2.25f / 2.75f; + + return 7.5625f * p * p + .9375f; + } + p -= 2.625f / 2.75f; + + return 7.5625f * p * p + .984375f; + }); + + BounceIn = new Easing(p => 1.0f - BounceOut.Ease(1 - p)); + } + + public Easing(Func easingFunc) + { + if (easingFunc == null) + throw new ArgumentNullException("easingFunc"); + + _easingFunc = easingFunc; + } + + public double Ease(double v) + { + return _easingFunc(v); + } + + public static implicit operator Easing(Func func) + { + return new Easing(func); + } + } +} \ No newline at end of file diff --git a/src/Tizen.UIExtensions.Common/Animation/Ticker.cs b/src/Tizen.UIExtensions.Common/Animation/Ticker.cs new file mode 100644 index 0000000..92173e3 --- /dev/null +++ b/src/Tizen.UIExtensions.Common/Animation/Ticker.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Tizen.NUI; + +namespace Tizen.UIExtensions.Common.Internal +{ + public class Ticker + { + static Ticker? s_ticker; + readonly Stopwatch _stopwatch; + readonly List>> _timeouts; + Timer _timer; + + int _count; + bool _enabled; + + protected Ticker() + { + _count = 0; + _timeouts = new List>>(); + _stopwatch = new Stopwatch(); + _timer = new Timer(16); + _timer.Tick += OnTick; + } + + bool OnTick(object source, Timer.TickEventArgs e) + { + SendSignals(-1); + return true; + } + + public static Ticker Default + { + get + { + if (s_ticker == null) + { + s_ticker = new Ticker(); + } + + return s_ticker; + } + } + + public virtual int Insert(Func timeout) + { + _count++; + _timeouts.Add(new Tuple>(_count, timeout)); + + if (!_enabled) + { + _enabled = true; + Enable(); + } + + return _count; + } + + public virtual void Remove(int handle) + { + global::ElmSharp.EcoreMainloop.Post(() => RemoveTimeout(handle)); + } + + void RemoveTimeout(int handle) + { + _timeouts.RemoveAll(t => t.Item1 == handle); + + if (_timeouts.Count == 0) + { + _enabled = false; + Disable(); + } + } + + protected void DisableTimer() + { + _timer.Stop(); + } + + protected void EnableTimer() + { + _timer.Start(); + } + + protected void SendFinish() + { + SendSignals(long.MaxValue); + } + + protected void SendSignals(int timestep = -1) + { + long step = timestep >= 0 + ? timestep + : _stopwatch.ElapsedMilliseconds; + + SendSignals(step); + } + + protected void SendSignals(long step) + { + _stopwatch.Reset(); + _stopwatch.Start(); + + var localCopy = new List>>(_timeouts); + foreach (Tuple> timeout in localCopy) + { + bool remove = !timeout.Item2(step); + if (remove) + _timeouts.RemoveAll(t => t.Item1 == timeout.Item1); + } + + if (_timeouts.Count == 0) + { + _enabled = false; + Disable(); + } + } + + void Disable() + { + _stopwatch.Reset(); + DisableTimer(); + } + + void Enable() + { + _stopwatch.Start(); + EnableTimer(); + } + } +} diff --git a/src/Tizen.UIExtensions.Common/Animation/Tweener.cs b/src/Tizen.UIExtensions.Common/Animation/Tweener.cs new file mode 100644 index 0000000..358c4af --- /dev/null +++ b/src/Tizen.UIExtensions.Common/Animation/Tweener.cs @@ -0,0 +1,148 @@ +// +// Tweener.cs +// +// Author: +// Jason Smith +// +// Copyright (c) 2012 Xamarin Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; + +namespace Tizen.UIExtensions.Common.Internal +{ + internal class Tweener + { + long _lastMilliseconds; + + int _timer; + long _frames; + + public Tweener(uint length) + { + Value = 0.0f; + Length = length; + Rate = 1; + Loop = false; + } + + public Tweener(uint length, uint rate) + { + Value = 0.0f; + Length = length; + Rate = rate; + Loop = false; + } + + public AnimatableKey? Handle { get; set; } + + public uint Length { get; } + + public uint Rate { get; } + + public bool Loop { get; set; } + + public double Value { get; private set; } + + public event EventHandler? Finished; + + public void Pause() + { + if (_timer != 0) + { + Ticker.Default.Remove(_timer); + _timer = 0; + } + } + + public void Start() + { + Pause(); + + _lastMilliseconds = 0; + _frames = 0; + + _timer = Ticker.Default.Insert(step => + { + if (step == long.MaxValue) + { + // We're being forced to finish + Value = 1.0; + } + else + { + long ms = step + _lastMilliseconds; + + Value = Math.Min(1.0f, ms / (double)Length); + + _lastMilliseconds = ms; + } + + long wantedFrames = (_lastMilliseconds / Rate) + 1; + if (wantedFrames > _frames || Value >= 1.0f) + { + ValueUpdated?.Invoke(this, EventArgs.Empty); + } + _frames = wantedFrames; + + if (Value >= 1.0f) + { + if (Loop) + { + _lastMilliseconds = 0; + Value = 0.0f; + return true; + } + + Finished?.Invoke(this, EventArgs.Empty); + Value = 0.0f; + _timer = 0; + return false; + } + return true; + }); + } + + public void Stop() + { + Pause(); + Value = 1.0f; + Finished?.Invoke(this, EventArgs.Empty); + Value = 0.0f; + } + + public event EventHandler? ValueUpdated; + + ~Tweener() + { + if (_timer != 0) + { + try + { + Ticker.Default.Remove(_timer); + } + catch (InvalidOperationException) + { + } + } + _timer = 0; + } + } +} \ No newline at end of file diff --git a/src/Tizen.UIExtensions.Common/DeviceInfo.cs b/src/Tizen.UIExtensions.Common/DeviceInfo.cs new file mode 100644 index 0000000..88846cc --- /dev/null +++ b/src/Tizen.UIExtensions.Common/DeviceInfo.cs @@ -0,0 +1,6 @@ +namespace Tizen.UIExtensions.Common +{ + public static partial class DeviceInfo + { + } +} diff --git a/src/Tizen.UIExtensions.Common/GraphicsView/ActivityIndicatorDrawable.cs b/src/Tizen.UIExtensions.Common/GraphicsView/ActivityIndicatorDrawable.cs new file mode 100644 index 0000000..2c8464d --- /dev/null +++ b/src/Tizen.UIExtensions.Common/GraphicsView/ActivityIndicatorDrawable.cs @@ -0,0 +1,112 @@ +using Microsoft.Maui.Graphics; +using Tizen.UIExtensions.Common.Internal; +using GColor = Microsoft.Maui.Graphics.Color; +using TSize = Tizen.UIExtensions.Common.Size; + +namespace Tizen.UIExtensions.Common.GraphicsView +{ + public class ActivityIndicatorDrawable : GraphicsViewDrawable, IAnimatable + { + public ActivityIndicatorDrawable(IActivityIndicator view) + { + View = view; + } + + IActivityIndicator View { get; } + + float MaterialActivityIndicatorRotate { get; set; } + + float MaterialActivityIndicatorStartAngle { get; set; } + + float MaterialActivityIndicatorEndAngle { get; set; } + + + public override void Draw(ICanvas canvas, RectangleF dirtyRect) + { + DrawMaterialActivityIndicator(canvas, dirtyRect); + } + + public void UpdateAnimation(bool animate) + { + if (!animate) + { + this.AbortAnimation("MaterialActivityIndicator"); + SendInvalidated(); + return; + } + + var materialActivityIndicatorAngleAnimation = new Animation(); + + + var startAngle = 90; + var endAngle = 360; + + var rotateAnimation = new Animation(v => + { + MaterialActivityIndicatorRotate = (int)v; + SendInvalidated(); + }, 0, 360, easing: Easing.Linear); + var startAngleAnimation = new Animation(v => MaterialActivityIndicatorStartAngle = (int)v, startAngle, startAngle - 360, easing: Easing.Linear); + var endAngleAnimation = new Animation(v => MaterialActivityIndicatorEndAngle = (int)v, endAngle, endAngle - 360, easing: Easing.Linear); + + materialActivityIndicatorAngleAnimation.Add(0, 1, rotateAnimation); + materialActivityIndicatorAngleAnimation.Add(0, 1, startAngleAnimation); + materialActivityIndicatorAngleAnimation.Add(0, 1, endAngleAnimation); + + materialActivityIndicatorAngleAnimation.Commit(this, "MaterialActivityIndicator", length: 1400, repeat: () => true, finished: (l, c) => materialActivityIndicatorAngleAnimation = null); + } + + void DrawMaterialActivityIndicator(ICanvas canvas, RectangleF dirtyRect) + { + canvas.SaveState(); + + float size = 40f; + float strokeWidth = 4f; + + canvas.StrokeSize = strokeWidth; + + var x = dirtyRect.X; + var y = dirtyRect.Y; + + if (View.IsRunning) + { + canvas.Rotate(MaterialActivityIndicatorRotate, x + strokeWidth + size / 2, y + strokeWidth + size / 2); + canvas.StrokeColor = View.Color.ToGraphicsColor(Material.Color.Blue); + canvas.DrawArc(x + strokeWidth, y + strokeWidth, size, size, MaterialActivityIndicatorStartAngle, MaterialActivityIndicatorEndAngle, false, false); + } + else + { + canvas.Rotate(0, x + strokeWidth + size / 2, y + strokeWidth + size / 2); + if (View.Color.IsDefault) + { + canvas.StrokeColor = GColor.FromArgb(Material.Color.LightBlue); + } + else + { + canvas.StrokeColor = View.Color.MultiplyAlpha(0.5).ToGraphicsColor(Material.Color.LightBlue); + } + canvas.DrawArc(x + strokeWidth, y + strokeWidth, size, size, 0, 360, false, false); + } + + canvas.RestoreState(); + } + + void IAnimatable.BatchBegin() { } + + void IAnimatable.BatchCommit() { } + + public override TSize Measure(double availableWidth, double availableHeight) + { + return new TSize(45 * DeviceInfo.ScalingFactor, 45 * DeviceInfo.ScalingFactor); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + this.AbortAnimation("MaterialActivityIndicator"); + } + base.Dispose(disposing); + } + } +} diff --git a/src/Tizen.UIExtensions.Common/GraphicsView/ButtonDrawable.cs b/src/Tizen.UIExtensions.Common/GraphicsView/ButtonDrawable.cs new file mode 100644 index 0000000..a361e02 --- /dev/null +++ b/src/Tizen.UIExtensions.Common/GraphicsView/ButtonDrawable.cs @@ -0,0 +1,102 @@ +using Microsoft.Maui.Graphics; +using GColor = Microsoft.Maui.Graphics.Color; +using GPoint = Microsoft.Maui.Graphics.Point; +using TSize = Tizen.UIExtensions.Common.Size; + +namespace Tizen.UIExtensions.Common.GraphicsView +{ + public class ButtonDrawable : GraphicsViewDrawable + { + const float MaterialBackgroundHeight = 36f; + const float MaterialShadowOffset = 3f; + + readonly RippleEffectDrawable _rippleEffect; + RectangleF _backgroundRect; + + public ButtonDrawable(IButton view) + { + View = view; + _rippleEffect = new RippleEffectDrawable(); + _rippleEffect.Invalidated += (s, e) => SendInvalidated(); + } + + IButton View { get; } + + public override void Draw(ICanvas canvas, RectangleF dirtyRect) + { + DrawMaterialButtonBackground(canvas, dirtyRect); + DrawMaterialButtonText(canvas, dirtyRect); + _rippleEffect.Draw(canvas, dirtyRect); + } + + public override TSize Measure(double availableWidth, double availableHeight) + { + + var fontSize = (float)(Material.Font.Button * DeviceInfo.ScalingFactor); + float textLength = 0; + + using (var paint = new SkiaSharp.SKPaint + { + TextSize = fontSize + }) + { + textLength = paint.MeasureText(View?.Text ?? ""); + } + + return new TSize(DeviceInfo.ScalingFactor * MaterialBackgroundHeight + textLength, DeviceInfo.ScalingFactor * MaterialBackgroundHeight); + } + + public override void OnTouchDown(GPoint point) + { + _rippleEffect.ClipRectangle = _backgroundRect; + _rippleEffect.OnTouchDown(point); + } + + public override void OnTouchUp(GPoint point) + { + _rippleEffect.OnTouchUp(point); + } + + void DrawMaterialButtonBackground(ICanvas canvas, RectangleF dirtyRect) + { + canvas.SaveState(); + + canvas.FillColor = View.BackgroundColor.ToGraphicsColor(Material.Color.Blue); + + var x = dirtyRect.X; + var y = dirtyRect.Y; + + var width = dirtyRect.Width - MaterialShadowOffset; + var height = MaterialBackgroundHeight - MaterialShadowOffset; + canvas.SetShadow(new SizeF(0, 1), 3, GColor.FromArgb(Material.Color.Gray2)); + + canvas.FillRoundedRectangle(x, y, width, height, (float)(View.CornerRadius * DeviceInfo.ScalingFactor)); + + canvas.RestoreState(); + + _backgroundRect = new RectangleF(x, y, width, height); + } + + void DrawMaterialButtonText(ICanvas canvas, RectangleF dirtyRect) + { + canvas.SaveState(); + + canvas.FontName = "Roboto"; + canvas.FontColor = View.TextColor.ToGraphicsColor(Material.Color.White); + var fontSize = canvas.FontSize = (float)(Material.Font.Button * DeviceInfo.ScalingFactor); + + var x = dirtyRect.X; + var y = dirtyRect.Y; + + var width = dirtyRect.Width - MaterialShadowOffset; + var height = dirtyRect.Height - MaterialShadowOffset; + + //canvas.DrawString(View.Text.ToUpper(), x, y, width, MaterialBackgroundHeight, HorizontalAlignment.Center, VerticalAlignment.Center); + + canvas.DrawString(View.Text, x, height / 2.0f - fontSize / 3.0f, width, height, HorizontalAlignment.Center, VerticalAlignment.Top); + + canvas.RestoreState(); + } + + } +} diff --git a/src/Tizen.UIExtensions.Common/GraphicsView/CheckBoxDrawable.cs b/src/Tizen.UIExtensions.Common/GraphicsView/CheckBoxDrawable.cs new file mode 100644 index 0000000..5f024d5 --- /dev/null +++ b/src/Tizen.UIExtensions.Common/GraphicsView/CheckBoxDrawable.cs @@ -0,0 +1,124 @@ +using Microsoft.Maui.Graphics; +using GPoint = Microsoft.Maui.Graphics.Point; +using TSize = Tizen.UIExtensions.Common.Size; + +namespace Tizen.UIExtensions.Common.GraphicsView +{ + public class CheckBoxDrawable : GraphicsViewDrawable + { + const string MaterialCheckBoxMark = "M13.3516 1.35156L5 9.71094L0.648438 5.35156L1.35156 4.64844L5 8.28906L12.6484 0.648438L13.3516 1.35156Z"; + + public CheckBoxDrawable(ICheckBox view) + { + View = view; + } + + ICheckBox View { get; } + + public override void Draw(ICanvas canvas, RectangleF dirtyRect) + { + DrawMaterialCheckBoxBackground(canvas, dirtyRect); + DrawMaterialCheckBoxMark(canvas, dirtyRect); + DrawMaterialCheckBoxText(canvas, dirtyRect); + } + + public override TSize Measure(double availableWidth, double availableHeight) + { + var fontSize = (float)(14 * DeviceInfo.ScalingFactor); + float textLength = 0; + + using (var paint = new SkiaSharp.SKPaint + { + TextSize = fontSize + }) + { + textLength = paint.MeasureText(View?.Text ?? ""); + } + + return new TSize(DeviceInfo.ScalingFactor * 28 + textLength, DeviceInfo.ScalingFactor * 20); + } + + public override void OnTouchDown(GPoint point) + { + View.IsChecked = !View.IsChecked; + } + + void DrawMaterialCheckBoxBackground(ICanvas canvas, RectangleF dirtyRect) + { + canvas.SaveState(); + + float size = 18f; + + var x = dirtyRect.X; + var y = dirtyRect.Y; + + if (dirtyRect.Height > size) + { + y += (dirtyRect.Height - size) / 2; + } + + if (View.IsChecked) + { + canvas.FillColor = View.Color.ToGraphicsColor(Material.Color.Blue); + canvas.FillRoundedRectangle(x, y, size, size, 2); + } + else + { + var strokeWidth = 2; + + canvas.StrokeSize = strokeWidth; + canvas.StrokeColor = View.Color.ToGraphicsColor(Material.Color.Gray1); + canvas.DrawRoundedRectangle(x + strokeWidth / 2, y + strokeWidth / 2, size - strokeWidth, size - strokeWidth, 2); + } + + canvas.RestoreState(); + } + + void DrawMaterialCheckBoxMark(ICanvas canvas, RectangleF dirtyRect) + { + if (View.IsChecked) + { + canvas.SaveState(); + + float size = 18f; + + float x = 2; + float y = 4; + + if (dirtyRect.Height > size) + { + y += (dirtyRect.Height - size) / 2; + } + + canvas.Translate((float)(x * DeviceInfo.ScalingFactor), (float)(y * DeviceInfo.ScalingFactor)); + + var vBuilder = new PathBuilder(); + var path = vBuilder.BuildPath(MaterialCheckBoxMark); + + canvas.StrokeColor = Colors.White; + canvas.DrawPath(path); + + canvas.RestoreState(); + } + } + + void DrawMaterialCheckBoxText(ICanvas canvas, RectangleF dirtyRect) + { + canvas.SaveState(); + + canvas.FontColor = View.TextColor.ToGraphicsColor(Cupertino.Color.Label.Light.Primary); + var fontSize = (float)(14 * DeviceInfo.ScalingFactor); + canvas.FontSize = fontSize; + + float size = 20f; + float margin = 8f; + + var height = dirtyRect.Height; + var width = dirtyRect.Width; + + canvas.DrawString(View.Text, size + margin, dirtyRect.Y, width - (size + margin), height, HorizontalAlignment.Left, VerticalAlignment.Center); + + canvas.RestoreState(); + } + } +} diff --git a/src/Tizen.UIExtensions.Common/GraphicsView/EditorDrawable.cs b/src/Tizen.UIExtensions.Common/GraphicsView/EditorDrawable.cs new file mode 100644 index 0000000..78696d3 --- /dev/null +++ b/src/Tizen.UIExtensions.Common/GraphicsView/EditorDrawable.cs @@ -0,0 +1,170 @@ +using Microsoft.Maui.Graphics; +using Tizen.UIExtensions.Common.Internal; +using GColor = Microsoft.Maui.Graphics.Color; +using TSize = Tizen.UIExtensions.Common.Size; + +namespace Tizen.UIExtensions.Common.GraphicsView +{ + public class EditorDrawable : GraphicsViewDrawable, IAnimatable + { + const float FocusedMaterialPlaceholderFontSize = 12f; + const float UnfocusedMaterialPlaceholderFontSize = 16f; + + const float FocusedMaterialPlaceholderPosition = 6f; + const float UnfocusedMaterialPlaceholderPosition = 22f; + + float _placeholderY; + float _placeholderFontSize; + + + public EditorDrawable(IEditor view) + { + View = view; + AnimateMaterialPlaceholder(false); + } + + IEditor View { get; } + + float PlaceholderY + { + get { return _placeholderY; } + set + { + _placeholderY = value; + SendInvalidated(); + } + } + + float PlaceholderFontSize + { + get { return _placeholderFontSize; } + set + { + _placeholderFontSize = value; + SendInvalidated(); + } + } + + public override void Draw(ICanvas canvas, RectangleF dirtyRect) + { + DrawMaterialEditorBackground(canvas, dirtyRect); + DrawMaterialEditorBorder(canvas, dirtyRect); + DrawMaterialEditorPlaceholder(canvas, dirtyRect); + } + + public override TSize Measure(double availableWidth, double availableHeight) + { + return new TSize(availableWidth, 114.95 * DeviceInfo.ScalingFactor); + } + + public override void OnFocused() + { + AnimateMaterialPlaceholder(true); + } + + public override void OnUnfocused() + { + AnimateMaterialPlaceholder(false); + } + + void DrawMaterialEditorBackground(ICanvas canvas, RectangleF dirtyRect) + { + canvas.SaveState(); + + canvas.FillColor = View.BackgroundColor.ToGraphicsColor(Material.Color.Gray5); + + //var width = dirtyRect.Width; + + //var vBuilder = new PathBuilder(); + //var path = + // vBuilder.BuildPath( + // $"M0 4C0 1.79086 1.79086 0 4 0H{width - 4}C{width - 2}.209 0 {width} 1.79086 {width} 4V114.95H0V4Z"); + + //canvas.FillPath(path); + + canvas.FillRectangle(dirtyRect); + + canvas.RestoreState(); + } + + void DrawMaterialEditorBorder(ICanvas canvas, RectangleF dirtyRect) + { + canvas.SaveState(); + + var strokeWidth = 1.0f; + canvas.FillColor = GColor.FromArgb(Material.Color.Black); + + if (View.IsFocused) + { + strokeWidth = 2.0f; + canvas.FillColor = GColor.FromArgb(Material.Color.Blue); + } + + var x = dirtyRect.X; + + //var y = 112.91f; + var y = dirtyRect.Y + dirtyRect.Height - strokeWidth * 2; + + + var width = dirtyRect.Width; + var height = strokeWidth; + + canvas.FillRectangle(x, y, width, height); + + canvas.RestoreState(); + } + + void DrawMaterialEditorPlaceholder(ICanvas canvas, RectangleF dirtyRect) + { + canvas.SaveState(); + + canvas.FontColor = View.PlaceholderColor.ToGraphicsColor(Material.Color.Dark); + canvas.FontSize = (float)(PlaceholderFontSize * DeviceInfo.ScalingFactor); + + float margin = 12f; + + var horizontalAlignment = HorizontalAlignment.Left; + + var x = dirtyRect.X + margin; + + //if (FlowDirection == FlowDirection.RightToLeft) + //{ + // x = dirtyRect.X; + // horizontalAlignment = HorizontalAlignment.Right; + //} + + var height = dirtyRect.Height; + var width = dirtyRect.Width; + + canvas.DrawString(View.Placeholder, x, PlaceholderY, width - margin, height, horizontalAlignment, VerticalAlignment.Top); + + canvas.RestoreState(); + } + + void AnimateMaterialPlaceholder(bool isFocused) + { + if (View.IsFocused && !string.IsNullOrEmpty(View.Text)) + return; + + var materialPlaceholderAnimation = new Animation(); + + float startFontSize = isFocused ? UnfocusedMaterialPlaceholderFontSize : FocusedMaterialPlaceholderFontSize; + float endFontSize = (isFocused || !string.IsNullOrEmpty(View.Text)) ? FocusedMaterialPlaceholderFontSize : UnfocusedMaterialPlaceholderFontSize; + var fontSizeAnimation = new Animation(v => PlaceholderFontSize = (int)v, startFontSize, endFontSize, easing: Easing.Linear); + + float startPosition = isFocused ? UnfocusedMaterialPlaceholderPosition : FocusedMaterialPlaceholderPosition; + float endPosition = (isFocused || !string.IsNullOrEmpty(View.Text)) ? FocusedMaterialPlaceholderPosition : UnfocusedMaterialPlaceholderPosition; + var placeholderPositionAnimation = new Animation(v => PlaceholderY = (int)v, startPosition, endPosition, easing: Easing.Linear); + + materialPlaceholderAnimation.Add(0, 1, fontSizeAnimation); + materialPlaceholderAnimation.Add(0, 1, placeholderPositionAnimation); + + materialPlaceholderAnimation.Commit(this, "MaterialPlaceholderAnimation", length: 100, finished: (l, c) => materialPlaceholderAnimation = null); + } + + + void IAnimatable.BatchBegin() { } + + void IAnimatable.BatchCommit() { } + } +} diff --git a/src/Tizen.UIExtensions.Common/GraphicsView/EntryDrawable.cs b/src/Tizen.UIExtensions.Common/GraphicsView/EntryDrawable.cs new file mode 100644 index 0000000..7b982b7 --- /dev/null +++ b/src/Tizen.UIExtensions.Common/GraphicsView/EntryDrawable.cs @@ -0,0 +1,231 @@ +using Microsoft.Maui.Graphics; +using Tizen.UIExtensions.Common.Internal; +using GColor = Microsoft.Maui.Graphics.Color; +using GPoint = Microsoft.Maui.Graphics.Point; +using TSize = Tizen.UIExtensions.Common.Size; + +namespace Tizen.UIExtensions.Common.GraphicsView +{ + public class EntryDrawable : GraphicsViewDrawable, IAnimatable + { + const string MaterialEntryIndicatorIcon = "M9.295 7.885C9.68436 8.27436 9.68436 8.90564 9.295 9.295C8.90564 9.68436 8.27436 9.68436 7.885 9.295L5 6.41L2.115 9.295C1.72564 9.68436 1.09436 9.68436 0.705 9.295C0.315639 8.90564 0.315639 8.27436 0.705 7.885L3.59 5L0.705 2.115C0.315639 1.72564 0.31564 1.09436 0.705 0.705C1.09436 0.315639 1.72564 0.315639 2.115 0.705L5 3.59L7.885 0.705C8.27436 0.315639 8.90564 0.31564 9.295 0.705C9.68436 1.09436 9.68436 1.72564 9.295 2.115L6.41 5L9.295 7.885Z"; + + const float FocusedMaterialPlaceholderFontSize = 12f; + const float UnfocusedMaterialPlaceholderFontSize = 16f; + + const float FocusedMaterialPlaceholderPosition = 6f; + const float UnfocusedMaterialPlaceholderPosition = 22f; + + float _placeholderY; + float _placeholderFontSize; + + RectangleF _indicatorRect; + + public EntryDrawable(IEntry view) + { + View = view; + + AnimateMaterialPlaceholder(false); + } + + IEntry View { get; } + + + float PlaceholderY + { + get { return _placeholderY; } + set + { + _placeholderY = value; + SendInvalidated(); + } + } + + float PlaceholderFontSize + { + get { return _placeholderFontSize; } + set + { + _placeholderFontSize = value; + SendInvalidated(); + } + } + + public override void Draw(ICanvas canvas, RectangleF dirtyRect) + { + DrawMaterialEntryBackground(canvas, dirtyRect); + DrawMaterialEntryBorder(canvas, dirtyRect); + DrawMaterialEntryPlaceholder(canvas, dirtyRect); + DrawMaterialEntryIndicators(canvas, dirtyRect); + } + + public override TSize Measure(double availableWidth, double availableHeight) + { + return new TSize(availableWidth, 56 * DeviceInfo.ScalingFactor); + } + + public override void OnFocused() + { + AnimateMaterialPlaceholder(true); + } + + public override void OnUnfocused() + { + AnimateMaterialPlaceholder(false); + } + + public override void OnTouchDown(GPoint point) + { + PointF touchPoint = new PointF((float)point.X, (float)point.Y); + + if (_indicatorRect.Contains(touchPoint)) + View.Text = string.Empty; + } + + void DrawMaterialEntryBackground(ICanvas canvas, RectangleF dirtyRect) + { + canvas.SaveState(); + + canvas.FillColor = View.BackgroundColor.ToGraphicsColor(Material.Color.Gray5); + + var width = dirtyRect.Width; + + var vBuilder = new PathBuilder(); + var path = + vBuilder.BuildPath( + $"M0 4C0 1.79086 1.79086 0 4 0H{width - 4}C{width - 2}.209 0 {width} 1.79086 {width} 4V56H0V4Z"); + + canvas.FillPath(path); + + canvas.RestoreState(); + } + + void DrawMaterialEntryBorder(ICanvas canvas, RectangleF dirtyRect) + { + canvas.SaveState(); + + var strokeWidth = 1.0f; + canvas.FillColor = GColor.FromArgb(Material.Color.Black); + + if (View.IsFocused) + { + strokeWidth = 2.0f; + canvas.FillColor = GColor.FromArgb(Material.Color.Blue); + } + + var x = dirtyRect.X; + var y = 53.91f; + + var width = dirtyRect.Width; + var height = strokeWidth; + + canvas.FillRectangle(x, y, width, height); + + canvas.RestoreState(); + } + + void DrawMaterialEntryPlaceholder(ICanvas canvas, RectangleF dirtyRect) + { + canvas.SaveState(); + + canvas.FontColor = View.PlaceholderColor.ToGraphicsColor(Material.Color.Dark); + canvas.FontSize = (float)(PlaceholderFontSize * DeviceInfo.ScalingFactor); + + float margin = 12f; + + var horizontalAlignment = HorizontalAlignment.Left; + + var x = dirtyRect.X + margin; + + //if (FlowDirection == FlowDirection.RightToLeft) + //{ + // x = dirtyRect.X; + // horizontalAlignment = HorizontalAlignment.Right; + //} + + var height = dirtyRect.Height; + var width = dirtyRect.Width; + + canvas.DrawString(View.Placeholder, x, PlaceholderY, width - margin, height, horizontalAlignment, VerticalAlignment.Top); + + canvas.RestoreState(); + } + + void DrawMaterialEntryIndicators(ICanvas canvas, RectangleF dirtyRect) + { + if (!string.IsNullOrEmpty(View.Text)) + { + canvas.SaveState(); + + float radius = 12f; + + var backgroundMarginX = 24; + var backgroundMarginY = 28; + + var x = dirtyRect.Width - backgroundMarginX; + var y = dirtyRect.Y + backgroundMarginY; + + //if (FlowDirection == FlowDirection.RightToLeft) + // x = backgroundMarginX; + + canvas.FillColor = View.BackgroundColor.ToGraphicsColor(Material.Color.Black); + canvas.Alpha = 0.12f; + + canvas.FillCircle(x, y, radius); + + canvas.RestoreState(); + + _indicatorRect = new RectangleF(x - radius, y - radius, radius * 2, radius * 2); + + canvas.SaveState(); + + var iconMarginX = 29; + var iconMarginY = 23; + + var tX = dirtyRect.Width - iconMarginX; + var tY = dirtyRect.Y + iconMarginY; + + //if (FlowDirection == FlowDirection.RightToLeft) + //{ + // iconMarginX = 19; + // tX = iconMarginX; + //} + + canvas.Translate((float)(tX * DeviceInfo.ScalingFactor), (float)(tY * DeviceInfo.ScalingFactor)); + + var vBuilder = new PathBuilder(); + var path = vBuilder.BuildPath(MaterialEntryIndicatorIcon); + + canvas.FillColor = View.BackgroundColor.ToGraphicsColor(Material.Color.Black); + canvas.FillPath(path); + + canvas.RestoreState(); + } + } + + void AnimateMaterialPlaceholder(bool isFocused) + { + if (View.IsFocused && !string.IsNullOrEmpty(View.Text)) + return; + + var materialPlaceholderAnimation = new Animation(); + + float startFontSize = isFocused ? UnfocusedMaterialPlaceholderFontSize : FocusedMaterialPlaceholderFontSize; + float endFontSize = (isFocused || !string.IsNullOrEmpty(View.Text)) ? FocusedMaterialPlaceholderFontSize : UnfocusedMaterialPlaceholderFontSize; + var fontSizeAnimation = new Animation(v => PlaceholderFontSize = (int)v, startFontSize, endFontSize, easing: Easing.Linear); + + float startPosition = isFocused ? UnfocusedMaterialPlaceholderPosition : FocusedMaterialPlaceholderPosition; + float endPosition = (isFocused || !string.IsNullOrEmpty(View.Text)) ? FocusedMaterialPlaceholderPosition : UnfocusedMaterialPlaceholderPosition; + var placeholderPositionAnimation = new Animation(v => PlaceholderY = (int)v, startPosition, endPosition, easing: Easing.Linear); + + materialPlaceholderAnimation.Add(0, 1, fontSizeAnimation); + materialPlaceholderAnimation.Add(0, 1, placeholderPositionAnimation); + + materialPlaceholderAnimation.Commit(this, "MaterialPlaceholderAnimation", length: 100, finished: (l, c) => materialPlaceholderAnimation = null); + } + + void IAnimatable.BatchBegin() { } + + void IAnimatable.BatchCommit() { } + } +} diff --git a/src/Tizen.UIExtensions.Common/GraphicsView/GraphicsColors.cs b/src/Tizen.UIExtensions.Common/GraphicsView/GraphicsColors.cs new file mode 100644 index 0000000..6108c38 --- /dev/null +++ b/src/Tizen.UIExtensions.Common/GraphicsView/GraphicsColors.cs @@ -0,0 +1,263 @@ +using GColor = Microsoft.Maui.Graphics.Color; +using TColor = Tizen.UIExtensions.Common.Color; + +namespace Tizen.UIExtensions.Common.GraphicsView +{ + public static class GraphicsColorExtensions + { + public static GColor ToGraphicsColor(this TColor color, string fallback) + { + if (!color.IsDefault) + return new GColor((float)color.R, (float)color.G, (float)color.B, (float)color.A); + else + return GColor.FromArgb(fallback); + } + } + + internal static class Material + { + public static class Color + { + public const string Red = "#f44336"; + public const string Pink = "#e91e63"; + public const string Purple = "#9c27b0"; + public const string DeepPurple = "#673ab7"; + public const string Indigo = "#3f51b5"; + public const string Blue = "#2196f3"; + public const string LightBlue = "#03a9f4"; + public const string Cyan = "#00bcd4"; + public const string Teal = "#009688"; + public const string Green = "#4caf50"; + public const string LightGreen = "#8bc34a"; + public const string Lime = "#cddc39"; + public const string Yellow = "#ffeb3b"; + public const string Amber = "#ffc107"; + public const string Orange = "#ff9800"; + public const string DeepOrange = "#ff5722"; + + public const string Gray1 = "#8E8E93"; + public const string Gray2 = "#C7C7CC"; + public const string Gray3 = "#D1D1D6"; + public const string Gray4 = "#E5E5EA"; + public const string Gray5 = "#EFEFF4"; + public const string Gray6 = "#F2F2F7"; + + public const string Black = "#000000"; + public const string Dark = "#1F1F1F"; + public const string White = "#FFFFFF"; + public const string Light = "#E3E3E3"; + } + + public static class Font + { + public const float H1 = 96; + public const float H2 = 60; + public const float H3 = 48; + public const float H4 = 34; + public const float H5 = 24; + public const float H6 = 20; + public const float Subtitle1 = 16; + public const float Subtitle2 = 14; + public const float Body1 = 16; + public const float Body2 = 14; + public const float Button = 14; + public const double Caption = 12; + public const float Overline = 10; + } + } + + internal static class Cupertino + { + public static class Color + { + public static class SystemColor + { + public static class Light + { + public const string Blue = "#007AFF"; + public const string Green = "#33C759"; + public const string Indigo = "#5856D6"; + public const string Orange = "#FF9500"; + public const string Pink = "#FF2D55"; + public const string Purple = "#AF52DE"; + public const string Red = "#FF3B30"; + public const string Teal = "#59C8FA"; + public const string Yellow = "#FFCC00"; + } + + public static class Dark + { + public const string Blue = "#0684FF"; + public const string Green = "#30D158"; + public const string Indigo = "#5E5CE6"; + public const string Orange = "#FF9F08"; + public const string Pink = "#FF375F"; + public const string Purple = "#BF5AF2"; + public const string Red = "#FF443A"; + public const string Teal = "#65D1FF"; + public const string Yellow = "#FFD606"; + } + } + + public static class SystemGray + { + public static class Light + { + public const string Gray1 = "#8E8E93"; + public const string Gray2 = "#C7C7CC"; + public const string Gray3 = "#D1D1D6"; + public const string Gray4 = "#E5E5EA"; + public const string Gray5 = "#EFEFF4"; + public const string Gray6 = "#F2F2F7"; + public const string InactiveGray = "#999999"; + } + + public static class Dark + { + public const string Gray1 = "#8E8E93"; + public const string Gray2 = "#636366"; + public const string Gray3 = "#48484A"; + public const string Gray4 = "#3A3A3C"; + public const string Gray5 = "#2C2C2E"; + public const string Gray6 = "#1C1C1E"; + public const string InactiveGray = "#757575"; + } + } + + public static class Label + { + public static class Light + { + public const string Primary = "#000000"; + public const string Secondary = "#7C7C80"; + public const string Tertiary = "#ACACAE"; + public const string Quaternary = "#BFBFC0"; + public const string Link = "#0077FF"; + public const string Black = "#000000"; + public const string Error = "#FF453A"; + } + + public static class Dark + { + public const string Primary = "#FFFFFF"; + public const string Secondary = "#A0A0A7"; + public const string Tertiary = "#67676A"; + public const string Quaternary = "#515153"; + public const string Link = "#007AFF"; + public const string Black = "#000000"; + public const string Error = "#FF453A"; + } + } + + public static class Fill + { + public static class Light + { + public const string Black = "#000000"; + public const string White = "#99EBEBF5"; + public const string Primary = "#C8C8CA"; + public const string Secondary = "#CCCCCD"; + public const string Tertiary = "#D0D0D1"; + public const string Quaternary = "#D4D4D5"; + public const string NonOpaqueSeparator = "#AEAEB0"; + public const string OpaqueSeparator = "#C6C6C8"; + } + + public static class Dark + { + public const string Black = "#000000"; + public const string White = "#99EBEBF5"; + public const string Primary = "#49494C"; + public const string Secondary = "#464649"; + public const string Tertiary = "#404042"; + public const string Quaternary = "#3B3B3E"; + public const string NonOpaqueSeparator = "#47474A"; + public const string OpaqueSeparator = "#38383A"; + } + } + + public static class Background + { + public static class Light + { + public const string Primary = "#FFFFFF"; + public const string Secondary = "#F2F2F7"; + public const string Transparent = "#FFFFFFFF"; + } + + public static class Dark + { + public const string Primary = "#1C1C1E"; + public const string Secondary = "#2C2C2E"; + public const string Transparent = "#FFFFFFFF"; + } + } + } + + public static class Font + { + public const double LargeTitle = 34; + public const double Title1 = 28; + public const double Title2 = 22; + public const double Title3 = 20; + public const double Headline = 17; + public const double Body = 17; + public const double Callout = 16; + public const double Subhead = 15; + public const double Footnote = 13; + public const double Caption1 = 12; + public const double Caption2 = 11; + } + } + + internal static class Fluent + { + public static class Color + { + public static class Primary + { + public const string ThemeDarker = "#004578"; + public const string ThemeDark = "#005a9e"; + public const string ThemeDarkAlt = "#106ebe"; + public const string ThemePrimary = "#0078d4"; + public const string ThemeSecondary = "#2b88d8"; + public const string ThemeTertiary = "#71afe5"; + public const string ThemeLight = "#c7e0f4"; + public const string ThemeLighter = "#deecf9"; + public const string ThemeLighterAlt = "#eff6fc"; + } + + public static class Foreground + { + public const string Black = "#000000"; + public const string NeutralDark = "#201f1e"; + public const string NeutralPrimary = "#323130"; + public const string NeutralPrimaryAlt = "#3b3a39"; + public const string NeutralSecondary = "#605e5c"; + public const string NeutralTertiary = "#a19f9d"; + public const string White = "#ffffff"; + } + + public static class Background + { + public const string NeutralTertiaryAlt = "#c8c6c4"; + public const string NeutralDark = "#201f1e"; + public const string NeutralQuaternaryAlt = "#e1dfdd"; + public const string NeutralLight = "#edebe9"; + public const string NeutralLighter = "#f3f2f1"; + public const string NeutralLighterAlt = "#faf9f8"; + } + } + + public static class Font + { + public const double Header = 46; + public const double SubHeader = 34; + public const double Title = 24; + public const double SubTitle = 20; + public const double Base = 14; + public const double Body = 14; + public const double Caption = 12; + } + } +} diff --git a/src/Tizen.UIExtensions.Common/GraphicsView/GraphicsViewDrawable.cs b/src/Tizen.UIExtensions.Common/GraphicsView/GraphicsViewDrawable.cs new file mode 100644 index 0000000..bd8588c --- /dev/null +++ b/src/Tizen.UIExtensions.Common/GraphicsView/GraphicsViewDrawable.cs @@ -0,0 +1,40 @@ +using System; +using Microsoft.Maui.Graphics; +using GPoint = Microsoft.Maui.Graphics.Point; +using TSize = Tizen.UIExtensions.Common.Size; + +namespace Tizen.UIExtensions.Common.GraphicsView +{ + public abstract class GraphicsViewDrawable : IDrawable, IMeasurable, IDisposable + { + public event EventHandler? Invalidated; + public abstract void Draw(ICanvas canvas, RectangleF dirtyRect); + + public virtual void OnTouchDown(GPoint point) { } + + public virtual void OnTouchUp(GPoint point) { } + + public virtual void OnTouchMove(GPoint point) { } + + public virtual void OnFocused() { } + + public virtual void OnUnfocused() { } + + protected void SendInvalidated() + { + Invalidated?.Invoke(this, EventArgs.Empty); + } + + public abstract TSize Measure(double availableWidth, double availableHeight); + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + } + } +} diff --git a/src/Tizen.UIExtensions.Common/GraphicsView/IActivityIndicator.cs b/src/Tizen.UIExtensions.Common/GraphicsView/IActivityIndicator.cs new file mode 100644 index 0000000..5493e8c --- /dev/null +++ b/src/Tizen.UIExtensions.Common/GraphicsView/IActivityIndicator.cs @@ -0,0 +1,9 @@ +namespace Tizen.UIExtensions.Common.GraphicsView +{ + public interface IActivityIndicator + { + bool IsRunning { get; } + + Color Color { get; } + } +} diff --git a/src/Tizen.UIExtensions.Common/GraphicsView/IButton.cs b/src/Tizen.UIExtensions.Common/GraphicsView/IButton.cs new file mode 100644 index 0000000..ca10450 --- /dev/null +++ b/src/Tizen.UIExtensions.Common/GraphicsView/IButton.cs @@ -0,0 +1,15 @@ +namespace Tizen.UIExtensions.Common.GraphicsView +{ + public interface IButton + { + bool IsPressed { get; } + + string Text { get; } + + Color TextColor { get; } + + Color BackgroundColor { get; } + + double CornerRadius { get; } + } +} diff --git a/src/Tizen.UIExtensions.Common/GraphicsView/ICheckBox.cs b/src/Tizen.UIExtensions.Common/GraphicsView/ICheckBox.cs new file mode 100644 index 0000000..9d5a926 --- /dev/null +++ b/src/Tizen.UIExtensions.Common/GraphicsView/ICheckBox.cs @@ -0,0 +1,18 @@ +namespace Tizen.UIExtensions.Common.GraphicsView +{ public interface ICheckBox + { + /// + /// Gets a value that indicates whether the CheckBox is checked. + /// + bool IsChecked { get; set; } + + /// + /// Gets a Color value that defines the display color. + /// + Color Color { get; } + + Color TextColor { get; } + + string Text { get; } + } +} diff --git a/src/Tizen.UIExtensions.Common/GraphicsView/IEditor.cs b/src/Tizen.UIExtensions.Common/GraphicsView/IEditor.cs new file mode 100644 index 0000000..902bdec --- /dev/null +++ b/src/Tizen.UIExtensions.Common/GraphicsView/IEditor.cs @@ -0,0 +1,16 @@ +namespace Tizen.UIExtensions.Common.GraphicsView +{ + public interface IEditor + { + string Text { get; set; } + + Color TextColor { get; } + + string Placeholder { get; } + + Color PlaceholderColor { get; } + + Color BackgroundColor { get; } + bool IsFocused { get; } + } +} diff --git a/src/Tizen.UIExtensions.Common/GraphicsView/IEntry.cs b/src/Tizen.UIExtensions.Common/GraphicsView/IEntry.cs new file mode 100644 index 0000000..937b339 --- /dev/null +++ b/src/Tizen.UIExtensions.Common/GraphicsView/IEntry.cs @@ -0,0 +1,16 @@ +namespace Tizen.UIExtensions.Common.GraphicsView +{ + public interface IEntry + { + string Text { get; set; } + + Color TextColor { get; } + + string Placeholder { get; } + + Color PlaceholderColor { get; } + + Color BackgroundColor { get; } + bool IsFocused { get; } + } +} diff --git a/src/Tizen.UIExtensions.Common/GraphicsView/IProgressBar.cs b/src/Tizen.UIExtensions.Common/GraphicsView/IProgressBar.cs new file mode 100644 index 0000000..9ac52e9 --- /dev/null +++ b/src/Tizen.UIExtensions.Common/GraphicsView/IProgressBar.cs @@ -0,0 +1,9 @@ +namespace Tizen.UIExtensions.Common.GraphicsView +{ + public interface IProgressBar + { + double Progress { get; set; } + + Color ProgressColor { get; } + } +} diff --git a/src/Tizen.UIExtensions.Common/GraphicsView/ISlider.cs b/src/Tizen.UIExtensions.Common/GraphicsView/ISlider.cs new file mode 100644 index 0000000..9f27d0f --- /dev/null +++ b/src/Tizen.UIExtensions.Common/GraphicsView/ISlider.cs @@ -0,0 +1,17 @@ +namespace Tizen.UIExtensions.Common.GraphicsView +{ + public interface ISlider + { + double Minimum { get; } + + double Maximum { get; } + + double Value { get; set; } + + Color MinimumTrackColor { get; } + + Color MaximumTrackColor { get; } + + Color ThumbColor { get; } + } +} diff --git a/src/Tizen.UIExtensions.Common/GraphicsView/IStepper.cs b/src/Tizen.UIExtensions.Common/GraphicsView/IStepper.cs new file mode 100644 index 0000000..4481179 --- /dev/null +++ b/src/Tizen.UIExtensions.Common/GraphicsView/IStepper.cs @@ -0,0 +1,12 @@ +namespace Tizen.UIExtensions.Common.GraphicsView +{ + public interface IStepper + { + public double Minimum { get; } + public double Maximum { get; } + + public double Increment { get; } + + public double Value { get; set; } + } +} diff --git a/src/Tizen.UIExtensions.Common/GraphicsView/ISwitch.cs b/src/Tizen.UIExtensions.Common/GraphicsView/ISwitch.cs new file mode 100644 index 0000000..3dc11ff --- /dev/null +++ b/src/Tizen.UIExtensions.Common/GraphicsView/ISwitch.cs @@ -0,0 +1,13 @@ +namespace Tizen.UIExtensions.Common.GraphicsView +{ + public interface ISwitch + { + bool IsToggled { get; set; } + + Color OnColor { get; } + + Color ThumbColor { get; } + + Color BackgroundColor { get; } + } +} diff --git a/src/Tizen.UIExtensions.Common/GraphicsView/ProgressBarDrawable.cs b/src/Tizen.UIExtensions.Common/GraphicsView/ProgressBarDrawable.cs new file mode 100644 index 0000000..fc5a799 --- /dev/null +++ b/src/Tizen.UIExtensions.Common/GraphicsView/ProgressBarDrawable.cs @@ -0,0 +1,61 @@ +using Microsoft.Maui.Graphics; +using GColor = Microsoft.Maui.Graphics.Color; +using TSize = Tizen.UIExtensions.Common.Size; + +namespace Tizen.UIExtensions.Common.GraphicsView +{ + public class ProgressBarDrawable : GraphicsViewDrawable + { + const float MaterialTrackHeight = 4.0f; + + public ProgressBarDrawable(IProgressBar view) + { + View = view; + } + + IProgressBar View { get; } + + public override void Draw(ICanvas canvas, RectangleF dirtyRect) + { + DrawMaterialProgressTrack(canvas, dirtyRect); + DrawMaterialProgressBar(canvas, dirtyRect); + } + + public override TSize Measure(double availableWidth, double availableHeight) + { + return new TSize(availableWidth, MaterialTrackHeight * DeviceInfo.ScalingFactor); + } + + void DrawMaterialProgressTrack(ICanvas canvas, RectangleF dirtyRect) + { + canvas.SaveState(); + + canvas.FillColor = GColor.FromArgb(Fluent.Color.Background.NeutralLight); + + var x = dirtyRect.X; + var y = (float)((dirtyRect.Height - MaterialTrackHeight) / 2); + + var width = dirtyRect.Width; + + canvas.FillRectangle(x, y, width, MaterialTrackHeight); + + canvas.RestoreState(); + } + + void DrawMaterialProgressBar(ICanvas canvas, RectangleF dirtyRect) + { + canvas.SaveState(); + + canvas.FillColor = View.ProgressColor.ToGraphicsColor(Material.Color.Blue); + + var x = dirtyRect.X; + var y = (float)((dirtyRect.Height - MaterialTrackHeight) / 2); + + var width = dirtyRect.Width; + + canvas.FillRectangle(x, y, (float)(width * View.Progress), MaterialTrackHeight); + + canvas.RestoreState(); + } + } +} diff --git a/src/Tizen.UIExtensions.Common/GraphicsView/RippleEffectDrawable.cs b/src/Tizen.UIExtensions.Common/GraphicsView/RippleEffectDrawable.cs new file mode 100644 index 0000000..c7c2d82 --- /dev/null +++ b/src/Tizen.UIExtensions.Common/GraphicsView/RippleEffectDrawable.cs @@ -0,0 +1,87 @@ +using Microsoft.Maui.Graphics; +using Tizen.UIExtensions.Common.Internal; +using GColor = Microsoft.Maui.Graphics.Color; +using GPoint = Microsoft.Maui.Graphics.Point; +using TSize = Tizen.UIExtensions.Common.Size; + +namespace Tizen.UIExtensions.Common.GraphicsView +{ + public class RippleEffectDrawable : GraphicsViewDrawable, IAnimatable + { + float _rippleEffectSize; + + public RippleEffectDrawable() + { + RippleColor = Colors.White; + } + + public override void Draw(ICanvas canvas, RectangleF dirtyRect) + { + if (ClipRectangle == RectangleF.Zero || ClipRectangle.Contains(TouchPoint)) + { + canvas.SaveState(); + + if (ClipRectangle == RectangleF.Zero) + ClipRectangle = dirtyRect; + + canvas.ClipRectangle(ClipRectangle); + + canvas.FillColor = RippleColor; + canvas.Alpha = 0.25f; + canvas.FillCircle((float)TouchPoint.X, (float)TouchPoint.Y, RippleEffectSize); + + canvas.RestoreState(); + } + } + + public override TSize Measure(double availableWidth, double availableHeight) + { + return new TSize(availableWidth, availableHeight); + } + + public GColor RippleColor { get; set; } + + public RectangleF ClipRectangle { get; set; } + + public PointF TouchPoint { get; set; } + + float RippleEffectSize + { + get { return _rippleEffectSize; } + set + { + _rippleEffectSize = value; + SendInvalidated(); + } + } + + public override void OnTouchDown(GPoint point) + { + var touchDownPoint = new PointF((float)point.X, (float)point.Y); + TouchPoint = touchDownPoint; + } + + public override void OnTouchUp(GPoint point) + { + if (ClipRectangle == RectangleF.Zero || ClipRectangle.Contains(TouchPoint)) + AnimateDrawRipple(); + } + + void AnimateDrawRipple() + { + var from = 0; + var to = ClipRectangle != RectangleF.Zero ? ClipRectangle.Width : 1000; + + var thumbSizeAnimation = new Animation(v => RippleEffectSize = (int)v, from, to, easing: Easing.SinInOut); + thumbSizeAnimation.Commit(this, "RippleEffectAnimation", length: 350, finished: (l, c) => + { + _rippleEffectSize = 0; + thumbSizeAnimation = null; + }); + } + + void IAnimatable.BatchBegin() { } + + void IAnimatable.BatchCommit() { } + } +} diff --git a/src/Tizen.UIExtensions.Common/GraphicsView/SliderDrawable.cs b/src/Tizen.UIExtensions.Common/GraphicsView/SliderDrawable.cs new file mode 100644 index 0000000..2cf68b4 --- /dev/null +++ b/src/Tizen.UIExtensions.Common/GraphicsView/SliderDrawable.cs @@ -0,0 +1,164 @@ +using Microsoft.Maui.Graphics; +using Tizen.UIExtensions.Common.Internal; +using GPoint = Microsoft.Maui.Graphics.Point; +using TSize = Tizen.UIExtensions.Common.Size; + +namespace Tizen.UIExtensions.Common.GraphicsView +{ + public class SliderDrawable : GraphicsViewDrawable, IAnimatable + { + const float NormalMaterialThumbSize = 12f; + const float SelectedMaterialThumbSize = 18f; + + bool _isThumbSelected; + + public SliderDrawable(ISlider view) + { + View = view; + } + + ISlider View { get; } + + float MaterialFloatThumb { get; set; } = NormalMaterialThumbSize; + + public RectangleF ThumbRect { get; set; } + public RectangleF TrackRect { get; set; } + + public double ValueRate + { + get + { + return View.Value / View.Maximum; + } + set + { + double start = View.Minimum; + double diff = View.Maximum - View.Minimum; + View.Value = start + diff * value; + } + } + + public override void Draw(ICanvas canvas, RectangleF dirtyRect) + { + DrawMaterialSliderTrackBackground(canvas, dirtyRect); + DrawMaterialSliderTrackProgress(canvas, dirtyRect); + DrawMaterialSliderThumb(canvas, dirtyRect); + } + + public override TSize Measure(double availableWidth, double availableHeight) + { + return new TSize(availableWidth, DeviceInfo.ScalingFactor * SelectedMaterialThumbSize); + } + + public override void OnTouchDown(GPoint point) + { + _isThumbSelected = ThumbRect.Contains(new PointF((float)point.X, (float)point.Y)); + + if (_isThumbSelected) + AnimateMaterialThumbSize(true); + UpdateValue(point); + } + + public override void OnTouchUp(GPoint point) + { + if (_isThumbSelected) + { + _isThumbSelected = false; + AnimateMaterialThumbSize(false); + } + } + + public override void OnTouchMove(GPoint point) + { + UpdateValue(point); + } + + + void UpdateValue(GPoint point) + { + ValueRate = (point.X - TrackRect.X) / TrackRect.Width; + SendInvalidated(); + } + + void DrawMaterialSliderTrackBackground(ICanvas canvas, RectangleF dirtyRect) + { + canvas.SaveState(); + + canvas.FillColor = View.MaximumTrackColor.ToGraphicsColor(Material.Color.LightBlue); + + var x = dirtyRect.X; + + var width = dirtyRect.Width; + var height = 2; + + var y = (float)((dirtyRect.Height - height) / 2); + + canvas.FillRoundedRectangle(x, y, width, height, 0); + + canvas.RestoreState(); + + TrackRect = new RectangleF(x, y, width, height); + } + + protected virtual void DrawMaterialSliderTrackProgress(ICanvas canvas, RectangleF dirtyRect) + { + canvas.SaveState(); + + canvas.FillColor = View.MinimumTrackColor.ToGraphicsColor(Material.Color.Blue); + + var x = dirtyRect.X; + + var value = ((double)ValueRate).Clamp(0, 1); + var width = (float)(dirtyRect.Width * value); + + var height = 2; + + var y = (float)((dirtyRect.Height - height) / 2); + + canvas.FillRoundedRectangle(x, y, width, height, 0); + + canvas.RestoreState(); + } + + protected virtual void DrawMaterialSliderThumb(ICanvas canvas, RectangleF dirtyRect) + { + canvas.SaveState(); + + var value = ((double)ValueRate).Clamp(0, 1); + var x = (float)((dirtyRect.Width * value) - (MaterialFloatThumb / 2)); + + if (x <= 0) + x = 0; + + if (x >= dirtyRect.Width - MaterialFloatThumb) + x = dirtyRect.Width - MaterialFloatThumb; + + var y = (float)((dirtyRect.Height - MaterialFloatThumb) / 2); + + canvas.FillColor = View.ThumbColor.ToGraphicsColor(Material.Color.Blue); + + canvas.FillEllipse(x, y, MaterialFloatThumb, MaterialFloatThumb); + + canvas.RestoreState(); + + ThumbRect = new RectangleF(x, y, MaterialFloatThumb, MaterialFloatThumb); + } + + public void AnimateMaterialThumbSize(bool increase) + { + float start = increase ? NormalMaterialThumbSize : SelectedMaterialThumbSize; + float end = increase ? SelectedMaterialThumbSize : NormalMaterialThumbSize; + + var thumbSizeAnimation = new Animation(v => + { + MaterialFloatThumb = (int)v; + SendInvalidated(); + }, start, end, easing: Easing.SinInOut); + thumbSizeAnimation.Commit(this, "ThumbSizeAnimation", length: 50, finished: (l, c) => thumbSizeAnimation = null); + } + + void IAnimatable.BatchBegin() { } + + void IAnimatable.BatchCommit() { } + } +} diff --git a/src/Tizen.UIExtensions.Common/GraphicsView/StepperDrawable.cs b/src/Tizen.UIExtensions.Common/GraphicsView/StepperDrawable.cs new file mode 100644 index 0000000..de05a3c --- /dev/null +++ b/src/Tizen.UIExtensions.Common/GraphicsView/StepperDrawable.cs @@ -0,0 +1,134 @@ +using Microsoft.Maui.Graphics; +using GColor = Microsoft.Maui.Graphics.Color; +using GPoint = Microsoft.Maui.Graphics.Point; +using TSize = Tizen.UIExtensions.Common.Size; + +namespace Tizen.UIExtensions.Common.GraphicsView +{ + public class StepperDrawable : GraphicsViewDrawable + { + const string MaterialStepperMinusIcon = "M0.990234 1.96143H13.0098C13.5161 1.96143 13.9478 1.53809 13.9478 1.01514C13.9478 0.500488 13.5161 0.0688477 13.0098 0.0688477H0.990234C0.500488 0.0688477 0.0522461 0.500488 0.0522461 1.01514C0.0522461 1.53809 0.500488 1.96143 0.990234 1.96143Z"; + const string MaterialStepperPlusIcon = "M0.990234 7.95312H6.05371V13.0166C6.05371 13.5312 6.47705 13.9629 7 13.9629C7.52295 13.9629 7.94629 13.5312 7.94629 13.0166V7.95312H13.0098C13.5244 7.95312 13.9561 7.52979 13.9561 7.00684C13.9561 6.48389 13.5244 6.06055 13.0098 6.06055H7.94629V0.99707C7.94629 0.482422 7.52295 0.0507812 7 0.0507812C6.47705 0.0507812 6.05371 0.482422 6.05371 0.99707V6.06055H0.990234C0.475586 6.06055 0.0439453 6.48389 0.0439453 7.00684C0.0439453 7.52979 0.475586 7.95312 0.990234 7.95312Z"; + + const float MaterialStepperHeight = 40.0f; + const float MaterialStepperWidth = 110.0f; + const float MaterialButtonMargin = 6.0f; + + RectangleF _minusRect; + RectangleF _plusRect; + + readonly RippleEffectDrawable _minusRippleEffect; + readonly RippleEffectDrawable _plusRippleEffect; + + public StepperDrawable(IStepper view) + { + _minusRippleEffect = new RippleEffectDrawable + { + RippleColor = GColor.FromArgb(Material.Color.Gray6) + }; + _plusRippleEffect = new RippleEffectDrawable + { + RippleColor = GColor.FromArgb(Material.Color.Gray6) + }; + + _minusRippleEffect.Invalidated += (s, e) => SendInvalidated(); + _plusRippleEffect.Invalidated += (s, e) => SendInvalidated(); + + View = view; + } + + IStepper View { get; } + + public override void Draw(ICanvas canvas, RectangleF dirtyRect) + { + DrawMaterialStepperMinus(canvas, dirtyRect); + DrawMaterialStepperPlus(canvas, dirtyRect); + _minusRippleEffect.Draw(canvas, dirtyRect); + _plusRippleEffect.Draw(canvas, dirtyRect); + } + + public override TSize Measure(double availableWidth, double availableHeight) + { + return new TSize((MaterialStepperWidth + MaterialButtonMargin) * DeviceInfo.ScalingFactor, MaterialStepperHeight * DeviceInfo.ScalingFactor); + } + + public override void OnTouchDown(GPoint point) + { + var touchDownPoint = new PointF((float)point.X, (float)point.Y); + + if (_minusRect.Contains(touchDownPoint)) + View.Value -= View.Increment; + + if (_plusRect.Contains(touchDownPoint)) + View.Value += View.Increment; + + _minusRippleEffect.ClipRectangle = _minusRect; + _plusRippleEffect.ClipRectangle = _plusRect; + + _plusRippleEffect.OnTouchDown(point); + _minusRippleEffect.OnTouchDown(point); + } + + public override void OnTouchUp(GPoint point) + { + _minusRippleEffect.OnTouchUp(point); + _plusRippleEffect.OnTouchUp(point); + } + + void DrawMaterialStepperMinus(ICanvas canvas, RectangleF dirtyRect) + { + canvas.SaveState(); + + canvas.StrokeSize = 1; + canvas.StrokeColor = GColor.FromArgb(Material.Color.Gray6); + + var x = dirtyRect.X+1; + var y = dirtyRect.Y; + + var height = MaterialStepperHeight; + var width = MaterialStepperWidth / 2; + + canvas.DrawRoundedRectangle(x, y, width, height, 6); + + canvas.Translate((float)(20 * DeviceInfo.ScalingFactor), (float)(20 * DeviceInfo.ScalingFactor)); + + var vBuilder = new PathBuilder(); + var path = vBuilder.BuildPath(MaterialStepperMinusIcon); + + canvas.FillColor = GColor.FromArgb(Material.Color.Black); + canvas.FillPath(path); + + canvas.RestoreState(); + + _minusRect = new RectangleF(x, y, width, height); + } + + void DrawMaterialStepperPlus(ICanvas canvas, RectangleF dirtyRect) + { + canvas.SaveState(); + + canvas.StrokeSize = 1; + canvas.StrokeColor = GColor.FromArgb(Material.Color.Gray6); + + var x = MaterialStepperWidth / 2 + MaterialButtonMargin; + var y = dirtyRect.Y; + + var height = MaterialStepperHeight; + var width = MaterialStepperWidth / 2; + + canvas.DrawRoundedRectangle(x, y, width, height, 6); + + canvas.Translate((float)(80 * DeviceInfo.ScalingFactor), (float)(14 * DeviceInfo.ScalingFactor)); + + var vBuilder = new PathBuilder(); + var path = vBuilder.BuildPath(MaterialStepperPlusIcon); + + canvas.FillColor = GColor.FromArgb(Material.Color.Black); + canvas.FillPath(path); + + canvas.RestoreState(); + + _plusRect = new RectangleF(x, y, width, height); + } + } +} diff --git a/src/Tizen.UIExtensions.Common/GraphicsView/SwitchDrawable.cs b/src/Tizen.UIExtensions.Common/GraphicsView/SwitchDrawable.cs new file mode 100644 index 0000000..d47ce15 --- /dev/null +++ b/src/Tizen.UIExtensions.Common/GraphicsView/SwitchDrawable.cs @@ -0,0 +1,113 @@ +using Microsoft.Maui.Graphics; +using Tizen.UIExtensions.Common.Internal; +using GPoint = Microsoft.Maui.Graphics.Point; +using TSize = Tizen.UIExtensions.Common.Size; + +namespace Tizen.UIExtensions.Common.GraphicsView +{ + public class SwitchDrawable : GraphicsViewDrawable, IAnimatable + { + const float MaterialThumbOffPosition = 12f; + const float MaterialThumbOnPosition = 34f; + const float MaterialSwitchBackgroundWidth = 34; + const float MaterialSwitchBackgroundMargin = 5; + + public SwitchDrawable(ISwitch view) + { + View = view; + } + + ISwitch View { get; } + + public override void Draw(ICanvas canvas, RectangleF dirtyRect) + { + DrawMaterialSwitchBackground(canvas, dirtyRect); + DrawMaterialSwitchThumb(canvas, dirtyRect); + } + + public override TSize Measure(double availableWidth, double availableHeight) + { + return new TSize((MaterialSwitchBackgroundWidth + 10) * DeviceInfo.ScalingFactor, 24 * DeviceInfo.ScalingFactor); + } + + + public override void OnTouchDown(GPoint point) + { + View.IsToggled = !View.IsToggled; + AnimateMaterialSwitchThumb(View.IsToggled); + } + + float _materialSwitchThumbPosition = MaterialThumbOffPosition; + + float MaterialSwitchThumbPosition + { + get { return _materialSwitchThumbPosition; } + set + { + _materialSwitchThumbPosition = value; + SendInvalidated(); + } + } + + void DrawMaterialSwitchBackground(ICanvas canvas, RectangleF dirtyRect) + { + canvas.SaveState(); + + if (View.IsToggled) + { + canvas.FillColor = View.OnColor.ToGraphicsColor(Material.Color.LightBlue); + canvas.Alpha = 0.5f; + } + else + { + canvas.FillColor = View.BackgroundColor.ToGraphicsColor(Material.Color.Gray2); + canvas.Alpha = 1.0f; + } + + var margin = MaterialSwitchBackgroundMargin; + + var x = dirtyRect.X + margin; + var y = dirtyRect.Y + margin; + + var height = 14; + var width = MaterialSwitchBackgroundWidth; + + canvas.FillRoundedRectangle(x, y, width, height, 10); + + canvas.RestoreState(); + } + + void DrawMaterialSwitchThumb(ICanvas canvas, RectangleF dirtyRect) + { + canvas.SaveState(); + + if (View.IsToggled) + canvas.FillColor = View.ThumbColor.ToGraphicsColor(Material.Color.Blue); + else + canvas.FillColor = View.ThumbColor.ToGraphicsColor(Fluent.Color.Foreground.White); + + var margin = 2; + var radius = 10; + + var y = dirtyRect.Y + margin + radius; + + canvas.SetShadow(new SizeF(0, 1), 2, CanvasDefaults.DefaultShadowColor); + canvas.FillCircle(MaterialSwitchThumbPosition, y, radius); + + canvas.RestoreState(); + } + + void AnimateMaterialSwitchThumb(bool on) + { + float start = on ? MaterialThumbOffPosition : MaterialThumbOnPosition; + float end = on ? MaterialThumbOnPosition : MaterialThumbOffPosition; + + var thumbPositionAnimation = new Animation(v => MaterialSwitchThumbPosition = (int)v, start, end, easing: Easing.Linear); + thumbPositionAnimation.Commit(this, "MaterialSwitchThumbAnimation", length: 100, finished: (l, c) => thumbPositionAnimation = null); + } + + void IAnimatable.BatchBegin() { } + + void IAnimatable.BatchCommit() { } + } +} diff --git a/src/Tizen.UIExtensions.Common/Tizen.UIExtensions.Common.projitems b/src/Tizen.UIExtensions.Common/Tizen.UIExtensions.Common.projitems index f74be91..ce1bc79 100644 --- a/src/Tizen.UIExtensions.Common/Tizen.UIExtensions.Common.projitems +++ b/src/Tizen.UIExtensions.Common/Tizen.UIExtensions.Common.projitems @@ -9,17 +9,44 @@ Tizen.UIExtensions.Common + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -40,7 +67,7 @@ - + diff --git a/src/Tizen.UIExtensions.ElmSharp/DateTimePickerDialog.cs b/src/Tizen.UIExtensions.ElmSharp/DateTimePickerDialog.cs index 1b18dbb..c236d2d 100644 --- a/src/Tizen.UIExtensions.ElmSharp/DateTimePickerDialog.cs +++ b/src/Tizen.UIExtensions.ElmSharp/DateTimePickerDialog.cs @@ -2,6 +2,7 @@ using ElmSharp; using DateChangedEventArgs = Tizen.UIExtensions.Common.DateChangedEventArgs; using EButton = ElmSharp.Button; +using DeviceInfo = Tizen.UIExtensions.Common.DeviceInfo; namespace Tizen.UIExtensions.ElmSharp { diff --git a/src/Tizen.UIExtensions.ElmSharp/DeviceInfo.cs b/src/Tizen.UIExtensions.ElmSharp/DeviceInfo.cs index bb016f6..892d651 100644 --- a/src/Tizen.UIExtensions.ElmSharp/DeviceInfo.cs +++ b/src/Tizen.UIExtensions.ElmSharp/DeviceInfo.cs @@ -1,10 +1,11 @@ using ElmSharp; using System; +using Tizen.UIExtensions.ElmSharp; using TSystemInfo = Tizen.System.Information; -namespace Tizen.UIExtensions.ElmSharp +namespace Tizen.UIExtensions.Common { - public static class DeviceInfo + public static partial class DeviceInfo { static Lazy s_profile = new Lazy(() => { diff --git a/src/Tizen.UIExtensions.ElmSharp/EditfieldEntry.cs b/src/Tizen.UIExtensions.ElmSharp/EditfieldEntry.cs index e042ad2..6b8813f 100644 --- a/src/Tizen.UIExtensions.ElmSharp/EditfieldEntry.cs +++ b/src/Tizen.UIExtensions.ElmSharp/EditfieldEntry.cs @@ -1,5 +1,6 @@ using System; using ElmSharp; +using DeviceInfo = Tizen.UIExtensions.Common.DeviceInfo; namespace Tizen.UIExtensions.ElmSharp { diff --git a/src/Tizen.UIExtensions.ElmSharp/Extensions/DPExtensions.cs b/src/Tizen.UIExtensions.ElmSharp/Extensions/DPExtensions.cs index 2826335..453b54b 100644 --- a/src/Tizen.UIExtensions.ElmSharp/Extensions/DPExtensions.cs +++ b/src/Tizen.UIExtensions.ElmSharp/Extensions/DPExtensions.cs @@ -1,4 +1,5 @@ using System; +using DeviceInfo = Tizen.UIExtensions.Common.DeviceInfo; namespace Tizen.UIExtensions.ElmSharp { diff --git a/src/Tizen.UIExtensions.ElmSharp/Skia/SKClipperView.cs b/src/Tizen.UIExtensions.ElmSharp/Skia/SKClipperView.cs new file mode 100644 index 0000000..f922483 --- /dev/null +++ b/src/Tizen.UIExtensions.ElmSharp/Skia/SKClipperView.cs @@ -0,0 +1,56 @@ +using ElmSharp; +using SkiaSharp.Views.Tizen; +using System; +using System.Runtime.InteropServices; + +namespace Tizen.UIExtensions.ElmSharp +{ + /// + /// A clipper area drawing view, it used for clipping + /// + public class SKClipperView : SKCanvasView + { + /// + /// Initializes a new instance of the class. + /// + /// Parent of this instance. + public SKClipperView(EvasObject parent) : base(parent) { } + + public bool ClippingRequired { get; set; } + + /// + /// Invalidate clipping area + /// + public new void Invalidate() + { + ClippingRequired = true; + OnDrawFrame(); + ClippingRequired = false; + } + } + + public static class ClipperExtension + { + /// + /// Set Clipper canvas + /// + /// A target view to clip + /// A clip area + public static void SetClipperCanvas(this EvasObject target, SKClipperView clipper) + { + if (target != null && clipper.ClippingRequired) + { + var realHandle = elm_object_part_content_get(clipper, "elm.swallow.content"); + + target.SetClip(null); // To restore original image + evas_object_clip_set(target, realHandle); + } + } + + [DllImport("libevas.so.1")] + internal static extern void evas_object_clip_set(IntPtr obj, IntPtr clip); + + [DllImport("libelementary.so.1")] + internal static extern IntPtr elm_object_part_content_get(IntPtr obj, string part); + } +} diff --git a/src/Tizen.UIExtensions.ElmSharp/ThemeManager.cs b/src/Tizen.UIExtensions.ElmSharp/ThemeManager.cs index 0162bf1..2a1e59a 100644 --- a/src/Tizen.UIExtensions.ElmSharp/ThemeManager.cs +++ b/src/Tizen.UIExtensions.ElmSharp/ThemeManager.cs @@ -9,6 +9,7 @@ using ESlider = ElmSharp.Slider; using EToolbarItem = ElmSharp.ToolbarItem; using TLog = Tizen.UIExtensions.Common.Log; +using DeviceInfo = Tizen.UIExtensions.Common.DeviceInfo; namespace Tizen.UIExtensions.ElmSharp { diff --git a/src/Tizen.UIExtensions.ElmSharp/Tizen.UIExtensions.ElmSharp.csproj b/src/Tizen.UIExtensions.ElmSharp/Tizen.UIExtensions.ElmSharp.csproj index f2ec36d..34349a7 100644 --- a/src/Tizen.UIExtensions.ElmSharp/Tizen.UIExtensions.ElmSharp.csproj +++ b/src/Tizen.UIExtensions.ElmSharp/Tizen.UIExtensions.ElmSharp.csproj @@ -24,7 +24,8 @@ - + + diff --git a/src/Tizen.UIExtensions.NUI/ActionSheetPopup.cs b/src/Tizen.UIExtensions.NUI/ActionSheetPopup.cs new file mode 100644 index 0000000..758eeac --- /dev/null +++ b/src/Tizen.UIExtensions.NUI/ActionSheetPopup.cs @@ -0,0 +1,165 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Tizen.NUI; +using Tizen.NUI.BaseComponents; +using Tizen.UIExtensions.Common; +using TColor = Tizen.UIExtensions.Common.Color; + +namespace Tizen.UIExtensions.NUI +{ + /// + /// A custom popup with list and returning a string value + /// + public class ActionSheetPopup : Popup + { + string _title; + string _cancel; + string? _destruction; + IEnumerable? _buttons; + + /// + /// Initializes a new instance of the class. + /// + /// Title text + /// Cancel text + /// Destruction text + /// A value list to choose + public ActionSheetPopup(string title, string cancel, string? destruction = null, IEnumerable? buttons = null) + { + _title = title; + _cancel = cancel; + _destruction = destruction; + _buttons = buttons; + } + + protected override View CreateContent() + { + Layout = new LinearLayout + { + LinearAlignment = LinearLayout.Alignment.Center + }; + BackgroundColor = new TColor(0.1f, 0.1f, 0.1f, 0.5f).ToNative(); + + var content = new View + { + Layout = new LinearLayout + { + LinearAlignment = LinearLayout.Alignment.Center, + LinearOrientation = LinearLayout.Orientation.Vertical, + }, + SizeWidth = Window.Instance.WindowSize.Width * 0.8f, + BackgroundColor = TColor.White.ToNative(), + }; + content.Add(new Label + { + Text = _title, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent, + Padding = new Extents(10, 10, 10, 10), + HorizontalTextAlignment = TextAlignment.Center, + VerticalTextAlignment = TextAlignment.Center, + FontAttributes = FontAttributes.Bold, + TextColor = TColor.White, + FontSize = 6 * DeviceInfo.ScalingFactor, + BackgroundColor = TColor.FromHex("#344955").ToNative() + }); + + if (_buttons != null) + { + var scrollview = new ScrollView + { + VerticalScrollBarVisibility = ScrollBarVisibility.Always, + WidthSpecification = LayoutParamPolicies.MatchParent, + }; + scrollview.ContentContainer.Layout = new LinearLayout + { + LinearOrientation = LinearLayout.Orientation.Vertical, + }; + content.Add(scrollview); + + foreach (var item in _buttons) + { + var itemLabel = new Label + { + Text = item, + HorizontalTextAlignment = TextAlignment.Center, + PixelSize = (int)(25 * DeviceInfo.ScalingFactor), + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent, + Margin = new Extents(0, 0, 10, 10), + }; + itemLabel.TouchEvent += (s, e) => + { + var state = e.Touch.GetState(0); + if (state == PointStateType.Up) + { + SendSubmit(item); + return true; + } + return false; + }; + scrollview.ContentContainer.Add(itemLabel); + scrollview.ContentContainer.Add(new View + { + BackgroundColor = TColor.Black.ToNative(), + SizeHeight = 2, + WidthSpecification = LayoutParamPolicies.MatchParent, + }); + } + + scrollview.SizeHeight = (float)((DeviceInfo.ScalingFactor * 45 + 2) * Math.Min(_buttons.Count(), 5)); + } + + var hlayout = new View + { + Layout = new LinearLayout + { + LinearAlignment = LinearLayout.Alignment.Center, + LinearOrientation = LinearLayout.Orientation.Horizontal, + }, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent + }; + content.Add(hlayout); + + if (_destruction != null) + { + var destructionButton = new Button + { + Margin = new Extents(20, 20, 10, 10), + Text = _destruction, + SizeWidth = content.SizeWidth * 0.4f, + HeightSpecification = LayoutParamPolicies.WrapContent, + }; + destructionButton.Clicked += (s, e) => SendSubmit(_destruction); + hlayout.Add(destructionButton); + } + + var cancelButton = new Button + { + Margin = new Extents(20, 20, 10, 10), + Text = _cancel, + SizeWidth = content.SizeWidth * 0.4f, + HeightSpecification = LayoutParamPolicies.WrapContent, + }; + cancelButton.Clicked += (s, e) => SendCancel(); + hlayout.Add(cancelButton); + + content.Relayout += (s, e) => + { + hlayout.Children[0].SizeWidth = content.SizeWidth * 0.4f; + if (hlayout.Children.Count > 1) + { + hlayout.Children[1].SizeWidth = content.SizeWidth * 0.4f; + } + }; + Relayout += (s, e) => + { + content.SizeWidth = Window.Instance.WindowSize.Width * 0.8f; + }; + + return content; + } + } +} diff --git a/src/Tizen.UIExtensions.NUI/DeviceInfo.cs b/src/Tizen.UIExtensions.NUI/DeviceInfo.cs index 6faf58f..ad2b349 100644 --- a/src/Tizen.UIExtensions.NUI/DeviceInfo.cs +++ b/src/Tizen.UIExtensions.NUI/DeviceInfo.cs @@ -1,12 +1,12 @@ using System; using System.Diagnostics; using Tizen.NUI; -using Size = Tizen.UIExtensions.Common.Size; +using Tizen.UIExtensions.NUI; using TSystemInfo = Tizen.System.Information; -namespace Tizen.UIExtensions.NUI +namespace Tizen.UIExtensions.Common { - public static class DeviceInfo + public static partial class DeviceInfo { static Lazy s_dpi = new Lazy(() => { diff --git a/src/Tizen.UIExtensions.NUI/GraphicsView/ActivityIndicator.cs b/src/Tizen.UIExtensions.NUI/GraphicsView/ActivityIndicator.cs new file mode 100644 index 0000000..982427e --- /dev/null +++ b/src/Tizen.UIExtensions.NUI/GraphicsView/ActivityIndicator.cs @@ -0,0 +1,51 @@ +using Tizen.UIExtensions.Common; +using Tizen.UIExtensions.Common.GraphicsView; + +namespace Tizen.UIExtensions.NUI.GraphicsView +{ + /// + /// A visual control used to indicate that something is ongoing. + /// + public class ActivityIndicator : GraphicsView, IActivityIndicator + { + /// + /// Initializes a new instance of the ActivityIndicator class. + /// + public ActivityIndicator() + { + Drawable = new ActivityIndicatorDrawable(this); + } + + /// + /// Gets or sets the value indicating if the ActivityIndicator is running. + /// + public bool IsRunning + { + get => GetProperty(nameof(IsRunning)); + set + { + SetProperty(nameof(IsRunning), value); + Drawable?.UpdateAnimation(value); + } + } + + /// + /// Gets or sets the Color of the ActivityIndicator. + /// + public new Color Color + { + get => GetProperty(nameof(Color)); + set => SetProperty(nameof(Color), value); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + Drawable?.Dispose(); + } + base.Dispose(disposing); + } + + } +} diff --git a/src/Tizen.UIExtensions.NUI/GraphicsView/Button.cs b/src/Tizen.UIExtensions.NUI/GraphicsView/Button.cs new file mode 100644 index 0000000..5f49bff --- /dev/null +++ b/src/Tizen.UIExtensions.NUI/GraphicsView/Button.cs @@ -0,0 +1,100 @@ +using System; +using Tizen.UIExtensions.Common; +using Tizen.UIExtensions.Common.GraphicsView; + +namespace Tizen.UIExtensions.NUI.GraphicsView +{ + /// + /// A button View that reacts to touch events. + /// + public class Button : GraphicsView, IButton + { + /// + /// Initializes a new instance of the Button class. + /// + public Button() + { + Drawable = new ButtonDrawable(this); + } + + /// + /// Occurs when the Button is clicked. + /// + public event EventHandler? Clicked; + + /// + /// Occurs when the Button is pressed. + /// + public event EventHandler? Pressed; + + /// + /// Occurs when the Button is released. + /// + public event EventHandler? Released; + + public bool IsPressed + { + get => GetProperty(nameof(IsPressed)); + set => SetProperty(nameof(IsPressed), value); + } + + /// + /// Gets or sets the Text displayed as the content of the button. + /// + public string Text + { + get => GetProperty(nameof(Text)); + set => SetProperty(nameof(Text), value); + } + + /// + /// Gets or sets the Color for the text of the button. + /// + public Color TextColor + { + get => GetProperty(nameof(TextColor)); + set => SetProperty(nameof(TextColor), value); + } + + /// + /// Gets or sets the color which will fill the background of a Button + /// + public new Color BackgroundColor + { + get => GetProperty(nameof(BackgroundColor)); + set => SetProperty(nameof(BackgroundColor), value); + } + Color IButton.BackgroundColor => BackgroundColor; + + /// + /// Gets or sets the corner radius for the button, in device-independent units. + /// + public new double CornerRadius + { + get => GetProperty(nameof(CornerRadius)); + set => SetProperty(nameof(CornerRadius), value); + } + double IButton.CornerRadius => CornerRadius; + + protected override bool OnTouch(object source, TouchEventArgs e) + { + if (!IsEnabled) + return false; + + var state = e.Touch.GetState(0); + + if (state == Tizen.NUI.PointStateType.Down) + { + IsPressed = true; + Pressed?.Invoke(this, EventArgs.Empty); + Clicked?.Invoke(this, EventArgs.Empty); + } + else if (state == Tizen.NUI.PointStateType.Up) + { + IsPressed = false; + Released?.Invoke(this, EventArgs.Empty); + } + return base.OnTouch(source, e); + } + } +} diff --git a/src/Tizen.UIExtensions.NUI/GraphicsView/CheckBox.cs b/src/Tizen.UIExtensions.NUI/GraphicsView/CheckBox.cs new file mode 100644 index 0000000..6137640 --- /dev/null +++ b/src/Tizen.UIExtensions.NUI/GraphicsView/CheckBox.cs @@ -0,0 +1,66 @@ +using System; +using Tizen.UIExtensions.Common; +using Tizen.UIExtensions.Common.GraphicsView; + +namespace Tizen.UIExtensions.NUI.GraphicsView +{ + /// + /// A visual control used to indicate that something is checked. + /// + public class CheckBox : GraphicsView, ICheckBox + { + /// + /// Initializes a new instance of the CheckBox class. + /// + public CheckBox() + { + Text = string.Empty; + Drawable = new CheckBoxDrawable(this); + } + + /// + /// Occurs when IsChecked is changed + /// + public event EventHandler? ValueChanged; + + /// + /// Gets a value indicating whether control is checked currently. + /// + public bool IsChecked + { + get => GetProperty(nameof(IsChecked)); + set + { + SetProperty(nameof(IsChecked), value); + ValueChanged?.Invoke(this, EventArgs.Empty); + } + } + + /// + /// Gets or sets the Color of the CheckBox. + /// + public new Color Color + { + get => GetProperty(nameof(Color)); + set => SetProperty(nameof(Color), value); + } + + /// + /// Gets or sets the Color for the text of the CheckBox + /// + public Color TextColor + { + get => GetProperty(nameof(TextColor)); + set => SetProperty(nameof(TextColor), value); + } + + /// + /// Gets or sets the Text displayed as the content of the CheckBox. + /// + public string Text + { + get => GetProperty(nameof(Text)); + set => SetProperty(nameof(Text), value); + } + } +} diff --git a/src/Tizen.UIExtensions.NUI/GraphicsView/Editor.cs b/src/Tizen.UIExtensions.NUI/GraphicsView/Editor.cs new file mode 100644 index 0000000..e42cf1c --- /dev/null +++ b/src/Tizen.UIExtensions.NUI/GraphicsView/Editor.cs @@ -0,0 +1,88 @@ +using Tizen.NUI.BaseComponents; +using Tizen.UIExtensions.Common; +using Tizen.UIExtensions.Common.GraphicsView; +using TEditor = Tizen.UIExtensions.NUI.Editor; + +namespace Tizen.UIExtensions.NUI.GraphicsView +{ + /// + /// A control that can edit multiple lines of text. + /// + public class Editor : GraphicsView, IEditor + { + /// + /// Initializes a new instance of the Editor class. + /// + public Editor() + { + Layout = new Tizen.NUI.LinearLayout(); + EmbedEditor = new TEditor + { + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.MatchParent, + Margin = new Tizen.NUI.Extents((ushort)(12 * DeviceInfo.ScalingFactor), (ushort)(12 * DeviceInfo.ScalingFactor), (ushort)(20 * DeviceInfo.ScalingFactor), (ushort)(12 * DeviceInfo.ScalingFactor)) + }; + EmbedEditor.FocusGained += OnFocused; + EmbedEditor.FocusLost += OnUnfocused; + EmbedEditor.TextChanged += OnTextChanged; + Add(EmbedEditor); + + Drawable = new EditorDrawable(this); + } + + public TEditor EmbedEditor { get; } + + /// + /// Gets or sets the text of the edtior + /// + public string Text + { + get => EmbedEditor.Text; + set => EmbedEditor.Text = value; + } + + /// + /// Gets or sets the text color. + /// + public Color TextColor + { + get => EmbedEditor.TextColor; + set => EmbedEditor.TextColor = value; + } + + /// + /// Gets or sets the text that is displayed when the control is empty. + /// + public string Placeholder + { + get => GetProperty(nameof(Placeholder)); + set => SetProperty(nameof(Placeholder), value); + } + + /// + /// Gets or sets the color of the placeholder text. + /// + public Color PlaceholderColor + { + get => GetProperty(nameof(PlaceholderColor)); + set => SetProperty(nameof(PlaceholderColor), value); + } + + /// + /// Gets or sets the color which will fill the background + /// + public new Color BackgroundColor + { + get => GetProperty(nameof(BackgroundColor)); + set => SetProperty(nameof(BackgroundColor), value); + } + Color IEditor.BackgroundColor => BackgroundColor; + + public bool IsFocused => EmbedEditor.HasFocus(); + + void OnTextChanged(object? sender, TextEditor.TextChangedEventArgs e) + { + Invalidate(); + } + } +} diff --git a/src/Tizen.UIExtensions.NUI/GraphicsView/Entry.cs b/src/Tizen.UIExtensions.NUI/GraphicsView/Entry.cs new file mode 100644 index 0000000..b14ff2d --- /dev/null +++ b/src/Tizen.UIExtensions.NUI/GraphicsView/Entry.cs @@ -0,0 +1,89 @@ +using Tizen.NUI.BaseComponents; +using Tizen.UIExtensions.Common; +using Tizen.UIExtensions.Common.GraphicsView; +using TEntry = Tizen.UIExtensions.NUI.Entry; + +namespace Tizen.UIExtensions.NUI.GraphicsView +{ + /// + /// A control that can edit a single line of text. + /// + public class Entry : GraphicsView, IEntry + { + /// + /// Initializes a new instance of the Entry class. + /// + public Entry() + { + Layout = new Tizen.NUI.LinearLayout(); + EmbedEntry = new TEntry + { + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.MatchParent, + VerticalTextAlignment = TextAlignment.Center, + Margin = new Tizen.NUI.Extents((ushort)(12 * DeviceInfo.ScalingFactor), (ushort)(40 * DeviceInfo.ScalingFactor), (ushort)(12 * DeviceInfo.ScalingFactor), 0) + }; + EmbedEntry.FocusGained += OnFocused; + EmbedEntry.FocusLost += OnUnfocused; + EmbedEntry.TextChanged += OnTextChanged; + Add(EmbedEntry); + + Drawable = new EntryDrawable(this); + } + + public TEntry EmbedEntry { get; } + + /// + /// Gets or sets the text of the entry + /// + public string Text + { + get => EmbedEntry.Text; + set => EmbedEntry.Text = value; + } + + /// + /// Gets or sets the text color. + /// + public Color TextColor + { + get => EmbedEntry.TextColor; + set => EmbedEntry.TextColor = value; + } + + /// + /// Gets or sets the text that is displayed when the control is empty. + /// + public string Placeholder + { + get => GetProperty(nameof(Placeholder)); + set => SetProperty(nameof(Placeholder), value); + } + + /// + /// Gets or sets the color of the placeholder text. + /// + public Color PlaceholderColor + { + get => GetProperty(nameof(PlaceholderColor)); + set => SetProperty(nameof(PlaceholderColor), value); + } + + /// + /// Gets or sets the color which will fill the background + /// + public new Color BackgroundColor + { + get => GetProperty(nameof(BackgroundColor)); + set => SetProperty(nameof(BackgroundColor), value); + } + Color IEntry.BackgroundColor => BackgroundColor; + + public bool IsFocused => EmbedEntry.HasFocus(); + + void OnTextChanged(object? sender, TextField.TextChangedEventArgs e) + { + Invalidate(); + } + } +} diff --git a/src/Tizen.UIExtensions.NUI/GraphicsView/GraphicsView.cs b/src/Tizen.UIExtensions.NUI/GraphicsView/GraphicsView.cs new file mode 100644 index 0000000..a90866e --- /dev/null +++ b/src/Tizen.UIExtensions.NUI/GraphicsView/GraphicsView.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using Tizen.UIExtensions.Common; +using Tizen.UIExtensions.Common.GraphicsView; +using GPoint = Microsoft.Maui.Graphics.Point; + +namespace Tizen.UIExtensions.NUI.GraphicsView +{ + public abstract class GraphicsView : SkiaGraphicsView, IMeasurable where TDrawable : GraphicsViewDrawable + { + Dictionary _propertyBag = new Dictionary(); + TDrawable? _drawable; + bool _isEnabled = true; + + public bool IsEnabled + { + get => _isEnabled; + set + { + EnableControlState = _isEnabled = value; + Invalidate(); + } + } + + public virtual Size Measure(double availableWidth, double availableHeight) + { + return Drawable?.Measure(availableWidth, availableHeight) ?? new Size(availableWidth, availableHeight); + } + + protected void SetProperty(string name, T value) + { + _propertyBag[name] = value!; + Invalidate(); + } + +#nullable disable + protected T GetProperty(string name) + { + if (_propertyBag.TryGetValue(name, out object value)) + { + return (T)value; + } + return default; + } +#nullable enable + + protected new TDrawable? Drawable + { + get => _drawable; + set + { + if (_drawable != value) + { + if (_drawable != null) + { + _drawable.Invalidated -= OnInvalidated; + } + + base.Drawable = _drawable = value; + + if (value != null) + { + value.Invalidated += OnInvalidated; + } + } + } + } + + protected GraphicsView() + { + TouchEvent += OnTouch; + } + + protected virtual void OnUnfocused(object? sender, EventArgs e) + { + Drawable?.OnUnfocused(); + } + + protected virtual void OnFocused(object? sender, EventArgs e) + { + Drawable?.OnFocused(); + } + + protected virtual bool OnTouch(object source, TouchEventArgs e) + { + if (!IsEnabled) + return false; + + var pos = e.Touch.GetLocalPosition(0); + var state = e.Touch.GetState(0); + var point = new GPoint(pos.X / DeviceInfo.ScalingFactor, pos.Y / DeviceInfo.ScalingFactor); + + if (state == Tizen.NUI.PointStateType.Down) + { + Drawable?.OnTouchDown(point); + } + else if (state == Tizen.NUI.PointStateType.Up) + { + Drawable?.OnTouchUp(point); + } + else if (state == Tizen.NUI.PointStateType.Motion) + { + Drawable?.OnTouchMove(point); + } + + return true; + } + + void OnInvalidated(object? sender, global::System.EventArgs e) + { + Invalidate(); + } + } +} diff --git a/src/Tizen.UIExtensions.NUI/GraphicsView/ProgressBar.cs b/src/Tizen.UIExtensions.NUI/GraphicsView/ProgressBar.cs new file mode 100644 index 0000000..883b5f5 --- /dev/null +++ b/src/Tizen.UIExtensions.NUI/GraphicsView/ProgressBar.cs @@ -0,0 +1,54 @@ +using System.Threading.Tasks; +using Tizen.UIExtensions.Common; +using Tizen.UIExtensions.Common.Internal; +using Tizen.UIExtensions.Common.GraphicsView; + +namespace Tizen.UIExtensions.NUI.GraphicsView +{ + + + /// + /// A View control that displays progress. + /// + public class ProgressBar : GraphicsView, IProgressBar, IAnimatable + { + /// + /// Initializes a new instance of the ProgressBar class + /// + public ProgressBar() + { + Drawable = new ProgressBarDrawable(this); + } + + /// + /// Gets or sets the progress value. + /// + public double Progress + { + get => GetProperty(nameof(Progress)); + set => SetProperty(nameof(Progress), value); + } + + /// + /// Get or sets the color of the progress bar. + /// + public Color ProgressColor + { + get => GetProperty(nameof(ProgressColor)); + set => SetProperty(nameof(ProgressColor), value); + } + + /// + /// Animate the Progress property to value. + /// + public Task ProgressTo(double value) + { + var tcs = new TaskCompletionSource(); + this.Animate("Progress", d => Progress = d, Progress, value, finished: (d, finished) => tcs.SetResult(finished)); + return tcs.Task; + } + + void IAnimatable.BatchBegin() { } + void IAnimatable.BatchCommit() { } + } +} diff --git a/src/Tizen.UIExtensions.NUI/GraphicsView/SkiaGraphicsView.cs b/src/Tizen.UIExtensions.NUI/GraphicsView/SkiaGraphicsView.cs new file mode 100644 index 0000000..0f34c75 --- /dev/null +++ b/src/Tizen.UIExtensions.NUI/GraphicsView/SkiaGraphicsView.cs @@ -0,0 +1,48 @@ +using Microsoft.Maui.Graphics; +using Microsoft.Maui.Graphics.Skia; +using SkiaSharp.Views.Tizen; +using Tizen.UIExtensions.Common; + +namespace Tizen.UIExtensions.NUI.GraphicsView +{ + public class SkiaGraphicsView : SKGLSurfaceView + { + private IDrawable? _drawable; + private SkiaCanvas _canvas; + private ScalingCanvas _scalingCanvas; + + public SkiaGraphicsView(IDrawable? drawable = null) + { + _canvas = new SkiaCanvas(); + _scalingCanvas = new ScalingCanvas(_canvas); + + _scalingCanvas.Scale((float)DeviceInfo.ScalingFactor, (float)DeviceInfo.ScalingFactor); + Drawable = drawable; + PaintSurface += OnPaintSurface; + } + + public IDrawable? Drawable + { + get => _drawable; + set + { + _drawable = value; + Invalidate(); + } + } + + protected virtual void OnPaintSurface(object? sender, SKPaintSurfaceEventArgs e) + { + if (_drawable == null) return; + + var skiaCanvas = e.Surface.Canvas; + skiaCanvas.Clear(); + + var width = (float)(e.Info.Width / DeviceInfo.ScalingFactor); + var height = (float)(e.Info.Height / DeviceInfo.ScalingFactor); + + _canvas.Canvas = skiaCanvas; + _drawable.Draw(_scalingCanvas, new RectangleF(0, 0, width, height)); + } + } +} diff --git a/src/Tizen.UIExtensions.NUI/GraphicsView/Slider.cs b/src/Tizen.UIExtensions.NUI/GraphicsView/Slider.cs new file mode 100644 index 0000000..06012b6 --- /dev/null +++ b/src/Tizen.UIExtensions.NUI/GraphicsView/Slider.cs @@ -0,0 +1,77 @@ +using System; +using Tizen.UIExtensions.Common; +using Tizen.UIExtensions.Common.GraphicsView; +using Color = Tizen.UIExtensions.Common.Color; + +namespace Tizen.UIExtensions.NUI.GraphicsView +{ + /// + /// A View control that inputs a linear value. + /// + public class Slider : GraphicsView, ISlider + { + /// + /// Initializes a new instance of the Slider class. + /// + public Slider() + { + Drawable = new SliderDrawable(this); + SizeHeight = (float)(DeviceInfo.ScalingFactor * 18); + } + + /// + /// Raised when the Value property changes. + /// + public event EventHandler? ValueChanged; + + /// + /// Gets or sets the current value. + /// + public double Value + { + get => GetProperty(nameof(Value)); + set + { + SetProperty(nameof(Value), value.Clamp(Minimum, Maximum)); + ValueChanged?.Invoke(this, EventArgs.Empty); + } + } + + /// + /// Gets or sets the maximum selectable value. + /// + public double Maximum { get; set; } = 1.0; + + /// + /// Gets or sets the minimum selectable value. + /// + public double Minimum { get; set; } = 0; + + /// + /// Gets or sets the color of the portion of the slider track that contains the minimum value of the slider. + /// + public Color MinimumTrackColor + { + get => GetProperty(nameof(MinimumTrackColor)); + set => SetProperty(nameof(MinimumTrackColor), value); + } + + /// + /// Gets or sets the color of the portion of the slider track that contains the maximum value of the slider. + /// + public Color MaximumTrackColor + { + get => GetProperty(nameof(MaximumTrackColor)); + set => SetProperty(nameof(MaximumTrackColor), value); + } + + /// + /// Gets or sets the color of the slider thumb button. + /// + public Color ThumbColor + { + get => GetProperty(nameof(ThumbColor)); + set => SetProperty(nameof(ThumbColor), value); + } + } +} diff --git a/src/Tizen.UIExtensions.NUI/GraphicsView/Stepper.cs b/src/Tizen.UIExtensions.NUI/GraphicsView/Stepper.cs new file mode 100644 index 0000000..22bb071 --- /dev/null +++ b/src/Tizen.UIExtensions.NUI/GraphicsView/Stepper.cs @@ -0,0 +1,69 @@ +using System; +using Tizen.UIExtensions.Common; +using Tizen.UIExtensions.Common.GraphicsView; + +namespace Tizen.UIExtensions.NUI.GraphicsView +{ + /// + /// A View control that inputs a discrete value, constrained to a range. + /// + public class Stepper : GraphicsView, IStepper + { + /// + /// Initializes a new instance of the Stepper class. + /// + public Stepper() + { + Value = 0; + Maximum = 10; + Minimum = 0; + Increment = 1; + Drawable = new StepperDrawable(this); + } + + /// + /// Raised when the Value property changes. + /// + public event EventHandler? ValueChanged; + + /// + /// Gets or sets the current value. + /// + public double Value + { + get => GetProperty(nameof(Value)); + set + { + SetProperty(nameof(Value), value.Clamp(Minimum, Maximum)); + ValueChanged?.Invoke(this, EventArgs.Empty); + } + } + + /// + /// Gets or sets the increment by which Value is increased or decreased. + /// + public double Increment + { + get => GetProperty(nameof(Increment)); + set => SetProperty(nameof(Increment), value); + } + + /// + /// Gets or sets the minimum selectabel value. + /// + public double Minimum + { + get => GetProperty(nameof(Minimum)); + set => SetProperty(nameof(Minimum), value); + } + + /// + /// Gets or sets the maximum selectable value. + /// + public double Maximum + { + get => GetProperty(nameof(Maximum)); + set => SetProperty(nameof(Maximum), value); + } + } +} diff --git a/src/Tizen.UIExtensions.NUI/GraphicsView/Switch.cs b/src/Tizen.UIExtensions.NUI/GraphicsView/Switch.cs new file mode 100644 index 0000000..31f1255 --- /dev/null +++ b/src/Tizen.UIExtensions.NUI/GraphicsView/Switch.cs @@ -0,0 +1,65 @@ +using System; +using Tizen.UIExtensions.Common; +using Tizen.UIExtensions.Common.GraphicsView; + +namespace Tizen.UIExtensions.NUI.GraphicsView +{ + /// + /// A View control that provides a toggled value. + /// + public class Switch : GraphicsView, ISwitch + { + /// + /// Initializes a new instance of the Switch class. + /// + public Switch() + { + Drawable = new SwitchDrawable(this); + } + + /// + /// Event that is raised when this Switch is toggled. + /// + public event EventHandler? Toggled; + + /// + /// Gets or sets a Boolean value that indicates whether this Switch element is toggled. + /// + public bool IsToggled + { + get => GetProperty(nameof(IsToggled)); + set + { + SetProperty(nameof(IsToggled), value); + Toggled?.Invoke(this, EventArgs.Empty); + } + } + + /// + /// Gets or sets the color of the switch when it is in the "On" position. + /// + public Color OnColor + { + get => GetProperty(nameof(OnColor)); + set => SetProperty(nameof(OnColor), value); + } + + /// + /// Gets or sets the color of the thumb + /// + public Color ThumbColor + { + get => GetProperty(nameof(ThumbColor)); + set => SetProperty(nameof(ThumbColor), value); + } + + /// + /// Gets or sets the color which will fill the background. + /// + public new Color BackgroundColor + { + get => GetProperty(nameof(BackgroundColor)); + set => SetProperty(nameof(BackgroundColor), value); + } + } +} diff --git a/src/Tizen.UIExtensions.NUI/MessagePopup.cs b/src/Tizen.UIExtensions.NUI/MessagePopup.cs new file mode 100644 index 0000000..8e24a48 --- /dev/null +++ b/src/Tizen.UIExtensions.NUI/MessagePopup.cs @@ -0,0 +1,136 @@ +using Tizen.NUI; +using Tizen.NUI.BaseComponents; +using Tizen.UIExtensions.Common; +using TColor = Tizen.UIExtensions.Common.Color; + +namespace Tizen.UIExtensions.NUI +{ + /// + /// A custom popup with message and returning a bool value + /// + public class MessagePopup : Popup + { + string _title; + string _message; + string _cancel; + string? _accept; + + /// + /// Initializes a new instance of the class. + /// + /// Title text + /// Message text + /// Accept text + /// Cancel text + public MessagePopup(string title, string message, string accept, string cancel) + { + _title = title; + _message = message; + _accept = accept; + _cancel = cancel; + } + + /// + /// Initializes a new instance of the class. + /// + /// Title text + /// Message text + /// Confirm text + public MessagePopup(string title, string message, string confirm) + { + _title = title; + _message = message; + _cancel = confirm; + } + + protected override View CreateContent() + { + Layout = new LinearLayout + { + LinearAlignment = LinearLayout.Alignment.Center + }; + + BackgroundColor = new TColor(0.1f, 0.1f, 0.1f, 0.5f).ToNative(); + + var content = new View + { + Layout = new LinearLayout + { + LinearAlignment = LinearLayout.Alignment.Center, + LinearOrientation = LinearLayout.Orientation.Vertical, + }, + SizeWidth = Window.Instance.WindowSize.Width * 0.8f, + BackgroundColor = TColor.White.ToNative(), + }; + content.Add(new Label + { + Text = _title, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent, + Padding = new Extents(10, 10, 10, 10), + HorizontalTextAlignment = TextAlignment.Center, + VerticalTextAlignment = TextAlignment.Center, + FontAttributes = FontAttributes.Bold, + TextColor = TColor.White, + FontSize = 6 * DeviceInfo.ScalingFactor, + BackgroundColor = TColor.FromHex("#344955").ToNative() + }); + content.Add(new Label + { + LineBreakMode = LineBreakMode.CharacterWrap, + Margin = new Extents(10, 10, 10, 10), + Text = _message, + WidthSpecification = LayoutParamPolicies.MatchParent, + }); + + var hlayout = new View + { + Layout = new LinearLayout + { + LinearAlignment = LinearLayout.Alignment.Center, + LinearOrientation = LinearLayout.Orientation.Horizontal, + }, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent + }; + content.Add(hlayout); + + var cancelButton = new Button + { + Margin = new Extents(20, 20, 10, 10), + Text = _cancel, + SizeWidth = content.SizeWidth * 0.4f, + HeightSpecification = LayoutParamPolicies.WrapContent, + }; + cancelButton.Clicked += (s, e) => SendSubmit(false); + hlayout.Add(cancelButton); + + if (_accept != null) + { + var acceptButton = new Button + { + Margin = new Extents(20, 20, 10, 10), + Text = _accept, + SizeWidth = content.SizeWidth * 0.4f, + HeightSpecification = LayoutParamPolicies.WrapContent, + }; + acceptButton.Clicked += (s, e) => SendSubmit(true); + hlayout.Add(acceptButton); + } + + content.Relayout += (s, e) => + { + hlayout.Children[0].SizeWidth = content.SizeWidth * 0.4f; + if (hlayout.Children.Count > 1) + { + hlayout.Children[1].SizeWidth = content.SizeWidth * 0.4f; + } + }; + Relayout += (s, e) => + { + content.SizeWidth = Window.Instance.WindowSize.Width * 0.8f; + }; + return content; + } + } +} diff --git a/src/Tizen.UIExtensions.NUI/Popup.cs b/src/Tizen.UIExtensions.NUI/Popup.cs index f254ff5..f4b86a5 100644 --- a/src/Tizen.UIExtensions.NUI/Popup.cs +++ b/src/Tizen.UIExtensions.NUI/Popup.cs @@ -1,11 +1,87 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Tizen.NUI; using Tizen.NUI.BaseComponents; namespace Tizen.UIExtensions.NUI { + /// + /// Base class for custom popup + /// + /// A type to return on Popup + public abstract class Popup : Popup + { + /// + /// Initializes a new instance of the class. + /// + protected Popup() + { + Closed += OnClosed; + } + + /// + /// A TaskCompletionSource for result of popup + /// + protected TaskCompletionSource? ResponseTcs { get; set; } + + /// + /// Open popup + /// + /// The return value on Popup + public new async Task Open() + { + if (ResponseTcs != null && !ResponseTcs.Task.IsCompleted) + return await ResponseTcs.Task; + + ResponseTcs = new TaskCompletionSource(); + + if (Content == null) + Content = CreateContent(); + + base.Open(); + + try + { + return await ResponseTcs.Task; + } + finally + { + ResponseTcs = null; + Close(); + } + } + + /// + /// Create content of popup + /// + /// Content View + protected abstract View CreateContent(); + + /// + /// Submit value to return + /// + /// The value to submit + protected void SendSubmit(T value) + { + ResponseTcs?.TrySetResult(value); + } + + /// + /// Cancel popup + /// + protected void SendCancel() + { + ResponseTcs?.TrySetCanceled(); + } + + void OnClosed(object? sender, EventArgs e) + { + SendCancel(); + } + } + /// /// A Popup provides a class which can be display topmost area /// diff --git a/src/Tizen.UIExtensions.NUI/PromptPopup.cs b/src/Tizen.UIExtensions.NUI/PromptPopup.cs new file mode 100644 index 0000000..21dd833 --- /dev/null +++ b/src/Tizen.UIExtensions.NUI/PromptPopup.cs @@ -0,0 +1,150 @@ +using Tizen.NUI; +using Tizen.NUI.BaseComponents; +using Tizen.UIExtensions.Common; +using TColor = Tizen.UIExtensions.Common.Color; + +namespace Tizen.UIExtensions.NUI +{ + /// + /// A custom popup with entry and returning a string value + /// + public class PromptPopup : Popup + { + string _title; + string _message; + string _accept; + string _cancel; + string? _placeholder; + int _maxLength; + Keyboard _keyboard; + string _initialValue; + + /// + /// Initializes a new instance of the class. + /// + /// Title text + /// Message text + /// Accept text + /// Cancel text + /// Placeholder text + /// A max length of entry + /// A keyboard type + /// A initial value of entry + public PromptPopup(string title, string message, string accept = "OK", string cancel = "Cancel", string? placeholder = null, int maxLength = -1, Keyboard keyboard = Keyboard.Normal, string initialValue = "") + { + _title = title; + _message = message; + _accept = accept; + _cancel = cancel; + _placeholder = placeholder; + _maxLength = maxLength; + _keyboard = keyboard; + _initialValue = initialValue; + } + + protected override View CreateContent() + { + Layout = new LinearLayout + { + LinearAlignment = LinearLayout.Alignment.Center + }; + BackgroundColor = new TColor(0.1f, 0.1f, 0.1f, 0.5f).ToNative(); + + var content = new View + { + Layout = new LinearLayout + { + LinearAlignment = LinearLayout.Alignment.Center, + LinearOrientation = LinearLayout.Orientation.Vertical, + }, + SizeWidth = Window.Instance.WindowSize.Width * 0.8f, + BackgroundColor = TColor.White.ToNative(), + }; + content.Add(new Label + { + Text = _title, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent, + Padding = new Extents(10, 10, 10, 10), + HorizontalTextAlignment = TextAlignment.Center, + VerticalTextAlignment = TextAlignment.Center, + FontAttributes = FontAttributes.Bold, + TextColor = TColor.White, + FontSize = 6 * DeviceInfo.ScalingFactor, + BackgroundColor = TColor.FromHex("#344955").ToNative() + }); + content.Add(new Label + { + LineBreakMode = LineBreakMode.CharacterWrap, + Margin = new Extents(10, 10, 10, 10), + Text = _message, + WidthSpecification = LayoutParamPolicies.MatchParent, + }); + + var entry = new Entry + { + Text = _initialValue, + Keyboard = _keyboard, + Placeholder = _placeholder ?? "", + WidthSpecification = LayoutParamPolicies.MatchParent, + PlaceholderColor = TColor.FromRgb(100, 100, 100), + HeightSpecification = LayoutParamPolicies.WrapContent, + PixelSize = (int)(25 * DeviceInfo.ScalingFactor), + BackgroundColor = TColor.FromRgb(220,220,220).ToNative(), + Margin = new Extents(20, 20, 0, 0), + }; + if (_maxLength != -1) + { + entry.MaxLength = _maxLength; + } + + content.Add(entry); + + var hlayout = new View + { + Layout = new LinearLayout + { + LinearAlignment = LinearLayout.Alignment.Center, + LinearOrientation = LinearLayout.Orientation.Horizontal, + }, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent + }; + content.Add(hlayout); + + var cancelButton = new Button + { + Margin = new Extents(20, 20, 10, 10), + Text = _cancel, + SizeWidth = content.SizeWidth * 0.4f, + HeightSpecification = LayoutParamPolicies.WrapContent, + }; + cancelButton.Clicked += (s, e) => SendCancel(); + hlayout.Add(cancelButton); + var acceptButton = new Button + { + Margin = new Extents(20, 20, 10, 10), + Text = _accept, + SizeWidth = content.SizeWidth * 0.4f, + HeightSpecification = LayoutParamPolicies.WrapContent, + }; + acceptButton.Clicked += (s, e) => SendSubmit(entry.Text); + hlayout.Add(acceptButton); + + content.Relayout += (s, e) => + { + hlayout.Children[0].SizeWidth = content.SizeWidth * 0.4f; + if (hlayout.Children.Count > 1) + { + hlayout.Children[1].SizeWidth = content.SizeWidth * 0.4f; + } + }; + Relayout += (s, e) => + { + content.SizeWidth = Window.Instance.WindowSize.Width * 0.8f; + }; + + return content; + } + } +} diff --git a/src/Tizen.UIExtensions.NUI/CustomRenderingView.cs b/src/Tizen.UIExtensions.NUI/Skia/CustomRenderingView.cs similarity index 100% rename from src/Tizen.UIExtensions.NUI/CustomRenderingView.cs rename to src/Tizen.UIExtensions.NUI/Skia/CustomRenderingView.cs diff --git a/src/Tizen.UIExtensions.NUI/Skia/SKCanvasView.cs b/src/Tizen.UIExtensions.NUI/Skia/SKCanvasView.cs index d976444..98b37e9 100644 --- a/src/Tizen.UIExtensions.NUI/Skia/SKCanvasView.cs +++ b/src/Tizen.UIExtensions.NUI/Skia/SKCanvasView.cs @@ -10,6 +10,7 @@ namespace Tizen.UIExtensions.NUI public class SKCanvasView : CustomRenderingView { NativeImageSource? _nativeImageSource; + ImageUrl? _imageUrl; int _bufferWidth = 0; int _bufferHeight = 0; @@ -38,8 +39,9 @@ protected override void OnDrawFrame() surface.Canvas.Flush(); } _nativeImageSource.ReleaseBuffer(); - - var url = _nativeImageSource.Url; + _imageUrl?.Dispose(); + _imageUrl = _nativeImageSource.GenerateUrl(); + var url = _imageUrl?.ToString(); SetImage(url); } @@ -56,6 +58,8 @@ protected override void Dispose(bool disposing) { _nativeImageSource?.Dispose(); _nativeImageSource = null; + _imageUrl?.Dispose(); + _imageUrl = null; } base.Dispose(disposing); } @@ -64,6 +68,7 @@ void UpdateSurface() { _nativeImageSource?.Dispose(); _nativeImageSource = new NativeImageSource((uint)Size.Width, (uint)Size.Height, NativeImageSource.ColorDepth.Default); + _imageUrl = _nativeImageSource.GenerateUrl(); } } } diff --git a/src/Tizen.UIExtensions.NUI/Skia/SKClipperView.cs b/src/Tizen.UIExtensions.NUI/Skia/SKClipperView.cs new file mode 100644 index 0000000..f03fd1c --- /dev/null +++ b/src/Tizen.UIExtensions.NUI/Skia/SKClipperView.cs @@ -0,0 +1,228 @@ +using SkiaSharp; +using SkiaSharp.Views.Tizen; +using System; +using System.Runtime.InteropServices; +using System.Threading; +using Tizen.NUI; +using NView = Tizen.NUI.BaseComponents.View; + +namespace Tizen.UIExtensions.NUI +{ + /// + /// A container view that clipping children view + /// + public class SKClipperView : NView + { + static readonly string VERTEX_SHADER = + "attribute mediump vec2 aPosition;\n" + + "varying mediump vec2 vTexCoord;\n" + + "uniform highp mat4 uMvpMatrix;\n" + + "uniform mediump vec3 uSize;\n" + + "varying mediump vec2 sTexCoordRect;\n" + + "void main()\n" + + "{\n" + + " gl_Position = uMvpMatrix * vec4(aPosition * uSize.xy, 0.0, 1.0);\n" + + " vTexCoord = aPosition + vec2(0.5);\n" + + "}\n"; + + static readonly string FRAGMENT_SHADER = "" + + "#extension GL_OES_EGL_image_external:require\n" + + "uniform lowp vec4 uColor;\n" + + "varying mediump vec2 vTexCoord;\n" + + "uniform samplerExternalOES sTexture;\n" + + "\n" + + "void main(){\n" + + " mediump vec4 texColor = texture2D(sTexture, vTexCoord) * uColor;\n" + + " if (texColor.r < 1 || texColor.g < 1 || texColor.b < 1) discard;\n" + + " gl_FragColor = texColor;\n" + + "}\n" + + ""; + + PropertyNotification _resized; + Renderer _renderer; + Geometry _geometry; + Shader _shader; + + NativeImageSource? _buffer; + Texture? _texture; + TextureSet? _textureSet; + + int _bufferWidth = 0; + int _bufferHeight = 0; + int _bufferStride = 0; + + bool _redrawRequest; + + SynchronizationContext MainloopContext { get; } + + /// + /// Initializes a new instance of the class. + /// + public SKClipperView() + { + ClippingMode = ClippingModeType.ClipChildren; + MainloopContext = SynchronizationContext.Current ?? throw new InvalidOperationException("Must create on main thread"); + _geometry = CreateQuadGeometry(); + _shader = new Shader(VERTEX_SHADER, FRAGMENT_SHADER); + _resized = AddPropertyNotification("Size", PropertyCondition.Step(0.1f)); + _resized.Notified += OnResized; + + RemoveRenderer(0); + + _buffer = new NativeImageSource(1, 1, NativeImageSource.ColorDepth.Default); + _texture = new Texture(_buffer); + _textureSet = new TextureSet(); + _textureSet.SetTexture(0u, _texture); + _renderer = new Renderer(_geometry, _shader); + _renderer.SetTextures(_textureSet); + AddRenderer(_renderer); + + OnResized(); + } + + /// + /// Occurs when need to draw clipping area. A white area will be shown, others will be clipped + /// + public event EventHandler? DrawClippingArea; + + /// + /// Invalidate clipping area + /// + public void Invalidate() + { + if (!_redrawRequest) + { + _redrawRequest = true; + MainloopContext.Post((s)=> + { + _redrawRequest = false; + if (!Disposed && _buffer != null) + { + OnDrawFrame(); + } + }, null); + } + } + + protected void OnDrawFrame() + { + if (Size.Width == 0 || Size.Height == 0) + return; + + UpdateSurface(); + + var buffer = _buffer!.AcquireBuffer(ref _bufferWidth, ref _bufferHeight, ref _bufferStride); + var info = new SKImageInfo(_bufferWidth, _bufferHeight); + using (var surface = SKSurface.Create(info, buffer, _bufferStride)) + { + // draw using SkiaSharp + OnDrawFrame(new SKPaintSurfaceEventArgs(surface, info)); + surface.Canvas.Flush(); + } + _buffer.ReleaseBuffer(); + + UpdateBuffer(); + } + + void UpdateBuffer() + { + _texture?.Dispose(); + _textureSet?.Dispose(); + _texture = new Texture(_buffer); + _textureSet = new TextureSet(); + _textureSet.SetTexture(0u, _texture); + _renderer.SetTextures(_textureSet); + } + + protected virtual void OnDrawFrame(SKPaintSurfaceEventArgs e) + { + DrawClippingArea?.Invoke(this, e); + } + + protected virtual void OnResized() + { + if (Size.Width == 0 || Size.Height == 0) + return; + + UpdateSurface(); + OnDrawFrame(); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _buffer?.Dispose(); + _texture?.Dispose(); + _textureSet?.Dispose(); + _renderer?.Dispose(); + } + base.Dispose(disposing); + } + + void UpdateSurface() + { + _buffer?.Dispose(); + _buffer = new NativeImageSource((uint)Size.Width, (uint)Size.Height, NativeImageSource.ColorDepth.Default); + } + + void OnResized(object source, PropertyNotification.NotifyEventArgs e) + { + OnResized(); + } + + static Geometry CreateQuadGeometry() + { + PropertyBuffer vertexData = CreateVertextBuffer(); + + TexturedQuadVertex vertex1 = new TexturedQuadVertex(); + TexturedQuadVertex vertex2 = new TexturedQuadVertex(); + TexturedQuadVertex vertex3 = new TexturedQuadVertex(); + TexturedQuadVertex vertex4 = new TexturedQuadVertex(); + vertex1.position = new Vec2(-0.5f, -0.5f); + vertex2.position = new Vec2(-0.5f, 0.5f); + vertex3.position = new Vec2(0.5f, -0.5f); + vertex4.position = new Vec2(0.5f, 0.5f); + + TexturedQuadVertex[] texturedQuadVertexData = new TexturedQuadVertex[4] { vertex1, vertex2, vertex3, vertex4 }; + + int lenght = Marshal.SizeOf(vertex1); + IntPtr pA = Marshal.AllocHGlobal(lenght * 4); + + for (int i = 0; i < 4; i++) + { + Marshal.StructureToPtr(texturedQuadVertexData[i], pA + i * lenght, true); + } + vertexData.SetData(pA, 4); + + Geometry geometry = new Geometry(); + geometry.AddVertexBuffer(vertexData); + geometry.SetType(Geometry.Type.TRIANGLE_STRIP); + return geometry; + } + + static PropertyBuffer CreateVertextBuffer() + { + PropertyMap vertexFormat = new PropertyMap(); + vertexFormat.Add("aPosition", new PropertyValue((int)PropertyType.Vector2)); + return new PropertyBuffer(vertexFormat); + } + + struct TexturedQuadVertex + { + public Vec2 position; + }; + + [StructLayout(LayoutKind.Sequential)] + struct Vec2 + { + float x; + float y; + public Vec2(float xIn, float yIn) + { + x = xIn; + y = yIn; + } + } + } +} diff --git a/src/Tizen.UIExtensions.NUI/Tizen.UIExtensions.NUI.csproj b/src/Tizen.UIExtensions.NUI/Tizen.UIExtensions.NUI.csproj index 40c9a52..6b9b557 100644 --- a/src/Tizen.UIExtensions.NUI/Tizen.UIExtensions.NUI.csproj +++ b/src/Tizen.UIExtensions.NUI/Tizen.UIExtensions.NUI.csproj @@ -21,7 +21,9 @@ - + + + diff --git a/test/ElmSharpExGallery/ElmSharpExGallery.csproj b/test/ElmSharpExGallery/ElmSharpExGallery.csproj index 613c2b7..13668e0 100644 --- a/test/ElmSharpExGallery/ElmSharpExGallery.csproj +++ b/test/ElmSharpExGallery/ElmSharpExGallery.csproj @@ -6,10 +6,6 @@ ElmSharpExGallery - - - - diff --git a/test/ElmSharpExGallery/TC/ClippingTest.cs b/test/ElmSharpExGallery/TC/ClippingTest.cs new file mode 100644 index 0000000..ea158e7 --- /dev/null +++ b/test/ElmSharpExGallery/TC/ClippingTest.cs @@ -0,0 +1,74 @@ +using ElmSharp; +using SkiaSharp; +using System; +using Tizen.Applications; +using Tizen.UIExtensions.Common; +using Tizen.UIExtensions.ElmSharp; +using Image = Tizen.UIExtensions.ElmSharp.Image; +using Label = Tizen.UIExtensions.ElmSharp.Label; + +namespace ElmSharpExGallery.TC +{ + public class ClippingTest : TestCaseBase + { + public override string TestName => "Clipping Test"; + + public override string TestDescription => "Clipping Test"; + + public override void Run(ElmSharp.Box parent) + { + var container = new Canvas(parent) + { + AlignmentX = -1, + AlignmentY = -1, + WeightX = 1, + WeightY = 1, + MinimumHeight = 300, + }; + container.Show(); + parent.PackEnd(container); + + container.LayoutUpdated += (s, e) => + { + foreach (var child in container.Children) + { + child.Geometry = container.Geometry; + if (child is SKClipperView clip) + { + clip.Invalidate(); + } + } + }; + + var img1 = new Image(parent); + img1.Show(); + img1.Load(Application.Current.DirectoryInfo.Resource + "animated.gif"); + img1.SetIsAnimationPlaying(true); + + var clipper = new SKClipperView(parent); + clipper.Show(); + clipper.PaintSurface += (s, e) => + { + var canvas = e.Surface.Canvas; + var width = e.Info.Width; + var height = e.Info.Height; + + using (var paint = new SKPaint + { + IsAntialias = true, + Color = SKColors.White, + Style = SKPaintStyle.Fill, + }) + { + canvas.Clear(); + canvas.DrawCircle(width / 2.0f, height / 2.0f, Math.Min(width / 2.0f, height / 2.0f) / 2.0f, paint); + } + + img1.SetClipperCanvas(clipper); + }; + clipper.Lower(); + container.Children.Add(img1); + container.Children.Add(clipper); + } + } +} diff --git a/test/NUIExGallery/TC/ActionSheetPopupTest.cs b/test/NUIExGallery/TC/ActionSheetPopupTest.cs new file mode 100644 index 0000000..98c991f --- /dev/null +++ b/test/NUIExGallery/TC/ActionSheetPopupTest.cs @@ -0,0 +1,86 @@ +using System.Threading.Tasks; +using Tizen.NUI; +using Tizen.NUI.BaseComponents; +using Tizen.UIExtensions.NUI; +using Color = Tizen.UIExtensions.Common.Color; + +namespace NUIExGallery.TC +{ + public class ActionSheetPopupTest : TestCaseBase + { + public override string TestName => "ActionSheetPopup Test"; + + public override string TestDescription => "ActionSheetPopup Test"; + + public override View Run() + { + var view = new View + { + BackgroundColor = Color.FromHex("#F9AA33").ToNative(), + Layout = new LinearLayout + { + LinearAlignment = LinearLayout.Alignment.Center, + LinearOrientation = LinearLayout.Orientation.Vertical, + } + }; + + { + var btn3 = new Button + { + Text = "Open1", + FontSize = 10, + }; + view.Add(btn3); + btn3.Clicked += async (s, e) => + { + try + { + var result = await new ActionSheetPopup("Choose", "Cancel", "Delete", new []{ + "Apple", + "Banana", + "Test", + "Test2", + "test3", + "bbbb" + }).Open(); + _ = new MessagePopup("Result", $"You choose {result}", "OK").Open(); + } + catch (TaskCanceledException) + { + _ = new MessagePopup("Result", $"Canceled", "OK").Open(); + } + + }; + } + + { + var btn3 = new Button + { + Text = "Open2", + FontSize = 10, + }; + view.Add(btn3); + btn3.Clicked += async (s, e) => + { + try + { + var result = await new ActionSheetPopup("Choose", "Cancel", buttons: new string[]{ + "Item1", + "Item2", + "Item3", + }).Open(); + _ = new MessagePopup("Result", $"You choose {result}", "OK").Open(); + } + catch (TaskCanceledException) + { + _ = new MessagePopup("Result", $"Canceled", "OK").Open(); + } + }; + } + + + + return view; + } + } +} diff --git a/test/NUIExGallery/TC/ActivityIndicatorTest.cs b/test/NUIExGallery/TC/ActivityIndicatorTest.cs new file mode 100644 index 0000000..819f351 --- /dev/null +++ b/test/NUIExGallery/TC/ActivityIndicatorTest.cs @@ -0,0 +1,63 @@ +using Tizen.NUI; +using Tizen.NUI.BaseComponents; +using Tizen.UIExtensions.NUI; +using Tizen.UIExtensions.NUI.GraphicsView; +using Color = Tizen.UIExtensions.Common.Color; + +namespace NUIExGallery.TC +{ + public class ActivityIndicatorTest : TestCaseBase + { + public override string TestName => "ActivityIndicator Test"; + + public override string TestDescription => "ActivityIndicator test1"; + + public override View Run() + { + var view = new View + { + Layout = new LinearLayout + { + LinearAlignment = LinearLayout.Alignment.Center, + LinearOrientation = LinearLayout.Orientation.Vertical, + } + }; + var ai = new ActivityIndicator(); + ai.SizeHeight = (float)ai.Measure(300, 300).Height; + ai.SizeWidth = (float)ai.Measure(300, 300).Width; + view.Add(ai); + + var ai2 = new ActivityIndicator + { + Color = Color.Red, + }; + ai2.SizeHeight = (float)ai2.Measure(300, 300).Height; + ai2.SizeWidth = (float)ai2.Measure(300, 300).Width; + view.Add(ai2); + + var start = new Tizen.UIExtensions.NUI.Button() + { + Text = "start" + }; + view.Add(start); + start.Clicked += (s, e) => + { + ai.IsRunning = true; + ai2.IsRunning = true; + }; + + var stop = new Tizen.UIExtensions.NUI.Button() + { + Text = "stop" + }; + view.Add(stop); + stop.Clicked += (s, e) => + { + ai.IsRunning = false; + ai2.IsRunning = false; + }; + + return view; + } + } +} diff --git a/test/NUIExGallery/TC/CheckBoxTest.cs b/test/NUIExGallery/TC/CheckBoxTest.cs new file mode 100644 index 0000000..bf713ef --- /dev/null +++ b/test/NUIExGallery/TC/CheckBoxTest.cs @@ -0,0 +1,63 @@ +using Tizen.NUI; +using Tizen.NUI.BaseComponents; +using Tizen.UIExtensions.NUI; +using Tizen.UIExtensions.NUI.GraphicsView; +using Color = Tizen.UIExtensions.Common.Color; + +namespace NUIExGallery.TC +{ + public class CheckBoxTest : TestCaseBase + { + public override string TestName => "CheckBox Test"; + + public override string TestDescription => "CheckBox test1"; + + public override View Run() + { + var view = new View + { + Layout = new LinearLayout + { + LinearAlignment = LinearLayout.Alignment.Center, + LinearOrientation = LinearLayout.Orientation.Vertical, + } + }; + var checkbox1 = new CheckBox + { + Text = "CheckBox1", + }; + checkbox1.SizeHeight = (float)checkbox1.Measure(300, 300).Height; + checkbox1.SizeWidth = (float)checkbox1.Measure(300, 300).Width; + + + view.Add(checkbox1); + + var checkbox2 = new CheckBox + { + Text = "CheckBox2", + Color = Color.Red, + BackgroundColor = global::Tizen.NUI.Color.Green, + }; + checkbox2.SizeHeight = (float)checkbox2.Measure(300, 300).Height + 20; + checkbox2.SizeWidth = (float)checkbox2.Measure(300, 300).Width; + + view.Add(checkbox2); + + + var switch1 = new Switch(); + switch1.SizeHeight = (float)switch1.Measure(300, 300).Height; + switch1.SizeWidth = (float)switch1.Measure(300, 300).Width; + view.Add(switch1); + + var switch2 = new Switch + { + OnColor = Color.Red + }; + switch2.SizeHeight = (float)switch2.Measure(300, 300).Height; + switch2.SizeWidth = (float)switch2.Measure(300, 300).Width; + view.Add(switch2); + + return view; + } + } +} diff --git a/test/NUIExGallery/TC/ClippingTest.cs b/test/NUIExGallery/TC/ClippingTest.cs new file mode 100644 index 0000000..7a8f99f --- /dev/null +++ b/test/NUIExGallery/TC/ClippingTest.cs @@ -0,0 +1,159 @@ +using System; +using Tizen.NUI.BaseComponents; +using Tizen.NUI; +using Tizen.UIExtensions.NUI; +using Tizen.UIExtensions.Common; +using SkiaSharp; +using Tizen.Applications; +using Color = Tizen.NUI.Color; + +namespace NUIExGallery.TC +{ + public class ClippingTest : TestCaseBase + { + public override string TestName => "Clipping Test1"; + + public override string TestDescription => "Clipping test1"; + + bool isCircle = true; + + public override View Run() + { + var scrollView = new Tizen.UIExtensions.NUI.ScrollView + { + BackgroundColor = Color.Blue, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.MatchParent + }; + + scrollView.ContentContainer.Layout = new LinearLayout + { + LinearAlignment = LinearLayout.Alignment.Center, + LinearOrientation = LinearLayout.Orientation.Vertical, + }; + + scrollView.ContentContainer.WidthSpecification = LayoutParamPolicies.MatchParent; + scrollView.ContentContainer.HeightSpecification = LayoutParamPolicies.WrapContent; + + { + var clipper = new SKClipperView + { + WidthSpecification = LayoutParamPolicies.MatchParent, + SizeHeight = 300, + }; + clipper.DrawClippingArea += OnDrawCircle; + clipper.Add(new Image + { + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.MatchParent, + ResourceUrl = Application.Current.DirectoryInfo.Resource + "image2.jpg", + }); + + scrollView.ContentContainer.Add(clipper); + + var btn = new Button + { + Text = "Change" + }; + scrollView.ContentContainer.Add(btn); + + btn.Clicked += (s, e) => + { + if (isCircle) + { + clipper.DrawClippingArea -= OnDrawCircle; + clipper.DrawClippingArea += OnDrawRoundRect; + } + else + { + clipper.DrawClippingArea -= OnDrawRoundRect; + clipper.DrawClippingArea += OnDrawCircle; + } + isCircle = !isCircle; + clipper.Invalidate(); + }; + } + + for (int i = 0; i < 10; i++) + { + var clipper = new SKClipperView + { + WidthSpecification = LayoutParamPolicies.MatchParent, + SizeHeight = 300, + }; + clipper.DrawClippingArea += (s, e) => + { + var canvas = e.Surface.Canvas; + var width = e.Info.Width; + var height = e.Info.Height; + + using (var paint = new SKPaint + { + IsAntialias = true, + Color = SKColors.White, + Style = SKPaintStyle.Fill, + }) + { + canvas.Clear(); + canvas.DrawCircle(width / 2.0f, height / 2.0f, Math.Min(width, height) / 2.0f, paint); + } + }; + + clipper.Add(new Image + { + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.MatchParent, + ResourceUrl = Application.Current.DirectoryInfo.Resource + "image2.jpg", + }); + + clipper.Add(new Label + { + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.MatchParent, + Text = "Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text", + LineBreakMode = LineBreakMode.CharacterWrap, + + }); + scrollView.ContentContainer.Add(clipper); + } + + return scrollView; + } + + private void OnDrawRoundRect(object sender, SkiaSharp.Views.Tizen.SKPaintSurfaceEventArgs e) + { + var canvas = e.Surface.Canvas; + var width = e.Info.Width; + var height = e.Info.Height; + + using (var paint = new SKPaint + { + IsAntialias = true, + Color = SKColors.White, + Style = SKPaintStyle.Fill, + }) + { + canvas.Clear(); + canvas.DrawRoundRect(100, 10, width - 200, height - 20, 40, 40, paint); + } + } + + private void OnDrawCircle(object sender, SkiaSharp.Views.Tizen.SKPaintSurfaceEventArgs e) + { + var canvas = e.Surface.Canvas; + var width = e.Info.Width; + var height = e.Info.Height; + + using (var paint = new SKPaint + { + IsAntialias = true, + Color = SKColors.White, + Style = SKPaintStyle.Fill, + }) + { + canvas.Clear(); + canvas.DrawCircle(width / 2.0f, height / 2.0f, Math.Min(width, height) / 2.0f, paint); + } + } + } +} diff --git a/test/NUIExGallery/TC/GraphicsViewTest.cs b/test/NUIExGallery/TC/GraphicsViewTest.cs new file mode 100644 index 0000000..8b3456e --- /dev/null +++ b/test/NUIExGallery/TC/GraphicsViewTest.cs @@ -0,0 +1,460 @@ +using Tizen.NUI; +using Tizen.NUI.BaseComponents; +using Tizen.UIExtensions.NUI; +using Tizen.UIExtensions.NUI.GraphicsView; +using Color = Tizen.UIExtensions.Common.Color; + +namespace NUIExGallery.TC +{ + public class GraphcisViewTest : TestCaseBase + { + public override string TestName => "GraphcisView Test"; + + public override string TestDescription => "GraphcisViewTest test1"; + + public override View Run() + { + var scrollview = new Tizen.UIExtensions.NUI.ScrollView(); + + scrollview.ContentContainer.Layout = new LinearLayout + { + LinearAlignment = LinearLayout.Alignment.Center, + LinearOrientation = LinearLayout.Orientation.Vertical, + }; + + var view = scrollview.ContentContainer; + + + view.Add(new Label + { + Text = "Graphics View", + TextColor = Color.White, + FontSize = 9, + FontAttributes = Tizen.UIExtensions.Common.FontAttributes.Bold, + VerticalTextAlignment = Tizen.UIExtensions.Common.TextAlignment.Center, + WidthSpecification = LayoutParamPolicies.MatchParent, + SizeHeight = 100, + Padding = new Extents(20, 10, 10, 10), + BackgroundColor = Color.FromHex("#2196f3").ToNative(), + BoxShadow = new Shadow(5, Color.FromHex("#bbbbbb").ToNative(), new Vector2(0, 5)) + }); + + view.Add(new View + { + SizeHeight = 20, + }); + + view.Add(new Label + { + Padding = new Extents(10, 0, 0, 0), + Text = "ActivityIndicator", + FontSize = 7, + HorizontalTextAlignment = Tizen.UIExtensions.Common.TextAlignment.Start, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent, + }); + + var hlayout = new View + { + Layout = new LinearLayout + { + LinearOrientation = LinearLayout.Orientation.Horizontal, + }, + Padding = 10, + }; + + { + var ai = new ActivityIndicator + { + IsRunning = true, + Margin = 5, + }; + ai.SizeHeight = (float)ai.Measure(300, 300).Height; + ai.SizeWidth = (float)ai.Measure(300, 300).Width; + view.Add(ai); + hlayout.Add(ai); + var timer = ElmSharp.EcoreMainloop.AddTimer(3, () => + { + ai.IsRunning = !ai.IsRunning; + return true; + }); + ai.RemovedFromWindow += (s, e) => + { + ElmSharp.EcoreMainloop.RemoveTimer(timer); + (s as View).Dispose(); + }; + } + + { + var ai = new ActivityIndicator + { + Color = Color.FromHex("#ff9800"), + IsRunning = true, + Margin = 5 + }; + ai.SizeHeight = (float)ai.Measure(300, 300).Height; + ai.SizeWidth = (float)ai.Measure(300, 300).Width; + view.Add(ai); + hlayout.Add(ai); + var timer = ElmSharp.EcoreMainloop.AddTimer(3.3, () => + { + ai.IsRunning = !ai.IsRunning; + return true; + }); + ai.RemovedFromWindow += (s, e) => + { + ElmSharp.EcoreMainloop.RemoveTimer(timer); + (s as View).Dispose(); + }; + } + + { + var ai = new ActivityIndicator + { + Color = Color.FromHex("#ffeb3b"), + IsRunning = true, + Margin = 5 + }; + ai.SizeHeight = (float)ai.Measure(300, 300).Height; + ai.SizeWidth = (float)ai.Measure(300, 300).Width; + view.Add(ai); + hlayout.Add(ai); + var timer = ElmSharp.EcoreMainloop.AddTimer(4, () => + { + ai.IsRunning = !ai.IsRunning; + return true; + }); + ai.RemovedFromWindow += (s, e) => + { + ElmSharp.EcoreMainloop.RemoveTimer(timer); + (s as View).Dispose(); + }; + } + + view.Add(hlayout); + + view.Add(new Label + { + Padding = new Extents(10, 0, 0, 0), + Text = "ProgressBar", + FontSize = 7, + HorizontalTextAlignment = Tizen.UIExtensions.Common.TextAlignment.Start, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent, + }); + + { + var progressBar2 = new ProgressBar + { + Margin = 10, + WidthSpecification = LayoutParamPolicies.MatchParent, + ProgressColor = Color.GreenYellow, + }; + progressBar2.SizeHeight = (float)progressBar2.Measure(300, 300).Height; + + view.Add(progressBar2); + var timer = ElmSharp.EcoreMainloop.AddTimer(1, () => + { + if (progressBar2.Progress >= 1.0) + progressBar2.Progress = 0; + + progressBar2.ProgressTo(progressBar2.Progress + 0.2); + return true; + }); + progressBar2.RemovedFromWindow += (s, e) => ElmSharp.EcoreMainloop.RemoveTimer(timer); + } + + + var progressBar1 = new ProgressBar + { + Margin = 10, + WidthSpecification = LayoutParamPolicies.MatchParent, + }; + progressBar1.SizeHeight = (float)progressBar1.Measure(300, 300).Height; + + view.Add(progressBar1); + + view.Add(new View + { + SizeHeight = 10, + WidthSpecification = LayoutParamPolicies.MatchParent, + }); + + view.Add(new Label + { + Padding = new Extents(10, 0, 10, 0), + Text = "Slider", + FontSize = 7, + HorizontalTextAlignment = Tizen.UIExtensions.Common.TextAlignment.Start, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent, + }); + + { + var slider1 = new Slider + { + Margin = 5, + Value = 0, + Minimum = 0, + Maximum = 1, + SizeHeight = 50, + WidthSpecification = LayoutParamPolicies.MatchParent, + }; + slider1.ValueChanged += (s, e) => + { + progressBar1.Progress = slider1.Value; + }; + view.Add(slider1); + } + + { + var slider1 = new Slider + { + Margin = 5, + Value = 0, + Minimum = 0, + Maximum = 1, + SizeHeight = 50, + MaximumTrackColor = Color.Red, + MinimumTrackColor = Color.Green, + ThumbColor = Color.Yellow, + WidthSpecification = LayoutParamPolicies.MatchParent, + }; + view.Add(slider1); + } + + view.Add(new Label + { + Padding = new Extents(10, 0, 10, 0), + Text = "Button", + FontSize = 7, + HorizontalTextAlignment = Tizen.UIExtensions.Common.TextAlignment.Start, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent, + }); + + { + var button = new Tizen.UIExtensions.NUI.GraphicsView.Button + { + Margin = 5, + Text = "Clicked 0", + CornerRadius = 10, + }; + + button.SizeHeight = (float)button.Measure(300, 300).Height; + button.SizeWidth = (float)button.Measure(300, 300).Width; + int count = 1; + button.Clicked += (s, e) => + { + button.Text = $"Clicked {count++}"; + }; + view.Add(button); + } + + { + var button = new Tizen.UIExtensions.NUI.GraphicsView.Button + { + Margin = 5, + BackgroundColor = Color.Green, + Text = "BUTTON", + }; + + button.SizeHeight = (float)button.Measure(300, 300).Height; + button.SizeWidth = (float)button.Measure(300, 300).Width; + view.Add(button); + } + + { + var button = new Tizen.UIExtensions.NUI.GraphicsView.Button + { + Margin = 5, + BackgroundColor = Color.Red, + Text = "BUTTON", + }; + + button.SizeHeight = (float)button.Measure(300, 300).Height; + button.SizeWidth = (float)button.Measure(300, 300).Width + 100; + view.Add(button); + } + + view.Add(new Label + { + Padding = new Extents(10, 0, 10, 0), + Text = "CheckBox", + FontSize = 7, + HorizontalTextAlignment = Tizen.UIExtensions.Common.TextAlignment.Start, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent, + }); + + { + var checkbox1 = new CheckBox + { + Margin = 5, + Text = "CheckBox1", + }; + checkbox1.SizeHeight = (float)checkbox1.Measure(300, 300).Height; + checkbox1.SizeWidth = (float)checkbox1.Measure(300, 300).Width; + view.Add(checkbox1); + } + { + var checkbox1 = new CheckBox + { + Margin = 5, + Color = Color.Red, + Text = "Red", + }; + checkbox1.SizeHeight = (float)checkbox1.Measure(300, 300).Height; + checkbox1.SizeWidth = (float)checkbox1.Measure(300, 300).Width; + view.Add(checkbox1); + } + { + var checkbox1 = new CheckBox + { + Margin = 5, + Color = Color.Blue, + Text = "Blue", + }; + checkbox1.SizeHeight = (float)checkbox1.Measure(300, 300).Height; + checkbox1.SizeWidth = (float)checkbox1.Measure(300, 300).Width; + view.Add(checkbox1); + } + + view.Add(new Label + { + Padding = new Extents(10, 0, 10, 0), + Text = "Switch", + FontSize = 7, + HorizontalTextAlignment = Tizen.UIExtensions.Common.TextAlignment.Start, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent, + }); + + hlayout = new View + { + Layout = new LinearLayout + { + LinearOrientation = LinearLayout.Orientation.Horizontal, + }, + Padding = 10, + }; + + { + var switch1 = new Switch + { + Margin = 5, + }; + switch1.SizeHeight = (float)switch1.Measure(300, 300).Height; + switch1.SizeWidth = (float)switch1.Measure(300, 300).Width; + hlayout.Add(switch1); + } + { + var switch1 = new Switch + { + Margin = 5, + ThumbColor = Color.Red, + OnColor = Color.Yellow + }; + switch1.SizeHeight = (float)switch1.Measure(300, 300).Height; + switch1.SizeWidth = (float)switch1.Measure(300, 300).Width; + hlayout.Add(switch1); + } + { + var switch1 = new Switch + { + Margin = 5, + ThumbColor = Color.BlueViolet, + OnColor = Color.Red, + }; + switch1.SizeHeight = (float)switch1.Measure(300, 300).Height; + switch1.SizeWidth = (float)switch1.Measure(300, 300).Width; + hlayout.Add(switch1); + } + view.Add(hlayout); + + view.Add(new Label + { + Padding = new Extents(10, 0, 10, 0), + Text = "Stepper", + FontSize = 7, + HorizontalTextAlignment = Tizen.UIExtensions.Common.TextAlignment.Start, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent, + }); + + var stepper = new Stepper(); + stepper.SizeHeight = (float)stepper.Measure(300, 300).Height; + stepper.SizeWidth = (float)stepper.Measure(300, 300).Width; + view.Add(stepper); + + var stepperLabel = new Label + { + Text = "0", + }; + view.Add(stepperLabel); + + stepper.ValueChanged += (s, e) => stepperLabel.Text = $"{stepper.Value}"; + + + view.Add(new Label + { + Padding = 10, + Text = "Entry", + FontSize = 7, + HorizontalTextAlignment = Tizen.UIExtensions.Common.TextAlignment.Start, + WidthSpecification = LayoutParamPolicies.MatchParent, + HeightSpecification = LayoutParamPolicies.WrapContent, + }); + + var gEntry = new Tizen.UIExtensions.NUI.GraphicsView.Entry + { + Placeholder = "Entry", + }; + + + gEntry.SizeHeight = (float)gEntry.Measure(600, 300).Height; + gEntry.SizeWidth = (float)gEntry.Measure(600, 300).Width; + + view.Add(gEntry); + + view.Add(new View + { + SizeHeight = 20 + }); + + var gEntry2 = new Tizen.UIExtensions.NUI.GraphicsView.Entry + { + Placeholder = "Entry2", + PlaceholderColor = Color.Red, + }; + gEntry2.SizeHeight = (float)gEntry2.Measure(600, 300).Height; + gEntry2.SizeWidth = (float)gEntry2.Measure(600, 300).Width; + + view.Add(gEntry2); + + view.Add(new View + { + SizeHeight = 60 + }); + + + var editor = new Tizen.UIExtensions.NUI.GraphicsView.Editor + { + Placeholder = "Editor" + }; + + editor.SizeHeight = 500; + editor.SizeWidth = (float)editor.Measure(600, 300).Width; + + view.Add(editor); + + + view.Add(new View + { + SizeHeight = 600 + }); + + return scrollview; + } + } +} diff --git a/test/NUIExGallery/TC/MessagePopupTest.cs b/test/NUIExGallery/TC/MessagePopupTest.cs new file mode 100644 index 0000000..044e146 --- /dev/null +++ b/test/NUIExGallery/TC/MessagePopupTest.cs @@ -0,0 +1,58 @@ +using Tizen.NUI; +using Tizen.NUI.BaseComponents; +using Tizen.UIExtensions.NUI; +using Color = Tizen.UIExtensions.Common.Color; + +namespace NUIExGallery.TC +{ + public class MessagePopupTest : TestCaseBase + { + public override string TestName => "MessagePopup Test"; + + public override string TestDescription => "MessagePopup Test"; + + public override View Run() + { + var view = new View + { + BackgroundColor = Color.FromHex("#F9AA33").ToNative(), + Layout = new LinearLayout + { + LinearAlignment = LinearLayout.Alignment.Center, + LinearOrientation = LinearLayout.Orientation.Vertical, + } + }; + var btn1 = new Button + { + Text = "Open popup", + FontSize = 10, + }; + view.Add(btn1); + btn1.Clicked += async (s, e) => + { + _ = new MessagePopup("Popup1", "Show your popup", "OK").Open(); + }; + + var btn2 = new Button + { + Text = "Open popup2", + FontSize = 10, + }; + view.Add(btn2); + btn2.Clicked += async (s, e) => + { + try + { + var result = await new MessagePopup("Popup2", "Show your popup", "Yes", "No").Open(); + await new MessagePopup("Result", $"Result is {result}", "OK").Open(); + } + catch + { + _ = new MessagePopup("Result", "Popup was closed", "OK").Open(); + } + }; + + return view; + } + } +} diff --git a/test/NUIExGallery/TC/ProgressBarTest1.cs b/test/NUIExGallery/TC/ProgressBarTest1.cs new file mode 100644 index 0000000..58a8f85 --- /dev/null +++ b/test/NUIExGallery/TC/ProgressBarTest1.cs @@ -0,0 +1,77 @@ +using Tizen.NUI; +using Tizen.NUI.BaseComponents; +using Tizen.UIExtensions.NUI.GraphicsView; + +namespace NUIExGallery.TC +{ + public class ProgressBarTest1 : TestCaseBase + { + public override string TestName => "ProgressBarTest1"; + + public override string TestDescription => "ProgressBarTest1"; + + public override View Run() + { + var view = new View + { + Layout = new LinearLayout + { + LinearAlignment = LinearLayout.Alignment.Center, + LinearOrientation = LinearLayout.Orientation.Vertical, + } + }; + var progressBar1 = new ProgressBar + { + WidthSpecification = LayoutParamPolicies.MatchParent, + }; + progressBar1.SizeHeight = (float)progressBar1.Measure(300, 300).Height; + + view.Add(progressBar1); + + view.Add(new View + { + SizeHeight = 100, + WidthSpecification = LayoutParamPolicies.MatchParent, + }); + + var slider1 = new Slider + { + Value = 0, + Minimum = 0, + Maximum = 1, + SizeHeight = 50, + WidthSpecification = LayoutParamPolicies.MatchParent, + }; + slider1.ValueChanged += (s, e) => + { + progressBar1.Progress = slider1.Value; + }; + view.Add(slider1); + + var btn1 = new Tizen.UIExtensions.NUI.Button() + { + Text = "To zero" + }; + + btn1.Clicked += (s, e) => + { + progressBar1.ProgressTo(0); + }; + view.Add(btn1); + + var btn2 = new Tizen.UIExtensions.NUI.Button() + { + Text = "To Max" + }; + + btn2.Clicked += (s, e) => + { + progressBar1.ProgressTo(1); + }; + view.Add(btn2); + + + return view; + } + } +} diff --git a/test/NUIExGallery/TC/PromptPopupTest.cs b/test/NUIExGallery/TC/PromptPopupTest.cs new file mode 100644 index 0000000..3266d46 --- /dev/null +++ b/test/NUIExGallery/TC/PromptPopupTest.cs @@ -0,0 +1,49 @@ +using System.Threading.Tasks; +using Tizen.NUI; +using Tizen.NUI.BaseComponents; +using Tizen.UIExtensions.NUI; +using Color = Tizen.UIExtensions.Common.Color; + +namespace NUIExGallery.TC +{ + public class PromptPopupTest : TestCaseBase + { + public override string TestName => "PromptPopup Test"; + + public override string TestDescription => "PromptPopup Test"; + + public override View Run() + { + var view = new View + { + BackgroundColor = Color.FromHex("#F9AA33").ToNative(), + Layout = new LinearLayout + { + LinearAlignment = LinearLayout.Alignment.Center, + LinearOrientation = LinearLayout.Orientation.Vertical, + } + }; + var btn3 = new Button + { + Text = "Open Prompt popup", + FontSize = 10, + }; + view.Add(btn3); + btn3.Clicked += async (s, e) => + { + try + { + var result = await new PromptPopup("Prompt", "Your name", placeholder: "Name").Open(); + await new MessagePopup("Result", $"Your name is {result}", "OK").Open(); + } + catch (TaskCanceledException) + { + _ = new MessagePopup("Result", $"Prompt is canceld", "OK").Open(); + } + }; + + + return view; + } + } +} diff --git a/test/NUIExGallery/TC/SliderTest.cs b/test/NUIExGallery/TC/SliderTest.cs new file mode 100644 index 0000000..19e98e3 --- /dev/null +++ b/test/NUIExGallery/TC/SliderTest.cs @@ -0,0 +1,64 @@ +using Tizen.NUI; +using Tizen.NUI.BaseComponents; +using Tizen.UIExtensions.NUI; +using Tizen.UIExtensions.NUI.GraphicsView; +using Color = Tizen.UIExtensions.Common.Color; + +namespace NUIExGallery.TC +{ + public class SliderTest : TestCaseBase + { + public override string TestName => "Slider Test"; + + public override string TestDescription => "Slider test1"; + + public override View Run() + { + var view = new View + { + Layout = new LinearLayout + { + LinearAlignment = LinearLayout.Alignment.Center, + LinearOrientation = LinearLayout.Orientation.Vertical, + } + }; + + var slider1 = new Slider + { + Value = 0, + Minimum = 0, + Maximum = 1, + SizeHeight = 50, + WidthSpecification = LayoutParamPolicies.MatchParent, + }; + var label1 = new Label(); + slider1.ValueChanged += (s, e) => + { + label1.Text = $"{slider1.Value}"; + }; + view.Add(slider1); + view.Add(label1); + + var slider2 = new Slider + { + Value = 0, + Minimum = 0, + Maximum = 100, + SizeHeight = 50, + WidthSpecification = LayoutParamPolicies.MatchParent, + MinimumTrackColor = Color.Blue, + MaximumTrackColor = Color.Red, + ThumbColor = Color.Yellow, + }; + var label2 = new Label(); + slider2.ValueChanged += (s, e) => + { + label2.Text = $"{slider2.Value}"; + }; + view.Add(slider2); + view.Add(label2); + + return view; + } + } +}