Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
ScrollingText.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 using System;
4 using System.Diagnostics;
5 
6 using SiliconStudio.Core;
7 using SiliconStudio.Core.Mathematics;
8 using SiliconStudio.Paradox.Games;
9 
10 namespace SiliconStudio.Paradox.UI.Controls
11 {
12  /// <summary>
13  /// A text viewer that scrolls automatically the text from right to left.
14  /// </summary>
15  [DebuggerDisplay("ScrollingText - Name={Name}")]
16  public class ScrollingText : TextBlock
17  {
18  /// <summary>
19  /// The key to the ScrollingSpeed dependency property.
20  /// </summary>
21  protected readonly static PropertyKey<float> ScrollingSpeedPropertyKey = new PropertyKey<float>("ScrollingSpeedKey", typeof(ScrollingText), DefaultValueMetadata.Static(40f), ValidateValueMetadata.New<float>(ValidateScrollingSpeedCallback));
22 
23  /// <summary>
24  /// The key to the TextWrapped dependency property.
25  /// </summary>
26  protected readonly static PropertyKey<bool> RepeatTextPropertyKey = new PropertyKey<bool>("TextWrappedKey", typeof(ScrollingText), DefaultValueMetadata.Static(true), ObjectInvalidationMetadata.New<bool>(RepeatTextInvalidationCallback));
27 
28  /// <summary>
29  /// The key to the DesiredCharacterNumber dependency property.
30  /// </summary>
31  protected readonly static PropertyKey<uint> DesiredCharacterNumberPropertyKey = new PropertyKey<uint>("DesiredCharacterNumberKey", typeof(ScrollingText), DefaultValueMetadata.Static((uint)10), ObjectInvalidationMetadata.New<uint>(InvalidateCharacterNumber));
32 
33  private static void InvalidateCharacterNumber(object propertyOwner, PropertyKey<uint> propertyKey, uint propertyOldValue)
34  {
35  var element = (UIElement)propertyOwner;
36  element.InvalidateMeasure();
37  }
38 
39  private string textToDisplay = "";
40 
41  private float elementWidth;
42 
43  /// <summary>
44  /// The index in <see cref="Controls.TextBlock.Text"/> defining the position of the next letter to add to <see cref="TextToDisplay"/>.
45  /// </summary>
46  private int nextLetterIndex;
47 
48  private bool textHasBeenAppended;
49 
50  /// <summary>
51  /// The current offset of the text in the Ox axis.
52  /// </summary>
53  public float ScrollingOffset { get; private set; }
54 
55  /// <summary>
56  /// The total accumulated width of the scrolling text since the last call the <see cref="ResetDisplayingText"/>
57  /// </summary>
58  public float AccumulatedWidth { get; private set; }
59 
60  public ScrollingText()
61  {
62  ResetDisplayingText();
63  SnapText = false;
64  DrawLayerNumber += 3; // (1: clipping border, 2: Text, 3: clipping border undraw)
65  }
66 
67  /// <summary>
68  /// Gets or sets the scrolling speed of the text. The unit is in virtual pixels.
69  /// </summary>
70  /// <exception cref="ArgumentOutOfRangeException">The provided speed must be positive or null.</exception>
71  public float ScrollingSpeed
72  {
73  get { return DependencyProperties.Get(ScrollingSpeedPropertyKey); }
74  set { DependencyProperties.Set(ScrollingSpeedPropertyKey, value); }
75  }
76 
77  /// <summary>
78  /// Gets or sets the desired number of character in average to display at a given time. This value is taken in account during the measurement stage of the element.
79  /// </summary>
80  public uint DesiredCharacterNumber
81  {
82  get { return DependencyProperties.Get(DesiredCharacterNumberPropertyKey); }
83  set { DependencyProperties.Set(DesiredCharacterNumberPropertyKey, value); }
84  }
85 
86  /// <summary>
87  /// Gets or sets the a value indicating if the text message must be repeated (wrapped) or not.
88  /// </summary>
89  public bool RepeatText
90  {
91  get { return DependencyProperties.Get(RepeatTextPropertyKey); }
92  set { DependencyProperties.Set(RepeatTextPropertyKey, value); }
93  }
94 
95  private static void ValidateScrollingSpeedCallback(ref float value)
96  {
97  if (value < 0)
98  throw new ArgumentOutOfRangeException("value");
99  }
100 
101  private static void RepeatTextInvalidationCallback(object propertyOwner, PropertyKey<bool> propertyKey, bool propertyOldValue)
102  {
103  var scrollingText = (ScrollingText)propertyOwner;
104  scrollingText.ResetDisplayingText();
105  }
106 
107  /// <summary>
108  /// Append the provided text to the end of the current <see cref="TextBlock.Text"/> without restarting the display to the begin of the <see cref="TextBlock.Text"/>.
109  /// </summary>
110  /// <param name="text">The text to append</param>
111  public void AppendText(string text)
112  {
113  if (text == null) throw new ArgumentNullException("text");
114 
115  textHasBeenAppended = true;
116  Text += text;
117  }
118 
119  /// <summary>
120  /// Clear the currently scrolling text.
121  /// </summary>
122  public void ClearText()
123  {
124  Text = "";
125  }
126 
127  protected override void OnTextChanged()
128  {
129  if (!textHasBeenAppended) // Text has been modified by the user -> reset scrolling offsets
130  ResetDisplayingText();
131 
132  textHasBeenAppended = false;
133  }
134 
135  private void ResetDisplayingText()
136  {
137  textToDisplay = "";
138  nextLetterIndex = 0;
139  ScrollingOffset = IsArrangeValid? ActualWidth: float.PositiveInfinity;
140  AccumulatedWidth = 0;
141  }
142 
143  public override string TextToDisplay
144  {
145  get { return textToDisplay; }
146  }
147 
148  /// <summary>
149  /// Calculate the width of the text to display in virtual pixels size.
150  /// </summary>
151  /// <returns>The size of the text in virtual pixels</returns>
152  private float CalculateTextToDisplayWidth()
153  {
154  return CalculateTextSize(TextToDisplay).X;
155  }
156 
157  protected override void Update(GameTime time)
158  {
159  base.Update(time);
160 
161  UpdateAndAdjustDisplayText(time);
162  }
163 
164  private void UpdateAndAdjustDisplayText(GameTime time = null)
165  {
166  if (string.IsNullOrEmpty(Text))
167  return;
168 
169  var elapsedSeconds = time != null ? (float)time.Elapsed.TotalSeconds : 0f;
170 
171  // calculate the shift offset
172  var nextOffsetShift = elapsedSeconds * ScrollingSpeed - ScrollingOffset;
173 
174  // calculate the size of the next TextToDisplay required
175  var sizeNextTextToDisplay = nextOffsetShift + elementWidth;
176 
177  // append characters to TextToDisplay so that it measures more than 'sizeNextTextToDisplay'
178  var textToDisplayWidth = CalculateTextToDisplayWidth();
179  while (textToDisplayWidth < sizeNextTextToDisplay && nextLetterIndex < Text.Length)
180  {
181  textToDisplay += Text[nextLetterIndex++];
182 
183  var addedCharacterWidth = CalculateTextToDisplayWidth() - textToDisplayWidth;
184  AccumulatedWidth += addedCharacterWidth;
185 
186  if (RepeatText && nextLetterIndex >= Text.Length)
187  nextLetterIndex = 0;
188 
189  textToDisplayWidth += addedCharacterWidth;
190  }
191 
192  // Check if all the string has finished to scroll, if clear the message
193  if (CalculateTextSize(textToDisplay).X < nextOffsetShift)
194  textToDisplay = "";
195 
196  // remove characters at the beginning of TextToDisplay as long as possible
197  var fontSize = new Vector2(TextSize, TextSize);
198  while (textToDisplay.Length > 1 && Font.MeasureString(textToDisplay, ref fontSize, 1).X < nextOffsetShift)
199  {
200  nextOffsetShift -= Font.MeasureString(textToDisplay, ref fontSize, 1).X;
201  textToDisplay = textToDisplay.Substring(1);
202  }
203 
204  // Update the scroll offset of the viewer
205  ScrollingOffset = -nextOffsetShift;
206  }
207 
208  protected override Vector3 MeasureOverride(Vector3 availableSizeWithoutMargins)
209  {
210  return MeasureSize();
211  }
212 
213  protected override Vector3 ArrangeOverride(Vector3 finalSizeWithoutMargins)
214  {
215  elementWidth = finalSizeWithoutMargins.X;
216 
217  ScrollingOffset = Math.Min(elementWidth, ScrollingOffset);
218 
219  UpdateAndAdjustDisplayText();
220 
221  return base.ArrangeOverride(finalSizeWithoutMargins);
222  }
223 
224  /// <summary>
225  /// Measure the size of the <see cref="ScrollingText"/> element.
226  /// </summary>
227  /// <returns>The size of the element</returns>
229  {
230  if (Font == null)
231  return Vector3.Zero;
232 
233  return new Vector3(Font.MeasureString(new string('A', (int)DesiredCharacterNumber)), 0);
234  }
235  }
236 }
Provides a base class for all the User Interface elements in Paradox applications.
Definition: UIElement.cs:21
SiliconStudio.Paradox.Games.Mathematics.Vector2 Vector2
void ClearText()
Clear the currently scrolling text.
Represents a three dimensional mathematical vector.
Definition: Vector3.cs:42
Vector3 MeasureSize()
Measure the size of the ScrollingText element.
override void Update(GameTime time)
Method called by IUIElementUpdate.Update. This method can be overridden by inherited classes to perfo...
SharpDX.DirectWrite.Font Font
override Vector3 ArrangeOverride(Vector3 finalSizeWithoutMargins)
When overridden in a derived class, positions possible child elements and determines a size for a UIE...
Current timing used for variable-step (real time) or fixed-step (game time) games.
Definition: GameTime.cs:31
override void OnTextChanged()
Method triggered when the Text changes. Can be overridden in inherited class to changed the default b...
void AppendText(string text)
Append the provided text to the end of the current TextBlock.Text without restarting the display to t...
A text viewer that scrolls automatically the text from right to left.
override Vector3 MeasureOverride(Vector3 availableSizeWithoutMargins)
When overridden in a derived class, measures the size in layout required for possible child elements ...
SiliconStudio.Core.Mathematics.Vector3 Vector3
Provides a lightweight control for displaying small amounts of text.
Definition: TextBlock.cs:18
A class that represents a tag propety.
Definition: PropertyKey.cs:17