/// <summary>Finds the index of the nearest character to x pixels.</summary>
        /// <param name="x">The number of pixels from the left edge of this text element.</param>
        /// <returns>The index of the nearest letter.</returns>
        public int LetterIndex(int x)
        {
            if (ChildNodes == null)
            {
                return(0);
            }

            int widthSoFar   = 0;
            int lettersSoFar = 0;

            for (int i = 0; i < ChildNodes.Count; i++)
            {
                WordElement word = (WordElement)ChildNodes[i];
                // Grab the words computed style:
                Css.ComputedStyle wordStyle = word.Style.Computed;

                // Bump up the current width:
                widthSoFar += wordStyle.PixelWidth;

                if (x <= widthSoFar)
                {
                    int localWidthOffset = x - (widthSoFar - wordStyle.PixelWidth);
                    lettersSoFar += wordStyle.Text.LetterIndex(localWidthOffset);
                    break;
                }
                else
                {
                    lettersSoFar += word.LetterCount();
                }
            }
            return(lettersSoFar);
        }
		public override void Apply(ComputedStyle style,Value value){
			
			// Is it lit?
			bool lit=(value!=null && value.Boolean);
			
			if(!lit && style.Shading==null){
				// Essentially ignore it anyway.
				return;
			}
			
			if(style.Shading==null){
				// It's lit:
				style.RequireShading().Lit=true;
			}else{
				// It's not lit:
				style.Shading.Lit=false;
				
				// Optimise - might no longer need the shading info:
				style.Shading.Optimise();
			}
			
			// Request a layout now:
			style.RequestLayout();
			
		}
		/// <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);
			}
		}
		public override void Apply(ComputedStyle style,Value value){
			
			// Got text at all?:
			if(GetText(style)==null){
				return;
			}
			
			// Apply the property:
			if(value==null || value.Text=="none"){
				
				// Clear the shadow:
				style.TextShadow=null;
				
			}else{
			
				// The glow properties:
				int blur=0;
				Color colour=Color.black;
				
				ShadowData data=style.TextShadow;
				
				if(data==null){
					data=new ShadowData();
					style.TextShadow=data;
				}
				
				data.HOffset=value[0].PX;
				data.VOffset=value[1].PX;
				
				// Grab the blur:
				Value innerValue=value[2];
				
				if(innerValue.Type==ValueType.Color){
					colour=innerValue.ToColor();
				}else{
					blur=innerValue.PX;
					
					// Grab the colour:
					innerValue=value[3];
					
					if(innerValue.Type==ValueType.Color){
						colour=innerValue.ToColor();
					}
					
				}
			
				if(colour.a==1f){
					// Default transparency:
					colour.a=0.8f;
				}
				
				data.Colour=colour;
				data.Blur=blur;
				
			}
			
			// Apply the changes - doesn't change anything about the actual text, so we just want a layout:
			style.RequestLayout();
			
		}
		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>Call this if the current property requires a background image object.</summary>
		public BackgroundImage GetBackground(ComputedStyle style){
			
			BackgroundImage image=style.BGImage;
			
			if(image==null){
				style.BGImage=image=new BackgroundImage(style.Element);
				style.EnforceNoInline();
			}
			
			// Flag it as having a change:
			image.Changed=true;
			
			return image;
		}
		/// <summary>Call this if the current property requies a border object.</summary>
		public BorderProperty GetBorder(ComputedStyle style){
			
			BorderProperty border=style.Border;
			
			if(border==null){
				style.Border=border=new BorderProperty(style.Element);
				style.EnforceNoInline();
			}
			
			// Flag it as having a change:
			border.Changed=true;
			
			return border;
		}
        /// <summary>Finds the index of the nearest character to x pixels.</summary>
        /// <param name="x">The number of pixels from the left edge of this text element.</param>
        /// <param name="y">The number of pixels from the top edge of this text element.</param>
        /// <returns>The index of the nearest letter.</returns>
        public int LetterIndex(int x, int y)
        {
            if (ChildNodes == null)
            {
                return(0);
            }

            int widthSoFar   = 0;
            int lettersSoFar = 0;

            for (int i = 0; i < ChildNodes.Count; i++)
            {
                WordElement word = (WordElement)ChildNodes[i];
                // Grab the words computed style:
                Css.ComputedStyle wordStyle = word.Style.Computed;
                // Find where the bottom of the line is at:
                int lineBottom = wordStyle.PixelHeight + wordStyle.ParentOffsetTop;

                if (lineBottom < y)
                {
                    // Not at the right line yet.
                    lettersSoFar += word.LetterCount();
                    continue;
                }

                // Crank over the width:
                widthSoFar += wordStyle.PixelWidth;


                if (x <= widthSoFar)
                {
                    int localWidthOffset = x - (widthSoFar - wordStyle.PixelWidth);
                    lettersSoFar += wordStyle.Text.LetterIndex(localWidthOffset);
                    break;
                }
                else
                {
                    lettersSoFar += word.LetterCount();
                }
            }

            return(lettersSoFar);
        }
		public override void Apply(ComputedStyle style,Value value){
			
			ShaderSet family=null;
			
			// Apply:
			if(value!=null){
				
				// Lowercase so we can have the best chance at spotting the standard set (which is optimised for).
				string familyLC=value.Text.ToLower();
				
				if(familyLC!="standardui" && familyLC!="standard" && familyLC!="" && familyLC!="none"){
					
					// Get the family:
					family=ShaderSet.Get(value.Text);
					
				}
				
			}
			
			// Apply it here:
			if(style.Shading!=null){
				
				// Update it:
				style.Shading.Shaders=family;
				
				if(family==null){
					// Check if the shading data is no longer in use:
					style.Shading.Optimise();
				}
				
			}else if(family!=null){
				
				style.RequireShading().Shaders=family;
				
			}
			
			// Request a layout now:
			style.RequestLayout();
			
		}
        /// <summary>Gets the relative position in pixels of the letter at the given index.</summary>
        /// <param name="index">The index of the letter in this text element.</param>
        /// <returns>The number of pixels from the left and top edges of this text element the letter is as a vector.</returns>
        public Vector2 GetPosition(ref int index)
        {
            if (index == 0 || ChildNodes == null)
            {
                return(Vector2.zero);
            }

            int         localOffset;
            WordElement word = GetWordWithLetter(index, out localOffset);

            if (word == null)
            {
                index -= localOffset;
                return(Vector2.zero);
            }

            Css.ComputedStyle computed = word.Style.Computed;
            float             left     = computed.ParentOffsetLeft + computed.Text.LocalPositionOf(localOffset);
            float             top      = computed.ParentOffsetTop;

            return(new Vector2(left, top));
        }
        /// <summary>Gets the fontsize for the given computed style.</summary>
        /// <param name="parentStyle">The style to get the fontsize from. Used for em calculations.</param>
        private int ParentFontSize(ComputedStyle style)
        {
            if (style == null)
            {
                return(12);
            }

            if (style.Text != null)
            {
                return((int)style.Text.FontSize);
            }

            // Note that most of the following is actually the namespace; this is just a single static var.
            Value fontSize = style[Css.Properties.FontSize.GlobalProperty];

            if (fontSize == null)
            {
                return(12);
            }
            else
            {
                return(fontSize.PX);
            }
        }
//--------------------------------------
		/// <summary>Horizontally aligns a line based on alignment settings in the given computed style.</summary>
		/// <param name="first">The style of the first element on the line.</param>
		/// <param name="last">The style of the last element on the line.</param>
		/// <param name="lineSpace">The amount of space available to the line.</param>
		/// <param name="elementCount">The number of elements on this line.</param>
		/// <param name="lineLength">The width of the line in pixels.</param>
		/// <param name="parent">The style which defines the alignment.</param>
		private void AlignLine(ComputedStyle first,ComputedStyle last,int lineSpace,int elementCount,int lineLength,ComputedStyle parent){
			if(elementCount==0){
				return;
			}
			
			// Is this the last line?
			bool lastLine=(last.NextPacked==null || last.NextPacked.Display==DisplayType.Block);
			
			HorizontalAlignType align=parent.HorizontalAlign;
			
			if(lastLine){
				align=parent.HorizontalAlignLast;
				
				if(align==HorizontalAlignType.Auto){
					// Pick an alignment based on parent's HorizontalAlign and GoingLeft.
					align=parent.HorizontalAlign;
					
					if(align==HorizontalAlignType.Justify){
						// Left or right:
						align=HorizontalAlignType.Auto;
					}
				}
				
			}
			
			if(align==HorizontalAlignType.Auto){
				if(GoingLeftwards){
					align=HorizontalAlignType.Right;
				}else{
					align=HorizontalAlignType.Left;
				}
			}
			
			if(align!=HorizontalAlignType.Left){
				// Does the last element on the line end with a space? If so, act like the space isn't there by reducing line length by it.
				
				lineLength-=last.EndSpaceSize;
				
			}
			
			// How many pixels each element will be moved over:
			float offsetBy=0f;
			// True if the text is going to be justified.
			bool justify=false;
			// How many pixels we add to offsetBy each time we shift an element over:
			float justifyDelta=0f;
			
			if(align==HorizontalAlignType.Center){
				// We're centering - shift by half the 'spare' pixels on this row.
				
				// How many pixels of space this line has left / 2:
				offsetBy=(float)(lineSpace-lineLength)/2f;
				
			}else if(align==HorizontalAlignType.Right){
				// How many pixels of space this line has left:
				offsetBy=(float)(lineSpace-lineLength);
				
			}else if(align==HorizontalAlignType.Justify){
				
				// Justify. This is where the total spare space on the line gets shared out evenly
				// between the elements on this line.
				// So, we take the spare space and divide it up by the elements on this line:
				justifyDelta=(float)(lineSpace-lineLength)/(float)elementCount;
				
				if(GoingLeftwards){
					// Make sure the first word starts in the correct spot if we're going leftwards:
					lineLength=lineSpace;
					
					// And also we actually want to be taking a little less each time, so invert justifyDelta:
					justifyDelta=-justifyDelta;
				}
				
				justify=true;
			}
			
			if(GoingLeftwards){
				// Everything is locally positioned off to the left.
				// Because of this, we need to shift them over the entire size of the row:
				offsetBy+=lineLength;
				// In this case it can also be left aligned.
			}
			
			ComputedStyle current=first;
			int counter=0;
			
			while(current!=null&&counter<elementCount){
				
				if(current.Float==FloatType.None){
					
					// Shift the element over by the offset.
					current.ParentOffsetLeft+=(int)offsetBy;
					
					if(justify){
						offsetBy+=justifyDelta;
					}
					
				}
				
				counter++;
				current=current.NextPacked;
			}
			
		}
Exemple #14
0
        /// <summary>Recomputes the space size and inner height of the parent element.</summary>
        public void SetDimensions()
        {
            if (FontToDraw == null || Characters == null)
            {
                return;
            }

            float width        = 0f;
            float size         = FontSize;
            float screenHeight = FontToDraw.GetHeight(size) * (1f + LineGap);

            Ascender = FontToDraw.GetAscend(size) + (LineGap * size / 2f);

            ScaleFactor = size / Fonts.Rasteriser.ScalarX;

            ComputedStyle computed = Element.Style.Computed;

            computed.FixedHeight = true;

            if (SpaceSize == 0f)
            {
                SpaceSize = StandardSpaceSize();
            }

            for (int i = 0; i < Characters.Length; i++)
            {
                Glyph dChar = Characters[i];

                if (dChar == null)
                {
                    continue;
                }

                if (dChar.Image != null)
                {
                    if (CharacterProviders.FixHeight)
                    {
                        if (dChar.Height > screenHeight)
                        {
                            screenHeight = dChar.Height;
                        }

                        width += dChar.Width;
                    }
                    else
                    {
                        width += FontSize;
                    }
                }
                else if (dChar.Space)
                {
                    width += SpaceSize;
                }
                else
                {
                    width += dChar.AdvanceWidth * size;
                }

                width += LetterSpacing;
            }

            computed.InnerHeight = (int)screenHeight;
            computed.InnerWidth  = (int)width;
            computed.FixedWidth  = true;
            computed.SetSize();
        }
Exemple #15
0
        protected override void Layout()
        {
            if (Characters == null || FontToDraw == null || Characters.Length == 0)
            {
                return;
            }

            // The blocks we allocate here come from FontToDraw.
            // They use the same renderer and same layout service, but just a different mesh.
            // This is to enable potentially very large font atlases with multiple fonts.
            ComputedStyle computed = Element.Style.Computed;
            Renderman     renderer = Element.Document.Renderer;

            float top  = computed.OffsetTop + computed.StyleOffsetTop;
            float left = computed.OffsetLeft + computed.StyleOffsetLeft;

            // Should we auto-alias the text?

            // Note that this property "drags" to following elements which is correct.
            // We don't really want to break batching chains for aliasing.

            if (Alias == float.MaxValue)
            {
                // Yep! Note all values here are const.
                float aliasing = Fonts.AutoAliasOffset - ((FontSize - Fonts.AutoAliasRelative) * Fonts.AutoAliasRamp);

                if (aliasing > 0.1f)
                {
                    renderer.FontAliasing = aliasing;
                }
            }
            else
            {
                // Write aliasing:
                renderer.FontAliasing = Alias;
            }

            if (Extrude != 0f)
            {
                // Compute the extrude now:
                if (Text3D == null)
                {
                    Text3D = Get3D(FontSize, FontColour, ref left, ref top);
                }
                else
                {
                    // Update it.
                }

                return;
            }
            else
            {
                Text3D = null;
            }


            if (!AllWhitespace)
            {
                // Firstly, make sure the batch is using the right font texture.
                // This may generate a new batch if the font doesn't match the previous or existing font.

                // Get the full shape of the element:
                int width  = computed.PaddedWidth;
                int height = computed.PaddedHeight;
                int minY   = computed.OffsetTop + computed.BorderTop;
                int minX   = computed.OffsetLeft + computed.BorderLeft;

                BoxRegion boundary = new BoxRegion(minX, minY, width, height);

                if (!boundary.Overlaps(renderer.ClippingBoundary))
                {
                    if (Visible)
                    {
                        SetVisibility(false);
                    }

                    return;
                }
                else if (!Visible)
                {
                    // ImageLocation will allocate here if it's needed.
                    SetVisibility(true);
                }
            }

            float     zIndex       = computed.ZIndex;
            BoxRegion screenRegion = new BoxRegion();

            // First up, underline.
            if (TextLine != null)
            {
                // We have one. Locate it next.
                float lineWeight = (FontToDraw.StrikeSize * FontSize);
                float yOffset    = 0f;

                switch (TextLine.Type)
                {
                case TextLineType.Underline:
                    yOffset = Ascender + lineWeight;
                    break;

                case TextLineType.StrikeThrough:
                    yOffset = (FontToDraw.StrikeOffset * FontSize);
                    yOffset = Ascender - yOffset;
                    break;

                case TextLineType.Overline:
                    yOffset = (lineWeight * 2f);
                    break;
                }

                Color lineColour = FontColour;

                if (TextLine.ColourOverride)
                {
                    lineColour = TextLine.Colour;
                }

                screenRegion.Set(left, top + yOffset, computed.PixelWidth, lineWeight);

                if (screenRegion.Overlaps(renderer.ClippingBoundary))
                {
                    // Ensure we have a batch:
                    SetupBatch(null, null);

                    // This region is visible. Clip it:
                    screenRegion.ClipBy(renderer.ClippingBoundary);
                    // And get our block ready:
                    MeshBlock block = Add();
                    // Set the UV to that of the solid block colour pixel:
                    block.SetSolidColourUV();
                    // Set the colour:
                    block.SetColour(lineColour);

                    block.SetClipped(renderer.ClippingBoundary, screenRegion, renderer, zIndex);
                }
            }


            // Next, render the characters.
            // If we're rendering from right to left, flip the punctuation over.

            // Is the word itself rightwards?
            bool rightwardWord = false;

            if (StartPunctuationCount < Characters.Length)
            {
                // Is the first actual character a rightwards one?
                Glyph firstChar = Characters[StartPunctuationCount];

                if (firstChar != null)
                {
                    rightwardWord = firstChar.Rightwards;
                }
            }

            // Right to left (e.g. arabic):
            if (computed.DrawDirection == DirectionType.RTL)
            {
                int end = Characters.Length - EndPunctuationCount;

                // Draw the punctuation from the end of the string first, backwards:
                if (EndPunctuationCount > 0)
                {
                    for (int i = Characters.Length - 1; i >= end; i--)
                    {
                        DrawInvertCharacter(i, ref left, top, renderer, zIndex, screenRegion);
                    }
                }

                if (rightwardWord)
                {
                    // Render the word itself backwards.

                    for (int i = end - 1; i >= StartPunctuationCount; i--)
                    {
                        DrawCharacter(i, ref left, top, renderer, zIndex, screenRegion);
                    }
                }
                else
                {
                    // Draw the middle characters:
                    for (int i = StartPunctuationCount; i < end; i++)
                    {
                        DrawCharacter(i, ref left, top, renderer, zIndex, screenRegion);
                    }
                }

                // Draw the punctuation from the start of the string last, backwards:

                if (StartPunctuationCount > 0)
                {
                    for (int i = StartPunctuationCount - 1; i >= 0; i--)
                    {
                        DrawInvertCharacter(i, ref left, top, renderer, zIndex, screenRegion);
                    }
                }
            }
            else if (rightwardWord)
            {
                // Render the word itself backwards.

                for (int i = Characters.Length - 1; i >= 0; i--)
                {
                    DrawCharacter(i, ref left, top, renderer, zIndex, screenRegion);
                }
            }
            else
            {
                // Draw it as is.

                for (int i = 0; i < Characters.Length; i++)
                {
                    DrawCharacter(i, ref left, top, renderer, zIndex, screenRegion);
                }
            }
        }
Exemple #16
0
        protected override void Layout()
        {
            if (Image == null || !Image.Loaded())
            {
                return;
            }

            if (Clipping == BackgroundClipping.Text)
            {
                return;
            }

            Renderman renderer = Element.Document.Renderer;

            if (Image.Animated || Image.IsDynamic || renderer.RenderMode == RenderMode.NoAtlas || Filtering != FilterMode.Point || ForcedIsolate)
            {
                // SPA is an animation format, so we need a custom texture atlas to deal with it.
                // This is because the frames of any animation would quickly exhaust our global texture atlas.
                // So to get a custom atlas, we must isolate this property.
                Isolate();
            }
            else if (Image.IsVideo)
            {
                // Similarly with a video, we need to isolate it aswell.
                Isolate();

                                #if !MOBILE
                if (!Image.Video.isPlaying && Element["autoplay"] != null)
                {
                    // Play now:
                    Image.Video.Play();

                    // Fire an onplay event:
                    Element.Run("onplay");

                    // Clear:
                    Element["autoplay"] = null;
                }
                                #endif
            }
            else
            {
                // Reverse isolation, if we are isolated already:
                Include();
            }

            ComputedStyle computed = Element.Style.Computed;

            // Get the full shape of the element:
            int width  = computed.PaddedWidth;
            int height = computed.PaddedHeight;
            int minY   = computed.OffsetTop + computed.BorderTop;
            int minX   = computed.OffsetLeft + computed.BorderLeft;

            if (width == 0 || height == 0)
            {
                if (Visible)
                {
                    SetVisibility(false);
                }
                return;
            }

            BoxRegion boundary = new BoxRegion(minX, minY, width, height);

            if (!boundary.Overlaps(renderer.ClippingBoundary))
            {
                if (Visible)
                {
                    SetVisibility(false);
                }

                return;
            }
            else if (!Visible)
            {
                // ImageLocation will allocate here if it's needed.
                SetVisibility(true);
            }

            boundary.ClipBy(renderer.ClippingBoundary);

            // Texture time - get it's location on that atlas:
            AtlasLocation locatedAt = ImageLocation;

            if (locatedAt == null)
            {
                // We're not using the atlas here.

                if (!Isolated)
                {
                    Isolate();
                }

                int imgWidth  = Image.Width();
                int imgHeight = Image.Height();
                locatedAt = new AtlasLocation(0, 0, imgWidth, imgHeight, imgWidth, imgHeight);
            }

            // Isolation is all done - safe to setup the batch now:
            SetupBatch(locatedAt.Atlas, null);

            // Great - Use locatedAt.Width/locatedAt.Height - this removes any risk of overflowing into some other image.

            int  imageCountX     = 1;
            int  imageCountY     = 1;
            int  trueImageWidth  = locatedAt.Width;
            int  trueImageHeight = locatedAt.Height;
            int  imageWidth      = trueImageWidth;
            int  imageHeight     = trueImageHeight;
            bool autoX           = false;
            bool autoY           = false;

            if (Image.PixelPerfect)
            {
                imageWidth  = (int)(imageWidth * ScreenInfo.ResolutionScale);
                imageHeight = (int)(imageWidth * ScreenInfo.ResolutionScale);
            }

            if (SizeX != null)
            {
                if (SizeX.Single != 0f)
                {
                    imageWidth = (int)(width * SizeX.Single);
                }
                else if (SizeX.PX != 0)
                {
                    imageWidth = SizeX.PX;
                }
                else if (SizeX.IsAuto())
                {
                    autoX = true;
                }
            }

            if (SizeY != null)
            {
                if (SizeY.Single != 0f)
                {
                    imageHeight = (int)(height * SizeY.Single);
                }
                else if (SizeY.PX != 0)
                {
                    imageHeight = SizeY.PX;
                }
                else if (SizeY.IsAuto())
                {
                    autoY = true;
                }
            }

            if (autoX)
            {
                imageWidth = imageHeight * trueImageWidth / trueImageHeight;
            }
            else if (autoY)
            {
                imageHeight = imageWidth * trueImageHeight / trueImageWidth;
            }

            // offsetX and offsetY are the images position offset from where it should be (e.g. x of -200 means it's 200px left)

            // Resolve the true offset values:
            int offsetX = 0;
            int offsetY = 0;

            if (OffsetX != null)
            {
                // Resolve a potential mixed % and px:
                offsetX = OffsetX.GetMixed(width - imageWidth);
            }

            if (OffsetY != null)
            {
                // Resolve a potential mixed % and px:
                offsetY = OffsetY.GetMixed(height - imageHeight);
            }

            if (RepeatX)
            {
                // Get the rounded up number of images:
                imageCountX = (width - 1) / imageWidth + 1;

                if (offsetX != 0)
                {
                    // If we have an offset, another image is introduced.
                    imageCountX++;
                }
            }

            if (RepeatY)
            {
                // Get the rounded up number of images:
                imageCountY = (height - 1) / imageHeight + 1;
                if (offsetY != 0)
                {
                    // If we have an offset, another image is introduced.
                    imageCountY++;
                }
            }

            int blockX = minX + offsetX;
            int blockY = minY + offsetY;

            if (RepeatX && offsetX > 0)
            {
                // We're repeating and the image is offset by a +ve number.
                // This means a small gap, OffsetX px wide, is open on this left side.
                // So to fill it, we need to offset this first image by a much bigger number - the value imageWidth-OffsetX.
                blockX -= (imageWidth - offsetX);
                // This results in the first image having OffsetX pixels exposed in the box - this is what we want.
            }

            if (RepeatY && offsetY > 0)
            {
                // Similar thing to above:
                blockY -= (imageHeight - offsetY);
            }

            BoxRegion screenRegion = new BoxRegion();

            bool  first  = true;
            int   startX = blockX;
            Color colour = computed.ColorOverlay;
            float zIndex = (computed.ZIndex - 0.003f);

            for (int y = 0; y < imageCountY; y++)
            {
                for (int x = 0; x < imageCountX; x++)
                {
                    // Draw at blockX/blockY.
                    screenRegion.Set(blockX, blockY, imageWidth, imageHeight);

                    if (screenRegion.Overlaps(boundary))
                    {
                        // If the two overlap, this means it's actually visible.
                        MeshBlock block = Add();

                        if (Image.Animated && first)
                        {
                            first = false;
                            // Make sure we have an instance:
                            Image.GoingOnDisplay();
                            block.ParentMesh.SetMaterial(Image.Animation.AnimatedMaterial);
                        }
                        else if (Image.IsVideo && first)
                        {
                            first = false;
                            block.ParentMesh.SetMaterial(Image.VideoMaterial);
                        }
                        else if (Isolated && first)
                        {
                            first = false;
                            block.ParentMesh.SetMaterial(Image.ImageMaterial);
                        }

                        // Set it's colour:
                        block.SetColour(colour);

                        // And clip our meshblock to fit within boundary:
                        block.TextUV  = null;
                        block.ImageUV = block.SetClipped(boundary, screenRegion, renderer, zIndex, locatedAt, block.ImageUV);
                    }

                    blockX += imageWidth;
                }
                blockX  = startX;
                blockY += imageHeight;
            }
        }
		/// <summary>Gets the fontsize for the given computed style.</summary>
		/// <param name="parentStyle">The style to get the fontsize from. Used for em calculations.</param>
		private int ParentFontSize(ComputedStyle style){
			if(style==null){
				return 12;
			}
			
			if(style.Text!=null){
				return (int)style.Text.FontSize;
			}
			
			// Note that most of the following is actually the namespace; this is just a single static var.
			Value fontSize=style[Css.Properties.FontSize.GlobalProperty];
			
			if(fontSize==null){
				return 12;
			}else{
				return fontSize.PX;
			}
		}
//--------------------------------------
 /// <summary>Apply this CSS style to the given computed style.
 /// Note that you can grab the element from the computed style if you need that.</summary>
 /// <param name="style">The computed style to apply the property to.</param>
 /// <param name="value">The new value being applied.</param>
 public virtual void Apply(ComputedStyle style, Value value)
 {
 }
		/// <summary>Lets the renderer know the current line doesn't fit anymore elements
		/// and has been finished.</summary>
		/// <param name="parentStyle">The computed style of the element holding this line.</param>
		public void CompleteLine(ComputedStyle parentStyle){
		
			if(PenX>LargestLineWidth){
				LargestLineWidth=PenX;
			}
			
			// Compute some alignment next.
			// Firstly, place the Pen on the line:
			PenY+=LineHeight;
			
			// Next, align the elements and apply their top offset.
			ComputedStyle current=FirstOnLine;
			
			while(current!=null){
				// Calculate the offset to where the top left corner is:
				
				if(current.Float==FloatType.None){
					
					current.ParentOffsetTop=PenY+Baseline-current.PixelHeight;
					
				}else{
					
					current.ParentOffsetTop=PenY+Baseline-LineHeight;
					
				}
				
				current=current.NextOnLine;
			}
			
			if(ActiveFloats!=null){
				
				// Are any now going to be "deactivated"?
				
				for(int i=ActiveFloats.Count-1;i>=0;i--){
					
					// Grab the style:
					ComputedStyle activeFloat=ActiveFloats[i];
					
					// Is the current render point now higher than this floating object?
					// If so, we must reduce LineStart/ increase MaxX depending on which type of float it is.
					
					if(PenY>=(activeFloat.ParentOffsetTop + activeFloat.PixelHeight)){
						
						// Yep! Deactivate and reduce our size:
						if(activeFloat.Float==FloatType.Right){
							
							if(GoingLeftwards){
								
								// Decrease LineStart:
								LineStart-=activeFloat.PixelWidth;
								
							}else{
								
								// Increase max x:
								MaxX+=activeFloat.PixelWidth;
								
							}
							
						}else{
							
							if(GoingLeftwards){
								
								// Increase max x:
								MaxX+=activeFloat.PixelWidth;
								
							}else{
								
								// Decrease LineStart:
								LineStart-=activeFloat.PixelWidth;
								
							}
							
						}
						
						// Remove it as an active float:
						ActiveFloats.RemoveAt(i);
						
					}
					
				}
				
			}
			
			FirstOnLine=null;
			LastOnLine=null;
			LineHeight=0;
			Baseline=0;
			PenX=LineStart;
			
		}
Exemple #21
0
 /// <summary>Creates a new element style for the given element.</summary>
 /// <param name="element">The element that this will be the style for.</param>
 public ElementStyle(Element element) : base(element)
 {
     Computed = new ComputedStyle(element);
 }
		/// <summary>Creates a new element style for the given element.</summary>
		/// <param name="element">The element that this will be the style for.</param>
		public ElementStyle(Element element):base(element){
			Computed=new ComputedStyle(element);
		}
Exemple #23
0
        protected override void Layout()
        {
            if (Corners != null)
            {
                Corners.PreLayout();
            }

            ComputedStyle computed = Element.Style.Computed;

            // Find the zIndex:
            // NB: At same depth as BGColour - right at the back.
            float zIndex = (computed.ZIndex - 0.006f);

            // Get the co-ord of the top edge:
            int top  = computed.OffsetTop;
            int left = computed.OffsetLeft;

            // And the dimensions of the lines:
            // Note: boxwidth doesn't include the left/right widths to prevent overlapping.
            int boxWidth  = computed.PaddedWidth;
            int boxHeight = computed.PaddedHeight + WidthTop + WidthBottom;

            BoxRegion screenRegion = new BoxRegion();
            Renderman renderer     = Element.Document.Renderer;

            // Get the default colour - that's the same as the text colour:
            Color colour = Color.black;

            // Is the border multicoloured?
            bool multiColour = false;

            // Does this border have a colour?
            if (Colour == null)
            {
                // Grab the text colour if there is one:
                if (computed.Text != null)
                {
                    // It's the same as the font colour:
                    colour = computed.Text.FontColour;
                }
                else
                {
                    // Nope - We need to set alpha:
                    colour.a = computed.ColorOverlay.a;
                }
            }
            else if (Colour.Length == 1)
            {
                colour = Colour[0];
            }
            else
            {
                multiColour = true;
            }

            for (int i = 0; i < 4; i++)
            {
                int lineHeight = 0;
                int lineWidth  = 0;

                // Co-ords of the top-left corner for our box:
                int cornerY = top;
                int cornerX = left;

                if (i == 0 || i == 2)
                {
                    // Top or bottom:
                    lineWidth  = boxWidth;
                    lineHeight = BorderWidth(i);
                }
                else
                {
                    lineWidth  = BorderWidth(i);
                    lineHeight = boxHeight;
                }

                // Does this border have multiple colours?
                if (multiColour)
                {
                    colour = Colour[i];
                }

                if (Corners != null)
                {
                    Corners.Layout(i, ref cornerX, ref cornerY, ref lineWidth, ref lineHeight);
                }
                else
                {
                    switch (i)
                    {
                    case 0:
                        // Top:
                        cornerX += WidthLeft;

                        break;

                    case 1:
                        // Right:
                        cornerX += boxWidth + WidthLeft;

                        break;

                    case 2:
                        // Bottom:
                        cornerY += boxHeight - WidthBottom;
                        cornerX += WidthLeft;

                        break;
                    }
                }

                screenRegion.Set(cornerX, cornerY, lineWidth, lineHeight);

                if (screenRegion.Overlaps(renderer.ClippingBoundary))
                {
                    // This region is visible. Clip it:
                    screenRegion.ClipBy(renderer.ClippingBoundary);

                    // Ensure we have a batch (doesn't change graphics or font textures, thus both null):
                    SetupBatch(null, null);

                    // And get our block ready:
                    MeshBlock block = Add();

                    // Set the UV to that of the solid block colour pixel:
                    block.SetSolidColourUV();

                    // Set the border colour:
                    block.SetColour(colour);

                    block.SetClipped(renderer.ClippingBoundary, screenRegion, renderer, zIndex);
                }
            }
        }
		/// <summary>Adds the given style to the current line.</summary>
		/// <param name="style">The style to add.</param>
		private void AddToLine(ComputedStyle style,Element parentNode){
			// Don't call with inline elements - block or inline-block only.
			ComputedStyle parentStyle=(parentNode==null)?null:parentNode.Style.Computed;
			
			
			if( (style.Display==DisplayType.Block && style.Float==FloatType.None) ){
				
				// Doesn't fit here.
				CompleteLine(parentStyle);
				
			}else if((parentStyle==null || parentStyle.WhiteSpace==WhiteSpaceType.Normal) && ((PenX+style.PixelWidth)>MaxX) ){
				
				// Does the last element on the line end with a space?
				if(LastOnLine!=null && style.Text!=null){
					
					TextRenderingProperty text=LastOnLine.Text;
					
					if(text!=null){
						
						// It's a word - does it end with a space?
						
						if(text.NoEndingSpace){
							// It's a word which does not end with a space.
							// These two inline words are actually "touching".
							// So, first pull that last word from the line it's on:
							
							if(FirstOnLine!=LastOnLine){
								
								ComputedStyle toMove=LastOnLine;
								
								// Must update NextOnLine of the previous element.
								ComputedStyle beforePrevious=FirstOnLine;
								toMove=FirstOnLine;
								
								// Note that there may actually be more than 2 in a row.
								// So, we'll go hunting for the first element that is before a sequence of
								// inline elements with text that does not end with a space.
								
								while(true){
									
									ComputedStyle next=toMove.NextOnLine;
									
									if(next==LastOnLine){
										break;
									}
									
									if(next.Text!=null && next.Text.NoEndingSpace){
										
										// Just advance toMove:
										toMove=next;
										continue;
										
									}
									
									// Advance both:
									beforePrevious=next;
									toMove=next;
									
								}
								
								// Update toMove:
								toMove=beforePrevious.NextOnLine;
								
								// Update before previous:
								beforePrevious.NextOnLine=null;
								LastOnLine=beforePrevious;
								
								// Complete the line:
								CompleteLine(parentStyle);
								
								// Re-add each one:
								while(toMove!=null){
									
									ComputedStyle next=toMove.NextOnLine;
									AddToLine(toMove,parentNode);
									toMove=next;
									
								}
							}
							
							// Add this word to the current line:
							goto AddNow;
							
						}
						
					}
					
				}
				
				// Doesn't fit here.
				CompleteLine(parentStyle);
				
			}
			
			AddNow:
			
			style.NextPacked=null;
			style.NextOnLine=null;
			
			if(style.Float==FloatType.Right){
				
				if(GoingLeftwards){
					style.ParentOffsetLeft=LineStart;
					PenX+=style.PixelWidth;
				}else{
					style.ParentOffsetLeft=MaxX-style.PixelWidth;
				}
				
				if(ActiveFloats==null){
					ActiveFloats=new List<ComputedStyle>(1);
				}
				
				ActiveFloats.Add(style);
				
			}else if(style.Float==FloatType.Left){
				
				if(GoingLeftwards){
					style.ParentOffsetLeft=MaxX-style.PixelWidth;
				}else{
					style.ParentOffsetLeft=LineStart;
					PenX+=style.PixelWidth;
				}
				
				if(ActiveFloats==null){
					ActiveFloats=new List<ComputedStyle>(1);
				}
				
				ActiveFloats.Add(style);
				
			}else if(GoingLeftwards){
				PenX+=style.PixelWidth;
				style.ParentOffsetLeft=LineStart*2-PenX;
			}else{
				style.ParentOffsetLeft=PenX;
				PenX+=style.PixelWidth;
			}
			
			if(style.Float==FloatType.None && ActiveFloats!=null){
				
				if(style.Display==DisplayType.Block || style.FixedWidth){
					
					// Get this elements width value:
					Css.Value widthValue=style[Css.Properties.Width.GlobalProperty];
					
					// Is it a percentage or not fixed width and block?
					if(widthValue==null){
						
						if(style.Display==DisplayType.Block){
							
							// Grab it:
							int parentWidth=parentStyle.InnerWidth;
							
							// Overwrite it:
							parentStyle.InnerWidth=MaxX-LineStart;
							
							// Update the size:
							style.SetSize();
							
							// And bubble upwards:
							style.Element.SetWidthForKids(style);
							
							// Write back:
							parentStyle.InnerWidth=parentWidth;
							
						}
						
					}else{
						if(widthValue.Type==Css.ValueType.Percentage){
							// Yep! We need to update it.
							
							// Grab it:
							int parentWidth=parentStyle.InnerWidth;
							
							// Overwrite it:
							parentStyle.InnerWidth=MaxX-LineStart;
							
							// Resolve it again:
							widthValue.MakeAbsolute(Css.Properties.Width.GlobalProperty,style.Element);
							
							// Apply it:
							style.InnerWidth=widthValue.PX;
							
							style.SetSize();
							
							// Update width:
							style.Element.SetWidthForKids(style);
							
							// Write back:
							parentStyle.InnerWidth=parentWidth;
							
						}
						
					}
					
				}
				
			}
			
			if(style.Float==FloatType.Left){
				
				if(GoingLeftwards){
				
					// Reduce max:
					MaxX-=style.PixelWidth;
					
				}else{
					
					// Push over where lines start at:
					LineStart+=style.PixelWidth;
					
				}
				
			}else if(style.Float==FloatType.Right){
				
				if(GoingLeftwards){
					
					// Push over where lines start at:
					LineStart+=style.PixelWidth;
					
				}else{
					
					// Reduce max:
					MaxX-=style.PixelWidth;
					
				}
				
			}else if(style.PixelHeight>LineHeight){
				LineHeight=style.PixelHeight;
			}
			
			if(style.Baseline>Baseline){
				Baseline=style.Baseline;
			}
			
			if(FirstPacked==null){
				FirstPacked=LastPacked=style;
			}else{
				LastPacked=LastPacked.NextPacked=style;
			}
			
			if(FirstOnLine==null){
				FirstOnLine=LastOnLine=style;
			}else{
				
				if(style.Float==FloatType.Left){
					
					// Push over all the elements before this on the line.
					ComputedStyle currentLine=FirstOnLine;
					
					while(currentLine!=null){
						
						if(currentLine.Float==FloatType.None){
							// Move it:
							currentLine.ParentOffsetLeft+=style.PixelWidth;
						}
						
						// Next one:
						currentLine=currentLine.NextOnLine;
						
					}
					
				}
				
				LastOnLine=LastOnLine.NextOnLine=style;
			}
			
			if(style.Display==DisplayType.Block && style.Float==FloatType.None){
				// A second newline after the block too.
				CompleteLine(parentStyle);
			}
		}
        /// <summary>Converts this relative value (such as a percentage or em) into a fixed one.</summary>
        /// <param name="property">The property that this value represents and is being made absolute.</param>
        /// <param name="element">The element holding all the values that represent 100%.</param>
        public void MakeAbsolute(CssProperty property, Element element)
        {
            if (element.ParentNode == null)
            {
                PX = 0;
                return;
            }

            ComputedStyle parentStyle = element.ParentNode.Style.Computed;

            switch (Type)
            {
            case ValueType.Em:
                BakePX(ParentFontSize(parentStyle));
                break;

            case ValueType.Percentage:

                ComputedStyle computed = element.Style.Computed;

                // Is this along x?
                if (property.IsXProperty)
                {
                    // Yep!
                    BakePX(parentStyle.InnerWidth - computed.PaddingLeft - computed.PaddingRight);
                }
                else
                {
                    // Nope!
                    BakePX(parentStyle.InnerHeight - computed.PaddingTop - computed.PaddingBottom);
                }

                break;

            case ValueType.Inherit:

                InheritFrom(parentStyle[property]);

                break;

            case ValueType.Calc:

                computed = element.Style.Computed;


                int size = 0;

                // Is this along x?
                if (property.IsXProperty)
                {
                    // Yep!
                    size = parentStyle.InnerWidth - computed.PaddingLeft - computed.PaddingRight;
                }
                else
                {
                    // Nope!
                    size = parentStyle.InnerHeight - computed.PaddingTop - computed.PaddingBottom;
                }

                PX = Calculation.Run(size);

                break;

            default:

                computed = element.Style.Computed;

                // It's a box or point - compute all values [y,x,y,x]
                // Both will have the first two values:
                bool useWidth = (Type == ValueType.Point);                     //x is first for a point.

                // Don't include padding in the value.
                int paddingWidth  = computed.PaddingLeft + computed.PaddingRight;
                int paddingHeight = computed.PaddingTop + computed.PaddingBottom;

                // The cached fontsize if any of these use EM; Chances are more than one will.
                int parentFontSize = -1;

                for (int i = 0; i < InnerValues.Length; i++)
                {
                    Value innerValue = InnerValues[i];

                    if (innerValue.Type == ValueType.Em)
                    {
                        if (parentFontSize == -1)
                        {
                            parentFontSize = ParentFontSize(parentStyle);
                        }

                        innerValue.BakePX(parentFontSize);
                    }
                    else
                    {
                        // Whats the block size?

                        if (useWidth)
                        {
                            innerValue.BakePX(parentStyle.InnerWidth - paddingWidth);
                        }
                        else
                        {
                            innerValue.BakePX(parentStyle.InnerHeight - paddingHeight);
                        }
                    }

                    // And flip useWidth:
                    useWidth = !useWidth;
                }

                break;
            }
        }
		/// <summary>Resolves any percentage heights for all child elements using the given parent element.</summary>
		/// <param name="parent">The computed style to base percentages on.</param>
		public void SetHeightForKids(ComputedStyle parent){
			SetDimensionForKids(parent,false);
		}
		/// <summary>Apply this CSS style to the given computed style.
		/// Note that you can grab the element from the computed style if you need that.</summary>
		/// <param name="style">The computed style to apply the property to.</param>
		/// <param name="value">The new value being applied.</param>
		public virtual void Apply(ComputedStyle style,Value value){}
//--------------------------------------
        /// <summary>Tells the UI the mouse was clicked or released.</summary>
        /// <param name="x">The x coordinate of the mouse in pixels from the left of the screen.</param>
        /// <param name="y">The y coordinate of the mouse in pixels from the top of the screen.</param>
        /// <param name="mouseDown">True if the button is now down.</param>
        /// <returns>True if the mouse was on the UI.</returns>
        private static bool RunClick(int x, int y, bool mouseDown, out UIEvent uiEvent)
        {
            uiEvent = new UIEvent(x, y, mouseDown);

            // Which button?
            if (Event.current != null)
            {
                uiEvent.keyCode = Event.current.button;
            }

            if (UI.document == null || UI.document.html == null)
            {
                return(false);
            }

            int invertedY = ScreenInfo.ScreenY - 1 - y;

            bool result = false;

            if (Mode == InputMode.Screen)
            {
                result = UI.document.html.RunClickOnKids(uiEvent);
            }
            else if (Mode == InputMode.Physics)
            {
                // Screen physics cast here.

                RaycastHit uiHit;
                if (Physics.Raycast(UI.GUICamera.ScreenPointToRay(new Vector2(x, invertedY)), out uiHit))
                {
                    // Did it hit the main UI?
                    HitResult hit = HandleUIHit(uiHit);
                    result = hit.Success;

                    if (result)
                    {
                        // Yes - As this is the main UI, We must have a HitElement available. All we need to do is ClickOn it!
                        ClickOn(hit.HitElement, uiEvent);
                    }
                }
            }

            if (!result && WorldInputMode != InputMode.None)
            {
                // Didn't hit the main UI - handle clicks on WorldUI's.
                RaycastHit worldUIHit;

                if (CameraFor3DInput == null)
                {
                    CameraFor3DInput = Camera.main;
                }

                bool hitSuccess = false;

                if (OnResolve3D != null)
                {
                    hitSuccess = OnResolve3D(out worldUIHit, uiEvent);
                }
                else
                {
                    hitSuccess = Physics.Raycast(CameraFor3DInput.ScreenPointToRay(new Vector2(x, invertedY)), out worldUIHit);
                }

                if (hitSuccess)
                {
                    // Did it hit a worldUI?
                    HitResult hit = HandleWorldUIHit(worldUIHit);
                    result = hit.Success;

                    if (result)
                    {
                        // Yes it did.
                        result = hit.RunClick(uiEvent);
                    }
                }
            }

            // Clear any LastMouseDown entries:
            if (!mouseDown)
            {
                // Clear their active state:
                for (int i = LastMouseDown.Count - 1; i >= 0; i--)
                {
                    // Get the element:
                    Element element = LastMouseDown[i];

                    // Get computed style:
                    Css.ComputedStyle computed = element.Style.Computed;

                    // Clear active:
                    computed.UnsetModifier("active");

                    // Still got the mouse over it?
                    if (element.MousedOver != MouseOverState.Out)
                    {
                        // Yep! Re-apply hover:
                        computed.Hover();
                    }
                }

                // Clear the set:
                LastMouseDown.Clear();
            }

            return(result);
        }
		/// <summary>Sets up this renderer so that it's ready to start packing child elements of
		/// a given element into lines.</summary>
		/// <param name="element">The parent element whose children will be packed.</param>
		public void BeginLinePack(Element element){
			ComputedStyle computed=element.Style.Computed;
			
			if(computed.Display==DisplayType.Block||computed.FixedWidth){
				// Block elements are 100% wide unless stated otherwise (ie with a fixed width).
				MaxX=computed.InnerWidth;
				
				if(element.VScrollbar){
					MaxX-=14;
					if(MaxX<0){
						MaxX=0;
					}
				}
			}else if(computed.Display==DisplayType.InlineBlock || computed.Display==DisplayType.TableCell){
				// An inline block element uses it's parents size as the maximum.
				// If it exceeds the space left on a line it will jump to the next line anyway.
				if(element.parentNode!=null){
					MaxX=element.parentNode.Style.Computed.InnerWidth;
				}
			
			}
			
			// Kids of elements that don't line pack are packed into the lines of the first parent which does.
			PenX=0;
			PenY=0;
			LineStart=0;
			LargestLineWidth=0;
			computed.ContentWidth=0;
			computed.ContentHeight=0;
			FirstPacked=LastPacked=null;
			GoingLeftwards=(computed.DrawDirection==DirectionType.RTL);
		}
		/// <summary>Resolves any percentage widths for all child elements using the given parent element.</summary>
		/// <param name="parent">The computed style to base percentages on.</param>
		public void SetWidthForKids(ComputedStyle parent){
			SetDimensionForKids(parent,true);
		}
		/// <summary>Call this if the current property requires a text object. NOTE: This one may be null.</summary>
		public TextRenderingProperty GetText(ComputedStyle style){
			
			// Grab it:
			TextRenderingProperty text=style.Text;
			
			if(text!=null){
				// Flag the change:
				text.Changed=true;
			}
			
			return text;
		}
		/// <summary>Resolves any percentages for all child elements using the given parent element.</summary>
		/// <param name="parent">The computed style to base percentages on.</param>
		/// <param name="isWidth">True if we should use the width of the parent; false for height.</param>
		private void SetDimensionForKids(ComputedStyle parent,bool isWidth){
			int dimension=isWidth?parent.InnerWidth:parent.InnerHeight;
			
			if(ChildNodes!=null){
				for(int i=0;i<ChildNodes.Count;i++){
					ChildNodes[i].Style.Computed.SetParentDimension(dimension,isWidth,parent);
				}
			}
			
			if(HScrollbar){
				HorizontalScrollbar.Element.Style.Computed.SetParentDimension(dimension,isWidth,parent);
			}
			
			if(VScrollbar){
				VerticalScrollbar.Element.Style.Computed.SetParentDimension(dimension,isWidth,parent);
			}
		}
		public override void Apply(ComputedStyle style,Value value){
			
			// Got any text at all?:
			if(GetText(style)==null){
				return;
			}
			
			// Apply the property:
			if(value==null || value.Text=="none"){
				
				// Clear the stroke:
				style.TextStroke=null;
				
			}else{
			
				// The stroke properties:
				int blur=0;
				Color colour=Color.black;
				
				int thickness=value[0].PX;
				
				if(thickness==0){
					
					style.TextStroke=null;
					
				}else{
					
					StrokeData data=style.TextStroke;
					
					if(data==null){
						data=new StrokeData();
						style.TextStroke=data;
					}
					
					data.Thickness=thickness;
				
					// Grab the blur:
					Value innerValue=value[1];
					
					if(innerValue.Type==ValueType.Color){
						colour=innerValue.ToColor();
					}else{
						blur=innerValue.PX;
						
						// Grab the colour:
						innerValue=value[2];
						
						if(innerValue.Type==ValueType.Color){
							colour=innerValue.ToColor();
						}
						
					}
					
					data.Colour=colour;
					data.Blur=blur;
				
				}
				
			}
			
			// Apply the changes - doesn't change anything about the actual text, so we just want a layout:
			style.RequestLayout();
		}
		/// <summary>Sets the clipping boundary from the given computed style.</summary>
		/// <param name="style">The computed style to find the clipping boundary from.</param>
		public void SetBoundary(ComputedStyle style){
			bool visibleX=(style.OverflowX==OverflowType.Visible);
			bool visibleY=(style.OverflowY==OverflowType.Visible);
			
			if(visibleX && visibleY){
				return;
			}
			
			BoxRegion newBoundary=null;
			
			if(visibleX){
				newBoundary=new BoxRegion(ClippingBoundary.X,style.OffsetTop+style.StyleOffsetTop+style.ScrollTop,ClippingBoundary.Width,style.InnerHeight);
			}else if(visibleY){
				newBoundary=new BoxRegion(style.OffsetLeft+style.StyleOffsetLeft+style.ScrollLeft,ClippingBoundary.Y,style.InnerWidth,ClippingBoundary.Height);
			}else{
				newBoundary=new BoxRegion(style.OffsetLeft+style.StyleOffsetLeft+style.ScrollLeft,style.OffsetTop+style.StyleOffsetTop+style.ScrollTop,style.InnerWidth,style.InnerHeight);
			}
			
			if(style.Clip){
				newBoundary.ClipBy(ClippingBoundary);
			}
			
			ClippingBoundary=newBoundary;
		}
		/// <summary>Recalculates the matrices if this transformation has changed.</summary>
		public void RecalculateMatrix(ComputedStyle style){
			
			if(Changed){
				CalculateOrigin(style);
				_Changed=false;
				
				_LocalMatrix=Matrix4x4.TRS(_Origin,Quaternion.identity,Vector3.one);
				
				// Skew:
				if(HasSkew){
					_LocalMatrix*=_Skew;
				}
				
				_LocalMatrix*=Matrix4x4.TRS(_Translate,_Rotation,_Scale);
				
				_LocalMatrix*=Matrix4x4.TRS(-_Origin,Quaternion.identity,Vector3.one);
			}
			
			if(Parent!=null){
				_Matrix=Parent.Matrix*_LocalMatrix;
			}else{
				_Matrix=_LocalMatrix;
			}
		}