public override void Select(CssEvent e) { // Get the CS: ComputedStyle cs = e.SelectorTarget.computedStyle; // Create and apply using an informer: SparkInformerNode informer = cs.GetOrCreateVirtualInformer(Priority, true); if (informer.OnStart == null) { // Add a text node as the first child: RenderableTextNode textNode = new RenderableTextNode(); informer.appendChild(textNode); // Setup the delegates! informer.OnStart = delegate(Renderman renderer, SparkInformerNode node){ // Apply first letter - it'll be consumed by the first text node to encounter it: renderer.FirstLetter = node; }; informer.OnEnd = delegate(Renderman renderer, SparkInformerNode node){ // Just in case it wasn't used up: renderer.FirstLetter = null; }; } e.SelectorTarget = informer.RenderData; }
/// <summary>Called when the parent element has started rendering its kids.</summary> public void Start(Renderman renderer) { // Push: NextInformer = renderer.FirstInformer; renderer.FirstInformer = this; if (OnStart != null) { OnStart(renderer, this); } }
/// <summary>Resets all values in the renderer. Called before each layout.</summary> public void Reset() { Depth = 0f; MaxDepth = 0f; ViewportBackground = true; BatchDepth = RenderQueue; PositionedAncestors.Clear(); TransformedAncestors.Clear(); FlowRootAncestors.Clear(); ColorOverlay = Color.white; FontAliasingTop = InfiniText.Fonts.OutlineLocation + InfiniText.Fonts.Aliasing; FontAliasingBottom = InfiniText.Fonts.OutlineLocation - InfiniText.Fonts.Aliasing; BoxStack.Clear(); LastBlockBox = null; ResetBoundary(); DepthUsed = false; CurrentBatch = null; CurrentShaderSet = ShaderSet.Standard; FirstInformer = null; FirstLetter = null; FirstLine = null; if (Counters != null) { Counters.Clear(); } // Reapply screen viewport as the root one: Viewport = ScreenViewport; // Clean input grid: InputGrid.Clean(Viewport.Width, Viewport.Height); if (InWorldUI == null) { // This is the main UI renderer. // Clear the root node: Node = null; } }
public override void Reflow(Renderman renderer) { // Get the text renderer (or create it): Css.TextRenderingProperty text = RequireTextProperty(); if (text.Characters == null || text.AllEmpty) { return; } LayoutBox box = null; // Get the baseline offset: float baseline = text.FontSize * text.FontToDraw.Descender; // Compute our line boxes based on text.Characters and the available space. // Safely ignore direction here because either way the selected characters are the same. // Note that things like first-letter are also considered. // Get the top of the stack: LineBoxMeta lbm = renderer.TopOfStack; float cssLineHeight = lbm.CssLineHeight; // Is it justify (if so, every word is its own box): bool breakAllWords = (lbm.HorizontalAlign == HorizontalAlignMode.Justify); // Offset the baseline: float lineHeightOffset = (cssLineHeight - text.FontSize) / 2f; text.LineHeightOffset = lineHeightOffset; baseline += lineHeightOffset; float wordWidth = 0f; float boxWidth = 0f; bool wrappable = ((lbm.WhiteSpace & WhiteSpaceMode.Wrappable) != 0); int i = 0; int latestBreakpoint = -1; // bidi text direction (0 = Not set yet, UnicodeBidiMode.LeftwardsNormal, UnicodeBidiMode.RightwardsNormal) int direction = 0; // Direction before the current block of weak chars. int beforeWeak = 0; while (i < text.Characters.Length) { // Get the glyph: InfiniText.Glyph glyph = text.Characters[i]; if (glyph == null) { // Skip! i++; continue; } // The glyph's width is.. float width = (glyph.AdvanceWidth * text.FontSize) + text.LetterSpacing; if (box == null) { // The box always has an inner height of 'font size': if (renderer.FirstLetter != null) { // Clear FL immediately (so it can't go recursive): SparkInformerNode firstLetter = renderer.FirstLetter; renderer.FirstLetter = null; // Update its internal text node: RenderableTextNode textNode = firstLetter.firstChild as RenderableTextNode; // Note that we have to do it this way as the node might // change the *font*. textNode.characterData_ = ((char)glyph.Charcode) + ""; Css.TextRenderingProperty textData = textNode.RenderData.RequireTextProperty(); textData.Dirty = true; // Ask it to reflow right now (must ask the node so it correctly takes the style into account): firstLetter.RenderData.UpdateCss(renderer); firstLetter.RenderData.Reflow(renderer); i++; continue; } // Create the box now: box = new LayoutBox(); box.PositionMode = PositionMode.Static; box.DisplayMode = DisplayMode.Inline; box.Baseline = baseline; box.TextStart = i; // Adopt current direction: box.UnicodeBidi = direction; if (FirstBox == null) { FirstBox = box; LastBox = box; } else { // add to this element: LastBox.NextInElement = box; LastBox = box; } // line-height is the inner height here: box.InnerHeight = cssLineHeight; boxWidth = 0f; wordWidth = 0f; } // Are we breaking this word? bool implicitNewline = (glyph.Charcode == (int)'\n'); int breakMode = implicitNewline ? 1 : 0; // direction: int dir = glyph.Directionality; if (dir == InfiniText.BidiBlock.Rightwards) { // Rightwards (includes weak ones): // Was the previous one 'weak'? if (dir == InfiniText.BidiBlock.WeakLeftToRight) { // Update before weak: beforeWeak = direction; } else { // Not weak: beforeWeak = 0; } if (direction != UnicodeBidiMode.RightwardsNormal) { if (direction == 0) { // Not set yet - Set the mode into the box: box.UnicodeBidi = UnicodeBidiMode.RightwardsNormal; } else { // Changed from leftwards to rightwards. Break the boxes here. breakMode = 3; } // Update dir: direction = UnicodeBidiMode.RightwardsNormal; } } else if (dir == InfiniText.BidiBlock.RightToLeft) { // Leftwards if (direction != UnicodeBidiMode.LeftwardsNormal) { if (direction == 0) { // Not set yet - Set the mode into the box: box.UnicodeBidi = UnicodeBidiMode.LeftwardsNormal; } else { // Changed from rightwards to leftwards. Break the boxes here. breakMode = 3; } // Update dir: direction = UnicodeBidiMode.LeftwardsNormal; } // Never weak: beforeWeak = 0; } else if (beforeWeak != 0) { // Neutral otherwise // (adopts whatever the current is, unless the previous one was *weak*, // in which case, it adopts whatever the direction was before that): if (direction != beforeWeak) { // Change direction! direction = beforeWeak; breakMode = 3; } } // Got a space? bool space = (glyph.Charcode == (int)' '); if (space) { // Advance width: width += text.WordSpacing; if (breakAllWords) { breakMode = 3; } else { latestBreakpoint = i; } // Lock in the previous text: boxWidth += wordWidth + width; wordWidth = 0f; } else { // Advance word width now: wordWidth += width; } // Word wrapping next: if (breakMode == 0 && wrappable && i != box.TextStart) { // Test if we can break here: breakMode = lbm.GetLineSpace(wordWidth, boxWidth); // Return to the previous space if we should. if (breakMode == 2 && text.OverflowWrapActive) { // The word doesn't fit at all (2) and we're supposed to break it. boxWidth += wordWidth - width; wordWidth = 0f; } else if (breakMode != 0) { if (latestBreakpoint == -1) { // Isn't a previous space! if (breakMode == 2) { // Instead, we'll try and break a parent. // This typically happens with inline elements // which are right on the end of the host line. lbm.TryBreakParent(); // Don't break the node: breakMode = 0; } else if (breakMode == 3) { // Break but no newline - just advance to the following char: i++; } else if (lbm.PenX != lbm.LineStart) { // Newline: lbm.CompleteLine(LineBreakMode.Normal); // Don't break the node: breakMode = 0; } else { // Don't break the node: breakMode = 0; } } else { i = latestBreakpoint + 1; } } } if (breakMode != 0) { // We're breaking! box.InnerWidth = boxWidth; box.TextEnd = i; latestBreakpoint = i; // If the previous glyph is a space, update EndSpaceSize: if (space) { // Update ending spaces: box.EndSpaceSize = width; } // Ensure dimensions are set: box.SetDimensions(false, false); // Add the box to the line: lbm.AddToLine(box); // Update dim's: lbm.AdvancePen(box); // Clear: box = null; boxWidth = 0f; if (breakMode != 3) { // Newline: if (implicitNewline) { // Also the last line: lbm.CompleteLine(LineBreakMode.Normal | LineBreakMode.Last); } else { // Normal: lbm.CompleteLine(LineBreakMode.Normal); // Process it again. continue; } } } // Next character: i++; } if (box != null) { // Always apply inner width: box.InnerWidth = boxWidth + wordWidth; box.TextEnd = text.Characters.Length; // Ensure dimensions are set: box.SetDimensions(false, false); // Add the box to the line: lbm.AddToLine(box); // Update dim's: lbm.AdvancePen(box); } }