OverviewFilesCommitsIssuesDocsReleasesPeers

ifsrenderer-linux / Shared / AppSettings.cs

114 lines · csharp ref:
using Newtonsoft.Json;

namespace IFSRenderer.Shared;

/// <summary>
/// User settings, stored as JSON under the XDG config directory.
/// </summary>
/// <remarks>
/// The WPF app uses <c>Properties.Settings</c>, which is Windows user-config and has no
/// portable equivalent. The settings themselves are a flat key/value set, so a plain JSON
/// file in the platform's own config location is the natural replacement.
/// </remarks>
public sealed class AppSettings
{
    public List<string> RecentFiles { get; set; } = [];

    // Authoring - stamped into saved fractals and exported image metadata.
    public string AuthorName { get; set; } = "";
    public string AuthorLink { get; set; } = "";

    // Interaction
    public bool InvertAxisY { get; set; }
    public double Sensitivity { get; set; } = 1.0;

    // Renderer tuning
    public int TargetFramerate { get; set; } = 60;
    public int WorkgroupCount { get; set; } = 256;

    /// <summary>Path to ffmpeg. Empty means "use whatever is on PATH".</summary>
    public string FfmpegPath { get; set; } = "";

    public bool ShowWelcomeOnStartup { get; set; } = true;

    /// <summary>Use a plain white palette for blank params instead of random colours.</summary>
    public bool UseWhiteForBlankParams { get; set; }

    // --- export options (Settings > Export options) ---

    /// <summary>Write title, authors, copyright, date and keywords into exported images.</summary>
    public bool SaveMetadata { get; set; } = true;

    /// <summary>Embed the fractal params in the image, so a PNG can be reopened as a document.</summary>
    public bool IncludeParamsInMetadata { get; set; } = true;

    // --- animation export (Settings > Animations) ---

    /// <summary>Export frames as 32-bit exr rather than 8-bit png.</summary>
    public bool IsRawFrameExportEnabled { get; set; }

    /// <summary>Run ffmpeg over the frames once they have rendered.</summary>
    public bool IsExportVideoFileEnabled { get; set; }

    /// <summary>Extra ffmpeg arguments; input, output, fps and audio are supplied by the app.</summary>
    public string FfmpegArgs { get; set; } = "";

    // --- performance ---

    /// <summary>
    /// Drop the display framerate to give the iteration loop more GPU. On by default, as in
    /// the canonical settings window.
    /// </summary>
    public bool PerceptuallyUniformUpdates { get; set; } = true;

    [JsonIgnore]
    public static string Directory =>
        Path.Combine(
            Environment.GetEnvironmentVariable("XDG_CONFIG_HOME")
                ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".config"),
            "ifsrenderer");

    [JsonIgnore]
    public static string FilePath => Path.Combine(Directory, "settings.json");

    private const int MaxRecents = 10;

    public static AppSettings Load()
    {
        try
        {
            if (File.Exists(FilePath))
                return JsonConvert.DeserializeObject<AppSettings>(File.ReadAllText(FilePath)) ?? new AppSettings();
        }
        catch
        {
            // A corrupt settings file must never stop the app starting.
        }
        return new AppSettings();
    }

    public void Save()
    {
        try
        {
            System.IO.Directory.CreateDirectory(Directory);
            File.WriteAllText(FilePath, JsonConvert.SerializeObject(this, Formatting.Indented));
        }
        catch
        {
            // Losing settings is not worth interrupting the user for.
        }
    }

    public void AddRecent(string path)
    {
        if (string.IsNullOrWhiteSpace(path))
            return;
        RecentFiles.RemoveAll(p => string.Equals(p, path, StringComparison.Ordinal));
        RecentFiles.Insert(0, path);
        if (RecentFiles.Count > MaxRecents)
            RecentFiles.RemoveRange(MaxRecents, RecentFiles.Count - MaxRecents);
        Save();
    }
}