Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Store column widths in the global preferences. #211

Merged
merged 2 commits into from
Jul 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Yafc/Utils/Preferences.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ public void Save() {
/// 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.
/// </summary>
public float initialMainScreenHeight { get; set; }
public float recipeColumnWidth { get; set; }
public float ingredientsColumWidth { get; set; }
public float productsColumWidth { get; set; }
public float modulesColumnWidth { get; set; }

public void AddProject(string path, string dataPath, string modsPath, bool expensiveRecipes, bool netProduction) {
recentProjects = recentProjects.Where(x => string.Compare(path, x.path, StringComparison.InvariantCultureIgnoreCase) != 0)
Expand Down
60 changes: 51 additions & 9 deletions Yafc/Widgets/DataGrid.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,65 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Reflection;
using SDL2;

namespace Yafc.UI {
public abstract class DataColumn<TData>(float width, float minWidth = 0f, float maxWidth = 0f) {
public readonly float minWidth = minWidth == 0f ? width : minWidth;
public readonly float maxWidth = maxWidth == 0f ? width : maxWidth;
public readonly bool isFixedSize = minWidth == maxWidth;
public float width = width;
public abstract class DataColumn<TData> {
public readonly float minWidth;
public readonly float maxWidth;
public bool isFixedSize => minWidth == maxWidth;
private readonly Func<float> getWidth;
private readonly Action<float> setWidth;
private float _width;

/// <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.
/// If the current value of the property is out of range, the initial width will be <paramref name="initialWidth"/>.</param>
public DataColumn(float initialWidth, float minWidth = 0f, float maxWidth = 0f, string? widthStorage = null) {
this.minWidth = minWidth == 0f ? initialWidth : minWidth;
this.maxWidth = maxWidth == 0f ? initialWidth : maxWidth;
if (widthStorage != null) {
(getWidth, setWidth) = getStorage(widthStorage);
}
else {
getWidth = () => _width;
setWidth = f => _width = f;
}

if (width < this.minWidth || width > this.maxWidth) {
width = initialWidth;
}

static (Func<float>, Action<float>) getStorage(string storage) {
try {
PropertyInfo? property = typeof(Preferences).GetProperty(storage);
Func<float>? getMethod = property?.GetGetMethod()?.CreateDelegate<Func<float>>(Preferences.Instance);
Action<float>? setMethod = property?.GetSetMethod()?.CreateDelegate<Action<float>>(Preferences.Instance);
if (getMethod == null || setMethod == null) {
throw new ArgumentException($"'{storage}' is not a public read-write property in {nameof(Preferences)}.");
}
return (getMethod, setMethod);
}
catch (ArgumentException) {
// Not including the CreateDelegate's exception, because YAFC displays only the innermost exception message.
throw new ArgumentException($"'{storage}' is not a instance property of type {typeof(float).Name} in {nameof(Preferences)}.");
}
}
}

public float width {
get => getWidth();
set => setWidth(value);
}

public abstract void BuildHeader(ImGui gui);
public abstract void BuildElement(ImGui gui, TData data);
}

public abstract class TextDataColumn<TData>(string header, float width, float minWidth = 0, float maxWidth = 0, bool hasMenu = false) : DataColumn<TData>(width, minWidth, maxWidth) {
public readonly string header = header;
private readonly bool hasMenu = hasMenu;

/// <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.
/// If the current value of the property is out of range, the initial width will be <paramref name="initialWidth"/>.</param>
public abstract class TextDataColumn<TData>(string header, float initialWidth, float minWidth = 0, float maxWidth = 0, bool hasMenu = false, string? widthStorage = null)
: DataColumn<TData>(initialWidth, minWidth, maxWidth, widthStorage) {
public override void BuildHeader(ImGui gui) {
gui.BuildText(header);
if (hasMenu) {
Expand Down
1 change: 1 addition & 0 deletions Yafc/Windows/MainScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,7 @@ protected override void WindowResize() {
}

public void ForceClose() {
Preferences.Instance.Save();
base.Close();
}

Expand Down
14 changes: 9 additions & 5 deletions Yafc/Workspace/ProductionTable/ProductionTableView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ public ProductionTableView() {
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.");
}

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) {
/// <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.
/// If the current value of the property is out of range, the initial width will be <paramref name="initialWidth"/>.</param>
private abstract class ProductionTableDataColumn(ProductionTableView view, string header, float initialWidth, float minWidth = 0, float maxWidth = 0, bool hasMenu = true, string? widthStorage = null)
: TextDataColumn<RecipeRow>(header, initialWidth, minWidth, maxWidth, hasMenu, widthStorage) {
protected readonly ProductionTableView view = view;
}

Expand Down Expand Up @@ -94,7 +97,7 @@ private void BuildRowMarker(ImGui gui, RecipeRow row) {
}
}

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

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

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

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

private void ModuleTemplateDrawer(ImGui gui, ProjectModuleTemplate element, int index) {
var evt = gui.BuildContextMenuButton(element.name, icon: element.icon?.icon ?? default, disabled: !element.template.IsCompatibleWith(editingRecipeModules));
Expand Down
1 change: 1 addition & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Version 0.7.4
Date: July 24th 2024
Features:
- Add the ability to switch through project pages with control-tab and control-shift-tab
- When opening the main window, use the same column widths as when it was last closed.
Bugfixes:
- Fix a possible threading race while destroying textures, which could cause an illegal access crash.
- Fix a loading error when mods use non-ASCII characters in their settings.
Expand Down