/// <summary>This is the second pass of layout requests. /// It positions the element in global screen space and also fires the render events /// which in turn generate or reallocates mesh blocks. This call applies to all it's /// children elements too.</summary> /// <param name="relativeTo">The current style we are positioning relative to.</param> public void PositionGlobally(ComputedStyle relativeTo) { ComputedStyle computed = Style.Computed; if (computed.Display == DisplayType.None) { // Don't draw this element or it's kids. return; } // Position globally: if (computed.Position == PositionType.Fixed) { // Fixed elements are nice and simple to deal with - Their essentially absolutely positioned but relative to the html tag. ComputedStyle html = Document.html.style.Computed; if (computed.RightPositioned) { // The right hand edge of parent minus how far from the edge minus the width. computed.OffsetLeft = computed.MarginLeft + html.OffsetLeft + html.PixelWidth - computed.PositionRight - computed.PixelWidth; } else { computed.OffsetLeft = computed.MarginLeft + html.OffsetLeft + computed.PositionLeft; } if (computed.BottomPositioned) { computed.OffsetTop = computed.MarginTop + html.OffsetTop + html.PixelHeight - computed.PositionBottom - computed.PixelHeight; } else { computed.OffsetTop = computed.MarginTop + html.OffsetTop + computed.PositionTop; } // Change relativeTo here - we are now hopping to this fixed objects kids. relativeTo = computed; } else if (computed.Position == PositionType.Relative) { // Relative to where they should have been. PositionLeft/PositionRight etc. may be zero, but not always. // The width of border/padding/margin + the position of the parent + the offet from the parent. if (relativeTo == null) { computed.OffsetLeft = computed.MarginLeft + computed.ParentOffsetLeft; } else { computed.OffsetLeft = computed.MarginLeft + relativeTo.StyleOffsetLeft + relativeTo.OffsetLeft + computed.ParentOffsetLeft; } if (computed.RightPositioned) { computed.OffsetLeft -= computed.PositionRight; } else { computed.OffsetLeft += computed.PositionLeft; } if (relativeTo == null) { computed.OffsetTop = computed.MarginTop + computed.ParentOffsetTop; } else { computed.OffsetTop = computed.MarginTop + relativeTo.StyleOffsetTop + relativeTo.OffsetTop + computed.ParentOffsetTop; } if (computed.BottomPositioned) { computed.OffsetTop -= computed.PositionBottom; } else { computed.OffsetTop += computed.PositionTop; } // Vertical alignment: bool tableCell = (computed.Display == DisplayType.TableCell); if (relativeTo != null || computed.AutoMarginY || tableCell) { if (computed.AutoMarginY || relativeTo.VerticalAlign == VerticalAlignType.Middle || (tableCell && computed.VerticalAlign == VerticalAlignType.Middle)) { // Similar to below - we find the gap, then add *half* of that onto OffsetTop. if (tableCell) { // Move upwards - we're sitting on the line and want to be above it. computed.OffsetTop -= (relativeTo.InnerHeight - computed.PixelHeight) / 2; } else { computed.OffsetTop += (relativeTo.InnerHeight - relativeTo.ContentHeight) / 2; } } else if (relativeTo.VerticalAlign == VerticalAlignType.Bottom) { // Find the gap - parent height-contentHeight. // Then simply add that onto offsetTop. computed.OffsetTop += relativeTo.InnerHeight - relativeTo.ContentHeight; } else if (tableCell && computed.VerticalAlign == VerticalAlignType.Top) { // This time we find the gap and remove it - we're at the bottom by default as a td sits on the line. computed.OffsetTop -= relativeTo.InnerHeight - computed.PixelHeight; } } // Note: relativeTo does not change here if we're in an inline element: if (computed.Display != DisplayType.Inline) { relativeTo = computed; } } else { // Absolute - relative to parent. It's ParentOffsetLeft/Top are both zero. if (computed.RightPositioned) { // The right hand edge of parent minus how far from the edge minus the width. computed.OffsetLeft = computed.MarginLeft + relativeTo.OffsetLeft - relativeTo.StyleOffsetLeft - relativeTo.ScrollLeft + relativeTo.PixelWidth - computed.PositionRight - computed.PixelWidth; } else { computed.OffsetLeft = computed.MarginLeft + relativeTo.OffsetLeft + relativeTo.StyleOffsetLeft + relativeTo.ScrollLeft + computed.PositionLeft; } if (computed.BottomPositioned) { computed.OffsetTop = computed.MarginTop + relativeTo.OffsetTop - relativeTo.StyleOffsetTop - relativeTo.ScrollTop + relativeTo.PixelHeight - computed.PositionBottom - computed.PixelHeight; } else { computed.OffsetTop = computed.MarginTop + relativeTo.OffsetTop + relativeTo.StyleOffsetTop + relativeTo.ScrollTop + computed.PositionTop; } // Set relativeTo to this - this is because the kids of absolute objects are relative to the absolute object itself. relativeTo = computed; } // Push the transform to our stack, if we have one. if (computed.Transform != null) { // Add it to the stack: Document.Renderer.Transformations.Push(computed.Transform); // Update it: computed.Transform.RecalculateMatrix(computed); } // Great, it's good to go! computed.Render(); if (KidsToRender != null || HScrollbar || VScrollbar) { BoxRegion parentBoundary = null; if (relativeTo == computed) { // We changed who we're relative to. // Change the clipping boundary: Renderman renderer = Document.Renderer; parentBoundary = renderer.ClippingBoundary; renderer.SetBoundary(computed); } if (KidsToRender != null) { for (int i = 0; i < KidsToRender.Count; i++) { Element child = KidsToRender[i]; if (child != null) { child.PositionGlobally(relativeTo); } } } if (HScrollbar) { HorizontalScrollbar.Element.PositionGlobally(relativeTo); } if (VScrollbar) { VerticalScrollbar.Element.PositionGlobally(relativeTo); } if (relativeTo == computed) { // Restore the previous boundary before this one: [Note - can't use SetBoundary here as it would destroy the box.] Document.Renderer.ClippingBoundary = parentBoundary; } } if (computed.Transform != null) { // Pop it off again: Document.Renderer.Transformations.Pop(); } }