mirror of
https://github.com/skoelle/marcer-gamedvd-launcher.git
synced 2026-09-17 18:50:25 +00:00
- Extract InputController from AppHost (key handling + ReloadGameEntries) - Centralize hardcoded values as code constants (non-configurable): FavoritesRootName, DefaultFileName, DefaultTitle, scroll fractions, AvailableLines helper, removed redundant ArgsTemplate fallback - Make color scheme configurable via 'Colors' section in launcher.config.json with Enum.TryParse + default fallback; MenuRenderer uses constructor injection - Clean up MenuRenderer: remove dead maxRow logic, unify cache sizing via EnsureCache - Document intentional error swallowing in FavoritesService.Save/UIErrorService - Update README + AGENTS.md with configurable color docs - Remove redundant ArgsTemplate fallback (config is single source of truth)
107 lines
3.7 KiB
C#
107 lines
3.7 KiB
C#
// Copyright (c) 2026 Stefan Koelle (https://stefankoelle.de)
|
|
// Licensed under the MIT License. See LICENSE file in project root for details.
|
|
|
|
namespace MarcerGameDvdLauncher
|
|
{
|
|
// Manages loading, saving and querying favorite ZIP paths.
|
|
public class FavoritesService
|
|
{
|
|
// Virtual folder name displayed at the root when favorites exist.
|
|
// Not configurable — fixed UI element.
|
|
public const string FavoritesRootName = "Favorites";
|
|
|
|
// Filename used for persisting favorites to disk.
|
|
// Not configurable — fixed persistence file.
|
|
public const string DefaultFileName = "favorites.txt";
|
|
|
|
private readonly string _filePath;
|
|
// Use a SortedSet so favorites are kept in sorted order in memory.
|
|
private SortedSet<string> _favorites = new(StringComparer.OrdinalIgnoreCase);
|
|
|
|
public FavoritesService(string filePath)
|
|
{
|
|
_filePath = filePath ?? throw new ArgumentNullException(nameof(filePath));
|
|
}
|
|
|
|
// Load favorites from disk (no-op if file missing)
|
|
public void Load()
|
|
{
|
|
_favorites = new SortedSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
if (!File.Exists(_filePath)) return;
|
|
var lines = File.ReadAllLines(_filePath, System.Text.Encoding.UTF8);
|
|
foreach (var l in lines)
|
|
{
|
|
var t = l?.Trim();
|
|
if (string.IsNullOrEmpty(t)) continue;
|
|
try
|
|
{
|
|
var full = Path.GetFullPath(t);
|
|
_favorites.Add(full);
|
|
}
|
|
catch
|
|
{
|
|
// ignore invalid paths in the file
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool IsFavorite(string path)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(path)) return false;
|
|
try
|
|
{
|
|
var full = Path.GetFullPath(path);
|
|
return _favorites.Contains(full);
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Returns true if any favorites are present
|
|
public bool HasFavorites() => _favorites.Count > 0;
|
|
|
|
// Returns all favorites as absolute paths in sorted order
|
|
public IReadOnlyList<string> GetAll() => _favorites.ToList();
|
|
|
|
// Toggle favorite state. Returns true if added, false if removed.
|
|
public bool Toggle(string path)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentNullException(nameof(path));
|
|
var full = Path.GetFullPath(path);
|
|
bool added;
|
|
if (_favorites.Contains(full))
|
|
{
|
|
_favorites.Remove(full);
|
|
added = false;
|
|
}
|
|
else
|
|
{
|
|
_favorites.Add(full);
|
|
added = true;
|
|
}
|
|
Save();
|
|
return added;
|
|
}
|
|
|
|
private void Save()
|
|
{
|
|
try
|
|
{
|
|
var dir = Path.GetDirectoryName(_filePath) ?? AppContext.BaseDirectory;
|
|
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
|
|
File.WriteAllLines(_filePath, _favorites.OrderBy(x => x), System.Text.Encoding.UTF8);
|
|
}
|
|
catch
|
|
{
|
|
// Swallowed intentionally ("bewusst still"): a persistence failure
|
|
// (e.g. disk full, read-only directory) must not crash or interrupt
|
|
// the UI. The in-memory state is still updated so the user sees
|
|
// immediate feedback; only the on-disk write is lost. On next
|
|
// application start the favorites reflect the last successful save.
|
|
}
|
|
}
|
|
}
|
|
}
|