From a057218acceed6fe5aec329ef4d2ae736b3d3878 Mon Sep 17 00:00:00 2001
From: Dale McCoy <21223975+DaleStan@users.noreply.github.com>
Date: Sat, 8 Jun 2024 14:35:26 -0400
Subject: [PATCH 1/3] Replace the extendHeader parameter with an
ObjectTooltipOptions parameter.
This allows addition of new options without changing every method that might eventually display a tooltip.
---
Yafc/Widgets/ImmediateWidgets.cs | 10 +++++-----
Yafc/Widgets/ObjectTooltip.cs | 19 ++++++++++++-------
Yafc/Windows/DependencyExplorer.cs | 2 +-
Yafc/Windows/MainScreen.cs | 4 ++--
Yafc/Windows/SelectMultiObjectPanel.cs | 4 ++--
Yafc/Windows/SelectObjectPanel.cs | 6 +++---
Yafc/Windows/SelectSingleObjectPanel.cs | 4 ++--
7 files changed, 27 insertions(+), 22 deletions(-)
diff --git a/Yafc/Widgets/ImmediateWidgets.cs b/Yafc/Widgets/ImmediateWidgets.cs
index 7befb7d3..802a8c6a 100644
--- a/Yafc/Widgets/ImmediateWidgets.cs
+++ b/Yafc/Widgets/ImmediateWidgets.cs
@@ -69,7 +69,7 @@ public static bool BuildFloatInput(this ImGui gui, float value, out float newVal
return false;
}
- public static Click BuildFactorioObjectButton(this ImGui gui, Rect rect, FactorioObject? obj, SchemeColor bgColor = SchemeColor.None, bool extendHeader = false) {
+ public static Click BuildFactorioObjectButton(this ImGui gui, Rect rect, FactorioObject? obj, SchemeColor bgColor = SchemeColor.None, ObjectTooltipOptions tooltipOptions = default) {
SchemeColor overColor;
if (bgColor == SchemeColor.None) {
overColor = SchemeColor.Grey;
@@ -82,7 +82,7 @@ public static Click BuildFactorioObjectButton(this ImGui gui, Rect rect, Factori
}
var evt = gui.BuildButton(rect, bgColor, overColor, button: 0);
if (evt == ButtonEvent.MouseOver && obj != null) {
- MainScreen.Instance.ShowTooltip(obj, gui, rect, extendHeader);
+ MainScreen.Instance.ShowTooltip(obj, gui, rect, tooltipOptions);
}
else if (evt == ButtonEvent.Click) {
if (gui.actionParameter == SDL.SDL_BUTTON_MIDDLE && obj != null) {
@@ -109,9 +109,9 @@ public static Click BuildFactorioObjectButton(this ImGui gui, Rect rect, Factori
/// Draws a button displaying the icon belonging to a , or an empty box as a placeholder if no object is available.
/// Draw the icon for this object, or an empty box if this is .
/// If , this icon will be displayed at , instead of at 100% scale.
- public static Click BuildFactorioObjectButton(this ImGui gui, FactorioObject? obj, float size = 2f, MilestoneDisplay display = MilestoneDisplay.Normal, SchemeColor bgColor = SchemeColor.None, bool extendHeader = false, bool useScale = false) {
+ public static Click BuildFactorioObjectButton(this ImGui gui, FactorioObject? obj, float size = 2f, MilestoneDisplay display = MilestoneDisplay.Normal, SchemeColor bgColor = SchemeColor.None, bool useScale = false, ObjectTooltipOptions tooltipOptions = default) {
gui.BuildFactorioObjectIcon(obj, display, size, useScale);
- return gui.BuildFactorioObjectButton(gui.lastRect, obj, bgColor, extendHeader);
+ return gui.BuildFactorioObjectButton(gui.lastRect, obj, bgColor, tooltipOptions);
}
public static Click BuildFactorioObjectButtonWithText(this ImGui gui, FactorioObject? obj, string? extraText = null, float size = 2f, MilestoneDisplay display = MilestoneDisplay.Normal) {
@@ -209,7 +209,7 @@ public static Click BuildFactorioObjectWithAmount(this ImGui gui, FactorioObject
using (gui.EnterFixedPositioning(3f, 3f, default)) {
gui.allocator = RectAllocator.Stretch;
gui.spacing = 0f;
- Click clicked = gui.BuildFactorioObjectButton(goods, 3f, MilestoneDisplay.Contained, bgColor, useScale: useScale);
+ Click clicked = gui.BuildFactorioObjectButton(goods, 3f, MilestoneDisplay.Contained, bgColor, useScale);
if (goods != null) {
gui.BuildText(DataUtils.FormatAmount(amount, unit), Font.text, false, RectAlignment.Middle, textColor);
if (InputSystem.Instance.control && gui.BuildButton(gui.lastRect, SchemeColor.None, SchemeColor.Grey) == ButtonEvent.MouseOver) {
diff --git a/Yafc/Widgets/ObjectTooltip.cs b/Yafc/Widgets/ObjectTooltip.cs
index 7a037f5a..f9651be5 100644
--- a/Yafc/Widgets/ObjectTooltip.cs
+++ b/Yafc/Widgets/ObjectTooltip.cs
@@ -10,15 +10,12 @@ public class ObjectTooltip : Tooltip {
public ObjectTooltip() : base(new Padding(0f, 0f, 0f, 0.5f), 25f) { }
private IFactorioObjectWrapper target = null!; // null-forgiving: Set by SetFocus, aka ShowTooltip.
- ///
- /// If and the target object is not a , this tooltip will specify the type of object.
- ///
- private bool extendHeader;
+ private ObjectTooltipOptions tooltipOptions;
private void BuildHeader(ImGui gui) {
using (gui.EnterGroup(new Padding(1f, 0.5f), RectAllocator.LeftAlign, spacing: 0f)) {
string name = target.text;
- if (extendHeader && target is not Goods) {
+ if (tooltipOptions.ExtendHeader && target is not Goods) {
name = name + " (" + target.target.type + ")";
}
@@ -500,12 +497,20 @@ private void BuildTechnology(Technology technology, ImGui gui) {
}
}
- public void SetFocus(IFactorioObjectWrapper target, ImGui gui, Rect rect, bool extendHeader = false) {
- this.extendHeader = extendHeader;
+ public void SetFocus(IFactorioObjectWrapper target, ImGui gui, Rect rect, ObjectTooltipOptions tooltipOptions) {
+ this.tooltipOptions = tooltipOptions;
this.target = target;
base.SetFocus(gui, rect);
}
public bool IsSameObjectHovered(ImGui gui, FactorioObject? factorioObject) => source == gui && factorioObject == target.target && gui.IsMouseOver(sourceRect);
}
+
+ public struct ObjectTooltipOptions {
+ ///
+ /// If and the target object is not a , this tooltip will specify the type of object.
+ /// e.g. "Radar" is the item, "Radar (Recipe)" is the recipe, and "Radar (Entity)" is the building.
+ ///
+ public bool ExtendHeader { get; set; }
+ }
}
diff --git a/Yafc/Windows/DependencyExplorer.cs b/Yafc/Windows/DependencyExplorer.cs
index 94a4bfd2..2c86de92 100644
--- a/Yafc/Windows/DependencyExplorer.cs
+++ b/Yafc/Windows/DependencyExplorer.cs
@@ -45,7 +45,7 @@ private void DrawFactorioObject(ImGui gui, FactorioId id) {
string text = fobj.locName + " (" + fobj.type + ")";
gui.RemainingRow(0.5f).BuildText(text, null, true, color: fobj.IsAccessible() ? SchemeColor.BackgroundText : SchemeColor.BackgroundTextFaint);
}
- if (gui.BuildFactorioObjectButton(gui.lastRect, fobj, extendHeader: true) == Click.Left) {
+ if (gui.BuildFactorioObjectButton(gui.lastRect, fobj, tooltipOptions: new() { ExtendHeader = true }) == Click.Left) {
Change(fobj);
}
}
diff --git a/Yafc/Windows/MainScreen.cs b/Yafc/Windows/MainScreen.cs
index b9aa39dc..761c78e5 100644
--- a/Yafc/Windows/MainScreen.cs
+++ b/Yafc/Windows/MainScreen.cs
@@ -523,8 +523,8 @@ private async void DoCheckForUpdates() {
}
}
- public void ShowTooltip(IFactorioObjectWrapper obj, ImGui source, Rect sourceRect, bool extendHeader = false) {
- objectTooltip.SetFocus(obj, source, sourceRect, extendHeader);
+ public void ShowTooltip(IFactorioObjectWrapper obj, ImGui source, Rect sourceRect, ObjectTooltipOptions tooltipOptions = default) {
+ objectTooltip.SetFocus(obj, source, sourceRect, tooltipOptions);
ShowTooltip(objectTooltip);
}
diff --git a/Yafc/Windows/SelectMultiObjectPanel.cs b/Yafc/Windows/SelectMultiObjectPanel.cs
index 64a43ecd..dd5d1d6a 100644
--- a/Yafc/Windows/SelectMultiObjectPanel.cs
+++ b/Yafc/Windows/SelectMultiObjectPanel.cs
@@ -28,8 +28,8 @@ public static void Select(IEnumerable list, string header, Action selec
}, false);
}
- protected override void NonNullElementDrawer(ImGui gui, FactorioObject element, int index) {
- Click click = gui.BuildFactorioObjectButton(element, 2.5f, MilestoneDisplay.Contained, results.Contains(element) ? SchemeColor.Primary : SchemeColor.None, extendHeader, true);
+ protected override void NonNullElementDrawer(ImGui gui, FactorioObject element) {
+ Click click = gui.BuildFactorioObjectButton(element, 2.5f, MilestoneDisplay.Contained, results.Contains(element) ? SchemeColor.Primary : SchemeColor.None, true, new() { ExtendHeader = extendHeader });
if (checkMark(element)) {
gui.DrawIcon(Rect.SideRect(gui.lastRect.TopLeft + new Vector2(1, 0), gui.lastRect.BottomRight - new Vector2(0, 1)), Icon.Check, SchemeColor.Green);
diff --git a/Yafc/Windows/SelectObjectPanel.cs b/Yafc/Windows/SelectObjectPanel.cs
index 5278b848..a0a02a46 100644
--- a/Yafc/Windows/SelectObjectPanel.cs
+++ b/Yafc/Windows/SelectObjectPanel.cs
@@ -17,7 +17,7 @@ public abstract class SelectObjectPanel : PseudoScreen {
private string? noneTooltip;
///
/// If and the object being hovered is not a , the should specify the type of object.
- /// See also .
+ /// See also .
///
protected bool extendHeader { get; private set; }
@@ -80,7 +80,7 @@ private void ElementDrawer(ImGui gui, FactorioObject? element, int index) {
}
}
else {
- NonNullElementDrawer(gui, element, index);
+ NonNullElementDrawer(gui, element);
}
}
@@ -88,7 +88,7 @@ private void ElementDrawer(ImGui gui, FactorioObject? element, int index) {
/// Called to draw a that should be displayed in this panel, and to handle mouse-over and click events.
/// will not be null. If a "none" or "clear" option is present, takes care of that option.
///
- protected abstract void NonNullElementDrawer(ImGui gui, FactorioObject element, int index);
+ protected abstract void NonNullElementDrawer(ImGui gui, FactorioObject element);
private bool ElementFilter(FactorioObject? data, SearchQuery query) => data?.Match(query) ?? true;
diff --git a/Yafc/Windows/SelectSingleObjectPanel.cs b/Yafc/Windows/SelectSingleObjectPanel.cs
index fec00ee3..2fb17b30 100644
--- a/Yafc/Windows/SelectSingleObjectPanel.cs
+++ b/Yafc/Windows/SelectSingleObjectPanel.cs
@@ -33,8 +33,8 @@ public static void Select(IEnumerable list, string header, Action selec
public static void SelectWithNone(IEnumerable list, string header, Action selectItem, IComparer? ordering = null, string? noneTooltip = null) where T : FactorioObject
=> Instance.Select(list, header, selectItem, ordering, (obj, mappedAction) => mappedAction(obj), true, noneTooltip);
- protected override void NonNullElementDrawer(ImGui gui, FactorioObject element, int index) {
- if (gui.BuildFactorioObjectButton(element, 2.5f, MilestoneDisplay.Contained, extendHeader: extendHeader, useScale: true) == Click.Left) {
+ protected override void NonNullElementDrawer(ImGui gui, FactorioObject element) {
+ if (gui.BuildFactorioObjectButton(element, 2.5f, MilestoneDisplay.Contained, useScale: true, tooltipOptions: new() { ExtendHeader = extendHeader }) == Click.Left) {
CloseWithResult(element);
}
}
From f008fbfd196160aa8a60084c0c82c5e04b8474e9 Mon Sep 17 00:00:00 2001
From: Dale McCoy <21223975+DaleStan@users.noreply.github.com>
Date: Fri, 17 May 2024 20:13:15 -0400
Subject: [PATCH 2/3] Add hints that ctrl+click can be used to add a recipe, or
to explain why it can't.
---
Yafc.Model/Data/DataUtils.cs | 34 ++++++++++++---
Yafc/Widgets/ImmediateWidgets.cs | 8 ++--
Yafc/Widgets/ObjectTooltip.cs | 36 ++++++++++++++++
.../ProductionTable/ProductionTableView.cs | 41 ++++++++++---------
changelog.txt | 2 +
5 files changed, 92 insertions(+), 29 deletions(-)
diff --git a/Yafc.Model/Data/DataUtils.cs b/Yafc.Model/Data/DataUtils.cs
index 1e574b7e..8ff39883 100644
--- a/Yafc.Model/Data/DataUtils.cs
+++ b/Yafc.Model/Data/DataUtils.cs
@@ -96,10 +96,31 @@ public static Bits GetMilestoneOrder(FactorioId id) {
public static readonly Random random = new Random();
- public static bool SelectSingle(this T[] list, [NotNullWhen(true)] out T? element) where T : FactorioObject {
+ ///
+ /// Call to get the favorite or only useful item in the list, considering milestones, accessibility, and , provided there is exactly one such item.
+ /// If no best item exists, returns . Always returns a tooltip applicable to using ctrl+click to add a recipe.
+ ///
+ /// The element type of . This type must be derived from .
+ /// The array of items to search.
+ /// Upon return, contains a hint that is applicable to using ctrl+click to add a recipe.
+ /// This will either suggest using ctrl+click, or explain why ctrl+click cannot be used.
+ /// It is not useful when is not .
+ /// Items that are not accessible at the current milestones are always ignored. After those have been discarded, the return value is the first applicable entry in the following list:
+ ///
+ /// - The only normal item in .
+ /// - The only normal user favorite in .
+ /// - If no previous options are applicable, .
+ ///
+ public static T? SelectSingle(this T[] list, out string recipeHint) where T : FactorioObject {
var userFavorites = Project.current.preferences.favorites;
bool acceptOnlyFavorites = false;
- element = null;
+ T? element = null;
+ if (list.Any(t => t.IsAccessible())) {
+ recipeHint = "Hint: Complete milestones to enable ctrl+click";
+ }
+ else {
+ recipeHint = "Hint: Mark a recipe as accessible to enable ctrl+click";
+ }
foreach (var elem in list) {
if (!elem.IsAccessibleWithCurrentMilestones() || elem.specialType != FactorioObjectSpecialType.Normal) {
continue;
@@ -108,25 +129,28 @@ public static bool SelectSingle(this T[] list, [NotNullWhen(true)] out T? ele
if (userFavorites.Contains(elem)) {
if (!acceptOnlyFavorites || element == null) {
element = elem;
+ recipeHint = "Hint: ctrl+click to add your favorited recipe";
acceptOnlyFavorites = true;
}
else {
- element = null;
- return false;
+ recipeHint = "Hint: Cannot ctrl+click with multiple favorited recipes";
+ return null;
}
}
else if (!acceptOnlyFavorites) {
if (element == null) {
element = elem;
+ recipeHint = "Hint: ctrl+click to add the accessible recipe";
}
else {
element = null;
+ recipeHint = "Hint: Set a favorite recipe to add it with ctrl+click";
acceptOnlyFavorites = true;
}
}
}
- return element != null;
+ return element;
}
public static void SetupForProject(Project project) {
diff --git a/Yafc/Widgets/ImmediateWidgets.cs b/Yafc/Widgets/ImmediateWidgets.cs
index 802a8c6a..380acdd5 100644
--- a/Yafc/Widgets/ImmediateWidgets.cs
+++ b/Yafc/Widgets/ImmediateWidgets.cs
@@ -205,11 +205,11 @@ public static void BuildInlineObjectListAndButtonWithNone(this ImGui gui, ICo
/// Display this value, formatted appropriately for .
/// Use this unit of measure when formatting for display.
/// If , this icon will be displayed at , instead of at 100% scale.
- public static Click BuildFactorioObjectWithAmount(this ImGui gui, FactorioObject? goods, float amount, UnitOfMeasure unit, SchemeColor bgColor = SchemeColor.None, SchemeColor textColor = SchemeColor.None, bool useScale = true) {
+ public static Click BuildFactorioObjectWithAmount(this ImGui gui, FactorioObject? goods, float amount, UnitOfMeasure unit, SchemeColor bgColor = SchemeColor.None, SchemeColor textColor = SchemeColor.None, bool useScale = true, ObjectTooltipOptions tooltipOptions = default) {
using (gui.EnterFixedPositioning(3f, 3f, default)) {
gui.allocator = RectAllocator.Stretch;
gui.spacing = 0f;
- Click clicked = gui.BuildFactorioObjectButton(goods, 3f, MilestoneDisplay.Contained, bgColor, useScale);
+ Click clicked = gui.BuildFactorioObjectButton(goods, 3f, MilestoneDisplay.Contained, bgColor, useScale, tooltipOptions);
if (goods != null) {
gui.BuildText(DataUtils.FormatAmount(amount, unit), Font.text, false, RectAlignment.Middle, textColor);
if (InputSystem.Instance.control && gui.BuildButton(gui.lastRect, SchemeColor.None, SchemeColor.Grey) == ButtonEvent.MouseOver) {
@@ -266,11 +266,11 @@ public static void BuildObjectSelectDropDownWithNone(this ImGui gui, ICollect
/// The new value entered by the user, if this returns . Otherwise, the original .
/// If , the default, the user can adjust the value by using the scroll wheel while hovering over the editable text.
/// If , the scroll wheel will be ignored when hovering.
- public static GoodsWithAmountEvent BuildFactorioObjectWithEditableAmount(this ImGui gui, FactorioObject? obj, float amount, UnitOfMeasure unit, out float newAmount, SchemeColor color = SchemeColor.None, bool useScale = true, bool allowScroll = true) {
+ public static GoodsWithAmountEvent BuildFactorioObjectWithEditableAmount(this ImGui gui, FactorioObject? obj, float amount, UnitOfMeasure unit, out float newAmount, SchemeColor color = SchemeColor.None, bool useScale = true, bool allowScroll = true, ObjectTooltipOptions tooltipOptions = default) {
using var group = gui.EnterGroup(default, RectAllocator.Stretch, spacing: 0f);
group.SetWidth(3f);
newAmount = amount;
- GoodsWithAmountEvent evt = (GoodsWithAmountEvent)gui.BuildFactorioObjectButton(obj, 3f, MilestoneDisplay.Contained, color);
+ GoodsWithAmountEvent evt = (GoodsWithAmountEvent)gui.BuildFactorioObjectButton(obj, 3f, MilestoneDisplay.Contained, color, useScale, tooltipOptions);
if (gui.BuildTextInput(DataUtils.FormatAmount(amount, unit), out string newText, null, Icon.None, true, default, RectAlignment.Middle, SchemeColor.Secondary)) {
if (DataUtils.TryParseAmount(newText, out newAmount, unit)) {
diff --git a/Yafc/Widgets/ObjectTooltip.cs b/Yafc/Widgets/ObjectTooltip.cs
index f9651be5..8c00f740 100644
--- a/Yafc/Widgets/ObjectTooltip.cs
+++ b/Yafc/Widgets/ObjectTooltip.cs
@@ -4,6 +4,27 @@
using Yafc.UI;
namespace Yafc {
+ ///
+ /// The location(s) where should display hints
+ /// (currently only "ctrl+click to add recipe" hints)
+ ///
+ [Flags]
+ public enum HintLocations {
+ ///
+ /// Do not display any hints.
+ ///
+ None = 0,
+ ///
+ /// Display the ctrl+click recipe-selection hint associated with recipes that produce this .
+ ///
+ OnProducingRecipes = 1,
+ ///
+ /// Display the ctrl+click recipe-selection hint associated with recipes that consume this .
+ ///
+ OnConsumingRecipes = 2,
+ // NOTE: This is [Flags]. The next item, if applicable, should be 4.
+ }
+
public class ObjectTooltip : Tooltip {
public static readonly Padding contentPadding = new Padding(1f, 0.25f);
@@ -280,6 +301,10 @@ private void BuildGoods(Goods goods, ImGui gui) {
BuildSubHeader(gui, "Made with");
using (gui.EnterGroup(contentPadding)) {
BuildIconRow(gui, goods.production, 2);
+ if (tooltipOptions.HintLocations.HasFlag(HintLocations.OnProducingRecipes)) {
+ goods.production.SelectSingle(out string recipeTip);
+ gui.BuildText(recipeTip, color: SchemeColor.BackgroundTextFaint);
+ }
}
}
@@ -294,6 +319,10 @@ private void BuildGoods(Goods goods, ImGui gui) {
BuildSubHeader(gui, "Needed for");
using (gui.EnterGroup(contentPadding)) {
BuildIconRow(gui, goods.usages, 4);
+ if (tooltipOptions.HintLocations.HasFlag(HintLocations.OnConsumingRecipes)) {
+ goods.usages.SelectSingle(out string recipeTip);
+ gui.BuildText(recipeTip, color: SchemeColor.BackgroundTextFaint);
+ }
}
}
@@ -512,5 +541,12 @@ public struct ObjectTooltipOptions {
/// e.g. "Radar" is the item, "Radar (Recipe)" is the recipe, and "Radar (Entity)" is the building.
///
public bool ExtendHeader { get; set; }
+ ///
+ /// Gets or sets flags indicating where hints should be displayed in the tooltip.
+ ///
+ public HintLocations HintLocations { get; set; }
+
+ // Reduce boilerplate by permitting unambiguous and relatively obvious implicit conversions.
+ public static implicit operator ObjectTooltipOptions(HintLocations hintLocations) => new() { HintLocations = hintLocations };
}
}
diff --git a/Yafc/Workspace/ProductionTable/ProductionTableView.cs b/Yafc/Workspace/ProductionTable/ProductionTableView.cs
index f8a6862d..22b6b377 100644
--- a/Yafc/Workspace/ProductionTable/ProductionTableView.cs
+++ b/Yafc/Workspace/ProductionTable/ProductionTableView.cs
@@ -326,7 +326,7 @@ public override void BuildElement(ImGui gui, RecipeRow recipe) {
gui.AllocateSpacing(0.5f);
if (recipe.fuel != Database.voidEnergy || recipe.entity == null || recipe.entity.energy.type != EntityEnergyType.Void) {
- view.BuildGoodsIcon(gui, recipe.fuel, recipe.links.fuel, (float)(recipe.parameters.fuelUsagePerSecondPerRecipe * recipe.recipesPerSecond), ProductDropdownType.Fuel, recipe, recipe.linkRoot);
+ view.BuildGoodsIcon(gui, recipe.fuel, recipe.links.fuel, (float)(recipe.parameters.fuelUsagePerSecondPerRecipe * recipe.recipesPerSecond), ProductDropdownType.Fuel, recipe, recipe.linkRoot, HintLocations.OnProducingRecipes);
}
else {
if (recipe.recipe == Database.electricityGeneration && recipe.entity.factorioType == "solar-panel") {
@@ -475,7 +475,7 @@ public override void BuildElement(ImGui gui, RecipeRow recipe) {
var link = recipe.hierarchyEnabled ? recipe.links.ingredients[i] : null;
var goods = recipe.hierarchyEnabled ? recipe.links.ingredientGoods[i] : null;
grid.Next();
- view.BuildGoodsIcon(gui, goods, link, (float)(ingredient.amount * recipe.recipesPerSecond), ProductDropdownType.Ingredient, recipe, recipe.linkRoot, ingredient.variants);
+ view.BuildGoodsIcon(gui, goods, link, (float)(ingredient.amount * recipe.recipesPerSecond), ProductDropdownType.Ingredient, recipe, recipe.linkRoot, HintLocations.OnProducingRecipes, ingredient.variants);
}
}
grid.Dispose();
@@ -502,8 +502,7 @@ public override void BuildElement(ImGui gui, RecipeRow recipe) {
amount += (float)(recipe.parameters.fuelUsagePerSecondPerRecipe * recipe.recipesPerSecond);
handledSpentFuel = true;
}
- view.BuildGoodsIcon(gui, goods, link, amount, ProductDropdownType.Product,
- recipe, recipe.linkRoot);
+ view.BuildGoodsIcon(gui, goods, link, amount, ProductDropdownType.Product, recipe, recipe.linkRoot, HintLocations.OnConsumingRecipes);
}
if (!handledSpentFuel && spentFuel != null) {
_ = recipe.FindLink(spentFuel, out ProductionLink? link);
@@ -512,7 +511,7 @@ public override void BuildElement(ImGui gui, RecipeRow recipe) {
link = null;
}
grid.Next();
- view.BuildGoodsIcon(gui, spentFuel, link, (float)(recipe.parameters.fuelUsagePerSecondPerRecipe * recipe.recipesPerSecond), ProductDropdownType.Product, recipe, recipe.linkRoot);
+ view.BuildGoodsIcon(gui, spentFuel, link, (float)(recipe.parameters.fuelUsagePerSecondPerRecipe * recipe.recipesPerSecond), ProductDropdownType.Product, recipe, recipe.linkRoot, HintLocations.OnConsumingRecipes);
}
}
grid.Dispose();
@@ -739,7 +738,7 @@ async void addRecipe(RecipeOrTechnology rec) {
if (InputSystem.Instance.control) {
bool isInput = type == ProductDropdownType.Fuel || type == ProductDropdownType.Ingredient || (type == ProductDropdownType.DesiredProduct && amount > 0);
var recipeList = isInput ? goods.production : goods.usages;
- if (recipeList.SelectSingle(out var selected)) {
+ if (recipeList.SelectSingle(out _) is Recipe selected) {
addRecipe(selected);
return;
}
@@ -771,7 +770,7 @@ void dropDownContent(ImGui gui) {
using (var grid = gui.EnterInlineGrid(3f)) {
foreach (var variant in variants) {
grid.Next();
- if (gui.BuildFactorioObjectButton(variant, 3f, MilestoneDisplay.Contained, variant == goods ? SchemeColor.Primary : SchemeColor.None) == Click.Left &&
+ if (gui.BuildFactorioObjectButton(variant, 3f, MilestoneDisplay.Contained, variant == goods ? SchemeColor.Primary : SchemeColor.None, tooltipOptions: HintLocations.OnProducingRecipes) == Click.Left &&
variant != goods) {
recipe!.RecordUndo().ChangeVariant(goods, variant); // null-forgiving: If variants is not null, neither is recipe: Only the call from BuildGoodsIcon sets variants, and the only call to BuildGoodsIcon that sets variants also sets recipe.
_ = gui.CloseDropdown();
@@ -925,15 +924,17 @@ private void DrawDesiredProduct(ImGui gui, ProductionLink element) {
}
}
- var evt = gui.BuildFactorioObjectWithEditableAmount(element.goods, element.amount, element.goods.flowUnitOfMeasure, out float newAmount, iconColor);
- if (evt == GoodsWithAmountEvent.LeftButtonClick) {
- OpenProductDropdown(gui, gui.lastRect, element.goods, element.amount, element, ProductDropdownType.DesiredProduct, null, element.owner);
- }
- else if (evt == GoodsWithAmountEvent.RightButtonClick) {
- DestroyLink(element);
- }
- else if (evt == GoodsWithAmountEvent.TextEditing && newAmount != 0) {
- element.RecordUndo().amount = newAmount;
+ ObjectTooltipOptions tooltipOptions = element.amount < 0 ? HintLocations.OnConsumingRecipes : HintLocations.OnProducingRecipes;
+ switch (gui.BuildFactorioObjectWithEditableAmount(element.goods, element.amount, element.goods.flowUnitOfMeasure, out float newAmount, iconColor, tooltipOptions: tooltipOptions)) {
+ case GoodsWithAmountEvent.LeftButtonClick:
+ OpenProductDropdown(gui, gui.lastRect, element.goods, element.amount, element, ProductDropdownType.DesiredProduct, null, element.owner);
+ break;
+ case GoodsWithAmountEvent.RightButtonClick:
+ DestroyLink(element);
+ break;
+ case GoodsWithAmountEvent.TextEditing when newAmount != 0:
+ element.RecordUndo().amount = newAmount;
+ break;
}
}
@@ -942,7 +943,7 @@ public override void Rebuild(bool visualOnly = false) {
base.Rebuild(visualOnly);
}
- private void BuildGoodsIcon(ImGui gui, Goods? goods, ProductionLink? link, float amount, ProductDropdownType dropdownType, RecipeRow? recipe, ProductionTable context, Goods[]? variants = null) {
+ private void BuildGoodsIcon(ImGui gui, Goods? goods, ProductionLink? link, float amount, ProductDropdownType dropdownType, RecipeRow? recipe, ProductionTable context, ObjectTooltipOptions tooltipOptions, Goods[]? variants = null) {
SchemeColor iconColor;
if (link != null) {
// The icon is part of a production link
@@ -978,7 +979,7 @@ private void BuildGoodsIcon(ImGui gui, Goods? goods, ProductionLink? link, float
textColor = SchemeColor.BackgroundTextFaint;
}
- switch (gui.BuildFactorioObjectWithAmount(goods, amount, goods?.flowUnitOfMeasure ?? UnitOfMeasure.None, iconColor, textColor)) {
+ switch (gui.BuildFactorioObjectWithAmount(goods, amount, goods?.flowUnitOfMeasure ?? UnitOfMeasure.None, iconColor, textColor, tooltipOptions: tooltipOptions)) {
case Click.Left when goods is not null:
OpenProductDropdown(gui, gui.lastRect, goods, amount, link, dropdownType, recipe, context, variants);
break;
@@ -1010,7 +1011,7 @@ private void BuildTableProducts(ImGui gui, ProductionTable table, ProductionTabl
}
grid.Next();
- BuildGoodsIcon(gui, flow[i].goods, flow[i].link, amt, ProductDropdownType.Product, null, context);
+ BuildGoodsIcon(gui, flow[i].goods, flow[i].link, amt, ProductDropdownType.Product, null, context, HintLocations.OnConsumingRecipes);
}
}
@@ -1126,7 +1127,7 @@ private void BuildTableIngredients(ImGui gui, ProductionTable table, ProductionT
}
grid.Next();
- BuildGoodsIcon(gui, flow.goods, flow.link, -flow.amount, ProductDropdownType.Ingredient, null, context);
+ BuildGoodsIcon(gui, flow.goods, flow.link, -flow.amount, ProductDropdownType.Ingredient, null, context, HintLocations.OnProducingRecipes);
}
}
diff --git a/changelog.txt b/changelog.txt
index dbc95b98..f64a1fc8 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -13,6 +13,8 @@
----------------------------------------------------------------------------------------------------------------------
Version x.y.z
Date:
+ Features:
+ - Provide hints that contol+clicking can add recipes, or to explain how to change things so it can.
Bugfixes:
- Fix regression in fluid variant selection when adding recipes.
----------------------------------------------------------------------------------------------------------------------
From 5317f13d6faabd7c17219853e2f9d83b1d21e6b4 Mon Sep 17 00:00:00 2001
From: Dale McCoy <21223975+DaleStan@users.noreply.github.com>
Date: Sat, 18 May 2024 13:42:44 -0400
Subject: [PATCH 3/3] Before failing to add a recipe, look for special recipes
too.
This allows you to use ctrl+click to produce or consume filled barrels, stacked items, or similar.
---
Yafc.Model/Data/DataUtils.cs | 73 ++++++++++++++++++++----------------
1 file changed, 40 insertions(+), 33 deletions(-)
diff --git a/Yafc.Model/Data/DataUtils.cs b/Yafc.Model/Data/DataUtils.cs
index 8ff39883..3eef19c8 100644
--- a/Yafc.Model/Data/DataUtils.cs
+++ b/Yafc.Model/Data/DataUtils.cs
@@ -109,48 +109,55 @@ public static Bits GetMilestoneOrder(FactorioId id) {
///
/// - The only normal item in .
/// - The only normal user favorite in .
+ /// - The only item in , considering both normal and special items.
+ /// - The only user favorite in , considering both normal and special items.
/// - If no previous options are applicable, .
///
public static T? SelectSingle(this T[] list, out string recipeHint) where T : FactorioObject {
- var userFavorites = Project.current.preferences.favorites;
- bool acceptOnlyFavorites = false;
- T? element = null;
- if (list.Any(t => t.IsAccessible())) {
- recipeHint = "Hint: Complete milestones to enable ctrl+click";
- }
- else {
- recipeHint = "Hint: Mark a recipe as accessible to enable ctrl+click";
- }
- foreach (var elem in list) {
- if (!elem.IsAccessibleWithCurrentMilestones() || elem.specialType != FactorioObjectSpecialType.Normal) {
- continue;
+ return @internal(list, true, out recipeHint) ?? @internal(list, false, out recipeHint);
+
+ static T? @internal(T[] list, bool excludeSpecial, out string recipeHint) {
+ HashSet userFavorites = Project.current.preferences.favorites;
+ bool acceptOnlyFavorites = false;
+ T? element = null;
+ if (list.Any(t => t.IsAccessible())) {
+ recipeHint = "Hint: Complete milestones to enable ctrl+click";
}
-
- if (userFavorites.Contains(elem)) {
- if (!acceptOnlyFavorites || element == null) {
- element = elem;
- recipeHint = "Hint: ctrl+click to add your favorited recipe";
- acceptOnlyFavorites = true;
- }
- else {
- recipeHint = "Hint: Cannot ctrl+click with multiple favorited recipes";
- return null;
- }
+ else {
+ recipeHint = "Hint: Mark a recipe as accessible to enable ctrl+click";
}
- else if (!acceptOnlyFavorites) {
- if (element == null) {
- element = elem;
- recipeHint = "Hint: ctrl+click to add the accessible recipe";
+ foreach (T elem in list) {
+ // Always consider normal entries. A list with two normals and one special should select nothing, rather than selecting the only special item.
+ if (!elem.IsAccessibleWithCurrentMilestones() || (elem.specialType != FactorioObjectSpecialType.Normal && excludeSpecial)) {
+ continue;
}
- else {
- element = null;
- recipeHint = "Hint: Set a favorite recipe to add it with ctrl+click";
- acceptOnlyFavorites = true;
+
+ if (userFavorites.Contains(elem)) {
+ if (!acceptOnlyFavorites || element == null) {
+ element = elem;
+ recipeHint = "Hint: ctrl+click to add your favorited recipe";
+ acceptOnlyFavorites = true;
+ }
+ else {
+ recipeHint = "Hint: Cannot ctrl+click with multiple favorited recipes";
+ return null;
+ }
+ }
+ else if (!acceptOnlyFavorites) {
+ if (element == null) {
+ element = elem;
+ recipeHint = excludeSpecial ? "Hint: ctrl+click to add the accessible normal recipe" : "Hint: ctrl+click to add the accessible recipe";
+ }
+ else {
+ element = null;
+ recipeHint = "Hint: Set a favorite recipe to add it with ctrl+click";
+ acceptOnlyFavorites = true;
+ }
}
}
- }
- return element;
+ return element;
+ }
}
public static void SetupForProject(Project project) {