/// <summary> /// Draws the margin at the given position. /// </summary> /// <param name="displayContext">The display context.</param> /// <param name="renderContext">The render context.</param> /// <param name="lineIndex">The line index being rendered.</param> /// <param name="point">The point of the specific line number.</param> /// <param name="height">The height of the rendered line.</param> /// <param name="lineBlockStyle">The line block style.</param> public abstract void Draw( IDisplayContext displayContext, IRenderContext renderContext, int lineIndex, PointD point, double height, LineBlockStyle lineBlockStyle);
/// <summary> /// Draws the margins at the given position. /// </summary> /// <param name="displayContext">The display context.</param> /// <param name="renderContext">The render context.</param> /// <param name="lineIndex">The line index being rendered.</param> /// <param name="point">The point of the specific line number.</param> /// <param name="height">The height of the rendered line.</param> /// <param name="lineBlockStyle">The line block style.</param> public void Draw( IDisplayContext displayContext, IRenderContext renderContext, int lineIndex, PointD point, double height, LineBlockStyle lineBlockStyle) { // Go through the margins and draw each one so they don't overlap. double dx = point.X; foreach (MarginRenderer marginRenderer in this) { // If it isn't visible, then we do nothing. if (!marginRenderer.Visible) { continue; } // Draw out the individual margin. marginRenderer.Draw( displayContext, renderContext, lineIndex, new PointD(dx, point.Y), height, lineBlockStyle); // Add to the x coordinate so we don't overlap the renders. dx += marginRenderer.Width; } }
public LineBlockStyle GetLineStyle( LinePosition linePosition, LineContexts lineContexts = LineContexts.None) { int lineIndex = linePosition.GetLineIndex(LineBuffer); LineBlockStyle results = GetLineStyle(lineIndex, lineContexts); return(results); }
/// <summary> /// Gets the line X coordinates from either the state if we have one /// or calculate it from the buffer position's X coordinate. /// </summary> /// <param name="controller">The action context.</param> /// <param name="wrappedLine">The wrapped line.</param> /// <param name="position">The position.</param> /// <returns></returns> private static int GetLineX( EditorViewController controller, LayoutLine wrappedLine, TextPosition position) { int lineX; var state = controller.States.Get <VerticalMovementActionState>(); if (state == null) { // Calculate the line state from the caret position. The cursor // is always to the left of the character unless we're at the // end, and then it's considered trailing of the previous // character. LineBuffer lineBuffer = controller.DisplayContext.LineBuffer; int lineIndex = position.LinePosition.GetLineIndex(lineBuffer.LineCount); string lineText = lineBuffer.GetLineText(lineIndex); int characterIndex = position.CharacterPosition.GetCharacterIndex(lineText); bool trailing = false; if (characterIndex == lineText.Length && lineText.Length > 0) { characterIndex--; trailing = true; } // Because Pango works with UTF-8-based indexes, we need to // convert the C# character index into that index to properly // identify the character. characterIndex = NormalizeEmptyStrings(lineText, characterIndex); int unicodeIndex = PangoUtility.TranslateStringToPangoIndex( lineText, characterIndex); lineX = wrappedLine.IndexToX(unicodeIndex, trailing); // We need the line's style since it may have left passing // which will change our columns. LineBlockStyle style = controller.DisplayContext.Renderer.GetLineStyle( lineIndex, LineContexts.CurrentLine); var pixelPadding = (int)style.Padding.Left.GetValueOrDefault(0); lineX += Units.FromPixels(pixelPadding); // Save a new state into the states. state = new VerticalMovementActionState(lineX); controller.States.Add(state); } else { // Get the line coordinate from the state. lineX = state.LayoutLineX; } return(lineX); }
/// <summary> /// Gets the buffer position from a given point. /// </summary> /// <param name="widgetPoint">The widget point.</param> /// <param name="displayContext">The display context.</param> /// <returns></returns> public static TextPosition GetTextPosition( PointD widgetPoint, EditorViewController controller) { IDisplayContext displayContext = controller.DisplayContext; double y = widgetPoint.Y + displayContext.BufferOffsetY; int lineIndex = displayContext.Renderer.GetLineLayoutRange(y); Layout layout = displayContext.Renderer.GetLineLayout( lineIndex, LineContexts.None); // Shift the buffer-relative coordinates to layout-relative coordinates. double layoutY = y; if (lineIndex > 0) { layoutY -= displayContext.Renderer.GetLineLayoutHeight(0, lineIndex - 1); } int pangoLayoutY = Units.FromPixels((int)layoutY); // Shift the buffer-relative coordinates to handle padding. LineBlockStyle style = displayContext.Renderer.GetLineStyle( lineIndex, LineContexts.None); double layoutX = widgetPoint.X - style.Left; // Determines where in the layout is the point. int pangoLayoutX = Units.FromPixels((int)layoutX); int unicodeIndex; int trailing; layout.XyToIndex(pangoLayoutX, pangoLayoutY, out unicodeIndex, out trailing); // When dealing with UTF-8 characters, we have to convert the // Unicode index into a C# index. string lineText = displayContext.LineBuffer.GetLineText(lineIndex); unicodeIndex = NormalizeEmptyStrings(lineText, unicodeIndex); int characterIndex = PangoUtility.TranslatePangoToStringIndex( lineText, unicodeIndex); // If the source text is empty, then we disable the trailing. if (lineText.Length == 0) { trailing = 0; } // Return the buffer position. return(new TextPosition(lineIndex, characterIndex + trailing)); }
/// <summary> /// Gets the height of a single line layout. /// </summary> /// <param name="lineIndex">The line.</param> /// <returns></returns> private int GetLineLayoutHeight(int lineIndex) { // Get the extents for the line while rendered. Layout lineLayout = GetLineLayout(lineIndex, LineContexts.None); int lineWidth, lineHeight; lineLayout.GetPixelSize(out lineWidth, out lineHeight); // Get the style to include the style's height. LineBlockStyle style = GetLineStyle(lineIndex, LineContexts.None); lineHeight += (int)Math.Ceiling(style.Height); // Return the resulting height. return(lineHeight); }
private static int GetLeftPaddingPixels( EditorViewController controller, int lineIndex) { // Get the style for the given line. LineBlockStyle style = controller.DisplayContext.Renderer.GetLineStyle( lineIndex, LineContexts.CurrentLine); if (style == null) { return(0); } var pixelPadding = (int)style.Padding.Left.GetValueOrDefault(0); return(pixelPadding); }
/// <summary> /// Caches information about a line into the cached line. /// </summary> /// <param name="view">The view.</param> /// <param name="line">The line.</param> public void Cache( EditorViewRenderer view, int line) { // If we already have a layout, we don't need to do anything. if (Layout != null) { return; } // Cache various elements of the rendering. This is an expensive // operation, so we want to minimize it. Layout layout = view.GetLineLayout(line, LineContexts.None); LineBlockStyle style = view.GetLineStyle(line, LineContexts.None); Style = style; Layout = layout; Height = (int)(layout.GetPixelHeight() + style.Height); }
/// <summary> /// Gets the line layout for a given line. /// </summary> /// <param name="lineIndex">The line.</param> /// <param name="lineContexts">The line contexts.</param> /// <returns></returns> public virtual Layout GetLineLayout( int lineIndex, LineContexts lineContexts) { // Get the layout. var layout = new Layout(DisplayContext.PangoContext); // Assign the given style to the layout. LineBlockStyle style = GetLineStyle(lineIndex, lineContexts); DisplayContext.SetLayout(layout, style, DisplayContext.TextWidth); // Set the markup and return. string markup = GetSelectionMarkup(lineIndex, lineContexts); string coloredMarkup = DrawingUtility.WrapColorMarkup( markup, style.GetForegroundColor()); layout.SetMarkup(coloredMarkup); return(layout); }
/// <summary> /// Gets the region that the caret would be drawn in. /// </summary> /// <returns></returns> public Rectangle GetDrawRegion() { // Get the coordinates on the screen and the height of the current line. int lineHeight; PointD point = Position.ToScreenCoordinates(displayContext, out lineHeight); double x = point.X; double y = point.Y; // Translate the buffer coordinates into the screen visible coordinates. y -= displayContext.BufferOffsetY; // Shift the contents to compenstate for the margins. LineBlockStyle style = displayContext.Renderer.GetLineStyle(Position.LinePosition); x += displayContext.TextX; x += style.Left; // Return the resulting rectangle. return(new Rectangle(x, y, 1, lineHeight)); }
/// <summary> /// Configures the theme for all the elements used in the demo. /// </summary> private void SetupTheme() { // Grab the theme. Theme theme = editorView.Theme; // Set up the indicator styles. theme.IndicatorStyles["Error"] = new IndicatorStyle( "Error", 100, new Color(1, 0, 0)); theme.IndicatorStyles["Warning"] = new IndicatorStyle( "Warning", 10, new Color(1, 165 / 255.0, 0)); theme.IndicatorRenderStyle = IndicatorRenderStyle.Ratio; theme.IndicatorPixelHeight = 2; theme.IndicatorRatioPixelGap = 1; var indicatorBackgroundStyle = new RegionBlockStyle(); indicatorBackgroundStyle.BackgroundColor = new Color(1, 0.9, 1); //indicatorBackgroundStyle.Borders.SetBorder(new Border(1, new Color(0.5, 0, 0))); theme.RegionStyles[IndicatorView.BackgroundRegionName] = indicatorBackgroundStyle; var indicatorVisibleStyle = new RegionBlockStyle(); indicatorVisibleStyle.BackgroundColor = new Color(1, 1, 0.9); indicatorVisibleStyle.Borders.SetBorder(new Border(1, new Color(0, 0.5, 0))); theme.RegionStyles[IndicatorView.VisibleRegionName] = indicatorVisibleStyle; // Set up the editable text styles. for (var type = DemoLineStyleType.Default; type <= DemoLineStyleType.Break; type++) { // Create a line style for this type. var lineStyle = new LineBlockStyle(theme.TextLineStyle); theme.LineStyles[type.ToString()] = lineStyle; // Custom the style based on type. switch (type) { case DemoLineStyleType.Chapter: lineStyle.FontDescription = FontDescriptionCache.GetFontDescription("Serif Bold 24"); lineStyle.Borders.Bottom = new Border(2, new Color(0, 0, 0)); lineStyle.Margins.Top = 6; lineStyle.Margins.Bottom = 6; break; case DemoLineStyleType.Heading: lineStyle.FontDescription = FontDescriptionCache.GetFontDescription("Sans Bold 18"); lineStyle.Padding.Left = 25; break; case DemoLineStyleType.Borders: lineStyle.Padding.Left = 15; lineStyle.Padding.Right = 15; lineStyle.Margins.Left = 10; lineStyle.Margins.Right = 10; lineStyle.Margins.Top = 10; lineStyle.Margins.Bottom = 10; lineStyle.Borders.Bottom = new Border(5, new Color(1, 0, 0)); lineStyle.Borders.Top = new Border(5, new Color(0, 1, 0)); lineStyle.Borders.Right = new Border(5, new Color(0, 0, 1)); lineStyle.Borders.Left = new Border(5, new Color(1, 0, 1)); break; case DemoLineStyleType.Default: lineStyle.Padding.Left = 50; break; case DemoLineStyleType.Break: lineStyle.Padding.Left = 50; lineStyle.Alignment = Alignment.Center; break; } } // Create the inactive header style. var inactiveHeadingStyle = new LineBlockStyle(theme.LineStyles["Heading"]); inactiveHeadingStyle.ForegroundColor = new Color(0.8, 0.8, 0.8); theme.LineStyles["Inactive Heading"] = inactiveHeadingStyle; }
/// <summary> /// Resets the cached line. /// </summary> public void Reset() { Height = 0; Style = null; Layout = null; }
/// <summary> /// Sets up the theme elements. /// </summary> /// <param name="theme"></param> public static void SetupTheme(Theme theme) { // Set up the indicator elements. SetupThemeIndicators(theme); // Use slightly more muted colors for the current line. theme.RegionStyles["EditorViewCurrentLine"].BackgroundColor = new Color( 1, 1, 1); theme.RegionStyles["EditorViewCurrentWrappedLine"].BackgroundColor = new Color(245 / 255.0, 245 / 255.0, 220 / 255.0); // Set up the paragraph style. var paragraphyStyle = new LineBlockStyle(theme.TextLineStyle) { FontDescription = FontDescriptionCache.GetFontDescription("Source Code Pro 16"), Margins = { Top = 8, Bottom = 8 } }; // Set up the chapter style. var chapterStyle = new LineBlockStyle(theme.TextLineStyle) { FontDescription = FontDescriptionCache.GetFontDescription("Source Code Pro Bold 32"), Margins = { Bottom = 5 }, Borders = { Bottom = new Border(2, new Color(0, 0, 0)) } }; // Set up the scene style. var sceneStyle = new LineBlockStyle(theme.TextLineStyle) { FontDescription = FontDescriptionCache.GetFontDescription("Source Code Pro Italic 24"), ForegroundColor = new Color(.5, .5, .5) }; // Set up the epigraph style. var epigraphStyle = new LineBlockStyle(theme.TextLineStyle) { FontDescription = FontDescriptionCache.GetFontDescription("Source Code Pro 12"), Padding = { Left = 20 } }; // Set up the epigraph attributation style. var epigraphAttributationStyle = new LineBlockStyle(theme.TextLineStyle) { FontDescription = FontDescriptionCache.GetFontDescription("Source Code Pro Italic 12"), Padding = { Left = 20 } }; // Add all the styles into the theme. theme.LineStyles[BlockTypeSupervisor.ParagraphName] = paragraphyStyle; theme.LineStyles[BlockTypeSupervisor.ChapterName] = chapterStyle; theme.LineStyles[BlockTypeSupervisor.SceneName] = sceneStyle; theme.LineStyles[BlockTypeSupervisor.EpigraphName] = epigraphStyle; theme.LineStyles[BlockTypeSupervisor.EpigraphAttributionName] = epigraphAttributationStyle; }
/// <summary> /// Converts the given line and character coordinates into pixel coordinates /// on the display. /// </summary> /// <param name="bufferPosition">The buffer position.</param> /// <param name="displayContext">The display context.</param> /// <param name="lineHeight">Will contains the height of the current line.</param> /// <returns></returns> public static PointD ToScreenCoordinates( this TextPosition bufferPosition, IDisplayContext displayContext, out int lineHeight) { // Get the line index, which needs to be a number in range. EditorViewRenderer buffer = displayContext.Renderer; int lineIndex = bufferPosition.LinePosition.GetLineIndex(displayContext.LineBuffer); // Pull out some of the common things we'll be using in this method. int bufferLineIndex = buffer.LineBuffer.NormalizeLineIndex(lineIndex); Layout layout = buffer.GetLineLayout( bufferLineIndex, LineContexts.Unformatted); LineBlockStyle style = buffer.GetLineStyle( bufferLineIndex, LineContexts.Unformatted); // Figure out the top of the current line in relation to the entire // buffer and view. For lines beyond the first, we use // GetLineLayoutHeight because it also takes into account the line // spacing and borders which we would have to calculate otherwise. double y = bufferLineIndex == 0 ? 0 : buffer.GetLineLayoutHeight(0, bufferLineIndex - 1); // Add the style offset for the top-padding. y += style.Top; // The cursor position code uses Unicode instead of C# character // positions. This means we have to advance more than just one // value to calculate it. This actually uses UTF-8 encoding to // calculate the indexes. string lineText = displayContext.LineBuffer.GetLineText(lineIndex); int characterIndex = bufferPosition.GetCharacterIndex(displayContext.LineBuffer); int unicodeCharacter = PangoUtility.TranslateStringToPangoIndex( lineText, characterIndex); // We need to figure out the relative position. If the position equals // the length of the string, we want to put the caret at the end of the // character. Otherwise, we put it on the front of the character to // indicate insert point. bool trailing = false; int lineLength = buffer.LineBuffer.GetLineLength( bufferLineIndex, LineContexts.Unformatted); if (unicodeCharacter == lineLength) { // Shift back one character to calculate the position and put // the cursor at the end of the character. unicodeCharacter--; trailing = true; } // Figure out which wrapped line we are actually on and the position // inside that line. If the character equals the length of the string, // then we want to move to the end of it. int wrappedLineIndex; int layoutX; layout.IndexToLineX( unicodeCharacter, trailing, out wrappedLineIndex, out layoutX); // Get the relative offset into the wrapped lines. Rectangle layoutPoint = layout.IndexToPos(unicodeCharacter); y += Units.ToPixels(layoutPoint.Y); // Get the height of the wrapped line. Rectangle ink = Rectangle.Zero; Rectangle logical = Rectangle.Zero; layout.Lines[wrappedLineIndex].GetPixelExtents(ref ink, ref logical); lineHeight = logical.Height; // Return the results. return(new PointD(Units.ToPixels(layoutX), y)); }
/// <summary> /// Draws the margin at the given position. /// </summary> /// <param name="displayContext">The display context.</param> /// <param name="renderContext">The render context.</param> /// <param name="lineIndex">The line index being rendered.</param> /// <param name="point">The point of the specific line number.</param> /// <param name="height">The height of the rendered line.</param> /// <param name="lineBlockStyle"></param> public override void Draw( IDisplayContext displayContext, IRenderContext renderContext, int lineIndex, PointD point, double height, LineBlockStyle lineBlockStyle) { // Figure out the style we need to use. MarginBlockStyle style = lineBlockStyle.MarginStyles.Get(Theme.LineNumberStyle); // Create a layout object if we don't have one. if (layout == null) { layout = new Layout(displayContext.PangoContext); displayContext.SetLayout(layout, style, Width); } // Figure out the line number. string lineNumber = displayContext.LineBuffer.GetLineNumber(lineIndex); if (string.IsNullOrEmpty(lineNumber)) { lineNumber = String.Empty; } // Wrap the text in a markup that includes the foreground color. string markup = DrawingUtility.WrapColorMarkup( lineNumber, style.GetForegroundColor()); layout.SetMarkup(markup); // Figure out if the current width of the margin is wider than what // we've already calculated. if (lineNumber.Length > maximumCharactersRendered) { // Get the new width as if we don't have line-wrapping. int layoutWidth, layoutHeight; layout.Width = Int32.MaxValue; layout.GetSize(out layoutWidth, out layoutHeight); // Set the layout width so we don't have to redo the entire // layout and update our margin width (including style). layout.Width = layoutWidth; SetWidth((int)(Units.ToPixels(layoutWidth) + style.Width)); // Since we are looking at a large length of line number, update // it so we don't continually calculate the line width. maximumCharactersRendered = lineNumber.Length; // Request a full redraw since this will change even the lines // we already drew. We draw the line anyways to avoid seeing a // white block. displayContext.RequestScrollToCaret(); displayContext.RequestRedraw(); } // Use the common drawing routine to handle the borders and padding. DrawingUtility.DrawLayout( displayContext, renderContext, new Rectangle(point.X, point.Y, Width, height), layout, style); }
/// <summary> /// Called when the widget is exposed or drawn. /// </summary> /// <param name="e">The e.</param> /// <returns></returns> protected override bool OnExposeEvent(EventExpose e) { // Figure out the area we are rendering into. Gdk.Rectangle area = e.Region.Clipbox; var cairoArea = new Rectangle(area.X, area.Y, area.Width, area.Height); using (Context cairoContext = CairoHelper.Create(e.Window)) { // Create a render context. var renderContext = new RenderContext(cairoContext); renderContext.RenderRegion = cairoArea; // If we don't have a buffer at this point, don't render anything. if (Renderer == null || LineBuffer == null) { return(true); } // Paint the background color of the window. RegionBlockStyle backgroundStyle = Theme.RegionStyles[Theme.BackgroundRegionStyleName]; DrawingUtility.DrawLayout(this, renderContext, cairoArea, backgroundStyle); // Reset the layout and its properties. Renderer.Width = area.Width - margins.Width; // Figure out the viewport area we'll be drawing. int offsetY = 0; if (verticalAdjustment != null) { offsetY += (int)verticalAdjustment.Value; } var viewArea = new Rectangle( area.X, area.Y + offsetY, area.Width, area.Height); // Determine the line range visible in the given area. int startLine, endLine; Renderer.GetLineLayoutRange(viewArea, out startLine, out endLine); // Determine where the first line actually starts. int startLineY = 0; if (startLine > 0) { startLineY = Renderer.GetLineLayoutHeight(0, startLine - 1); } // Go through the lines and draw each one in the correct position. double currentY = startLineY - offsetY; for (int lineIndex = startLine; lineIndex <= endLine; lineIndex++) { // Figure out if we are on the current line. var lineContexts = LineContexts.None; bool currentLine = false; if (lineIndex == caret.Position.LinePosition) { // Add the curent line to the context. lineContexts |= LineContexts.CurrentLine; currentLine = true; } // Pull out the layout and style since we'll use it. Layout layout = Renderer.GetLineLayout(lineIndex, lineContexts); LineBlockStyle style = Renderer.GetLineStyle(lineIndex, lineContexts); // Get the extents for that line. int layoutWidth, layoutHeight; layout.GetPixelSize(out layoutWidth, out layoutHeight); // Figure out the height of the line including padding. double height = layoutHeight + style.Height; if (currentLine) { // If we have a full-line background color, display it. RegionBlockStyle currentLineStyle = Theme.RegionStyles[Theme.CurrentLineRegionStyleName]; if (currentLineStyle != null) { var lineArea = new Rectangle(TextX, currentY, TextWidth, height); DrawingUtility.DrawLayout( this, renderContext, lineArea, currentLineStyle); } // If we have a wrapped line background color, draw it. RegionBlockStyle currentWrappedLineStyle = Theme.RegionStyles[Theme.CurrentWrappedLineRegionStyleName]; if (currentWrappedLineStyle != null) { // Get the wrapped line for the caret's position. LayoutLine wrappedLine = caret.Position.GetWrappedLine(this); Pango.Rectangle wrappedLineExtents; wrappedLine.GetPixelExtents(out wrappedLineExtents); // Draw the current wrapped line index. var wrappedLineArea = new Rectangle( TextX, currentY + wrappedLineExtents.Y + style.Top, TextWidth, wrappedLineExtents.Height); DrawingUtility.DrawLayout( this, renderContext, wrappedLineArea, currentWrappedLineStyle); } } // Draw the current line along with wrapping and padding. DrawingUtility.DrawLayout( this, renderContext, new Rectangle(TextX, currentY, TextWidth, height), layout, style); // Render out the margin renderers. margins.Draw( this, renderContext, lineIndex, new PointD(0, currentY), height, style); // Move down a line. currentY += height; } // Draw the caret on the screen, but only if we have focus. if (IsFocus) { caret.Draw(renderContext); } // Show the scroll region, if requested. if (editorViewSettings.ShowScrollPadding) { cairoContext.Color = new Color(1, 0.5, 0.5); cairoContext.Rectangle(scrollPaddingRegion); cairoContext.Stroke(); } } return(true); }