Server/MarqueeLabel.cs
author StephaneLenclud
Wed, 04 Feb 2015 17:44:25 +0100
changeset 104 189aac7dd3d6
parent 42 a9048f350975
child 110 31e63bd07dfa
permissions -rw-r--r--
Adding events for display closed/opened.
Display types now loaded dynamically from our MiniDisplay library.
Adding editor config.
Tested against Futaba GP1212A02.
     1 using System;
     2 using System.Collections.Generic;
     3 using System.ComponentModel;
     4 using System.Diagnostics;
     5 using System.Linq;
     6 using System.Text;
     7 using System.Threading.Tasks;
     8 //using System.Timers;
     9 using System.Windows.Forms;
    10 using System.Drawing;
    11 
    12 namespace SharpDisplayManager
    13 {
    14     [System.ComponentModel.DesignerCategory("Code")]
    15     public class MarqueeLabel : Label
    16     {
    17         private bool iOwnTimer;
    18         private StringFormat iStringFormat;
    19         private SolidBrush iBrush;
    20         private SizeF iTextSize;
    21         private SizeF iSeparatorSize;
    22         private SizeF iScrollSize;
    23         private Font iFontInUse;
    24 
    25         [Category("Appearance")]
    26         [Description("Separator in our scrolling loop.")]
    27         [DefaultValue(" | ")]
    28         [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
    29         [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    30         public string Separator { get; set; }
    31 
    32         [Category("Behavior")]
    33         [Description("How fast is our text scrolling, in pixels per second.")]
    34         [DefaultValue(32)]
    35         [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
    36         [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    37         public int PixelsPerSecond { get; set; }
    38 
    39         [Category("Behavior")]
    40         [Description("Should we scale down our font to try fit our text without scrolling.")]
    41         [DefaultValue(false)]
    42         [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
    43         [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    44         public bool ScaleToFit { get; set; }
    45 
    46         [Category("Behavior")]
    47         [Description("Minimum size of our font allowed when scaling is enabled.")]
    48         [DefaultValue(15)]
    49         [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
    50         [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    51         public float MinFontSize { get; set; }
    52 
    53         [Category("Behavior")]
    54         [Description("Use an internal or an external timer.")]
    55         [DefaultValue(true)]
    56         [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
    57         [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    58         public bool OwnTimer
    59         {
    60             get
    61             {
    62                 return iOwnTimer;
    63             }
    64             set
    65             {
    66                 iOwnTimer = value;
    67 
    68                 if (iOwnTimer)
    69                 {
    70                     Timer = new Timer();
    71                     Timer.Interval = 10;
    72                     Timer.Tick += new EventHandler(Timer_Tick);
    73                     Timer.Start();
    74                 }
    75                 else
    76                 {
    77                     if (Timer != null)
    78                         Timer.Dispose();
    79                     Timer = null;
    80                 }
    81 
    82             }
    83         }
    84 
    85         private int CurrentPosition { get; set; }
    86         private Timer Timer { get; set; }
    87         private DateTime LastTickTime { get; set; }
    88         private double PixelsLeft { get; set; }
    89         //DateTime a = new DateTime(2010, 05, 12, 13, 15, 00);
    90         //DateTime b = new DateTime(2010, 05, 12, 13, 45, 00);
    91         //Console.WriteLine(b.Subtract(a).TotalMinutes);
    92 
    93         public MarqueeLabel()
    94         {
    95             UseCompatibleTextRendering = true;
    96             //PixelsPerSecond = 32;
    97             LastTickTime = DateTime.Now;
    98             PixelsLeft = 0;
    99             CurrentPosition = 0;
   100             iBrush = new SolidBrush(ForeColor);
   101             MinFontSize = 15;
   102             ScaleToFit = true;
   103             //Just clone our font
   104             iFontInUse = new Font(Font, Font.Style);
   105 
   106             //Following is needed if we ever switch from Label to Control base class.
   107             //Without it you get some pretty nasty flicker
   108             //SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
   109             //SetStyle(ControlStyles.UserPaint, true);
   110             //SetStyle(ControlStyles.AllPaintingInWmPaint, true);
   111             //SetStyle(ControlStyles.DoubleBuffer, true);
   112         }
   113 
   114         public void UpdateAnimation(DateTime aLastTickTime, DateTime aNewTickTime)
   115         {
   116             if (!NeedToScroll())
   117             {
   118                 CurrentPosition = 0;
   119                 return;
   120             }
   121 
   122             /*
   123             while (CurrentPosition > (iTextSize.Width + iSeparatorSize.Width))
   124             {
   125                 CurrentPosition -= ((int)(iTextSize.Width + iSeparatorSize.Width));
   126             }
   127              */
   128 
   129             while (CurrentPosition > iScrollSize.Width)
   130             {
   131                 CurrentPosition -= (int)iScrollSize.Width;
   132             }
   133 
   134 
   135             PixelsLeft += aNewTickTime.Subtract(aLastTickTime).TotalSeconds * PixelsPerSecond;
   136 
   137             //Keep track of our pixels left over
   138             //PixelsLeft = offset - Math.Truncate(offset);
   139             double offset = Math.Truncate(PixelsLeft);
   140             PixelsLeft -= offset;
   141 
   142             CurrentPosition += Convert.ToInt32(offset);
   143 
   144             /*
   145             if (offset > 1.0)
   146             {
   147                 BackColor = Color.Red;
   148             }
   149             else if (offset==1.0)
   150             {
   151                 if (BackColor != Color.White)
   152                 {
   153                     BackColor = Color.White;
   154                 }
   155 
   156             }
   157             else
   158             {
   159                 //Too slow
   160                 //BackColor = Color.Green;
   161             }*/
   162 
   163             //Only redraw if something has changed
   164             if (offset != 0)
   165             {
   166                 Invalidate();
   167             }
   168         }
   169 
   170         void Timer_Tick(object sender, EventArgs e)
   171         {
   172             DateTime NewTickTime = DateTime.Now;
   173             //
   174             UpdateAnimation(LastTickTime, NewTickTime);
   175             //
   176             LastTickTime = NewTickTime;
   177         }
   178 
   179         private StringFormat GetStringFormatFromContentAllignment(ContentAlignment ca)
   180         {
   181             StringFormat format = new StringFormat(StringFormat.GenericTypographic);
   182             switch (ca)
   183             {
   184                 case ContentAlignment.TopCenter:
   185                     format.Alignment = StringAlignment.Center;
   186                     format.LineAlignment = StringAlignment.Near;
   187                     break;
   188                 case ContentAlignment.TopLeft:
   189                     format.Alignment = StringAlignment.Near;
   190                     format.LineAlignment = StringAlignment.Near;
   191                     break;
   192                 case ContentAlignment.TopRight:
   193                     format.Alignment = StringAlignment.Far;
   194                     format.LineAlignment = StringAlignment.Near;
   195                     break;
   196                 case ContentAlignment.MiddleCenter:
   197                     format.Alignment = StringAlignment.Center;
   198                     format.LineAlignment = StringAlignment.Center;
   199                     break;
   200                 case ContentAlignment.MiddleLeft:
   201                     format.Alignment = StringAlignment.Near;
   202                     format.LineAlignment = StringAlignment.Center;
   203                     break;
   204                 case ContentAlignment.MiddleRight:
   205                     format.Alignment = StringAlignment.Far;
   206                     format.LineAlignment = StringAlignment.Center;
   207                     break;
   208                 case ContentAlignment.BottomCenter:
   209                     format.Alignment = StringAlignment.Center;
   210                     format.LineAlignment = StringAlignment.Far;
   211                     break;
   212                 case ContentAlignment.BottomLeft:
   213                     format.Alignment = StringAlignment.Near;
   214                     format.LineAlignment = StringAlignment.Far;
   215                     break;
   216                 case ContentAlignment.BottomRight:
   217                     format.Alignment = StringAlignment.Far;
   218                     format.LineAlignment = StringAlignment.Far;
   219                     break;
   220             }
   221 
   222             format.FormatFlags |= StringFormatFlags.NoWrap;
   223             format.FormatFlags |= StringFormatFlags.NoClip;
   224             format.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces;
   225             format.Trimming = StringTrimming.None;
   226 
   227             return format;
   228         }
   229 
   230         protected override void OnForeColorChanged(EventArgs e)
   231         {
   232             //Color has changed recreate our brush
   233             iBrush = new SolidBrush(ForeColor);
   234 
   235             base.OnForeColorChanged(e);
   236         }
   237 
   238 
   239         private void ComputeSizes()
   240         {
   241             //For all string measurements and drawing issues refer to the following article:
   242             // http://stackoverflow.com/questions/1203087/why-is-graphics-measurestring-returning-a-higher-than-expected-number
   243             //Update text size according to text and font
   244             Graphics g = this.CreateGraphics();
   245             g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
   246             iStringFormat = GetStringFormatFromContentAllignment(TextAlign);
   247             iTextSize = g.MeasureString(Text, iFontInUse, Int32.MaxValue, iStringFormat);
   248             iSeparatorSize = g.MeasureString(Separator, iFontInUse, Int32.MaxValue, iStringFormat);
   249             //Scroll width is the width of our text and our separator without taking kerning into account since
   250             //both text and separator are drawn independently from each other.
   251             iScrollSize.Width = iSeparatorSize.Width + iTextSize.Width;
   252             iScrollSize.Height = Math.Max(iSeparatorSize.Height, iTextSize.Height); //Not relevant for now
   253             //We don't want scroll width to take kerning into account so we don't use the following
   254             //iScrollSize = g.MeasureString(Text + Separator, Font, Int32.MaxValue, iStringFormat);
   255         }
   256 
   257         private void HandleTextSizeChange()
   258         {
   259             ComputeSizes();
   260 
   261             if (NeedToScroll())
   262             {
   263                 if (ScaleToFit && iFontInUse.SizeInPoints > MinFontSize)
   264                 {
   265                     //Try scaling down
   266                     iFontInUse = new Font(Font.FontFamily, iFontInUse.SizeInPoints - 1, Font.Style);
   267                     //Recurse until we are done
   268                     HandleTextSizeChange();
   269                 }
   270                 else
   271                 {
   272                     if (ScaleToFit)
   273                     {
   274                         //Our minimum font size still needs scrolling
   275                         //Reset our font then
   276                         iFontInUse = new Font(Font,Font.Style);
   277                         ComputeSizes();
   278                     }
   279 
   280                     //Scrolling is ok or needed
   281                     //Always align left when scrolling
   282                     iStringFormat.Alignment = StringAlignment.Near;
   283                 }
   284             }
   285 
   286             //Reset our timer whenever our text changes
   287             CurrentPosition = 0;
   288             PixelsLeft = 0;
   289             LastTickTime = DateTime.Now;
   290         }
   291 
   292         protected override void OnTextChanged(EventArgs e)
   293         {
   294             //Just clone our font
   295             iFontInUse = new Font(Font, Font.Style);
   296 
   297             HandleTextSizeChange();
   298 
   299             base.OnTextChanged(e);
   300         }
   301 
   302         protected override void OnFontChanged(EventArgs e)
   303         {
   304             //Just clone our font
   305             iFontInUse = new Font(Font,Font.Style);
   306 
   307             HandleTextSizeChange();
   308 
   309             base.OnFontChanged(e);
   310         }
   311 
   312         protected override void OnSizeChanged(EventArgs e)
   313         {
   314             //Just clone our font
   315             iFontInUse = new Font(Font, Font.Style);
   316 
   317             HandleTextSizeChange();
   318 
   319             base.OnSizeChanged(e);
   320         }
   321 
   322         protected override void OnTextAlignChanged(EventArgs e)
   323         {
   324             iStringFormat = GetStringFormatFromContentAllignment(TextAlign);
   325             if (NeedToScroll())
   326             {
   327                 //Always align left when scrolling to avoid bugs
   328                 iStringFormat.Alignment = StringAlignment.Near;
   329             }
   330 
   331             Invalidate();
   332             //
   333             base.OnTextAlignChanged(e);
   334         }
   335 
   336         protected override void OnPaint(PaintEventArgs e)
   337         {
   338             //Disable anti-aliasing
   339             e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
   340             if (NeedToScroll())
   341             {
   342                 //Draw it all in a single call would take kerning into account
   343                 //e.Graphics.TranslateTransform(-(float)CurrentPosition, 0);
   344                 //e.Graphics.DrawString(Text + Separator + Text, Font, iBrush, ClientRectangle, StringFormat);
   345 
   346                 //Doing separate draw operation allows us not to take kerning into account between separator and string
   347                 //Draw the first one
   348                 e.Graphics.TranslateTransform(-(float)CurrentPosition, 0);
   349                 e.Graphics.DrawString(Text, iFontInUse, iBrush, ClientRectangle, iStringFormat);
   350                 //Draw separator
   351                 e.Graphics.TranslateTransform(iTextSize.Width, 0);
   352                 e.Graphics.DrawString(Separator, iFontInUse, iBrush, ClientRectangle, iStringFormat);
   353                 //Draw the last one
   354                 e.Graphics.TranslateTransform(iSeparatorSize.Width, 0);
   355                 e.Graphics.DrawString(Text, iFontInUse, iBrush, ClientRectangle, iStringFormat);
   356             }
   357             else
   358             {
   359                 e.Graphics.DrawString(Text, iFontInUse, iBrush, ClientRectangle, iStringFormat);
   360             }
   361 
   362 
   363 
   364             //DrawText is not working without anti-aliasing. See: stackoverflow.com/questions/8283631/graphics-drawstring-vs-textrenderer-drawtextwhich-can-deliver-better-quality
   365             //TextRenderer.DrawText(e.Graphics, Text, Font, ClientRectangle, ForeColor, BackColor, iTextFormatFlags);
   366 
   367             //base.OnPaint(e);
   368         }
   369 
   370         public bool NeedToScroll()
   371         {
   372             //if (Width < e.Graphics.MeasureString(Text, Font).Width)
   373             if (Width < iTextSize.Width)
   374             {
   375                 return true;
   376             }
   377             return false;
   378         }
   379 
   380         protected override void Dispose(bool disposing)
   381         {
   382             if (disposing)
   383             {
   384                 if (Timer != null)
   385                     Timer.Dispose();
   386             }
   387             Timer = null;
   388         }
   389     }
   390 }