EAR: Actions now support multiple iterations.
2 // Copyright (C) 2014-2015 Stéphane Lenclud.
4 // This file is part of SharpDisplayManager.
6 // SharpDisplayManager is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
11 // SharpDisplayManager is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with SharpDisplayManager. If not, see <http://www.gnu.org/licenses/>.
21 using System.Collections.Generic;
22 using System.ComponentModel;
23 using System.Diagnostics;
26 using System.Threading.Tasks;
27 //using System.Timers;
28 using System.Windows.Forms;
31 namespace SharpDisplayManager
33 [System.ComponentModel.DesignerCategory("Code")]
34 public class MarqueeLabel : Label
36 private bool iOwnTimer;
37 private StringFormat iStringFormat;
38 private SolidBrush iBrush;
39 private SizeF iTextSize;
40 private SizeF iSeparatorSize;
41 private SizeF iScrollSize;
42 private Font iFontInUse;
43 private string iSeparator;
45 [Category("Appearance")]
46 [Description("Separator in our scrolling loop.")]
48 [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
49 [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
50 public string Separator
58 if (value != Separator)
61 OnTextChanged(EventArgs.Empty);
66 [Category("Behavior")]
67 [Description("How fast is our text scrolling, in pixels per second.")]
69 [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
70 [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
71 public int PixelsPerSecond { get; set; }
73 [Category("Behavior")]
74 [Description("Should we scale down our font to try fit our text without scrolling.")]
76 [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
77 [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
78 public bool ScaleToFit { get; set; }
80 [Category("Behavior")]
81 [Description("Minimum size of our font allowed when scaling is enabled.")]
83 [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
84 [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
85 public float MinFontSize { get; set; }
87 [Category("Behavior")]
88 [Description("Use an internal or an external timer.")]
90 [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
91 [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
106 Timer.Tick += new EventHandler(Timer_Tick);
119 private int CurrentPosition { get; set; }
120 private Timer Timer { get; set; }
121 private DateTime LastTickTime { get; set; }
122 private double PixelsLeft { get; set; }
123 //DateTime a = new DateTime(2010, 05, 12, 13, 15, 00);
124 //DateTime b = new DateTime(2010, 05, 12, 13, 45, 00);
125 //Trace.WriteLine(b.Subtract(a).TotalMinutes);
127 public MarqueeLabel()
129 UseCompatibleTextRendering = true;
130 //PixelsPerSecond = 32;
131 LastTickTime = DateTime.Now;
134 iBrush = new SolidBrush(ForeColor);
137 //Just clone our font
138 iFontInUse = new Font(Font, Font.Style);
140 //Following is needed if we ever switch from Label to Control base class.
141 //Without it you get some pretty nasty flicker
142 //SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
143 //SetStyle(ControlStyles.UserPaint, true);
144 //SetStyle(ControlStyles.AllPaintingInWmPaint, true);
145 //SetStyle(ControlStyles.DoubleBuffer, true);
148 public void UpdateAnimation(DateTime aLastTickTime, DateTime aNewTickTime)
157 while (CurrentPosition > (iTextSize.Width + iSeparatorSize.Width))
159 CurrentPosition -= ((int)(iTextSize.Width + iSeparatorSize.Width));
163 while (CurrentPosition > iScrollSize.Width)
165 CurrentPosition -= (int)iScrollSize.Width;
169 PixelsLeft += aNewTickTime.Subtract(aLastTickTime).TotalSeconds * PixelsPerSecond;
171 //Keep track of our pixels left over
172 //PixelsLeft = offset - Math.Truncate(offset);
173 double offset = Math.Truncate(PixelsLeft);
174 PixelsLeft -= offset;
176 CurrentPosition += Convert.ToInt32(offset);
181 BackColor = Color.Red;
183 else if (offset==1.0)
185 if (BackColor != Color.White)
187 BackColor = Color.White;
194 //BackColor = Color.Green;
197 //Only redraw if something has changed
204 void Timer_Tick(object sender, EventArgs e)
206 DateTime NewTickTime = DateTime.Now;
208 UpdateAnimation(LastTickTime, NewTickTime);
210 LastTickTime = NewTickTime;
213 private StringFormat GetStringFormatFromContentAllignment(ContentAlignment ca)
215 StringFormat format = new StringFormat(StringFormat.GenericTypographic);
218 case ContentAlignment.TopCenter:
219 format.Alignment = StringAlignment.Center;
220 format.LineAlignment = StringAlignment.Near;
222 case ContentAlignment.TopLeft:
223 format.Alignment = StringAlignment.Near;
224 format.LineAlignment = StringAlignment.Near;
226 case ContentAlignment.TopRight:
227 format.Alignment = StringAlignment.Far;
228 format.LineAlignment = StringAlignment.Near;
230 case ContentAlignment.MiddleCenter:
231 format.Alignment = StringAlignment.Center;
232 format.LineAlignment = StringAlignment.Center;
234 case ContentAlignment.MiddleLeft:
235 format.Alignment = StringAlignment.Near;
236 format.LineAlignment = StringAlignment.Center;
238 case ContentAlignment.MiddleRight:
239 format.Alignment = StringAlignment.Far;
240 format.LineAlignment = StringAlignment.Center;
242 case ContentAlignment.BottomCenter:
243 format.Alignment = StringAlignment.Center;
244 format.LineAlignment = StringAlignment.Far;
246 case ContentAlignment.BottomLeft:
247 format.Alignment = StringAlignment.Near;
248 format.LineAlignment = StringAlignment.Far;
250 case ContentAlignment.BottomRight:
251 format.Alignment = StringAlignment.Far;
252 format.LineAlignment = StringAlignment.Far;
256 format.FormatFlags |= StringFormatFlags.NoWrap;
257 format.FormatFlags |= StringFormatFlags.NoClip;
258 format.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces;
259 format.Trimming = StringTrimming.None;
264 protected override void OnForeColorChanged(EventArgs e)
266 //Color has changed recreate our brush
267 iBrush = new SolidBrush(ForeColor);
269 base.OnForeColorChanged(e);
273 private void ComputeSizes()
275 //For all string measurements and drawing issues refer to the following article:
276 // http://stackoverflow.com/questions/1203087/why-is-graphics-measurestring-returning-a-higher-than-expected-number
277 //Update text size according to text and font
278 Graphics g = this.CreateGraphics();
279 g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
280 iStringFormat = GetStringFormatFromContentAllignment(TextAlign);
281 iTextSize = g.MeasureString(Text, iFontInUse, Int32.MaxValue, iStringFormat);
282 iSeparatorSize = g.MeasureString(Separator, iFontInUse, Int32.MaxValue, iStringFormat);
283 //Scroll width is the width of our text and our separator without taking kerning into account since
284 //both text and separator are drawn independently from each other.
285 iScrollSize.Width = iSeparatorSize.Width + iTextSize.Width;
286 iScrollSize.Height = Math.Max(iSeparatorSize.Height, iTextSize.Height); //Not relevant for now
287 //We don't want scroll width to take kerning into account so we don't use the following
288 //iScrollSize = g.MeasureString(Text + Separator, Font, Int32.MaxValue, iStringFormat);
291 private void HandleTextSizeChange()
297 if (ScaleToFit && iFontInUse.SizeInPoints > MinFontSize)
300 iFontInUse = new Font(Font.FontFamily, iFontInUse.SizeInPoints - 1, Font.Style);
301 //Recurse until we are done
302 HandleTextSizeChange();
308 //Our minimum font size still needs scrolling
309 //Reset our font then
310 iFontInUse = new Font(Font,Font.Style);
314 //Scrolling is ok or needed
315 //Always align left when scrolling
316 iStringFormat.Alignment = StringAlignment.Near;
320 //Reset our timer whenever our text changes
323 LastTickTime = DateTime.Now;
326 protected override void OnTextChanged(EventArgs e)
328 //Just clone our font
329 iFontInUse = new Font(Font, Font.Style);
331 HandleTextSizeChange();
333 base.OnTextChanged(e);
336 protected override void OnFontChanged(EventArgs e)
338 //Just clone our font
339 iFontInUse = new Font(Font,Font.Style);
341 HandleTextSizeChange();
343 base.OnFontChanged(e);
346 protected override void OnSizeChanged(EventArgs e)
348 //Just clone our font
349 iFontInUse = new Font(Font, Font.Style);
351 HandleTextSizeChange();
353 base.OnSizeChanged(e);
356 protected override void OnTextAlignChanged(EventArgs e)
358 iStringFormat = GetStringFormatFromContentAllignment(TextAlign);
361 //Always align left when scrolling to avoid bugs
362 iStringFormat.Alignment = StringAlignment.Near;
367 base.OnTextAlignChanged(e);
370 protected override void OnPaint(PaintEventArgs e)
372 //Disable anti-aliasing
373 e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
376 //Draw it all in a single call would take kerning into account
377 //e.Graphics.TranslateTransform(-(float)CurrentPosition, 0);
378 //e.Graphics.DrawString(Text + Separator + Text, Font, iBrush, ClientRectangle, StringFormat);
380 //Doing separate draw operation allows us not to take kerning into account between separator and string
382 e.Graphics.TranslateTransform(-(float)CurrentPosition, 0);
383 e.Graphics.DrawString(Text, iFontInUse, iBrush, ClientRectangle, iStringFormat);
385 e.Graphics.TranslateTransform(iTextSize.Width, 0);
386 e.Graphics.DrawString(Separator, iFontInUse, iBrush, ClientRectangle, iStringFormat);
388 e.Graphics.TranslateTransform(iSeparatorSize.Width, 0);
389 e.Graphics.DrawString(Text, iFontInUse, iBrush, ClientRectangle, iStringFormat);
393 e.Graphics.DrawString(Text, iFontInUse, iBrush, ClientRectangle, iStringFormat);
398 //DrawText is not working without anti-aliasing. See: stackoverflow.com/questions/8283631/graphics-drawstring-vs-textrenderer-drawtextwhich-can-deliver-better-quality
399 //TextRenderer.DrawText(e.Graphics, Text, Font, ClientRectangle, ForeColor, BackColor, iTextFormatFlags);
404 public bool NeedToScroll()
406 //if (Width < e.Graphics.MeasureString(Text, Font).Width)
407 if (Width < iTextSize.Width)
414 protected override void Dispose(bool disposing)