public override void Render(RenderingBuffer buffer) { Attr captionAttrs; if (Disabled) { captionAttrs = Colors.Blend(Color.Gray, Color.DarkGray); } else { if (HasFocus) captionAttrs = Colors.Blend(Color.White, Color.DarkGreen); else captionAttrs = Colors.Blend(Color.Black, Color.DarkGreen); } if (pressed || pressedUsingKeyboard) { buffer.FillRectangle(1, 0, ActualWidth - 1, ActualHeight - 1, ' ', captionAttrs); buffer.SetOpacityRect(0, 0, 1, ActualHeight, 3); buffer.FillRectangle(0, 0, 1, ActualHeight, ' ', captionAttrs); if (!string.IsNullOrEmpty(Caption)) { RenderString(Caption, buffer, 2 + 1 +(ActualWidth - 2 * 2 - Caption.Length) / 2, (ActualHeight-1)/2, ActualWidth - 2 * 2, captionAttrs); } buffer.SetOpacityRect(0, ActualHeight-1, ActualWidth, 1, 3); buffer.FillRectangle(0, ActualHeight - 1, ActualWidth, 1, ' ', Attr.NO_ATTRIBUTES); } else { buffer.FillRectangle(0, 0, ActualWidth - 1, ActualHeight, ' ', captionAttrs); if (!string.IsNullOrEmpty(Caption)) { RenderString(Caption, buffer, 2 + (ActualWidth - 2 * 2 - Caption.Length) / 2, (ActualHeight - 1) / 2, ActualWidth - 2 * 2, captionAttrs); } buffer.SetPixel(0, ActualHeight-1, ' '); buffer.SetOpacityRect(0, ActualHeight -1, ActualWidth, 1, 3); buffer.FillRectangle(1, ActualHeight-1, ActualWidth - 1, 1, UnicodeTable.UpperHalfBlock, Attr.NO_ATTRIBUTES); buffer.SetOpacityRect(ActualWidth - 1, 0, 1, ActualHeight, 3); buffer.FillRectangle(ActualWidth - 1, 1, 1, ActualHeight - 2, UnicodeTable.FullBlock, Attr.NO_ATTRIBUTES); buffer.SetPixel(ActualWidth - 1, 0, UnicodeTable.LowerHalfBlock); } }
public override void Render( RenderingBuffer buffer ) { Attr attr = Colors.Blend( Color.DarkCyan, Color.DarkBlue ); buffer.FillRectangle(0, 0, ActualWidth, ActualHeight, UnicodeTable.MediumShade, attr); int filled = ( int ) ( ActualWidth*( Percent*0.01 ) ); buffer.FillRectangle(0, 0, Math.Min( filled, ActualWidth ), ActualHeight, UnicodeTable.DarkShade, attr); }
/// <summary> /// Рисует исключительно себя - просто фон. /// </summary> /// <param name="buffer"></param> public override void Render(RenderingBuffer buffer) { for (int x = 0; x < ActualWidth; ++x) { for (int y = 0; y < ActualHeight; ++y) { buffer.SetPixel(x, y, ' ', Attr.BACKGROUND_BLUE | Attr.BACKGROUND_GREEN | Attr.BACKGROUND_RED | Attr.FOREGROUND_BLUE | Attr.FOREGROUND_GREEN | Attr.FOREGROUND_RED | Attr.FOREGROUND_INTENSITY); buffer.SetOpacity( x, y, 4 ); } } }
public override void Render(RenderingBuffer buffer) { Attr attr = Colors.Blend(Color.Black, Color.DarkYellow); buffer.FillRectangle( 0, 0, ActualWidth, ActualHeight, ' ', attr); for (int x = 0; x < ActualWidth; ++x) { for (int y = 0; y < ActualHeight; ++y) { if (y == 0 && x < text.Length) { buffer.SetPixel(x, y, text[x], attr); } } } buffer.SetOpacityRect(0, 0, ActualWidth, ActualHeight, 3); }
public void CopyFrom(RenderingBuffer renderingBuffer) { this.buffer = new CHAR_INFO[renderingBuffer.width, renderingBuffer.height]; this.opacityMatrix = new int[renderingBuffer.width, renderingBuffer.height]; this.width = renderingBuffer.width; this.height = renderingBuffer.height; // for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { buffer[x, y] = renderingBuffer.buffer[x, y]; opacityMatrix[x, y] = renderingBuffer.opacityMatrix[x, y]; } } }
public override void Render( RenderingBuffer buffer ) { Attr attr = Colors.Blend( Color.Black, Color.DarkGreen ); Attr inactiveAttr = Colors.Blend( Color.DarkGray, Color.DarkGreen ); // Transparent background for borders buffer.SetOpacityRect( 0, 0, ActualWidth, ActualHeight, 3 ); buffer.FillRectangle( 0, 0, ActualWidth, ActualHeight, ' ', attr ); // Transparent child content part if ( ActualWidth > 2 && ActualHeight > 3 ) buffer.SetOpacityRect( 1, 3, ActualWidth - 2, ActualHeight - 4, 2 ); renderBorderSafe( buffer, 0, 2, Math.Max( getTabHeaderWidth( ) - 1, ActualWidth - 1 ), ActualHeight - 1 ); // Start to render header buffer.FillRectangle( 0, 0, ActualWidth, Math.Min( 2, ActualHeight ), ' ', attr ); int x = 0; // Render tabs before active tab for ( int tab = 0; tab < tabDefinitions.Count; x += TabDefinitions[ tab++ ].Title.Length + 3 ) { var tabDefinition = TabDefinitions[ tab ]; if ( tab <= activeTabIndex ) { buffer.SetPixelSafe( x, 0, UnicodeTable.SingleFrameTopLeftCorner ); buffer.SetPixelSafe( x, 1, UnicodeTable.SingleFrameVertical ); } if ( tab == activeTabIndex ) { buffer.SetPixelSafe( x, 2, activeTabIndex == 0 ? UnicodeTable.SingleFrameVertical : UnicodeTable.SingleFrameBottomRightCorner ); } for ( int i = 0; i < tabDefinition.Title.Length + 2; i++ ) { buffer.SetPixelSafe( x + 1 + i, 0, UnicodeTable.SingleFrameHorizontal ); if ( tab == activeTabIndex ) buffer.SetPixelSafe( x + 1 + i, 2, ' ' ); } buffer.RenderStringSafe( " " + tabDefinition.Title + " ", x + 1, 1, activeTabIndex == tab ? attr : inactiveAttr ); if ( tab >= activeTabIndex ) { buffer.SetPixelSafe( x + tabDefinition.Title.Length + 3, 0, UnicodeTable.SingleFrameTopRightCorner ); buffer.SetPixelSafe( x + tabDefinition.Title.Length + 3, 1, UnicodeTable.SingleFrameVertical ); } if ( tab == activeTabIndex ) { buffer.SetPixelSafe( x + tabDefinition.Title.Length + 3, 2, activeTabIndex == tabDefinitions.Count - 1 && ActualWidth - 1 == x + tabDefinition.Title.Length + 3 ? UnicodeTable.SingleFrameVertical : UnicodeTable.SingleFrameBottomLeftCorner ); } } }
public override void Render(RenderingBuffer buffer) { Attr captionAttrs; if (HasFocus) captionAttrs = Colors.Blend(Color.White, Color.DarkGreen); else captionAttrs = Colors.Blend(Color.Black, Color.DarkGreen); Attr buttonAttrs = captionAttrs; // if ( pressed ) // buttonAttrs = Colors.Blend(Color.Black, Color.DarkGreen); buffer.SetOpacityRect( 0, 0, ActualWidth, ActualHeight, 3 ); buffer.SetPixel(0, 0, pressed ? '<' : '[', buttonAttrs); buffer.SetPixel(1, 0, Checked ? 'X' : ' ', buttonAttrs); buffer.SetPixel(2, 0, pressed ? '>' : ']', buttonAttrs); buffer.SetPixel(3, 0, ' ', buttonAttrs); if (null != caption) RenderString( caption, buffer, 4, 0, ActualWidth - 4, captionAttrs ); }
public override void Render(RenderingBuffer buffer) { Attr attr = Colors.Blend( Color.Black, Color.DarkGreen ); // прозрачный фон для рамки buffer.SetOpacityRect( 0, 0, ActualWidth, ActualHeight, 3 ); // полностью прозрачный внутри if (ActualWidth > 2 && ActualHeight > 2) buffer.SetOpacityRect( 1, 1, ActualWidth-2, ActualHeight-2, 2 ); // title int titleRenderedWidth = 0; if ( !string.IsNullOrEmpty( title ) ) titleRenderedWidth = RenderString( title, buffer, 2, 0, ActualWidth - 4, attr ); // upper border for ( int x = 0; x < ActualWidth; x++ ) { char? c = null; if ( x == 0 ) c = UnicodeTable.SingleFrameTopLeftCorner; else if (x == ActualWidth - 1) c = UnicodeTable.SingleFrameTopRightCorner; else if (x == 1 || x == 2 + titleRenderedWidth) c = ' '; else if ( x > 2 + titleRenderedWidth && x < ActualWidth - 1 ) c = UnicodeTable.SingleFrameHorizontal; if (c != null) buffer.SetPixel( x, 0, c.Value, attr ); } // left border if (ActualHeight > 2) buffer.FillRectangle(0, 1, 1, ActualHeight - 2, UnicodeTable.SingleFrameVertical, attr); if (ActualHeight > 1) buffer.SetPixel(0, ActualHeight - 1, UnicodeTable.SingleFrameBottomLeftCorner, attr); // right border if ( ActualWidth > 1 ) { if (ActualHeight > 2) buffer.FillRectangle(ActualWidth - 1, 1, 1, ActualHeight - 2, UnicodeTable.SingleFrameVertical, attr); if (ActualHeight > 1) buffer.SetPixel(ActualWidth - 1, ActualHeight - 1, UnicodeTable.SingleFrameBottomRightCorner, attr); } // bottom border if ( ActualHeight > 1 && ActualWidth > 2 ) { buffer.FillRectangle(1, ActualHeight - 1, ActualWidth - 2, 1, UnicodeTable.SingleFrameHorizontal, attr); } }
public override void Render(RenderingBuffer buffer) { buffer.FillRectangle(0, 0, ActualWidth, ActualHeight, ' ', Attr.BACKGROUND_BLUE); }
/// <summary> /// Оверлоад для оптимизированного наложения в случае, когда известно, что в дочернем /// контроле поменялась лишь часть, идентифицируемая параметром affectedRect. /// Будет обработана только эта часть дочернего контрола, и количество операций уменьшится. /// </summary> /// <param name="childBuffer"></param> /// <param name="actualOffset"></param> /// <param name="childRenderSize"></param> /// <param name="renderSlotRect"></param> /// <param name="layoutClip"></param> /// <param name="affectedRect">Прямоугольник в дочернем контроле, который был изменен.</param> public void ApplyChild(RenderingBuffer childBuffer, Vector actualOffset, Size childRenderSize, Rect renderSlotRect, Rect layoutClip, Rect? affectedRect) { // Считаем finalRect - прямоугольник относительно parent, который нужно закрасить Rect finalRect = layoutClip; if (affectedRect != null) finalRect.Intersect(affectedRect.Value); // Если child.RenderSlotRect больше child.RenderSize, а rendering buffer // дочернего контрола больше его RenderSize (такое бывает после уменьшения // размеров контрола - т.к. буфер может только увеличиваться, но не уменьшаться) - // то нам нужно либо передать в метод ApplyChild и child.RenderSize, либо // выполнить пересечение заранее finalRect.Intersect(new Rect(new Point(0, 0), childRenderSize)); // Because cannot call Offset() method of empty rect if ( finalRect.IsEmpty ) return; finalRect.Offset(actualOffset); finalRect.Intersect( renderSlotRect ); // Нужно также учесть размеры буфера текущего контрола finalRect.Intersect( new Rect(new Point(0, 0), new Size(this.width, this.height)) ); for ( int x = finalRect.Left; x < finalRect.Right; x++ ) { int parentX = x; int childX = parentX - actualOffset.x; for ( int y = finalRect.Top; y < finalRect.Bottom; y++ ) { int parentY = y; int childY = parentY - actualOffset.y; CHAR_INFO charInfo = childBuffer.buffer[childX, childY]; int opacity = childBuffer.opacityMatrix[childX, childY]; // Для полностью прозрачных пикселей родительского буфера - присваиваем и значение // пикселя, и значение opacity, дальше дело за следующим родителем if ( this.opacityMatrix[ parentX, parentY ] == 2 || this.opacityMatrix[ parentX, parentY ] == 6 ) { this.buffer[ parentX, parentY ] = charInfo; this.opacityMatrix[ parentX, parentY ] = opacity; } else { // В остальных случаях opacity родительского буфера остаётся, а // сам пиксель зависит от opacity дочернего элемента if ( opacity == 0 || opacity == 4 ) { this.buffer[ parentX, parentY ] = charInfo; } else if ( opacity == 1 || opacity == 5 ) { charInfo.Attributes = Colors.Blend( Color.DarkGray, Color.Black ); charInfo.UnicodeChar = buffer[ parentX, parentY ].UnicodeChar; buffer[ parentX, parentY ] = charInfo; } else if ( opacity == 3 || opacity == 7 ) { // берем фоновые атрибуты символа из родительского буфера Attr parentAttr = buffer[ parentX, parentY ].Attributes; if ( ( parentAttr & Attr.BACKGROUND_BLUE ) == Attr.BACKGROUND_BLUE ) { charInfo.Attributes |= Attr.BACKGROUND_BLUE; } else { charInfo.Attributes &= ~Attr.BACKGROUND_BLUE; } if ( ( parentAttr & Attr.BACKGROUND_GREEN ) == Attr.BACKGROUND_GREEN ) { charInfo.Attributes |= Attr.BACKGROUND_GREEN; } else { charInfo.Attributes &= ~Attr.BACKGROUND_GREEN; } if ( ( parentAttr & Attr.BACKGROUND_RED ) == Attr.BACKGROUND_RED ) { charInfo.Attributes |= Attr.BACKGROUND_RED; } else { charInfo.Attributes &= ~Attr.BACKGROUND_RED; } if ( ( parentAttr & Attr.BACKGROUND_INTENSITY ) == Attr.BACKGROUND_INTENSITY ) { charInfo.Attributes |= Attr.BACKGROUND_INTENSITY; } else { charInfo.Attributes &= ~Attr.BACKGROUND_INTENSITY; } buffer[ parentX, parentY ] = charInfo; } } } } }
public override void Render(RenderingBuffer buffer) { Attr captionAttrs; if (HasFocus) captionAttrs = Colors.Blend(Color.Black, Color.DarkGreen); else captionAttrs = Colors.Blend(Color.Black, Color.Gray); buffer.FillRectangle(0, 0, ActualWidth, ActualHeight, UnicodeTable.SingleFrameHorizontal, captionAttrs); }
/// <summary> /// Renders string using attr, but if character is prefixed with underscore, /// symbol will use specialAttrs instead. To render underscore pass two underscores. /// Example: "_File" renders File when 'F' is rendered using specialAttrs. /// </summary> private static int renderString( string s, RenderingBuffer buffer, int x, int y, int maxWidth, Attr attr, Attr specialAttr) { bool underscore = false; int j = 0; for ( int i = 0; i < s.Length && j < maxWidth; i++ ) { char c; if ( underscore ) { c = s[ i ]; } else { if ( s[ i ] == '_' ) { underscore = true; continue; } else { c = s[ i ]; } } Attr a; if ( j + 2 >= maxWidth && j >= 2 && s.Length > maxWidth ) { c = '.'; a = attr; } else { a = underscore ? specialAttr : attr; } buffer.SetPixel( x + j, y, c, a ); j++; underscore = false; } return j; }
public override void Render(RenderingBuffer buffer) { Attr attr = Colors.Blend(Color.DarkCyan, Color.DarkBlue); buffer.SetOpacityRect( 0,0, ActualWidth, ActualHeight, 2 ); if ( horizontalScrollVisible ) { buffer.SetOpacityRect( 0, ActualHeight-1, ActualWidth, 1, 0 ); buffer.SetPixel(0, ActualHeight - 1, UnicodeTable.ArrowLeft, attr); // ◄ // оставляем дополнительный пиксель справа, если одновременно видны оба скроллбара int rightOffset = verticalScrollVisible ? 1 : 0; if ( ActualWidth > 2 + rightOffset ) { buffer.FillRectangle(1, ActualHeight - 1, ActualWidth - (2 + rightOffset), 1, UnicodeTable.MediumShade, attr); // ▒ } if ( ActualWidth > 1 + rightOffset ) { buffer.SetPixel(ActualWidth - (1 + rightOffset), ActualHeight - 1, UnicodeTable.ArrowRight, attr); // ► } // определим, в каком месте находится ползунок if ( ActualWidth > 3 + ( verticalScrollVisible ? 1 : 0 ) ) { int remainingWidth = ActualWidth - ( verticalScrollVisible ? 1 : 0 ); int extraWidth = Content.RenderSize.Width - remainingWidth; int pages = extraWidth/( remainingWidth - 2 - 1 ); //Debugger.Log( 1, "", "pages: " + pages + "\n" ); int scrollerPos; if ( pages == 0 ) { double posInDelta = ( remainingWidth*1.0 - 2 - 1 )/extraWidth; //Debugger.Log( 1, "", "posInDelta: " + posInDelta + "\n" ); scrollerPos = ( int ) Math.Round( posInDelta*deltaX ); } else { double deltaInPos = ( extraWidth*1.0 )/( remainingWidth - 2 - 1 ); //Debugger.Log( 1, "", "deltaX/( deltaInPos ): " + deltaX/( deltaInPos ) + "\n" ); scrollerPos = ( int ) Math.Round( deltaX/( deltaInPos ) ); } buffer.SetPixel(1 + scrollerPos, ActualHeight - 1, UnicodeTable.BlackSquare, attr); // ■ } else if ( ActualWidth == 3 + ( verticalScrollVisible ? 1 : 0 ) ) { buffer.SetPixel(1, ActualHeight - 1, UnicodeTable.BlackSquare, attr); // ■ } } if ( verticalScrollVisible ) { buffer.SetOpacityRect(ActualWidth-1, 0, 1, ActualHeight, 0); buffer.SetPixel(ActualWidth - 1, 0, UnicodeTable.ArrowUp, attr); // ▲ // оставляем дополнительный пиксель снизу, если одновременно видны оба скроллбара int downOffset = horizontalScrollVisible ? 1 : 0; if ( ActualHeight > 2 + downOffset ) { buffer.FillRectangle(ActualWidth - 1, 1, 1, ActualHeight - (2 + downOffset), UnicodeTable.MediumShade, attr); // ▒ } if ( ActualHeight > 1 + downOffset ) { buffer.SetPixel(ActualWidth - 1, ActualHeight - (1 + downOffset), UnicodeTable.ArrowDown, attr); // ▼ } // определим, в каком месте находится ползунок if ( ActualHeight > 3 + ( horizontalScrollVisible ? 1 : 0 ) ) { int remainingHeight = ActualHeight - (horizontalScrollVisible ? 1 : 0); int extraHeight = Content.RenderSize.Height - remainingHeight; int pages = extraHeight/( remainingHeight - 2 - 1 ); //Debugger.Log( 1, "", "pages: " + pages + "\n" ); int scrollerPos; if ( pages == 0 ) { double posInDelta = ( remainingHeight*1.0 - 2 - 1 )/extraHeight; //Debugger.Log( 1, "", "posInDelta: " + posInDelta + "\n" ); scrollerPos = ( int ) Math.Round( posInDelta*deltaY ); } else { double deltaInPos = ( extraHeight*1.0 )/( remainingHeight - 2 - 1 ); //Debugger.Log( 1, "", "deltaY/( deltaInPos ): " + deltaY/( deltaInPos ) + "\n" ); scrollerPos = ( int ) Math.Round( deltaY/( deltaInPos ) ); } buffer.SetPixel(ActualWidth - 1, 1 + scrollerPos, UnicodeTable.BlackSquare, attr); // ■ } else if ( ActualHeight == 3 + ( horizontalScrollVisible ? 1 : 0 ) ) { buffer.SetPixel(ActualWidth - 1, 1, UnicodeTable.BlackSquare, attr); // ■ } } if ( horizontalScrollVisible && verticalScrollVisible ) { buffer.SetPixel(ActualWidth - 1, ActualHeight - 1, UnicodeTable.SingleFrameBottomRightCorner, attr); // ┘ } }
protected void RenderBorders( RenderingBuffer buffer, Point a, Point b, bool singleOrDouble, Attr attrs ) { if ( singleOrDouble ) { // Corners buffer.SetPixel(a.X, a.Y, UnicodeTable.SingleFrameTopLeftCorner, attrs); buffer.SetPixel(b.X, b.Y, UnicodeTable.SingleFrameBottomRightCorner, attrs); buffer.SetPixel(a.X, b.Y, UnicodeTable.SingleFrameBottomLeftCorner, attrs); buffer.SetPixel(b.X, a.Y, UnicodeTable.SingleFrameTopRightCorner, attrs); // Horizontal & vertical frames buffer.FillRectangle(a.X + 1, a.Y, b.X - a.X - 1, 1, UnicodeTable.SingleFrameHorizontal, attrs); buffer.FillRectangle(a.X + 1, b.Y, b.X - a.X - 1, 1, UnicodeTable.SingleFrameHorizontal, attrs); buffer.FillRectangle(a.X, a.Y + 1, 1, b.Y - a.Y - 1, UnicodeTable.SingleFrameVertical, attrs); buffer.FillRectangle(b.X, a.Y + 1, 1, b.Y - a.Y - 1, UnicodeTable.SingleFrameVertical, attrs); } else { // Corners buffer.SetPixel( a.X, a.Y, UnicodeTable.DoubleFrameTopLeftCorner, attrs ); buffer.SetPixel( b.X, b.Y, UnicodeTable.DoubleFrameBottomRightCorner, attrs ); buffer.SetPixel( a.X, b.Y, UnicodeTable.DoubleFrameBottomLeftCorner, attrs ); buffer.SetPixel( b.X, a.Y, UnicodeTable.DoubleFrameTopRightCorner, attrs ); // Horizontal & vertical frames buffer.FillRectangle( a.X + 1, a.Y, b.X - a.X - 1, 1, UnicodeTable.DoubleFrameHorizontal, attrs ); buffer.FillRectangle( a.X + 1, b.Y, b.X - a.X - 1, 1, UnicodeTable.DoubleFrameHorizontal, attrs ); buffer.FillRectangle( a.X, a.Y + 1, 1, b.Y - a.Y - 1, UnicodeTable.DoubleFrameVertical, attrs ); buffer.FillRectangle( b.X, a.Y + 1, 1, b.Y - a.Y - 1, UnicodeTable.DoubleFrameVertical, attrs ); } }
private RenderingBuffer getOrCreateFullBufferForControl(Control control) { RenderingBuffer value; if (fullBuffers.TryGetValue(control, out value)) { return value; } else { RenderingBuffer buffer = new RenderingBuffer(control.ActualWidth, control.ActualHeight); fullBuffers.Add(control, buffer); return buffer; } }
private RenderingBuffer processControl(Control control, List <Control> revalidatedControls) { RenderingBuffer buffer = getOrCreateBufferForControl(control); RenderingBuffer fullBuffer = getOrCreateFullBufferForControl(control); // LayoutInfo lastLayoutInfo = control.lastLayoutInfo; LayoutInfo layoutInfo = control.layoutInfo; // control.Measure(lastLayoutInfo.measureArgument); control.Arrange(lastLayoutInfo.renderSlotRect); // if lastLayoutInfo eq layoutInfo we can use last rendered buffer // if (layoutInfo.Equals(lastLayoutInfo) && lastLayoutInfo.validity == LayoutValidity.Render) { if (checkRenderingWasNotChangedRecursively(control)) { if (control.SetValidityToRender()) { revalidatedControls.Add(control); } return(fullBuffer); } // replace buffers if control has grown if (layoutInfo.renderSize.width > buffer.Width || layoutInfo.renderSize.height > buffer.Height) { buffer = new RenderingBuffer(layoutInfo.renderSize.width, layoutInfo.renderSize.height); fullBuffer = new RenderingBuffer(layoutInfo.renderSize.width, layoutInfo.renderSize.height); buffers[control] = buffer; fullBuffers[control] = fullBuffer; } // otherwise we should assemble full rendered buffer using childs buffer.Clear(); if (control.RenderSize.Width != 0 && control.RenderSize.Height != 0) { control.Render(buffer); } // fullBuffer.CopyFrom(buffer); foreach (Control child in control.Children) { if (child.Visibility == Visibility.Visible) { RenderingBuffer fullChildBuffer = processControl(child, revalidatedControls); fullBuffer.ApplyChild(fullChildBuffer, child.ActualOffset, child.RenderSize, child.RenderSlotRect, child.LayoutClip); } else { // чтобы следующий Invalidate для этого контрола // перезаписал lastLayoutInfo if (child.SetValidityToRender()) { revalidatedControls.Add(child); } } } // Save overlappingRect for each control child refreshChildrenLastOverlappedRects(control, false); if (control.SetValidityToRender()) { revalidatedControls.Add(control); } return(fullBuffer); }
private void updateLayout(Control control, List <Control> revalidatedControls) { LayoutInfo lastLayoutInfo = control.lastLayoutInfo; // работаем с родительским элементом управления if (control.Parent != null) { bool needUpdateParentLayout = true; // если размер текущего контрола не изменился, то состояние ревалидации не распространяется // вверх по дереву элементов, и мы переходим к работе с дочерними элементами // в противном случае мы добавляем родительский элемент в конец очереди ревалидации, и // возвращаем управление if (lastLayoutInfo.validity != LayoutValidity.Nothing) { control.Measure(lastLayoutInfo.measureArgument); // if (lastLayoutInfo.unclippedDesiredSize == control.layoutInfo.unclippedDesiredSize) { if (checkDesiredSizeNotChangedRecursively(control)) { needUpdateParentLayout = false; } } if (needUpdateParentLayout) { // mark the parent control for invalidation too and enqueue them control.Parent.Invalidate(); // мы можем закончить с этим элементом, поскольку мы уже добавили // в конец очереди его родителя, и мы все равно вернемся к нему в след. раз return; } } // работаем с дочерними элементами управления // вызываем для текущего контрола Measure&Arrange с последними значениями аргументов if (lastLayoutInfo.validity == LayoutValidity.Nothing && control.Parent != null) { throw new InvalidOperationException("Assertion failed."); } // rootElement - особый случай if (control.Parent == null) { if (control != RootElement) { throw new InvalidOperationException("Control has no parent but is not known rootElement."); } control.Measure(RootElementRect.Size); control.Arrange(RootElementRect); } else { control.Measure(lastLayoutInfo.measureArgument); control.Arrange(lastLayoutInfo.renderSlotRect); } // update render buffers of current control and its children RenderingBuffer buffer = getOrCreateBufferForControl(control); RenderingBuffer fullBuffer = getOrCreateFullBufferForControl(control); // replace buffers if control has grown LayoutInfo layoutInfo = control.layoutInfo; if (layoutInfo.renderSize.width > buffer.Width || layoutInfo.renderSize.height > buffer.Height) { buffer = new RenderingBuffer(layoutInfo.renderSize.width, layoutInfo.renderSize.height); fullBuffer = new RenderingBuffer(layoutInfo.renderSize.width, layoutInfo.renderSize.height); buffers[control] = buffer; fullBuffers[control] = fullBuffer; } buffer.Clear(); if (control.RenderSize.Width != 0 && control.RenderSize.Height != 0) { control.Render(buffer); } // проверяем дочерние контролы - если их layoutInfo не изменился по сравнению с последним, // то мы можем взять их последний renderBuffer без обновления и применить к текущему контролу fullBuffer.CopyFrom(buffer); IList <Control> children = control.Children; foreach (Control child in children) { if (child.Visibility == Visibility.Visible) { RenderingBuffer fullChildBuffer = processControl(child, revalidatedControls); fullBuffer.ApplyChild(fullChildBuffer, child.ActualOffset, child.RenderSize, child.RenderSlotRect, child.LayoutClip); } else { // чтобы следующий Invalidate перезаписал lastLayoutInfo if (child.SetValidityToRender()) { revalidatedControls.Add(child); } } } // Save overlappingRect for each control child refreshChildrenLastOverlappedRects(control, false); if (control.SetValidityToRender()) { revalidatedControls.Add(control); } addControlToRenderingUpdatedList(control); }
/// <summary> /// Получает для указанного контрола full render buffer и применяет его последовательно /// ко всем родительским элементам управления, вплоть до изображения на экране. /// Возвращает прямоугольник, необходимый для ревалидации на экране (affected rect). /// Учитывает Z-Order контролов-соседей (если родительский контрол имеет несколько дочерних, они могут перекрывать /// друг друга). /// Первый вызов производится с affectedRect = control.RenderSize. /// </summary> /// <returns>Affected rectangle in canvas should be copyied to console screen.</returns> private Rect applyChangesToCanvas(Control control, Rect affectedRect) { // если системой лайаута были определены размеры дочернего контрола, превышающие размеры слота // (такое может произойти, если дочерний контрол игнорирует переданные аргументы в MeasureOverride // и ArrangeOverride), то в этом месте может прийти affectedRect, выходящий за рамки // текущего RenderSize контрола, и мы должны выполнить intersection для корректного наложения affectedRect.Intersect(new Rect(new Point(0, 0), control.RenderSize)); RenderingBuffer fullBuffer = getOrCreateFullBufferForControl(control); if (control.Parent != null) { RenderingBuffer fullParentBuffer = getOrCreateFullBufferForControl(control.Parent); // если буфер контрола содержит opacity пиксели в affectedRect, то мы вынуждены переинициализировать // буфер парента целиком (не вызывая Render, конечно, но переналожением буферов дочерних элементов) if (fullBuffer.ContainsOpacity(affectedRect)) { fullParentBuffer.Clear(); fullParentBuffer.CopyFrom(getOrCreateBufferForControl(control.Parent)); foreach (Control child in control.Parent.Children) { if (child.Visibility == Visibility.Visible) { RenderingBuffer childBuffer = getOrCreateFullBufferForControl(child); fullParentBuffer.ApplyChild(childBuffer, child.ActualOffset, child.RenderSize, child.RenderSlotRect, child.LayoutClip); } } } if (control.Visibility == Visibility.Visible) { if (affectedRect == new Rect(new Point(0, 0), control.RenderSize)) { fullParentBuffer.ApplyChild(fullBuffer, control.ActualOffset, control.RenderSize, control.RenderSlotRect, control.LayoutClip); } else { fullParentBuffer.ApplyChild(fullBuffer, control.ActualOffset, control.RenderSize, control.RenderSlotRect, control.LayoutClip, affectedRect); } } // определим соседей контрола, которые могут перекрывать его IList <Control> neighbors = control.Parent.GetChildrenOrderedByZIndex(); // восстанавливаем изображение поверх обновленного контрола, если // имеются контролы, лежащие выше по z-order int controlIndex = neighbors.IndexOf(control); // начиная с controlIndex + 1 в списке лежат контролы с z-index больше чем z-index текущего контрола for (int i = controlIndex + 1; i < neighbors.Count; i++) { Control neighbor = neighbors[i]; fullParentBuffer.ApplyChild(getOrCreateFullBufferForControl(neighbor), neighbor.ActualOffset, neighbor.RenderSize, neighbor.RenderSlotRect, neighbor.LayoutClip); } Rect parentAffectedRect = control.RenderSlotRect; parentAffectedRect.Intersect(new Rect(affectedRect.x + control.ActualOffset.x, affectedRect.y + control.ActualOffset.y, affectedRect.width, affectedRect.height)); // нет смысла продолжать подъем вверх по дереву, если контрола точно уже не видно if (parentAffectedRect.IsEmpty) { return(Rect.Empty); } return(applyChangesToCanvas(control.Parent, parentAffectedRect)); } else { if (control != RootElement) { throw new InvalidOperationException("Assertion failed."); } // мы добрались до экрана консоли fullBuffer.CopyToPhysicalCanvas(Canvas, affectedRect, RootElementRect.TopLeft); return(affectedRect); } }
public override void Render(RenderingBuffer buffer) { Attr attrs; if ( HasFocus ) { attrs = Colors.Blend(Color.White, Color.DarkGreen); } else attrs = Colors.Blend( Color.Black, Color.DarkCyan ); buffer.SetPixel( 0, 0, ' ', attrs ); int usedForCurrentItem = 0; if ( Items.Count != 0 && ActualWidth > 4 ) { usedForCurrentItem = RenderString(Items[SelectedItemIndex ?? 0], buffer, 1, 0, ActualWidth - 4, attrs); } buffer.FillRectangle( 1 + usedForCurrentItem, 0, ActualWidth - (usedForCurrentItem + 1), 1, ' ', attrs ); if (ActualWidth > 2) { buffer.SetPixel(ActualWidth - 2, 0, opened ? '^' : 'v', attrs); } }
public override void Render(RenderingBuffer buffer) { Attr borderAttrs = Colors.Blend(Color.Black, Color.DarkCyan); // Background buffer.FillRectangle(1, 1, this.ActualWidth - 1, this.ActualHeight - 1, ' ', borderAttrs); // First row and first column are transparent // Column is also transparent for mouse events buffer.SetOpacityRect( 0,0,ActualWidth, 1, 2 ); buffer.SetOpacityRect( 0, 1, 1, ActualHeight-1, 6 ); if ( shadow ) { buffer.SetOpacity( 1, ActualHeight - 1, 2+4 ); buffer.SetOpacity( ActualWidth - 1, 0, 2+4 ); buffer.SetOpacityRect( ActualWidth - 1, 1, 1, ActualHeight - 1, 1+4 ); buffer.FillRectangle(ActualWidth - 1, 1, 1, ActualHeight - 1, UnicodeTable.FullBlock, borderAttrs); buffer.SetOpacityRect( 2, ActualHeight - 1, ActualWidth - 2, 1, 3+4 ); buffer.FillRectangle(2, ActualHeight - 1, ActualWidth - 2, 1, UnicodeTable.UpperHalfBlock, Attr.NO_ATTRIBUTES ); } }
public override void Render( RenderingBuffer buffer ) { buffer.SetOpacityRect( 0, 0, ActualWidth, ActualHeight, 2 ); }
private RenderingBuffer processControl(Control control, List<Control> revalidatedControls) { RenderingBuffer buffer = getOrCreateBufferForControl(control); RenderingBuffer fullBuffer = getOrCreateFullBufferForControl(control); // LayoutInfo lastLayoutInfo = control.lastLayoutInfo; LayoutInfo layoutInfo = control.layoutInfo; // control.Measure(lastLayoutInfo.measureArgument); control.Arrange(lastLayoutInfo.renderSlotRect); // if lastLayoutInfo eq layoutInfo we can use last rendered buffer // if (layoutInfo.Equals(lastLayoutInfo) && lastLayoutInfo.validity == LayoutValidity.Render) { if (checkRenderingWasNotChangedRecursively(control)) { if (control.SetValidityToRender()) { revalidatedControls.Add(control); } return fullBuffer; } // replace buffers if control has grown if (layoutInfo.renderSize.width > buffer.Width || layoutInfo.renderSize.height > buffer.Height) { buffer = new RenderingBuffer(layoutInfo.renderSize.width, layoutInfo.renderSize.height); fullBuffer = new RenderingBuffer(layoutInfo.renderSize.width, layoutInfo.renderSize.height); buffers[control] = buffer; fullBuffers[control] = fullBuffer; } // otherwise we should assemble full rendered buffer using childs buffer.Clear(); if (control.RenderSize.Width != 0 && control.RenderSize.Height != 0) control.Render(buffer); // fullBuffer.CopyFrom(buffer); foreach (Control child in control.Children) { if (child.Visibility == Visibility.Visible) { RenderingBuffer fullChildBuffer = processControl(child, revalidatedControls); fullBuffer.ApplyChild(fullChildBuffer, child.ActualOffset, child.RenderSize, child.RenderSlotRect, child.LayoutClip); } else { // чтобы следующий Invalidate для этого контрола // перезаписал lastLayoutInfo if (child.SetValidityToRender()) { revalidatedControls.Add(child); } } } // Save overlappingRect for each control child refreshChildrenLastOverlappedRects( control, false ); if (control.SetValidityToRender()) { revalidatedControls.Add(control); } return fullBuffer; }
public override void Render(RenderingBuffer buffer) { Attr borderAttrs = moving ? Colors.Blend(Color.Green, Color.Gray) : Colors.Blend(Color.White, Color.Gray); // background buffer.FillRectangle(0, 0, this.ActualWidth, this.ActualHeight, ' ', borderAttrs); // Borders RenderBorders(buffer, new Point(0, 0), new Point(ActualWidth - 3, ActualHeight - 2), this.moving || this.resizing, borderAttrs); // close button if (ActualWidth > 4) { buffer.SetPixel(2, 0, '['); buffer.SetPixel(3, 0, showClosingGlyph ? UnicodeTable.WindowClosePressedSymbol : UnicodeTable.WindowCloseSymbol, Colors.Blend(Color.Green, Color.Gray)); buffer.SetPixel(4, 0, ']'); } // shadows buffer.SetOpacity(0, ActualHeight - 1, 2+4); buffer.SetOpacity(1, ActualHeight - 1, 2+4); buffer.SetOpacity(ActualWidth - 1, 0, 2+4); buffer.SetOpacity(ActualWidth - 2, 0, 2+4); buffer.SetOpacityRect(2, ActualHeight - 1, ActualWidth - 2, 1, 1+4); buffer.SetOpacityRect(ActualWidth - 2, 1, 2, ActualHeight - 1, 1+4); // title if (!string.IsNullOrEmpty(Title)) { int titleStartX = 7; bool renderTitle = false; string renderTitleString = null; int availablePixelsCount = ActualWidth - titleStartX*2; if (availablePixelsCount > 0) { renderTitle = true; if (Title.Length <= availablePixelsCount) { // dont truncate title titleStartX += (availablePixelsCount - Title.Length)/2; renderTitleString = Title; } else { renderTitleString = Title.Substring(0, availablePixelsCount); if (renderTitleString.Length > 2) { renderTitleString = renderTitleString.Substring(0, renderTitleString.Length - 2) + ".."; } else { renderTitle = false; } } } if (renderTitle) { // assert !string.IsNullOrEmpty(renderingTitleString); buffer.SetPixel(titleStartX - 1, 0, ' ', borderAttrs); for (int i = 0; i < renderTitleString.Length; i++) { buffer.SetPixel(titleStartX + i, 0, renderTitleString[i], borderAttrs); } buffer.SetPixel(titleStartX + renderTitleString.Length, 0, ' ', borderAttrs); } } }
private void updateLayout(Control control, List<Control> revalidatedControls) { LayoutInfo lastLayoutInfo = control.lastLayoutInfo; // работаем с родительским элементом управления if (control.Parent != null) { bool needUpdateParentLayout = true; // если размер текущего контрола не изменился, то состояние ревалидации не распространяется // вверх по дереву элементов, и мы переходим к работе с дочерними элементами // в противном случае мы добавляем родительский элемент в конец очереди ревалидации, и // возвращаем управление if (lastLayoutInfo.validity != LayoutValidity.Nothing) { control.Measure(lastLayoutInfo.measureArgument); // if (lastLayoutInfo.unclippedDesiredSize == control.layoutInfo.unclippedDesiredSize) { if (checkDesiredSizeNotChangedRecursively(control)) { needUpdateParentLayout = false; } } if (needUpdateParentLayout) { // mark the parent control for invalidation too and enqueue them control.Parent.Invalidate(); // мы можем закончить с этим элементом, поскольку мы уже добавили // в конец очереди его родителя, и мы все равно вернемся к нему в след. раз return; } } // работаем с дочерними элементами управления // вызываем для текущего контрола Measure&Arrange с последними значениями аргументов if (lastLayoutInfo.validity == LayoutValidity.Nothing && control.Parent != null) { throw new InvalidOperationException("Assertion failed."); } // rootElement - особый случай if (control.Parent == null) { if (control != RootElement) { throw new InvalidOperationException("Control has no parent but is not known rootElement."); } control.Measure(RootElementRect.Size); control.Arrange(RootElementRect); } else { control.Measure(lastLayoutInfo.measureArgument); control.Arrange(lastLayoutInfo.renderSlotRect); } // update render buffers of current control and its children RenderingBuffer buffer = getOrCreateBufferForControl(control); RenderingBuffer fullBuffer = getOrCreateFullBufferForControl(control); // replace buffers if control has grown LayoutInfo layoutInfo = control.layoutInfo; if (layoutInfo.renderSize.width > buffer.Width || layoutInfo.renderSize.height > buffer.Height) { buffer = new RenderingBuffer(layoutInfo.renderSize.width, layoutInfo.renderSize.height); fullBuffer = new RenderingBuffer(layoutInfo.renderSize.width, layoutInfo.renderSize.height); buffers[control] = buffer; fullBuffers[control] = fullBuffer; } buffer.Clear(); if (control.RenderSize.Width != 0 && control.RenderSize.Height != 0) control.Render(buffer); // проверяем дочерние контролы - если их layoutInfo не изменился по сравнению с последним, // то мы можем взять их последний renderBuffer без обновления и применить к текущему контролу fullBuffer.CopyFrom(buffer); IList<Control> children = control.Children; foreach (Control child in children) { if (child.Visibility == Visibility.Visible) { RenderingBuffer fullChildBuffer = processControl(child, revalidatedControls); fullBuffer.ApplyChild(fullChildBuffer, child.ActualOffset, child.RenderSize, child.RenderSlotRect, child.LayoutClip); } else { // чтобы следующий Invalidate перезаписал lastLayoutInfo if (child.SetValidityToRender()) { revalidatedControls.Add(child); } } } // Save overlappingRect for each control child refreshChildrenLastOverlappedRects( control, false ); if (control.SetValidityToRender()) { revalidatedControls.Add(control); } addControlToRenderingUpdatedList(control); }
public override void Render(RenderingBuffer buffer) { Attr captionAttrs; Attr specialAttrs; if ( HasFocus || this.expanded ) { captionAttrs = Colors.Blend( Color.Black, Color.DarkGreen ); specialAttrs = Colors.Blend( Color.DarkRed, Color.DarkGreen ); } else { captionAttrs = Colors.Blend( Color.Black, Color.Gray ); specialAttrs = Colors.Blend( Color.DarkRed, Color.Gray ); } if ( disabled ) captionAttrs = Colors.Blend( Color.DarkGray, Color.Gray ); buffer.FillRectangle( 0, 0, ActualWidth, ActualHeight, ' ', captionAttrs ); if ( null != Title ) { renderString( Title, buffer, 1, 0, ActualWidth, captionAttrs, Disabled ? captionAttrs : specialAttrs ); } if ( null != TitleRight ) RenderString( TitleRight, buffer, ActualWidth - TitleRight.Length - 1, 0, TitleRight.Length, captionAttrs ); }
public override void Render(RenderingBuffer buffer) { Attr selectedAttr = Colors.Blend(Color.White, Color.DarkGreen); Attr attr = Colors.Blend(Color.Black, Color.DarkCyan); Attr disabledAttr = Colors.Blend(Color.Gray, Color.DarkCyan); for ( int y = 0; y < ActualHeight; y++ ) { string item = y < items.Count ? items[ y ] : null; if ( item != null ) { Attr currentAttr = disabledItemsIndexes.Contains(y) ? disabledAttr : (SelectedItemIndex == y ? selectedAttr : attr); buffer.SetPixel( 0, y, ' ', currentAttr ); if ( ActualWidth > 1 ) { // минус 2 потому что у нас есть по пустому пикселю слева и справа int rendered = RenderString( item, buffer, 1, y, ActualWidth - 2, currentAttr ); buffer.FillRectangle( 1 + rendered, y, ActualWidth - (1 + rendered), 1, ' ', currentAttr); } } else { buffer.FillRectangle(0, y, ActualWidth, 1, ' ', attr); } } }
public override void Render(RenderingBuffer buffer) { Attr borderAttrs = Colors.Blend(Color.Black, Color.Gray); // Background buffer.FillRectangle(0, 1, ActualWidth, ActualHeight - 1, ' ', borderAttrs); // Первые width пикселей первой строки - прозрачные, но события мыши не пропускают // По нажатию на них мы закрываем всплывающее окно вручную buffer.SetOpacityRect(0, 0, parentItemWidth, 1, 2); // Оставшиеся пиксели первой строки - пропускают события мыши // И WindowsHost закроет всплывающее окно автоматически при нажатии или // перемещении нажатого курсора над этим местом buffer.SetOpacityRect( parentItemWidth, 0, ActualWidth - parentItemWidth, 1, 6 ); if (shadow) { buffer.SetOpacity(0, ActualHeight - 1, 2 + 4); buffer.SetOpacity(ActualWidth - 1, 1, 2 + 4); buffer.SetOpacityRect(ActualWidth - 1, 2, 1, ActualHeight - 2, 1 + 4); buffer.FillRectangle(ActualWidth - 1, 2, 1, ActualHeight - 2, UnicodeTable.FullBlock, borderAttrs); buffer.SetOpacityRect(1, ActualHeight - 1, ActualWidth - 1, 1, 3 + 4); buffer.FillRectangle(1, ActualHeight - 1, ActualWidth - 1, 1, UnicodeTable.UpperHalfBlock, Attr.NO_ATTRIBUTES); } RenderBorders( buffer, new Point( 1, 1 ), shadow ? new Point( ActualWidth - 3, ActualHeight - 2 ) : new Point( ActualWidth - 2, ActualHeight - 1 ), true, borderAttrs ); }
public override void Render(RenderingBuffer buffer) { Attr borderAttrs = Colors.Blend(Color.Black, Color.DarkCyan); // устанавливаем прозрачными первую строку и первый столбец // для столбца дополнительно включена прозрачность для событий мыши // background buffer.FillRectangle(1, 1, this.ActualWidth - 1, this.ActualHeight - 1, ' ', borderAttrs); buffer.SetOpacityRect( 0,0,ActualWidth, 1, 2 ); buffer.SetOpacityRect( 0, 1, 1, ActualHeight-1, 6 ); if ( shadow ) { buffer.SetOpacity( 1, ActualHeight - 1, 2+4 ); buffer.SetOpacity( ActualWidth - 1, 0, 2+4 ); buffer.SetOpacityRect( ActualWidth - 1, 1, 1, ActualHeight - 1, 1+4 ); buffer.FillRectangle(ActualWidth - 1, 1, 1, ActualHeight - 1, UnicodeTable.FullBlock, borderAttrs); buffer.SetOpacityRect( 2, ActualHeight - 1, ActualWidth - 2, 1, 3+4 ); buffer.FillRectangle(2, ActualHeight - 1, ActualWidth - 2, 1, UnicodeTable.UpperHalfBlock, Attr.NO_ATTRIBUTES ); } }
public override void Render( RenderingBuffer buffer ) { Attr attr = Colors.Blend( Color.Black, Color.Gray ); buffer.FillRectangle(0, 0, ActualWidth, ActualHeight, ' ', attr); }
private void renderBorderSafe( RenderingBuffer buffer, int x, int y, int x2, int y2 ) { if ( ActualWidth > x && ActualHeight > y ) buffer.SetPixel( x, y, UnicodeTable.SingleFrameTopLeftCorner ); if ( ActualWidth > x && ActualHeight > y2 && y2 > y ) buffer.SetPixel( x, y2, UnicodeTable.SingleFrameBottomLeftCorner ); if ( ActualHeight > y ) for ( int i = x + 1; i <= Math.Min( x2 - 1, ActualWidth - 1 ); i++ ) buffer.SetPixel( i, y, UnicodeTable.SingleFrameHorizontal ); if ( ActualHeight > y2 ) for ( int i = x + 1; i <= Math.Min( x2 - 1, ActualWidth - 1 ); i++ ) buffer.SetPixel( i, y2, UnicodeTable.SingleFrameHorizontal ); if ( ActualWidth > x ) for ( int j = y + 1; j <= Math.Min( y2 - 1, ActualHeight - 1 ); j++ ) buffer.SetPixel( x, j, UnicodeTable.SingleFrameVertical ); if ( ActualWidth > x2 ) for ( int j = y + 1; j <= Math.Min( y2 - 1, ActualHeight - 1 ); j++ ) buffer.SetPixel( x2, j, UnicodeTable.SingleFrameVertical ); if ( ActualWidth > x2 && ActualHeight > y && x2 > x ) buffer.SetPixel( x2, y, UnicodeTable.SingleFrameTopRightCorner ); if ( ActualWidth > x2 && ActualHeight > y2 && y2 > y && x2 > x ) buffer.SetPixel( x2, y2, UnicodeTable.SingleFrameBottomRightCorner ); }
/// <summary> /// Накладывает буфер дочернего элемента на текущий. Дочерний буфер виртуально накладывается на текущий /// в соответствии с переданным actualOffset, а потом та часть дочернего буфера, которая попадает в /// renderSlotRect, прорисовывается. renderSlotRect определен отн-но текущего буфера (а не дочернего). /// layoutClip определяет, какая часть дочернего буфера будет прорисована в текущий буфер (клиппинг, /// возникающий при применении Margin и Alignment'ов). /// </summary> /// <param name="childBuffer"></param> /// <param name="actualOffset">Смещение буфера дочернего элемента относительно текущего.</param> /// <param name="childRenderSize">Размер отрендеренного дочерним элементом контента - может /// быть меньше размера childBuffer, поэтому нужно передавать явно.</param> /// <param name="renderSlotRect">Размер и положение слота, выделенного дочернему элементу.</param> /// <param name="layoutClip">Часть дочернего буфера, которая будет отрисована - по размеру может быть /// меньше или равна RenderSlotRect.Size. По координатам соотносится с childBuffer.</param> public void ApplyChild(RenderingBuffer childBuffer, Vector actualOffset, Size childRenderSize, Rect renderSlotRect, Rect layoutClip) { ApplyChild( childBuffer, actualOffset, childRenderSize, renderSlotRect, layoutClip, null ); }
/// <summary> /// Оверлоад для оптимизированного наложения в случае, когда известно, что в дочернем /// контроле поменялась лишь часть, идентифицируемая параметром affectedRect. /// Будет обработана только эта часть дочернего контрола, и количество операций уменьшится. /// </summary> /// <param name="childBuffer"></param> /// <param name="actualOffset"></param> /// <param name="childRenderSize"></param> /// <param name="renderSlotRect"></param> /// <param name="layoutClip"></param> /// <param name="affectedRect">Прямоугольник в дочернем контроле, который был изменен.</param> public void ApplyChild(RenderingBuffer childBuffer, Vector actualOffset, Size childRenderSize, Rect renderSlotRect, Rect layoutClip, Rect?affectedRect) { // Считаем finalRect - прямоугольник относительно parent, который нужно закрасить Rect finalRect = layoutClip; if (affectedRect != null) { finalRect.Intersect(affectedRect.Value); } // Если child.RenderSlotRect больше child.RenderSize, а rendering buffer // дочернего контрола больше его RenderSize (такое бывает после уменьшения // размеров контрола - т.к. буфер может только увеличиваться, но не уменьшаться) - // то нам нужно либо передать в метод ApplyChild и child.RenderSize, либо // выполнить пересечение заранее finalRect.Intersect(new Rect(new Point(0, 0), childRenderSize)); // Because cannot call Offset() method of empty rect if (finalRect.IsEmpty) { return; } finalRect.Offset(actualOffset); finalRect.Intersect(renderSlotRect); // Нужно также учесть размеры буфера текущего контрола finalRect.Intersect(new Rect(new Point(0, 0), new Size(this.width, this.height))); for (int x = finalRect.Left; x < finalRect.Right; x++) { int parentX = x; int childX = parentX - actualOffset.x; for (int y = finalRect.Top; y < finalRect.Bottom; y++) { int parentY = y; int childY = parentY - actualOffset.y; CHAR_INFO charInfo = childBuffer.buffer[childX, childY]; int opacity = childBuffer.opacityMatrix[childX, childY]; // Для полностью прозрачных пикселей родительского буфера - присваиваем и значение // пикселя, и значение opacity, дальше дело за следующим родителем if (this.opacityMatrix[parentX, parentY] == 2 || this.opacityMatrix[parentX, parentY] == 6) { this.buffer[parentX, parentY] = charInfo; this.opacityMatrix[parentX, parentY] = opacity; } else { // В остальных случаях opacity родительского буфера остаётся, а // сам пиксель зависит от opacity дочернего элемента if (opacity == 0 || opacity == 4) { this.buffer[parentX, parentY] = charInfo; } else if (opacity == 1 || opacity == 5) { charInfo.Attributes = Colors.Blend(Color.DarkGray, Color.Black); charInfo.UnicodeChar = buffer[parentX, parentY].UnicodeChar; buffer[parentX, parentY] = charInfo; } else if (opacity == 3 || opacity == 7) { // берем фоновые атрибуты символа из родительского буфера Attr parentAttr = buffer[parentX, parentY].Attributes; if ((parentAttr & Attr.BACKGROUND_BLUE) == Attr.BACKGROUND_BLUE) { charInfo.Attributes |= Attr.BACKGROUND_BLUE; } else { charInfo.Attributes &= ~Attr.BACKGROUND_BLUE; } if ((parentAttr & Attr.BACKGROUND_GREEN) == Attr.BACKGROUND_GREEN) { charInfo.Attributes |= Attr.BACKGROUND_GREEN; } else { charInfo.Attributes &= ~Attr.BACKGROUND_GREEN; } if ((parentAttr & Attr.BACKGROUND_RED) == Attr.BACKGROUND_RED) { charInfo.Attributes |= Attr.BACKGROUND_RED; } else { charInfo.Attributes &= ~Attr.BACKGROUND_RED; } if ((parentAttr & Attr.BACKGROUND_INTENSITY) == Attr.BACKGROUND_INTENSITY) { charInfo.Attributes |= Attr.BACKGROUND_INTENSITY; } else { charInfo.Attributes &= ~Attr.BACKGROUND_INTENSITY; } buffer[parentX, parentY] = charInfo; } } } } }
public override void Render(RenderingBuffer buffer) { Attr attr = Colors.Blend(Color.White, Color.DarkBlue); buffer.FillRectangle(0, 0, ActualWidth, ActualHeight, ' ', attr); if (null != text) { for (int i = displayOffset; i < text.Length; i++) { if (i - displayOffset < ActualWidth - 2 && i - displayOffset >= 0) { buffer.SetPixel(1 + i - displayOffset, 0, text[i]); } } } Attr arrowsAttr = Colors.Blend(Color.Green, Color.DarkBlue); if (displayOffset > 0) buffer.SetPixel(0, 0, '<', arrowsAttr); if (!String.IsNullOrEmpty(text) && ActualWidth - 2 + displayOffset < text.Length) buffer.SetPixel(ActualWidth - 1, 0, '>', arrowsAttr); }
/// <summary> /// Накладывает буфер дочернего элемента на текущий. Дочерний буфер виртуально накладывается на текущий /// в соответствии с переданным actualOffset, а потом та часть дочернего буфера, которая попадает в /// renderSlotRect, прорисовывается. renderSlotRect определен отн-но текущего буфера (а не дочернего). /// layoutClip определяет, какая часть дочернего буфера будет прорисована в текущий буфер (клиппинг, /// возникающий при применении Margin и Alignment'ов). /// </summary> /// <param name="childBuffer"></param> /// <param name="actualOffset">Смещение буфера дочернего элемента относительно текущего.</param> /// <param name="childRenderSize">Размер отрендеренного дочерним элементом контента - может /// быть меньше размера childBuffer, поэтому нужно передавать явно.</param> /// <param name="renderSlotRect">Размер и положение слота, выделенного дочернему элементу.</param> /// <param name="layoutClip">Часть дочернего буфера, которая будет отрисована - по размеру может быть /// меньше или равна RenderSlotRect.Size. По координатам соотносится с childBuffer.</param> public void ApplyChild(RenderingBuffer childBuffer, Vector actualOffset, Size childRenderSize, Rect renderSlotRect, Rect layoutClip) { ApplyChild(childBuffer, actualOffset, childRenderSize, renderSlotRect, layoutClip, null); }