public RichTextPanelRow(RichTextPanelRow row) { Start = row.Start; End = row.End; Dimensions = new Point(Dimensions.X, Dimensions.Y); Position = new Point(Position.X, Position.Y); Margin = new Thickness(row.Margin.Left, row.Margin.Top, row.Margin.Right, row.Margin.Bottom); }
/// <summary> /// Gets the element at the provided position /// </summary> /// <param name="position">Position</param> /// <returns>Element at the position or null</returns> public UIElement GetRowElementAtPosition(Point position, RichTextPanelRow row) { UIElement result = null; int startIndex = ContentChildren.IndexOf(row.Start); int endIndex = ContentChildren.IndexOf(row.End); double x; double width; int i; if (startIndex >= 0) { for (i = startIndex; i <= endIndex; i++) { x = (double)ContentChildren[i].GetValue(Canvas.LeftProperty); width = (double)ContentChildren[i].GetValue(Canvas.WidthProperty); if (ContentChildren[i] is TextBlockPlus) { width = ((TextBlockPlus)ContentChildren[i]).ContentWidth; } else { width = (double)ContentChildren[i].GetValue(Canvas.ActualWidthProperty); } if (position.X >= x && position.X <= x + width) { result = ContentChildren[i]; break; } } if (result == null) { x = (double)ContentChildren[startIndex].GetValue(Canvas.LeftProperty); result = (position.X < x ? ContentChildren[startIndex] : ContentChildren[endIndex]); } } return(result); }
/// <summary> /// Removes the provided element from the panel /// </summary> /// <param name="element">Element to remove</param> public UIElement Remove(UIElement element) { RichTextPanelRow insertRow = GetRowForElement(element); if (insertRow == null) { return(null); } List <UIElement> children = insertRow.GetChildren(_contentChildren.Children); if (children.Count > 1) { if (children[0] == element) { insertRow.Start = children[1]; } } ContentChildren.Remove(element); return(insertRow.Start); }
/// <summary> /// Calculates the size of a given row /// </summary> /// <param name="row">Row index</param> private double CalculateRowDimensions(RichTextPanelRow row, bool updatePosition) { int startIndex = ContentChildren.IndexOf(row.Start); int endIndex = ContentChildren.IndexOf(row.End); int rowIndex = Rows.IndexOf(row); double tempY; double tempX; Point dims = new Point(); double topY = 0; FrameworkElement element; TextBlockPlus currentTextBlock; double marginTop = 0; double marginBottom = 0; int i; if (startIndex < 0) { return(0); } for (i = startIndex; i <= endIndex; i++) { element = (FrameworkElement)ContentChildren[i]; if (element is TextBlockPlus) { currentTextBlock = (TextBlockPlus)element; if (currentTextBlock.Text.Length == 0) { currentTextBlock.Text = "I"; tempY = currentTextBlock.ContentHeight; currentTextBlock.Text = ""; } else { tempY = currentTextBlock.ContentHeight; } tempY *= currentTextBlock.LineHeightMultiplier; tempX = currentTextBlock.ContentWidth; } else { tempY = (double.IsNaN(element.Height) ? element.ActualHeight : element.Height); tempX = (double.IsNaN(element.Width) ? element.ActualWidth : element.Width); if (element is Bullet) { tempX = ((Bullet)element).IndentWidth; } } if (element.Margin.Top > marginTop) { marginTop = element.Margin.Top; } if (element.Margin.Bottom > marginBottom) { marginBottom = element.Margin.Bottom; } if (tempY > dims.Y) { dims.Y = tempY; } dims.X += tempX; tempY = (double)element.GetValue(Canvas.TopProperty); if (tempY > topY) { topY = tempY; } } if (!(row.End is Newline) || (rowIndex >= 0 && rowIndex == Rows.Count - 1)) { marginBottom = 0; } if (startIndex > 0) { if (!(ContentChildren[startIndex - 1] is Newline)) { marginTop = 0; } } row.Margin = new Thickness(0, marginTop, 0, marginBottom); row.Dimensions = new Point(dims.X, dims.Y + marginTop + marginBottom); if (updatePosition) { row.Position = new Point(0, topY); } return(row.Dimensions.Y); }
/// <summary> /// Positions each row element based on it's alignment properties /// </summary> /// <param name="row">Row index</param> /// <returns>Height of the row</returns> private double SetElementAlignment(RichTextPanelRow row) { Point currentDims = new Point(); FrameworkElement current; Point position = new Point(); int startIndex = ContentChildren.IndexOf(row.Start); int endIndex = ContentChildren.IndexOf(row.End); double baseX = 0; int i; if (startIndex < 0) { return(0); } for (i = startIndex; i <= endIndex; i++) { current = (FrameworkElement)ContentChildren[i]; currentDims.X = (current is TextBlockPlus ? ((TextBlockPlus)current).ContentWidth : (double.IsNaN(current.Width) ? current.ActualWidth : current.Width)); currentDims.Y = (current is TextBlockPlus ? ((TextBlockPlus)current).ContentHeight : (double.IsNaN(current.Height) ? current.ActualHeight : current.Height)); if (current is Bullet) { currentDims.X = ((Bullet)current).IndentWidth; } switch (current.VerticalAlignment) { case VerticalAlignment.Top: position.Y = row.Position.Y; break; case VerticalAlignment.Center: position.Y = row.Position.Y + ((row.Dimensions.Y - (currentDims.Y + row.Margin.Top + row.Margin.Bottom)) * 0.5); break; default: position.Y = ((row.Position.Y + (row.Dimensions.Y - row.Margin.Bottom)) - currentDims.Y) - row.Margin.Top; break; } if (row.Margin.Top != current.Margin.Top) { position.Y -= current.Margin.Top; } switch (((FrameworkElement)ContentChildren[startIndex]).HorizontalAlignment) { case HorizontalAlignment.Center: baseX = (ActualWidth - row.Dimensions.X) * 0.5; break; case HorizontalAlignment.Right: baseX = ActualWidth - row.Dimensions.X; break; default: baseX = 0; break; } current.SetValue(Canvas.LeftProperty, baseX + position.X); position.X += currentDims.X; current.SetValue(Canvas.TopProperty, position.Y); } row.Position = new Point(baseX, row.Position.Y); return(row.Dimensions.Y); }
/// <summary> /// Merges TextBlock elements with the same style name (Tag property) /// </summary> /// <param name="row">Row to compact</param> /// <param name="ignore">Element to ignore when compacting the row</param> private void CompactRow(RichTextPanelRow row, UIElement ignore) { _removed = 0; if (row == null) { return; } int startIndex = ContentChildren.IndexOf(row.Start); int endIndex = ContentChildren.IndexOf(row.End); RichTextPanelEventArgs args; TextBlockPlus current; TextBlockPlus next; bool tagMatched = false; int i; if (startIndex < 0 || endIndex < 0) { return; } for (i = endIndex - 1; i >= startIndex; i--) { if (ContentChildren[i] is TextBlockPlus && ContentChildren[i] != ignore) { current = (TextBlockPlus)ContentChildren[i]; if (ContentChildren[i + 1] is TextBlockPlus && ContentChildren[i + 1] != ignore) { next = (TextBlockPlus)ContentChildren[i + 1]; tagMatched = false; if (current.Tag is RichTextTag && next.Tag is RichTextTag) { if (((RichTextTag)current.Tag).CompareTo(next.Tag) == 0) { if (current.Text.Length > 0) { args = new RichTextPanelEventArgs(current) { Created = next, Index = current.Text.Length }; current.Text += next.Text; RaiseEvent(TextBlockMerge, this, args); if (!args.Cancel) { ContentChildren.Remove(next); if (row.End == next) { row.End = current; } _removed++; } } else { ContentChildren.Remove(current); if (row.End == current) { row.End = next; } if (row.Start == current) { row.Start = next; } _removed++; } tagMatched = true; } } if (!tagMatched && current.Text.Length == 0) { ContentChildren.Remove(current); if (row.Start == current) { row.Start = next; } _removed++; } } } } }
public int GetRowIndexForElement(UIElement element) { RichTextPanelRow row = GetRowForElement(element); return(_rows.IndexOf(row)); }
internal void Update(RichTextPanelRow topRow, bool updateAll, UIElement ignore) { int rowIndex = _rows.IndexOf(topRow); int startIndex = 0; int rowEndIndex = 0; Point pos = new Point(); double thisWidth = 0; List <string> splitText; TextBlockPlus currentTextBlock; TextBlockPlus next; RichTextPanelRow temp; RichTextPanelEventArgs args; FrameworkElement current; Table table; bool updateNextLine = false; bool newlineStarted = false; bool createBlankTextBlock; int startRow = 0; int lineOut = 1; int endRow; double offset; int i; int a; int b; if (ActualWidth == 0) { return; } if (rowIndex >= 0) { startIndex = ContentChildren.IndexOf(_rows[rowIndex].Start); rowEndIndex = ContentChildren.IndexOf(_rows[rowIndex].End); } else { rowIndex = 0; } startRow = rowIndex; _tempRows.Clear(); if (rowIndex < _rows.Count) { pos.Y = _rows[rowIndex].Position.Y; } if (startIndex < 0) { return; } for (i = startIndex; i < ContentChildren.Count; i++) { newlineStarted = false; current = (FrameworkElement)ContentChildren[i]; current.SetValue(Canvas.LeftProperty, pos.X); current.SetValue(Canvas.TopProperty, pos.Y); if (!(current is TextBlockPlus)) { if (current is Table) { table = (Table)current; if (table.AutoWidth) { table.Width = (double.IsNaN(Width) ? ActualWidth : Width) - pos.X; } thisWidth = table.Width; } else { thisWidth = (double.IsNaN(current.Width) ? current.ActualWidth : current.Width); } } else { currentTextBlock = (TextBlockPlus)current; thisWidth = currentTextBlock.ContentWidth; if (pos.X + thisWidth > ActualWidth) { splitText = SplitText(currentTextBlock, currentTextBlock.Text, pos.X, ActualWidth); if (splitText.Count > 1) { b = 0; args = new RichTextPanelEventArgs(currentTextBlock) { Created = null, Index = b }; RaiseEvent(TextBlockSplit, this, args); if (!args.Cancel) { currentTextBlock.Text = splitText[0]; args = new RichTextPanelEventArgs(currentTextBlock) { Created = currentTextBlock, Index = b }; RaiseEvent(TextBlockSplit, this, args); for (a = 1; a < splitText.Count; a++) { next = CreateTextBlock(splitText[a]); args = new RichTextPanelEventArgs(currentTextBlock) { Created = next, Index = b }; RaiseEvent(TextBlockSplit, this, args); if (!args.Cancel) { ContentChildren.Insert(i + a, next); b += splitText[a].Length; } } } if (currentTextBlock.Text.Length == 0) { pos.X = 99999; } } updateNextLine = true; lineOut = 1; } thisWidth = currentTextBlock.ContentWidth; } pos.X += thisWidth; if (current is Newline) { createBlankTextBlock = (i == 0); if (i > 0) { if (ContentChildren[i - 1] is TextBlockPlus) { currentTextBlock = (TextBlockPlus)ContentChildren[i - 1]; if (current.Tag != currentTextBlock.Tag) { RaiseEvent(ApplyFormatting, this, new RichTextPanelEventArgs(currentTextBlock) { Created = current }); } } else { createBlankTextBlock = true; } } if (createBlankTextBlock) { next = CreateTextBlock(""); args = new RichTextPanelEventArgs(current) { Created = next }; RaiseEvent(TextBlockSplit, this, args); if (!args.Cancel) { next.SetValue(Canvas.LeftProperty, pos.X - thisWidth); next.SetValue(Canvas.TopProperty, pos.Y); ContentChildren.Insert(i, next); i++; } } newlineStarted = true; pos.Y += UpdateRowContent(startIndex, i, ignore); i -= _removed; pos.X = 0; startIndex = i + 1; } else { if (pos.X > ActualWidth) { newlineStarted = true; pos.Y += UpdateRowContent(startIndex, i - 1, ignore); i -= _removed; if (i < 0) { break; } pos.X = 0; current.SetValue(Canvas.LeftProperty, pos.X); current.SetValue(Canvas.TopProperty, pos.Y); pos.X = thisWidth; startIndex = i; } } if (newlineStarted && i > rowEndIndex) { if (!updateAll && !updateNextLine) { if (lineOut <= 0) { break; } lineOut--; } updateNextLine = false; } else if (i == ContentChildren.Count - 1 && startIndex < i) { UpdateRowContent(startIndex, i, ignore); i -= _removed; } } temp = GetRowForElementIndex(i); endRow = (i == ContentChildren.Count && _rows.Count > 0 ? _rows.Count - 1 : _rows.IndexOf(temp)); if (endRow >= 0) { _rows.RemoveRange(startRow, (endRow - startRow) + 1); } _rows.InsertRange(startRow, _tempRows); a = startRow + _tempRows.Count; if (endRow >= 0 && endRow < _rows.Count - 1 && a < _rows.Count) { offset = (_rows[a - 1].Position.Y + _rows[a - 1].Dimensions.Y) - _rows[a].Position.Y; startIndex = ContentChildren.IndexOf(_rows[a].Start); if (offset != 0) { // Reposition following rows if the size of the modified rows has changed for (i = a; i < _rows.Count; i++) { _rows[i].Position = new Point(_rows[i].Position.X, _rows[i].Position.Y + offset); } for (i = startIndex; i < ContentChildren.Count; i++) { ContentChildren[i].SetValue(Canvas.TopProperty, (double)ContentChildren[i].GetValue(Canvas.TopProperty) + offset); } } } if (Rows.Count > 0) { CalculateRowDimensions(Rows[Rows.Count - 1], false); SetElementAlignment(Rows[Rows.Count - 1]); } _lastUpdatedSize = ActualWidth; UpdateCanvasSize(); RaiseEvent(Updated, this, null); }