static void Richtextify(ICoreClientAPI capi, VtmlToken token, ref List <RichTextComponentBase> elems, Stack <CairoFont> fontStack, Action <LinkTextComponent> didClickLink) { if (token is VtmlTagToken) { VtmlTagToken tagToken = token as VtmlTagToken; switch (tagToken.Name) { case "br": elems.Add(new RichTextComponent(capi, "\r\n", fontStack.Peek())); break; case "i": CairoFont font = fontStack.Peek().Clone(); font.Slant = FontSlant.Italic; fontStack.Push(font); foreach (var val in tagToken.ChildElements) { Richtextify(capi, val, ref elems, fontStack, didClickLink); } fontStack.Pop(); break; case "a": LinkTextComponent cmp = new LinkTextComponent(capi, tagToken.ContentText, fontStack.Peek(), didClickLink); tagToken.Attributes.TryGetValue("href", out cmp.Href); elems.Add(cmp); break; case "icon": string iconName; tagToken.Attributes.TryGetValue("name", out iconName); IconComponent iconcmp = new IconComponent(capi, iconName, fontStack.Peek()); elems.Add(iconcmp); break; case "itemstack": string code; string type; float size = (float)fontStack.Peek().GetFontExtents().Height; EnumFloat floatType = EnumFloat.Inline; string floattypestr; if (tagToken.Attributes.TryGetValue("floattype", out floattypestr)) { if (!Enum.TryParse(floattypestr, out floatType)) { floatType = EnumFloat.Inline; } } tagToken.Attributes.TryGetValue("code", out code); if (!tagToken.Attributes.TryGetValue("type", out type)) { type = "block"; } ItemStack stack; if (type == "item") { stack = new ItemStack(capi.World.GetItem(new AssetLocation(code))); } else { stack = new ItemStack(capi.World.GetBlock(new AssetLocation(code))); } float sizemul = 1f; if (tagToken.Attributes.TryGetValue("rsize", out var sizemulstr)) { sizemul = sizemulstr.ToFloat(); } SlideshowItemstackTextComponent stckcmp = new SlideshowItemstackTextComponent(capi, new ItemStack[] { stack }, size / RuntimeEnv.GUIScale, floatType); stckcmp.renderSize *= sizemul; stckcmp.VerticalAlign = EnumVerticalAlign.Middle; if (tagToken.Attributes.TryGetValue("offx", out var offxstr)) { stckcmp.offX = GuiElement.scaled(offxstr.ToFloat(0)); } if (tagToken.Attributes.TryGetValue("offy", out var offystr)) { stckcmp.offY = GuiElement.scaled(offystr.ToFloat(0)); } elems.Add(stckcmp); break; case "font": fontStack.Push(getFont(tagToken, fontStack)); foreach (var val in tagToken.ChildElements) { Richtextify(capi, val, ref elems, fontStack, didClickLink); } fontStack.Pop(); break; case "clear": elems.Add(new ClearFloatTextComponent(capi)); break; case "strong": fontStack.Push(fontStack.Peek().Clone().WithWeight(Cairo.FontWeight.Bold)); foreach (var val in tagToken.ChildElements) { Richtextify(capi, val, ref elems, fontStack, didClickLink); } fontStack.Pop(); break; } if (tagToken.Name != null && TagConverters.ContainsKey(tagToken.Name)) { RichTextComponentBase elem = TagConverters[tagToken.Name](capi, tagToken, fontStack, didClickLink); if (elem != null) { elems.Add(elem); } } } else { VtmlTextToken textToken = token as VtmlTextToken; elems.Add(new RichTextComponent(capi, textToken.Text, fontStack.Peek())); } }
public ItemstackTextComponent(ICoreClientAPI capi, ItemStack itemstack, double size, double sidePadding = 0, EnumFloat floatType = EnumFloat.Left, Common.Action <ItemStack> onStackClicked = null) : base(capi) { slot = new DummySlot(itemstack); this.onStackClicked = onStackClicked; this.Float = floatType; this.size = size; this.BoundsPerLine = new LineRectangled[] { new LineRectangled(0, 0, size + sidePadding, size) }; //PaddingRight = 0; }
/// <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, 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 (allstacks != null) { 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) { if (val.Collectible.Attributes?["handbook"]?["isDuplicate"].AsBool(false) == true) { nowGroupedStacks.Add(val); continue; } 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)) }; }
/// <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); }
private void ConstrainTextFlowPath(List <TextFlowPath> flowPath, double posY, RichTextComponentBase comp) { Rectangled rect = comp.BoundsPerLine[0]; EnumFloat elementFloat = comp.Float; double x1 = elementFloat == EnumFloat.Left ? rect.Width + comp.PaddingRight : 0; double x2 = elementFloat == EnumFloat.Right ? Bounds.InnerWidth - rect.Width - comp.PaddingLeft : Bounds.InnerWidth; double remainingHeight = rect.Height; for (int i = 0; i < flowPath.Count; i++) { TextFlowPath tfp = flowPath[i]; if (tfp.Y2 <= posY) { continue; // we already passed this one } double hereX1 = Math.Max(x1, tfp.X1); double hereX2 = Math.Min(x2, tfp.X2); // Current bounds are taller, let's make a split and insert ours if (tfp.Y2 > posY + rect.Height) { // Already more contrained, don't touch if (x1 <= tfp.X1 && x2 >= tfp.X2) { continue; } if (i == 0) { // We're at the begining, so don't need a "before" element flowPath[i] = new TextFlowPath(hereX1, posY, hereX2, posY + rect.Height); flowPath.Insert(i + 1, new TextFlowPath(tfp.X1, posY + rect.Height, tfp.X2, tfp.Y2)); } else { flowPath[i] = new TextFlowPath(tfp.X1, tfp.Y1, tfp.X2, posY); flowPath.Insert(i + 1, new TextFlowPath(tfp.X1, posY + rect.Height, tfp.X2, tfp.Y2)); flowPath.Insert(i, new TextFlowPath(hereX1, posY, hereX2, posY + rect.Height)); } remainingHeight = 0; break; } else // Current bounds are shorter, let's update it { flowPath[i].X1 = hereX1; flowPath[i].X2 = hereX2; remainingHeight -= tfp.Y2 - posY; } } if (remainingHeight > 0) { flowPath.Add(new TextFlowPath(x1, posY, x2, posY + remainingHeight)); } }
/// <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?"); } }