Skip to content
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
10 changes: 8 additions & 2 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,19 @@ A failed mutation or failed write does not replace committed settings and does n
| Per-application overlay scope | `ApplicationAssignment.OverlayScopeId` |
| Optional native-menu profile reference and inheritance sentinel | `ApplicationAssignment.MenuVisualProfileId` and `ApplicationMenuProfilePolicy` |
| Scope enum values, canonical identifiers, aliases, default, and display names | `OverlayScopePolicy` definition table |
| Built-in profile IDs, names, transforms, ordering, tuning capability and canonicalization | `VisualProfileCatalog` definition table |
| Built-in profile IDs, names, transforms, ordering, tuning capability, canonical tuning and normalization policy | `VisualProfileCatalog` definition table |
| Assignment defaults, fallbacks, user-ID and user-name rules | `VisualProfilePolicy` |
| Canonical tuning values and numeric normalization | `VisualProfileDefaults` |
| Built-in tuning constants and shared numeric helpers | `VisualProfileDefaults` |
| Parameter ranges | `VisualProfileLimits` |
| Runtime polling, refresh, transition and fault-recovery intervals | `RuntimeTimingPolicy.Default` |
| Product name, version, milestone, repository, author, and license | project and assembly metadata exposed through `ProductInfo` |

## Visual-profile tuning authority

Each registered `VisualProfileDefinition` is the authority for its transform's editable status, canonical/reset tuning and normalization function. `VisualProfileManagementService.UpdateTuning` resolves the target profile's transform definition before normalizing submitted values, and `VisualProfileEditorForm` resolves the same definition for reset wording and canonical reset values. Adding another editable transform therefore does not inherit Soft Invert normalization or defaults implicitly.

`VisualProfileDefaults` retains the built-in numeric constants and reusable normalization helpers used by the current definitions; it is not a second transform registry.

`ApplicationIdentityCache` is an optimization, not a product source of truth. Entries are keyed by both PID and process creation time so a reused PID cannot inherit another process lifetime's identity.

## Foreground and overlay lifecycle
Expand Down
87 changes: 63 additions & 24 deletions src/SightAdapt/VisualProfileCatalog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public VisualProfileDefinition(
string displayName,
bool supportsTuning,
IVisualTransform transform,
VisualProfileTuning canonicalTuning,
Func<VisualProfile, VisualProfileTuning> normalizeTuning)
{
ArgumentException.ThrowIfNullOrWhiteSpace(profileId);
Expand All @@ -24,6 +25,7 @@ public VisualProfileDefinition(
DisplayName = displayName;
SupportsTuning = supportsTuning;
Transform = transform;
CanonicalTuning = canonicalTuning;
_normalizeTuning = normalizeTuning;
}

Expand All @@ -37,6 +39,8 @@ public VisualProfileDefinition(

public IVisualTransform Transform { get; }

public VisualProfileTuning CanonicalTuning { get; }

public VisualProfile CreateBuiltInProfile()
{
var profile = new VisualProfile
Expand All @@ -45,9 +49,7 @@ public VisualProfile CreateBuiltInProfile()
Name = DisplayName,
TransformId = TransformId,
};
VisualProfileDefaults.ApplyTuning(
profile,
_normalizeTuning(profile));
ResetTuning(profile);
return profile;
}

Expand All @@ -72,10 +74,14 @@ public bool CanonicalizeBuiltInProfile(
profile.Id = ProfileId;
profile.Name = DisplayName;
profile.TransformId = TransformId;
return VisualProfileDefaults.ApplyTuningIfChanged(
profile,
_normalizeTuning(profile)) ||
changed;
return NormalizeTuning(profile) || changed;
}

public VisualProfileTuning GetNormalizedTuning(
VisualProfile profile)
{
ArgumentNullException.ThrowIfNull(profile);
return _normalizeTuning(profile);
}

public bool NormalizeTuning(
Expand All @@ -84,7 +90,16 @@ public bool NormalizeTuning(
ArgumentNullException.ThrowIfNull(profile);
return VisualProfileDefaults.ApplyTuningIfChanged(
profile,
_normalizeTuning(profile));
GetNormalizedTuning(profile));
}

public void ResetTuning(
VisualProfile profile)
{
ArgumentNullException.ThrowIfNull(profile);
VisualProfileDefaults.ApplyTuning(
profile,
CanonicalTuning);
}
}

Expand All @@ -105,27 +120,29 @@ private static readonly VisualProfileDefinition[]
"Exact invert",
supportsTuning: false,
new InvertVisualTransform(),
VisualProfileDefaults.ExactInvertTuning,
_ => VisualProfileDefaults.ExactInvertTuning),
new(
DefaultSoftInvertId,
SoftInvertVisualTransform.TransformId,
"Soft invert",
supportsTuning: true,
new SoftInvertVisualTransform(),
VisualProfileDefaults.SoftInvertTuning,
VisualProfileDefaults.NormalizeSoftInvertTuning),
new(
DefaultNoneId,
NoneVisualTransform.TransformId,
"None",
supportsTuning: false,
new NoneVisualTransform(),
VisualProfileDefaults.ExactInvertTuning,
_ => VisualProfileDefaults.ExactInvertTuning),
];

private static readonly IReadOnlyList<VisualProfileDefinition>
ReadOnlyDefinitions = Array.AsReadOnly(
CanonicalDefinitions);

private readonly VisualProfileDefinition[] _definitions;
private readonly IReadOnlyList<VisualProfileDefinition>
_readOnlyDefinitions;
private readonly IReadOnlyDictionary<
string,
VisualProfileDefinition> _definitionsByProfileId;
Expand All @@ -134,25 +151,43 @@ private readonly IReadOnlyDictionary<
VisualProfileDefinition> _definitionsByTransformId;

private VisualProfileCatalog()
: this(CanonicalDefinitions)
{
}

internal VisualProfileCatalog(
IEnumerable<VisualProfileDefinition> definitions)
{
ArgumentNullException.ThrowIfNull(definitions);

_definitions = definitions.ToArray();
if (_definitions.Length == 0)
{
throw new ArgumentException(
"At least one visual profile definition is required.",
nameof(definitions));
}

_definitionsByProfileId =
CanonicalDefinitions.ToDictionary(
_definitions.ToDictionary(
definition => definition.ProfileId,
StringComparer.OrdinalIgnoreCase);
_definitionsByTransformId =
CanonicalDefinitions.ToDictionary(
_definitions.ToDictionary(
definition => definition.TransformId,
StringComparer.OrdinalIgnoreCase);
_readOnlyDefinitions =
Array.AsReadOnly(_definitions);
}

public static VisualProfileCatalog Default { get; } = new();

public IReadOnlyList<VisualProfileDefinition> Definitions =>
ReadOnlyDefinitions;
_readOnlyDefinitions;

public IEnumerable<VisualProfile> CreateBuiltInProfiles()
{
return CanonicalDefinitions.Select(
return _definitions.Select(
definition => definition.CreateBuiltInProfile());
}

Expand Down Expand Up @@ -227,12 +262,20 @@ public string GetTransformDisplayName(

public IVisualTransform GetRequiredTransform(
string transformId)
{
return GetRequiredTransformDefinition(
transformId)
.Transform;
}

public VisualProfileDefinition GetRequiredTransformDefinition(
string transformId)
{
if (TryGetTransformDefinition(
transformId,
out var definition))
{
return definition.Transform;
return definition;
}

throw new InvalidOperationException(
Expand All @@ -243,13 +286,9 @@ public bool NormalizeTuningForTransform(
VisualProfile profile)
{
ArgumentNullException.ThrowIfNull(profile);

return TryGetTransformDefinition(
profile.TransformId,
out var definition)
? definition.NormalizeTuning(profile)
: throw new InvalidOperationException(
$"The visual transform '{profile.TransformId}' is not registered.");
return GetRequiredTransformDefinition(
profile.TransformId)
.NormalizeTuning(profile);
}

private bool TryGetTransformDefinition(
Expand Down
35 changes: 24 additions & 11 deletions src/SightAdapt/VisualProfileEditorForm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ namespace SightAdapt;
internal sealed class VisualProfileEditorForm : Form
{
private readonly VisualProfile _workingProfile;
private readonly VisualProfileDefinition _definition;
private readonly ColorProfilePreview _preview;
private readonly OutputLimitPreview _outputPreview;
private readonly IReadOnlyDictionary<string, VisualAdjustmentBinding>
Expand All @@ -16,9 +17,21 @@ private readonly IReadOnlyDictionary<string, VisualAdjustmentBinding>
private bool _loadingValues;

internal VisualProfileEditorForm(VisualProfile profile)
: this(profile, VisualProfileCatalog.Default)
{
}

internal VisualProfileEditorForm(
VisualProfile profile,
VisualProfileCatalog catalog)
{
ArgumentNullException.ThrowIfNull(profile);
if (!profile.SupportsTuning)
ArgumentNullException.ThrowIfNull(catalog);

_definition =
catalog.GetRequiredTransformDefinition(
profile.TransformId);
if (!_definition.SupportsTuning)
{
throw new ArgumentException(
"Only editable visual profiles can be edited.",
Expand All @@ -31,14 +44,14 @@ internal VisualProfileEditorForm(VisualProfile profile)
Dock = DockStyle.Fill,
Margin = Padding.Empty,
Profile = _workingProfile,
TransformCatalog = VisualProfileCatalog.Default,
TransformCatalog = catalog,
};
_outputPreview = new OutputLimitPreview
{
Dock = DockStyle.Fill,
Margin = Padding.Empty,
Profile = _workingProfile,
TransformCatalog = VisualProfileCatalog.Default,
TransformCatalog = catalog,
};
_adjustments = VisualAdjustmentDefinitions.All
.Select(definition => new VisualAdjustmentBinding(
Expand Down Expand Up @@ -299,13 +312,12 @@ private static Control CreateSliderPanel(
private Control CreateActionBar()
{
var reset = CreateButton(
"Reset soft profile",
"Reset profile",
ModernButtonStyle.Secondary,
160);
reset.AccessibleDescription =
$"Restore the canonical {VisualProfileCatalog.Default.GetTransformDisplayName(
SoftInvertVisualTransform.TransformId)} tuning values.";
reset.Click += (_, _) => ResetValues();
$"Restore the canonical {_definition.DisplayName} tuning values.";
reset.Click += (_, _) => ResetToCanonicalTuning();

var cancel = CreateButton("Cancel", ModernButtonStyle.Ghost, 100);
cancel.DialogResult = DialogResult.Cancel;
Expand Down Expand Up @@ -411,11 +423,12 @@ private void LoadValues()
}
}

private void ResetValues()
internal VisualProfile WorkingProfile =>
_workingProfile;

internal void ResetToCanonicalTuning()
{
VisualProfileDefaults.ApplyTuning(
_workingProfile,
VisualProfileDefaults.SoftInvertTuning);
_definition.ResetTuning(_workingProfile);
LoadValues();
InvalidatePreviews();
}
Expand Down
12 changes: 8 additions & 4 deletions src/SightAdapt/VisualProfileManagementService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,24 +69,28 @@ public static void Rename(
public static void UpdateTuning(
SightAdaptSettings settings,
VisualProfile profile,
VisualProfile values)
VisualProfile values,
VisualProfileCatalog? catalog = null)
{
ArgumentNullException.ThrowIfNull(settings);
ArgumentNullException.ThrowIfNull(profile);
ArgumentNullException.ThrowIfNull(values);
settings.EnsureCollections();
EnsureMember(settings, profile);

if (!profile.SupportsTuning)
catalog ??= VisualProfileCatalog.Default;
var definition =
catalog.GetRequiredTransformDefinition(
profile.TransformId);
if (!definition.SupportsTuning)
{
throw new SettingsValidationException(
"Only editable visual profiles can be tuned.");
}

VisualProfileDefaults.ApplyTuning(
profile,
VisualProfileDefaults
.NormalizeSoftInvertTuning(values));
definition.GetNormalizedTuning(values));
}

public static int Delete(
Expand Down
Loading
Loading