Skip to content

Commit

Permalink
Focus input field when clicking set building count or set production …
Browse files Browse the repository at this point in the history
…count (#330)

Closes #297; I checked that the focusing the sheet name still happens
when adding a new production sheet and doesn't happen when editing an
existing sheet.

I also found a subtle UI wart and worked around it in 9da8948; it
becomes more visible now that the fixed and built counts are focused
more often.
  • Loading branch information
shpaass authored Oct 26, 2024
2 parents b3c0ebc + 9666a5f commit 3deb0c2
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 20 deletions.
29 changes: 29 additions & 0 deletions Yafc.Model/Model/ProductionTableContent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,35 @@ public IEnumerable<RecipeRowProduct> Products {
}
}

private SetKeyboardFocus _focusBuiltCount, _focusFixedCount;

/// <summary>
/// Returns <see cref="SetKeyboardFocus.Always"/> exactly once after each call to <see cref="FocusBuiltCountOnNextDraw"/>, to focus the newly created edit box on that draw cycle.
/// </summary>
public SetKeyboardFocus ShouldFocusBuiltCountThisTime() {
SetKeyboardFocus result = _focusBuiltCount;
_focusBuiltCount = SetKeyboardFocus.No;
return result;
}
/// <summary>
/// Call when preparing to add a built building count edit box, so the new box will be focused as part of the next draw loop.
/// </summary>
public void FocusBuiltCountOnNextDraw() => _focusBuiltCount = SetKeyboardFocus.Always;

/// <summary>
/// Returns <see cref="SetKeyboardFocus.Always"/> exactly once after each call to <see cref="FocusFixedCountOnNextDraw"/>, to focus the newly created edit box on that draw cycle.
/// </summary>
public SetKeyboardFocus ShouldFocusFixedCountThisTime() {
SetKeyboardFocus result = _focusFixedCount;
_focusFixedCount = SetKeyboardFocus.No;
return result;
}
/// <summary>
/// Call when preparing to add or move a fixed count edit box (building, fuel, ingredient, or product), so the new box will be focused as part of the next draw loop.
/// </summary>
public void FocusFixedCountOnNextDraw() => _focusFixedCount = SetKeyboardFocus.Always;


// Computed variables
internal RecipeParameters parameters { get; set; } = RecipeParameters.Empty;
public double recipesPerSecond { get; internal set; }
Expand Down
31 changes: 25 additions & 6 deletions Yafc.UI/ImGui/ImGuiBuilding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,23 @@

namespace Yafc.UI;

public enum SetKeyboardFocus {
/// <summary>
/// Do not explicitly focus this edit control.
/// </summary>
No,
/// <summary>
/// Explicitly focus this edit control the first time the panel/pseudoscreen is drawn, but not after that.<br/>
/// If you are adding a new control to an existing panel (e.g. the production table), you will need to use <see cref="Always"/> instead.
/// </summary>
OnFirstPanelDraw,
/// <summary>
/// Always explicitly focus this edit control.<br/>
/// The caller is responsible for properly implementing "only the first time the control is drawn".
/// </summary>
Always,
};

public partial class ImGui {
private readonly struct DrawCommand<T>(Rect rect, T data, SchemeColor color) {
public readonly Rect rect = rect;
Expand Down Expand Up @@ -145,23 +162,25 @@ public void DrawText(Rect rect, string text, RectAlignment alignment = RectAlign
}

private ImGuiTextInputHelper? textInputHelper;
public bool BuildTextInput(string? text, out string newText, string? placeholder, Icon icon = Icon.None, bool delayed = false, bool setInitialFocus = false) {
public bool BuildTextInput(string? text, out string newText, string? placeholder, Icon icon = Icon.None, bool delayed = false, SetKeyboardFocus setKeyboardFocus = SetKeyboardFocus.No) {
TextBoxDisplayStyle displayStyle = TextBoxDisplayStyle.DefaultTextInput;

if (icon != Icon.None) {
displayStyle = displayStyle with { Icon = icon };
}

return BuildTextInput(text, out newText, placeholder, displayStyle, delayed, setInitialFocus);
return BuildTextInput(text, out newText, placeholder, displayStyle, delayed, setKeyboardFocus);
}

public bool BuildTextInput(string? text, out string newText, string? placeholder, TextBoxDisplayStyle displayStyle, bool delayed, bool setInitialFocus = false) {
setInitialFocus &= textInputHelper == null;
public bool BuildTextInput(string? text, out string newText, string? placeholder, TextBoxDisplayStyle displayStyle, bool delayed, SetKeyboardFocus setKeyboardFocus = SetKeyboardFocus.No) {
if (setKeyboardFocus != SetKeyboardFocus.Always && textInputHelper != null) {
setKeyboardFocus = SetKeyboardFocus.No;
}
textInputHelper ??= new ImGuiTextInputHelper(this);
bool result = textInputHelper.BuildTextInput(text, out newText, placeholder, GetFontSize(), delayed, displayStyle);

if (setInitialFocus) {
SetTextInputFocus(lastRect, "");
if (setKeyboardFocus != SetKeyboardFocus.No) {
SetTextInputFocus(lastRect, newText);
}

return result;
Expand Down
8 changes: 4 additions & 4 deletions Yafc.UI/ImGui/ImGuiUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -295,8 +295,8 @@ public static bool BuildErrorRow(this ImGui gui, string text) {
return closed;
}

public static bool BuildIntegerInput(this ImGui gui, int value, out int newValue, bool setInitialFocus = false) {
if (gui.BuildTextInput(value.ToString(), out string newText, null, delayed: true, setInitialFocus: setInitialFocus) && int.TryParse(newText, out newValue)) {
public static bool BuildIntegerInput(this ImGui gui, int value, out int newValue, SetKeyboardFocus setKeyboardFocus = SetKeyboardFocus.No) {
if (gui.BuildTextInput(value.ToString(), out string newText, null, delayed: true, setKeyboardFocus: setKeyboardFocus) && int.TryParse(newText, out newValue)) {
return true;
}

Expand Down Expand Up @@ -417,10 +417,10 @@ public static bool BuildSlider(this ImGui gui, float value, out float newValue,
return true;
}

public static bool BuildSearchBox(this ImGui gui, SearchQuery searchQuery, out SearchQuery newQuery, string placeholder = "Search", bool setInitialFocus = false) {
public static bool BuildSearchBox(this ImGui gui, SearchQuery searchQuery, out SearchQuery newQuery, string placeholder = "Search", SetKeyboardFocus setKeyboardFocus = SetKeyboardFocus.No) {
newQuery = searchQuery;

if (gui.BuildTextInput(searchQuery.query, out string newText, placeholder, Icon.Search, setInitialFocus: setInitialFocus)) {
if (gui.BuildTextInput(searchQuery.query, out string newText, placeholder, Icon.Search, setKeyboardFocus: setKeyboardFocus)) {
newQuery = new SearchQuery(newText);
return true;
}
Expand Down
8 changes: 4 additions & 4 deletions Yafc/Widgets/ImmediateWidgets.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ public static void BuildFactorioObjectIcon(this ImGui gui, FactorioObject? obj,
}
}

public static bool BuildFloatInput(this ImGui gui, DisplayAmount amount, TextBoxDisplayStyle displayStyle, bool setInitialFocus = false) {
if (gui.BuildTextInput(DataUtils.FormatAmount(amount.Value, amount.Unit), out string newText, null, displayStyle, true, setInitialFocus)
public static bool BuildFloatInput(this ImGui gui, DisplayAmount amount, TextBoxDisplayStyle displayStyle, SetKeyboardFocus setKeyboardFocus = SetKeyboardFocus.No) {
if (gui.BuildTextInput(DataUtils.FormatAmount(amount.Value, amount.Unit), out string newText, null, displayStyle, true, setKeyboardFocus)
&& DataUtils.TryParseAmount(newText, out float newValue, amount.Unit)) {
amount.Value = newValue;
return true;
Expand Down Expand Up @@ -316,13 +316,13 @@ public static void BuildObjectSelectDropDownWithNone<T>(this ImGui gui, ICollect
/// <param name="allowScroll">If <see langword="true"/>, the default, the user can adjust the value by using the scroll wheel while hovering over the editable text.
/// If <see langword="false"/>, the scroll wheel will be ignored when hovering.</param>
public static GoodsWithAmountEvent BuildFactorioObjectWithEditableAmount(this ImGui gui, FactorioObject? obj, DisplayAmount amount, ButtonDisplayStyle buttonDisplayStyle,
bool allowScroll = true, ObjectTooltipOptions tooltipOptions = default) {
bool allowScroll = true, ObjectTooltipOptions tooltipOptions = default, SetKeyboardFocus setKeyboardFocus = SetKeyboardFocus.No) {

using var group = gui.EnterGroup(default, RectAllocator.Stretch, spacing: 0f);
group.SetWidth(3f);
GoodsWithAmountEvent evt = (GoodsWithAmountEvent)gui.BuildFactorioObjectButton(obj, buttonDisplayStyle, tooltipOptions);

if (gui.BuildFloatInput(amount, TextBoxDisplayStyle.FactorioObjectInput)) {
if (gui.BuildFloatInput(amount, TextBoxDisplayStyle.FactorioObjectInput, setKeyboardFocus)) {
return GoodsWithAmountEvent.TextEditing;
}

Expand Down
2 changes: 1 addition & 1 deletion Yafc/Windows/ProjectPageSettingsPanel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ private ProjectPageSettingsPanel(ProjectPage? editingPage, Action<string, Factor
}

private void Build(ImGui gui, Action<FactorioObject?> setIcon) {
_ = gui.BuildTextInput(name, out name, "Input name", setInitialFocus: editingPage == null);
_ = gui.BuildTextInput(name, out name, "Input name", setKeyboardFocus: editingPage == null ? SetKeyboardFocus.OnFirstPanelDraw : SetKeyboardFocus.No);
if (gui.BuildFactorioObjectButton(icon, new ButtonDisplayStyle(4f, MilestoneDisplay.None, SchemeColor.Grey) with { UseScaleSetting = false }) == Click.Left) {
SelectSingleObjectPanel.Select(Database.objects.all, "Select icon", setIcon);
}
Expand Down
2 changes: 1 addition & 1 deletion Yafc/Windows/SelectObjectPanel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ private void ElementDrawer(ImGui gui, FactorioObject? element, int index) {

public override void Build(ImGui gui) {
BuildHeader(gui, header);
if (gui.BuildSearchBox(list.filter, out var newFilter, "Start typing for search", setInitialFocus: true)) {
if (gui.BuildSearchBox(list.filter, out var newFilter, "Start typing for search", setKeyboardFocus: SetKeyboardFocus.OnFirstPanelDraw)) {
list.filter = newFilter;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public override void Build(ImGui gui) {
RefreshList();
}

_ = gui.RemainingRow().BuildTextInput(newPageName, out newPageName, "Create new template", setInitialFocus: true);
_ = gui.RemainingRow().BuildTextInput(newPageName, out newPageName, "Create new template", setKeyboardFocus: SetKeyboardFocus.OnFirstPanelDraw);
}
}
}
21 changes: 18 additions & 3 deletions Yafc/Workspace/ProductionTable/ProductionTableView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,8 @@ public override void BuildElement(ImGui gui, RecipeRow recipe) {
group.SetWidth(3f);
if (recipe.fixedBuildings > 0 && !recipe.fixedFuel && recipe.fixedIngredient == null && recipe.fixedProduct == null) {
DisplayAmount amount = recipe.fixedBuildings;
GoodsWithAmountEvent evt = gui.BuildFactorioObjectWithEditableAmount(recipe.entity, amount, ButtonDisplayStyle.ProductionTableUnscaled);
GoodsWithAmountEvent evt = gui.BuildFactorioObjectWithEditableAmount(recipe.entity, amount, ButtonDisplayStyle.ProductionTableUnscaled,
setKeyboardFocus: recipe.ShouldFocusFixedCountThisTime());

if (evt == GoodsWithAmountEvent.TextEditing && amount.Value >= 0) {
recipe.RecordUndo().fixedBuildings = amount.Value;
Expand All @@ -313,7 +314,9 @@ public override void BuildElement(ImGui gui, RecipeRow recipe) {
if (recipe.builtBuildings != null) {
DisplayAmount amount = recipe.builtBuildings.Value;

if (gui.BuildFloatInput(amount, TextBoxDisplayStyle.FactorioObjectInput with { ColorGroup = SchemeColorGroup.Grey }) && amount.Value >= 0) {
if (gui.BuildFloatInput(amount, TextBoxDisplayStyle.FactorioObjectInput with { ColorGroup = SchemeColorGroup.Grey }, recipe.ShouldFocusBuiltCountThisTime())
&& amount.Value >= 0) {

recipe.RecordUndo().builtBuildings = (int)amount.Value;
}
}
Expand All @@ -339,9 +342,16 @@ public override void BuildElement(ImGui gui, RecipeRow recipe) {
}
else if (recipe.fixedBuildings > 0) {
recipe.RecordUndo().fixedBuildings = 0;
// Clear the keyboard focus: If we hide and then recreate the edit box without removing the focus, the UI system will restore the old value.
// (If the focus was on a text box we aren't hiding, other code also removes the focus.)
// To observe (prior to this fix), add a fixed or built count with a non-default value, clear it with right-click, and then click the "Set ... building count" button again.
// The old behavior is that the non-default value is restored.
InputSystem.Instance.currentKeyboardFocus?.FocusChanged(false);
}
else if (recipe.builtBuildings != null) {
recipe.RecordUndo().builtBuildings = null;
// Clear the keyboard focus: as above
InputSystem.Instance.currentKeyboardFocus?.FocusChanged(false);
}
}

Expand Down Expand Up @@ -421,6 +431,7 @@ private static void ShowEntityDropdown(ImGui imgui, RecipeRow recipe) => imgui.S
recipe.fixedFuel = false;
recipe.fixedIngredient = null;
recipe.fixedProduct = null;
recipe.FocusFixedCountOnNextDraw();
}
}
Expand All @@ -440,6 +451,7 @@ private static void ShowEntityDropdown(ImGui imgui, RecipeRow recipe) => imgui.S
}
else if (gui.BuildButton("Set built building count") && gui.CloseDropdown()) {
recipe.RecordUndo().builtBuildings = Math.Max(0, Convert.ToInt32(Math.Ceiling(recipe.buildingCount)));
recipe.FocusBuiltCountOnNextDraw();
}
}
Expand Down Expand Up @@ -1009,6 +1021,7 @@ void dropDownContent(ImGui gui) {
default:
break;
}
recipe.FocusFixedCountOnNextDraw();
targetGui.Rebuild();
}
}
Expand Down Expand Up @@ -1144,7 +1157,9 @@ private void BuildGoodsIcon(ImGui gui, Goods? goods, ProductionLink? link, float
&& ((dropdownType == ProductDropdownType.Fuel && recipe.fixedFuel)
|| (dropdownType == ProductDropdownType.Ingredient && recipe.fixedIngredient == goods)
|| (dropdownType == ProductDropdownType.Product && recipe.fixedProduct == goods))) {
evt = gui.BuildFactorioObjectWithEditableAmount(goods, displayAmount, ButtonDisplayStyle.ProductionTableScaled(iconColor), tooltipOptions: tooltipOptions);

evt = gui.BuildFactorioObjectWithEditableAmount(goods, displayAmount, ButtonDisplayStyle.ProductionTableScaled(iconColor), tooltipOptions: tooltipOptions,
setKeyboardFocus: recipe.ShouldFocusFixedCountThisTime());
}
else {
evt = (GoodsWithAmountEvent)gui.BuildFactorioObjectWithAmount(goods, displayAmount, ButtonDisplayStyle.ProductionTableScaled(iconColor),
Expand Down
2 changes: 2 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
----------------------------------------------------------------------------------------------------------------------
Version: 2.x
Date:
Features:
- Focus the built count and fixed count edit boxes when first opening them.
Bugfixes:
- Recipes no longer have excessive question marks in their names
----------------------------------------------------------------------------------------------------------------------
Expand Down

0 comments on commit 3deb0c2

Please sign in to comment.