public void Evaluate(int SpreadMax) { if (this.FInText.IsConnected == false) { this.metricsCount.SliceCount = 0; this.FCanWrapLineAfter.SliceCount = 0; this.FIsRightToLeft.SliceCount = 0; this.FIsSoftHyphent.SliceCount = 0; this.FIsWhitespace.SliceCount = 0; this.FLength.SliceCount = 0; this.FNewLine.SliceCount = 0; this.FWidth.SliceCount = 0; return; } if (this.FInText.IsChanged) { this.metricsCount.SliceCount = SpreadMax; cm.Clear(); for (int i = 0; i < SpreadMax; i++) { TextLayout tl = this.FInText[i]; ClusterMetrics[] cms = tl.GetClusterMetrics(); this.metricsCount[i] = cms.Length; cm.AddRange(cms); } this.FCanWrapLineAfter.SliceCount = cm.Count; this.FIsRightToLeft.SliceCount = cm.Count; this.FIsSoftHyphent.SliceCount = cm.Count; this.FIsWhitespace.SliceCount = cm.Count; this.FLength.SliceCount = cm.Count; this.FNewLine.SliceCount = cm.Count; this.FWidth.SliceCount = cm.Count; for (int i = 0; i < cm.Count; i++) { ClusterMetrics c = cm[i]; this.FCanWrapLineAfter[i] = c.CanWrapLineAfter; this.FIsRightToLeft[i] = c.IsRightToLeft; this.FIsSoftHyphent[i] = c.IsSoftHyphen; this.FIsWhitespace[i] = c.IsWhitespace; this.FLength[i] = c.Length; this.FWidth[i] = c.Width; } } }
/// <summary> /// 取得字符对应的宽度 /// </summary> /// <param name="character"></param> /// <returns></returns> private float GetCharacterWidth(Character character) { DWriteFactory dwFactory = new DWriteFactory(SharpDX.DirectWrite.FactoryType.Shared); Vector2 offset = new Vector2(0.0f, 0.0f); TextFormat textFormat = new TextFormat(dwFactory, character.font.FontFamily.Name, character.font.Bold ? SharpDX.DirectWrite.FontWeight.Bold : SharpDX.DirectWrite.FontWeight.Regular, character.font.Italic ? SharpDX.DirectWrite.FontStyle.Italic : SharpDX.DirectWrite.FontStyle.Normal, character.font.Size); TextLayout textLayout = new TextLayout(dwFactory, "好" + character.content, textFormat, this.pageWidth, this.pageHeight); textLayout.SetUnderline(character.font.Underline, new TextRange(0, character.content.Length)); textLayout.SetFontStyle(character.font.Italic ? FontStyle.Italic : FontStyle.Normal, new TextRange(0, character.content.Length)); textLayout.SetFontWeight(character.font.Bold ? FontWeight.Bold : FontWeight.Normal, new TextRange(0, character.content.Length)); ClusterMetrics[] clusterMetrics = textLayout.GetClusterMetrics(); float result = 0.0f; float prefixCharWidth = clusterMetrics[0].Width; foreach (ClusterMetrics v in clusterMetrics) { result += v.Width; } dwFactory.Dispose(); textFormat.Dispose(); textLayout.Dispose(); dwFactory = null; textFormat = null; textLayout = null; return(result - prefixCharWidth); }
/// <summary> /// Sets text selection. This may possibly only move the caret, not selecting characters.</summary> /// <param name="moveMode">Text selection mode</param> /// <param name="advance">Number of characters to advance or start selection</param> /// <param name="extendSelection">Whether to extend current selection to additional selection</param> /// <param name="updateCaretFormat">Whether to update caret format based on selection</param> /// <returns>True iff caret changed position as result of selection</returns> public bool SetSelection(SelectionMode moveMode, int advance, bool extendSelection, bool updateCaretFormat) { // Moves the caret relatively or absolutely, optionally extending the // selection range (for example, when shift is held). int line = int.MaxValue; // current line number, needed by a few modes int absolutePosition = m_caretPosition + m_caretPositionOffset; int oldAbsolutePosition = absolutePosition; int oldCaretAnchor = m_caretAnchor; switch (moveMode) { case SelectionMode.Left: m_caretPosition += m_caretPositionOffset; if (m_caretPosition > 0) { --m_caretPosition; AlignCaretToNearestCluster(false, true); // special check for CR/LF pair absolutePosition = m_caretPosition + m_caretPositionOffset; if (absolutePosition >= 1 && absolutePosition < TextLayout.Text.Length && TextLayout.Text[absolutePosition - 1] == '\r' && TextLayout.Text[absolutePosition] == '\n') { m_caretPosition = absolutePosition - 1; AlignCaretToNearestCluster(false, true); } } break; case SelectionMode.Right: m_caretPosition = absolutePosition; AlignCaretToNearestCluster(true, true); // special check for CR/LF pair absolutePosition = m_caretPosition + m_caretPositionOffset; if (absolutePosition >= 1 && absolutePosition < TextLayout.Text.Length && TextLayout.Text[absolutePosition - 1] == '\r' && TextLayout.Text[absolutePosition] == '\n') { m_caretPosition = absolutePosition + 1; AlignCaretToNearestCluster(false, true); } break; case SelectionMode.LeftChar: m_caretPosition = absolutePosition; m_caretPosition -= Math.Min(advance, absolutePosition); m_caretPositionOffset = 0; break; case SelectionMode.RightChar: m_caretPosition = absolutePosition + advance; m_caretPositionOffset = 0; { // Use hit-testing to limit text position. HitTestMetrics hitTestMetrics = TextLayout.HitTestTextPosition( m_caretPosition, false); m_caretPosition = Math.Min(m_caretPosition, hitTestMetrics.TextPosition + hitTestMetrics.Length); } break; case SelectionMode.Up: case SelectionMode.Down: { // Retrieve the line metrics to figure out what line we are on. var lineMetrics = TextLayout.GetLineMetrics(); int linePosition; GetLineFromPosition(lineMetrics, m_caretPosition, out line, out linePosition); // Move up a line or down if (moveMode == SelectionMode.Up) { if (line <= 0) { break; // already top line } line--; linePosition -= lineMetrics[line].Length; if (line <= TopLine) { TopLine = TopLine - 1 >= 0 ? TopLine - 1 : 0; // scroll down a line of text } } else { linePosition += lineMetrics[line].Length; line++; if (line >= lineMetrics.Length) { break; // already bottom line } // scroll up a line of text TopLine = TopLine + 1; } // To move up or down, we need three hit-testing calls to determine: // 1. The x of where we currently are. // 2. The y of the new line. // 3. New text position from the determined x and y. // This is because the characters are variable size. float caretX, caretY; // Get x of current text position var hitTestMetrics = TextLayout.HitTestTextPosition( m_caretPosition, m_caretPositionOffset > 0 // trailing if nonzero, else leading edge ); caretX = hitTestMetrics.Point.X; // Get y of new position hitTestMetrics = TextLayout.HitTestTextPosition( linePosition, false // leading edge ); caretY = hitTestMetrics.Point.Y; // Now get text position of new x,y hitTestMetrics = TextLayout.HitTestPoint(caretX, caretY); m_caretPosition = hitTestMetrics.TextPosition; m_caretPositionOffset = hitTestMetrics.IsTrailingHit ? (hitTestMetrics.Length > 0) ? 1 : 0 : 0; } break; case SelectionMode.LeftWord: case SelectionMode.RightWord: { // To navigate by whole words, we look for the canWrapLineAfter // flag in the cluster metrics. // Now we actually read them. var clusterMetrics = TextLayout.GetClusterMetrics(); if (clusterMetrics.Length == 0) { break; } m_caretPosition = absolutePosition; int clusterPosition = 0; int oldCaretPosition = m_caretPosition; if (moveMode == SelectionMode.LeftWord) { // Read through the clusters, keeping track of the farthest valid // stopping point just before the old position. m_caretPosition = 0; m_caretPositionOffset = 0; // leading edge for (int cluster = 0; cluster < clusterMetrics.Length; ++cluster) { clusterPosition += clusterMetrics[cluster].Length; if (clusterMetrics[cluster].CanWrapLineAfter) { if (clusterPosition >= oldCaretPosition) { break; } // Update in case we pass this point next loop. m_caretPosition = clusterPosition; } } } else // SetSelectionModeRightWord { // Read through the clusters, looking for the first stopping point // after the old position. for (int cluster = 0; cluster < clusterMetrics.Length; ++cluster) { int clusterLength = clusterMetrics[cluster].Length; m_caretPosition = clusterPosition; m_caretPositionOffset = clusterLength; // trailing edge if (clusterPosition >= oldCaretPosition && clusterMetrics[cluster].CanWrapLineAfter) { break; // first stopping point after old position. } clusterPosition += clusterLength; } } } break; case SelectionMode.SingleWord: { var clusterMetrics = TextLayout.GetClusterMetrics(); if (clusterMetrics.Length == 0) { break; } // Left of word m_caretPosition = absolutePosition; int clusterPosition = 0; int oldCaretPosition = m_caretPosition; // Read through the clusters, keeping track of the farthest valid // stopping point just before the old position. m_caretPosition = 0; m_caretPositionOffset = 0; // leading edge for (int cluster = 0; cluster < clusterMetrics.Length; ++cluster) { clusterPosition += clusterMetrics[cluster].Length; if (clusterMetrics[cluster].CanWrapLineAfter) { if (clusterPosition >= oldCaretPosition) { break; } // Update in case we pass this point next loop. m_caretPosition = clusterPosition; } } int leftOfWord = m_caretPosition; // Right of word // Read through the clusters, looking for the first stopping point // after the old position. for (int cluster = 0; cluster < clusterMetrics[cluster].Length; ++cluster) { int clusterLength = clusterMetrics[cluster].Length; m_caretPosition = clusterPosition; m_caretPositionOffset = clusterLength; // trailing edge if (clusterPosition >= oldCaretPosition && clusterMetrics[cluster].CanWrapLineAfter) { break; // first stopping point after old position. } clusterPosition += clusterLength; } int rightOfWord = m_caretPosition - 1; m_caretPositionOffset = 0; m_caretAnchor = leftOfWord; //while (rightOfWord > leftOfWord) //{ // char c = TextLayout.Text[rightOfWord]; // if (!(char.IsWhiteSpace(c) || char.IsPunctuation(c))) // break; // --rightOfWord; //} m_caretPosition = rightOfWord; } break; case SelectionMode.Home: case SelectionMode.End: { // Retrieve the line metrics to know first and last position // on the current line. var lineMetrics = TextLayout.GetLineMetrics(); int linePosition; GetLineFromPosition(lineMetrics, m_caretPosition, out line, out linePosition); m_caretPosition = linePosition; m_caretPositionOffset = 0; if (moveMode == SelectionMode.End) { // Place the caret at the last character on the line, // excluding line breaks. In the case of wrapped lines, // newlineLength will be 0. int lineLength = lineMetrics[line].Length - lineMetrics[line].NewlineLength; m_caretPositionOffset = Math.Min(lineLength, 1); m_caretPosition += lineLength - m_caretPositionOffset; AlignCaretToNearestCluster(true, false); } } break; case SelectionMode.First: m_caretPosition = 0; m_caretPositionOffset = 0; break; case SelectionMode.All: m_caretAnchor = 0; extendSelection = true; goto fallthrough; case SelectionMode.Last: fallthrough: m_caretPosition = int.MaxValue; m_caretPositionOffset = 0; AlignCaretToNearestCluster(true, false); break; case SelectionMode.AbsoluteLeading: m_caretPosition = advance; m_caretPositionOffset = 0; break; case SelectionMode.AbsoluteTrailing: m_caretPosition = advance; AlignCaretToNearestCluster(true, false); break; } absolutePosition = m_caretPosition + m_caretPositionOffset; if (!extendSelection) { m_caretAnchor = absolutePosition; } bool caretMoved = (absolutePosition != oldAbsolutePosition) || (m_caretAnchor != oldCaretAnchor); if (caretMoved) { // scroll the text automatically to avoid caret lost var lineMetrics = TextLayout.GetLineMetrics(); int linePosition; GetLineFromPosition(lineMetrics, m_caretPosition, out line, out linePosition); if (line < TopLine) { TopLine = line; } float visibleLines = TextLayout.Height / lineMetrics[0].Height; if (line > TopLine + visibleLines) { TopLine = TopLine + 1; } //RectF rect; GetCaretRect(); //UpdateSystemCaret(rect); //UpdateSelectionRange(); } //Trace.TraceInformation("caretMoved {0} caretPosition {1} caretPositionOffset {2} caretAnchor {3} ", // caretMoved, m_caretPosition, m_caretPositionOffset, m_caretAnchor); Validate(); return(caretMoved); }