public override void OnComputeBox(Renderman renderer, Css.LayoutBox box, ref bool widthUndefined, ref bool heightUndefined) { // Replaced: box.OrdinaryInline = false; if (widthUndefined) { if (heightUndefined) { // Both undefined - use the base: box.InnerWidth = RawWidth; box.InnerHeight = RawHeight; } else { // Apply width from the height: box.InnerWidth = box.InnerHeight * AspectRatio; } } else if (heightUndefined) { // Apply height from the width: box.InnerHeight = box.InnerWidth * InverseAspectRatio; } // They're always defined by the end of this: widthUndefined = false; heightUndefined = false; }
public override void OnComputeBox(Renderman renderer, Css.LayoutBox box, ref bool widthUndefined, ref bool heightUndefined) { if (Context2D != null) { Context2D.UpdateDimensions(box); } }
/// <summary>Clears all content from the UI and all WorldUI's. /// Please note that it is safer to set innerHTML to a blank string for a particular UI than calling this.</summary> public static void ClearAll() { content = null; if (Renderer != null) { Renderer.Destroy(); Renderer = null; document = null; } Fonts.Clear(); AtlasStacks.Clear(); Web.Clear(); Spa.SPA.Clear(); UIAnimation.Clear(); ScreenInfo.Clear(); WorldUI currentWorldUI = FirstWorldUI; while (currentWorldUI != null) { currentWorldUI.Destroy(); currentWorldUI = currentWorldUI.UIAfter; } LastWorldUI = null; FirstWorldUI = null; }
/// <summary>Clears all content from the UI and all WorldUI's. /// Please note that it is safer to set innerHTML to a blank string for a particular UI than calling this.</summary> public static void ClearAll() { content = null; if (Renderer != null) { Renderer.Destroy(); Renderer = null; document = null; } Fonts.Clear(); AtlasStacks.Clear(); Http.Clear(); SPA.Clear(); UIAnimation.Clear(); DynamicTexture.RemoveAll(); PowerUI.Input.Clear(); ScreenInfo.Clear(); WorldUI currentWorldUI = FirstWorldUI; while (currentWorldUI != null) { currentWorldUI.Destroy(); currentWorldUI = currentWorldUI.UIAfter; } LastWorldUI = null; FirstWorldUI = null; }
/// <summary>Creates a new document which will be rendered with the given renderer.</summary> /// <param name="renderer">The renderer to use when rendering this document.</param> /// <param name="parentWindow">The window that will become the parent window. Used in e.g. iframes.</param> /// <param name="aot">True if this is a Nitro AOT document (used in the Editor only).</param> public HtmlDocument(Renderman renderer, Window parentWindow, bool aot) : base(renderer == null?null:renderer.InWorldUI) { AotDocument = aot; if (renderer == null) { renderer = new Renderman(this); } Renderer = renderer; // Set the XHTML namespace: Namespace = Dom.HtmlLexer.XHTMLNamespace; window = new Window(); window.SetDocument(this); window.parent = parentWindow; if (parentWindow != null) { window.top = parentWindow.top; } else { window.top = window; } // Clear style; this loads in the default stylesheet: ClearStyle(); }
/// <summary>Creates a new World UI with the given pixels of space and a given name. /// The gameobjects origin sits at the middle of the UI by default. See <see cref="PowerUI.WorldUI.SetOrigin"/>. /// By default, 100 pixels are 1 world unit. See <see cref="PowerUI.WorldUI.SetResolution"/>.</summary> /// <param name="name">The name for the UI's gameobject.</param> /// <param name="widthPX">The width in pixels of this UI.</param> /// <param name="heightPX">The height in pixels of this UI.</param> public WorldUI(string name, int widthPX, int heightPX) { // Start the UI: UI.Start(); // Create the gameobject: gameObject = new GameObject(); gameObject.name = name; // Grab the name: Name = name; transform = gameObject.transform; Renderer = new Renderman(this); SetDepthResolution(1f); // Apply the default scale: transform.localScale = new Vector3(1 / 100f, 1 / 100f, 1 / 100f); document = Renderer.RootDocument as HtmlDocument; // Add it to the UI update linked list: if (UI.FirstWorldUI == null) { UI.FirstWorldUI = UI.LastWorldUI = this; } else { UIBefore = UI.LastWorldUI; UI.LastWorldUI = UI.LastWorldUI.UIAfter = this; } SetDimensions(widthPX, heightPX); }
public override void OnComputeBox(Renderman renderer, Css.LayoutBox box, ref bool widthUndefined, ref bool heightUndefined) { if (document is SVGDocument) { return; } // Occurs on inline SVG's. // Set the size: Context.SetSize((int)box.InnerWidth, (int)box.InnerHeight); UnityEngine.Texture tex = Context.Texture; if (tex == null) { return; } // Update the background raw image: BackgroundImage img = RenderData.BGImage; if (img == null) { img = new Css.BackgroundImage(RenderData); RenderData.BGImage = img; } // Update the bg image: img.UpdateImage(tex); }
/// <summary>Called when the renderer for this batch has changed.</summary> public void ChangeRenderer(Renderman renderer) { Renderer = renderer; RenderWithCamera(renderer.RenderLayer); // Let the mesh know that the parent changed: Mesh.ChangeParent(); InputMode mode; if (Renderer.RenderingInWorld) { mode = PowerUI.Input.WorldInputMode; } else { mode = PowerUI.Input.Mode; } bool isPhysics = (mode == InputMode.Physics); if (isPhysics == PhysicsMode) { return; } Mesh.SetPhysicsMode(isPhysics); }
protected override void Layout() { Renderman renderer = Element.Document.Renderer; ComputedStyle computed = Element.Style.Computed; // Get the top left inner corner (inside margin and border): int width = computed.PaddedWidth; int height = computed.PaddedHeight; int top = computed.OffsetTop + computed.BorderTop; int left = computed.OffsetLeft + computed.BorderLeft; // Is it clipped? if (renderer.IsInvisible(left, top, width, height)) { // Totally not visible. return; } // Ensure we have a batch (doesn't change graphics or font thus both nulls): SetupBatch(null, null); Add(); // Using firstblock as our block here. // Set the UV to that of the solid block colour pixel: FirstBlock.SetSolidColourUV(); // Set the (overlay) colour: FirstBlock.SetColour(BackingColour); // And finally sort out the verts: FirstBlock.SetClipped(renderer.ClippingBoundary, new BoxRegion(left, top, width, height), renderer, computed.ZIndex - 0.006f); }
/// <summary>Calculates where the transformation origin should go in screen space.</summary> /// <param name="relativeTo">The computed style of the element that the origin will be /// relative to if the origin position is 'Relative'</param> private void CalculateOrigin(ComputedStyle relativeTo) { // We need to figure out where the origin is and then apply the parent transformation to it. _Origin = _OriginOffset; if (_OriginOffsetPercX) { _Origin.x *= relativeTo.PixelWidth; } if (_OriginOffsetPercY) { _Origin.y *= relativeTo.PixelHeight; } if (_OriginPosition == PositionType.Relative) { _Origin.x += relativeTo.OffsetLeft; _Origin.y += relativeTo.OffsetTop; } // Map origin to world space: Renderman renderer = relativeTo.Element.Document.Renderer; _Origin = renderer.PixelToWorldUnit(_Origin.x, _Origin.y, relativeTo.ZIndex); if (Parent != null) { _Origin = Parent.Apply(_Origin); } }
/// <summary>Sets the vertices of this box to that specified by the given block /// but clipped to fit within a boundary.</summary> /// <param name="boundary">The clipping boundary. The vertices will be clipped to within this.</param> /// <param name="block">The position of the vertices.</param> /// <param name="zIndex">The depth of the vertices.</param> public void SetClipped(BoxRegion boundary, BoxRegion block, Renderman renderer, float zIndex) { // Clipping with no image/ affect on UVs: block.ClipBy(boundary); // And just apply the result: ApplyVertices(block, renderer, zIndex); }
/// <summary>Gets a batch from the pool. Null if the pool is empty.</summary> public static UIBatch Get(Renderman renderer) { if (First == null) { return(null); } UIBatch result = First; First = result.BatchAfter; result.BatchAfter = null; result.Setup = false; // Show it: #if PRE_UNITY4 result.Mesh.OutputGameObject.active = true; #else result.Mesh.OutputGameObject.SetActive(true); #endif if (result.Renderer != renderer) { result.ChangeRenderer(renderer); } return(result); }
public override void OnRender(Renderman renderer) { // Get the target: HtmlElement target = ScrollBar.scrollTarget; if (target == null) { return; } ComputedStyle computed = target.style.Computed; LayoutBox box = computed.FirstBox; if (box == null) { // Not visible or hasn't been drawn yet. return; } int overflowMode = IsVertical?box.OverflowY : box.OverflowX; float visible = IsVertical?box.VisiblePercentageY() : box.VisiblePercentageX(); if (visible > 1f) { visible = 1f; } else if (visible < 0f) { visible = 0f; } // Handle auto next: if (overflowMode == VisibilityMode.Auto && visible == 1f) { // Hide it: ScrollBar.Style.display = "none"; // Make sure it's not scrolled: if (IsVertical && box.Scroll.Top != 0f) { if (box.Scroll.Top != 0f) { // Clear it: computed.ChangeTagProperty("scroll-top", new Css.Units.DecimalUnit(0f)); } } else if (box.Scroll.Left != 0f) { computed.ChangeTagProperty("scroll-left", new Css.Units.DecimalUnit(0f)); } // Mark as hidden so the informer can watch out for it: ScrollBar.Hidden = true; return; } ApplyTabSize(visible, Style.Computed.FirstBox); }
public SVGDocument() : base(null) { // Apply the namespace: Namespace = SVGNamespace; Renderer = new Renderman(this); // Clear style; this loads in the default stylesheet: ClearStyle(); }
/// <summary>Called when the renderer for this batch has changed.</summary> public void ChangeRenderer(Renderman renderer) { Renderer = renderer; RenderWithCamera(renderer.RenderLayer); // Let the mesh know that the parent changed: Mesh.ChangeParent(); }
public override void OnComputeBox(Renderman renderer, Css.LayoutBox box, ref bool widthUndefined, ref bool heightUndefined) { // Update viewport: if (ContentDocument == null) { return; } ContentDocument.Viewport.Height = box.InnerHeight; ContentDocument.Viewport.Width = box.InnerWidth; }
/// <summary>This locates the vertices of this block in world space to the position defined by the given box.</summary> /// <param name="block">The position of the vertices in screen coordinates.</param> /// <param name="renderer">The renderer used when rendering this block.</param> /// <param name="zIndex">The depth of the vertices.</param> internal void ApplyVertices(BoxRegion block, Renderman renderer, float zIndex) { // Compute the min/max pixels: Vector3 min = renderer.PixelToWorldUnit(block.X, block.Y, zIndex); Vector3 max = renderer.PixelToWorldUnit(block.MaxX, block.MaxY, zIndex); // Get the 4 corners: VertexTopLeft = min; VertexBottomRight = max; VertexTopRight = new Vector3(max.x, min.y, min.z); VertexBottomLeft = new Vector3(min.x, max.y, min.z); }
public RoundedCorners(BorderProperty border){ Border=border; // Grab the renderer: Renderer=Border.Element.Document.Renderer; // Grab the computed style: Computed=border.Element.style.Computed; // Create the inverse border set: InverseBorder=new RoundBorderInverseProperty(border.Element); }
public RoundedCorners(BorderProperty border) { Border = border; // Grab the renderer: Renderer = Border.Element.Document.Renderer; // Grab the computed style: Computed = border.Element.style.Computed; // Create the inverse border set: InverseBorder = new RoundBorderInverseProperty(border.Element); }
/// <summary>Creates a new document which will be rendered with the given renderer.</summary> /// <param name="renderer">The renderer to use when rendering this document.</param> /// <param name="parentWindow">The window that will become the parent window. Used in e.g. iframes.</param> /// <param name="aot">True if this is a Nitro AOT document (used in the Editor only).</param> public Document(Renderman renderer, Window parentWindow, bool aot) : base() { AotDocument = aot; if (!aot && DefaultStyleSheet == null) { // No default styles loaded yet. Load them now. string styleText = ((TextAsset)Resources.Load("style")).text; // Have they applied any overrides? TextAsset extraStyle = Resources.Load("customStyle") as TextAsset; if (extraStyle != null && extraStyle.text != null) { styleText += "\n\n" + extraStyle.text; } DefaultStyleSheet = new Css.StyleSheet(this); DefaultStyleSheet.ParseCss(styleText); } #if !NoNitroRuntime // Get the default security domain: SecurityDomain = UI.DefaultSecurityDomain; #endif Renderer = renderer; window = new Window(); window.document = this; window.parent = parentWindow; if (parentWindow != null) { window.top = parentWindow.top; } else { window.top = window; } ActiveFonts = new Dictionary <string, DynamicFont>(); Style = new Css.StyleSheet(this); html = new Element(this, null); html.SetTag("html"); string ddbox = ""; if (parentWindow == null) { // Dropdown box belongs to the top window only: ddbox = "<ddbox></ddbox>"; } html.innerHTML = "<body></body>" + ddbox; }
public override void OnComputeBox(Renderman renderer, Css.LayoutBox box, ref bool widthUndefined, ref bool heightUndefined) { RectangleProvider rect = Rectangle; // Check to see if w/h was updated via CSS: Css.Value newWidth = Width; Css.Value newHeight = Height; if (rect.Width != newWidth || rect.Height != newHeight) { rect.Width = newWidth; rect.Height = newHeight; RebuildPath(); } }
public override void OnComputeBox(Renderman renderer, Css.LayoutBox box, ref bool widthUndefined, ref bool heightUndefined) { // Replaced: box.OrdinaryInline = false; if (widthUndefined) { if (heightUndefined) { // Both undefined - establish which we'll be primarily clipping by. if (Style.Computed.ShouldClipHeight()) { // Height priority. Clip by min/max-height: box.InnerHeight = Style.Computed.ClipHeight( box.DisplayMode, RawHeight * Style.Computed.RenderData.ValueScale ); // Derive height from the aspect ratio: box.InnerWidth = box.InnerHeight * AspectRatio; } else { // Width priority. Clip by min/max-width: box.InnerWidth = Style.Computed.ClipWidth( box.DisplayMode, RawWidth * Style.Computed.RenderData.ValueScale ); // Derive height from the aspect ratio: box.InnerHeight = box.InnerWidth * InverseAspectRatio; } } else { // Apply width from the height: box.InnerWidth = box.InnerHeight * AspectRatio; } } else if (heightUndefined) { // Apply height from the width: box.InnerHeight = box.InnerWidth * InverseAspectRatio; } // They're always defined by the end of this: widthUndefined = false; heightUndefined = false; }
public override void OnComputeBox(Renderman renderer, Css.LayoutBox box, ref bool widthUndefined, ref bool heightUndefined) { // Get meta: LineBoxMeta lbm = renderer.TopOfStackSafe; // If the line is empty, set some height: if (lbm.FirstOnLine == null) { heightUndefined = false; box.InnerHeight = Style.Computed.FontSizeX; box.Height = box.InnerHeight; } // Implicit line break: lbm.CompleteLine(LineBreakMode.Normal | LineBreakMode.Last); }
public override void OnComputeBox(Renderman renderer, Css.LayoutBox thumbBox, ref bool widthUndefined, ref bool heightUndefined) { if (IsVertical) { if (thumbBox.Position.Top == float.MaxValue) { // Initial push over the first arrow: thumbBox.Position.Top = StartArrowSize; } } else if (thumbBox.Position.Left == float.MaxValue) { // Initial push over the first arrow: thumbBox.Position.Left = StartArrowSize; } }
/// <summary>Destroys this UI. Note that this also occurs if the gameobject is destroyed; /// Just destroying the gameobject or a parent gameObject is all that is required.</summary> public virtual void Destroy() { if (Renderer == null) { return; } // Remove from the physics lookup if needed: if (PhysicsLookup != null && transform != null) { PhysicsLookup.Remove(transform); if (PhysicsLookup.Count == 0) { PhysicsLookup = null; } } Renderer.Destroy(); Renderer = null; if (gameObject != null) { GameObject.Destroy(gameObject); gameObject = null; transform = null; } // Remove it from the UI update linked list: if (UIBefore == null) { UI.FirstWorldUI = UIAfter; } else { UIBefore.UIAfter = UIAfter; } if (UIAfter == null) { UI.LastWorldUI = UIBefore; } else { UIAfter.UIBefore = UIBefore; } }
/// <summary>Attempts to find the element from the set WorldUI; if there is no WorldUI, the main UI is used. /// This search uses the triangle of the hit to figure out exactly which element was clicked.</summary> /// <param name="hit">The hit in 3D that must be resolved to an element.</param> public void FindElement(RaycastHit hit) { // Which triangle was hit, and as a result, which element did it come from? // If the element is found, apply it to our result; otherwise assume unsuccessful hit. Renderman renderer = null; if (OnWorldUI != null) { renderer = OnWorldUI.Renderer; } else { renderer = UI.GetRenderer(); } if (renderer == null) { return; } Transform transform = hit.transform; // Which batch? Will only be from the non-pooled ones: UIBatch current = renderer.FirstBatch; while (current != null) { if (current.Mesh.OutputTransform == transform) { // Got it! break; } current = current.BatchAfter; } if (current == null) { return; } // Current is the batch the hit was on. Next, resolve to the MeshBlock and finally to the element that made it. }
/// <summary>Allocates a block from this mesh. Note that the block object is actually shared. The block can then have /// its vertices/triangles edited. Changes will be outputted visually when MeshBlock.Done is called.</summary> public MeshBlock Allocate(Renderman renderer) { MeshBlock block = renderer.Block; block.Colour = Color.white; block.TextUV = null; block.ImageUV = null; if (FirstBuffer == null || CurrentBufferBlocks == MeshDataBufferPool.BlockCount) { NextBuffer(); } // Apply buffer and block index: block.Buffer = LastBuffer; block.BlockIndex = CurrentBufferBlocks; // Bump up the index: CurrentBufferBlocks++; return(block); }
public override void OnComputeBox(Renderman renderer, Css.LayoutBox box, ref bool widthUndefined, ref bool heightUndefined) { // Locate the caret if we need to: if (Locate) { Locate = false; RenderableTextNode htn = TextHolder; Vector2 position; if (htn == null) { // Just at 0,0: position = Vector2.zero; } else { // Clip: if (Index >= htn.length) { Index = htn.length; } // Get the position of the given letter: position = htn.GetPosition(Index); } // Scroll it if position is out of range: ScrollIfBeyond(ref position); // Set it in for this pass: box.Position.Top = position.y; box.Position.Left = position.x; // Write it out: Style.Computed.ChangeTagProperty("left", new Css.Units.DecimalUnit(position.x), false); Style.Computed.ChangeTagProperty("top", new Css.Units.DecimalUnit(position.y), false); } }
/// <summary>Permanently destroys this UI batch.</summary> public void Destroy() { if (Renderer == null) { return; } if (IsolatedProperty != null) { IsolatedProperty.Isolated = false; IsolatedProperty.OnBatchDestroy(); IsolatedProperty = null; } if (Mesh != null) { Mesh.Destroy(); Mesh = null; } Renderer = null; }
/// <summary>Called during the layout pass.</summary> public override void OnRender(Renderman renderer) { if (ParticleTransform == null) { return; } // Grab the computed style and the renderer: ComputedStyle computed = Style.Computed; LayoutBox box = computed.FirstBox; if (box == null) { // display:none. return; } // Get the top left inner corner (inside margin and border): float width = box.PaddedWidth; float height = box.PaddedHeight; float top = box.Y + box.Border.Top; float left = box.X + box.Border.Left; // Figure out the middle of that: float middleX = left + (width / 2); float middleY = top + (height / 2); // Map it to our world location: ParticleTransform.localPosition = renderer.PixelToWorldUnit(middleX, middleY, computed.ZIndex); BatchProperty.GotBatchAlready = false; // Setup the batch (so we can get the queue number): renderer.SetupBatch(BatchProperty, null, null); // Set the particle material to the batch - this'll ensure it gets the right renderQueue: renderer.CurrentBatch.Mesh.SetMaterial(ParticleMaterial); }
/// <summary>Creates a new UI Camera which will be rendered with the given renderer.</summary> /// <param name="renderer">The renderer that will render this camera.</param> public UICamera(Renderman renderer) { Renderer = renderer; // Create the root gameobject: Gameobject = new GameObject(); // Create camera gameobject: CameraObject = new GameObject(); // Parent the camera to the root: CameraObject.transform.parent = Gameobject.transform; // Add a camera: SourceCamera = CameraObject.AddComponent <Camera>(); // Set the clear flags: SourceCamera.clearFlags = CameraClearFlags.Depth; // Set the culling mask: SourceCamera.cullingMask = (1 << UI.Layer); // Make it forward rendered: SourceCamera.renderingPath = RenderingPath.Forward; // Setup the cameras distance: SetCameraDistance(UI.GetCameraDistance()); // Setup the field of view: SetFieldOfView(UI.GetFieldOfView()); // Parent it to the root: Gameobject.transform.parent = UI.GUINode.transform; // Call the camera creation method: UI.CameraGotCreated(SourceCamera); }
/// <summary>Draws a character with x-inverted UV's. Used for rendering e.g. "1 < 2" in right-to-left.</summary> private void DrawInvertCharacter(int index,ref float left,float top,Renderman renderer,float zIndex,BoxRegion screenRegion){ Glyph character=Characters[index]; if(character==null){ return; } if(Kerning!=null){ left+=Kerning[index] * FontSize; } if(character.Space){ left+=SpaceSize+LetterSpacing; return; } float y=top+Ascender-((character.Height+character.MinY) * FontSize); AtlasLocation locatedAt=character.Location; if(locatedAt==null){ // Not in font. return; } screenRegion.Set(left + (character.LeftSideBearing * FontSize),y,locatedAt.Width * ScaleFactor,locatedAt.Height * ScaleFactor); if(screenRegion.Overlaps(renderer.ClippingBoundary)){ // True if this character is visible. // Ensure correct batch: SetupBatch(null,locatedAt.Atlas); MeshBlock block=Add(); block.SetColour(FontColour); // And clip our meshblock to fit within boundary: block.ImageUV=null; UVBlock uvs=block.SetClipped(renderer.ClippingBoundary,screenRegion,renderer,zIndex,locatedAt,block.TextUV); if(uvs.Shared){ uvs=new UVBlock(uvs); } // Invert along X: float temp=uvs.MinX; uvs.MinX=uvs.MaxX; uvs.MaxX=temp; // Assign to the block: block.TextUV=uvs; } left+=(character.AdvanceWidth * FontSize)+LetterSpacing; }
/// <summary>Draws a character and advances the pen onwards.</summary> private void DrawCharacter(int index,ref float left,float top,Renderman renderer,float zIndex,BoxRegion screenRegion){ Glyph character=Characters[index]; if(character==null){ return; } if(Kerning!=null){ left+=Kerning[index] * FontSize; } AtlasLocation locatedAt; if(character.Image!=null){ if(!character.Image.Loaded()){ return; } // It's an image (e.g. Emoji). locatedAt=RequireImage(character.Image); if(locatedAt==null){ // It needs to be isolated. Big emoji image! return; } if(CharacterProviders.FixHeight){ // Set the region: screenRegion.Set(left,top,locatedAt.Width,locatedAt.Height); }else{ screenRegion.Set(left,top,FontSize,FontSize); } if(screenRegion.Overlaps(renderer.ClippingBoundary)){ // Ensure correct batch: SetupBatch(locatedAt.Atlas,null); // If the two overlap, this means it's actually visible. MeshBlock block=Add(); // Set it's colour: block.SetColour(Element.Style.Computed.ColorOverlay); // And clip our meshblock to fit within boundary: block.TextUV=null; block.ImageUV=block.SetClipped(renderer.ClippingBoundary,screenRegion,renderer,zIndex,locatedAt,block.ImageUV); } left+=(character.AdvanceWidth)+LetterSpacing; return; }else if(character.Space){ left+=SpaceSize+LetterSpacing; return; } locatedAt=character.Location; if(locatedAt==null){ // Not in font. return; } float y=top+Ascender-((character.Height+character.MinY) * FontSize); screenRegion.Set(left + (character.LeftSideBearing * FontSize),y,locatedAt.Width * ScaleFactor,locatedAt.Height * ScaleFactor); if(screenRegion.Overlaps(renderer.ClippingBoundary)){ // True if this character is visible. // Ensure correct batch: SetupBatch(null,locatedAt.Atlas); MeshBlock block=Add(); block.SetColour(FontColour); // And clip our meshblock to fit within boundary: block.ImageUV=null; block.TextUV=block.SetClipped(renderer.ClippingBoundary,screenRegion,renderer,zIndex,locatedAt,block.TextUV); } left+=(character.AdvanceWidth * FontSize)+LetterSpacing; }