/// <summary> /// Calculates the parameters with which to draw the selection. /// </summary> private void UpdateSelection() { if (View == null || textLayoutStream.Count == 0 || !selectionPosition.HasValue || pendingTextLayout) return; if (SelectionLength == 0) { selectionLineStart = 0; selectionLineCount = 0; selectionTop = default(Ultraviolet.Rectangle); selectionBottom = default(Ultraviolet.Rectangle); } else { var selectionStart = SelectionStart; var selectionStartLineInfo = default(LineInfo); var selectionStartGlyphBounds = View.Resources.TextRenderer.GetGlyphBounds(textLayoutStream, selectionStart, out selectionStartLineInfo, true); var selectionEnd = SelectionStart + (SelectionLength - 1); var selectionEndLineInfo = default(LineInfo); var selectionEndGlyphBounds = View.Resources.TextRenderer.GetGlyphBounds(textLayoutStream, selectionEnd, out selectionEndLineInfo, true); selectionLineStart = selectionStartLineInfo.LineIndex; selectionLineCount = 1 + (selectionEndLineInfo.LineIndex - selectionStartLineInfo.LineIndex); // Top var selectionTopX = selectionStartGlyphBounds.X; var selectionTopY = selectionStartGlyphBounds.Y; var selectionTopWidth = (selectionLineCount == 1) ? selectionEndGlyphBounds.Right - selectionStartGlyphBounds.Left : selectionStartLineInfo.Width - (selectionStartGlyphBounds.X - selectionStartLineInfo.X); var selectionTopHeight = selectionStartLineInfo.Height; selectionTop = new Ultraviolet.Rectangle(selectionTopX, selectionTopY, selectionTopWidth, selectionTopHeight); // Bottom if (selectionLineCount > 1) { var selectionBottomX = selectionEndLineInfo.X; var selectionBottomY = selectionEndGlyphBounds.Y; var selectionBottomWidth = selectionEndGlyphBounds.Right - selectionBottomX; var selectionBottomHeight = selectionEndLineInfo.Height; selectionBottom = new Ultraviolet.Rectangle(selectionBottomX, selectionBottomY, selectionBottomWidth, selectionBottomHeight); } } }
/// <summary> /// Scrolls to ensure that the caret is within the viewport. /// </summary> private void ScrollToCaret(Boolean showMaximumLineWidth, Boolean jumpLeft, Boolean jumpRight) { var scrollViewer = Parent as ScrollViewer; if (scrollViewer == null) return; var boundsViewport = new RectangleD(scrollViewer.HorizontalOffset, scrollViewer.VerticalOffset, scrollViewer.ViewportWidth, scrollViewer.ViewportHeight); var boundsCaretPixs = new Ultraviolet.Rectangle(caretBounds.X, caretBounds.Y, caretRenderBounds.Width, caretBounds.Height); var boundsCaretDips = Display.PixelsToDips(boundsCaretPixs); var isHorizontalScrollingNecessary = (boundsCaretDips.Left < boundsViewport.Left || boundsCaretDips.Right > boundsViewport.Right); var isVerticalScrollingNecessary = (boundsCaretDips.Top < boundsViewport.Top || boundsCaretDips.Bottom > boundsViewport.Bottom); if (!isHorizontalScrollingNecessary && !isVerticalScrollingNecessary) return; if (isVerticalScrollingNecessary) { if (boundsCaretDips.Top < boundsViewport.Top) { var verticalOffset = boundsCaretDips.Top; scrollViewer.ScrollToVerticalOffset(verticalOffset); } else { var verticalOffset = (boundsCaretDips.Bottom - boundsViewport.Height); scrollViewer.ScrollToVerticalOffset(verticalOffset); } } if (showMaximumLineWidth) { var owner = TemplatedParent as Control; var alignment = (owner == null) ? TextAlignment.Left : owner.GetValue<TextAlignment>(TextBox.TextAlignmentProperty); var horizontalOffset = (alignment == TextAlignment.Right) ? boundsCaretDips.Left : boundsCaretDips.Right - boundsViewport.Width; scrollViewer.ScrollToHorizontalOffset(horizontalOffset); } else { if (isHorizontalScrollingNecessary) { if (boundsCaretDips.Left < boundsViewport.Left) { var horizontalOffset = boundsCaretDips.Left - (jumpLeft ? (boundsViewport.Width / 3.0) : 0); scrollViewer.ScrollToHorizontalOffset(horizontalOffset); } else { var horizontalOffset = (boundsCaretDips.Right - boundsViewport.Width) + (jumpRight ? (boundsViewport.Width / 3.0) : 0); scrollViewer.ScrollToHorizontalOffset(horizontalOffset); } } } }
/// <summary> /// Calculates the position at which to draw the text caret. /// </summary> private void UpdateCaret() { if (View == null || pendingTextLayout) return; var owner = TemplatedParent as Control; var isReadOnly = (owner != null && owner.GetValue<Boolean>(TextBox.IsReadOnlyProperty)); var fontFace = TextFontFace; var fontLineSpacing = (fontFace != null) ? fontFace.LineSpacing : 0; var fontLineSpacingHalf = (Int32)Math.Ceiling(fontLineSpacing / 2f); var styledCaretWidth = (Int32)Display.DipsToPixels(CaretWidth); var styledCaretThickness = (Int32)Display.DipsToPixels(CaretThickness); var caretAlignment = TextAlignment.Left; var caretRenderX = 0; var caretRenderY = 0; var caretRenderWidth = 0; var caretRenderHeight = 0; if (caretInsertionMode == CaretMode.Overwrite && !IsLineBreak(caretPosition) && !isReadOnly) { actualInsertionMode = CaretMode.Overwrite; var caretDefaultSize = GetDefaultSizeOfOverwriteCaret(); var caretX = 0; var caretY = 0; var caretWidth = caretDefaultSize.Width; var caretHeight = caretDefaultSize.Height; if (textLayoutStream.TotalLength > 0) { var glyphLineInfo = default(LineInfo); var glyphBounds = View.Resources.TextRenderer.GetGlyphBounds(textLayoutStream, caretPosition, out glyphLineInfo, true); caretX = glyphBounds.Left; caretY = glyphBounds.Top; caretWidth = glyphBounds.Width; caretBounds = new Ultraviolet.Rectangle(caretX, caretY, caretWidth, glyphBounds.Height); caretLineIndex = glyphLineInfo.LineIndex; } else { caretAlignment = (owner == null) ? TextAlignment.Left : owner.GetValue<TextAlignment>(TextBox.TextAlignmentProperty); caretBounds = new Ultraviolet.Rectangle(caretX, caretY, caretWidth, fontLineSpacing); caretLineIndex = 0; } caretRenderWidth = caretWidth; caretRenderHeight = Math.Min(caretBounds.Height, styledCaretThickness); caretRenderY = caretBounds.Bottom - caretRenderHeight; } else { actualInsertionMode = CaretMode.Insert; var caretDefaultSize = GetDefaultSizeOfInsertionCaret(); var caretX = 0; var caretY = 0; var caretWidth = caretDefaultSize.Width; var caretHeight = caretDefaultSize.Height; if (textLayoutStream.TotalLength > 0) { var lineInfo = default(LineInfo); var boundsGlyph = default(Ultraviolet.Rectangle?); var boundsInsert = View.Resources.TextRenderer.GetInsertionPointBounds(textLayoutStream, caretPosition, out lineInfo, out boundsGlyph); caretX = boundsInsert.X; caretY = boundsInsert.Y; caretWidth = (boundsGlyph.HasValue && boundsGlyph.Value.Width > 0) ? boundsGlyph.Value.Width : fontLineSpacingHalf; caretHeight = boundsInsert.Height; caretBounds = new Ultraviolet.Rectangle(caretX, caretY, caretWidth, caretHeight); caretLineIndex = lineInfo.LineIndex; } else { caretAlignment = (owner == null) ? TextAlignment.Left : owner.GetValue<TextAlignment>(TextBox.TextAlignmentProperty); caretBounds = new Ultraviolet.Rectangle(caretX, caretY, caretWidth, caretHeight); caretLineIndex = 0; } caretRenderWidth = Math.Min(caretBounds.Width, styledCaretWidth); caretRenderHeight = caretBounds.Height; caretRenderY = caretBounds.Top; } switch (caretAlignment) { case TextAlignment.Left: caretRenderX = caretBounds.Left; break; case TextAlignment.Center: caretRenderX = caretBounds.Center.X - (caretRenderWidth / 2); break; case TextAlignment.Right: caretRenderX = (caretBounds.Right - caretRenderWidth); break; } caretRenderBounds = new Ultraviolet.Rectangle(caretRenderX, caretRenderY, caretRenderWidth, caretRenderHeight); }
/// <summary> /// Draws the current selection. /// </summary> private void DrawSelection(UltravioletTime time, DrawingContext dc) { if (!selectionPosition.HasValue || SelectionLength == 0) return; // Don't draw the selection unless we have keyboard focus (unless IsInactiveSelectionHighlightEnabled is true). var owner = TemplatedParent as Control; if (owner == null) return; var isActive = owner.IsKeyboardFocusWithin; var isEnabledIfInactive = owner.GetValue<Boolean>(TextBox.IsInactiveSelectionHighlightEnabledProperty); if (!isActive && !isEnabledIfInactive) return; var selectionColor = isActive ? SelectionColor : InactiveSelectionColor; // Draw the first line var selectionTopDips = Display.PixelsToDips(selectionTop); DrawImage(dc, SelectionImage, selectionTopDips, selectionColor, true); // Draw the middle if (selectionLineCount > 2) { textLayoutStream.AcquirePointers(); var lineInfo = textLayoutStream.GetLineInfo(selectionLineStart); for (int i = selectionLineStart + 1; i < selectionLineStart + selectionLineCount - 1; i++) { textLayoutStream.GetNextLineInfoRef(ref lineInfo, out lineInfo); var lineBounds = new Ultraviolet.Rectangle(lineInfo.X, lineInfo.Y, lineInfo.Width, lineInfo.Height); var lineBoundsDips = Display.PixelsToDips(lineBounds); DrawImage(dc, SelectionImage, lineBoundsDips, selectionColor, true); } textLayoutStream.ReleasePointers(); } // Draw the last line if (selectionLineCount > 1) { var selectionBottomDips = Display.PixelsToDips(selectionBottom); DrawImage(dc, SelectionImage, selectionBottomDips, selectionColor, true); } }