text:
using SharpDX;
using System;
using System.Linq;
using System.Collections.Generic;
using Point3D = SharpDX.Vector3;
using Vector3D = SharpDX.Vector3;
using Windows.UI.Core;
using System.Diagnostics;

#if WINUI
using HelixToolkit.SharpDX.Core;
using HelixToolkit.SharpDX.Core.Utilities;
using HelixToolkit.SharpDX.Core.Cameras;
using Point = Windows.Foundation.Point;
using Microsoft.UI.Xaml.Input;
namespace HelixToolkit.WinUI
#else
using Point = Windows.Foundation.Point;
using Windows.UI.Xaml.Input;
namespace HelixToolkit.UWP
#endif
{
#if WINDOWS_UWP
    using Cameras;
    using Utilities;
#endif
    public class CameraController : IDisposable
    {
        private InputController inputController;
        public InputController InputController
        {
            set
            {
                if(inputController == value)
                {
                    return;
                }
                if(inputController != null)
                {
                    inputController.Viewport = null;
                    inputController.OnStartPan -= InputController_OnStartPan;
                    inputController.OnStartRotate -= InputController_OnStartRotate;
                    inputController.OnStartZoom -= InputController_OnStartZoom;
                    inputController.OnStartZoomExtends -= InputController_OnStartZoomExtends;
                    inputController.OnStartZoomRectangle -= InputController_OnStartZoomRectangle;
                    inputController.OnResetCamera -= InputController_OnResetCamera;
                    inputController.OnChangeFieldOfView -= InputController_OnChangeFieldOfView;
                    inputController.OnChangeLookAt -= InputController_OnChangeLookAt;
                    inputController.OnTopView -= InputController_OnTopView;
                    inputController.OnBottomView -= InputController_OnBottomView;
                    inputController.OnLeftView -= InputController_OnLeftView;
                    inputController.OnRightView -= InputController_OnRightView;
                    inputController.OnFrontView -= InputController_OnFrontView;
                    inputController.OnBackView -= InputController_OnBackView;
                    inputController.OnAddMoveForce -= InputController_OnAddMoveForce;
                    inputController.OnAddPanForce -= InputController_OnAddPanForce;
                    inputController.OnAddRotationForce -= InputController_OnAddRotationForce;
                    inputController.OnAddZoomForce -= InputController_OnAddZoomForce;
                    inputController.OnRestoreCameraSettings -= InputController_OnRestoreCameraSettings;
                }
                inputController = value;
                if(inputController != null)
                {
                    inputController.Viewport = this.Viewport;
                    inputController.OnStartPan += InputController_OnStartPan;
                    inputController.OnStartRotate += InputController_OnStartRotate;
                    inputController.OnStartZoom += InputController_OnStartZoom;
                    inputController.OnStartZoomExtends += InputController_OnStartZoomExtends;
                    inputController.OnStartZoomRectangle += InputController_OnStartZoomRectangle;
                    inputController.OnResetCamera += InputController_OnResetCamera;
                    inputController.OnChangeFieldOfView += InputController_OnChangeFieldOfView;
                    inputController.OnChangeLookAt += InputController_OnChangeLookAt;
                    inputController.OnTopView += InputController_OnTopView;
                    inputController.OnBottomView += InputController_OnBottomView;
                    inputController.OnLeftView += InputController_OnLeftView;
                    inputController.OnRightView += InputController_OnRightView;
                    inputController.OnFrontView += InputController_OnFrontView;
                    inputController.OnBackView += InputController_OnBackView;
                    inputController.OnAddMoveForce += InputController_OnAddMoveForce;
                    inputController.OnAddPanForce += InputController_OnAddPanForce;
                    inputController.OnAddRotationForce += InputController_OnAddRotationForce;
                    inputController.OnAddZoomForce += InputController_OnAddZoomForce;
                    inputController.OnRestoreCameraSettings += InputController_OnRestoreCameraSettings;
                }
            }
            get
            {
                return inputController;
            }
        }

        /// <summary>
        /// Gets ActualCamera.
        /// </summary>
        public ProjectionCamera ActualCamera
        {
            set; get;
        }

        /// <summary>
        /// Gets or sets the default camera (used when resetting the view).
        /// </summary>
        /// <value> The default camera. </value>
        public ProjectionCamera DefaultCamera
        {
            set; get;
        }

        /// <summary>
        /// Gets or sets CameraLookDirection.
        /// </summary>
        public Vector3D CameraLookDirection
        {
            get
            {
                return this.ActualCamera.LookDirection;
            }

            set
            {
                this.ActualCamera.LookDirection = value;
            }
        }

        /// <summary>
        /// Gets or sets CameraPosition.
        /// </summary>
        public Point3D CameraPosition
        {
            get
            {
                return this.ActualCamera.Position;
            }

            set
            {
                this.ActualCamera.Position = value;
            }
        }
        /// <summary>
        /// Gets or sets CameraTarget.
        /// </summary>
        public Point3D CameraTarget
        {
            get
            {
                return this.CameraPosition + this.CameraLookDirection;
            }

            set
            {
                this.CameraLookDirection = value - this.CameraPosition;
            }
        }
        /// <summary>
        /// Gets or sets CameraUpDirection.
        /// </summary>
        public Vector3D CameraUpDirection
        {
            get
            {
                return this.ActualCamera.UpDirection;
            }

            set
            {
                this.ActualCamera.UpDirection = value;
            }
        }

#region Properties
        /// <summary>
        /// Gets or sets CameraMode.
        /// </summary>
        public CameraMode CameraMode
        {
            set; get;
        } = CameraMode.Inspect;

        /// <summary>
        /// Gets or sets CameraRotationMode.
        /// </summary>
        public CameraRotationMode CameraRotationMode
        {
            set; get;
        } = CameraRotationMode.Turntable;

        /// <summary>
        /// Gets or sets the change field of view Cursor
        /// </summary>
        /// <value> The change field of view Cursor. </value>
        public CoreCursorType ChangeFieldOfViewCursor
        {
            set; get;
        } = CoreCursorType.SizeNorthSouth;

        /// <summary>
        /// Gets or sets InertiaFactor.
        /// </summary>
        public double InertiaFactor
        {
            set; get;
        } = 0.93;

        /// <summary>
        /// Gets or sets a value indicating whether InfiniteSpin.
        /// </summary>
        public bool InfiniteSpin
        {
            set; get;
        } = false;

        /// <summary>
        /// Gets or sets a value indicating whether field of view can be changed.
        /// </summary>
        public bool IsChangeFieldOfViewEnabled
        {
            set; get;
        } = true;

        /// <summary>
        /// Gets or sets a value indicating whether inertia is enabled for the camera manipulations.
        /// </summary>
        /// <value><c>true</c> if inertia is enabled; otherwise, <c>false</c>.</value>
        public bool IsInertiaEnabled
        {
            set; get;
        } = true;

        /// <summary>
        /// Gets or sets a value indicating whether move is enabled.
        /// </summary>
        /// <value> <c>true</c> if move is enabled; otherwise, <c>false</c> . </value>
        public bool IsMoveEnabled
        {
            set; get;
        } = true;

        /// <summary>
        /// Gets or sets a value indicating whether pan is enabled.
        /// </summary>
        public bool IsPanEnabled
        {
            set; get;
        } = true;

        /// <summary>
        /// Gets or sets a value indicating whether IsRotationEnabled.
        /// </summary>
        public bool IsRotationEnabled
        {
            set; get;
        } = true;

        /// <summary>
        /// Gets or sets a value indicating whether IsZoomEnabled.
        /// </summary>
        public bool IsZoomEnabled
        {
            set; get;
        } = true;

        /// <summary>
        /// Gets or sets the sensitivity for pan by the left and right keys.
        /// </summary>
        /// <value> The pan sensitivity. </value>
        /// <remarks>
        /// Use -1 to invert the pan direction.
        /// </remarks>
        public double LeftRightPanSensitivity
        {
            set; get;
        } = 1.0;

        /// <summary>
        /// Gets or sets the sensitivity for rotation by the left and right keys.
        /// </summary>
        /// <value> The rotation sensitivity. </value>
        /// <remarks>
        /// Use -1 to invert the rotation direction.
        /// </remarks>
        public double LeftRightRotationSensitivity
        {
            set; get;
        } = 1.0;

        /// <summary>
        /// Gets or sets the maximum field of view.
        /// </summary>
        /// <value> The maximum field of view. </value>
        public double MaximumFieldOfView
        {
            set; get;
        } = 120.0;

        /// <summary>
        /// Gets or sets the minimum field of view.
        /// </summary>
        /// <value> The minimum field of view. </value>
        public double MinimumFieldOfView
        {
            set; get;
        } = 10.0;

        /// <summary>
        /// Gets or sets the model up direction.
        /// </summary>
        public Vector3D ModelUpDirection
        {
            set; get;
        } = new Vector3D(0, 1, 0);

        /// <summary>
        /// Gets or sets the move sensitivity.
        /// </summary>
        /// <value> The move sensitivity. </value>
        public double MoveSensitivity
        {
            set; get;
        } = 1.0;

        /// <summary>
        /// Gets or sets the sensitivity for zoom by the page up and page down keys.
        /// </summary>
        /// <value> The zoom sensitivity. </value>
        /// <remarks>
        /// Use -1 to invert the zoom direction.
        /// </remarks>
        public double PageUpDownZoomSensitivity
        {
            set; get;
        } = 1.0;

        /// <summary>
        /// Gets or sets the pan Cursor.
        /// </summary>
        /// <value> The pan Cursor. </value>
        public CoreCursorType PanCursor
        {
            set; get;
        } = CoreCursorType.Hand;

        /// <summary>
        /// Gets or sets a value indicating whether to rotate around the mouse down point.
        /// </summary>
        /// <value> <c>true</c> if rotation around the mouse down point is enabled; otherwise, <c>false</c> . </value>
        public bool RotateAroundMouseDownPoint
        {
            set; get;
        } = false;

        /// <summary>
        /// Gets or sets the rotate Cursor.
        /// </summary>
        /// <value> The rotate Cursor. </value>
        public CoreCursorType RotateCursor
        {
            set; get;
        } = CoreCursorType.SizeAll;

        /// <summary>
        /// Gets or sets the rotation sensitivity (degrees/pixel).
        /// </summary>
        /// <value> The rotation sensitivity. </value>
        public double RotationSensitivity
        {
            set; get;
        } = 1.0;

        /// <summary>
        /// Gets or sets a value indicating whether to show a target adorner when manipulating the camera.
        /// </summary>
        public bool ShowCameraTarget
        {
            set; get;
        } = true;

        /// <summary>
        /// Gets or sets the max duration of mouse drag to activate spin.
        /// </summary>
        /// <remarks>
        /// If the time between mouse down and mouse up is less than this value, spin is activated.
        /// </remarks>
        public int SpinReleaseTime
        {
            set; get;
        } = 200;

        /// <summary>
        /// Gets or sets the sensitivity for pan by the up and down keys.
        /// </summary>
        /// <value> The pan sensitivity. </value>
        /// <remarks>
        /// Use -1 to invert the pan direction.
        /// </remarks>
        public double UpDownPanSensitivity
        {
            set; get;
        } = 1.0;

        /// <summary>
        /// Gets or sets the sensitivity for rotation by the up and down keys.
        /// </summary>
        /// <value> The rotation sensitivity. </value>
        /// <remarks>
        /// Use -1 to invert the rotation direction.
        /// </remarks>
        public double UpDownRotationSensitivity
        {
            set; get;
        } = 1.0;

        /// <summary>
        /// Gets or sets a value indicating whether to zoom around mouse down point.
        /// </summary>
        /// <value> <c>true</c> if zooming around the mouse down point is enabled; otherwise, <c>false</c> . </value>
        public bool ZoomAroundMouseDownPoint
        {
            set; get;
        } = false;

        /// <summary>
        /// Gets or sets the zoom Cursor.
        /// </summary>
        /// <value> The zoom CursorType. </value>
        public CoreCursorType ZoomCursor
        {
            set; get;
        } = CoreCursorType.SizeNorthSouth;

        /// <summary>
        /// Gets or sets the zoom rectangle Cursor.
        /// </summary>
        /// <value> The zoom rectangle Cursor. </value>
        public CoreCursorType ZoomRectangleCursor
        {
            set; get;
        } = CoreCursorType.SizeNorthwestSoutheast;

        /// <summary>
        /// Gets or sets ZoomSensitivity.
        /// </summary>
        public double ZoomSensitivity
        {
            set; get;
        } = 1.0;
        /// <summary>
        /// Gets or sets the zoom distance limit far.
        /// </summary>
        /// <value>
        /// The zoom distance limit far.
        /// </value>
        public double ZoomDistanceLimitFar { set; get; } = double.PositiveInfinity;
        /// <summary>
        /// Gets or sets the zoom distance limit near.
        /// </summary>
        /// <value>
        /// The zoom distance limit near.
        /// </value>
        public double ZoomDistanceLimitNear { set; get; } = 0.001;

        /// <summary>
        /// Gets a value indicating whether IsOrthographicCamera.
        /// </summary>
        protected bool IsOrthographicCamera
        {
            get
            {
                return this.ActualCamera is OrthographicCamera;
            }
        }

        /// <summary>
        /// Gets a value indicating whether IsPerspectiveCamera.
        /// </summary>
        protected bool IsPerspectiveCamera
        {
            get
            {
                return this.ActualCamera is PerspectiveCamera;
            }
        }

        /// <summary>
        /// Gets OrthographicCamera.
        /// </summary>
        protected OrthographicCamera OrthographicCamera
        {
            get
            {
                return this.ActualCamera as OrthographicCamera;
            }
        }

        /// <summary>
        /// Gets PerspectiveCamera.
        /// </summary>
        protected PerspectiveCamera PerspectiveCamera
        {
            get
            {
                return this.ActualCamera as PerspectiveCamera;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [fixed rotation point enabled].
        /// </summary>
        /// <value>
        ///   <c>true</c> if [fixed rotation point enabled]; otherwise, <c>false</c>.
        /// </value>
        public bool FixedRotationPointEnabled
        {
            set; get;
        } = false;
        /// <summary>
        /// Gets or sets the fixed rotation point.
        /// </summary>
        /// <value>
        /// The fixed rotation point.
        /// </value>
        public Point3D FixedRotationPoint
        {
            set; get;
        } = new Point3D();

        /// <summary>
        /// Gets or sets to allow rotate x direction and y direction globally. X, Y is screen space.
        /// <para>X = 1: Allow left/right rotation. Y = 1: Allow up/down rotation</para>
        /// <para>Default is (1, 1)</para>
        /// </summary>
        /// <value>
        /// The allow rotate xy.
        /// </value>
        public Vector2 AllowRotateXY
        {
            set; get;
        } = Vector2.One;

        internal int Width;
        internal int Height;
#endregion

#region TouchGesture
        public bool EnableTouchRotate { set; get; } = true;
        public bool EnablePinchZoom { set; get; } = true;
        public bool EnableThreeFingerPan { set; get; } = true;
        public bool PinchZoomAtCenter { set; get; } = false;
#endregion
        /// <summary>
        /// The camera history stack.
        /// </summary>
        /// <remarks>
        /// Implemented as a list since we want to remove items at the bottom of the stack.
        /// </remarks>
        private readonly SimpleRingBuffer<CameraSetting> cameraHistory = new SimpleRingBuffer<CameraSetting>(100);
        /// <summary>
        /// Records series of mouse down cursor changes. And play back during mouse up.
        /// </summary>
        internal Stack<CoreCursor> CursorHistory { get; } = new Stack<CoreCursor>();
        /// <summary>
        /// Decides if combined manipulation is allowed.
        /// </summary>
        private bool allowCombinedManipulation;

        /// <summary>
        /// The change field of view event handler.
        /// </summary>
        private ZoomHandler changeFieldOfViewHandler;

        /// <summary>
        /// The change look at event handler.
        /// </summary>
        private RotateHandler changeLookAtHandler;

        /// <summary>
        /// The is spinning flag.
        /// </summary>
        private bool isSpinning;

        /// <summary>
        /// The last tick.
        /// </summary>
        private long lastTick;

        /// <summary>
        /// The move speed.
        /// </summary>
        private Vector3D moveSpeed;

        /// <summary>
        /// The number of fingers used for panning.
        /// </summary>
        private int panFingerCount;

        /// <summary>
        /// The pan event handler.
        /// </summary>
        private PanHandler panHandler;

        /// <summary>
        /// The pan speed.
        /// </summary>
        private Vector3D panSpeed;

        ///// <summary>
        ///// The rectangle adorner.
        ///// </summary>
        //private RectangleAdorner rectangleAdorner;

        /// <summary>
        /// The number of fingers used for rotating.
        /// </summary>
        private int rotateFingerCount;

        /// <summary>
        /// The rotation event handler.
        /// </summary>
        private RotateHandler rotateHandler;

        /// <summary>
        /// The 3D rotation point.
        /// </summary>
        private Point3D rotationPoint3D;

        /// <summary>
        /// The rotation position.
        /// </summary>
        private Point rotationPosition;

        /// <summary>
        /// The rotation speed.
        /// </summary>
        private Vector2 rotationSpeed;

        /// <summary>
        /// The 3D point to spin around.
        /// </summary>
        private Point3D spinningPoint3D;

        /// <summary>
        /// The spinning position.
        /// </summary>
        private Point spinningPosition;

        /// <summary>
        /// The spinning speed.
        /// </summary>
        private Vector2 spinningSpeed;

        ///// <summary>
        ///// The target adorner.
        ///// </summary>
        //private Adorner targetAdorner;

        /// <summary>
        /// The touch point in the last touch delta event
        /// </summary>
        private Point touchPreviousPoint;

        /// <summary>
        /// The number of touch manipulators (fingers) in the last touch delta event
        /// </summary>
        private int manipulatorCount;

        /// <summary>
        /// The number of fingers used for zooming.
        /// </summary>
        private int zoomFingerCount;

        /// <summary>
        /// The zoom event handler.
        /// </summary>
        private readonly ZoomHandler zoomHandler;

        /// <summary>
        /// The point to zoom around.
        /// </summary>
        private Point3D zoomPoint3D;

        /// <summary>
        /// The zoom rectangle event handler.
        /// </summary>
        private ZoomRectangleHandler zoomRectangleHandler;

        /// <summary>
        /// The zoom speed.
        /// </summary>
        private double zoomSpeed;

        private static readonly Point PointZero = new Point(0, 0);
        private static readonly Vector2 VectorZero = new Vector2();
        private static readonly Vector3D Vector3DZero = new Vector3D();
        internal List<MouseGestureHandler> MouseHandlers { get; } = new List<MouseGestureHandler>();
        public Viewport3DX Viewport { private set; get; }

        public CameraController(Viewport3DX viewport)
        {
            this.Viewport = viewport;
            this.changeLookAtHandler = new RotateHandler(this, true);
            this.rotateHandler = new RotateHandler(this);
            this.zoomRectangleHandler = new ZoomRectangleHandler(this);
            this.zoomHandler = new ZoomHandler(this);
            this.panHandler = new PanHandler(this);
            this.changeFieldOfViewHandler = new ZoomHandler(this, true);
            MouseHandlers.Add(changeLookAtHandler);
            MouseHandlers.Add(rotateHandler);
            MouseHandlers.Add(zoomRectangleHandler);
            MouseHandlers.Add(zoomHandler);
            MouseHandlers.Add(panHandler);
            MouseHandlers.Add(changeFieldOfViewHandler);
            this.Viewport.SizeChanged += (s, e) =>
            {
                Width = (int)e.NewSize.Width;
                Height = (int)e.NewSize.Height;
            };
            Width = (int)Viewport.Width;
            Height = (int)Viewport.Height;
        }

#region Input Events
        private void InputController_OnBackView(object sender, EventArgs e)
        {
            ExecuteBackView();
        }

        private void InputController_OnFrontView(object sender, EventArgs e)
        {
            ExecuteFrontView();
        }

        private void InputController_OnRightView(object sender, EventArgs e)
        {
            ExecuteRightView();
        }

        private void InputController_OnLeftView(object sender, EventArgs e)
        {
            ExecuteLeftView();
        }

        private void InputController_OnBottomView(object sender, EventArgs e)
        {
            ExecuteBottomView();
        }

        private void InputController_OnTopView(object sender, EventArgs e)
        {
            ExecuteTopView();
        }

        private void InputController_OnChangeLookAt(object sender, PointerRoutedEventArgs e)
        {
            changeLookAtHandler.Execute(Viewport, e);
        }

        private void InputController_OnChangeFieldOfView(object sender, PointerRoutedEventArgs e)
        {
            changeFieldOfViewHandler.Execute(Viewport, e);
        }

        private void InputController_OnResetCamera(object sender, EventArgs e)
        {
            ExecuteResetCamera();
        }

        private void InputController_OnStartZoomRectangle(object sender, PointerRoutedEventArgs e)
        {
            zoomRectangleHandler.Execute(Viewport, e);
        }

        private void InputController_OnStartZoomExtends(object sender, PointerRoutedEventArgs e)
        {
            ExecuteZoomExtents();
        }

        private void InputController_OnStartZoom(object sender, PointerRoutedEventArgs e)
        {
            OnMouseWheel(e);
        }

        private void InputController_OnStartRotate(object sender, PointerRoutedEventArgs e)
        {
            rotateHandler.Execute(Viewport, e);
        }

        private void InputController_OnStartPan(object sender, PointerRoutedEventArgs e)
        {
            panHandler.Execute(Viewport, e);
        }

        private void InputController_OnAddZoomForce(object sender, InputController.AddForceEventArgs e)
        {
            AddZoomForce(e.Force.X);
        }

        private void InputController_OnAddRotationForce(object sender, InputController.AddForceEventArgs e)
        {
            AddRotateForce(e.Force.X, e.Force.Y);
        }

        private void InputController_OnAddPanForce(object sender, InputController.AddForceEventArgs e)
        {
            AddPanForce(e.Force.X, e.Force.Y);
        }

        private void InputController_OnAddMoveForce(object sender, InputController.AddForceEventArgs e)
        {
            AddMoveForce(e.Move);
        }

        private void InputController_OnRestoreCameraSettings(object sender, EventArgs e)
        {
            RestoreCameraSetting();
        }
#endregion
        /// <summary>
        /// Adds the specified move force.
        /// </summary>
        /// <param name="delta">
        /// The delta.
        /// </param>
        public void AddMoveForce(Vector3D delta)
        {
            if (!this.Viewport.IsMoveEnabled)
            {
                return;
            }

            this.PushCameraSetting();
            this.moveSpeed += delta * 40;
            Viewport.InvalidateRender();
        }

        /// <summary>
        /// Adds the specified pan force.
        /// </summary>
        /// <param name="dx">
        /// The delta x.
        /// </param>
        /// <param name="dy">
        /// The delta y.
        /// </param>
        public void AddPanForce(double dx, double dy)
        {
            this.AddPanForce(this.FindPanVector(dx, dy));
        }

        /// <summary>
        /// The add pan force.
        /// </summary>
        /// <param name="pan">
        /// The pan.
        /// </param>
        public void AddPanForce(Vector3D pan)
        {
            if (!this.Viewport.IsPanEnabled)
            {
                return;
            }

            this.PushCameraSetting();
            if (this.Viewport.IsInertiaEnabled)
            {
                this.panSpeed += pan;
            }
            else
            {
                this.panHandler.Pan(pan);
            }
            Viewport.InvalidateRender();
        }

        /// <summary>
        /// Adds the rotate force.
        /// </summary>
        /// <param name="force">The force.</param>
        public void AddRotateForce(Vector2 force)
        {
            AddRotateForce(force.X, force.Y);
        }
        /// <summary>
        /// The add rotate force.
        /// </summary>
        /// <param name="dx">
        /// The delta x.
        /// </param>
        /// <param name="dy">
        /// The delta y.
        /// </param>
        public void AddRotateForce(double dx, double dy)
        {
            if (!this.Viewport.IsRotationEnabled)
            {
                return;
            }

            this.PushCameraSetting();
            if (this.Viewport.IsInertiaEnabled)
            {
                this.rotationPoint3D = this.CameraTarget;
                this.rotationPosition = new Point(Width / 2.0, Height / 2.0);
                this.rotationSpeed.X += (float)dx * 40;
                this.rotationSpeed.Y += (float)dy * 40;
            }
            else if (FixedRotationPointEnabled)
            {
                this.rotationPosition = new Point(Width / 2.0, Height / 2.0);
                this.rotateHandler.Rotate(
                    this.rotationPosition.ToVector2(), new Vector2((float)(this.rotationPosition.X + dx), (float)(this.rotationPosition.Y + dy)), FixedRotationPoint);
            }
            else
            {
                this.rotationPosition = new Point(Width / 2.0, Height / 2.0);
                this.rotateHandler.Rotate(
                    this.rotationPosition.ToVector2(), new Vector2((float)(this.rotationPosition.X + dx), (float)(this.rotationPosition.Y + dy)), this.CameraTarget);
            }
            Viewport.InvalidateRender();
        }

        /// <summary>
        /// Adds the zoom force.
        /// </summary>
        /// <param name="delta">
        /// The delta.
        /// </param>
        public void AddZoomForce(double delta)
        {
            this.AddZoomForce(delta, this.CameraTarget);
        }

        /// <summary>
        /// Adds the zoom force.
        /// </summary>
        /// <param name="delta">
        /// The delta.
        /// </param>
        /// <param name="zoomOrigin">
        /// The zoom origin.
        /// </param>
        public void AddZoomForce(double delta, Point3D zoomOrigin)
        {
            if (!this.Viewport.IsZoomEnabled)
            {
                return;
            }
            this.PushCameraSetting();

            if (this.Viewport.IsInertiaEnabled)
            {
                this.zoomPoint3D = zoomOrigin;
                this.zoomSpeed += delta * 8;
            }
            else
            {
                this.zoomHandler.Zoom(delta, zoomOrigin);
            }
            Viewport.InvalidateRender();
        }

        /// <summary>
        /// Changes the direction of the camera.
        /// </summary>
        /// <param name="lookDir">
        /// The look direction.
        /// </param>
        /// <param name="upDir">
        /// The up direction.
        /// </param>
        /// <param name="animationTime">
        /// The animation time.
        /// </param>
        public void ChangeDirection(Vector3D lookDir, Vector3D upDir, double animationTime = 500)
        {
            if (!Viewport.IsRotationEnabled)
            {
                return;
            }

            this.StopAnimations();
            this.PushCameraSetting();
            this.ActualCamera.ChangeDirection(lookDir, upDir, animationTime);
        }

        /// <summary>
        /// Push the current camera settings on an internal stack.
        /// </summary>
        public void PushCameraSetting()
        {
            if (ActualCamera == null)
            {
                return;
            }
            this.cameraHistory.Add(new CameraSetting(this.ActualCamera));
        }

        /// <summary>
        /// Resets the camera.
        /// </summary>
        public void ResetCamera()
        {
            if (!Viewport.IsZoomEnabled || !Viewport.IsRotationEnabled || !Viewport.IsPanEnabled)
            {
                return;
            }

            this.PushCameraSetting();
            if (this.DefaultCamera != null)
            {
                this.DefaultCamera.CopyTo(this.ActualCamera);
            }
            else
            {
                this.ActualCamera.Reset();
                this.ActualCamera.ZoomExtents(this.Viewport);
            }
        }

        /// <summary>
        /// Resets the camera up direction.
        /// </summary>
        public void ResetCameraUpDirection()
        {
            this.CameraUpDirection = Viewport.ModelUpDirection;
        }

        /// <summary>
        /// Restores the most recent camera setting from the internal stack.
        /// </summary>
        /// <returns> The restore camera setting. </returns>
        public bool RestoreCameraSetting()
        {
            if (this.cameraHistory.Count > 0)
            {
                var cs = this.cameraHistory.Last;
                this.cameraHistory.RemoveLast();
                cs.UpdateCamera(this.ActualCamera);
                return true;
            }

            return false;
        }

        /// <summary>
        /// Starts the spin.
        /// </summary>
        /// <param name="speed">
        /// The speed.
        /// </param>
        /// <param name="position">
        /// The position.
        /// </param>
        /// <param name="aroundPoint">
        /// The spin around point.
        /// </param>
        public void StartSpin(Vector2 speed, Point position, Point3D aroundPoint)
        {
            this.spinningSpeed = speed;
            this.spinningPosition = position;
            this.spinningPoint3D = aroundPoint;
            this.isSpinning = true;
        }

        /// <summary>
        /// Stops the spin.
        /// </summary>
        public void StopSpin()
        {
            this.isSpinning = false;
            this.spinningSpeed = new Vector2();
        }
        /// <summary>
        /// Stops the zooming inertia
        /// </summary>
        public void StopZooming()
        {
            this.zoomSpeed = 0;
        }
        /// <summary>
        /// Stops the panning.
        /// </summary>
        public void StopPanning()
        {
            this.panSpeed = Vector3.Zero;
        }
        /// <summary>
        /// Zooms by the specified delta value.
        /// </summary>
        /// <param name="delta">
        /// The delta value.
        /// </param>
        public void Zoom(double delta)
        {
            this.zoomHandler.Zoom(delta);
        }

        /// <summary>
        /// Zooms to the extents of the model.
        /// </summary>
        /// <param name="animationTime">
        /// The animation time (milliseconds).
        /// </param>
        public void ZoomExtents(double animationTime = 200)
        {
            if (!Viewport.IsZoomEnabled)
            {
                return;
            }

            this.PushCameraSetting();
            this.ActualCamera.ZoomExtents(this.Viewport, animationTime);
        }

        /// <summary>
        /// Called when the <see cref="E:System.Windows.UIElement.ManipulationCompleted"/> event occurs.
        /// </summary>
        /// <param name="e">
        /// The data for the event.
        /// </param>
        public void OnManipulationCompleted(ManipulationCompletedRoutedEventArgs e)
        {
            var p = new Point(e.Position.X + e.Cumulative.Translation.X, e.Position.Y + e.Cumulative.Translation.Y);

            if (this.manipulatorCount == 1)
            {
                this.rotateHandler.Completed(p);
            }

            if (this.manipulatorCount == 2)
            {
                this.panHandler.Completed(p);
                this.zoomHandler.Completed(p);
            }
        }

        private float prevScale = 1;
        private bool disposedValue;

        /// <summary>
        /// Called when the <see cref="E:System.Windows.UIElement.ManipulationDelta"/> event occurs.
        /// </summary>
        /// <param name="e">
        /// The data for the event.
        /// </param>
        public void OnManipulationDelta(ManipulationDeltaRoutedEventArgs e)
        {
            // number of manipulators (fingers)
            if (Viewport.PointerCaptures == null)
            { return; }
            int n = Viewport.PointerCaptures.Count();
            var p = e.Position;
            var position = new Point(touchPreviousPoint.X + e.Cumulative.Translation.X, touchPreviousPoint.Y + e.Cumulative.Translation.Y);

            // http://msdn.microsoft.com/en-us/library/system.windows.uielement.manipulationdelta.aspx

            //System.Diagnostics.Debug.WriteLine($"OnManipulationDelta: T={e.Cumulative.Translation}, S={e.Cumulative.Scale}, R={e.Cumulative.Rotation}, E={e.Cumulative.Expansion}");
            //// System.Diagnostics.Debug.WriteLine(n + " Delta:" + e.DeltaManipulation.Translation + " Origin:" + e.ManipulationOrigin + " pos:" + position);

            if (this.manipulatorCount != n)
            {
                // the number of manipulators has changed

                // cancel old manipulations
                var combine = true;
                if (this.manipulatorCount == this.rotateFingerCount) // && combine)
                {
                    this.rotateHandler.Completed(position);
                    combine = this.allowCombinedManipulation;
                }

                if (this.manipulatorCount == this.zoomFingerCount && combine)
                {
                    this.zoomHandler.Completed(p);
                    combine = this.allowCombinedManipulation;
                }

                if (this.manipulatorCount == this.panFingerCount && combine)
                {
                    this.panHandler.Completed(position);
                    //combine = this.allowCombinedManipulation;
                }

                // start new manipulations
                combine = true;
                if (this.EnableTouchRotate && n == this.rotateFingerCount) // && combine)
                {
                    this.rotateHandler.Started(position);
                    e.Handled = true;
                    combine = this.allowCombinedManipulation;
                }

                if (this.EnablePinchZoom && n == this.zoomFingerCount && combine)
                {
                    this.zoomHandler.Started(p);
                    e.Handled = true;
                    combine = this.allowCombinedManipulation;
                }

                if (this.EnableThreeFingerPan && n == this.panFingerCount && combine)
                {
                    this.panHandler.Started(position);
                    e.Handled = true;
                    //combine = this.allowCombinedManipulation;
                }

                this.manipulatorCount = n;
                // skip this event, the origin may have changed
                return;
            }
            else
            {
                if (this.EnableTouchRotate && n == this.rotateFingerCount)
                {
                    this.rotateHandler.Delta(position.ToVector2());
                    e.Handled = true;
                    if (!this.allowCombinedManipulation) return;
                }

                if (this.EnablePinchZoom && n == this.zoomFingerCount)
                {
                    if (prevScale == 1)
                    {
                        prevScale = e.Cumulative.Scale;
                    }
                    else
                    {
                        if (this.PinchZoomAtCenter)
                        {
                            var s = e.Cumulative.Scale;
                            this.zoomHandler.Zoom(prevScale - s, CameraPosition + CameraLookDirection, true);
                            prevScale = s;
                        }
                        else
                        {
                            var zoomAroundPoint = this.zoomHandler.UnProject(
                                p, this.zoomHandler.Origin, this.CameraLookDirection);
                            if (zoomAroundPoint.HasValue)
                            {
                                var s = e.Cumulative.Scale;
                                this.zoomHandler.Zoom(prevScale - s, zoomAroundPoint.Value, true);
                                prevScale = s;
                            }
                        }
                    }

                    e.Handled = true;
                    if (!this.allowCombinedManipulation) return;
                }

                if (this.EnableThreeFingerPan && n == this.panFingerCount)
                {
                    this.panHandler.Delta(position.ToVector2());
                    e.Handled = true;
                    //if (!this.allowCombinedManipulation) return;
                }
            }
        }

        /// <summary>
        /// Called when the <see cref="E:System.Windows.UIElement.ManipulationStarted"/> event occurs.
        /// </summary>
        /// <param name="e">
        /// The data for the event.
        /// </param>
        public void OnManipulationStarted(ManipulationStartedRoutedEventArgs e)
        {
            this.touchPreviousPoint = e.Position;
            this.manipulatorCount = 0;
            this.prevScale = 1;
            this.panFingerCount = -1;
            this.zoomFingerCount = -1;
            this.rotateFingerCount = -1;
            this.allowCombinedManipulation = false;

            foreach (var mb in this.Viewport.ManipulationBindings)
            {
                this.allowCombinedManipulation = true;
                if (mb.Command == ViewportCommands.Pan)
                {
                    this.panFingerCount = mb.FingerCount;
                }
                else if (mb.Command == ViewportCommands.Zoom)
                {
                    this.zoomFingerCount = mb.FingerCount;
                }
                else if (mb.Command == ViewportCommands.Rotate)
                {
                    this.rotateFingerCount = mb.FingerCount;
                }
            }

            if (!this.allowCombinedManipulation)
            {
                this.panFingerCount = 3;
                this.zoomFingerCount = 2;
                this.rotateFingerCount = 1;
            }
        }

        /// <summary>
        /// Invoked when an unhandled MouseDown attached event reaches an element in its route that is derived from this class. Implement this method to add class handling for this event.
        /// </summary>
        /// <param name="e">
        /// The <see cref="T:System.Windows.Input.MouseButtonEventArgs"/> that contains the event data. This event data reports details about the mouse button that was pressed and the handled state.
        /// </param>
        public void OnMouseDown(PointerRoutedEventArgs e)
        {
            if(e.Pointer.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Pen)
            {
                OnStylusSystemGesture(e);
            }
            else if(e.Pointer.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Touch)
            {
                Viewport.CapturePointer(e.Pointer);
            }
            else
            {
                inputController.OnPointerPressed(e);
            }
        }

        public void OnMouseUp(PointerRoutedEventArgs e)
        {
            if(e.Pointer.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Touch)
            {
                Viewport.ReleasePointerCapture(e.Pointer);
                foreach(var handler in MouseHandlers)
                {
                    if (handler.IsActive)
                    {

                    }
                }
            }
        }

        /// <summary>
        /// Invoked when an unhandled StylusSystemGesture attached event reaches an element in its route that is derived from this class. Implement this method to add class handling for this event.
        /// </summary>
        /// <param name="e">
        /// The <see cref="T:System.Windows.Input.StylusSystemGestureEventArgs"/> that contains the event data.
        /// </param>
        private void OnStylusSystemGesture(PointerRoutedEventArgs e)
        {
            // Debug.WriteLine("OnStylusSystemGesture: " + e.SystemGesture);
            if (e.Pointer.IsInContact)
            {
                var p = e.GetCurrentPoint(Viewport).Position;
                this.changeLookAtHandler.Started(p);
                this.changeLookAtHandler.Completed(p);
                e.Handled = true;
            }

            //if (e.SystemGesture == SystemGesture.TwoFingerTap)
            //{
            //    this.ZoomExtents();
            //    e.Handled = true;
            //}
        }

        /// <summary>
        /// Clamps the specified value between the limits.
        /// </summary>
        /// <param name="value">
        /// The value.
        /// </param>
        /// <param name="min">
        /// The min.
        /// </param>
        /// <param name="max">
        /// The max.
        /// </param>
        /// <returns>
        /// The clamp.
        /// </returns>
        private double Clamp(double value, double min, double max)
        {
            if (value < min)
            {
                return min;
            }

            if (value > max)
            {
                return max;
            }

            return value;
        }

        /// <summary>
        /// Finds the pan vector.
        /// </summary>
        /// <param name="dx">
        /// The delta x.
        /// </param>
        /// <param name="dy">
        /// The delta y.
        /// </param>
        /// <returns>
        /// The <see cref="Vector3D"/> .
        /// </returns>
        private Vector3D FindPanVector(double dx, double dy)
        {
            var axis1 = Vector3D.Cross(this.CameraLookDirection, this.CameraUpDirection);
            var axis2 = Vector3D.Cross(axis1, this.CameraLookDirection);
            axis1.Normalize();
            axis2.Normalize();
            axis1 *= (ActualCamera.CreateLeftHandSystem ? -1 : 1);
            float l = 0;
            if (ActualCamera is PerspectiveCamera)
            {
                // this should be dependent on distance to target?
                l = this.CameraLookDirection.Length();
            }
            else if (ActualCamera.CameraInternal is OrthographicCameraCore orth)
            {
                // this should be dependent on width
                l = orth.Width;
            }
            var f = l * 0.001f;
            var move = (-axis1 * f * (float)dx) + (axis2 * f * (float)dy);

            // this should be dependent on distance to target?
            return move;
        }

        /// <summary>
        /// The on time step.
        /// </summary>
        /// <param name="ticks">
        /// The time.
        /// </param>
        public void OnTimeStep(long ticks)
        {
            if (lastTick == 0)
            {
                lastTick = ticks;
            }
            var time = (float)(ticks - this.lastTick) / Stopwatch.Frequency;
            time = time == 0 ? 0.016f : time;
            time = Math.Min(time, 0.05f); // Clamp the maximum time elapse to prevent over shooting
            // should be independent of time
            var factor = Viewport.IsInertiaEnabled ? (float)Clamp(Math.Pow(Viewport.CameraInertiaFactor, time / 0.02f), 0.1f, 1) : 0;
            bool needUpdate = false;

            if (this.rotationSpeed.LengthSquared() > 0.1)
            {
                this.rotateHandler.Rotate(
                    this.rotationPosition.ToVector2(), rotationPosition.ToVector2() + (rotationSpeed * time), this.rotationPoint3D, false);
                this.rotationSpeed *= factor;
                needUpdate = true;
                this.spinningSpeed = VectorZero;
            }
            else
            {
                this.rotationSpeed = VectorZero;
                if (this.isSpinning && this.spinningSpeed.LengthSquared() > 0.1)
                {
                    this.rotateHandler.Rotate(
                        this.spinningPosition.ToVector2(), spinningPosition.ToVector2() + (spinningSpeed * time), this.spinningPoint3D, false);
                    if (!Viewport.InfiniteSpin)
                    {
                        this.spinningSpeed *= factor;
                    }
                    needUpdate = true;
                }
                else
                {
                    this.spinningSpeed = VectorZero;
                }
            }

            if (this.panSpeed.LengthSquared() > 0.0001)
            {
                this.panHandler.Pan(this.panSpeed * time, false);
                this.panSpeed *= factor;
                needUpdate = true;
            }
            else
            {
                this.panSpeed = Vector3DZero;
            }

            if (this.moveSpeed.LengthSquared() > 0.0001)
            {
                this.zoomHandler.MoveCameraPosition(this.moveSpeed * time, false);
                this.moveSpeed *= factor;
                needUpdate = true;
            }
            else
            {
                this.moveSpeed = Vector3DZero;
            }

            if (Math.Abs(this.zoomSpeed) > 0.001)
            {
                this.zoomHandler.Zoom(this.zoomSpeed * time, this.zoomPoint3D, false, false);
                this.zoomSpeed *= factor;
                needUpdate = true;
            }
            else
            {
                zoomSpeed = 0;
            }

            if (ActualCamera != null && ActualCamera.OnTimeStep())
            {
                needUpdate = true;
            }
            if (needUpdate)
            {
                lastTick = ticks;
                Viewport.InvalidateRender();
            }
            else
            {
                lastTick = 0;
            }
        }

        public void OnMouseWheel(PointerRoutedEventArgs e)
        {
            if (!Viewport.IsZoomEnabled)
            {
                return;
            }
            var delta = e.GetCurrentPoint(Viewport).Properties.MouseWheelDelta;
            if (Viewport.ZoomAroundMouseDownPoint)
            {
                var point = e.GetCurrentPoint(Viewport).Position;
                if (this.Viewport.FindNearest(point, out Point3D nearestPoint, out Vector3D normal, out Element3D visual, out var node))
                {
                    this.AddZoomForce(-delta * 0.001, nearestPoint);
                    e.Handled = true;
                    return;
                }
            }

            this.AddZoomForce(-delta * 0.001);
            e.Handled = true;
        }

        /// <summary>
        /// The stop animations.
        /// </summary>
        public void StopAnimations()
        {
            StopPanning();
            StopZooming();
            StopSpin();
        }

        private void ExecuteZoomExtents()
        {
            this.StopAnimations();
            this.ZoomExtents();
        }

        /// <summary>
        /// Determines whether the model up direction is (0,1,0).
        /// </summary>
        /// <returns>
        ///   <c>true</c> if the up direction is (0,1,0); otherwise, <c>false</c>.
        /// </returns>
        public bool IsModelUpDirectionY()
        {
            return Viewport.ModelUpDirection.Y.Equals(1);
        }

        private void ExecuteTopView()
        {
            if (this.IsModelUpDirectionY())
            {
                this.ChangeDirection(new Vector3D(0, -1, 0), new Vector3D(0, 0, 1));
            }
            else
            {
                this.ChangeDirection(new Vector3D(0, 0, -1), new Vector3D(0, 1, 0));
            }
        }

        private void ExecuteBottomView()
        {
            if (this.IsModelUpDirectionY())
            {
                this.ChangeDirection(new Vector3D(0, 1, 0), new Vector3D(0, 0, 1));
            }
            else
            {
                this.ChangeDirection(new Vector3D(0, 0, 1), new Vector3D(0, -1, 0));
            }
        }

        private void ExecuteLeftView()
        {
            if (this.IsModelUpDirectionY())
            {
                this.ChangeDirection(new Vector3D(1, 0, 0), new Vector3D(0, 1, 0));
            }
            else
            {
                this.ChangeDirection(new Vector3D(0, 1, 0), new Vector3D(0, 0, 1));
            }
        }

        private void ExecuteRightView()
        {
            if (this.IsModelUpDirectionY())
            {
                this.ChangeDirection(new Vector3D(-1, 0, 0), new Vector3D(0, 1, 0));
            }
            else
            {
                this.ChangeDirection(new Vector3D(0, -1, 0), new Vector3D(0, 0, 1));
            }
        }

        private void ExecuteFrontView()
        {
            if (this.IsModelUpDirectionY())
            {
                this.ChangeDirection(new Vector3D(0, 0, -1), new Vector3D(0, 1, 0));
            }
            else
            {
                this.ChangeDirection(new Vector3D(-1, 0, 0), new Vector3D(0, 0, 1));
            }
        }

        private void ExecuteBackView()
        {
            if (this.IsModelUpDirectionY())
            {
                this.ChangeDirection(new Vector3D(0, 0, 1), new Vector3D(0, 1, 0));
            }
            else
            {
                this.ChangeDirection(new Vector3D(1, 0, 0), new Vector3D(0, 0, 1));
            }
        }

        private void ExecuteResetCamera()
        {
            if (Viewport.IsPanEnabled && Viewport.IsZoomEnabled && Viewport.CameraMode != CameraMode.FixedPosition)
            {
                this.StopAnimations();
                this.ResetCamera();
            }
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                }

                // TODO: free unmanaged resources (unmanaged objects) and override finalizer
                // TODO: set large fields to null
                disposedValue = true;
            }
        }

        public void Dispose()
        {
            // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
            Dispose(disposing: true);
        }
    }
}


text:
// Copyright 2022 Esri.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
// language governing permissions and limitations under the License.

using ArcGISRuntime.Samples.Managers;
using ArcGISRuntime.Samples.Shared.Managers;
using ArcGISRuntime.Samples.Shared.Models;
using Esri.ArcGISRuntime.Security;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

using Navigation = Microsoft.UI.Xaml.Navigation;

namespace ArcGISRuntime.WinUI.Viewer
{
    public sealed partial class MainPage
    {
        private bool _waitFlag;

        private List<string> _namedUserSamples = new List<string> {
            "AuthorMap",
            "SearchPortalMaps",
            "OAuth" };

        public MainPage()
        {
            InitializeComponent();

            this.Loaded += FirstLoaded;

            // Use required cache mode so we create only one page.
            NavigationCacheMode = Navigation.NavigationCacheMode.Required;

            Initialize();

            LoadTreeView(SampleManager.Current.FullTree);

            // Set the ItemsSource for the grid from the first category.
            SamplesGridView.ItemsSource = CategoriesTree.RootNodes[0].Children.ToList().Select(x => (SampleInfo)x.Content).ToList();
        }

        private void FirstLoaded(object sender, RoutedEventArgs e)
        {
            this.Loaded -= FirstLoaded;

            _ = CheckApiKey();
        }

        private async Task CheckApiKey()
        {
            // Attempt to load a locally stored API key.
            await ApiKeyManager.TrySetLocalKey();

            // Check that the current API key is valid.
            ApiKeyStatus status = await ApiKeyManager.CheckKeyValidity();
            if (status != ApiKeyStatus.Valid)
            {
                await ApiKeyDialog.ShowAsync();
            }
        }

        private void LoadTreeView(SearchableTreeNode fullTree)
        {
            // This happens when there are no search results.
            if (fullTree == null)
            {
                return;
            }
            CategoriesTree.RootNodes.Clear();

            TreeViewNode rootNode;

            foreach (SearchableTreeNode category in fullTree.Items)
            {
                rootNode = new TreeViewNode() { Content = category };
                category.Items.ForEach(info => rootNode.Children.Add(new TreeViewNode() { Content = info }));

                CategoriesTree.RootNodes.Add(rootNode);
            }
        }

        private void Initialize()
        {
            // Initialize manager that handles all the samples, this will load all the items from samples assembly and related files
            SampleManager.Current.Initialize();

            SearchBoxBorder.Background = (Brush)Application.Current.Resources["ApplicationPageBackgroundThemeBrush"];
        }

        private async void OnSampleItemTapped(object sender, TappedRoutedEventArgs e)
        {
            SampleInfo selectedSample = (sender as FrameworkElement)?.DataContext as SampleInfo;
            await SelectSample(selectedSample);
        }

        private async Task SelectSample(SampleInfo selectedSample)
        {
            // Restore API key if leaving named user sample.
            if (_namedUserSamples.Contains(SampleManager.Current?.SelectedSample?.FormalName))
            {
                ApiKeyManager.EnableKey();
            }

            // Remove API key if opening named user sample.
            if (_namedUserSamples.Contains(selectedSample.FormalName))
            {
                ApiKeyManager.DisableKey();
            }

            // Call disposable on currently selected sample.
            if (SampleManager.Current?.SelectedSample is IDisposable disposableSample)
            {
                disposableSample.Dispose();
            }

            // Call a function to clear any existing credentials from AuthenticationManager
            ClearCredentials();

            SampleManager.Current.SelectedSample = selectedSample;

            try
            {
                if (selectedSample.OfflineDataItems != null)
                {
                    CancellationTokenSource cancellationSource = new CancellationTokenSource();

                    // Show the waiting page
                    var waitPage = new WaitPage(cancellationSource);
                    SamplePageContainer.Content = waitPage;
                    SamplePageContainer.Visibility = Visibility.Visible;
                    SampleSelectionGrid.Visibility = Visibility.Collapsed;

                    // Wait for offline data to complete
                    await DataManager.EnsureSampleDataPresent(selectedSample, cancellationSource.Token,
                    (info) =>
                    {
                        waitPage.SetProgress(info.Percentage, info.HasPercentage, info.TotalBytes);
                    });
                }

                // Show the sample
                SamplePageContainer.Content = new SamplePage();
                SamplePageContainer.Visibility = Visibility.Visible;
                SampleSelectionGrid.Visibility = Visibility.Collapsed;
            }
            catch (Exception exception)
            {
                // Failed to create new instance of the sample.
                SamplePageContainer.Visibility = Visibility.Collapsed;
                SampleSelectionGrid.Visibility = Visibility.Visible;
                CategoriesTree.SelectionMode = TreeViewSelectionMode.None;
                await new MessageDialog2(exception.Message).ShowAsync();
                CategoriesTree.SelectionMode = TreeViewSelectionMode.Single;
            }
        }

        private static void ClearCredentials()
        {
            // Clear credentials (if any) from previous sample runs
            foreach (Credential cred in AuthenticationManager.Current.Credentials)
            {
                if (cred != null)
                {
                    AuthenticationManager.Current.RemoveCredential(cred);
                }
            }

            // Clear the challenge handler.
            AuthenticationManager.Current.ChallengeHandler = null;
        }

        private async void OnSearchQuerySubmitted(AutoSuggestBox searchBox, AutoSuggestBoxTextChangedEventArgs searchBoxQueryChangedEventArgs)
        {
            // Dont search again until wait from previous search expires.
            if (_waitFlag) { return; }

            _waitFlag = true;
            await Task.Delay(200);
            _waitFlag = false;

            // Search using the sample manager
            var categoriesList = SampleManager.Current.FullTree.Search(SampleSearchFunc);
            if (categoriesList == null)
            {
                categoriesList = new SearchableTreeNode("Search", new[] { new SearchableTreeNode("No results", new List<object>()) });
            }

            // Load the tree of the current categories.
            LoadTreeView(categoriesList);

            // Check if there are search results.
            if (CategoriesTree.RootNodes.Any())
            {
                // Set the items source of the grid to the first category from the search.
                SamplesGridView.ItemsSource = CategoriesTree.RootNodes[0].Children.ToList().Select(x => (SampleInfo)x.Content).ToList();

                if (!string.IsNullOrWhiteSpace(searchBox.Text))
                {
                    foreach (TreeViewNode node in CategoriesTree.RootNodes)
                    {
                        node.IsExpanded = true;
                    }
                }
            }

            // Switch to the sample selection grid.
            SamplePageContainer.Visibility = Visibility.Collapsed;
            SamplePageContainer.Content = null;
            SampleSelectionGrid.Visibility = Visibility.Visible;
        }

        private bool SampleSearchFunc(SampleInfo sample)
        {
            return SampleManager.Current.SampleSearchFunc(sample, SearchBox.Text);
        }

        private async void CategoriesTree_ItemInvoked(TreeView sender, TreeViewItemInvokedEventArgs e)
        {
            TreeViewNode selected = (TreeViewNode)e.InvokedItem;

            if (selected.Content.GetType() == typeof(SearchableTreeNode))
            {
                SamplePageContainer.Visibility = Visibility.Collapsed;
                SamplePageContainer.Content = null;
                SampleSelectionGrid.Visibility = Visibility.Visible;
                List<SampleInfo> samples = selected.Children.ToList().Select(x => (SampleInfo)x.Content).ToList();
                SamplesGridView.ItemsSource = samples;
            }
            else if (selected.Content.GetType() == typeof(SampleInfo))
            {
                await SelectSample((SampleInfo)selected.Content);
            }
        }

        private void Settings_Click(object sender, RoutedEventArgs e)
        {
            _ = settingsDialog.ShowAsync();
        }
    }

    internal class TreeViewItemTemplateSelector : DataTemplateSelector
    {
        public DataTemplate CategoryTemplate { get; set; }
        public DataTemplate SampleTemplate { get; set; }

        protected override DataTemplate SelectTemplateCore(object item)
        {
            // Select the correct template to display the text for the node in the treeview.
            if (((TreeViewNode)item).Content.GetType() == typeof(SearchableTreeNode))
            {
                return CategoryTemplate;
            }
            else if (((TreeViewNode)item).Content.GetType() == typeof(SampleInfo))
            {
                return SampleTemplate;
            }
            else
            {
                // No text will be displayed if another type of content is in the treeview.
                return null;
            }
        }
    }
}

redpajama_set_name: RedPajamaGithub


text:
export PROJECT=$(gcloud config get-value project)

docker build -f Dockerfile.master -t gcr.io/$PROJECT/scanner-master:cpu .
docker build -f Dockerfile.worker -t gcr.io/$PROJECT/scanner-worker:cpu .

gcloud docker -- push gcr.io/$PROJECT/scanner-master:cpu
gcloud docker -- push gcr.io/$PROJECT/scanner-worker:cpu

kubectl delete deploy --all
kubectl create -f master.yml
kubectl create -f worker.yml


text:
/**
 * A mixin for {@link Ext.container.Container} components that are likely to have form fields in their
 * items subtree. Adds the following capabilities:
 *
 * - Methods for handling the addition and removal of {@link Ext.form.Labelable} and {@link Ext.form.field.Field}
 *   instances at any depth within the container.
 * - Events ({@link #fieldvaliditychange} and {@link #fielderrorchange}) for handling changes to the state
 *   of individual fields at the container level.
 * - Automatic application of {@link #fieldDefaults} config properties to each field added within the
 *   container, to facilitate uniform configuration of all fields.
 *
 * This mixin is primarily for internal use by {@link Ext.form.Panel} and {@link Ext.form.FieldContainer},
 * and should not normally need to be used directly.
 *
 * @docauthor Jason Johnston <jason@sencha.com>
 */
Ext.define('Ext.form.FieldAncestor', {

    requires: [
        'Ext.container.Monitor'
    ],

    /**
     * @cfg {Object} fieldDefaults
     * If specified, the properties in this object are used as default config values for each {@link Ext.form.Labelable}
     * instance (e.g. {@link Ext.form.field.Base} or {@link Ext.form.FieldContainer}) that is added as a descendant of
     * this container. Corresponding values specified in an individual field's own configuration, or from the {@link
     * Ext.container.Container#defaults defaults config} of its parent container, will take precedence. See the
     * documentation for {@link Ext.form.Labelable} to see what config options may be specified in the fieldDefaults.
     *
     * Example:
     *
     *     new Ext.form.Panel({
     *         fieldDefaults: {
     *             labelAlign: 'left',
     *             labelWidth: 100
     *         },
     *         items: [{
     *             xtype: 'fieldset',
     *             defaults: {
     *                 labelAlign: 'top'
     *             },
     *             items: [{
     *                 name: 'field1'
     *             }, {
     *                 name: 'field2'
     *             }]
     *         }, {
     *             xtype: 'fieldset',
     *             items: [{
     *                 name: 'field3',
     *                 labelWidth: 150
     *             }, {
     *                 name: 'field4'
     *             }]
     *         }]
     *     });
     *
     * In this example, field1 and field2 will get labelAlign:'top' (from the fieldset's defaults) and labelWidth:100
     * (from fieldDefaults), field3 and field4 will both get labelAlign:'left' (from fieldDefaults and field3 will use
     * the labelWidth:150 from its own config.
     */

    xhooks: {
        initHierarchyState: function(hierarchyState) {
            if (this.fieldDefaults) {
                if (hierarchyState.fieldDefaults) {
                    hierarchyState.fieldDefaults = Ext.apply(Ext.Object.chain(hierarchyState.fieldDefaults), this.fieldDefaults);
                } else {
                    hierarchyState.fieldDefaults = this.fieldDefaults;
                }
            }
        }
    },

    /**
     * Initializes the FieldAncestor's state; this must be called from the initComponent method of any components
     * importing this mixin.
     * @protected
     */
    initFieldAncestor: function() {
        var me = this;

        me.addEvents(
            /**
             * @event fieldvaliditychange
             * Fires when the validity state of any one of the {@link Ext.form.field.Field} instances within this
             * container changes.
             * @param {Ext.form.FieldAncestor} this
             * @param {Ext.form.Labelable} The Field instance whose validity changed
             * @param {String} isValid The field's new validity state
             */
            'fieldvaliditychange',

            /**
             * @event fielderrorchange
             * Fires when the active error message is changed for any one of the {@link Ext.form.Labelable} instances
             * within this container.
             * @param {Ext.form.FieldAncestor} this
             * @param {Ext.form.Labelable} The Labelable instance whose active error was changed
             * @param {String} error The active error message
             */
            'fielderrorchange'
        );

        // We use the monitor here as opposed to event bubbling. The problem with bubbling is it doesn't
        // let us react to items being added/remove at different places in the hierarchy which may have an
        // impact on the error/valid state.
        me.monitor = new Ext.container.Monitor({
            scope: me,
            selector: '[isFormField]:not([excludeForm])',
            addHandler: me.onChildFieldAdd,
            removeHandler: me.onChildFieldRemove
        });
        me.initFieldDefaults();
    },

    initMonitor: function() {
        this.monitor.bind(this);
    },

    onChildFieldAdd: function(field) {
        var me = this;
        me.mon(field, 'errorchange', me.handleFieldErrorChange, me);
        me.mon(field, 'validitychange', me.handleFieldValidityChange, me);
    },

    onChildFieldRemove: function(field) {
        var me = this;
        me.mun(field, 'errorchange', me.handleFieldErrorChange, me);
        me.mun(field, 'validitychange', me.handleFieldValidityChange, me);
    },

    /**
     * @private Initialize the {@link #fieldDefaults} object
     */
    initFieldDefaults: function() {
        if (!this.fieldDefaults) {
            this.fieldDefaults = {};
        }
    },

    /**
     * @private Handle bubbled validitychange events from descendants; invoke the aggregated event and method
     */
    handleFieldValidityChange: function(field, isValid) {
        var me = this;
        if (field !== me) {
            me.fireEvent('fieldvaliditychange', me, field, isValid);
            me.onFieldValidityChange(field, isValid);
        }
    },

    /**
     * @private Handle bubbled errorchange events from descendants; invoke the aggregated event and method
     */
    handleFieldErrorChange: function(labelable, activeError) {
        var me = this;
        if (labelable !== me) {
            me.fireEvent('fielderrorchange', me, labelable, activeError);
            me.onFieldErrorChange(labelable, activeError);
        }
    },

    /**
     * Fired when the validity of any field within the container changes.
     * @param {Ext.form.field.Field} field The sub-field whose validity changed
     * @param {Boolean} valid The new validity state
     * @protected
     */
    onFieldValidityChange: Ext.emptyFn,

    /**
     * Fired when the error message of any field within the container changes.
     * @param {Ext.form.Labelable} field The sub-field whose active error changed
     * @param {String} error The new active error message
     * @protected
     */
    onFieldErrorChange: Ext.emptyFn,

    beforeDestroy: function(){
        this.monitor.unbind();
        this.callParent();
    }

});


text:
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html
    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">

  <title>Qt 4.3: main.cpp Example File (tutorial/t9/main.cpp)</title>
  <link href="classic.css" rel="stylesheet" type="text/css" />

  	Home · All Classes · Main Classes · Grouped Classes · Modules · Functions

main.cpp Example File
tutorial/t9/main.cpp

 #include <QApplication>
 #include <QFont>
 #include <QGridLayout>
 #include <QPushButton>

 #include "cannonfield.h"
 #include "lcdrange.h"

 class MyWidget : public QWidget
 {
 public:
     MyWidget(QWidget *parent = 0);
 };

 MyWidget::MyWidget(QWidget *parent)
     : QWidget(parent)
 {
     QPushButton *quit = new QPushButton(tr("Quit"));
     quit->setFont(QFont("Times", 18, QFont::Bold));

     connect(quit, SIGNAL(clicked()), qApp, SLOT(quit()));

     LCDRange *angle = new LCDRange;
     angle->setRange(5, 70);

     CannonField *cannonField = new CannonField;

     connect(angle, SIGNAL(valueChanged(int)),
             cannonField, SLOT(setAngle(int)));
     connect(cannonField, SIGNAL(angleChanged(int)),
             angle, SLOT(setValue(int)));

     QGridLayout *gridLayout = new QGridLayout;
     gridLayout->addWidget(quit, 0, 0);
     gridLayout->addWidget(angle, 1, 0);
     gridLayout->addWidget(cannonField, 1, 1, 2, 1);
     gridLayout->setColumnStretch(1, 10);
     setLayout(gridLayout);

     angle->setValue(60);
     angle->setFocus();
 }

 int main(int argc, char *argv[])
 {
     QApplication app(argc, argv);
     MyWidget widget;
     widget.setGeometry(100, 100, 500, 355);
     widget.show();
     return app.exec();
 }

<address>

Copyright © 2008 Trolltech
Trademarks

Qt 4.3.5

</address>


text:
using Rabbit.Blogs.Models;
using System;

namespace Rabbit.Blogs.ViewModels.Themes
{
    public class PostDetailedViewModel
    {
        public Lazy<PostRecord> BeforePost { get; set; }

        public PostRecord CurrentPost { get; set; }

        public Lazy<PostRecord> AfterPost { get; set; }
    }
}


text:
Namespace Ribbons.Workflow.Documents

    Public Class AssignWorkflow
        Inherits RibbonButtonBase

        Public Sub New()
            _Image = ""
            _Order = 1
            _Text = "Assign Workflow"
            _ToolTip = ""
        End Sub

        Protected Friend Overrides Sub OnClick()

        End Sub

        Protected Friend Overrides Sub OnIsEnabled()

        End Sub

    End Class

End Namespace


text:
namespace impeller {
namespace testing {

using DisplayListTest = DisplayListPlayground;
INSTANTIATE_PLAYGROUND_SUITE(DisplayListTest);

TEST_P(DisplayListTest, CanDrawRect) {
  flutter::DisplayListBuilder builder;
  builder.setColor(SK_ColorBLUE);
  builder.drawRect(SkRect::MakeXYWH(10, 10, 100, 100));
  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}

TEST_P(DisplayListTest, CanDrawTextBlob) {
  flutter::DisplayListBuilder builder;
  builder.setColor(SK_ColorBLUE);
  builder.drawTextBlob(SkTextBlob::MakeFromString("Hello", CreateTestFont()),
                       100, 100);
  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}

TEST_P(DisplayListTest, CanDrawImage) {
  auto texture = CreateTextureForFixture("embarcadero.jpg");
  flutter::DisplayListBuilder builder;
  builder.drawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100),
                    flutter::DlImageSampling::kNearestNeighbor, true);
  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}

TEST_P(DisplayListTest, CanDrawCapsAndJoins) {
  flutter::DisplayListBuilder builder;

  builder.setStyle(flutter::DlDrawStyle::kStroke);
  builder.setStrokeWidth(30);
  builder.setColor(SK_ColorRED);

  auto path =
      SkPathBuilder{}.moveTo(-50, 0).lineTo(0, -50).lineTo(50, 0).snapshot();

  builder.translate(100, 100);
  {
    builder.setStrokeCap(flutter::DlStrokeCap::kButt);
    builder.setStrokeJoin(flutter::DlStrokeJoin::kMiter);
    builder.setStrokeMiter(4);
    builder.drawPath(path);
  }

  {
    builder.save();
    builder.translate(0, 100);
    // The joint in the path is 45 degrees. A miter length of 1 convert to a
    // bevel in this case.
    builder.setStrokeMiter(1);
    builder.drawPath(path);
    builder.restore();
  }

  builder.translate(150, 0);
  {
    builder.setStrokeCap(flutter::DlStrokeCap::kSquare);
    builder.setStrokeJoin(flutter::DlStrokeJoin::kBevel);
    builder.drawPath(path);
  }

  builder.translate(150, 0);
  {
    builder.setStrokeCap(flutter::DlStrokeCap::kRound);
    builder.setStrokeJoin(flutter::DlStrokeJoin::kRound);
    builder.drawPath(path);
  }

  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}

TEST_P(DisplayListTest, CanDrawArc) {
  bool first_frame = true;
  auto callback = [&]() {
    if (first_frame) {
      first_frame = false;
      ImGui::SetNextWindowSize({400, 100});
      ImGui::SetNextWindowPos({300, 550});
    }

    static float start_angle = 45;
    static float sweep_angle = 270;
    static bool use_center = true;

    ImGui::Begin("Controls");
    ImGui::SliderFloat("Start angle", &start_angle, -360, 360);
    ImGui::SliderFloat("Sweep angle", &sweep_angle, -360, 360);
    ImGui::Checkbox("Use center", &use_center);
    ImGui::End();

    auto [p1, p2] = IMPELLER_PLAYGROUND_LINE(
        Point(200, 200), Point(400, 400), 20, Color::White(), Color::White());

    flutter::DisplayListBuilder builder;

    Vector2 scale = GetContentScale();
    builder.scale(scale.x, scale.y);
    builder.setStyle(flutter::DlDrawStyle::kStroke);
    builder.setStrokeCap(flutter::DlStrokeCap::kRound);
    builder.setStrokeJoin(flutter::DlStrokeJoin::kMiter);
    builder.setStrokeMiter(10);
    auto rect = SkRect::MakeLTRB(p1.x, p1.y, p2.x, p2.y);
    builder.setColor(SK_ColorGREEN);
    builder.setStrokeWidth(2);
    builder.drawRect(rect);
    builder.setColor(SK_ColorRED);
    builder.setStrokeWidth(10);
    builder.drawArc(rect, start_angle, sweep_angle, use_center);

    return builder.Build();
  };
  ASSERT_TRUE(OpenPlaygroundHere(callback));
}

TEST_P(DisplayListTest, StrokedPathsDrawCorrectly) {
  flutter::DisplayListBuilder builder;
  builder.setColor(SK_ColorRED);
  builder.setStyle(flutter::DlDrawStyle::kStroke);
  builder.setStrokeWidth(10);

  // Rectangle
  builder.translate(100, 100);
  builder.drawRect(SkRect::MakeSize({100, 100}));

  // Rounded rectangle
  builder.translate(150, 0);
  builder.drawRRect(SkRRect::MakeRectXY(SkRect::MakeSize({100, 50}), 10, 10));

  // Double rounded rectangle
  builder.translate(150, 0);
  builder.drawDRRect(
      SkRRect::MakeRectXY(SkRect::MakeSize({100, 50}), 10, 10),
      SkRRect::MakeRectXY(SkRect::MakeXYWH(10, 10, 80, 30), 10, 10));

  // Contour with duplicate join points
  {
    builder.translate(150, 0);
    SkPath path;
    path.lineTo({100, 0});
    path.lineTo({100, 0});
    path.lineTo({100, 100});
    builder.drawPath(path);
  }

  // Contour with duplicate end points
  {
    builder.setStrokeCap(flutter::DlStrokeCap::kRound);
    builder.translate(150, 0);
    SkPath path;
    path.moveTo(0, 0);
    path.lineTo({0, 0});
    path.lineTo({50, 50});
    path.lineTo({100, 0});
    path.lineTo({100, 0});
    builder.drawPath(path);
  }

  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}

TEST_P(DisplayListTest, CanDrawWithOddPathWinding) {
  flutter::DisplayListBuilder builder;
  builder.setColor(SK_ColorRED);
  builder.setStyle(flutter::DlDrawStyle::kFill);

  builder.translate(300, 300);
  SkPath path;
  path.setFillType(SkPathFillType::kEvenOdd);
  path.addCircle(0, 0, 100);
  path.addCircle(0, 0, 50);
  builder.drawPath(path);

  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}

TEST_P(DisplayListTest, CanDrawWithMaskBlur) {
  auto texture = CreateTextureForFixture("embarcadero.jpg");
  flutter::DisplayListBuilder builder;

  // Mask blurred image.
  {
    auto filter = flutter::DlBlurMaskFilter(kNormal_SkBlurStyle, 10.0f);
    builder.setMaskFilter(&filter);
    builder.drawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100),
                      flutter::DlImageSampling::kNearestNeighbor, true);
  }

  // Mask blurred filled path.
  {
    builder.setColor(SK_ColorYELLOW);
    auto filter = flutter::DlBlurMaskFilter(kOuter_SkBlurStyle, 10.0f);
    builder.setMaskFilter(&filter);
    builder.drawArc(SkRect::MakeXYWH(410, 110, 100, 100), 45, 270, true);
  }

  // Mask blurred text.
  {
    auto filter = flutter::DlBlurMaskFilter(kSolid_SkBlurStyle, 10.0f);
    builder.setMaskFilter(&filter);
    builder.drawTextBlob(
        SkTextBlob::MakeFromString("Testing", CreateTestFont()), 220, 170);
  }

  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}

TEST_P(DisplayListTest, CanDrawWithBlendColorFilter) {
  auto texture = CreateTextureForFixture("embarcadero.jpg");
  flutter::DisplayListBuilder builder;

  // Pipeline blended image.
  {
    auto filter = flutter::DlBlendColorFilter(SK_ColorYELLOW,
                                              flutter::DlBlendMode::kModulate);
    builder.setColorFilter(&filter);
    builder.drawImage(DlImageImpeller::Make(texture), SkPoint::Make(100, 100),
                      flutter::DlImageSampling::kNearestNeighbor, true);
  }

  // Advanced blended image.
  {
    auto filter =
        flutter::DlBlendColorFilter(SK_ColorRED, flutter::DlBlendMode::kScreen);
    builder.setColorFilter(&filter);
    builder.drawImage(DlImageImpeller::Make(texture), SkPoint::Make(250, 250),
                      flutter::DlImageSampling::kNearestNeighbor, true);
  }

  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}

TEST_P(DisplayListTest, CanDrawWithImageBlurFilter) {
  auto texture = CreateTextureForFixture("embarcadero.jpg");

  bool first_frame = true;
  auto callback = [&]() {
    if (first_frame) {
      first_frame = false;
      ImGui::SetNextWindowSize({400, 100});
      ImGui::SetNextWindowPos({300, 550});
    }

    static float sigma[] = {10, 10};

    ImGui::Begin("Controls");
    ImGui::SliderFloat2("Sigma", sigma, 0, 100);
    ImGui::End();

    flutter::DisplayListBuilder builder;

    auto filter = flutter::DlBlurImageFilter(sigma[0], sigma[1],
                                             flutter::DlTileMode::kClamp);
    builder.setImageFilter(&filter);
    builder.drawImage(DlImageImpeller::Make(texture), SkPoint::Make(200, 200),
                      flutter::DlImageSampling::kNearestNeighbor, true);

    return builder.Build();
  };

  ASSERT_TRUE(OpenPlaygroundHere(callback));
}

TEST_P(DisplayListTest, CanDrawBackdropFilter) {
  auto texture = CreateTextureForFixture("embarcadero.jpg");

  bool first_frame = true;
  auto callback = [&]() {
    if (first_frame) {
      first_frame = false;
      ImGui::SetNextWindowPos({10, 10});
    }

    static float sigma[] = {10, 10};
    static float ctm_scale = 1;
    static bool use_bounds = true;
    static bool draw_circle = true;
    static bool add_clip = true;

    ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
    ImGui::SliderFloat2("Sigma", sigma, 0, 100);
    ImGui::SliderFloat("Scale", &ctm_scale, 0, 10);
    ImGui::NewLine();
    ImGui::TextWrapped(
        "If everything is working correctly, none of the options below should "
        "impact the filter's appearance.");
    ImGui::Checkbox("Use SaveLayer bounds", &use_bounds);
    ImGui::Checkbox("Draw child element", &draw_circle);
    ImGui::Checkbox("Add pre-clip", &add_clip);
    ImGui::End();

    flutter::DisplayListBuilder builder;

    Vector2 scale = ctm_scale * GetContentScale();
    builder.scale(scale.x, scale.y);

    auto filter = flutter::DlBlurImageFilter(sigma[0], sigma[1],
                                             flutter::DlTileMode::kClamp);

    std::optional<SkRect> bounds;
    if (use_bounds) {
      auto [p1, p2] = IMPELLER_PLAYGROUND_LINE(
          Point(350, 150), Point(800, 600), 20, Color::White(), Color::White());
      bounds = SkRect::MakeLTRB(p1.x, p1.y, p2.x, p2.y);
    }

    // Insert a clip to test that the backdrop filter handles stencil depths > 0
    // correctly.
    if (add_clip) {
      builder.clipRect(SkRect::MakeLTRB(0, 0, 99999, 99999),
                       SkClipOp::kIntersect, true);
    }

    builder.drawImage(DlImageImpeller::Make(texture), SkPoint::Make(200, 200),
                      flutter::DlImageSampling::kNearestNeighbor, true);
    builder.saveLayer(bounds.has_value() ? &bounds.value() : nullptr, nullptr,
                      &filter);

    if (draw_circle) {
      auto circle_center =
          IMPELLER_PLAYGROUND_POINT(Point(500, 400), 20, Color::Red());

      builder.setStyle(flutter::DlDrawStyle::kStroke);
      builder.setStrokeCap(flutter::DlStrokeCap::kButt);
      builder.setStrokeJoin(flutter::DlStrokeJoin::kBevel);
      builder.setStrokeWidth(10);
      builder.setColor(flutter::DlColor::kRed().withAlpha(100));
      builder.drawCircle({circle_center.x, circle_center.y}, 100);
    }

    return builder.Build();
  };

  ASSERT_TRUE(OpenPlaygroundHere(callback));
}

TEST_P(DisplayListTest, CanDrawNinePatchImage) {
  // Image is drawn with corners to scale and center pieces stretched to fit.
  auto texture = CreateTextureForFixture("embarcadero.jpg");
  flutter::DisplayListBuilder builder;
  auto size = texture->GetSize();
  builder.drawImageNine(
      DlImageImpeller::Make(texture),
      SkIRect::MakeLTRB(size.width / 4, size.height / 4, size.width * 3 / 4,
                        size.height * 3 / 4),
      SkRect::MakeLTRB(0, 0, size.width * 2, size.height * 2),
      flutter::DlFilterMode::kNearest, true);
  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}

TEST_P(DisplayListTest, CanDrawNinePatchImageCenterWidthBiggerThanDest) {
  // Edge case, the width of the corners does not leave any room for the
  // center slice. The center (across the vertical axis) is folded out of the
  // resulting image.
  auto texture = CreateTextureForFixture("embarcadero.jpg");
  flutter::DisplayListBuilder builder;
  auto size = texture->GetSize();
  builder.drawImageNine(
      DlImageImpeller::Make(texture),
      SkIRect::MakeLTRB(size.width / 4, size.height / 4, size.width * 3 / 4,
                        size.height * 3 / 4),
      SkRect::MakeLTRB(0, 0, size.width / 2, size.height),
      flutter::DlFilterMode::kNearest, true);
  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}

TEST_P(DisplayListTest, CanDrawNinePatchImageCenterHeightBiggerThanDest) {
  // Edge case, the height of the corners does not leave any room for the
  // center slice. The center (across the horizontal axis) is folded out of the
  // resulting image.
  auto texture = CreateTextureForFixture("embarcadero.jpg");
  flutter::DisplayListBuilder builder;
  auto size = texture->GetSize();
  builder.drawImageNine(
      DlImageImpeller::Make(texture),
      SkIRect::MakeLTRB(size.width / 4, size.height / 4, size.width * 3 / 4,
                        size.height * 3 / 4),
      SkRect::MakeLTRB(0, 0, size.width, size.height / 2),
      flutter::DlFilterMode::kNearest, true);
  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}

TEST_P(DisplayListTest, CanDrawNinePatchImageCenterBiggerThanDest) {
  // Edge case, the width and height of the corners does not leave any
  // room for the center slices. Only the corners are displayed.
  auto texture = CreateTextureForFixture("embarcadero.jpg");
  flutter::DisplayListBuilder builder;
  auto size = texture->GetSize();
  builder.drawImageNine(
      DlImageImpeller::Make(texture),
      SkIRect::MakeLTRB(size.width / 4, size.height / 4, size.width * 3 / 4,
                        size.height * 3 / 4),
      SkRect::MakeLTRB(0, 0, size.width / 2, size.height / 2),
      flutter::DlFilterMode::kNearest, true);
  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}

TEST_P(DisplayListTest, CanDrawNinePatchImageCornersScaledDown) {
  // Edge case, there is not enough room for the corners to be drawn
  // without scaling them down.
  auto texture = CreateTextureForFixture("embarcadero.jpg");
  flutter::DisplayListBuilder builder;
  auto size = texture->GetSize();
  builder.drawImageNine(
      DlImageImpeller::Make(texture),
      SkIRect::MakeLTRB(size.width / 4, size.height / 4, size.width * 3 / 4,
                        size.height * 3 / 4),
      SkRect::MakeLTRB(0, 0, size.width / 4, size.height / 4),
      flutter::DlFilterMode::kNearest, true);
  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}

TEST_P(DisplayListTest, CanDrawPoints) {
  flutter::DisplayListBuilder builder;
  SkPoint points[7] = {
      {0, 0},      //
      {100, 100},  //
      {100, 0},    //
      {0, 100},    //
      {0, 0},      //
      {48, 48},    //
      {52, 52},    //
  };
  std::vector<flutter::DlStrokeCap> caps = {
      flutter::DlStrokeCap::kButt,
      flutter::DlStrokeCap::kRound,
      flutter::DlStrokeCap::kSquare,
  };
  flutter::DlPaint paint =
      flutter::DlPaint()                                         //
          .setColor(flutter::DlColor::kYellow().withAlpha(127))  //
          .setStrokeWidth(20);
  builder.translate(50, 50);
  for (auto cap : caps) {
    paint.setStrokeCap(cap);
    builder.save();
    builder.drawPoints(SkCanvas::kPoints_PointMode, 7, points, paint);
    builder.translate(150, 0);
    builder.drawPoints(SkCanvas::kLines_PointMode, 5, points, paint);
    builder.translate(150, 0);
    builder.drawPoints(SkCanvas::kPolygon_PointMode, 5, points, paint);
    builder.restore();
    builder.translate(0, 150);
  }
  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}

TEST_P(DisplayListTest, CanDrawZeroLengthLine) {
  flutter::DisplayListBuilder builder;
  std::vector<flutter::DlStrokeCap> caps = {
      flutter::DlStrokeCap::kButt,
      flutter::DlStrokeCap::kRound,
      flutter::DlStrokeCap::kSquare,
  };
  flutter::DlPaint paint =
      flutter::DlPaint()                                         //
          .setColor(flutter::DlColor::kYellow().withAlpha(127))  //
          .setDrawStyle(flutter::DlDrawStyle::kStroke)           //
          .setStrokeCap(flutter::DlStrokeCap::kButt)             //
          .setStrokeWidth(20);
  SkPath path = SkPath().addPoly({{150, 50}, {150, 50}}, false);
  for (auto cap : caps) {
    paint.setStrokeCap(cap);
    builder.drawLine({50, 50}, {50, 50}, paint);
    builder.drawPath(path, paint);
    builder.translate(0, 150);
  }
  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}

TEST_P(DisplayListTest, CanDrawShadow) {
  flutter::DisplayListBuilder builder;
  std::array<SkPath, 3> paths = {
      SkPath{}.addRect(SkRect::MakeXYWH(0, 0, 200, 100)),
      SkPath{}.addRRect(
          SkRRect::MakeRectXY(SkRect::MakeXYWH(0, 0, 200, 100), 30, 30)),
      SkPath{}.addCircle(100, 50, 50),
  };
  builder.setColor(flutter::DlColor::kWhite());
  builder.drawPaint();
  builder.setColor(flutter::DlColor::kCyan());
  builder.translate(100, 100);
  for (size_t x = 0; x < paths.size(); x++) {
    builder.save();
    for (size_t y = 0; y < 5; y++) {
      builder.drawShadow(paths[x], flutter::DlColor::kBlack(), 3 + y * 5, false,
                         1);
      builder.drawPath(paths[x]);
      builder.translate(0, 200);
    }
    builder.restore();
    builder.translate(300, 0);
  }

  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}

// Draw a hexagon using triangle fan
TEST_P(DisplayListTest, CanConvertTriangleFanToTriangles) {
  constexpr Scalar hexagon_radius = 125;
  auto hex_start = Point(200.0, -hexagon_radius + 200.0);
  auto center_to_flat = 1.73 / 2 * hexagon_radius;

  // clang-format off
  std::vector<SkPoint> vertices = {
    SkPoint::Make(hex_start.x, hex_start.y),
    SkPoint::Make(hex_start.x + center_to_flat, hex_start.y + 0.5 * hexagon_radius),
    SkPoint::Make(hex_start.x + center_to_flat, hex_start.y + 1.5 * hexagon_radius),
    SkPoint::Make(hex_start.x + center_to_flat, hex_start.y + 1.5 * hexagon_radius),
    SkPoint::Make(hex_start.x, hex_start.y + 2 * hexagon_radius),
    SkPoint::Make(hex_start.x, hex_start.y + 2 * hexagon_radius),
    SkPoint::Make(hex_start.x - center_to_flat, hex_start.y + 1.5 * hexagon_radius),
    SkPoint::Make(hex_start.x - center_to_flat, hex_start.y + 1.5 * hexagon_radius),
    SkPoint::Make(hex_start.x - center_to_flat, hex_start.y + 0.5 * hexagon_radius)
  };
  // clang-format on
  auto paint = flutter::DlPaint().setColor(flutter::DlColor::kDarkGrey());
  auto dl_vertices = flutter::DlVertices::Make(
      flutter::DlVertexMode::kTriangleFan, vertices.size(), vertices.data(),
      nullptr, nullptr);
  flutter::DisplayListBuilder builder;
  builder.drawVertices(dl_vertices, flutter::DlBlendMode::kSrcOver, paint);
  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}

TEST_P(DisplayListTest, CanDrawZeroWidthLine) {
  flutter::DisplayListBuilder builder;
  std::vector<flutter::DlStrokeCap> caps = {
      flutter::DlStrokeCap::kButt,
      flutter::DlStrokeCap::kRound,
      flutter::DlStrokeCap::kSquare,
  };
  flutter::DlPaint paint =                              //
      flutter::DlPaint()                                //
          .setColor(flutter::DlColor::kWhite())         //
          .setDrawStyle(flutter::DlDrawStyle::kStroke)  //
          .setStrokeWidth(0);
  flutter::DlPaint outline_paint =                      //
      flutter::DlPaint()                                //
          .setColor(flutter::DlColor::kYellow())        //
          .setDrawStyle(flutter::DlDrawStyle::kStroke)  //
          .setStrokeCap(flutter::DlStrokeCap::kSquare)  //
          .setStrokeWidth(1);
  SkPath path = SkPath().addPoly({{150, 50}, {160, 50}}, false);
  for (auto cap : caps) {
    paint.setStrokeCap(cap);
    builder.drawLine({50, 50}, {60, 50}, paint);
    builder.drawRect({45, 45, 65, 55}, outline_paint);
    builder.drawLine({100, 50}, {100, 50}, paint);
    if (cap != flutter::DlStrokeCap::kButt) {
      builder.drawRect({95, 45, 105, 55}, outline_paint);
    }
    builder.drawPath(path, paint);
    builder.drawRect(path.getBounds().makeOutset(5, 5), outline_paint);
    builder.translate(0, 150);
  }
  ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}

}  // namespace testing
}  // namespace impeller


text:
<?php

namespace Orchestra\Messages\Tests\Feature;

use Mockery as m;
use Orchestra\Support\Facades\Messages;

class MessageBagTest extends TestCase
{
    /** @test */
    public function it_can_make_a_message_instance()
    {
        Messages::add('welcome', 'Hello world');
        Messages::setFormat();

        $this->assertInstanceOf('Orchestra\Messages\MessageBag', $this->app['orchestra.messages']);
        $this->assertEquals(['Hello world'], Messages::get('welcome'));

        Messages::add('welcome', 'Hi Foobar')
            ->add('welcome', 'Heya Admin');

        $this->assertEquals(['Hello world', 'Hi Foobar', 'Heya Admin'], Messages::get('welcome'));
    }

    /** @test */
    public function it_can_flash_empty_message_to_session()
    {
        $session = m::mock('\Illuminate\Session\Store');
        $session->shouldReceive('pull')->once()->with('message', [])->andReturn([])
            ->shouldReceive('flash')->once()->andReturn(true);

        $this->assertNull(Messages::setSessionStore($session)->save());
        $this->assertSame($session, Messages::getSessionStore());
    }

    /** @test */
    public function it_can_store_messages_to_session()
    {
        $session = m::mock('\Illuminate\Session\Store');
        $session->shouldReceive('pull')->once()->with('message', [])->andReturn([])
            ->shouldReceive('flash')->once()->andReturn(true);

        Messages::setSessionStore($session);
        Messages::add('hello', 'Hi World')
                ->add('bye', 'Goodbye');

        $this->assertSame($session, Messages::getSessionStore());

        Messages::save();
    }

    /** @test */
    public function it_can_copy_message_from_session()
    {
        $session = m::mock('\Illuminate\Session\Store');
        $session->shouldReceive('pull')->once()->with('message', [])
                ->andReturn(['hello' => ['Hi World'], 'bye' => ['Goodbye']]);

        $clone = Messages::setSessionStore($session)->copy();
        $clone->setFormat();

        $this->assertSame($session, Messages::getSessionStore());
        $this->assertInstanceOf('Illuminate\Support\MessageBag', $clone);
        $this->assertEquals(['Hi World'], $clone->get('hello'));
        $this->assertEquals(['Goodbye'], $clone->get('bye'));
    }

    /** @test */
    public function it_can_extend_messages_to_current_request()
    {
        $session = m::mock('\Illuminate\Session\Store');
        $session->shouldReceive('pull')->once()->with('message', [])->andReturn(['hello' => ['Hi World']]);

        $callback = function ($msg) {
            $msg->add('hello', 'Hi Orchestra Platform');
        };

        $stub = Messages::setSessionStore($session);
        $output = $stub->extend($callback);

        $clone = $stub->copy();
        $clone->setFormat();

        $this->assertSame($session, Messages::getSessionStore());
        $this->assertInstanceOf('Illuminate\Support\MessageBag', $output);
        $this->assertInstanceOf('Illuminate\Support\MessageBag', $clone);
        $this->assertEquals(['Hi World', 'Hi Orchestra Platform'], $clone->get('hello'));
    }
}