#region CPL License /* Nuclex Framework Copyright (C) 2002-2010 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #endregion using System; using System.Collections.Generic; using Microsoft.Xna.Framework; namespace Nuclex.UserInterface.Visuals.Flat.Renderers { /// Renders text input controls in a traditional flat style public class FlatInputControlRenderer : IFlatControlRenderer, Controls.Desktop.IOpeningLocator { /// Style from the skin this renderer uses private const string Style = "input.normal"; /// /// Renders the specified control using the provided graphics interface /// /// Control that will be rendered /// /// Graphics interface that will be used to draw the control /// public void Render( Controls.Desktop.InputControl control, IFlatGuiGraphics graphics ) { RectangleF controlBounds = control.GetAbsoluteBounds(); // Draw the control's frame and background graphics.DrawElement(Style, controlBounds); using(graphics.SetClipRegion(controlBounds)) { string text = control.Text ?? string.Empty; // Amount by which the text will be moved within the input box in // order to keep the caret in view even when the text is wider than // the input box. float left = 0; // Only scroll the text within the input box when it has the input // focus and the caret is being shown. if(control.HasFocus) { // Find out where the cursor is from the left end of the text RectangleF stringSize = graphics.MeasureString( Style, controlBounds, text.Substring(0, control.CaretPosition) ); // TODO: Renderer should query the size of the control's frame // Otherwise text will be visible over the frame, which might look bad // if a skin uses a frame wider than 2 pixels or in a different color // than the text. while(stringSize.Width + left > controlBounds.Width) { left -= controlBounds.Width / 10.0f; } } // Draw the text into the input box controlBounds.X += left; graphics.DrawString(Style, controlBounds, control.Text); // If the input box is in focus, also draw the caret so the user knows // where characters will be inserted into the text. if(control.HasFocus) { if(control.MillisecondsSinceLastCaretMovement % 500 < 250) { graphics.DrawCaret( "input.normal", controlBounds, control.Text, control.CaretPosition ); } } } // Let the control know that we can provide it with additional informations // about how its text is being rendered control.OpeningLocator = this; this.graphics = graphics; } /// /// Calculates which opening between two letters is closest to a position /// /// /// Boundaries of the control, should be in absolute coordinates /// /// Text in which the opening will be looked for /// /// Position to which the closest opening will be found, /// should be in absolute coordinates /// /// The index of the opening closest to the provided position public int GetClosestOpening( RectangleF bounds, string text, Vector2 position ) { return this.graphics.GetClosestOpening("input.normal", bounds, text, position); } // TODO: Find a better solution than remembering the graphics interface here // Otherwise the renderer could try to renderer when no frame is being drawn. // Also, the renderer makes the assumption that all drawing happens through // one graphics interface only. /// Graphics interface we used for the last draw call private IFlatGuiGraphics graphics; } } // namespace Nuclex.UserInterface.Visuals.Flat.Renderers