--- /dev/null
+using System;
+using Tizen.NUI.Components;
+using Tizen.NUI.BaseComponents;
+using System.Collections.Generic;
+using Tizen.NUI;
+
+namespace Oobe.Controls
+{
+ /// <summary>
+ /// ViewStack implemenents stack based navigation in which all child views are stacked over each other
+ /// and only the most recent child added is visible.
+ /// </summary>
+ public class ViewStack : Control
+ {
+ private class ViewStackBaseCustomLayout : LayoutGroup
+ {
+ protected override void OnMeasure(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
+ {
+ ViewStack stack = this.Owner as ViewStack;
+
+ if (!stack) return;
+
+ int totalWidth = stack.Size2D.Width;
+ int totalHeight = stack.Size2D.Height;
+
+ SetMeasuredDimensions(ResolveSizeAndState(new LayoutLength(totalWidth), widthMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK),
+ ResolveSizeAndState(new LayoutLength(totalHeight), heightMeasureSpec, MeasuredSize.StateType.MeasuredSizeOK));
+ }
+
+ protected override void OnLayout(bool changed, LayoutLength left, LayoutLength top, LayoutLength right, LayoutLength bottom)
+ {
+ ViewStack stack = this.Owner as ViewStack;
+
+ if (!changed || !stack) return;
+
+ int totalWidth = stack.Size2D.Width;
+ int totalHeight = stack.Size2D.Height;
+
+ foreach (LayoutItem childLayout in LayoutChildren)
+ {
+ if (childLayout.Owner == stack.Current)
+ {
+ childLayout.Layout(
+ new LayoutLength(0),
+ new LayoutLength(0),
+ new LayoutLength(totalWidth),
+ new LayoutLength(totalHeight));
+ } else
+ {
+ childLayout.Layout(
+ new LayoutLength(- stack.PagesLeftPadding),
+ new LayoutLength(0),
+ new LayoutLength(totalWidth - stack.PagesLeftPadding),
+ new LayoutLength(totalHeight));
+ }
+ }
+ }
+ }
+
+ private List<View> views = new List<View>();
+ private List<View> viewsToRemove = new List<View>();
+
+ private Animation pageEnterAnimation;
+ private Animation pageLeaveAnimation;
+
+ /// <summary>
+ /// Informs about transition animation end
+ /// </summary>
+ public event EventHandler TransitionFinished;
+
+ /// <summary>
+ /// Number of pixels between stack right boundary and newly pushed child view left boundary,
+ /// which will be applied before starting page entering animation.
+ /// </summary>
+ /// <value></value>
+ public int PagesRightPadding { get; set; } = 0;
+
+ /// <summary>
+ /// Number of pixels between stack right boundary and current top child view left boundary,
+ /// which will be applied after finishing page leaving animation
+ /// </summary>
+ public int PagesLeftPadding { get; set; } = 0;
+
+ /// <summary>
+ /// Duration of page entering and page leaving animations
+ /// </summary>
+ public int ScrollDuration { get; set; } = 125;
+
+ /// <summary>
+ /// Get view currently stacked on Top of ViewStack
+ /// </summary>
+ public View Current { get { return views.Count > 0 ? views[views.Count-1] : null; } }
+
+ /// <summary>
+ /// Get view currently stacked under Current
+ /// </summary>
+ public View Previous { get { return views.Count > 1 ? views[views.Count - 2] : null; } }
+
+ public ViewStack() : base()
+ {
+ ClippingMode = ClippingModeType.ClipToBoundingBox;
+ Layout = new ViewStackBaseCustomLayout();
+ pageEnterAnimation = new Animation();
+ pageLeaveAnimation = new Animation();
+ pageEnterAnimation.Finished += OnPageEntered;
+ pageLeaveAnimation.Finished += OnPageLeave;
+ }
+
+ /// <summary>
+ /// Pop Current view with animation
+ /// </summary>
+ public void Pop()
+ {
+ if (Current != null)
+ {
+ FinishAnimations();
+ RemoveAllDelayedViews();
+ StartPageLeaveAnimation(Previous, Current);
+ AddViewToDelayRemove(Current);
+ views.Remove(Current);
+ }
+ }
+
+ /// <summary>
+ /// Pushed new view on stack with animation.
+ /// </summary>
+ public void Push(View view)
+ {
+ FinishAnimations();
+ RemoveAllDelayedViews();
+ Add(view);
+ StartPageEnterAnimation(Previous, Current);
+ }
+
+ public override void OnChildRemove(View view)
+ {
+ views.Remove(view);
+ }
+
+ public override void OnChildAdd(View view)
+ {
+ views.Add(view);
+ }
+
+ private void AddViewToDelayRemove(View view)
+ {
+ if (view)
+ {
+ viewsToRemove.Add(view);
+ }
+ }
+
+ private void RemoveAllDelayedViews()
+ {
+ foreach (View view in viewsToRemove)
+ {
+ // hide view to resolve issue with rendering artifacts
+ view.Hide();
+ Remove(view);
+ }
+ viewsToRemove.Clear();
+ }
+
+ private void FinishAnimations()
+ {
+ FinishAnimation(pageLeaveAnimation);
+ FinishAnimation(pageEnterAnimation);
+ }
+
+ static private void FinishAnimation(Animation anim)
+ {
+ if (anim != null)
+ {
+ if (anim.State == Animation.States.Playing)
+ {
+ anim.Stop(Animation.EndActions.StopFinal);
+ }
+ anim.Clear();
+ }
+ }
+
+ private void StartPageLeaveAnimation(View back, View front)
+ {
+ pageLeaveAnimation.Duration = ScrollDuration;
+ pageLeaveAnimation.DefaultAlphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.EaseOutSine);
+
+ if (back != null)
+ {
+ // place 'back' out of viewport (should be already there)
+ // run animation to place 'back' in viewport
+ pageLeaveAnimation.AnimateTo(back, "PositionX", 0, null);
+ }
+ if (front != null)
+ {
+ // place 'front' in viewport (should be already there)
+ // run animation to place 'front' out of viewport
+ pageLeaveAnimation.AnimateTo(front, "PositionX", Size2D.Width + PagesRightPadding);
+ }
+ pageLeaveAnimation.Play();
+ }
+ private void StartPageEnterAnimation(View back, View front)
+ {
+ pageEnterAnimation.Duration = ScrollDuration;
+ pageEnterAnimation.DefaultAlphaFunction = new AlphaFunction(AlphaFunction.BuiltinFunctions.EaseOutSine);
+
+ if (back != null)
+ {
+ // place back in viewport (should be already there)
+ // run anim to place 'back' out of viewport
+ int diff = Position2D.X - PagesLeftPadding;
+ pageEnterAnimation.AnimateTo(back, "PositionX", diff);
+ }
+ if (front != null)
+ {
+ // place 'front' out of viewport
+ front.Position2D.X = Size2D.Width + PagesRightPadding;
+
+ // run anim to place 'front' on viewport
+ pageEnterAnimation.AnimateTo(front, "PositionX", 0, null);
+ }
+ pageEnterAnimation.Play();
+ }
+
+ private void OnPageEntered(object sender, EventArgs args)
+ {
+ TransitionFinished?.Invoke(this, new EventArgs());
+ }
+
+ private void OnPageLeave(object sender, EventArgs args)
+ {
+ RemoveAllDelayedViews();
+ TransitionFinished?.Invoke(this, new EventArgs());
+ }
+
+ }
+}
\ No newline at end of file
using System;
using Tizen.NUI;
using Tizen.NUI.BaseComponents;
-using Oobe.Language;
+using Tizen.NUI.Components;
+using Oobe.Controls;
+using Oobe.Views;
namespace Oobe
{
class Program : NUIApplication
{
+ private OobeViewSwitcher switcher;
+
protected override void OnCreate()
{
base.OnCreate();
Initialize();
}
+ TextLabel makeLabel(string txt, Color color)
+ {
+ TextLabel text2 = new TextLabel(txt);
+ text2.HorizontalAlignment = HorizontalAlignment.Center;
+ text2.VerticalAlignment = VerticalAlignment.Center;
+ text2.TextColor = Color.Black;
+ text2.PointSize = 12.0f;
+ text2.HeightResizePolicy = ResizePolicyType.FillToParent;
+ text2.WidthResizePolicy = ResizePolicyType.FillToParent;
+
+ return text2;
+ }
+
void Initialize()
{
Window.Instance.KeyEvent += OnKeyEvent;
- var test = new LanguageStep(null);
-
- TextLabel text = new TextLabel("Hello Tizen NUI World");
- text.HorizontalAlignment = HorizontalAlignment.Center;
- text.VerticalAlignment = VerticalAlignment.Center;
- text.TextColor = Color.Blue;
- text.PointSize = 12.0f;
- text.HeightResizePolicy = ResizePolicyType.FillToParent;
- text.WidthResizePolicy = ResizePolicyType.FillToParent;
- Window.Instance.GetDefaultLayer().Add(text);
-
- Animation animation = new Animation(2000);
- animation.AnimateTo(text, "Orientation", new Rotation(new Radian(new Degree(180.0f)), PositionAxis.X), 0, 500);
- animation.AnimateTo(text, "Orientation", new Rotation(new Radian(new Degree(0.0f)), PositionAxis.X), 500, 1000);
- animation.Looping = true;
- animation.Play();
+
+ switcher = new OobeViewSwitcher(Window.Instance);
+
+ Button button = new Button();
+ button.Text = "Push";
+ button.BackgroundColor = Color.Blue;
+ button.Size2D = new Size2D(100, 80);
+ button.Position2D.X = 48;
+ button.Position2D.Y = 720;
+ button.ClickEvent += PushClicked;
+ Window.Instance.GetDefaultLayer().Add(button);
+
+ button = new Button();
+ button.Text = "Pop";
+ button.Position2D = new Position2D(250, 720);
+ button.BackgroundColor = Color.White;
+ button.Size2D = new Size2D(100, 80);
+ button.ClickEvent += PopClicked;
+ Window.Instance.GetDefaultLayer().Add(button);
+
+ }
+ int current = 0;
+ public static Color[] PossibleColors = new Color[]{
+ Color.White,
+ Color.White,
+ Color.White
+ };
+
+ public void PushClicked(object sender, EventArgs args)
+ {
+ Tizen.Log.Debug("ViewStack", "Push");
+ TextLabel text = makeLabel("More text", PossibleColors[current++ % PossibleColors.Length]);
+ switcher.Push(text);
+ }
+
+ public void PopClicked(object sender, EventArgs args)
+ {
+ Tizen.Log.Debug("ViewStack", "Pop");
+ switcher.Pop();
}
public void OnKeyEvent(object sender, Window.KeyEventArgs e)
<PackageReference Include="Tizen.NET" Version="8.0.0.15106">
<ExcludeAssets>Runtime</ExcludeAssets>
</PackageReference>
- <PackageReference Include="Tizen.NET.Sdk" Version="1.0.1" />
+ <PackageReference Include="Tizen.NET.Sdk" Version="1.0.9" />
</ItemGroup>
<ItemGroup>
--- /dev/null
+using Tizen.NUI.BaseComponents;
+using Tizen.NUI;
+using Oobe.Controls;
+
+namespace Oobe.Views
+{
+ /// <summary>
+ /// Implementation of OOBE GUI Guideline for IoT Headed
+ /// </summary>
+ public class OobeViewSwitcher
+ {
+ private ViewStack stack;
+ private Animation dimEffectAnimation;
+ private const int TransitionTime = 750;
+
+ public OobeViewSwitcher(Window win)
+ {
+ View backImage = new View();
+ backImage.BackgroundImage = NUIApplication.Current.DirectoryInfo.Resource + "0_BG_dark.svg";
+ backImage.WidthResizePolicy = ResizePolicyType.FillToParent;
+ backImage.HeightResizePolicy = ResizePolicyType.FillToParent;
+
+ stack = new ViewStack();
+ stack.ScrollDuration = TransitionTime;
+ stack.Position2D = new Position2D(48 - 6, 48 - 6); // include shadow
+ stack.Size2D = new Size2D(1184 + 6 + 9, 624 + 6 + 9); // includes surrounding shadow
+ stack.PagesRightPadding = 48;
+ stack.PagesLeftPadding = (int)(0.5f * stack.Size2D.Width);
+ stack.ClippingMode = ClippingModeType.Disabled;
+ dimEffectAnimation = new Animation(TransitionTime);
+
+ win.GetDefaultLayer().Add(backImage);
+ win.GetDefaultLayer().Add(stack);
+ }
+
+ public void Push(View view)
+ {
+ FinishDimAnimation();
+
+ var newView = new Views.Page();
+ newView.HeightResizePolicy = ResizePolicyType.FillToParent;
+ newView.WidthResizePolicy = ResizePolicyType.FillToParent;
+ newView.Content = view;
+ newView.Overlay.Opacity = 0.0f;
+
+ if (stack.Current is Views.Page overlayedView)
+ {
+ StartDimAnimation(overlayedView);
+ }
+
+ stack.Push(newView);
+ }
+
+ public void Pop()
+ {
+ FinishDimAnimation();
+ if (stack.Previous is Views.Page previous)
+ {
+ StartUndimAnimation(previous);
+ }
+ if (stack.Current is Views.Page current)
+ {
+ StartDimAnimation(current);
+ }
+ stack.Pop();
+ }
+
+ private void StartDimAnimation(Views.Page view)
+ {
+ view.Overlay.Opacity = 0.0f;
+ view.Opacity = 1.0f;
+ dimEffectAnimation.AnimateTo(view.Overlay, "Opacity", 0.5f);
+ dimEffectAnimation.AnimateTo(view, "Opacity", 0.0f);
+ dimEffectAnimation.Play();
+ }
+
+ private void StartUndimAnimation(Views.Page view)
+ {
+ view.Overlay.Opacity = 0.5f;
+ view.Opacity = 0.0f;
+ dimEffectAnimation.AnimateTo(view.Overlay, "Opacity", 0.0f);
+ dimEffectAnimation.AnimateTo(view, "Opacity", 1.0f);
+ dimEffectAnimation.Play();
+ }
+
+ private void FinishDimAnimation()
+ {
+ if (dimEffectAnimation.State == Animation.States.Playing)
+ {
+ dimEffectAnimation.Stop(Animation.EndActions.StopFinal);
+ }
+ dimEffectAnimation.Clear();
+ }
+ }
+}
--- /dev/null
+using Tizen.NUI.BaseComponents;
+using Tizen.NUI;
+using Tizen.NUI.Components;
+
+namespace Oobe.Views
+{
+ /// <summary>
+ /// Implementation of OOBE GUI guideline for Tizen IoT headed
+ /// </summary>
+ public class Page : View
+ {
+ private View content;
+ private View overlay;
+
+ public View Content
+ {
+ set
+ {
+ if (value == content)
+ return;
+ if (value)
+ {
+ Remove(content);
+ Remove(overlay);
+ value.HeightResizePolicy = ResizePolicyType.FillToParent;
+ value.WidthResizePolicy = ResizePolicyType.FillToParent;
+ Add(value);
+ Add(overlay);
+ content = value;
+ }
+ }
+ get {
+ return content;
+ }
+ }
+
+ public View Overlay { get => overlay; }
+
+ public Page()
+ {
+ BackgroundImage = NUIApplication.Current.DirectoryInfo.Resource + "0_BG_WHITEsmall.svg";
+ overlay = new View();
+ overlay.BackgroundColor = Color.Black;
+ overlay.Opacity = 0.5f;
+ overlay.HeightResizePolicy = ResizePolicyType.FillToParent;
+ overlay.WidthResizePolicy = ResizePolicyType.FillToParent;
+ }
+
+ }
+}
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1199" height="639" viewBox="0 0 1199 639">\r
+ <defs>\r
+ <filter id="Rectangle_860" x="0" y="0" width="1199" height="639" filterUnits="userSpaceOnUse">\r
+ <feOffset dx="1" dy="1" input="SourceAlpha"/>\r
+ <feGaussianBlur stdDeviation="2.5" result="blur"/>\r
+ <feFlood flood-opacity="0.161"/>\r
+ <feComposite operator="in" in2="blur"/>\r
+ <feComposite in="SourceGraphic"/>\r
+ </filter>\r
+ </defs>\r
+ <g id="_0_BG_WHITEsmall" data-name="0_BG_WHITEsmall" transform="translate(6.5 6.5)">\r
+ <g transform="matrix(1, 0, 0, 1, -6.5, -6.5)" filter="url(#Rectangle_860)">\r
+ <rect id="Rectangle_860-2" data-name="Rectangle 860" width="1184" height="624" rx="16" transform="translate(6.5 6.5)" fill="#fff" opacity="0.95"/>\r
+ </g>\r
+ </g>\r
+</svg>\r
--- /dev/null
+<svg id="_0_BG_dark" data-name="0_BG_dark" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1280" height="720" viewBox="0 0 1280 720">\r
+ <defs>\r
+ <clipPath id="clip-path">\r
+ <rect id="Rectangle_876" data-name="Rectangle 876" width="1280" height="720" fill="none"/>\r
+ </clipPath>\r
+ <linearGradient id="linear-gradient" x1="0.164" y1="-0.433" x2="1.021" y2="-0.433" gradientUnits="objectBoundingBox">\r
+ <stop offset="0" stop-color="#052759"/>\r
+ <stop offset="1" stop-color="#a4b7d6"/>\r
+ </linearGradient>\r
+ <linearGradient id="linear-gradient-2" x1="-2.829" y1="-2.482" x2="-1.972" y2="-2.482" gradientUnits="objectBoundingBox">\r
+ <stop offset="0" stop-color="#00737f"/>\r
+ <stop offset="0.126" stop-color="#80a8b2"/>\r
+ <stop offset="0.351" stop-color="#1e6fa6"/>\r
+ <stop offset="1" stop-color="#5068e8"/>\r
+ </linearGradient>\r
+ <linearGradient id="linear-gradient-3" x1="0.152" y1="-5.005" x2="0.895" y2="-5.005" gradientUnits="objectBoundingBox">\r
+ <stop offset="0" stop-color="#ef34a4"/>\r
+ <stop offset="1" stop-color="#051a46"/>\r
+ </linearGradient>\r
+ <linearGradient id="linear-gradient-5" x1="-0.438" y1="2.176" x2="0.081" y2="2.176" gradientUnits="objectBoundingBox">\r
+ <stop offset="0" stop-color="#e682be"/>\r
+ <stop offset="1" stop-color="#24468d"/>\r
+ </linearGradient>\r
+ <linearGradient id="linear-gradient-6" x1="-1.354" y1="-3.765" x2="-0.473" y2="-3.765" gradientUnits="objectBoundingBox">\r
+ <stop offset="0" stop-color="#07458b"/>\r
+ <stop offset="1" stop-color="#2bcb4b"/>\r
+ </linearGradient>\r
+ </defs>\r
+ <rect id="Rectangle_860" data-name="Rectangle 860" width="1278" height="720" transform="translate(2)" fill="#fff"/>\r
+ <g id="Group_2" data-name="Group 2" clip-path="url(#clip-path)">\r
+ <g id="Group_511" data-name="Group 511" transform="translate(-188.33 -193.578)">\r
+ <path id="Rectangle_860-2" data-name="Rectangle 860" d="M0,0H1280V720H0Z" transform="translate(188.322 193.567)" fill="#172061"/>\r
+ <path id="Path_238" data-name="Path 238" d="M57.869-.015l352.173-.3a57.749,57.749,0,0,1,57.814,57.824h0a57.99,57.99,0,0,1-57.914,57.924l-352.172.3A57.749,57.749,0,0,1-.045,57.91h0A57.99,57.99,0,0,1,57.869-.015Z" transform="translate(923.638 108.552) rotate(138)" opacity="0.5" fill="url(#linear-gradient)"/>\r
+ <path id="Path_239" data-name="Path 239" d="M57.869-.015l352.172-.3a57.749,57.749,0,0,1,57.814,57.825h0a57.99,57.99,0,0,1-57.914,57.924l-352.172.3A57.749,57.749,0,0,1-.045,57.91h0A57.99,57.99,0,0,1,57.869-.015Z" transform="translate(1608.571 696.239) rotate(138.039)" fill="url(#linear-gradient-2)"/>\r
+ <path id="Path_240" data-name="Path 240" d="M57.862-.02,340.828-.259a57.882,57.882,0,0,1-.1,115.763l-282.966.238A57.882,57.882,0,0,1,57.862-.02Z" transform="translate(385.194 815.83) rotate(142)" opacity="0.6" fill="url(#linear-gradient-3)"/>\r
+ <path id="Path_241" data-name="Path 241" d="M57.874-.021,340.9-.267a57.869,57.869,0,0,1-.1,115.739l-283.026.246a57.869,57.869,0,1,1,.1-115.739Z" transform="translate(1687.259 224.683) rotate(135)" opacity="0.6" fill="url(#linear-gradient-3)"/>\r
+ <path id="Path_242" data-name="Path 242" d="M57.869-.014,417.663-.325A57.749,57.749,0,0,1,475.476,57.5h0a57.99,57.99,0,0,1-57.914,57.924l-359.793.311A57.749,57.749,0,0,1-.045,57.911h0A57.99,57.99,0,0,1,57.869-.014Z" transform="translate(482.093 280.677) rotate(138.039)" fill="#99a5e6" opacity="0.9"/>\r
+ <path id="Path_243" data-name="Path 243" d="M57.871-.021l278.44-.241A57.749,57.749,0,0,1,394.127,57.56h0a57.99,57.99,0,0,1-57.916,57.923l-278.441.241A57.749,57.749,0,0,1-.045,57.9h0A57.99,57.99,0,0,1,57.871-.021Z" transform="translate(1455.802 84.73) rotate(137)" fill="url(#linear-gradient-5)"/>\r
+ <path id="Path_244" data-name="Path 244" d="M57.858-.027l194.9-.161a57.754,57.754,0,0,1,57.806,57.837h0a57.985,57.985,0,0,1-57.9,57.933l-194.9.161A57.754,57.754,0,0,1-.043,57.906h0A57.985,57.985,0,0,1,57.858-.027Z" transform="matrix(0.81, -0.587, 0.587, 0.81, 534.439, 965.677)" opacity="0.21" fill="url(#linear-gradient-6)"/>\r
+ </g>\r
+ </g>\r
+</svg>\r