//------------------------------------------------------ // // Protected Methods // //------------------------------------------------------ #region Protected Methods /// <summary> /// Render override to render the composition adorner here. /// </summary> protected override void OnRender(DrawingContext drawingContext) { // Get the matrix from AdornedElement to the visual parent to get the transformed // start/end point Visual parent2d = VisualTreeHelper.GetParent(this.AdornedElement) as Visual; if (parent2d == null) { return; } GeneralTransform transform = AdornedElement.TransformToAncestor(parent2d); if (transform == null) { return; } // Please note that we do the highlight adornment for the CONVERTED text only // for Simplified Chinese IMEs. Doing this uniformly across all IMEs wasnt possible // because it was noted that some of them (viz. Japanese) werent being consistent // about this attribute. bool isChinesePinyin = chinesePinyin.Equals(InputLanguageManager.Current.CurrentInputLanguage.IetfLanguageTag); // Render the each of the composition string attribute from the attribute ranges. for (int i = 0; i < _attributeRanges.Count; i++) { DoubleCollection dashArray; // Get the composition attribute range from the attribute range lists AttributeRange attributeRange = (AttributeRange)_attributeRanges[i]; // Skip the rendering composition lines if the composition line doesn't exist. if (attributeRange.CompositionLines.Count == 0) { continue; } // Set the line bold and squiggle bool lineBold = attributeRange.TextServicesDisplayAttribute.IsBoldLine ? true : false; bool squiggle = false; bool hasVirtualSelection = (attributeRange.TextServicesDisplayAttribute.AttrInfo & UnsafeNativeMethods.TF_DA_ATTR_INFO.TF_ATTR_TARGET_CONVERTED) != 0; Brush selectionBrush = null; double selectionOpacity = -1; Pen selectionPen = null; if (isChinesePinyin && hasVirtualSelection) { DependencyObject owner = _textView.TextContainer.Parent; selectionBrush = (Brush)owner.GetValue(TextBoxBase.SelectionBrushProperty); selectionOpacity = (double)owner.GetValue(TextBoxBase.SelectionOpacityProperty); } // Set the line height and cluse gap value that base on the ratio of text height double height = attributeRange.Height; double lineHeight = height * (lineBold ? BoldLineHeightRatio : NormalLineHeightRatio); double clauseGap = height * ClauseGapRatio; // Create Pen for drawing the composition lines with the specified line color Pen pen = new Pen(new SolidColorBrush(Colors.Black), lineHeight); // Set the pen style that based on IME's composition line style switch (attributeRange.TextServicesDisplayAttribute.LineStyle) { case UnsafeNativeMethods.TF_DA_LINESTYLE.TF_LS_DOT: // Add the dot length and specify the start/end line cap as the round dashArray = new DoubleCollection(); dashArray.Add(DotLength); dashArray.Add(DotLength); pen.DashStyle = new DashStyle(dashArray, 0); pen.DashCap = System.Windows.Media.PenLineCap.Round; pen.StartLineCap = System.Windows.Media.PenLineCap.Round; pen.EndLineCap = System.Windows.Media.PenLineCap.Round; // Update the line height for the dot line. Dot line will be more thickness than // other line to show it clearly. lineHeight = height * (lineBold ? BoldDotLineHeightRatio : NormalDotLineHeightRatio); break; case UnsafeNativeMethods.TF_DA_LINESTYLE.TF_LS_DASH: double dashLength = height * (lineBold ? BoldDashRatio : NormalDashRatio); double dashGapLength = height * (lineBold ? BoldDashGapRatio : NormalDashGapRatio); // Add the dash and dash gap legth dashArray = new DoubleCollection(); dashArray.Add(dashLength); dashArray.Add(dashGapLength); pen.DashStyle = new DashStyle(dashArray, 0); pen.DashCap = System.Windows.Media.PenLineCap.Round; pen.StartLineCap = System.Windows.Media.PenLineCap.Round; pen.EndLineCap = System.Windows.Media.PenLineCap.Round; break; case UnsafeNativeMethods.TF_DA_LINESTYLE.TF_LS_SOLID: pen.StartLineCap = System.Windows.Media.PenLineCap.Round; pen.EndLineCap = System.Windows.Media.PenLineCap.Round; break; case UnsafeNativeMethods.TF_DA_LINESTYLE.TF_LS_SQUIGGLE: squiggle = true; break; } double halfLineHeight = lineHeight / 2; // Draw the each of the composition line for (int j = 0; j < attributeRange.CompositionLines.Count; j++) { CompositionLine compositionLine = (CompositionLine)attributeRange.CompositionLines[j]; // Get the start/end point for composition adorner. // Currently Text doesn't aware of the spaceroom for the drawing of the composition // adorner(like as normal/bold dot/line/squggle), so we should draw the composition adorners // to the closest area of the bottom text. Point startPoint = new Point(compositionLine.StartPoint.X + clauseGap, compositionLine.StartPoint.Y - halfLineHeight); Point endPoint = new Point(compositionLine.EndPoint.X - clauseGap, compositionLine.EndPoint.Y - halfLineHeight); // Apply composition line color which is actually the foreground of text as well pen.Brush = new SolidColorBrush(compositionLine.LineColor); // Apply matrix to start/end point // transform.TryTransform(startPoint, out startPoint); transform.TryTransform(endPoint, out endPoint); if (isChinesePinyin && hasVirtualSelection) { Rect rect = Rect.Union(compositionLine.StartRect, compositionLine.EndRect); rect = transform.TransformBounds(rect); drawingContext.PushOpacity(selectionOpacity); drawingContext.DrawRectangle(selectionBrush, selectionPen, rect); drawingContext.Pop(); } if (squiggle) { // Draw the squiggle line with using of the PathFigure and DrawGemetry. // We may revisit this logic to render the smooth squiggle line. Point pathPoint = new Point(startPoint.X, startPoint.Y - halfLineHeight); double squiggleGap = halfLineHeight; PathFigure pathFigure = new PathFigure(); pathFigure.StartPoint = pathPoint; int indexPoint = 0; while (indexPoint < ((endPoint.X - startPoint.X) / (squiggleGap))) { if (indexPoint % 4 == 0 || indexPoint % 4 == 3) { pathPoint = new Point(pathPoint.X + squiggleGap, pathPoint.Y + halfLineHeight); pathFigure.Segments.Add(new LineSegment(pathPoint, true)); } else if (indexPoint % 4 == 1 || indexPoint % 4 == 2) { pathPoint = new Point(pathPoint.X + squiggleGap, pathPoint.Y - halfLineHeight); pathFigure.Segments.Add(new LineSegment(pathPoint, true)); } indexPoint++; } PathGeometry pathGeometry = new PathGeometry(); pathGeometry.Figures.Add(pathFigure); // Draw the composition line with the squiggle drawingContext.DrawGeometry(null, pen, pathGeometry); } else { drawingContext.DrawLine(pen, startPoint, endPoint); } } } }