From d40fed5ce2bc806a91245adb18039634eac13ed0 Mon Sep 17 00:00:00 2001 From: MichaelTheShifter Date: Wed, 20 Jul 2016 09:40:36 -0400 Subject: Move ShiftUI source code to ShiftOS This'll be a lot easier to work on. --- source/ShiftUI/Internal/TextRenderer.cs | 527 ++++++++++++++++++++++++++++++++ 1 file changed, 527 insertions(+) create mode 100644 source/ShiftUI/Internal/TextRenderer.cs (limited to 'source/ShiftUI/Internal/TextRenderer.cs') diff --git a/source/ShiftUI/Internal/TextRenderer.cs b/source/ShiftUI/Internal/TextRenderer.cs new file mode 100644 index 0000000..89b3dfa --- /dev/null +++ b/source/ShiftUI/Internal/TextRenderer.cs @@ -0,0 +1,527 @@ +// +// TextRenderer.cs +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Copyright (c) 2006 Novell, Inc. +// +// Authors: +// Jonathan Pobst (monkey@jpobst.com) +// + +// This has become a monster class for all things text measuring and drawing. +// +// The public API is MeasureText/DrawText, which uses GDI on Win32, and +// GDI+ on other platforms. +// +// There is an internal API MeasureTextInternal/DrawTextInternal, which allows +// you to pass a flag of whether to use GDI or GDI+. This is used mainly for +// Widgets that have the UseCompatibleTextRendering flag. +// +// There are also thread-safe versions of MeasureString/MeasureCharacterRanges +// for things that want to measure strings without having a Graphics object. + +using System.Drawing; +using System.Runtime.InteropServices; +using System.Text; +using System.Drawing.Text; +using System; + +namespace ShiftUI +{ + public sealed class TextRenderer + { + private TextRenderer () + { + } + + #region Public Methods + public static void DrawText (IDeviceContext dc, string text, Font font, Point pt, Color foreColor) + { + DrawTextInternal (dc, text, font, pt, foreColor, Color.Transparent, TextFormatFlags.Default, false); + } + + public static void DrawText (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor) + { + DrawTextInternal (dc, text, font, bounds, foreColor, Color.Transparent, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter, false); + } + + public static void DrawText (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, Color backColor) + { + DrawTextInternal (dc, text, font, pt, foreColor, backColor, TextFormatFlags.Default, false); + } + + public static void DrawText (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, TextFormatFlags flags) + { + DrawTextInternal (dc, text, font, pt, foreColor, Color.Transparent, flags, false); + } + + public static void DrawText (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor) + { + DrawTextInternal (dc, text, font, bounds, foreColor, backColor, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter, false); + } + + public static void DrawText (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, TextFormatFlags flags) + { + DrawTextInternal (dc, text, font, bounds, foreColor, Color.Transparent, flags, false); + } + + public static void DrawText (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, Color backColor, TextFormatFlags flags) + { + DrawTextInternal (dc, text, font, pt, foreColor, backColor, flags, false); + } + + public static void DrawText (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor, TextFormatFlags flags) + { + DrawTextInternal (dc, text, font, bounds, foreColor, backColor, flags, false); + } + + public static Size MeasureText (string text, Font font) + { + return MeasureTextInternal (Hwnd.GraphicsContext, text, font, Size.Empty, TextFormatFlags.Default, false); + } + + public static Size MeasureText (IDeviceContext dc, string text, Font font) + { + return MeasureTextInternal (dc, text, font, Size.Empty, TextFormatFlags.Default, false); + } + + public static Size MeasureText (string text, Font font, Size proposedSize) + { + return MeasureTextInternal (Hwnd.GraphicsContext, text, font, proposedSize, TextFormatFlags.Default, false); + } + + public static Size MeasureText (IDeviceContext dc, string text, Font font, Size proposedSize) + { + return MeasureTextInternal (dc, text, font, proposedSize, TextFormatFlags.Default, false); + } + + public static Size MeasureText (string text, Font font, Size proposedSize, TextFormatFlags flags) + { + return MeasureTextInternal (Hwnd.GraphicsContext, text, font, proposedSize, flags, false); + } + + public static Size MeasureText (IDeviceContext dc, string text, Font font, Size proposedSize, TextFormatFlags flags) + { + return MeasureTextInternal (dc, text, font, proposedSize, flags, false); + } + #endregion + + #region Internal Methods That Do Stuff + internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor, TextFormatFlags flags, bool useDrawString) + { + if (dc == null) + throw new ArgumentNullException ("dc"); + + if (text == null || text.Length == 0) + return; + + // We use MS GDI API's unless told not to, or we aren't on Windows + if (!useDrawString && !XplatUI.RunningOnUnix) { + if ((flags & TextFormatFlags.VerticalCenter) == TextFormatFlags.VerticalCenter || (flags & TextFormatFlags.Bottom) == TextFormatFlags.Bottom) + flags |= TextFormatFlags.SingleLine; + + // Calculate the text bounds (there is often padding added) + Rectangle new_bounds = PadRectangle (bounds, flags); + new_bounds.Offset ((int)(dc as Graphics).Transform.OffsetX, (int)(dc as Graphics).Transform.OffsetY); + + IntPtr hdc = IntPtr.Zero; + bool clear_clip_region = false; + + // If we need to use the graphics clipping region, add it to our hdc + if ((flags & TextFormatFlags.PreserveGraphicsClipping) == TextFormatFlags.PreserveGraphicsClipping) { + Graphics graphics = (Graphics)dc; + Region clip_region = graphics.Clip; + + if (!clip_region.IsInfinite (graphics)) { + IntPtr hrgn = clip_region.GetHrgn (graphics); + hdc = dc.GetHdc (); + SelectClipRgn (hdc, hrgn); + DeleteObject (hrgn); + + clear_clip_region = true; + } + } + + if (hdc == IntPtr.Zero) + hdc = dc.GetHdc (); + + // Set the fore color + if (foreColor != Color.Empty) + SetTextColor (hdc, ColorTranslator.ToWin32 (foreColor)); + + // Set the back color + if (backColor != Color.Transparent && backColor != Color.Empty) { + SetBkMode (hdc, 2); //1-Transparent, 2-Opaque + SetBkColor (hdc, ColorTranslator.ToWin32 (backColor)); + } + else { + SetBkMode (hdc, 1); //1-Transparent, 2-Opaque + } + + XplatUIWin32.RECT r = XplatUIWin32.RECT.FromRectangle (new_bounds); + + IntPtr prevobj; + + if (font != null) { + prevobj = SelectObject (hdc, font.ToHfont ()); + Win32DrawText (hdc, text, text.Length, ref r, (int)flags); + prevobj = SelectObject (hdc, prevobj); + DeleteObject (prevobj); + } + else { + Win32DrawText (hdc, text, text.Length, ref r, (int)flags); + } + + if (clear_clip_region) + SelectClipRgn (hdc, IntPtr.Zero); + + dc.ReleaseHdc (); + } + // Use Graphics.DrawString as a fallback method + else { + Graphics g; + IntPtr hdc = IntPtr.Zero; + + if (dc is Graphics) + g = (Graphics)dc; + else { + hdc = dc.GetHdc (); + g = Graphics.FromHdc (hdc); + } + + StringFormat sf = FlagsToStringFormat (flags); + + Rectangle new_bounds = PadDrawStringRectangle (bounds, flags); + + g.DrawString (text, font, ThemeEngine.Current.ResPool.GetSolidBrush (foreColor), new_bounds, sf); + + if (!(dc is Graphics)) { + g.Dispose (); + dc.ReleaseHdc (); + } + } + } + + internal static Size MeasureTextInternal (IDeviceContext dc, string text, Font font, Size proposedSize, TextFormatFlags flags, bool useMeasureString) + { + if (!useMeasureString && !XplatUI.RunningOnUnix) { + // Tell DrawText to calculate size instead of draw + flags |= (TextFormatFlags)1024; // DT_CALCRECT + + IntPtr hdc = dc.GetHdc (); + + XplatUIWin32.RECT r = XplatUIWin32.RECT.FromRectangle (new Rectangle (Point.Empty, proposedSize)); + + IntPtr prevobj; + + if (font != null) { + prevobj = SelectObject (hdc, font.ToHfont ()); + Win32DrawText (hdc, text, text.Length, ref r, (int)flags); + prevobj = SelectObject (hdc, prevobj); + DeleteObject (prevobj); + } + else { + Win32DrawText (hdc, text, text.Length, ref r, (int)flags); + } + + dc.ReleaseHdc (); + + // Really, I am just making something up here, which as far as I can tell, MS + // just makes something up as well. This will require lots of tweaking to match MS. :( + Size retval = r.ToRectangle ().Size; + + if (retval.Width > 0 && (flags & TextFormatFlags.NoPadding) == 0) { + retval.Width += 6; + retval.Width += (int)retval.Height / 8; + } + + return retval; + } + else { + StringFormat sf = FlagsToStringFormat (flags); + + Size retval; + + int proposedWidth; + if (proposedSize.Width == 0) + proposedWidth = Int32.MaxValue; + else { + proposedWidth = proposedSize.Width; + if ((flags & TextFormatFlags.NoPadding) == 0) + proposedWidth -= 9; + } + if (dc is Graphics) + retval = (dc as Graphics).MeasureString (text, font, proposedWidth, sf).ToSize (); + else + retval = TextRenderer.MeasureString (text, font, proposedWidth, sf).ToSize (); + + if (retval.Width > 0 && (flags & TextFormatFlags.NoPadding) == 0) + retval.Width += 9; + + return retval; + } + } + #endregion + +#region Internal Methods That Are Just Overloads + internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, bool useDrawString) + { + DrawTextInternal (dc, text, font, pt, foreColor, Color.Transparent, TextFormatFlags.Default, useDrawString); + } + + internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, bool useDrawString) + { + DrawTextInternal (dc, text, font, bounds, foreColor, Color.Transparent, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter, useDrawString); + } + + internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, Color backColor, bool useDrawString) + { + DrawTextInternal (dc, text, font, pt, foreColor, backColor, TextFormatFlags.Default, useDrawString); + } + + internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, TextFormatFlags flags, bool useDrawString) + { + DrawTextInternal (dc, text, font, pt, foreColor, Color.Transparent, flags, useDrawString); + } + + internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor, bool useDrawString) + { + DrawTextInternal (dc, text, font, bounds, foreColor, backColor, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter, useDrawString); + } + + internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, TextFormatFlags flags, bool useDrawString) + { + DrawTextInternal (dc, text, font, bounds, foreColor, Color.Transparent, flags, useDrawString); + } + + internal static Size MeasureTextInternal (string text, Font font, bool useMeasureString) + { + return MeasureTextInternal (Hwnd.GraphicsContext, text, font, Size.Empty, TextFormatFlags.Default, useMeasureString); + } + + internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, Color backColor, TextFormatFlags flags, bool useDrawString) + { + Size sz = MeasureTextInternal (dc, text, font, useDrawString); + DrawTextInternal (dc, text, font, new Rectangle (pt, sz), foreColor, backColor, flags, useDrawString); + } + + internal static Size MeasureTextInternal (IDeviceContext dc, string text, Font font, bool useMeasureString) + { + return MeasureTextInternal (dc, text, font, Size.Empty, TextFormatFlags.Default, useMeasureString); + } + + internal static Size MeasureTextInternal (string text, Font font, Size proposedSize, bool useMeasureString) + { + return MeasureTextInternal (Hwnd.GraphicsContext, text, font, proposedSize, TextFormatFlags.Default, useMeasureString); + } + + internal static Size MeasureTextInternal (IDeviceContext dc, string text, Font font, Size proposedSize, bool useMeasureString) + { + return MeasureTextInternal (dc, text, font, proposedSize, TextFormatFlags.Default, useMeasureString); + } + + internal static Size MeasureTextInternal (string text, Font font, Size proposedSize, TextFormatFlags flags, bool useMeasureString) + { + return MeasureTextInternal (Hwnd.GraphicsContext, text, font, proposedSize, flags, useMeasureString); + } +#endregion + + #region Thread-Safe Static Graphics Methods + internal static SizeF MeasureString (string text, Font font) + { + return Hwnd.GraphicsContext.MeasureString (text, font); + } + + internal static SizeF MeasureString (string text, Font font, int width) + { + return Hwnd.GraphicsContext.MeasureString (text, font, width); + } + + internal static SizeF MeasureString (string text, Font font, SizeF layoutArea) + { + return Hwnd.GraphicsContext.MeasureString (text, font, layoutArea); + } + + internal static SizeF MeasureString (string text, Font font, int width, StringFormat format) + { + return Hwnd.GraphicsContext.MeasureString (text, font, width, format); + } + + internal static SizeF MeasureString (string text, Font font, PointF origin, StringFormat stringFormat) + { + return Hwnd.GraphicsContext.MeasureString (text, font, origin, stringFormat); + } + + internal static SizeF MeasureString (string text, Font font, SizeF layoutArea, StringFormat stringFormat) + { + return Hwnd.GraphicsContext.MeasureString (text, font, layoutArea, stringFormat); + } + + internal static SizeF MeasureString (string text, Font font, SizeF layoutArea, StringFormat stringFormat, out int charactersFitted, out int linesFilled) + { + return Hwnd.GraphicsContext.MeasureString (text, font, layoutArea, stringFormat, out charactersFitted, out linesFilled); + } + + internal static Region[] MeasureCharacterRanges (string text, Font font, RectangleF layoutRect, StringFormat stringFormat) + { + return Hwnd.GraphicsContext.MeasureCharacterRanges (text, font, layoutRect, stringFormat); + } + + internal static SizeF GetDpi () + { + return new SizeF (Hwnd.GraphicsContext.DpiX, Hwnd.GraphicsContext.DpiY); + } + #endregion + +#region Private Methods + private static StringFormat FlagsToStringFormat (TextFormatFlags flags) + { + StringFormat sf = new StringFormat (); + + // Translation table: http://msdn.microsoft.com/msdnmag/issues/06/03/TextRendering/default.aspx?fig=true#fig4 + + // Horizontal Alignment + if ((flags & TextFormatFlags.HorizontalCenter) == TextFormatFlags.HorizontalCenter) + sf.Alignment = StringAlignment.Center; + else if ((flags & TextFormatFlags.Right) == TextFormatFlags.Right) + sf.Alignment = StringAlignment.Far; + else + sf.Alignment = StringAlignment.Near; + + // Vertical Alignment + if ((flags & TextFormatFlags.Bottom) == TextFormatFlags.Bottom) + sf.LineAlignment = StringAlignment.Far; + else if ((flags & TextFormatFlags.VerticalCenter) == TextFormatFlags.VerticalCenter) + sf.LineAlignment = StringAlignment.Center; + else + sf.LineAlignment = StringAlignment.Near; + + // Ellipsis + if ((flags & TextFormatFlags.EndEllipsis) == TextFormatFlags.EndEllipsis) + sf.Trimming = StringTrimming.EllipsisCharacter; + else if ((flags & TextFormatFlags.PathEllipsis) == TextFormatFlags.PathEllipsis) + sf.Trimming = StringTrimming.EllipsisPath; + else if ((flags & TextFormatFlags.WordEllipsis) == TextFormatFlags.WordEllipsis) + sf.Trimming = StringTrimming.EllipsisWord; + else + sf.Trimming = StringTrimming.Character; + + // Hotkey Prefix + if ((flags & TextFormatFlags.NoPrefix) == TextFormatFlags.NoPrefix) + sf.HotkeyPrefix = HotkeyPrefix.None; + else if ((flags & TextFormatFlags.HidePrefix) == TextFormatFlags.HidePrefix) + sf.HotkeyPrefix = HotkeyPrefix.Hide; + else + sf.HotkeyPrefix = HotkeyPrefix.Show; + + // Text Padding + if ((flags & TextFormatFlags.NoPadding) == TextFormatFlags.NoPadding) + sf.FormatFlags |= StringFormatFlags.FitBlackBox; + + // Text Wrapping + if ((flags & TextFormatFlags.SingleLine) == TextFormatFlags.SingleLine) + sf.FormatFlags |= StringFormatFlags.NoWrap; + else if ((flags & TextFormatFlags.TextBoxControl) == TextFormatFlags.TextBoxControl) + sf.FormatFlags |= StringFormatFlags.LineLimit; + + // Other Flags + //if ((flags & TextFormatFlags.RightToLeft) == TextFormatFlags.RightToLeft) + // sf.FormatFlags |= StringFormatFlags.DirectionRightToLeft; + if ((flags & TextFormatFlags.NoClipping) == TextFormatFlags.NoClipping) + sf.FormatFlags |= StringFormatFlags.NoClip; + + return sf; + } + + private static Rectangle PadRectangle (Rectangle r, TextFormatFlags flags) + { + if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Right) == 0 && (flags & TextFormatFlags.HorizontalCenter) == 0) { + r.X += 3; + r.Width -= 3; + } + if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Right) == TextFormatFlags.Right) { + r.Width -= 4; + } + if ((flags & TextFormatFlags.LeftAndRightPadding) == TextFormatFlags.LeftAndRightPadding) { + r.X += 2; + r.Width -= 2; + } + if ((flags & TextFormatFlags.WordEllipsis) == TextFormatFlags.WordEllipsis || (flags & TextFormatFlags.EndEllipsis) == TextFormatFlags.EndEllipsis || (flags & TextFormatFlags.WordBreak) == TextFormatFlags.WordBreak) { + r.Width -= 4; + } + if ((flags & TextFormatFlags.VerticalCenter) == TextFormatFlags.VerticalCenter) { + r.Y += 1; + } + + return r; + } + + private static Rectangle PadDrawStringRectangle (Rectangle r, TextFormatFlags flags) + { + if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Right) == 0 && (flags & TextFormatFlags.HorizontalCenter) == 0) { + r.X += 1; + r.Width -= 1; + } + if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Right) == TextFormatFlags.Right) { + r.Width -= 4; + } + if ((flags & TextFormatFlags.NoPadding) == TextFormatFlags.NoPadding) { + r.X -= 2; + } + if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Bottom) == TextFormatFlags.Bottom) { + r.Y += 1; + } + if ((flags & TextFormatFlags.LeftAndRightPadding) == TextFormatFlags.LeftAndRightPadding) { + r.X += 2; + r.Width -= 2; + } + if ((flags & TextFormatFlags.VerticalCenter) == TextFormatFlags.VerticalCenter && XplatUI.RunningOnUnix) { + r.Y -= 1; + } + + return r; + } +#endregion + +#region DllImports (Windows) + [DllImport ("user32", CharSet = CharSet.Unicode, EntryPoint = "DrawText")] + static extern int Win32DrawText (IntPtr hdc, string lpStr, int nCount, ref XplatUIWin32.RECT lpRect, int wFormat); + + [DllImport ("gdi32")] + static extern int SetTextColor (IntPtr hdc, int crColor); + + [DllImport ("gdi32")] + static extern IntPtr SelectObject (IntPtr hDC, IntPtr hObject); + + [DllImport ("gdi32")] + static extern int SetBkColor (IntPtr hdc, int crColor); + + [DllImport ("gdi32")] + static extern int SetBkMode (IntPtr hdc, int iBkMode); + + [DllImport ("gdi32")] + static extern bool DeleteObject (IntPtr objectHandle); + + [DllImport("gdi32")] + static extern bool SelectClipRgn(IntPtr hdc, IntPtr hrgn); +#endregion + } +} -- cgit v1.2.3