5 using System.Collections.Generic;
7 using SiliconStudio.Core;
8 using SiliconStudio.Core.Diagnostics;
9 using SiliconStudio.Core.Mathematics;
10 using SiliconStudio.Paradox.Games;
11 using SiliconStudio.Paradox.Graphics;
12 using SiliconStudio.Paradox.Input;
13 using SiliconStudio.Paradox.UI.Controls;
15 namespace SiliconStudio.
Paradox.UI
22 internal Matrix WorldMatrix;
31 get {
return rootElement;}
36 if(rootElement != null)
41 internal Matrix ViewProjectionInternal;
47 public Matrix ViewMatrix {
get;
private set; }
53 public Matrix ProjectionMatrix {
get;
private set; }
59 public Matrix ViewProjectionMatrix {
get {
return ViewProjectionInternal; } }
63 internal UIBatch Batch {
get;
private set; }
71 internal Vector2 BackBufferVirtualResolutionRatio {
get;
private set; }
73 private Vector3 virtualResolution;
77 private UIElement lastTouchedElement;
78 private Vector3 lastIntersectionPoint;
81 private readonly HashSet<UIElement> newlySelectedElementParents =
new HashSet<UIElement>();
83 private readonly List<PointerEvent> compactedPointerEvents =
new List<PointerEvent>();
87 private float uiFrustrumHeight;
89 private Matrix inverseViewMatrix;
94 private float viewPositionZ;
96 private float nearPlane;
98 private float farPlane;
100 private Vector2 lastMousePosition;
102 private UIElement lastOveredElement;
107 Services.AddService(typeof(
UISystem),
this);
110 gameVirtualResolution.VirtualResolutionChanged += OnGameVirtualResolutionChanged;
113 private void OnGameVirtualResolutionChanged(
object sender,
EventArgs eventArgs)
115 VirtualResolution = gameVirtualResolution.VirtualResolution;
129 Game.Window.ClientSizeChanged += WindowOnClientSizeChanged;
131 Game.Activated += OnApplicationResumed;
132 Game.Deactivated += OnApplicationPaused;
137 gameVirtualResolution.VirtualResolutionChanged -= OnGameVirtualResolutionChanged;
139 Game.Activated -= OnApplicationResumed;
140 Game.Deactivated -= OnApplicationPaused;
143 OnApplicationPaused(
this,
EventArgs.Empty);
148 private void WindowOnClientSizeChanged(
object sender,
EventArgs eventArgs)
150 BackBufferVirtualResolutionRatio = CalculateBackBufferVirtualResolutionRatio();
153 private Vector2 CalculateBackBufferVirtualResolutionRatio()
168 StencilEnable =
true,
171 StencilDepthBufferFail = StencilOperation.Keep,
172 StencilFail = StencilOperation.Keep,
173 StencilPass = StencilOperation.Keep,
174 StencilFunction = CompareFunction.Equal
177 KeepStencilValueState = DepthStencilState.New(
GraphicsDevice, depthStencilDescription);
179 depthStencilDescription.FrontFace.StencilPass = StencilOperation.Increment;
180 IncreaseStencilValueState = DepthStencilState.New(GraphicsDevice, depthStencilDescription);
182 depthStencilDescription.FrontFace.StencilPass = StencilOperation.Decrement;
183 DecreaseStencilValueState = DepthStencilState.New(GraphicsDevice, depthStencilDescription);
186 var designsTexture = TextureExtensions.CreateTextureFromFileData(GraphicsDevice, DefaultDesigns.Designs);
187 Button.PressedImagePropertyKey.DefaultValueMetadata = DefaultValueMetadata.Static(
new UIImage(
"Default button pressed design", designsTexture) { Borders = 8 * Vector4.One, Region =
new RectangleF(71, 3, 32, 32)});
188 Button.NotPressedImagePropertyKey.DefaultValueMetadata = DefaultValueMetadata.Static(
new UIImage(
"Default button not pressed design", designsTexture) { Borders = 8 * Vector4.One, Region =
new RectangleF(3, 3, 32, 32) });
189 Button.MouseOverImagePropertyKey.DefaultValueMetadata = DefaultValueMetadata.Static(
new UIImage(
"Default button overred design", designsTexture) { Borders = 8 * Vector4.One, Region =
new RectangleF(37, 3, 32, 32) });
190 EditText.ActiveImagePropertyKey.DefaultValueMetadata = DefaultValueMetadata.Static(
new UIImage(
"Default edit active design", designsTexture) { Borders = 12 * Vector4.One, Region =
new RectangleF(105, 3, 32, 32) });
191 EditText.InactiveImagePropertyKey.DefaultValueMetadata = DefaultValueMetadata.Static(
new UIImage(
"Default edit inactive design", designsTexture) { Borders = 12 * Vector4.One, Region =
new RectangleF(139, 3, 32, 32) });
192 EditText.MouseOverImagePropertyKey.DefaultValueMetadata = DefaultValueMetadata.Static(
new UIImage(
"Default edit overred design", designsTexture) { Borders = 12 * Vector4.One, Region =
new RectangleF(173, 3, 32, 32) });
193 ToggleButton.CheckedImagePropertyKey.DefaultValueMetadata = DefaultValueMetadata.Static(
new UIImage(
"Default toggle button checked design", designsTexture) { Borders = 8 * Vector4.One, Region =
new RectangleF(71, 3, 32, 32) });
194 ToggleButton.UncheckedImagePropertyKey.DefaultValueMetadata = DefaultValueMetadata.Static(
new UIImage(
"Default toggle button unchecked design", designsTexture) { Borders = 8 * Vector4.One, Region =
new RectangleF(3, 3, 32, 32) });
195 ToggleButton.IndeterminateImagePropertyKey.DefaultValueMetadata = DefaultValueMetadata.Static(
new UIImage(
"Default toggle button indeterminate design", designsTexture) { Borders = 8 * Vector4.One, Region =
new RectangleF(37, 3, 32, 32) });
196 Slider.TrackBackgroundImagePropertyKey.DefaultValueMetadata = DefaultValueMetadata.Static(
new UIImage(
"Default slider track background design", designsTexture) { Borders = 14 * Vector4.One, Region =
new RectangleF(207, 3, 32, 32) });
197 Slider.TrackForegroundImagePropertyKey.DefaultValueMetadata = DefaultValueMetadata.Static(
new UIImage(
"Default slider track foreground design", designsTexture) { Borders = 0 * Vector4.One, Region =
new RectangleF(3, 37, 32, 32) });
198 Slider.ThumbImagePropertyKey.DefaultValueMetadata = DefaultValueMetadata.Static(
new UIImage(
"Default slider thumb design", designsTexture) { Borders = 4 * Vector4.One, Region =
new RectangleF(37, 37, 16, 32) });
199 Slider.MouseOverThumbImagePropertyKey.DefaultValueMetadata = DefaultValueMetadata.Static(
new UIImage(
"Default slider thumb overred design", designsTexture) { Borders = 4 * Vector4.One, Region =
new RectangleF(71, 37, 16, 32) });
200 Slider.TickImagePropertyKey.DefaultValueMetadata = DefaultValueMetadata.Static(
new UIImage(
"Default slider track foreground design", designsTexture) { Region =
new RectangleF(245, 3, 3, 6) });
201 Slider.TickOffsetPropertyKey.DefaultValueMetadata = DefaultValueMetadata.Static(13f);
202 Slider.TrackStartingOffsetsrPropertyKey.DefaultValueMetadata = DefaultValueMetadata.Static(
new Vector2(3));
208 void OnApplicationPaused(
object sender,
EventArgs e)
211 var focusedEdit = UIElement.FocusedElement as
EditText;
212 if (focusedEdit != null)
213 focusedEdit.IsSelectionActive =
false;
219 void OnApplicationResumed(
object sender,
EventArgs e)
235 internal Vector3 VirtualResolution
239 return virtualResolution;
244 throw new InvalidOperationException(
"The resolution along the X axis is not valid. [Value=" + value.X +
"]");
246 throw new InvalidOperationException(
"The resolution along the Y axis is not valid. [Value=" + value.Y +
"]");
248 throw new InvalidOperationException(
"The resolution along the Z axis is not valid. [Value=" + value.Z +
"]");
250 if(virtualResolution == value)
253 virtualResolution = value;
255 var uiFieldOfView = (float)Math.Atan2(virtualResolution.Y / 2, virtualResolution.Z + 1f) * 2;
256 uiFrustrumHeight = 2 * (float)Math.Tan(uiFieldOfView / 2);
259 farPlane = 1f + 2 * virtualResolution.Z;
260 var projection = Matrix.PerspectiveFovRH(uiFieldOfView, virtualResolution.X / virtualResolution.Y, nearPlane, farPlane);
261 projection.M22 = -projection.M22;
263 ProjectionMatrix = projection;
264 viewPositionZ = virtualResolution.Z + 1f;
265 ViewMatrix = Matrix.LookAtRH(
new Vector3(0, 0, viewPositionZ), Vector3.Zero, Vector3.UnitY);
266 inverseViewMatrix = Matrix.Invert(ViewMatrix);
267 ViewProjectionInternal = ViewMatrix * ProjectionMatrix;
268 WorldMatrix = Matrix.Translation(-VirtualResolution.X / 2, -VirtualResolution.Y / 2, 0);
269 BackBufferVirtualResolutionRatio = CalculateBackBufferVirtualResolutionRatio();
271 var handler = ResolutionChanged;
279 base.Update(gameTime);
281 if (RootElement == null)
286 using (var profiler = Profiler.Begin(UIProfilerKeys.TouchEventsUpdate))
289 UpdateTouchEvents(gameTime);
294 private void UpdateKeyEvents()
296 foreach (var keyEvent
in input.KeyEvents)
299 var key = keyEvent.Key;
302 UIElement.FocusedElement.RaiseKeyPressedEvent(
new KeyEventArgs { Key = key, Input = input });
306 UIElement.FocusedElement.RaiseKeyReleasedEvent(
new KeyEventArgs { Key = key, Input = input });
310 foreach (var key
in input.KeyDown)
312 if (UIElement.FocusedElement == null || !UIElement.FocusedElement.IsHierarchyEnabled)
return;
313 UIElement.FocusedElement.RaiseKeyDownEvent(
new KeyEventArgs { Key = key, Input = input });
317 private void UpdateMouseOver()
322 var intersectionPoint = Vector3.Zero;
323 var mousePosition = input.MousePosition;
324 var overredElement = lastOveredElement;
327 if(mousePosition != lastMousePosition)
328 overredElement = GetElementFromPointerScreenPosition(mousePosition, ref intersectionPoint);
331 var commonElement = FindCommonParent(overredElement, lastOveredElement);
334 var parent = lastOveredElement;
335 while (parent != commonElement && parent != null)
337 parent.MouseOverState = MouseOverState.MouseOverNone;
338 parent = parent.VisualParent;
342 if(overredElement != null)
345 overredElement.MouseOverState = MouseOverState.MouseOverElement;
348 parent = overredElement.VisualParent;
349 while (parent != null)
351 if (parent.IsHierarchyEnabled)
352 parent.MouseOverState = MouseOverState.MouseOverChild;
354 parent = parent.VisualParent;
359 lastOveredElement = overredElement;
360 lastMousePosition = mousePosition;
363 private void UpdateTouchEvents(
GameTime gameTime)
366 var aggregatedTranslation = Vector2.Zero;
367 for (var index = 0; index < input.PointerEvents.Count; ++index)
369 var pointerEvent = input.PointerEvents[index];
373 aggregatedTranslation = Vector2.Zero;
374 compactedPointerEvents.Add(pointerEvent.Clone());
378 aggregatedTranslation += pointerEvent.DeltaPosition;
380 if (index + 1 >= input.PointerEvents.Count || input.PointerEvents[index + 1].State !=
PointerState.Move)
382 var compactedMoveEvent = pointerEvent.Clone();
383 compactedMoveEvent.DeltaPosition = aggregatedTranslation;
384 compactedPointerEvents.Add(compactedMoveEvent);
388 var intersectionPoint = Vector3.Zero;
389 var lastTouchPosition =
new Vector2(
float.NegativeInfinity);
392 foreach (var pointerEvent
in compactedPointerEvents)
395 if(lastTouchedElement == null && pointerEvent.State !=
PointerState.Down)
398 var time = gameTime.Total;
399 var currentTouchPosition = pointerEvent.Position;
400 var currentTouchedElement = lastTouchedElement;
403 if (lastTouchPosition != currentTouchPosition)
404 currentTouchedElement = GetElementFromPointerScreenPosition(currentTouchPosition, ref intersectionPoint);
407 lastIntersectionPoint = intersectionPoint;
409 var touchEvent =
new TouchEventArgs
411 Action = TouchAction.Down,
413 ScreenPosition = currentTouchPosition,
414 ScreenTranslation = pointerEvent.DeltaPosition,
415 WorldPosition = intersectionPoint,
416 WorldTranslation = intersectionPoint - lastIntersectionPoint
419 switch (pointerEvent.State)
421 case PointerState.Down:
422 touchEvent.Action = TouchAction.Down;
423 if (currentTouchedElement != null)
424 currentTouchedElement.RaiseTouchDownEvent(touchEvent);
427 case PointerState.Up:
428 touchEvent.Action = TouchAction.Up;
431 if (currentTouchedElement != lastTouchedElement)
432 ThrowEnterAndLeaveTouchEvents(currentTouchedElement, lastTouchedElement, touchEvent);
435 if (currentTouchedElement != null)
436 currentTouchedElement.RaiseTouchUpEvent(touchEvent);
439 case PointerState.Move:
440 touchEvent.Action = TouchAction.Move;
443 if (currentTouchedElement != null)
444 currentTouchedElement.RaiseTouchMoveEvent(touchEvent);
447 if (currentTouchedElement != lastTouchedElement)
448 ThrowEnterAndLeaveTouchEvents(currentTouchedElement, lastTouchedElement, touchEvent);
451 case PointerState.Out:
452 case PointerState.Cancel:
453 touchEvent.Action = TouchAction.Move;
456 if (currentTouchedElement != lastTouchedElement)
457 ThrowEnterAndLeaveTouchEvents(currentTouchedElement, lastTouchedElement, touchEvent);
460 var element = currentTouchedElement;
461 while (element != null)
463 if(element.IsTouched)
464 element.RaiseTouchLeaveEvent(touchEvent);
465 element = element.VisualParent;
469 throw new ArgumentOutOfRangeException();
472 lastTouchedElement = currentTouchedElement;
473 lastTouchPosition = currentTouchPosition;
474 lastIntersectionPoint = intersectionPoint;
480 foreach (var pointerEvent
in compactedPointerEvents)
481 PointerEvent.Pool.Enqueue(pointerEvent);
483 compactedPointerEvents.Clear();
486 private UIElement FindCommonParent(UIElement element1, UIElement element2)
489 newlySelectedElementParents.Clear();
490 var newElementParent = element1;
491 while (newElementParent != null)
493 newlySelectedElementParents.Add(newElementParent);
494 newElementParent = newElementParent.VisualParent;
498 var commonElement = element2;
499 while (commonElement != null && !newlySelectedElementParents.Contains(commonElement))
500 commonElement = commonElement.VisualParent;
502 return commonElement;
505 private void ThrowEnterAndLeaveTouchEvents(UIElement currentElement, UIElement previousElement, TouchEventArgs touchEvent)
507 var commonElement = FindCommonParent(currentElement, previousElement);
510 var previousElementParent = previousElement;
511 while (previousElementParent != commonElement && previousElementParent != null)
513 if (previousElementParent.IsHierarchyEnabled && previousElementParent.IsTouched)
515 touchEvent.Handled =
false;
516 previousElementParent.RaiseTouchLeaveEvent(touchEvent);
518 previousElementParent = previousElementParent.VisualParent;
522 var newElementParent = currentElement;
523 while (newElementParent != commonElement && newElementParent != null)
525 if (newElementParent.IsHierarchyEnabled && !newElementParent.IsTouched)
527 touchEvent.Handled =
false;
528 newElementParent.RaiseTouchEnterEvent(touchEvent);
530 newElementParent = newElementParent.VisualParent;
533 private UIElement GetElementFromPointerScreenPosition(
Vector2 position, ref
Vector3 intersectionPoint)
536 var touchPosition = position -
new Vector2(0.5f);
537 var rayDirectionView = Vector3.Normalize(
new Vector3(touchPosition.X * uiFrustrumHeight * virtualResolution.X / virtualResolution.Y, touchPosition.Y * uiFrustrumHeight, -1));
538 var clickRay =
new Ray(inverseViewMatrix.TranslationVector,
Vector3.
TransformNormal(rayDirectionView, inverseViewMatrix));
541 UIElement clickedElement = null;
542 var smallestDepth = float.PositiveInfinity;
543 PerformRecursiveHitTest(rootElement, ref clickRay, ref clickedElement, ref intersectionPoint, ref smallestDepth);
545 return clickedElement;
548 private void PerformRecursiveHitTest(UIElement element, ref
Ray ray, ref UIElement clickedElement, ref
Vector3 intersectionPoint, ref
float smallestDepth)
551 if (!element.IsVisible)
554 if (element.ClipToBounds || element.CanBeHitByUser)
557 var intersect = element.Intersects(ref ray, out intersection);
560 if (element.ClipToBounds && !intersect)
564 var depthInView = (intersection.Z - viewPositionZ);
565 var depthWithBias = (nearPlane * farPlane) / depthInView / (farPlane - nearPlane) - element.DepthBias * BatchBase<int>.DepthBiasShiftOneUnit;
568 if (element.CanBeHitByUser && intersect && depthWithBias < smallestDepth)
570 smallestDepth = depthWithBias;
571 intersectionPoint = intersection;
572 clickedElement = element;
577 foreach (var child
in element.HitableChildren)
578 PerformRecursiveHitTest(child, ref ray, ref clickedElement, ref intersectionPoint, ref smallestDepth);
Provides a base class for all the User Interface elements in Paradox applications.
SiliconStudio.Paradox.Games.Mathematics.Vector2 Vector2
bool IsHierarchyEnabled
Gets the value indicating whether this element and all its upper hierarchy are enabled or not...
Represents a two dimensional mathematical vector.
EventHandler< EventArgs > ResolutionChanged
Contains depth-stencil state for the device.
Interface providing services to deal with the virtual resolution of the game. The virtual resolution ...
Interface for the update of the UIElements.
RenderTarget BackBuffer
Gets the back buffer sets by the current Presenter setup on this device.
A utility class to batch and draw UI images.
Represents a three dimensional mathematical vector.
A service registry is a IServiceProvider that provides methods to register and unregister services...
override void Destroy()
Disposes of object resources.
Performs primitive-based rendering, creates resources, handles system-level variables, adjusts gamma ramp levels, and creates shaders. See The+GraphicsDevice+class to learn more about the class.
Base class for a GameSystemBase component.
static void TransformNormal(ref Vector3 normal, ref Matrix transform, out Vector3 result)
Performs a normal transformation using the given SiliconStudio.Core.Mathematics.Matrix.
override void LoadContent()
Loads the assets.
int Width
Gets the width in texel.
Current timing used for variable-step (real time) or fixed-step (game time) games.
Represents a three dimensional line based on a point in space and a direction.
Interface of the UI system.
int Height
Gets the height in texel.
override void Update(GameTime gameTime)
This method is called when this game component is updated.
Describes a depth stencil state.
SiliconStudio.Core.Mathematics.Vector3 Vector3
Class holding all the data required to define an UI image.
override void Initialize()
This method is called when the component is added to the game.
SiliconStudio.Core.Mathematics.RectangleF RectangleF
UISystem(IServiceRegistry registry)
Represent an edit text where the user can enter text.
Represents a 4x4 mathematical matrix.