Skip to content

Commit

Permalink
Add building count tooltips 101 (shpaass#213)
Browse files Browse the repository at this point in the history
This closes shpaass#101, and should make it easy for similar tooltips to be
added in other locations.
  • Loading branch information
shpaass authored Jul 27, 2024
2 parents 0ae9d9c + 21c92f7 commit 3c357b0
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 65 deletions.
1 change: 1 addition & 0 deletions Yafc.UI/Core/Rect.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,5 +126,6 @@ public override int GetHashCode() {
public readonly Rect Expand(float amount) => new Rect(X - amount, Y - amount, Width + (2 * amount), Height + (2 * amount));

public static Rect Square(Vector2 center, float side) => new Rect(center.X - (side * 0.5f), center.Y - (side * 0.5f), side, side);
public static Rect Square(float centerX, float centerY, float side) => new Rect(centerX - (side * 0.5f), centerY - (side * 0.5f), side, side);
}
}
46 changes: 46 additions & 0 deletions Yafc.UI/ImGui/ImGuiUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -382,5 +382,51 @@ public static bool CloseDropdown(this ImGui gui) {
gui.PropagateMessage<CloseDropdownEvent>(default);
return true;
}

/// <summary>
/// Draws a row with a (?) help icon at its right side, and a tooltip when the user hovers over the icon.
/// </summary>
/// <param name="tooltip">The tooltip that should be displayed when the user hovers over the (?) icon.</param>
/// <param name="rightJustify">If <see langword="true"/>, the default, the help icon will be as far right as possible.
/// If false, it will be still be drawn at the right end of the row, but as far left as possible.</param>
public static IDisposable EnterRowWithHelpIcon(this ImGui gui, string tooltip, bool rightJustify = true) => new RowWithHelpIcon(gui, tooltip, rightJustify);

/// <summary>
/// The class that sets up and stores the state needed to build a row with a help icon.
/// </summary>
private sealed class RowWithHelpIcon : IDisposable {
private readonly ImGui gui;
private readonly string tooltip;
private readonly ImGui.Context row;
private readonly float helpCenterX;
private readonly ImGui.Context group;

public RowWithHelpIcon(ImGui gui, string tooltip, bool rightJustify) {
this.gui = gui;
this.tooltip = tooltip;
row = gui.EnterRow(); // using (gui.EnterRow()) {
if (rightJustify) {
gui.allocator = RectAllocator.RightRow;
helpCenterX = gui.AllocateRect(1, 1).Center.X;
group = gui.EnterGroup(new Padding(), RectAllocator.RemainingRow); // using (gui.EnterGroup(...)) { // Required to produce the expected spacing/padding behavior.
gui.allocator = RectAllocator.LeftRow;
}
}

public void Dispose() {
Rect rect;
if (helpCenterX != 0) { // if (rightJustify)
group.Dispose(); // end using block for EnterGroup
rect = Rect.Square(helpCenterX, gui.lastRect.Center.Y, 1.25f);
}
else {
rect = gui.AllocateRect(1.25f, 1.25f); // Despite requesting 1.25 x 1.25, rect will be 1.25 x RowHeight, which might be greater than 1.25.
rect = Rect.Square(rect.Center, 1.25f); // Get a vertically-centered rect that's actually 1.25 x 1.25.
}
gui.DrawIcon(rect, Icon.Help, SchemeColor.BackgroundText);
gui.BuildButton(rect, SchemeColor.None, SchemeColor.Grey).WithTooltip(gui, tooltip, rect);
row.Dispose(); // end using block for EnterRow
}
}
}
}
2 changes: 1 addition & 1 deletion Yafc/Widgets/ImmediateWidgets.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ public static bool BuildInlineObjectList<T>(this ImGui gui, IEnumerable<T> list,
}

if (checkMark != null && gui.isBuilding && checkMark(elem)) {
gui.DrawIcon(Rect.Square(new Vector2(gui.lastRect.Right - 1f, gui.lastRect.Center.Y), 1.5f), Icon.Check, SchemeColor.Green);
gui.DrawIcon(Rect.Square(gui.lastRect.Right - 1f, gui.lastRect.Center.Y, 1.5f), Icon.Check, SchemeColor.Green);
}
}

Expand Down
41 changes: 14 additions & 27 deletions Yafc/Windows/PreferencesScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,21 @@ public override void Build(ImGui gui) {
gui.BuildText("Fluid production/consumption:", Font.subheader);
BuildUnitPerTime(gui, true, prefs);

drawInputRowWithTooltip(gui, "Pollution cost modifier", "0 for off, 100% for old default",
gui => {
if (gui.BuildFloatInput(settings.PollutionCostModifier, out float pollutionCostModifier, UnitOfMeasure.Percent, new Padding(0.5f))) {
settings.RecordUndo().PollutionCostModifier = pollutionCostModifier;
gui.Rebuild();
}
});
using (gui.EnterRowWithHelpIcon("0 for off, 100% for old default")) {
gui.BuildText("Pollution cost modifier", topOffset: 0.5f);
if (gui.BuildFloatInput(settings.PollutionCostModifier, out float pollutionCostModifier, UnitOfMeasure.Percent, new Padding(0.5f))) {
settings.RecordUndo().PollutionCostModifier = pollutionCostModifier;
gui.Rebuild();
}
}

drawInputRowWithTooltip(gui, "Display scale for linkable icons", "Some mod icons have little or no transparency, hiding the background color. This setting reduces the size of icons that could hide link information.",
gui => {
if (gui.BuildFloatInput(prefs.iconScale, out float iconScale, UnitOfMeasure.Percent, new Padding(0.5f)) && iconScale > 0 && iconScale <= 1) {
prefs.RecordUndo().iconScale = iconScale;
gui.Rebuild();
}
});
using (gui.EnterRowWithHelpIcon("Some mod icons have little or no transparency, hiding the background color. This setting reduces the size of icons that could hide link information.")) {
gui.BuildText("Display scale for linkable icons", topOffset: 0.5f);
if (gui.BuildFloatInput(prefs.iconScale, out float iconScale, UnitOfMeasure.Percent, new Padding(0.5f)) && iconScale > 0 && iconScale <= 1) {
prefs.RecordUndo().iconScale = iconScale;
gui.Rebuild();
}
}

ChooseObject(gui, "Default belt:", Database.allBelts, prefs.defaultBelt, s => {
prefs.RecordUndo().defaultBelt = s;
Expand Down Expand Up @@ -100,19 +100,6 @@ public override void Build(ImGui gui) {
if (settings.justChanged) {
Project.current.RecalculateDisplayPages();
}

static void drawInputRowWithTooltip(ImGui gui, string text, string tooltip, Action<ImGui> handleInput) {
using (gui.EnterRow()) {
gui.BuildText(text, topOffset: 0.5f);
gui.AllocateSpacing();
gui.allocator = RectAllocator.RightRow;
var rect = gui.AllocateRect(1, 1);
handleInput(gui);
rect = new Rect(rect.Center.X, gui.lastRect.Center.Y, 0, 0).Expand(.625f);
gui.DrawIcon(rect, Icon.Help, SchemeColor.BackgroundText);
gui.BuildButton(rect, SchemeColor.None, SchemeColor.Grey).WithTooltip(gui, tooltip, rect);
}
}
}

protected override void ReturnPressed() => Close();
Expand Down
5 changes: 4 additions & 1 deletion Yafc/Windows/WelcomeScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,10 @@ protected override void BuildContents(ImGui gui) {
gui.BuildText("In-game objects language:");
}

using (gui.EnterRow()) {
using (gui.EnterRowWithHelpIcon("""
If checked, YAFC will only suggest production or consumption recipes that have a net production or consumption of that item or fluid.
For example, kovarex enrichment will not be suggested when adding recipes that produce U-238 or consume U-235.
""", false)) {
gui.BuildCheckBox("Use net production/consumption when analyzing recipes", netProduction, out netProduction);
}

Expand Down
81 changes: 45 additions & 36 deletions Yafc/Workspace/ProductionTable/ProductionTableView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -367,57 +367,66 @@ private static void ShowEntityDropdown(ImGui imgui, RecipeRow recipe) => imgui.S
}
}, "Select crafting entity", extra: x => DataUtils.FormatAmount(x.craftingSpeed, UnitOfMeasure.Percent));
if (recipe.fixedBuildings > 0f) {
ButtonEvent evt = gui.BuildButton("Clear fixed building count");
if (willResetFixed) {
evt.WithTooltip(gui, "Shortcut: right-click");
}
if (evt && gui.CloseDropdown()) {
recipe.RecordUndo().fixedBuildings = 0f;
gui.AllocateSpacing(0.5f);
using (gui.EnterRowWithHelpIcon("Tell YAFC how many buildings it must use when solving this page.\nUse this to ask questions like 'What does it take to handle the output of ten miners?'")) {
gui.allocator = RectAllocator.RemainingRow;
if (recipe.fixedBuildings > 0f) {
ButtonEvent evt = gui.BuildButton("Clear fixed building count");
if (willResetFixed) {
evt.WithTooltip(gui, "Shortcut: right-click");
}
if (evt && gui.CloseDropdown()) {
recipe.RecordUndo().fixedBuildings = 0f;
}
}
}
else {
if (gui.BuildButton("Set fixed building count") && gui.CloseDropdown()) {
else if (gui.BuildButton("Set fixed building count") && gui.CloseDropdown()) {
recipe.RecordUndo().fixedBuildings = recipe.buildingCount <= 0f ? 1f : recipe.buildingCount;
}
}
if (recipe.builtBuildings != null) {
ButtonEvent evt = gui.BuildButton("Clear built building count");
if (willResetBuilt) {
evt.WithTooltip(gui, "Shortcut: right-click");
}
if (evt && gui.CloseDropdown()) {
recipe.RecordUndo().builtBuildings = null;
using (gui.EnterRowWithHelpIcon("Tell YAFC how many of these buildings you have in your factory.\nYAFC will warn you if you need to build more buildings.")) {
gui.allocator = RectAllocator.RemainingRow;
if (recipe.builtBuildings != null) {
ButtonEvent evt = gui.BuildButton("Clear built building count");
if (willResetBuilt) {
evt.WithTooltip(gui, "Shortcut: right-click");
}
if (evt && gui.CloseDropdown()) {
recipe.RecordUndo().builtBuildings = null;
}
}
}
else {
if (gui.BuildButton("Set built building count") && gui.CloseDropdown()) {
else if (gui.BuildButton("Set built building count") && gui.CloseDropdown()) {
recipe.RecordUndo().builtBuildings = Math.Max(0, Convert.ToInt32(Math.Ceiling(recipe.buildingCount)));
}
}
if (recipe.entity != null && gui.BuildButton("Create single building blueprint") && gui.CloseDropdown()) {
BlueprintEntity entity = new BlueprintEntity { index = 1, name = recipe.entity.name };
if (recipe.recipe is not Mechanics) {
entity.recipe = recipe.recipe.name;
}
if (recipe.entity != null) {
using (gui.EnterRowWithHelpIcon("Generate a blueprint for one of these buildings, with the recipe and internal modules set.")) {
gui.allocator = RectAllocator.RemainingRow;
if (gui.BuildButton("Create single building blueprint") && gui.CloseDropdown()) {
BlueprintEntity entity = new BlueprintEntity { index = 1, name = recipe.entity.name };
if (recipe.recipe is not Mechanics) {
entity.recipe = recipe.recipe.name;
}
var modules = recipe.parameters.modules.modules;
if (modules != null) {
entity.items = [];
foreach (var (module, count, beacon) in modules) {
if (!beacon) {
entity.items[module.name] = count;
var modules = recipe.parameters.modules.modules;
if (modules != null) {
entity.items = [];
foreach (var (module, count, beacon) in modules) {
if (!beacon) {
entity.items[module.name] = count;
}
}
}
BlueprintString bp = new BlueprintString(recipe.recipe.locName) { blueprint = { entities = { entity } } };
_ = SDL.SDL_SetClipboardText(bp.ToBpString());
}
}
BlueprintString bp = new BlueprintString(recipe.recipe.locName) { blueprint = { entities = { entity } } };
_ = SDL.SDL_SetClipboardText(bp.ToBpString());
}
if (recipe.recipe.crafters.Length > 1) {
BuildFavorites(gui, recipe.entity, "Add building to favorites");
if (recipe.recipe.crafters.Length > 1) {
BuildFavorites(gui, recipe.entity, "Add building to favorites");
}
}
});

Expand Down
1 change: 1 addition & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Date:
Features:
- Autofocus the project name field when you create a new project
- When opening the main window, use the same column widths as when it was last closed.
- Add explanatory tips for the buttons in the building dropdown.
Bugfixes:
- Sometimes, deleting and/or right-click resetting modules would not work.
----------------------------------------------------------------------------------------------------------------------
Expand Down

0 comments on commit 3c357b0

Please sign in to comment.