internal override bool UpdateSubTree() { if (!base.UpdateSubTree()) { return(false); } if (!autoSize.EnsureValid()) { autoSize.Refresh(delegate { Vector2 b = DrawQuadForBounds.BottomRight; InternalSize = new Vector2((RelativeSizeAxes & Axes.X) > 0 ? InternalSize.X : b.X, (RelativeSizeAxes & Axes.Y) > 0 ? InternalSize.Y : b.Y); Invalidate(Invalidation.Position); //note that this is called before autoSize becomes valid. may be something to consider down the line. //might work better to add an OnRefresh event in Cached<> and invoke there. OnAutoSize?.Invoke(); return(b); }); } return(true); }
private void updateAutoSize() { isComputingAutosize = true; try { if (autoSize.EnsureValid()) { return; } autoSize.Refresh(delegate { Vector2 b = computeAutoSize() + Padding.Total; if ((RelativeSizeAxes & Axes.X) == 0) { base.Width = b.X; } if ((RelativeSizeAxes & Axes.Y) == 0) { base.Height = b.Y; } //note that this is called before autoSize becomes valid. may be something to consider down the line. //might work better to add an OnRefresh event in Cached<> and invoke there. OnAutoSize?.Invoke(); }); } finally { isComputingAutosize = false; } }
protected override void UpdateLayout() { base.UpdateLayout(); if (!activeMode.EnsureValid()) { activeMode.Refresh(() => modeButtonLine.MoveToX(activeButton.DrawPosition.X, 200, EasingTypes.OutQuint)); } }
protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); if (!layout.EnsureValid()) { layout.Refresh(delegate { OnLayout?.Invoke(); if (!Children.Any()) { return; } var positions = ComputeLayoutPositions().ToArray(); int i = 0; foreach (var d in FlowingChildren) { if (i > positions.Length) { throw new InvalidOperationException( $"{GetType().FullName}.{nameof(ComputeLayoutPositions)} returned a total of {positions.Length} positions for {i} children. {nameof(ComputeLayoutPositions)} must return 1 position per child."); } if ((d.RelativeSizeAxes & AutoSizeAxes) != 0) { throw new InvalidOperationException( "Drawables inside a flow container may not have a relative size axis that the flow container is auto sizing for." + $"The flow container is set to autosize in {AutoSizeAxes} axes and the child is set to relative size in {d.RelativeSizeAxes} axes."); } if (d.RelativePositionAxes != Axes.None) { throw new InvalidOperationException($"A flow container cannot contain a child with relative positioning (it is {d.RelativePositionAxes})."); } var finalPos = positions[i]; if (d.Position != finalPos) { d.MoveTo(finalPos, LayoutDuration, LayoutEasing); } ++i; } if (i != positions.Length) { throw new InvalidOperationException( $"{GetType().FullName}.{nameof(ComputeLayoutPositions)} returned a total of {positions.Length} positions for {i} children. {nameof(ComputeLayoutPositions)} must return 1 position per child."); } }); } }
/// <summary> /// Apply this drawable to the draw node /// </summary> /// <param name="node">The node to apply properties to</param> protected virtual void ApplyDrawNode(DrawNode3D node) { node.WorldMatrix = WorldMatrix; node.ColourInfo = ColourInfo; if (node.ColourInfo.HasSingleColour) { node.ColourInfo = node.ColourInfo.MultiplyAlpha(Alpha); } node.Blending = blendingInfo.EnsureValid() ? blendingInfo.Value : blendingInfo.Refresh(() => new BlendingInfo(blendingMode)); }
private void refreshLayout() { if (internalSize.EnsureValid()) { return; } internalSize.Refresh(delegate { if (FixedWidth && !constantWidth.HasValue) { constantWidth = CreateCharacterDrawable('D').DrawWidth; } //keep sprites which haven't changed since last layout. List <Drawable> keepDrawables = new List <Drawable>(); bool allowKeepingExistingDrawables = true; //adjust shadow alpha based on highest component intensity to avoid muddy display of darker text. //squared result for quadratic fall-off seems to give the best result. var avgColour = (Color4)DrawInfo.Colour.AverageColour; float shadowAlpha = (float)Math.Pow(Math.Max(Math.Max(avgColour.R, avgColour.G), avgColour.B), 2); //we can't keep existing drawabled if our shadow has changed, as the shadow is applied in the add-loop. //this could potentially be optimised if necessary. allowKeepingExistingDrawables &= shadowAlpha == lastShadowAlpha && font == lastFont; lastShadowAlpha = shadowAlpha; lastFont = font; if (allowKeepingExistingDrawables) { int length = Math.Min(lastText?.Length ?? 0, text.Length); keepDrawables.AddRange(Children.TakeWhile((n, i) => i < length && lastText[i] == text[i])); Remove(keepDrawables); //doesn't dispose } Clear(); if (text.Length == 0) { return(Vector2.Zero); } foreach (var k in keepDrawables) { Add(k); } for (int index = keepDrawables.Count; index < text.Length; index++) { char c = text[index]; bool fixedWidth = FixedWidth && !FixedWidthExceptionCharacters.Contains(c); Drawable d; if (char.IsWhiteSpace(c)) { float width = fixedWidth ? constantWidth.GetValueOrDefault() : spaceWidth; switch ((int)c) { case 0x3000: //double-width space width *= 2; break; } d = new Container { Size = new Vector2(width), Scale = new Vector2(TextSize), Colour = Color4.Transparent, }; } else { d = CreateCharacterDrawable(c); if (fixedWidth) { d.Anchor = Anchor.TopCentre; d.Origin = Anchor.TopCentre; } var ctn = new Container { Size = new Vector2(fixedWidth ? constantWidth.GetValueOrDefault() : d.DrawSize.X, UseFullGlyphHeight ? 1 : d.DrawSize.Y), Scale = new Vector2(TextSize), Children = new[] { d } }; if (shadow && shadowAlpha > 0) { Drawable shadowDrawable = CreateCharacterDrawable(c); shadowDrawable.Position = new Vector2(0, 0.06f); shadowDrawable.Anchor = d.Anchor; shadowDrawable.Origin = d.Origin; shadowDrawable.Alpha = shadowAlpha; shadowDrawable.Colour = shadowColour; shadowDrawable.Depth = float.MaxValue; ctn.Add(shadowDrawable); } d = ctn; } Add(d); } lastText = text; return(Vector2.Zero); }); }
private void refreshLayout() { if (internalSize.EnsureValid()) { return; } internalSize.Refresh(delegate { if (FixedWidth && !constantWidth.HasValue) { constantWidth = CreateCharacterDrawable('D').DrawWidth; } //keep sprites which haven't changed since last layout. List <Drawable> keepDrawables = new List <Drawable>(); int length = Math.Min(lastText?.Length ?? 0, text?.Length ?? 0); keepDrawables.AddRange(Children.TakeWhile((n, i) => i < length && lastText[i] == text[i])); Remove(keepDrawables); Clear(); foreach (var k in keepDrawables) { Add(k); } for (int index = keepDrawables.Count; index < text.Length; index++) { char c = text[index]; Drawable d; if (char.IsWhiteSpace(c)) { float width = FixedWidth ? constantWidth.GetValueOrDefault() : spaceWidth; switch ((int)c) { case 0x3000: //double-width space width *= 2; break; } d = new Container { Size = new Vector2(width), Scale = new Vector2(TextSize), Colour = Color4.Transparent, }; } else { d = CreateCharacterDrawable(c); if (FixedWidth) { d.Anchor = Anchor.TopCentre; d.Origin = Anchor.TopCentre; } var ctn = new Container { Size = new Vector2(FixedWidth ? constantWidth.GetValueOrDefault() : d.DrawSize.X, UseFullGlyphHeight ? 1 : d.DrawSize.Y), Scale = new Vector2(TextSize), Children = new[] { d } }; if (shadow) { Drawable shadowDrawable = CreateCharacterDrawable(c); shadowDrawable.Position = new Vector2(0, 0.06f); shadowDrawable.Colour = shadowColour; shadowDrawable.Depth = float.MaxValue; ctn.Add(shadowDrawable); } d = ctn; } Add(d); } lastText = text; return(Vector2.Zero); }); }
private void refreshLayout() { if (internalSize.EnsureValid()) { return; } internalSize.Refresh(delegate { if (FixedWidth && !constantWidth.HasValue) { constantWidth = getSprite('D').DrawWidth; } //keep sprites which haven't changed since last layout. List <Drawable> keepDrawables = new List <Drawable>(); int length = Math.Min(lastText?.Length ?? 0, text?.Length ?? 0); keepDrawables.AddRange(Children.TakeWhile((n, i) => i < length && lastText[i] == text[i])); Remove(keepDrawables); Clear(); foreach (var k in keepDrawables) { Add(k); } for (int index = keepDrawables.Count; index < text.Length; index++) { char c = text[index]; Drawable s; if (char.IsWhiteSpace(c)) { float width = FixedWidth ? constantWidth.GetValueOrDefault() : spaceWidth; switch ((int)c) { case 0x3000: //double-width space width *= 2; break; } s = new Container { Size = new Vector2(width), Colour = Color4.Transparent, }; } else { s = getSprite(c); if (FixedWidth) { s.Anchor = Anchor.TopCentre; s.Origin = Anchor.TopCentre; } var ctn = new Container { Size = new Vector2(FixedWidth ? constantWidth.GetValueOrDefault() : s.DrawSize.X, 1f), Children = new[] { s } }; s = ctn; } Add(s); } lastText = text; return(Vector2.Zero); }); }
protected override void UpdateLayout() { base.UpdateLayout(); if (!layout.EnsureValid()) { layout.Refresh(delegate { OnLayout?.Invoke(); if (Children.FirstOrDefault() == null) { return(Vector2.Zero); } Vector2 current = new Vector2(Math.Max(0, Padding.X), Math.Max(0, Padding.Y)); Vector2 max = maximumSize; if (direction == FlowDirection.Full && maximumSize == Vector2.Zero) { var s = Size; //If we are autosize and haven't specified a maximum size, we should allow infinite expansion. //If we are inheriting then we need to use the parent size (our ActualSize). max.X = (RelativeSizeAxes & Axes.X) == 0 ? float.MaxValue : s.X; max.Y = (RelativeSizeAxes & Axes.Y) == 0 ? float.MaxValue : s.Y; } float rowMaxHeight = 0; foreach (Drawable d in Children) { if (((int)direction & (int)d.RelativeSizeAxes) > 0) { //if the inheriting mode of the drawable shares the same directional value as our flow direction, we have to ignore it. continue; } Vector2 size = Vector2.Zero; if (d.IsVisible) { size = d.Size * d.Scale * ChildrenScale; if (Direction != FlowDirection.HorizontalOnly && current.X + size.X > max.X) { current.X = Math.Max(0, Padding.X); current.Y += rowMaxHeight; rowMaxHeight = 0; } //todo: check this is correct if (size.X > 0) { size.X = Math.Max(0, size.X + Padding.X); } if (size.Y > 0) { size.Y = Math.Max(0, size.Y + Padding.Y); } if (size.Y > rowMaxHeight) { rowMaxHeight = size.Y; } } if (current != d.Position) { d.MoveTo(current, LayoutDuration, LayoutEasing); } current.X += size.X; } return(current); }); } }