Skip to content

Commit 01526a3

Browse files
authored
Store column widths in the global preferences. (#211)
This remembers your column widths and reuses them next time you open YAFC.
2 parents a6f9d78 + 8eb39f3 commit 01526a3

File tree

5 files changed

+66
-14
lines changed

5 files changed

+66
-14
lines changed

Yafc/Utils/Preferences.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ public void Save() {
5757
/// The initial height of the main screen or the height the main screen will be after being restored, depending on whether it starts restored or maximized.
5858
/// </summary>
5959
public float initialMainScreenHeight { get; set; }
60+
public float recipeColumnWidth { get; set; }
61+
public float ingredientsColumWidth { get; set; }
62+
public float productsColumWidth { get; set; }
63+
public float modulesColumnWidth { get; set; }
6064

6165
public void AddProject(string path, string dataPath, string modsPath, bool expensiveRecipes, bool netProduction) {
6266
recentProjects = recentProjects.Where(x => string.Compare(path, x.path, StringComparison.InvariantCultureIgnoreCase) != 0)

Yafc/Widgets/DataGrid.cs

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,65 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Numerics;
4+
using System.Reflection;
45
using SDL2;
56

67
namespace Yafc.UI {
7-
public abstract class DataColumn<TData>(float width, float minWidth = 0f, float maxWidth = 0f) {
8-
public readonly float minWidth = minWidth == 0f ? width : minWidth;
9-
public readonly float maxWidth = maxWidth == 0f ? width : maxWidth;
10-
public readonly bool isFixedSize = minWidth == maxWidth;
11-
public float width = width;
8+
public abstract class DataColumn<TData> {
9+
public readonly float minWidth;
10+
public readonly float maxWidth;
11+
public bool isFixedSize => minWidth == maxWidth;
12+
private readonly Func<float> getWidth;
13+
private readonly Action<float> setWidth;
14+
private float _width;
15+
16+
/// <param name="widthStorage">If not <see langword="null"/>, names a public read-write instance property in <see cref="Preferences"/> that will be used to store the width of this column.
17+
/// If the current value of the property is out of range, the initial width will be <paramref name="initialWidth"/>.</param>
18+
public DataColumn(float initialWidth, float minWidth = 0f, float maxWidth = 0f, string? widthStorage = null) {
19+
this.minWidth = minWidth == 0f ? initialWidth : minWidth;
20+
this.maxWidth = maxWidth == 0f ? initialWidth : maxWidth;
21+
if (widthStorage != null) {
22+
(getWidth, setWidth) = getStorage(widthStorage);
23+
}
24+
else {
25+
getWidth = () => _width;
26+
setWidth = f => _width = f;
27+
}
28+
29+
if (width < this.minWidth || width > this.maxWidth) {
30+
width = initialWidth;
31+
}
32+
33+
static (Func<float>, Action<float>) getStorage(string storage) {
34+
try {
35+
PropertyInfo? property = typeof(Preferences).GetProperty(storage);
36+
Func<float>? getMethod = property?.GetGetMethod()?.CreateDelegate<Func<float>>(Preferences.Instance);
37+
Action<float>? setMethod = property?.GetSetMethod()?.CreateDelegate<Action<float>>(Preferences.Instance);
38+
if (getMethod == null || setMethod == null) {
39+
throw new ArgumentException($"'{storage}' is not a public read-write property in {nameof(Preferences)}.");
40+
}
41+
return (getMethod, setMethod);
42+
}
43+
catch (ArgumentException) {
44+
// Not including the CreateDelegate's exception, because YAFC displays only the innermost exception message.
45+
throw new ArgumentException($"'{storage}' is not a instance property of type {typeof(float).Name} in {nameof(Preferences)}.");
46+
}
47+
}
48+
}
49+
50+
public float width {
51+
get => getWidth();
52+
set => setWidth(value);
53+
}
1254

1355
public abstract void BuildHeader(ImGui gui);
1456
public abstract void BuildElement(ImGui gui, TData data);
1557
}
1658

17-
public abstract class TextDataColumn<TData>(string header, float width, float minWidth = 0, float maxWidth = 0, bool hasMenu = false) : DataColumn<TData>(width, minWidth, maxWidth) {
18-
public readonly string header = header;
19-
private readonly bool hasMenu = hasMenu;
20-
59+
/// <param name="widthStorage">If not <see langword="null"/>, names an instance property in <see cref="Preferences"/> that will be used to store the width of this column.
60+
/// If the current value of the property is out of range, the initial width will be <paramref name="initialWidth"/>.</param>
61+
public abstract class TextDataColumn<TData>(string header, float initialWidth, float minWidth = 0, float maxWidth = 0, bool hasMenu = false, string? widthStorage = null)
62+
: DataColumn<TData>(initialWidth, minWidth, maxWidth, widthStorage) {
2163
public override void BuildHeader(ImGui gui) {
2264
gui.BuildText(header);
2365
if (hasMenu) {

Yafc/Windows/MainScreen.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,7 @@ protected override void WindowResize() {
464464
}
465465

466466
public void ForceClose() {
467+
Preferences.Instance.Save();
467468
base.Close();
468469
}
469470

Yafc/Workspace/ProductionTable/ProductionTableView.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ public ProductionTableView() {
1717
flatHierarchyBuilder = new FlatHierarchy<RecipeRow, ProductionTable>(grid, BuildSummary, "This is a nested group. You can drag&drop recipes here. Nested groups can have their own linked materials.");
1818
}
1919

20-
private abstract class ProductionTableDataColumn(ProductionTableView view, string header, float width, float minWidth = 0, float maxWidth = 0, bool hasMenu = true) : TextDataColumn<RecipeRow>(header, width, minWidth, maxWidth, hasMenu) {
20+
/// <param name="widthStorage">If not <see langword="null"/>, names an instance property in <see cref="Preferences"/> that will be used to store the width of this column.
21+
/// If the current value of the property is out of range, the initial width will be <paramref name="initialWidth"/>.</param>
22+
private abstract class ProductionTableDataColumn(ProductionTableView view, string header, float initialWidth, float minWidth = 0, float maxWidth = 0, bool hasMenu = true, string? widthStorage = null)
23+
: TextDataColumn<RecipeRow>(header, initialWidth, minWidth, maxWidth, hasMenu, widthStorage) {
2124
protected readonly ProductionTableView view = view;
2225
}
2326

@@ -94,7 +97,7 @@ private void BuildRowMarker(ImGui gui, RecipeRow row) {
9497
}
9598
}
9699

97-
private class RecipeColumn(ProductionTableView view) : ProductionTableDataColumn(view, "Recipe", 13f, 13f, 30f) {
100+
private class RecipeColumn(ProductionTableView view) : ProductionTableDataColumn(view, "Recipe", 13f, 13f, 30f, widthStorage: nameof(Preferences.recipeColumnWidth)) {
98101
public override void BuildElement(ImGui gui, RecipeRow recipe) {
99102
gui.spacing = 0.5f;
100103
switch (gui.BuildFactorioObjectButton(recipe.recipe, 3f)) {
@@ -451,7 +454,7 @@ public override void BuildMenu(ImGui gui) {
451454
}
452455
}
453456

454-
private class IngredientsColumn(ProductionTableView view) : ProductionTableDataColumn(view, "Ingredients", 32f, 16f, 100f, hasMenu: false) {
457+
private class IngredientsColumn(ProductionTableView view) : ProductionTableDataColumn(view, "Ingredients", 32f, 16f, 100f, hasMenu: false, nameof(Preferences.ingredientsColumWidth)) {
455458
public override void BuildElement(ImGui gui, RecipeRow recipe) {
456459
var grid = gui.EnterInlineGrid(3f, 1f);
457460
if (recipe.isOverviewMode) {
@@ -470,7 +473,7 @@ public override void BuildElement(ImGui gui, RecipeRow recipe) {
470473
}
471474
}
472475

473-
private class ProductsColumn(ProductionTableView view) : ProductionTableDataColumn(view, "Products", 12f, 10f, 70f, hasMenu: false) {
476+
private class ProductsColumn(ProductionTableView view) : ProductionTableDataColumn(view, "Products", 12f, 10f, 70f, hasMenu: false, nameof(Preferences.productsColumWidth)) {
474477
public override void BuildElement(ImGui gui, RecipeRow recipe) {
475478
var grid = gui.EnterInlineGrid(3f, 1f);
476479
if (recipe.isOverviewMode) {
@@ -511,7 +514,8 @@ private class ModulesColumn : ProductionTableDataColumn {
511514
private readonly VirtualScrollList<ProjectModuleTemplate> moduleTemplateList;
512515
private RecipeRow editingRecipeModules = null!; // null-forgiving: This is set as soon as we open a module dropdown.
513516

514-
public ModulesColumn(ProductionTableView view) : base(view, "Modules", 10f, 7f, 16f) => moduleTemplateList = new VirtualScrollList<ProjectModuleTemplate>(15f, new Vector2(20f, 2.5f), ModuleTemplateDrawer, collapsible: true);
517+
public ModulesColumn(ProductionTableView view) : base(view, "Modules", 10f, 7f, 16f, widthStorage: nameof(Preferences.modulesColumnWidth))
518+
=> moduleTemplateList = new VirtualScrollList<ProjectModuleTemplate>(15f, new Vector2(20f, 2.5f), ModuleTemplateDrawer, collapsible: true);
515519

516520
private void ModuleTemplateDrawer(ImGui gui, ProjectModuleTemplate element, int index) {
517521
var evt = gui.BuildContextMenuButton(element.name, icon: element.icon?.icon ?? default, disabled: !element.template.IsCompatibleWith(editingRecipeModules));

changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Version 0.7.4
2020
Date: July 24th 2024
2121
Features:
2222
- Add the ability to switch through project pages with control-tab and control-shift-tab
23+
- When opening the main window, use the same column widths as when it was last closed.
2324
Bugfixes:
2425
- Fix a possible threading race while destroying textures, which could cause an illegal access crash.
2526
- Fix a loading error when mods use non-ASCII characters in their settings.

0 commit comments

Comments
 (0)