/// <summary> /// Flips through given array of item stacks every second /// </summary> /// <param name="itemstacks"></param> /// <param name="unscaleSize"></param> /// <param name="floatType"></param> public SlideshowItemstackTextComponent(ICoreClientAPI capi, ItemStack[] itemstacks, double unscaleSize, EnumFloat floatType, Action <ItemStack> onStackClicked = null) : base(capi) { initSlot(); this.Itemstacks = itemstacks; this.Float = floatType; this.BoundsPerLine = new LineRectangled[] { new LineRectangled(0, 0, GuiElement.scaled(unscaleSize), GuiElement.scaled(unscaleSize)) }; this.onStackClicked = onStackClicked; }
public ItemstackTextComponent(ICoreClientAPI capi, ItemStack itemstack, double size, double rightSidePadding = 0, EnumFloat floatType = EnumFloat.Left, Action <ItemStack> onStackClicked = null) : base(capi) { size = GuiElement.scaled(size); slot = new DummySlot(itemstack); this.onStackClicked = onStackClicked; this.Float = floatType; this.size = size; this.BoundsPerLine = new LineRectangled[] { new LineRectangled(0, 0, size, size) }; PaddingRight = GuiElement.scaled(rightSidePadding); }
/// <summary> /// Looks at the collectibles handbook groupBy attribute and makes a list of itemstacks from that /// </summary> /// <param name="itemstackgroup"></param> /// <param name="unscaleSize"></param> /// <param name="floatType"></param> public SlideshowItemstackTextComponent(ICoreClientAPI capi, ItemStack itemstackgroup, List <ItemStack> allstacks, double unscaleSize, EnumFloat floatType, Common.Action <ItemStack> onStackClicked = null) : base(capi) { initSlot(); this.onStackClicked = onStackClicked; string[] groups = itemstackgroup.Collectible.Attributes?["handbook"]?["groupBy"]?.AsArray <string>(null); List <ItemStack> nowGroupedStacks = new List <ItemStack>(); List <ItemStack> stacks = new List <ItemStack>(); nowGroupedStacks.Add(itemstackgroup); stacks.Add(itemstackgroup); if (groups != null) { AssetLocation[] groupWildCards = new AssetLocation[groups.Length]; for (int i = 0; i < groups.Length; i++) { if (!groups[i].Contains(":")) { groupWildCards[i] = new AssetLocation(itemstackgroup.Collectible.Code.Domain, groups[i]); } else { groupWildCards[i] = new AssetLocation(groups[i]); } } foreach (var val in allstacks) { for (int i = 0; i < groupWildCards.Length; i++) { if (val.Collectible.WildCardMatch(groupWildCards[i])) { stacks.Add(val); nowGroupedStacks.Add(val); break; } } } } foreach (var val in nowGroupedStacks) { allstacks.Remove(val); } this.Itemstacks = stacks.ToArray(); this.Float = floatType; this.BoundsPerLine = new LineRectangled[] { new LineRectangled(0, 0, GuiElement.scaled(unscaleSize), GuiElement.scaled(unscaleSize)) }; //PaddingRight = 0; }
public override void ComposeElements(Context ctx, ImageSurface surface) { ctx.SetSourceRGBA(1, 1, 1, 0.2); for (int x = 0; x < 3; x++) { for (int y = 0; y < 3; y++) { ctx.Rectangle(BoundsPerLine[0].X + x * (size + GuiElement.scaled(3)), BoundsPerLine[0].Y + y * (size + GuiElement.scaled(3)), size, size); ctx.Fill(); } } }
/// <summary> /// Takes a texture and applies some text to it. /// </summary> /// <param name="text">The text to texture.</param> /// <param name="font">The font of the text.</param> /// <param name="width">The width of the text.</param> /// <param name="height">The height of the text.</param> /// <param name="loadedTexture">The texture to be loaded on to.</param> /// <param name="background">The background of the text. (default: none/null)</param> /// <param name="orientation">The orientation of the text. (default: left)</param> public void GenOrUpdateTextTexture(string text, CairoFont font, int width, int height, ref LoadedTexture loadedTexture, TextBackground background = null, EnumTextOrientation orientation = EnumTextOrientation.Left, bool demulAlpha = false) { if (background == null) { background = defaultBackground; } ElementBounds bounds = new ElementBounds().WithFixedSize(width, height); ImageSurface surface = new ImageSurface(Format.Argb32, width, height); Context ctx = new Context(surface); GuiElementTextBase elTeBa = new GuiElementTextBase(capi, text, font, bounds); ctx.SetSourceRGBA(background.FillColor); GuiElement.RoundRectangle(ctx, 0, 0, width, height, background.Radius); if (background.BorderWidth > 0) { ctx.FillPreserve(); ctx.Operator = Operator.Atop; ctx.LineWidth = background.BorderWidth; ctx.SetSourceRGBA(background.BorderColor); ctx.Stroke(); ctx.Operator = Operator.Over; } else { ctx.Fill(); } //ctx.Antialias = Antialias.Subpixel; elTeBa.textUtil.AutobreakAndDrawMultilineTextAt(ctx, font, text, background.Padding, background.Padding, width, orientation); //int textureId = capi.Gui.LoadCairoTexture(surface, true); - WTF! What was this for?! if (demulAlpha) { surface.DemulAlpha(); } capi.Gui.LoadOrUpdateCairoTexture(surface, true, ref loadedTexture); //surface.WriteToPng("test.png"); surface.Dispose(); ctx.Dispose(); }
/// <summary> /// Unfocuses all elements except one specific element. /// </summary> /// <param name="elem">The element to remain in focus.</param> public void UnfocusOwnElementsExcept(GuiElement elem) { foreach (GuiElement element in interactiveElements.Values) { if (element == elem) { continue; } if (element.Focusable && element.HasFocus) { element.OnFocusLost(); OnFocusChanged?.Invoke(false); } } }
/// <summary> /// Initializes the size and stuff. Return true if you had to enter the next line /// </summary> /// <param name="flowPath"></param> /// <param name="xPos"></param> /// <returns>True when longer than 1 line</returns> public override bool CalcBounds(TextFlowPath[] flowPath, double currentLineHeight, double lineX, double lineY) { lines = textUtil.Lineize(font, displayText, flowPath, lineX + GuiElement.scaled(PaddingLeft), lineY); BoundsPerLine = new LineRectangled[lines.Length]; for (int i = 0; i < lines.Length; i++) { TextLine line = lines[i]; BoundsPerLine[i] = line.Bounds; } if (lines.Length > 0) { lines[0].PaddingLeft = GuiElement.scaled(PaddingLeft); lines[lines.Length - 1].PaddingRight = PaddingRight; lines[lines.Length - 1].Bounds.Width += PaddingRight; } return(lines.Length > 1); }
/// <summary> /// Adds an interactive element to the composer. /// </summary> /// <param name="element">The element to add.</param> /// <param name="key">The name of the element. (default: null)</param> public GuiComposer AddInteractiveElement(GuiElement element, string key = null) { if (conditionalAdds.Count > 0 && !conditionalAdds.Peek()) { return(this); } element.RenderAsPremultipliedAlpha = this.premultipliedAlpha; if (key == null) { key = "element-" + (++currentElementKey); } interactiveElements.Add(key, element); staticElements.Add(key, element); if (element.Focusable) { element.TabIndex = currentFocusableElementKey++; } else { element.TabIndex = -1; } element.InsideClipBounds = InsideClipBounds; if (parentBoundsForNextElement.Peek() == element.Bounds) { throw new ArgumentException(string.Format("Fatal: Attempting to add a self referencing bounds->child bounds reference. This would cause a stack overflow. Make sure you don't re-use the same bounds for a parent and child element (key {0})", key)); } parentBoundsForNextElement.Peek().WithChild(element.Bounds); lastAddedElementBounds = element.Bounds; return(this); }
/// <summary> /// Takes a texture and applies some text to it. /// </summary> /// <param name="text">The text to texture.</param> /// <param name="font">The font of the text.</param> /// <param name="loadedTexture">The texture to be loaded on to.</param> /// <param name="background">The background of the text. (default: none/null)</param> public void GenOrUpdateTextTexture(string text, CairoFont font, ref LoadedTexture loadedTexture, TextBackground background = null) { if (background == null) { background = defaultBackground.Clone(); if (font.StrokeWidth > 0) { background.Padding = (int)Math.Ceiling(font.StrokeWidth); } } ElementBounds bounds = new ElementBounds(); font.AutoBoxSize(text, bounds); int width = (int)Math.Ceiling(GuiElement.scaled(bounds.fixedWidth + 1 + 2 * background.Padding)); int height = (int)Math.Ceiling(GuiElement.scaled(bounds.fixedHeight + 1 + 2 * background.Padding)); GenOrUpdateTextTexture(text, font, width, height, ref loadedTexture, background); }
public override void RenderTo(ICoreClientAPI capi, double x, double y) { float size = (float)GuiElement.scaled(25); float pad = (float)GuiElement.scaled(10); //capi.Render.RenderItemstackToGui(Stack, x + pad + size / 2, y + size / 2, 100, size, ColorUtil.WhiteArgb, true, false, false); if (Texture == null) { Recompose(capi); } capi.Render.Render2DTexturePremultipliedAlpha( Texture.TextureId, (x + pad), y + size / 4 - 3, Texture.Width, Texture.Height, 50 ); }
/// <summary> /// Adds a static element to the composer. /// </summary> /// <param name="element">The element to add.</param> /// <param name="key">The name of the element (default: null)</param> public GuiComposer AddStaticElement(GuiElement element, string key = null) { if (conditionalAdds.Count > 0 && !conditionalAdds.Peek()) { return(this); } element.RenderAsPremultipliedAlpha = this.premultipliedAlpha; if (key == null) { key = "element-" + (++currentElementKey); } staticElements.Add(key, element); parentBoundsForNextElement.Peek().WithChild(element.Bounds); lastAddedElementBounds = element.Bounds; element.InsideClipBounds = InsideClipBounds; return(this); }
/// <summary> /// marks an element as in focus. /// </summary> /// <param name="tabIndex">The tab index to focus at.</param> /// <returns>Whether or not the focus could be done.</returns> public bool FocusElement(int tabIndex) { GuiElement newFocusedElement = null; foreach (GuiElement element in interactiveElements.Values) { if (element.Focusable && element.TabIndex == tabIndex) { newFocusedElement = element; break; } } if (newFocusedElement != null) { UnfocusOwnElementsExcept(newFocusedElement); newFocusedElement.OnFocusGained(); OnFocusChanged?.Invoke(true); return(true); } return(false); }
public override void RenderTo(ICoreClientAPI capi, double x, double y) { float size = (float)GuiElement.scaled(25); float pad = (float)GuiElement.scaled(10); int index = (int)((capi.ElapsedMilliseconds / 1000) % Stacks.Count); dummySlot.Itemstack = Stacks[index]; capi.Render.RenderItemstackToGui(dummySlot, x + pad + size / 2, y + size / 2, 100, size, ColorUtil.WhiteArgb, true, false, false); if (Texture == null) { Texture = new TextTextureUtil(capi).GenTextTexture(Name, CairoFont.WhiteSmallText()); } capi.Render.Render2DTexturePremultipliedAlpha( Texture.TextureId, (x + size + GuiElement.scaled(25)), y + size / 4 - 3, Texture.Width, Texture.Height, 50 ); }
public override void ComposeElements(Context ctx, ImageSurface surface) { capi.Gui.Icons.DrawIcon(ctx, iconName, BoundsPerLine[0].X, BoundsPerLine[0].Y, GuiElement.scaled(font.UnscaledFontsize), GuiElement.scaled(font.UnscaledFontsize), font.Color); }
public override void RenderInteractiveElements(float deltaTime) { if (text == null || text.Length == 0) { return; } if (api.Render.ScissorStack.Count > 0) { api.Render.GlScissorFlag(false); } int mouseX = api.Input.MouseX; int mouseY = api.Input.MouseY; isnowshown = false; if ((autoDisplay && IsPositionInside(mouseX, mouseY)) || visible) { isnowshown = true; // Compose on demand only if (hoverTexture.TextureId == 0 && !hoverTexture.Disposed) { Recompose(); } int pad = (int)scaled(padding); double x = Bounds.renderX; double y = Bounds.renderY; if (followMouse) { x = mouseX + GuiElement.scaled(10); y = mouseY + GuiElement.scaled(15); } if (x + hoverWidth > api.Render.FrameWidth) { x -= (x + hoverWidth) - api.Render.FrameWidth; } if (y + hoverHeight > api.Render.FrameHeight) { y -= (y + hoverHeight) - api.Render.FrameHeight; } api.Render.Render2DTexture(hoverTexture.TextureId, (int)x + (int)Bounds.absPaddingX, (int)y + (int)Bounds.absPaddingY, (int)hoverWidth + 1, (int)hoverHeight + 1, zPosition, RenderColor); Bounds.renderOffsetX = x - Bounds.renderX + pad; Bounds.renderOffsetY = y - Bounds.renderY + pad; descriptionElement.RenderColor = rendercolor; descriptionElement.RenderAsPremultipliedAlpha = RenderAsPremultipliedAlpha; descriptionElement.RenderInteractiveElements(deltaTime); Bounds.renderOffsetX = 0; Bounds.renderOffsetY = 0; } if (api.Render.ScissorStack.Count > 0) { api.Render.GlScissorFlag(true); } }
public override void RenderInteractiveElements(float deltaTime, double renderX, double renderY) { LineRectangled bounds = BoundsPerLine[0]; ElementBounds scibounds = ElementBounds.FixedSize((int)(bounds.Width / API.Config.RuntimeEnv.GUIScale), (int)(bounds.Height / API.Config.RuntimeEnv.GUIScale)); scibounds.ParentBounds = capi.Gui.WindowBounds; scibounds.CalcWorldBounds(); scibounds.absFixedX = renderX + bounds.X; scibounds.absFixedY = renderY + bounds.Y + offY; api.Render.PushScissor(scibounds, true); api.Render.RenderItemstackToGui( slot, renderX + bounds.X + bounds.Width * 0.5f + offX, renderY + bounds.Y + bounds.Height * 0.5f + offY, GuiElement.scaled(100), (float)size * 0.58f, ColorUtil.WhiteArgb, true, false, false); api.Render.PopScissor(); int relx = (int)(api.Input.MouseX - renderX); int rely = (int)(api.Input.MouseY - renderY); if (bounds.PointInside(relx, rely)) { RenderItemstackTooltip(slot, renderX + relx + offX, renderY + rely + offY, deltaTime); } }
public override bool CalcBounds(TextFlowPath[] flowPath, double currentLineHeight, double lineX, double lineY) { TextFlowPath curfp = GetCurrentFlowPathSection(flowPath, lineY); bool requireLinebreak = lineX + BoundsPerLine[0].Width > curfp.X2; this.BoundsPerLine[0].X = requireLinebreak ? 0 : lineX; this.BoundsPerLine[0].Y = lineY + (requireLinebreak ? currentLineHeight + GuiElement.scaled(UnscaledMarginTop) : 0); return(requireLinebreak); }
public void CalcHeightAndPositions() { Bounds.CalcWorldBounds(); if (DebugLogging) { api.Logger.VerboseDebug("GuiElementRichtext: before bounds: {0}/{1} w/h = {2},{3}", Bounds.absX, Bounds.absY, Bounds.OuterWidth, Bounds.OuterHeight); } double posX = 0; double posY = 0; List <int> currentLine = new List <int>(); List <TextFlowPath> flowPathList = new List <TextFlowPath>(); flowPathList.Add(new TextFlowPath(Bounds.InnerWidth)); double lineHeight = 0; double ascentHeight = 0; RichTextComponentBase comp = null; for (int i = 0; i < Components.Length; i++) { comp = Components[i]; bool didLineBreak = comp.CalcBounds(flowPathList.ToArray(), lineHeight, posX, posY); if (DebugLogging) { api.Logger.VerboseDebug("GuiElementRichtext, add comp {0}, posY={1}, lineHeight={2}", i, posY, lineHeight); api.Logger.VerboseDebug("GuiElementRichtext, Comp bounds 0 w/h: {0}/{1}", comp.BoundsPerLine[0].Width, comp.BoundsPerLine[0].Height); } posX += scaled(comp.PaddingLeft); if (comp.Float == EnumFloat.None) { posX = 0; posY += Math.Max(lineHeight, comp.BoundsPerLine[0].Height) + (didLineBreak ? GuiElement.scaled(comp.UnscaledMarginTop) : 0); posY = Math.Ceiling(posY); currentLine.Clear(); lineHeight = 0; ascentHeight = 0; continue; } if (didLineBreak) { lineHeight = Math.Ceiling(Math.Max(lineHeight, comp.BoundsPerLine[0].Height)); ascentHeight = Math.Ceiling(Math.Max(ascentHeight, comp.BoundsPerLine[0].AscentOrHeight)); // All previous elements in this line might need to have their Y pos adjusted due to a larger element in the line foreach (int index in currentLine) { RichTextComponentBase lineComp = Components[index]; Rectangled lastLineBounds = lineComp.BoundsPerLine[lineComp.BoundsPerLine.Length - 1]; if (lineComp.VerticalAlign == EnumVerticalAlign.Bottom) { lastLineBounds.Y = Math.Ceiling(lastLineBounds.Y + ascentHeight - lineComp.BoundsPerLine[lineComp.BoundsPerLine.Length - 1].AscentOrHeight); } if (lineComp.VerticalAlign == EnumVerticalAlign.Middle) { lastLineBounds.Y = Math.Ceiling(lastLineBounds.Y + ascentHeight - lineComp.BoundsPerLine[lineComp.BoundsPerLine.Length - 1].AscentOrHeight / 2); } } // The current element that was still on the same line as well // Offset all lines by the gained y-offset on the first line if (comp.VerticalAlign == EnumVerticalAlign.Bottom) { foreach (var val in comp.BoundsPerLine) { val.Y = Math.Ceiling(val.Y + ascentHeight - comp.BoundsPerLine[0].AscentOrHeight); } } if (comp.VerticalAlign == EnumVerticalAlign.Middle) { foreach (var val in comp.BoundsPerLine) { val.Y = Math.Ceiling(val.Y + ascentHeight - comp.BoundsPerLine[0].AscentOrHeight / 2); } } currentLine.Clear(); currentLine.Add(i); posY += lineHeight; for (int k = 1; k < comp.BoundsPerLine.Length - 1; k++) { posY += comp.BoundsPerLine[k].Height; } posY += scaled(comp.UnscaledMarginTop); posY = Math.Ceiling(posY); posX = comp.BoundsPerLine[comp.BoundsPerLine.Length - 1].Width; // + GuiElement.scaled(comp.PaddingLeft); - this adds too much padding when there is a line break inside a rich text compoment and afterwards there comes a link if (comp.BoundsPerLine[comp.BoundsPerLine.Length - 1].Width > 0) { lineHeight = comp.BoundsPerLine[comp.BoundsPerLine.Length - 1].Height; ascentHeight = comp.BoundsPerLine[comp.BoundsPerLine.Length - 1].AscentOrHeight; } else { lineHeight = 0; ascentHeight = 0; } } else { if (comp.Float == EnumFloat.Inline && comp.BoundsPerLine.Length > 0) { posX += comp.BoundsPerLine[0].Width; lineHeight = Math.Max(comp.BoundsPerLine[0].Height, lineHeight); ascentHeight = Math.Max(comp.BoundsPerLine[0].AscentOrHeight, ascentHeight); currentLine.Add(i); } } if (comp.Float != EnumFloat.Inline) { ConstrainTextFlowPath(flowPathList, posY, comp); } } if (DebugLogging) { api.Logger.VerboseDebug("GuiElementRichtext: after loop. posY = {0}", posY); } if (comp != null && posX > 0 && comp.BoundsPerLine.Length > 0) { posY += lineHeight; } Bounds.fixedHeight = (posY + 1) / RuntimeEnv.GUIScale; double maxHeight = 0; foreach (int index in currentLine) { RichTextComponentBase lineComp = Components[index]; Rectangled lastLineBounds = lineComp.BoundsPerLine[lineComp.BoundsPerLine.Length - 1]; maxHeight = Math.Max(maxHeight, lastLineBounds.Height); } foreach (int index in currentLine) { RichTextComponentBase lineComp = Components[index]; Rectangled lastLineBounds = lineComp.BoundsPerLine[lineComp.BoundsPerLine.Length - 1]; if (lineComp.VerticalAlign == EnumVerticalAlign.Bottom) { lastLineBounds.Y = Math.Ceiling(lastLineBounds.Y + ascentHeight - lineComp.BoundsPerLine[lineComp.BoundsPerLine.Length - 1].AscentOrHeight); } if (lineComp.VerticalAlign == EnumVerticalAlign.Middle) { lastLineBounds.Y = (maxHeight - lastLineBounds.Height) / 2f; } } this.flowPath = flowPathList.ToArray(); if (DebugLogging) { api.Logger.VerboseDebug("GuiElementRichtext: after bounds: {0}/{1} w/h = {2},{3}", Bounds.absX, Bounds.absY, Bounds.OuterWidth, Bounds.OuterHeight); api.Logger.VerboseDebug("GuiElementRichtext: posY = {0}", posY); api.Logger.VerboseDebug("GuiElementRichtext: framewidth/height: {0}/{1}", api.Render.FrameWidth, api.Render.FrameHeight); } }
public override void ComposeElements(Context ctxStatic, ImageSurface surfaceStatic) { insideBounds = new ElementBounds().WithFixedPadding(unscaledCellSpacing).WithEmptyParent(); insideBounds.CalcWorldBounds(); CalcTotalHeight(); Bounds.CalcWorldBounds(); ImageSurface surface = new ImageSurface(Format.Argb32, (int)Bounds.InnerWidth, (int)GuiElement.scaled(unscaledCellHeight)); Context ctx = new Context(surface); ctx.SetSourceRGBA(1, 1, 1, 0.5); ctx.Paint(); generateTexture(surface, ref hoverOverlayTexture); ctx.Dispose(); surface.Dispose(); }
public override void RenderInteractiveElements(float deltaTime, double renderX, double renderY) { LineRectangled bounds = BoundsPerLine[0]; GridRecipeAndUnnamedIngredients recipeunin = GridRecipesAndUnIn[curItemIndex]; if ((secondsVisible -= deltaTime) <= 0) { secondsVisible = 1; curItemIndex = (curItemIndex + 1) % GridRecipesAndUnIn.Length; secondCounter++; } LoadedTexture extraTextTexture; if (extraTexts.TryGetValue(curItemIndex, out extraTextTexture)) { capi.Render.Render2DTexturePremultipliedAlpha(extraTextTexture.TextureId, (float)(renderX + bounds.X), (float)(renderY + bounds.Y + 3 * (size + 3)), extraTextTexture.Width, extraTextTexture.Height); } int mx = api.Input.MouseX; int my = api.Input.MouseY; double rx = 0, ry = 0; for (int x = 0; x < 3; x++) { for (int y = 0; y < 3; y++) { int index = recipeunin.Recipe.GetGridIndex(y, x, recipeunin.Recipe.resolvedIngredients, recipeunin.Recipe.Width); CraftingRecipeIngredient ingred = recipeunin.Recipe.GetElementInGrid(y, x, recipeunin.Recipe.resolvedIngredients, recipeunin.Recipe.Width); if (ingred == null) { continue; } rx = renderX + bounds.X + x * (size + GuiElement.scaled(3)); ry = renderY + bounds.Y + y * (size + GuiElement.scaled(3)); ItemStack[] unnamedWildcardStacklist = null; if (recipeunin.unnamedIngredients?.TryGetValue(index, out unnamedWildcardStacklist) == true) { dummyslot.Itemstack = unnamedWildcardStacklist[secondCounter % unnamedWildcardStacklist.Length]; dummyslot.Itemstack.StackSize = ingred.Quantity; } else { dummyslot.Itemstack = ingred.ResolvedItemstack.Clone(); } var scale = RuntimeEnv.GUIScale; ElementBounds scissorBounds = ElementBounds.Fixed(rx / scale, ry / scale, size / scale, size / scale).WithEmptyParent(); scissorBounds.CalcWorldBounds(); api.Render.PushScissor(scissorBounds, true); // 1.16.0: Fugly (but backwards compatible) hack: We temporarily store the ingredient code in an unused field of ItemSlot so that OnHandbookRecipeRender() has access to that number. Proper solution would be to alter the method signature to pass on this value. dummyslot.BackgroundIcon = index + ""; dummyslot.Itemstack.Collectible.OnHandbookRecipeRender(capi, recipeunin.Recipe, dummyslot, rx + size * 0.5f, ry + size * 0.5f, size); dummyslot.BackgroundIcon = null; api.Render.PopScissor(); // Super weird coordinates, no idea why double dx = mx - rx + 1; double dy = my - ry + 2; if (dx >= 0 && dx < size && dy >= 0 && dy < size) { RenderItemstackTooltip(dummyslot, rx + dx, ry + dy, deltaTime); } } } }
/// <summary> /// Takes a string of text and applies a texture to it. /// </summary> /// <param name="text">The text to texture.</param> /// <param name="font">The font of the text.</param> /// <param name="width">The width of the text.</param> /// <param name="height">The height of the text.</param> /// <param name="background">The background of the text. (default: none/null)</param> /// <returns>The texturized text.</returns> public LoadedTexture GenTextTexture(string text, CairoFont font, int width, int height, TextBackground background = null) { if (background == null) { background = defaultBackground; } ImageSurface surface = new ImageSurface(Format.Argb32, width, height); Context ctx = new Context(surface); if (background?.FillColor != null) { ctx.SetSourceRGBA(background.FillColor); GuiElement.RoundRectangle(ctx, 0, 0, width, height, background.Radius); ctx.Fill(); } if (background?.Shade == true) { ctx.SetSourceRGBA(GuiStyle.DialogLightBgColor[0] * 1.4, GuiStyle.DialogStrongBgColor[1] * 1.4, GuiStyle.DialogStrongBgColor[2] * 1.4, 1); ctx.LineWidth = 5;// background.BorderWidth * 1.75; GuiElement.RoundRectangle(ctx, 0, 0, width, height, background.Radius); ctx.StrokePreserve(); surface.Blur(6.2); ctx.SetSourceRGBA(new double[] { 45 / 255.0, 35 / 255.0, 33 / 255.0, 1 }); ctx.LineWidth = background.BorderWidth; ctx.Stroke(); } if (background?.BorderColor != null) { ctx.SetSourceRGBA(background.BorderColor); GuiElement.RoundRectangle(ctx, 0, 0, width, height, background.Radius); ctx.LineWidth = background.BorderWidth; ctx.Stroke(); } font.SetupContext(ctx); double fontHeight = font.GetFontExtents().Height; string[] lines = text.Split('\n'); for (int i = 0; i < lines.Length; i++) { lines[i] = lines[i].TrimEnd(); ctx.MoveTo(background.Padding, background.Padding + ctx.FontExtents.Ascent + i * fontHeight); if (font.StrokeWidth > 0) { ctx.TextPath(lines[i]); ctx.LineWidth = font.StrokeWidth; ctx.SetSourceRGBA(font.StrokeColor); ctx.StrokePreserve(); ctx.SetSourceRGBA(font.Color); ctx.Fill(); } else { ctx.ShowText(lines[i]); if (font.RenderTwice) { ctx.ShowText(lines[i]); } } } int textureId = capi.Gui.LoadCairoTexture(surface, true); surface.Dispose(); ctx.Dispose(); return(new LoadedTexture(capi) { TextureId = textureId, Width = width, Height = height }); }
/// <summary> /// Flips through given array of grid recipes every second /// </summary> /// <param name="capi"></param> /// <param name="gridrecipes"></param> /// <param name="size"></param> /// <param name="floatType"></param> /// <param name="onStackClicked"></param> /// <param name="allStacks">If set, will resolve wildcards based on this list, otherwise will search all available blocks/items</param> public SlideshowGridRecipeTextComponent(ICoreClientAPI capi, GridRecipe[] gridrecipes, double size, EnumFloat floatType, Action <ItemStack> onStackClicked = null, ItemStack[] allStacks = null) : base(capi) { size = GuiElement.scaled(size); this.onStackClicked = onStackClicked; this.Float = floatType; this.BoundsPerLine = new LineRectangled[] { new LineRectangled(0, 0, 3 * (size + 3), 3 * (size + 3)) }; this.size = size; Random fixedRand = new Random(123); // Expand wild cards List <GridRecipeAndUnnamedIngredients> resolvedGridRecipes = new List <GridRecipeAndUnnamedIngredients>(); Queue <GridRecipe> halfResolvedRecipes = new Queue <GridRecipe>(gridrecipes); bool allResolved = false; while (!allResolved) { allResolved = true; int cnt = halfResolvedRecipes.Count; while (cnt-- > 0) { GridRecipe toTestRecipe = halfResolvedRecipes.Dequeue(); Dictionary <int, ItemStack[]> unnamedIngredients = null; bool thisResolved = true; for (int j = 0; j < toTestRecipe.resolvedIngredients.Length; j++) { CraftingRecipeIngredient ingred = toTestRecipe.resolvedIngredients[j]; if (ingred != null && ingred.IsWildCard) { allResolved = false; thisResolved = false; ItemStack[] stacks = ResolveWildCard(capi.World, ingred, allStacks); if (ingred.Name == null) { if (unnamedIngredients == null) { unnamedIngredients = new Dictionary <int, ItemStack[]>(); } unnamedIngredients[j] = ((ItemStack[])stacks.Clone()).Shuffle(fixedRand); thisResolved = true; continue; } if (stacks.Length == 0) { throw new ArgumentException("Attempted to resolve the recipe ingredient wildcard " + ingred.Type + " " + ingred.Code + " but there are no such items/blocks!"); } for (int k = 0; k < stacks.Length; k++) { GridRecipe cloned = toTestRecipe.Clone(); for (int m = 0; m < cloned.resolvedIngredients.Length; m++) { CraftingRecipeIngredient clonedingred = cloned.resolvedIngredients[m]; if (clonedingred != null && clonedingred.Code.Equals(ingred.Code)) { clonedingred.Code = stacks[k].Collectible.Code; clonedingred.IsWildCard = false; clonedingred.ResolvedItemstack = stacks[k]; } } halfResolvedRecipes.Enqueue(cloned); } break; } } if (thisResolved) { resolvedGridRecipes.Add(new GridRecipeAndUnnamedIngredients() { Recipe = toTestRecipe, unnamedIngredients = unnamedIngredients }); } } } resolveCache.Clear(); this.GridRecipesAndUnIn = resolvedGridRecipes.ToArray(); this.GridRecipesAndUnIn.Shuffle(fixedRand); for (int i = 0; i < GridRecipesAndUnIn.Length; i++) { string trait = GridRecipesAndUnIn[i].Recipe.RequiresTrait; if (trait != null) { extraTexts[i] = capi.Gui.TextTexture.GenTextTexture(Lang.Get("* Requires {0} trait", trait), CairoFont.WhiteDetailText()); } } if (GridRecipesAndUnIn.Length == 0) { throw new ArgumentException("Could not resolve any of the supplied grid recipes?"); } }