From 5f1063a5f22c3de9fa61ed78d271690793481d1c Mon Sep 17 00:00:00 2001 From: Curtis Wensley Date: Sat, 15 Nov 2014 22:44:13 -0800 Subject: [PATCH] WinForms: Turn off CompatibleTextRendering by default, and use FontRenderer for Label, DropDown, and DateTimePicker to make fonts consistent. Fixes #246. Add Eto.WinForms.Drawing.GraphicsHandler.UseCompatibleTextRendering to optionally use better fonts for Graphics, but it is on by default to support translation --- .../CustomControls/ExtendedDateTimePicker.cs | 123 ++++----- .../Eto.WinForms/Drawing/GraphicsHandler.cs | 237 ++++++++++-------- .../Eto.WinForms/Forms/ApplicationHandler.cs | 1 + .../Forms/Controls/DropDownHandler.cs | 18 +- .../Forms/Controls/LabelHandler.cs | 110 +++----- .../Forms/Controls/ListBoxHandler.cs | 32 ++- Source/Eto.WinForms/Forms/FormHandler.cs | 6 +- 7 files changed, 248 insertions(+), 279 deletions(-) diff --git a/Source/Eto.WinForms/CustomControls/ExtendedDateTimePicker.cs b/Source/Eto.WinForms/CustomControls/ExtendedDateTimePicker.cs index 0b8168e938..7746535464 100644 --- a/Source/Eto.WinForms/CustomControls/ExtendedDateTimePicker.cs +++ b/Source/Eto.WinForms/CustomControls/ExtendedDateTimePicker.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; using swf = System.Windows.Forms; using sd = System.Drawing; using System.Reflection; @@ -19,39 +18,29 @@ namespace Eto.WinForms.CustomControls /// public class ExtendedDateTimePicker : swf.DateTimePicker { - static sd.Image img = sd.Image.FromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream("Eto.WinForms.CustomControls.CalendarPicker.png")); + static readonly sd.Image img = sd.Image.FromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream("Eto.WinForms.CustomControls.CalendarPicker.png")); int selectedSegment = -1; - static sd.StringFormat stringFormat; - - static ExtendedDateTimePicker() - { - stringFormat = new sd.StringFormat(sd.StringFormat.GenericTypographic); - stringFormat.FormatFlags |= - sd.StringFormatFlags.MeasureTrailingSpaces - | sd.StringFormatFlags.NoWrap - | sd.StringFormatFlags.NoClip; - stringFormat.Alignment = sd.StringAlignment.Far; - stringFormat.LineAlignment = sd.StringAlignment.Center; - } + const swf.TextFormatFlags RenderTextFormat = swf.TextFormatFlags.SingleLine + | swf.TextFormatFlags.NoPrefix + | swf.TextFormatFlags.TextBoxControl + | swf.TextFormatFlags.Right + | swf.TextFormatFlags.VerticalCenter + | swf.TextFormatFlags.NoPadding; class Segment { public SegmentDef Def { get; set; } public string Format { get; set; } - public float Start { get; set; } - public float End { get { return Start + Width; } } + public int Start { get; set; } + public int End { get { return Start + Width; } } public string StaticText { get; set; } public bool IsUpdatable { get { return Def != null; } } - public float Width { get; set; } + public int Width { get; set; } } - List segments = new List(); - public ExtendedDateTimePicker() - { - //ExtendedMode = true; - } + readonly List segments = new List(); public bool ExtendedMode { @@ -68,18 +57,18 @@ class SegmentDef public char Char { get; set; } public Func Update { get; set; } public Func MaxWidth { get; set; } - public int? CharWidth { get; set; } } - static SegmentDef[] defs = new[] { - new SegmentDef { Char = 'y', Update = (d,i) => d.AddYears(i), MaxWidth = s => new string('9', s.Length) }, - new SegmentDef { Char = 'M', Update = (d,i) => d.AddMonths(i), MaxWidth = s => "99" }, - new SegmentDef { Char = 'd', Update = (d,i) => d.AddDays(i), MaxWidth = s => "99" }, - new SegmentDef { Char = 'h', Update = (d,i) => d.AddHours(i), MaxWidth = s => "99" }, - new SegmentDef { Char = 'm', Update = (d,i) => d.AddMinutes(i), MaxWidth = s => "99" }, - new SegmentDef { Char = 's', Update = (d,i) => d.AddSeconds(i), MaxWidth = s => "99" }, - new SegmentDef { Char = 't', Update = (d,i) => d.AddHours(d.Hour > 12 ? -12 : 12), MaxWidth = s => CultureInfo.CurrentCulture.DateTimeFormat.AMDesignator } - }; + static readonly SegmentDef[] defs = + { + new SegmentDef { Char = 'y', Update = (d,i) => d.AddYears(i), MaxWidth = s => new string('9', s.Length) }, + new SegmentDef { Char = 'M', Update = (d,i) => d.AddMonths(i), MaxWidth = s => "99" }, + new SegmentDef { Char = 'd', Update = (d,i) => d.AddDays(i), MaxWidth = s => "99" }, + new SegmentDef { Char = 'h', Update = (d,i) => d.AddHours(i), MaxWidth = s => "99" }, + new SegmentDef { Char = 'm', Update = (d,i) => d.AddMinutes(i), MaxWidth = s => "99" }, + new SegmentDef { Char = 's', Update = (d,i) => d.AddSeconds(i), MaxWidth = s => "99" }, + new SegmentDef { Char = 't', Update = (d,i) => d.AddHours(d.Hour > 12 ? -12 : 12), MaxWidth = s => CultureInfo.CurrentCulture.DateTimeFormat.AMDesignator } + }; protected override void OnFormatChanged(EventArgs e) { @@ -114,11 +103,10 @@ void UpdateSegments() using (var g = CreateGraphics()) { - var pixelPos = 0f; + var pixelPos = 0; var pos = 0; while (pos < format.Length) { - pixelPos = (float)Math.Round(pixelPos); var ch = format[pos]; var def = defs.FirstOrDefault(r => r.Char == ch); if (def != null) @@ -127,11 +115,11 @@ void UpdateSegments() while (endPos < format.Length - 1 && format[endPos + 1] == ch) endPos++; var str = def.MaxWidth(format.Substring(pos, (endPos - pos + 1))); - var strSize = g.MeasureString(str, Font, int.MaxValue, stringFormat); + var strSize = swf.TextRenderer.MeasureText(g, str, Font, sd.Size.Empty, RenderTextFormat); var segment = new Segment { Start = pixelPos, - Width = (float)Math.Round(strSize.Width + .5f), + Width = strSize.Width, Format = format.Substring(pos, endPos - pos + 1), Def = def }; @@ -141,12 +129,12 @@ void UpdateSegments() } else { - var strSize = g.MeasureString(ch.ToString(), Font, int.MaxValue, stringFormat); + var strSize = swf.TextRenderer.MeasureText(g, ch.ToString(CultureInfo.InvariantCulture), Font, sd.Size.Empty, RenderTextFormat); var segment = new Segment { Start = pixelPos, - Width = (float)Math.Round(strSize.Width + .5f), - StaticText = ch.ToString() + Width = strSize.Width, + StaticText = ch.ToString(CultureInfo.InvariantCulture) }; segments.Add(segment); pixelPos += segment.Width; @@ -283,9 +271,9 @@ protected override void OnPaint(swf.PaintEventArgs e) // calculate text location var font = Font; - var fontSize = g.MeasureString("9/", font, int.MaxValue, stringFormat); + var fontSize = swf.TextRenderer.MeasureText(g, "9/", font, new sd.Size(int.MaxValue, int.MaxValue), RenderTextFormat); - int textOffset = (int)Math.Round((ClientSize.Height - fontSize.Height) / 2); + int textOffset = (ClientSize.Height - fontSize.Height) / 2; var textRect = rect; textRect.X += 2; textRect.Y += textOffset; @@ -307,33 +295,28 @@ protected override void OnPaint(swf.PaintEventArgs e) // text segments int currentSegment = 0; - using (var foreBrush = new sd.SolidBrush(foreColor)) - using (var highlightBrush = new sd.SolidBrush(sd.SystemColors.HighlightText)) + foreach (var segment in segments) { - - foreach (var segment in segments) + string str; + var textColor = foreColor; + if (segment.Def != null) { - string str; - var textBrush = foreBrush; - if (segment.Def != null) + var s = Value.ToString(" " + segment.Format); + str = s.Substring(1); + if (Focused && currentSegment == selectedSegment) { - var s = Value.ToString(" " + segment.Format); - str = s.Substring(1); - if (Focused && currentSegment == selectedSegment) + using (var highlightBgBrush = new sd.SolidBrush(sd.SystemColors.Highlight)) { - using (var highlightBgBrush = new sd.SolidBrush(sd.SystemColors.Highlight)) - { - g.FillRectangle(highlightBgBrush, new sd.RectangleF(textRect.X + segment.Start + 0.5f, textRect.Y - 1, segment.Width - 1f, textRect.Height + 2)); - } - textBrush = highlightBrush; + g.FillRectangle(highlightBgBrush, new sd.RectangleF(textRect.X + segment.Start, textRect.Y, segment.Width, textRect.Height)); } + textColor = sd.SystemColors.HighlightText; } - else - str = segment.StaticText; - - g.DrawString(str, font, textBrush, new sd.RectangleF(textRect.X + segment.Start, textRect.Y, segment.Width, textRect.Height), stringFormat); - currentSegment++; } + else + str = segment.StaticText; + + swf.TextRenderer.DrawText(g, str, font, new sd.Rectangle((int)(textRect.X + segment.Start), textRect.Y, segment.Width, textRect.Height), textColor, RenderTextFormat); + currentSegment++; } // calendar button @@ -372,21 +355,15 @@ private swf.VisualStyles.CheckBoxState GetCheckBoxState() { if (Focused && selectedSegment == -1) return swf.VisualStyles.CheckBoxState.CheckedHot; - else - return swf.VisualStyles.CheckBoxState.CheckedNormal; + return swf.VisualStyles.CheckBoxState.CheckedNormal; } - else if (Focused && selectedSegment == -1) + if (Focused && selectedSegment == -1) return swf.VisualStyles.CheckBoxState.UncheckedHot; - else - return swf.VisualStyles.CheckBoxState.UncheckedNormal; - } - else - { - if (Checked) - return swf.VisualStyles.CheckBoxState.CheckedDisabled; - else - return swf.VisualStyles.CheckBoxState.UncheckedDisabled; + return swf.VisualStyles.CheckBoxState.UncheckedNormal; } + if (Checked) + return swf.VisualStyles.CheckBoxState.CheckedDisabled; + return swf.VisualStyles.CheckBoxState.UncheckedDisabled; } bool calendarOpen; diff --git a/Source/Eto.WinForms/Drawing/GraphicsHandler.cs b/Source/Eto.WinForms/Drawing/GraphicsHandler.cs index a381c6b8cf..4b7879a0e8 100644 --- a/Source/Eto.WinForms/Drawing/GraphicsHandler.cs +++ b/Source/Eto.WinForms/Drawing/GraphicsHandler.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using Eto.Drawing; using sd = System.Drawing; using sdd = System.Drawing.Drawing2D; @@ -8,7 +9,7 @@ namespace Eto.WinForms.Drawing { /// - /// Handler for + /// Handler for /// /// (c) 2012-2014 by Curtis Wensley /// See LICENSE for full terms @@ -16,15 +17,37 @@ public class GraphicsHandler : WidgetHandler, { Stack savedTransforms; + /// + /// Gets or sets a value indicating that the and methods + /// use compatible rendering (GDI+), or newer graphics-dependent (GDI) methods that have better cleartype rendering. + /// Setting this to false will not allow the text to be fully transformed, so it is only recommended to turn + /// off when rendering a custom control. + /// Use via styles like so: + /// + /// Eto.Style.Add<GraphicsHandler>(null, h => h.UseCompatibleTextRendering = false); + /// + /// + [DefaultValue(true)] + public bool UseCompatibleTextRendering { get; set; } + public bool IsRetained { get { return false; } } + public static swf.TextFormatFlags DefaultTextFormat { get; private set; } + public static sd.StringFormat DefaultStringFormat { get; private set; } - static GraphicsHandler () + static GraphicsHandler() { + DefaultTextFormat = swf.TextFormatFlags.Left + | swf.TextFormatFlags.NoPadding + | swf.TextFormatFlags.NoClipping + | swf.TextFormatFlags.PreserveGraphicsClipping + | swf.TextFormatFlags.PreserveGraphicsTranslateTransform + | swf.TextFormatFlags.NoPrefix; + // Set the StringFormat - DefaultStringFormat = new sd.StringFormat (sd.StringFormat.GenericTypographic); - DefaultStringFormat.FormatFlags |= + DefaultStringFormat = new sd.StringFormat(sd.StringFormat.GenericTypographic); + DefaultStringFormat.FormatFlags |= sd.StringFormatFlags.MeasureTrailingSpaces | sd.StringFormatFlags.NoWrap | sd.StringFormatFlags.NoClip; @@ -32,13 +55,15 @@ static GraphicsHandler () ImageInterpolation imageInterpolation; - public GraphicsHandler () + public GraphicsHandler() { + UseCompatibleTextRendering = true; } bool shouldDisposeGraphics = true; public GraphicsHandler(sd.Graphics graphics, bool shouldDisposeGraphics = true) + : this() { this.Control = graphics; this.shouldDisposeGraphics = shouldDisposeGraphics; @@ -51,7 +76,7 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } - + public bool AntiAlias { get @@ -73,14 +98,14 @@ public ImageInterpolation ImageInterpolation set { imageInterpolation = value; - Control.InterpolationMode = value.ToSD (); + Control.InterpolationMode = value.ToSD(); } } public PixelOffsetMode PixelOffsetMode { - get { return Control.PixelOffsetMode.ToEto (); } - set { Control.PixelOffsetMode = value.ToSD (); } + get { return Control.PixelOffsetMode.ToEto(); } + set { Control.PixelOffsetMode = value.ToSD(); } } public float PointsPerPixel @@ -88,9 +113,9 @@ public float PointsPerPixel get { return 72f / Control.DpiX; } } - public void CreateFromImage (Bitmap image) + public void CreateFromImage(Bitmap image) { - Control = sd.Graphics.FromImage ((sd.Image)image.ControlObject); + Control = sd.Graphics.FromImage((sd.Image)image.ControlObject); } protected override void Initialize() @@ -101,36 +126,36 @@ protected override void Initialize() Control.InterpolationMode = sdd.InterpolationMode.HighQualityBilinear; } - public void Commit () + public void Commit() { } - public void DrawLine (Pen pen, float startx, float starty, float endx, float endy) + public void DrawLine(Pen pen, float startx, float starty, float endx, float endy) { - this.Control.DrawLine (pen.ToSD (), startx, starty, endx, endy); + this.Control.DrawLine(pen.ToSD(), startx, starty, endx, endy); } - public void DrawRectangle (Pen pen, float x, float y, float width, float height) + public void DrawRectangle(Pen pen, float x, float y, float width, float height) { - Control.DrawRectangle (pen.ToSD (), x, y, width, height); + Control.DrawRectangle(pen.ToSD(), x, y, width, height); } - public void FillRectangle (Brush brush, float x, float y, float width, float height) + public void FillRectangle(Brush brush, float x, float y, float width, float height) { - Control.FillRectangle (brush.ToSD (), x - 0.5f, y - 0.5f, width, height); + Control.FillRectangle(brush.ToSD(), x - 0.5f, y - 0.5f, width, height); } - public void DrawEllipse (Pen pen, float x, float y, float width, float height) + public void DrawEllipse(Pen pen, float x, float y, float width, float height) { - Control.DrawEllipse (pen.ToSD (), x, y, width, height); + Control.DrawEllipse(pen.ToSD(), x, y, width, height); } - public void FillEllipse (Brush brush, float x, float y, float width, float height) + public void FillEllipse(Brush brush, float x, float y, float width, float height) { - Control.FillEllipse (brush.ToSD (), x - 0.5f, y - 0.5f, width, height); + Control.FillEllipse(brush.ToSD(), x - 0.5f, y - 0.5f, width, height); } - public float GetConvertedAngle (float initialAngle, float majorRadius, float minorRadius, bool circularToElliptical) + public float GetConvertedAngle(float initialAngle, float majorRadius, float minorRadius, bool circularToElliptical) { var angle = initialAngle; while (angle < 0) @@ -144,25 +169,31 @@ public float GetConvertedAngle (float initialAngle, float majorRadius, float min var quadrant4 = (modAngle > 270 && modAngle <= 360); if (quadrant2 || quadrant4) angle = 90.0f - angle; - angle = DegreeToRadian (angle); + angle = DegreeToRadian(angle); double functionReturnValue = 0; double dTan = 0; - dTan = Math.Tan (angle); + dTan = Math.Tan(angle); - if (Math.Abs (dTan) < 1E-10 | Math.Abs (dTan) > 10000000000.0) { + if (Math.Abs(dTan) < 1E-10 | Math.Abs(dTan) > 10000000000.0) + { functionReturnValue = angle; - } else if (circularToElliptical) { - functionReturnValue = Math.Atan (dTan * majorRadius / minorRadius); - } else { - functionReturnValue = Math.Atan (dTan * minorRadius / majorRadius); + } + else if (circularToElliptical) + { + functionReturnValue = Math.Atan(dTan * majorRadius / minorRadius); + } + else + { + functionReturnValue = Math.Atan(dTan * minorRadius / majorRadius); } - if (functionReturnValue < 0) { + if (functionReturnValue < 0) + { functionReturnValue = functionReturnValue + 2 * Math.PI; } - var ret = RadianToDegree ((float)functionReturnValue); + var ret = RadianToDegree((float)functionReturnValue); // convert back to right quadrant if (quadrant2) @@ -173,150 +204,160 @@ public float GetConvertedAngle (float initialAngle, float majorRadius, float min ret += 180.0f; // get in the same range - while (initialAngle < 0) { + while (initialAngle < 0) + { initialAngle += 360.0f; ret -= 360.0f; } - while (initialAngle > 360) { + while (initialAngle > 360) + { initialAngle -= 360.0f; ret += 360.0f; } - return ret; + return ret; } - float DegreeToRadian (float angle) + float DegreeToRadian(float angle) { return (float)Math.PI * angle / 180.0f; } - float RadianToDegree (float radians) + float RadianToDegree(float radians) { return radians * 180.0f / (float)Math.PI; } - public void DrawArc (Pen pen, float x, float y, float width, float height, float startAngle, float sweepAngle) + public void DrawArc(Pen pen, float x, float y, float width, float height, float startAngle, float sweepAngle) { - if (width != height) { + if (width != height) + { var endAngle = startAngle + sweepAngle; - startAngle = GetConvertedAngle (startAngle, width / 2, height / 2, false); - endAngle = GetConvertedAngle (endAngle, width / 2, height / 2, false); + startAngle = GetConvertedAngle(startAngle, width / 2, height / 2, false); + endAngle = GetConvertedAngle(endAngle, width / 2, height / 2, false); sweepAngle = endAngle - startAngle; } - Control.DrawArc (pen.ToSD (), x, y, width, height, startAngle, sweepAngle); + Control.DrawArc(pen.ToSD(), x, y, width, height, startAngle, sweepAngle); } - public void FillPie (Brush brush, float x, float y, float width, float height, float startAngle, float sweepAngle) + public void FillPie(Brush brush, float x, float y, float width, float height, float startAngle, float sweepAngle) { - if (width != height) { + if (width != height) + { var endAngle = startAngle + sweepAngle; - startAngle = GetConvertedAngle (startAngle, width / 2, height / 2, false); - endAngle = GetConvertedAngle (endAngle, width / 2, height / 2, false); + startAngle = GetConvertedAngle(startAngle, width / 2, height / 2, false); + endAngle = GetConvertedAngle(endAngle, width / 2, height / 2, false); sweepAngle = endAngle - startAngle; } - Control.FillPie (brush.ToSD (), x - 0.5f, y - 0.5f, width, height, startAngle, sweepAngle); + Control.FillPie(brush.ToSD(), x - 0.5f, y - 0.5f, width, height, startAngle, sweepAngle); } - public void FillPath (Brush brush, IGraphicsPath path) + public void FillPath(Brush brush, IGraphicsPath path) { var old = Control.PixelOffsetMode; Control.PixelOffsetMode = old == sdd.PixelOffsetMode.Half ? sdd.PixelOffsetMode.None : sdd.PixelOffsetMode.Half; - Control.FillPath (brush.ToSD (), path.ToSD ()); + Control.FillPath(brush.ToSD(), path.ToSD()); Control.PixelOffsetMode = old; } - public void DrawPath (Pen pen, IGraphicsPath path) + public void DrawPath(Pen pen, IGraphicsPath path) { - Control.DrawPath (pen.ToSD (), path.ToSD ()); + Control.DrawPath(pen.ToSD(), path.ToSD()); } - public void DrawImage (Image image, float x, float y) + public void DrawImage(Image image, float x, float y) { var handler = image.Handler as IWindowsImage; - handler.DrawImage (this, x, y); + handler.DrawImage(this, x, y); } - public void DrawImage (Image image, float x, float y, float width, float height) + public void DrawImage(Image image, float x, float y, float width, float height) { var handler = image.Handler as IWindowsImage; - handler.DrawImage (this, x, y, width, height); + handler.DrawImage(this, x, y, width, height); } - public void DrawImage (Image image, RectangleF source, RectangleF destination) + public void DrawImage(Image image, RectangleF source, RectangleF destination) { var handler = image.Handler as IWindowsImage; - handler.DrawImage (this, source, destination); + handler.DrawImage(this, source, destination); } public void DrawText(Font font, SolidBrush brush, float x, float y, string text) { - Control.DrawString(text, (sd.Font)font.ControlObject, brush.ControlObject as sd.Brush, x, y, DefaultStringFormat); + if (UseCompatibleTextRendering) + { + Control.DrawString(text, (sd.Font)font.ControlObject, (sd.Brush)brush.ControlObject, x, y, DefaultStringFormat); + } + else + { + swf.TextRenderer.DrawText(Control, text, (sd.Font)font.ControlObject, new sd.Point((int)x, (int)y), brush.Color.ToSD(), DefaultTextFormat); + } } - public static SizeF MeasureString(sd.Graphics g, string text, sd.Font font, sd.RectangleF? layoutRect = null) + public SizeF MeasureString(Font font, string text) { - /* BAD (but not really!?) - * - return this.Control.MeasureString (text, FontHandler.GetControl (font), sd.PointF.Empty, defaultStringFormat).ToEto (); - /**/ if (string.IsNullOrEmpty(text)) return Size.Empty; - sd.CharacterRange[] ranges = { new sd.CharacterRange(0, text.Length) }; - DefaultStringFormat.SetMeasurableCharacterRanges(ranges); - var regions = g.MeasureCharacterRanges(text, font, layoutRect ?? sd.RectangleF.Empty, DefaultStringFormat); - var rect = regions[0].GetBounds(g); + var sdFont = FontHandler.GetControl(font); + if (UseCompatibleTextRendering) + { + sd.CharacterRange[] ranges = { new sd.CharacterRange(0, text.Length) }; + DefaultStringFormat.SetMeasurableCharacterRanges(ranges); - return rect.Size.ToEto(); - /**/ - } + var regions = Control.MeasureCharacterRanges(text, sdFont, sd.RectangleF.Empty, DefaultStringFormat); + var rect = regions[0].GetBounds(Control); - public SizeF MeasureString (Font font, string text) - { - return MeasureString(Control, text, FontHandler.GetControl(font), sd.RectangleF.Empty); + return rect.Size.ToEto(); + } + + var size = swf.TextRenderer.MeasureText(Control, text, sdFont, sd.Size.Empty, DefaultTextFormat); + return size.ToEto(); } - public void Flush () + public void Flush() { - Control.Flush (); + Control.Flush(); } - public void TranslateTransform (float offsetX, float offsetY) + public void TranslateTransform(float offsetX, float offsetY) { - this.Control.TranslateTransform (offsetX, offsetY); + this.Control.TranslateTransform(offsetX, offsetY); } - public void RotateTransform (float angle) + public void RotateTransform(float angle) { - this.Control.RotateTransform (angle); + this.Control.RotateTransform(angle); } - public void ScaleTransform (float scaleX, float scaleY) + public void ScaleTransform(float scaleX, float scaleY) { - this.Control.ScaleTransform (scaleX, scaleY); + this.Control.ScaleTransform(scaleX, scaleY); } - public void MultiplyTransform (IMatrix matrix) + public void MultiplyTransform(IMatrix matrix) { - this.Control.MultiplyTransform ((sd.Drawing2D.Matrix)matrix.ControlObject); + this.Control.MultiplyTransform((sd.Drawing2D.Matrix)matrix.ControlObject); } - public void SaveTransform () + public void SaveTransform() { if (savedTransforms == null) - savedTransforms = new Stack (); + savedTransforms = new Stack(); - savedTransforms.Push (Control.Transform); + savedTransforms.Push(Control.Transform); } - public void RestoreTransform () + public void RestoreTransform() { - if (savedTransforms != null && savedTransforms.Count > 0) { - var t = savedTransforms.Pop (); + if (savedTransforms != null && savedTransforms.Count > 0) + { + var t = savedTransforms.Pop(); Control.Transform = t; - t.Dispose (); + t.Dispose(); } } @@ -325,27 +366,27 @@ public RectangleF ClipBounds get { return this.Control.ClipBounds.ToEto(); } } - public void SetClip (RectangleF rectangle) + public void SetClip(RectangleF rectangle) { - this.Control.SetClip (rectangle.ToSD ()); + this.Control.SetClip(rectangle.ToSD()); } - public void SetClip (IGraphicsPath path) + public void SetClip(IGraphicsPath path) { - this.Control.SetClip (path.ToSD ()); + this.Control.SetClip(path.ToSD()); } - public void ResetClip () + public void ResetClip() { - this.Control.ResetClip (); + this.Control.ResetClip(); } public void Clear(SolidBrush brush) { if (brush != null) - Control.Clear (brush.Color.ToSD ()); + Control.Clear(brush.Color.ToSD()); else - Control.Clear (sd.Color.Transparent); + Control.Clear(sd.Color.Transparent); } } } diff --git a/Source/Eto.WinForms/Forms/ApplicationHandler.cs b/Source/Eto.WinForms/Forms/ApplicationHandler.cs index 34247edbc7..d9577f3426 100644 --- a/Source/Eto.WinForms/Forms/ApplicationHandler.cs +++ b/Source/Eto.WinForms/Forms/ApplicationHandler.cs @@ -24,6 +24,7 @@ public ApplicationHandler() { mainThread = Thread.CurrentThread; swf.Application.EnableVisualStyles(); + swf.Application.SetCompatibleTextRenderingDefault(false); } public void RunIteration() diff --git a/Source/Eto.WinForms/Forms/Controls/DropDownHandler.cs b/Source/Eto.WinForms/Forms/Controls/DropDownHandler.cs index 279d346841..2829afc33e 100644 --- a/Source/Eto.WinForms/Forms/Controls/DropDownHandler.cs +++ b/Source/Eto.WinForms/Forms/Controls/DropDownHandler.cs @@ -23,21 +23,21 @@ public void ResetSize() public sd.Size MinSize { get; set; } - static readonly sd.Graphics graphics = sd.Graphics.FromHwnd(IntPtr.Zero); - public override sd.Size GetPreferredSize(sd.Size proposedSize) { if (cachedSize == null) { var size = new sd.Size(16, 20); var font = Font; - - foreach (object item in Items) + using (var g = CreateGraphics()) { - var text = GetItemText(item); - var itemSize = graphics.MeasureString(text, font); - size.Width = Math.Max(size.Width, (int)itemSize.Width); - size.Height = Math.Max(size.Height, (int)itemSize.Height); + foreach (object item in Items) + { + var text = GetItemText(item); + var itemSize = swf.TextRenderer.MeasureText(g, text, font); + size.Width = Math.Max(size.Width, (int) itemSize.Width); + size.Height = Math.Max(size.Height, (int) itemSize.Height); + } } // for drop down glyph and border if (DrawMode == swf.DrawMode.OwnerDrawFixed) @@ -82,7 +82,7 @@ protected override void OnDrawItem(swf.DrawItemEventArgs e) string text = Items[e.Index].ToString(); // Determine the forecolor based on whether or not the item is selected - e.Graphics.DrawString(text, Font, new sd.SolidBrush(ForeColor), e.Bounds.X, e.Bounds.Y); + swf.TextRenderer.DrawText(e.Graphics, text, Font, e.Bounds, ForeColor, swf.TextFormatFlags.Left); } e.DrawFocusRectangle(); diff --git a/Source/Eto.WinForms/Forms/Controls/LabelHandler.cs b/Source/Eto.WinForms/Forms/Controls/LabelHandler.cs index a11d4bb0c3..0d97ae7052 100644 --- a/Source/Eto.WinForms/Forms/Controls/LabelHandler.cs +++ b/Source/Eto.WinForms/Forms/Controls/LabelHandler.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using sd = System.Drawing; using swf = System.Windows.Forms; using Eto.Forms; @@ -6,17 +7,17 @@ namespace Eto.WinForms.Forms.Controls { - public class LabelHandler : WindowsControl, Label.IHandler + public class LabelHandler : WindowsControl, Label.IHandler { - public class MyLabel : swf.Label + public class EtoLabel : swf.Label { - readonly sd.StringFormat stringFormat; WrapMode wrapMode; HorizontalAlign horizontalAlign; sd.SizeF? measuredSize; sd.Size proposedSizeCache; sd.SizeF? measuredSizeMax; VerticalAlign verticalAlign; + swf.TextFormatFlags textFormat = swf.TextFormatFlags.Default; void ClearSize() { @@ -71,18 +72,16 @@ public VerticalAlign VerticalAlign set { verticalAlign = value; + SetStringFormat(); ClearSize(); } } - public MyLabel() + public EtoLabel() { - stringFormat = new sd.StringFormat(); Wrap = WrapMode.Word; } - static readonly sd.Graphics graphics = sd.Graphics.FromHwnd(IntPtr.Zero); - public override sd.Size GetPreferredSize(sd.Size proposedSize) { var bordersAndPadding = Margin.Size; // this.SizeFromClientSize (SD.Size.Empty); @@ -94,7 +93,7 @@ public override sd.Size GetPreferredSize(sd.Size proposedSize) { proposedSize -= bordersAndPadding; proposedSize.Height = Math.Max(0, proposedSize.Height); - measuredSizeMax = graphics.MeasureString(Text, Font, proposedSize.Width, stringFormat); + measuredSizeMax = swf.TextRenderer.MeasureText(Text, Font, new sd.Size(proposedSize.Width, int.MaxValue), textFormat); } measuredSize = measuredSizeMax; } @@ -103,7 +102,7 @@ public override sd.Size GetPreferredSize(sd.Size proposedSize) proposedSizeCache = proposedSize; proposedSize -= bordersAndPadding; proposedSize.Height = Math.Max(0, proposedSize.Height); - measuredSize = graphics.MeasureString(Text, Font, proposedSize.Width, stringFormat); + measuredSize = swf.TextRenderer.MeasureText(Text, Font, new sd.Size(proposedSize.Width, int.MaxValue), textFormat); } var size = measuredSize.Value; size += bordersAndPadding; @@ -116,29 +115,40 @@ public override sd.Size GetPreferredSize(sd.Size proposedSize) void SetStringFormat() { - stringFormat.HotkeyPrefix = System.Drawing.Text.HotkeyPrefix.Show; + textFormat = swf.TextFormatFlags.Default; switch (Wrap) { case WrapMode.None: - stringFormat.Trimming = System.Drawing.StringTrimming.None; - stringFormat.FormatFlags = System.Drawing.StringFormatFlags.NoWrap; + textFormat |= swf.TextFormatFlags.SingleLine; break; case WrapMode.Word: - stringFormat.Trimming = System.Drawing.StringTrimming.Word; - stringFormat.FormatFlags = 0; + textFormat |= swf.TextFormatFlags.WordBreak; break; case WrapMode.Character: - stringFormat.Trimming = System.Drawing.StringTrimming.Character; - stringFormat.FormatFlags = System.Drawing.StringFormatFlags.NoWrap; break; } switch (HorizontalAlign) { + case HorizontalAlign.Left: + textFormat |= swf.TextFormatFlags.Left; + break; case HorizontalAlign.Right: - stringFormat.Alignment = System.Drawing.StringAlignment.Far; + textFormat |= swf.TextFormatFlags.Right; break; case HorizontalAlign.Center: - stringFormat.Alignment = System.Drawing.StringAlignment.Center; + textFormat |= swf.TextFormatFlags.HorizontalCenter; + break; + } + switch (VerticalAlign) + { + case VerticalAlign.Top: + textFormat |= swf.TextFormatFlags.Top; + break; + case VerticalAlign.Bottom: + textFormat |= swf.TextFormatFlags.Bottom; + break; + case VerticalAlign.Middle: + textFormat |= swf.TextFormatFlags.VerticalCenter; break; } @@ -146,72 +156,14 @@ void SetStringFormat() protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) { - using (var b = new sd.SolidBrush(ForeColor)) - { - if (Wrap == WrapMode.Character) - { - // draw string one line at a time to trim to character.. - int charactersFitted, linesFilled; - string text = Text; - sd.PointF drawPoint = sd.PointF.Empty; - var font = Font; - var height = font.GetHeight(e.Graphics); - while (!string.IsNullOrEmpty(text)) - { - e.Graphics.MeasureString(text, font, Bounds.Size, stringFormat, out charactersFitted, out linesFilled); - - e.Graphics.DrawString(text.Substring(0, charactersFitted), font, b, drawPoint, stringFormat); - - if (charactersFitted >= text.Length) break; - text = text.Substring(charactersFitted); - - drawPoint.Y += height; - } - } - else - { - var rect = new sd.RectangleF(Margin.Left, Margin.Top, Bounds.Width - Margin.Horizontal, Bounds.Height - Margin.Vertical); - var size = e.Graphics.MeasureString(Text, Font, (int)rect.Width, stringFormat); - - if (size.Height < rect.Height) - { - switch (VerticalAlign) - { - case Eto.Forms.VerticalAlign.Bottom: - rect.Y += rect.Height - size.Height; - rect.Height = size.Height; - break; - case Eto.Forms.VerticalAlign.Middle: - rect.Y += (rect.Height - size.Height) / 2; - rect.Height = size.Height; - break; - } - } - - if (size.Width < rect.Width) - { - switch (HorizontalAlign) - { - case HorizontalAlign.Right: - rect.X = rect.Width - size.Width - Margin.Top; - rect.Width = size.Width; - break; - case HorizontalAlign.Center: - rect.X = (rect.Width - size.Width) / 2 - Margin.Top; - rect.Width = size.Width; - break; - } - } - - e.Graphics.DrawString(Text, Font, b, rect, stringFormat); - } - } + var rect = new sd.Rectangle(Margin.Left, Margin.Top, Bounds.Width - Margin.Horizontal, Bounds.Height - Margin.Vertical); + swf.TextRenderer.DrawText(e.Graphics, Text, Font, rect, ForeColor, BackColor, textFormat); } } public LabelHandler() { - Control = new MyLabel + Control = new EtoLabel { AutoSize = true }; diff --git a/Source/Eto.WinForms/Forms/Controls/ListBoxHandler.cs b/Source/Eto.WinForms/Forms/Controls/ListBoxHandler.cs index 0b8f131a67..214313e2c9 100644 --- a/Source/Eto.WinForms/Forms/Controls/ListBoxHandler.cs +++ b/Source/Eto.WinForms/Forms/Controls/ListBoxHandler.cs @@ -12,9 +12,9 @@ public class ListBoxHandler : WindowsControl, Form.IHandler { - public class MyForm : swf.Form + public class EtoForm : swf.Form { bool hideFromAltTab; @@ -62,7 +62,7 @@ public FormHandler(swf.Form form) public FormHandler() { - Control = new MyForm + Control = new EtoForm { StartPosition = swf.FormStartPosition.CenterParent, AutoSize = true, @@ -88,7 +88,7 @@ public override bool ShowInTaskbar set { base.ShowInTaskbar = value; - var myForm = Control as MyForm; + var myForm = Control as EtoForm; if (myForm != null) myForm.HideFromAltTab = !value; }