mirror of
https://github.com/Maassoft/ColorControl.git
synced 2026-10-22 00:22:18 -05:00
fix issues with new UI, add color profiles, add sdr brightness for AMD
This commit is contained in:
1 parent
4d2ca8a552
commit
f42a46e53f
95 files changed
+12129
-10005
No files matched your search
@@ -1,8 +1,15 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Services
|
||||
@using ColorControl.Shared.Common
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.UI.Services
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
|
||||
|
||||
@inject AppState AppState
|
||||
@inject RpcUiClientService ClientService;
|
||||
@inject NavigationManager _navigationManager;
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject NavigationManager _navigationManager
|
||||
@inject NotificationService _notificationService
|
||||
@inject ProtectedSessionStorage _sessionStorage
|
||||
|
||||
@implements IDisposable
|
||||
@attribute [StreamRendering]
|
||||
|
||||
@@ -15,28 +22,33 @@
|
||||
<base href="/" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||
<link rel="stylesheet" href="app.css" />
|
||||
<link rel="stylesheet" href="ColorControl.UI.styles.css" />
|
||||
<link rel="icon" type="image/png" href="favicon.png" />
|
||||
|
||||
<HeadOutlet />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<Routes />
|
||||
<script src="_framework/blazor.web.js"></script>
|
||||
<NotificationsComponent></NotificationsComponent>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@code {
|
||||
private Config? Config;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var config = await ClientService.CallAsync<Config>("OptionsService", "GetConfig");
|
||||
Config ??= await _rpcClientService.CallAsync<Config>("OptionsService", "GetConfig");
|
||||
|
||||
var newTheme = config.UseDarkMode ? "dark" : "light";
|
||||
var newTheme = Config?.UseDarkMode == true ? "dark" : "light";
|
||||
|
||||
if (newTheme != AppState.SelectedTheme)
|
||||
{
|
||||
AppState.SelectedTheme = config.UseDarkMode ? "dark" : "light";
|
||||
AppState.SelectedTheme = Config?.UseDarkMode == true ? "dark" : "light";
|
||||
_navigationManager.Refresh();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.UI.Services
|
||||
|
||||
@rendermode InteractiveServer
|
||||
|
||||
@@ -28,28 +29,34 @@
|
||||
@foreach (var module in modules.Where(m => m.IsActive))
|
||||
{
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link px-3" href="@module.Link">
|
||||
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> @module.DisplayName
|
||||
<NavLink class="nav-link px-3 align-items-baseline" href="@module.Link">
|
||||
<span class="bi bi-@module.GetIconClass()" aria-hidden="true"></span> @module.DisplayName
|
||||
</NavLink>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link px-3" href="options">
|
||||
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Options
|
||||
<NavLink class="nav-link px-3 align-items-baseline" href="options">
|
||||
<span class="bi bi-gear-fill" aria-hidden="true"></span> Options
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link px-3" href="logging">
|
||||
<span class="bi bi-list-logging-nav-menu" aria-hidden="true"></span> Logging
|
||||
<NavLink class="nav-link px-3 align-items-baseline" href="ColorProfile">
|
||||
<span class="bi bi-palette-fill" aria-hidden="true"></span> Color Profiles
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link px-3" href="info">
|
||||
<span class="bi bi-list-logging-nav-menu" aria-hidden="true"></span> Info
|
||||
<NavLink class="nav-link px-3 align-items-baseline" href="logging">
|
||||
<span class="bi bi-list-columns-reverse" aria-hidden="true"></span> Logging
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link px-3 align-items-baseline" href="info">
|
||||
<span class="bi bi-info-circle-fill" aria-hidden="true"></span> Info
|
||||
</NavLink>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Services
|
||||
@using ColorControl.UI.Services
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
@inject AppState AppState
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.AMD
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using ColorControl.Shared.Common;
|
||||
@using ColorControl.UI.Services
|
||||
|
||||
@rendermode InteractiveServer
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
|
||||
<div class="modal" id="presetModal" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalOnFocus">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@(Preset?.IsDisplayPreset == true ? $"Settings for display {Preset?.displayName}" : Preset?.id == 0 ? $"Settings for new preset" : $"Settings of preset {Preset?.IdOrName}")</h5>
|
||||
@@ -316,7 +316,7 @@
|
||||
selectedVirtualResolution = Preset?.DisplayConfig.Resolution?.GetVirtualString();
|
||||
selectedRefreshRate = Preset?.DisplayConfig.RefreshRate?.ToString();
|
||||
|
||||
if (Preset?.IsDisplayPreset != true)
|
||||
if (Preset?.IsDisplayPreset == false)
|
||||
{
|
||||
displayPresets = await _rpcClientService.CallAsync<List<AmdPreset>>("AmdService", "GetDisplayPresets");
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.AMD
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
|
||||
<div class="modal" id="settingsModal" tabindex="-1" @onfocus="OnFocus">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">AMD settings</h5>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
@using ColorControl.Shared.Common
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.AMD
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using ColorControl.UI.Generics
|
||||
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
|
||||
@@ -92,6 +92,10 @@ else
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
@if (!Presets.Any())
|
||||
{
|
||||
<span>No presets found</span>
|
||||
}
|
||||
<div class="list-group">
|
||||
@foreach (var preset in Presets)
|
||||
{
|
||||
@@ -106,11 +110,21 @@ else
|
||||
}
|
||||
</h5>
|
||||
</div>
|
||||
<span>
|
||||
<span class="d-none d-lg-block">
|
||||
<button class="btn btn-danger btn-sm" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => PresetOnDeleteClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Delete</button>
|
||||
<button class="btn btn-secondary btn-sm" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnCloneClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Clone</button>
|
||||
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnEditClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Edit</button>
|
||||
</span>
|
||||
<div class="d-lg-none" @onclick:preventDefault @onclick:stopPropagation>
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-three-dots-vertical"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => PresetOnDeleteClick(preset)">Delete</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnCloneClick(preset)">Clone</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnEditClick(preset)">Edit</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<p class="mb-1">@preset.DisplayConfig.Resolution.ToString() @@ @preset.DisplayConfig.RefreshRate.ToString()Hz @(preset.applyColorData ? preset.colorDepth : "")</p>
|
||||
<small class="text-muted">@preset.shortcut</small>
|
||||
@@ -124,7 +138,6 @@ else
|
||||
@code {
|
||||
private List<AmdPreset>? Presets;
|
||||
private List<AmdPreset>? DisplayPresets;
|
||||
private List<VirtualResolution>? Resolutions;
|
||||
|
||||
private AmdPreset? CurrentPreset;
|
||||
|
||||
@@ -134,7 +147,6 @@ else
|
||||
{
|
||||
await RefreshPresets();
|
||||
DisplayPresets = await _rpcClientService.CallAsync<List<AmdPreset>>("AmdService", "GetDisplayPresets");
|
||||
Resolutions = await _rpcClientService.CallAsync<List<VirtualResolution>>("AmdService", "GetAvailableResolutionsV2", DisplayPresets?.FirstOrDefault());
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstTime)
|
||||
|
||||
@@ -0,0 +1,452 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.AMD
|
||||
@using ColorControl.Shared.Contracts.DisplayInfo
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using ColorControl.Shared.Common;
|
||||
@using ColorControl.UI.Services
|
||||
@using MHC2Gen
|
||||
@using Microsoft.JSInterop;
|
||||
|
||||
@rendermode InteractiveServer
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject JSHelper jsHelper;
|
||||
|
||||
<div class="modal modal-lg" id="colorProfileModal" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalOnFocus">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@(ColorProfile?.Name == null ? $"Settings for new color profile" : $"Settings of color profile {ColorProfile.Name}")</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @onclick="() => IsVisible = false"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
@if (ColorProfile?.HasExtraInfo == false)
|
||||
{
|
||||
<div class="alert alert-warning" role="alert">
|
||||
This is not a profile created by ColorControl. Some fields are set to default.
|
||||
</div>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(_rpcClientService.LastErrorMessage))
|
||||
{
|
||||
<div class="alert alert-danger" role="alert">
|
||||
Error: @_rpcClientService.LastErrorMessage
|
||||
</div>
|
||||
}
|
||||
<form class="row g-1 needs-validation @WasValidated" id="profileForm" novalidate>
|
||||
@if (ColorProfile == null || DisplayInfo == null || CurrentColorInfo == null)
|
||||
{
|
||||
<div>Loading...</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="display">Display</label>
|
||||
<input class="form-control" type="text" id="display" value="@DisplayInfo" disabled />
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="name">Name</label>
|
||||
<input class="form-control" type="text" id="name" @bind="ColorProfile.Name" @bind:after="AfterNameChanged" required />
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="description">Description</label>
|
||||
<input class="form-control" type="text" id="description" @bind="ColorProfile.Description" />
|
||||
</div>
|
||||
<div class="accordion accordion-flush" id="presetAccordion">
|
||||
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<span class="d-flex align-items-baseline">
|
||||
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#displaySettings">
|
||||
Color gamut
|
||||
</button>
|
||||
</span>
|
||||
</h2>
|
||||
<div id="displaySettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
|
||||
<div class="accordion-body">
|
||||
<div class="mb-2">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label class="form-label" for="primariesSource"><small>Show Display Primaries and Luminance values of</small></label>
|
||||
<select class="form-select" id="primariesSource" @bind="DisplayPrimariesSource" @bind:after="DisplayPrimariesSourceChanged">
|
||||
@foreach (var colorInfo in DisplayColors)
|
||||
{
|
||||
<option value="@colorInfo.DisplayPrimariesSource">@(colorInfo.CustomName ?? colorInfo.DisplayPrimariesSource.ToString())</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col align-content-end">
|
||||
<button class="btn btn-primary" type="button" disabled="@(DisplayPrimariesSource == DisplayPrimariesSource.ColorProfile)" @onclick="OverwritePrimariesOnClick">Overwrite color profile values</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label text-danger" for="redX">Red X-coordinate</label>
|
||||
<input class="form-control" id="redX" type="number" min="0" max="1" step="0.000000000000001" @bind="CurrentColorInfo.RgbPrimaries.Red.X" disabled="@PrimariesDisabled" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label text-danger" for="redY">Red Y-coordinate</label>
|
||||
<input class="form-control" id="redY" type="number" min="0" max="1" step="0.000000000000001" @bind="CurrentColorInfo.RgbPrimaries.Red.Y" disabled="@PrimariesDisabled" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label text-success" for="greenX">Green X-coordinate</label>
|
||||
<input class="form-control" id="greenX" type="number" min="0" max="1" step="0.000000000000001" @bind="CurrentColorInfo.RgbPrimaries.Green.X" disabled="@PrimariesDisabled" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label text-success" for="greenY">Green Y-coordinate</label>
|
||||
<input class="form-control" id="greenY" type="number" min="0" max="1" step="0.000000000000001" @bind="CurrentColorInfo.RgbPrimaries.Green.Y" disabled="@PrimariesDisabled" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label text-primary" for="blueX">Blue X-coordinate</label>
|
||||
<input class="form-control" id="blueX" type="number" min="0" max="1" step="0.000000000000001" @bind="CurrentColorInfo.RgbPrimaries.Blue.X" disabled="@PrimariesDisabled" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label text-primary" for="blueY">Blue Y-coordinate</label>
|
||||
<input class="form-control" id="blueY" type="number" min="0" max="1" step="0.000000000000001" @bind="CurrentColorInfo.RgbPrimaries.Blue.Y" disabled="@PrimariesDisabled" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="whiteX">White X-coordinate</label>
|
||||
<input class="form-control" id="whiteX" type="number" min="0" max="1" step="0.000000000000001" @bind="CurrentColorInfo.RgbPrimaries.White.X" disabled="@PrimariesDisabled" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="whiteY">White Y-coordinate</label>
|
||||
<input class="form-control" id="whiteY" type="number" min="0" max="1" step="0.000000000000001" @bind="CurrentColorInfo.RgbPrimaries.White.Y" disabled="@PrimariesDisabled" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="targetGamut">Target gamut</label>
|
||||
<select class="form-select" id="targetGamut" @bind="ColorProfile.ColorGamut">
|
||||
@foreach (var gamut in Enum.GetValues<ColorGamut>())
|
||||
{
|
||||
<option value="@gamut">@gamut.GetDescription()</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<span class="d-flex align-items-baseline">
|
||||
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#luminanceSettings">
|
||||
Luminance settings
|
||||
</button>
|
||||
</span>
|
||||
</h2>
|
||||
<div id="luminanceSettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
|
||||
<div class="accordion-body">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="minLuminance">Minimum luminance (nits)</label>
|
||||
<input class="form-control" id="minLuminance" type="number" min="0" max="10" @bind="CurrentColorInfo.BlackLuminance" disabled="@PrimariesDisabled" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="maxLuminance">Max full frame luminance (nits)</label>
|
||||
<input class="form-control" id="maxLuminance" type="number" min="0" max="10000" @bind="CurrentColorInfo.WhiteLuminance" disabled="@PrimariesDisabled" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="minCLL">Minimum brightness (MinCLL) (nits)</label>
|
||||
<input class="form-control" id="minCLL" type="number" min="0" max="10" @bind="CurrentColorInfo.MinCLL" disabled="@PrimariesDisabled" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="maxCLL">Peak brightness (MaxCLL) (nits)</label>
|
||||
<input class="form-control" id="maxCLL" type="number" min="0" max="10000" @bind="CurrentColorInfo.MaxCLL" disabled="@PrimariesDisabled" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<span class="d-flex align-items-baseline">
|
||||
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#hdrSettings">
|
||||
Advanced EOTF settings
|
||||
</button>
|
||||
</span>
|
||||
</h2>
|
||||
<div id="hdrSettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
|
||||
<div class="accordion-body">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="sdrTransfer">SDR Transfer Function</label>
|
||||
<select class="form-select" id="sdrTransfer" @bind="ColorProfile.SDRTransferFunction">
|
||||
@foreach (var function in Enum.GetValues<SDRTransferFunction>())
|
||||
{
|
||||
<option value="@function">@function.GetDescription()</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="gamma">Gamma</label>
|
||||
<input class="form-control" id="gamma" type="number" min="0.1" max="10" step="0.01" @bind="ColorProfile.Gamma" disabled="@(ColorProfile.SDRTransferFunction != SDRTransferFunction.PurePower)" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@if (ColorProfile.IsHDRProfile)
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="minSDR">Minimum SDR brightness (nits)</label>
|
||||
<input class="form-control" id="minSDR" type="number" min="0" max="10" step="0.001" @bind="ColorProfile.SDRMinBrightness" disabled="@(ColorProfile.SDRTransferFunction != SDRTransferFunction.PurePower)" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="maxSDR">Maximum SDR brightness (nits)</label>
|
||||
<input class="form-control" id="maxSDR" type="number" min="0" max="10000" @bind="ColorProfile.SDRMaxBrightness" disabled="@(ColorProfile.SDRTransferFunction != SDRTransferFunction.PurePower)" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="brightnessBoost">Brightness boost (%)</label>
|
||||
<input class="form-control" id="brightnessBoost" type="number" min="-100" max="100" @bind="ColorProfile.SDRBrightnessBoost" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="shadowDetailBoost">Shadow detail boost (%)</label>
|
||||
<input class="form-control" id="shadowDetailBoost" type="number" min="0" max="100" @bind="ColorProfile.ShadowDetailBoost" disabled="@(ColorProfile.SDRTransferFunction == SDRTransferFunction.Piecewise)" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
}
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary" @onclick="InstallOnClick" disabled="@(ColorProfile == null)">Install and set as default</button>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @onclick="() => IsVisible = false">Close</button>
|
||||
<button type="submit" class="btn btn-primary" @onclick="ApplyClick" disabled="@(ColorProfile == null)">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private ColorProfileDto? ColorProfile { get; set; }
|
||||
private DisplayColorInfo? CurrentColorInfo { get; set; }
|
||||
private DisplayColorInfo? ColorProfileColorInfo { get; set; }
|
||||
private List<DisplayColorInfo> DisplayColors { get; set; } = new();
|
||||
private bool PrimariesDisabled = false;
|
||||
|
||||
[Parameter]
|
||||
public string? ColorProfileName { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public bool UseAsTemplate { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public DisplayInfoDto? DisplayInfo { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<ColorProfileDto, Task>? AfterApply { get; set; }
|
||||
|
||||
private string WasValidated = "";
|
||||
private bool IsVisible;
|
||||
private DisplayPrimariesSource DisplayPrimariesSource;
|
||||
private bool IsInstalled;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
if (ColorProfileName == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ColorProfileName == "")
|
||||
{
|
||||
ColorProfile = ColorProfileDto.CreateDefault(DisplayInfo?.IsHdrSupportedAndEnabled ?? false);
|
||||
}
|
||||
else
|
||||
{
|
||||
ColorProfile = await _rpcClientService.CallAsync<ColorProfileDto>("ColorProfileService", "GetColorProfile", ColorProfileName);
|
||||
}
|
||||
|
||||
if (ColorProfile != null)
|
||||
{
|
||||
ColorProfileColorInfo = new DisplayColorInfo
|
||||
{
|
||||
DisplayPrimariesSource = DisplayPrimariesSource.ColorProfile,
|
||||
BlackLuminance = ColorProfile.BlackLuminance,
|
||||
WhiteLuminance = ColorProfile.WhiteLuminance,
|
||||
MinCLL = ColorProfile.MinCLL,
|
||||
MaxCLL = ColorProfile.MaxCLL,
|
||||
RgbPrimaries = new RgbPrimariesDto(ColorProfile.DevicePrimaries)
|
||||
};
|
||||
|
||||
CurrentColorInfo = ColorProfileColorInfo;
|
||||
|
||||
DisplayColors.Clear();
|
||||
DisplayColors.Add(CurrentColorInfo);
|
||||
|
||||
if (DisplayInfo != null)
|
||||
{
|
||||
DisplayColors.AddRange(DisplayInfo.DisplayColors);
|
||||
}
|
||||
|
||||
if (UseAsTemplate)
|
||||
{
|
||||
if (ColorProfile.Name != null)
|
||||
{
|
||||
ColorProfile.Name = Path.GetFileNameWithoutExtension(ColorProfile.Name) + " (clone).icm";
|
||||
}
|
||||
else
|
||||
{
|
||||
ColorProfile.Name = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ApplyClick(MouseEventArgs e)
|
||||
{
|
||||
if (ColorProfile == null || CurrentColorInfo == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(await IsValid()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ColorProfile.UpdatePrimariesAndLuminance(CurrentColorInfo);
|
||||
|
||||
var result = await _rpcClientService.CallAsync<bool>("ColorProfileService", "UpdateColorProfile", ColorProfile, DisplayInfo?.DeviceName);
|
||||
|
||||
if (result)
|
||||
{
|
||||
await jsHelper.CloseModal("colorProfileModal");
|
||||
IsVisible = false;
|
||||
if (AfterApply != null)
|
||||
{
|
||||
await AfterApply.Invoke(ColorProfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task InstallOnClick()
|
||||
{
|
||||
if (ColorProfile == null || DisplayInfo == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(await IsValid()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ColorProfile.UpdatePrimariesAndLuminance(CurrentColorInfo);
|
||||
|
||||
var result = await _rpcClientService.CallAsync<bool>("ColorProfileService", "InstallAndActivateColorProfile", ColorProfile, DisplayInfo.DeviceName);
|
||||
|
||||
IsInstalled = true;
|
||||
}
|
||||
|
||||
private void ModalOnFocus(FocusEventArgs e)
|
||||
{
|
||||
if (!IsVisible)
|
||||
{
|
||||
WasValidated = "";
|
||||
IsVisible = true;
|
||||
DisplayPrimariesSource = DisplayPrimariesSource.ColorProfile;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> IsValid()
|
||||
{
|
||||
var valid = await jsHelper.IsFormValid("profileForm");
|
||||
|
||||
if (!valid)
|
||||
{
|
||||
WasValidated = "was-validated";
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
private void DisplayPrimariesSourceChanged()
|
||||
{
|
||||
if (DisplayInfo == null || ColorProfile == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentColorInfo = DisplayColors.First(dc => dc.DisplayPrimariesSource == DisplayPrimariesSource);
|
||||
PrimariesDisabled = CurrentColorInfo.DisplayPrimariesSource != DisplayPrimariesSource.ColorProfile;
|
||||
}
|
||||
|
||||
private void OverwritePrimariesOnClick(MouseEventArgs e)
|
||||
{
|
||||
if (ColorProfileColorInfo == null || CurrentColorInfo == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ColorProfileColorInfo.BlackLuminance = CurrentColorInfo.BlackLuminance;
|
||||
ColorProfileColorInfo.WhiteLuminance = CurrentColorInfo.WhiteLuminance;
|
||||
ColorProfileColorInfo.MinCLL = CurrentColorInfo.MinCLL;
|
||||
ColorProfileColorInfo.MaxCLL = CurrentColorInfo.MaxCLL;
|
||||
ColorProfileColorInfo.RgbPrimaries = new RgbPrimariesDto(CurrentColorInfo.RgbPrimaries);
|
||||
}
|
||||
|
||||
private void AfterNameChanged()
|
||||
{
|
||||
if (ColorProfile?.Name == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ColorProfile.Name.EndsWith(".icm", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
ColorProfile.Name += ".icm";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
@page "/ColorProfile"
|
||||
|
||||
@using ColorControl.Shared.Common
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.DisplayInfo
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using ColorControl.UI.Generics
|
||||
@using ColorControl.UI.Services
|
||||
|
||||
@rendermode InteractiveServer
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject JSHelper jsHelper;
|
||||
|
||||
<PageTitle>Color profiles</PageTitle>
|
||||
|
||||
<ColorProfilePage ColorProfileName="@SelectedColorProfileName" UseAsTemplate="UseAsTemplate" DisplayInfo="SelectedDisplay" AfterApply="AfterColorProfileSaved"></ColorProfilePage>
|
||||
|
||||
<ConfirmModal Title="Delete color profile" Message="Are you sure you want to delete the color profile?" OnConfirm="ColorProfileOnDeleteConfirmed" />
|
||||
<ConfirmModal ModalId="disassociateConfirmModal" Title="Disassociate color profile" Message="Are you sure you want to disassociate the color profile from the selected display?" OnConfirm="ColorProfileOnDisassociateConfirmed" />
|
||||
<ConfirmModal ModalId="associateConfirmModal" Title="Associate color profile" Message="Are you sure you want to associate the color profile with the selected display?" OnConfirm="ColorProfileOnAssociateConfirmed" />
|
||||
|
||||
<h2>Displays</h2>
|
||||
|
||||
@if (Displays == null)
|
||||
{
|
||||
<div>Loading...</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="row mb-3">
|
||||
@if (!Displays.Any())
|
||||
{
|
||||
<span>No displays found</span>
|
||||
}
|
||||
@foreach (var display in Displays)
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="device">Selected display</label>
|
||||
<select class="form-select" id="device" @bind="SelectedDisplayId" @bind:after="SelectedDisplayChanged">
|
||||
@foreach (var device in Displays)
|
||||
{
|
||||
<option value="@display.DisplayId">@display.DisplayName @@ @display.AdapterName</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
||||
@* <div class="col-lg-6 mb-3">
|
||||
<div class="card h-100">
|
||||
<div class="card-header d-flex align-items-baseline">
|
||||
<span>
|
||||
@display.DisplayName @@ @display.AdapterName
|
||||
</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<small class="text-body">@display.DevicePath</small>
|
||||
<small class="text-muted">EDID? @display.DisplayColors.Any(dc => dc.DisplayPrimariesSource == MHC2Gen.DisplayPrimariesSource.EDID)</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
*@ }
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (SelectedDisplay == null)
|
||||
{
|
||||
<div>Loading...</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="mb-2 d-flex align-items-center">
|
||||
<h2>Display Associated Color profiles (@(SelectedDisplay.IsHdrSupportedAndEnabled ? "HDR" : "SDR"))</h2>
|
||||
<div class="d-flex ms-auto">
|
||||
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#colorProfileModal" @onclick="() => AddColorProfileOnClick()">
|
||||
Add
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
@if (!SelectedDisplay.ColorProfiles.Any())
|
||||
{
|
||||
<span>No color profiles found that are associated with the display</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="list-group">
|
||||
@foreach (var colorProfile in SelectedDisplay.ColorProfiles)
|
||||
{
|
||||
<a class="list-group-item list-group-item-action" @onclick="() => SetColorProfileOnClick(colorProfile)" @onclick:preventDefault @onclick:stopPropagation>
|
||||
<div class="d-flex align-items-start justify-content-between">
|
||||
<div>
|
||||
<h5 class="mb-1">
|
||||
@colorProfile
|
||||
@if (colorProfile == SelectedDisplay?.DefaultColorProfile)
|
||||
{
|
||||
<span class="ms-2 badge text-bg-warning" title="Active color profile">Active</span>
|
||||
}
|
||||
</h5>
|
||||
</div>
|
||||
|
||||
<span class="d-none d-lg-block">
|
||||
<button class="btn btn-warning btn-sm" data-bs-toggle="modal" data-bs-target="#disassociateConfirmModal" @onclick="() => SelectedColorProfileName = colorProfile" @onclick:preventDefault @onclick:stopPropagation>Disassociate</button>
|
||||
<button class="btn btn-danger btn-sm" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => DeleteColorProfileOnClick(colorProfile)" @onclick:preventDefault @onclick:stopPropagation>Delete</button>
|
||||
<button class="btn btn-secondary btn-sm" data-bs-toggle="modal" data-bs-target="#colorProfileModal" @onclick="() => CloneColorProfileOnClick(colorProfile)" @onclick:preventDefault @onclick:stopPropagation>Clone</button>
|
||||
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#colorProfileModal" @onclick="() => EditColorProfileOnClick(colorProfile)" @onclick:preventDefault @onclick:stopPropagation>Edit</button>
|
||||
</span>
|
||||
<div class="d-lg-none" @onclick:preventDefault @onclick:stopPropagation>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-three-dots-vertical"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#disassociateConfirmModal" @onclick="() => SelectedColorProfileName = colorProfile">Disassociate</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => DeleteColorProfileOnClick(colorProfile)">Delete</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#colorProfileModal" @onclick="() => CloneColorProfileOnClick(colorProfile)">Clone</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#colorProfileModal" @onclick="() => EditColorProfileOnClick(colorProfile)">Edit</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="mb-2 d-flex align-items-center">
|
||||
<h2>Other Color profiles</h2>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
@if (ColorProfileNames == null)
|
||||
{
|
||||
<span>No profiles found</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="list-group">
|
||||
@foreach (var colorProfile in ColorProfileNames)
|
||||
{
|
||||
<span class="list-group-item list-group-item-action" @onclick="() => EditColorProfileOnClick(colorProfile)" @onclick:preventDefault @onclick:stopPropagation>
|
||||
<div class="d-flex align-items-start justify-content-between">
|
||||
<div>
|
||||
<h5 class="mb-1">
|
||||
@colorProfile
|
||||
</h5>
|
||||
</div>
|
||||
|
||||
<span class="d-none d-lg-block">
|
||||
<button class="btn btn-warning btn-sm" data-bs-toggle="modal" data-bs-target="#associateConfirmModal" @onclick="() => SelectedColorProfileName = colorProfile" @onclick:preventDefault @onclick:stopPropagation>Associate</button>
|
||||
<button class="btn btn-danger btn-sm" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => DeleteColorProfileOnClick(colorProfile)" @onclick:preventDefault @onclick:stopPropagation>Delete</button>
|
||||
<button class="btn btn-secondary btn-sm" data-bs-toggle="modal" data-bs-target="#colorProfileModal" @onclick="() => CloneColorProfileOnClick(colorProfile)" @onclick:preventDefault @onclick:stopPropagation>Clone</button>
|
||||
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#colorProfileModal" @onclick="() => EditColorProfileOnClick(colorProfile)" @onclick:preventDefault @onclick:stopPropagation>Edit</button>
|
||||
</span>
|
||||
<div class="d-lg-none" @onclick:preventDefault @onclick:stopPropagation>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-three-dots-vertical"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#associateConfirmModal" @onclick="() => SelectedColorProfileName = colorProfile">Associate</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => DeleteColorProfileOnClick(colorProfile)">Delete</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#colorProfileModal" @onclick="() => CloneColorProfileOnClick(colorProfile)">Clone</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#colorProfileModal" @onclick="() => EditColorProfileOnClick(colorProfile)">Edit</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
|
||||
private string? SelectedDisplayId;
|
||||
private DisplayInfoDto? SelectedDisplay;
|
||||
private List<DisplayInfoDto>? Displays;
|
||||
private string? SelectedColorProfileName;
|
||||
private bool UseAsTemplate;
|
||||
private List<string>? ColorProfileNames;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await RefreshDisplays();
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstTime)
|
||||
{
|
||||
}
|
||||
|
||||
private async Task AddColorProfileOnClick()
|
||||
{
|
||||
SelectedColorProfileName = "";
|
||||
}
|
||||
|
||||
private async Task EditColorProfileOnClick(string colorProfile)
|
||||
{
|
||||
UseAsTemplate = false;
|
||||
SelectedColorProfileName = colorProfile;
|
||||
await jsHelper.OpenModal("colorProfileModal");
|
||||
}
|
||||
|
||||
private async Task SetColorProfileOnClick(string colorProfile)
|
||||
{
|
||||
if (SelectedDisplay == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var result = await _rpcClientService.CallAsync<bool>("ColorProfileService", "SetDisplayDefaultColorProfile", SelectedDisplay.DeviceName, colorProfile);
|
||||
if (result)
|
||||
{
|
||||
SelectedDisplay.DefaultColorProfile = colorProfile;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DeleteColorProfileOnClick(string colorProfile)
|
||||
{
|
||||
SelectedColorProfileName = colorProfile;
|
||||
}
|
||||
|
||||
private async Task CloneColorProfileOnClick(string colorProfile)
|
||||
{
|
||||
UseAsTemplate = true;
|
||||
SelectedColorProfileName = colorProfile;
|
||||
}
|
||||
|
||||
private async Task ColorProfileOnDeleteConfirmed()
|
||||
{
|
||||
if (SelectedColorProfileName == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await _rpcClientService.CallAsync<bool>("ColorProfileService", "DeleteColorProfile", SelectedColorProfileName);
|
||||
|
||||
SelectedColorProfileName = null;
|
||||
|
||||
await RefreshDisplays();
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task ColorProfileOnDisassociateConfirmed()
|
||||
{
|
||||
if (SelectedColorProfileName == null || SelectedDisplay == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await _rpcClientService.CallAsync<bool>("ColorProfileService", "RemoveDisplayColorProfile", SelectedDisplay.DeviceName, SelectedColorProfileName);
|
||||
|
||||
SelectedColorProfileName = null;
|
||||
|
||||
await RefreshDisplays();
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task ColorProfileOnAssociateConfirmed()
|
||||
{
|
||||
if (SelectedColorProfileName == null || SelectedDisplay == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await _rpcClientService.CallAsync<bool>("ColorProfileService", "AddDisplayColorProfile", SelectedDisplay.DeviceName, SelectedColorProfileName, false);
|
||||
|
||||
SelectedColorProfileName = null;
|
||||
|
||||
await RefreshDisplays();
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private void SelectedDisplayChanged()
|
||||
{
|
||||
SelectedDisplay = Displays?.FirstOrDefault(d => d.DisplayId == SelectedDisplayId);
|
||||
}
|
||||
|
||||
private async Task AfterColorProfileSaved(ColorProfileDto colorProfile)
|
||||
{
|
||||
await RefreshDisplays();
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task RefreshDisplays()
|
||||
{
|
||||
Displays = await _rpcClientService.CallAsync<List<DisplayInfoDto>>("ColorProfileService", "GetDisplayInfos");
|
||||
|
||||
ColorProfileNames = await _rpcClientService.CallAsync<List<string>>("ColorProfileService", "GetAllColorProfileNames");
|
||||
|
||||
if (Displays?.Any() == true)
|
||||
{
|
||||
SelectedDisplay = Displays.FirstOrDefault(d => d.DisplayId == SelectedDisplayId) ?? Displays.First();
|
||||
SelectedDisplayId = SelectedDisplay.DisplayId;
|
||||
|
||||
ColorProfileNames = ColorProfileNames?.Except(SelectedDisplay.ColorProfiles).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.NVIDIA
|
||||
@using ColorControl.Shared.Contracts.Game
|
||||
@using ColorControl.Shared.Services
|
||||
@using ColorControl.UI.Services
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using NvAPIWrapper.Native.Display
|
||||
@using ColorControl.Shared.Common
|
||||
@@ -13,7 +13,7 @@
|
||||
@inject NavigationManager _navigationManager;
|
||||
|
||||
<div class="modal" id="presetModal" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalOnFocus">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@(Preset?.id == 0 ? $"Settings for new preset" : $"Settings of preset {Preset?.IdOrName}")</h5>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.NVIDIA
|
||||
@using ColorControl.Shared.Contracts.Game
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
|
||||
<div class="modal" id="settingsModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">NVIDIA settings</h5>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@using ColorControl.Shared.Contracts;
|
||||
@using ColorControl.Shared.Contracts.Game;
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using ColorControl.Shared.Common;
|
||||
@using ColorControl.UI.Generics
|
||||
@@ -52,6 +52,10 @@ else
|
||||
{
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
@if (!Presets.Any())
|
||||
{
|
||||
<span>No presets found</span>
|
||||
}
|
||||
<div class="list-group">
|
||||
@foreach (var preset in Presets)
|
||||
{
|
||||
@@ -66,11 +70,21 @@ else
|
||||
}
|
||||
</h5>
|
||||
</div>
|
||||
<span>
|
||||
<span class="d-none d-lg-block">
|
||||
<button class="btn btn-danger btn-sm" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => PresetOnDeleteClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Delete</button>
|
||||
<button class="btn btn-secondary btn-sm" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnCloneClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Clone</button>
|
||||
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnEditClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Edit</button>
|
||||
</span>
|
||||
<div class="d-lg-none" @onclick:preventDefault @onclick:stopPropagation>
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-three-dots-vertical"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => PresetOnDeleteClick(preset)">Delete</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnCloneClick(preset)">Clone</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnEditClick(preset)">Edit</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
|
||||
@rendermode InteractiveServer
|
||||
|
||||
<FieldsModal ModalId="confirmModal" Fields="Fields" Title="@Title" OnConfirm="ConfirmClick"></FieldsModal>
|
||||
<FieldsModal ModalId="@ModalId" Fields="Fields" Title="@Title" OnConfirm="ConfirmClick"></FieldsModal>
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
@@ -16,6 +16,9 @@
|
||||
[Parameter]
|
||||
public Func<Task>? OnConfirm { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string ModalId { get; set; } = "confirmModal";
|
||||
|
||||
public List<FieldDefinition>? Fields { get; set; }
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
|
||||
@rendermode InteractiveServer
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
|
||||
@rendermode InteractiveServer
|
||||
@@ -7,7 +7,7 @@
|
||||
@inject NavigationManager _navigationManager;
|
||||
|
||||
<div class="modal" id="@ModalId" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalFocus">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@Title</h5>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.EventDispatcher
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
|
||||
<div class="row mx-0 mb-2">
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.EventDispatcher
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
@using ColorControl.UI.Services
|
||||
|
||||
@rendermode InteractiveServer
|
||||
|
||||
@inject JSHelper jsHelper;
|
||||
@inject NotificationService notificationService;
|
||||
@inject NavigationManager _navigationManager;
|
||||
|
||||
@if (Notifications?.Any() == true)
|
||||
{
|
||||
<div aria-live="polite" aria-atomic="true" class="position-relative">
|
||||
<div class="toast-container bottom-0 end-0 p-3">
|
||||
|
||||
@foreach (var notification in Notifications)
|
||||
{
|
||||
<div class="toast" id="toast_@notification.Id" role="alert" aria-live="assertive" aria-atomic="true" @onclick="() => NotificationOnClick(notification)">
|
||||
<div class="toast-header" style="background-color: var(--bs-@notification.Level-bg-subtle)">
|
||||
<span class="w-100 d-flex">
|
||||
<div class="alert alert-@notification.Level mb-0 py-1">
|
||||
<strong class="me-auto">@notification.Category</strong>
|
||||
</div>
|
||||
</span>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="toast-body">
|
||||
@if (notification.InternalUrl != null)
|
||||
{
|
||||
<ul class="list-group-flush p-0 mb-0">
|
||||
<li class="list-group-item">@notification.Message</li>
|
||||
</ul>
|
||||
}
|
||||
else
|
||||
{
|
||||
@notification.Message
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@code {
|
||||
private List<NotificationDto> Notifications { get; } = [];
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
notificationService.OnNotification += AddNotification;
|
||||
}
|
||||
}
|
||||
|
||||
protected async Task AddNotification(NotificationDto notification)
|
||||
{
|
||||
RemoveOldNotifications();
|
||||
|
||||
var minShownAt = DateTime.UtcNow.AddSeconds(-5);
|
||||
|
||||
// Suppress duplicate noficiations
|
||||
if (Notifications.Any(n => n.Message == notification.Message && n.Level == notification.Level && n.ShownAt >= minShownAt))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Notifications.Add(notification);
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
await jsHelper.ShowToast($"toast_{notification.Id}");
|
||||
notification.ShownAt = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
private async Task NotificationOnClick(NotificationDto notification)
|
||||
{
|
||||
if (notification.InternalUrl != null)
|
||||
{
|
||||
_navigationManager.NavigateTo(notification.InternalUrl);
|
||||
}
|
||||
|
||||
Notifications.Remove(notification);
|
||||
}
|
||||
|
||||
private void RemoveOldNotifications()
|
||||
{
|
||||
var minDateTime = DateTime.UtcNow.AddMinutes(-1);
|
||||
|
||||
Notifications.RemoveAll(n => n.ShownAt.HasValue && n.ShownAt <= minDateTime);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.EventDispatcher
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
|
||||
@rendermode InteractiveServer
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
@using ColorControl.Shared.Contracts.NVIDIA
|
||||
@using ColorControl.Shared.Contracts.Samsung
|
||||
@using ColorControl.Shared.EventDispatcher
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
|
||||
@typeparam TPreset where TPreset : PresetBase
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.EventDispatcher
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
|
||||
@rendermode InteractiveServer
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
@page "/"
|
||||
|
||||
@using ColorControl.Shared.Common
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.UI.Generics
|
||||
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
|
||||
|
||||
@rendermode InteractiveServer
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject AppState AppState
|
||||
@inject NavigationManager _navigationManager;
|
||||
@inject NotificationService _notificationService
|
||||
@inject ProtectedSessionStorage _sessionStorage
|
||||
|
||||
<PageTitle>Home</PageTitle>
|
||||
|
||||
@@ -24,10 +29,10 @@ else
|
||||
<div class="row">
|
||||
@foreach (var module in Modules.Where(m => m.IsActive))
|
||||
{
|
||||
<div class="col">
|
||||
<div class="card h-100 mb-3">
|
||||
<div class="col-lg-4 col-xl mb-3">
|
||||
<div class="card h-100">
|
||||
<div class="card-header">
|
||||
@module.DisplayName
|
||||
<span class="me-2 bi bi-@module.GetIconClass()" aria-hidden="true"></span>@module.DisplayName
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<a href="@module.Link" class="btn btn-primary stretched-link mb-2">Open module</a>
|
||||
@@ -47,6 +52,8 @@ else
|
||||
@code {
|
||||
private Config? Config;
|
||||
private List<ModuleDto>? Modules;
|
||||
private InfoDto? Info;
|
||||
private bool IsUpdateAvailable;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
@@ -57,4 +64,28 @@ else
|
||||
Modules.ForEach(m => m.Link = m.DisplayName.Split(' ')[0]);
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender || Info != null || Config?.CheckForUpdates != true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var checkedForUpdates = await _sessionStorage.TryGet<bool>("checkedForUpdates");
|
||||
|
||||
if (!checkedForUpdates)
|
||||
{
|
||||
Info = await _rpcClientService.CallAsync<InfoDto>("OptionsService", "GetInfo");
|
||||
|
||||
await _sessionStorage.SetAsync("checkedForUpdates", true);
|
||||
|
||||
if (!IsUpdateAvailable && Info?.UpdateInfoDto?.UpdateAvailable == true)
|
||||
{
|
||||
IsUpdateAvailable = true;
|
||||
var message = $"Update available: version {Info.UpdateInfoDto.NewVersionNumber}";
|
||||
_notificationService.SendNotification(new NotificationDto(message) { InternalUrl = "/info", ScheduledAt = DateTime.UtcNow.AddSeconds(5) });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,14 @@
|
||||
@page "/info"
|
||||
|
||||
@using ColorControl.Shared.Common;
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
|
||||
@rendermode InteractiveServer
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject AppState AppState
|
||||
@inject NavigationManager _navigationManager;
|
||||
@inject IHttpContextAccessor _httpContextAccessor
|
||||
|
||||
<PageTitle>Logging</PageTitle>
|
||||
|
||||
@@ -20,7 +21,41 @@
|
||||
else
|
||||
{
|
||||
<h3 class="mb-2">@info.ApplicationTitle - @info.LegalCopyright</h3>
|
||||
|
||||
<div class="d-flex align-items-baseline">
|
||||
@if (IsUpdateInstalled)
|
||||
{
|
||||
if (IsEmbeddedBrowser)
|
||||
{
|
||||
<button type="button" class="btn btn-primary me-2" @onclick="RestartOnClick">Restart</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="button" class="btn btn-primary me-2" @onclick="ReloadOnClick">Reload</button>
|
||||
}
|
||||
<span>New version @info.UpdateInfoDto.NewVersionNumber is installed. Click the button to restart the application.</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="button" class="btn btn-primary me-2" disabled="@(!info.UpdateInfoDto.UpdateAvailable || IsInstallingUpdate)" @onclick="InstallUpdateOnClick">
|
||||
@if (IsInstallingUpdate)
|
||||
{
|
||||
<span class="spinner-border spinner-border-sm me-1" aria-hidden="true"></span>
|
||||
}
|
||||
<span role="status">Install update</span>
|
||||
</button>
|
||||
@if (info.UpdateInfoDto.UpdateAvailable)
|
||||
{
|
||||
<span>New version @info.UpdateInfoDto.NewVersionNumber available. Click the button to install the update.</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>You have the latest version</span>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<h5>This app contains the following 3rd party plugins:</h5>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">lgtv.net by gr4b4z</li>
|
||||
@@ -41,10 +76,46 @@ else
|
||||
|
||||
@code {
|
||||
private InfoDto? info;
|
||||
private bool IsInstallingUpdate;
|
||||
private bool IsUpdateInstalled;
|
||||
private bool IsEmbeddedBrowser;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
info = await _rpcClientService.CallAsync<InfoDto>("OptionsService", "GetInfo");
|
||||
|
||||
}
|
||||
|
||||
private async Task InstallUpdateOnClick(MouseEventArgs e)
|
||||
{
|
||||
IsInstallingUpdate = true;
|
||||
try
|
||||
{
|
||||
var result = await _rpcClientService.CallAsync<bool>("OptionsService", "InstallUpdate");
|
||||
|
||||
if (!result)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IsUpdateInstalled = true;
|
||||
IsEmbeddedBrowser = _httpContextAccessor.HttpContext?.Request.Headers["user-agent"].ToString().Contains("ColorControlEmbedded") == true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsInstallingUpdate = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RestartOnClick(MouseEventArgs e)
|
||||
{
|
||||
var result = await _rpcClientService.CallAsync<bool>("OptionsService", "RestartAfterUpdate");
|
||||
}
|
||||
|
||||
private async Task ReloadOnClick(MouseEventArgs e)
|
||||
{
|
||||
var result = await _rpcClientService.CallAsync<bool>("OptionsService", "RestartAfterUpdate");
|
||||
|
||||
_navigationManager.Refresh();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.LG
|
||||
@using ColorControl.Shared.Services
|
||||
@using ColorControl.UI.Services
|
||||
@using NvAPIWrapper.Native.Display
|
||||
@using ColorControl.Shared.Common
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
@inject NavigationManager _navigationManager;
|
||||
|
||||
<div class="modal" id="deviceModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@(Device?.Name == null ? $"Settings for new device" : $"Settings of device {Device.Name}")</h5>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.LG
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.LG
|
||||
@using ColorControl.Shared.Services
|
||||
@using ColorControl.UI.Services
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using NvAPIWrapper.Native.Display
|
||||
@using ColorControl.Shared.Common
|
||||
@@ -13,7 +13,7 @@
|
||||
@inject NavigationManager _navigationManager;
|
||||
|
||||
<div class="modal" id="presetModal" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalOnFocus">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@(Preset?.id == 0 ? $"Settings for new preset" : $"Settings of preset {Preset?.IdOrName}")</h5>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.LG
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.NVIDIA
|
||||
@using ColorControl.Shared.Contracts.LG
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using ColorControl.UI.Generics
|
||||
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
|
||||
@@ -155,6 +155,10 @@ else
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
@if (!Presets.Any())
|
||||
{
|
||||
<span>No presets found</span>
|
||||
}
|
||||
<div class="list-group">
|
||||
@foreach (var preset in Presets)
|
||||
{
|
||||
@@ -168,11 +172,21 @@ else
|
||||
}
|
||||
<div><small class="text-muted">@preset.shortcut</small></div>
|
||||
</div>
|
||||
<span>
|
||||
<span class="d-none d-lg-block">
|
||||
<button class="btn btn-danger btn-sm" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => PresetOnDeleteClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Delete</button>
|
||||
<button class="btn btn-secondary btn-sm" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnCloneClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Clone</button>
|
||||
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnEditClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Edit</button>
|
||||
</span>
|
||||
<div class="d-lg-none" @onclick:preventDefault @onclick:stopPropagation>
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-three-dots-vertical"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => PresetOnDeleteClick(preset)">Delete</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnCloneClick(preset)">Clone</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnEditClick(preset)">Edit</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
}
|
||||
@@ -215,7 +229,7 @@ else
|
||||
Devices = await _rpcClientService.CallAsync<List<LgDeviceDto>>("LgService", "GetDevices");
|
||||
InvokableActions = await _rpcClientService.CallAsync<List<InvokableActionDto<LgPreset>>>("LgService", "GetInvokableActions");
|
||||
|
||||
ActionGroups = InvokableActions.GroupBy(a => a.Category).Select(g => new ActionGroup(g.Key, g.ToList())).ToList();
|
||||
ActionGroups = InvokableActions?.GroupBy(a => a.Category).Select(g => new ActionGroup(g.Key, g.ToList())).ToList() ?? new List<ActionGroup>();
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstTime)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@page "/logging"
|
||||
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
|
||||
@rendermode InteractiveServer
|
||||
|
||||
@@ -154,10 +154,13 @@ else
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
config = await _rpcClientService.CallAsync<Config>("OptionsService", "GetConfig");
|
||||
config ??= await _rpcClientService.CallAsync<Config>("OptionsService", "GetConfig");
|
||||
logLevel = config.LogLevel;
|
||||
|
||||
await GetLog();
|
||||
if (allLines == null)
|
||||
{
|
||||
await GetLog();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task GetLog()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.NVIDIA
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using NvAPIWrapper.Native.Display
|
||||
@using ColorControl.Shared.Common;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.NVIDIA
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using NvAPIWrapper.Native.Display
|
||||
@using ColorControl.Shared.Common;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
@inject NavigationManager _navigationManager;
|
||||
|
||||
<div class="modal modal-lg" id="driverSettingsModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" @onfocus="OnFocus">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@(Preset != null ? Preset.IsDisplayPreset ? $"Driver settings for display {Preset.displayName}" : $"Driver settings for {Preset.IdOrName}" : "Driver settings")</h5>
|
||||
@@ -38,6 +38,12 @@
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox" @bind="OnlyFoundPrograms" id="onlyFoundPrograms" @bind:after="OnlyFoundProgramsOnChange">
|
||||
<label class="form-check-label" for="onlyFoundPrograms">
|
||||
Only show found programs
|
||||
</label>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="accordion accordion-flush" id="driverSettingsAccordion">
|
||||
@@ -137,6 +143,8 @@
|
||||
|
||||
private bool IsVisible { get; set; }
|
||||
|
||||
private bool OnlyFoundPrograms = true;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
if (_preset != Preset)
|
||||
@@ -232,12 +240,17 @@
|
||||
await LoadDriverSettings();
|
||||
}
|
||||
|
||||
private async Task OnlyFoundProgramsOnChange()
|
||||
{
|
||||
ProfileNames = await _rpcClientService.CallAsync<List<string>>("NvService", "GetDriverProfileNames", OnlyFoundPrograms);
|
||||
}
|
||||
|
||||
private async Task OnFocus(FocusEventArgs e)
|
||||
{
|
||||
if (!IsVisible)
|
||||
{
|
||||
IsVisible = true;
|
||||
ProfileNames ??= await _rpcClientService.CallAsync<List<string>>("NvService", "GetDriverProfileNames");
|
||||
ProfileNames ??= await _rpcClientService.CallAsync<List<string>>("NvService", "GetDriverProfileNames", OnlyFoundPrograms);
|
||||
await LoadDriverSettings();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.NVIDIA
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using NvAPIWrapper.Native.Display
|
||||
@using ColorControl.Shared.Common;
|
||||
@@ -12,7 +12,7 @@
|
||||
@inject NavigationManager _navigationManager;
|
||||
|
||||
<div class="modal" id="presetModal" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalOnFocus">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@(Preset?.IsDisplayPreset == true ? $"Settings for display {Preset?.displayName}" : Preset?.id == 0 ? $"Settings for new preset" : $"Settings of preset {Preset?.IdOrName}")</h5>
|
||||
@@ -448,6 +448,7 @@
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="extendedColorimetry">Extended colorimetry</label>
|
||||
<select class="form-select" id="extendedColorimetry" @bind="Preset.HdmiInfoFrameSettings.ExtendedColorimetry" @oninput="HdmiSettingsOnInput">
|
||||
<option value="">Not selected</option>
|
||||
@foreach (var extendedColorimetry in Enum.GetValues<InfoFrameVideoExtendedColorimetry>())
|
||||
{
|
||||
<option value="@extendedColorimetry.ToString()">@extendedColorimetry.GetDescription()</option>
|
||||
@@ -458,6 +459,48 @@
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (ColorProfileNames?.Any() == true)
|
||||
{
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<span class="d-flex align-items-baseline">
|
||||
@if (!Preset.IsDisplayPreset)
|
||||
{
|
||||
<input class="form-check-input me-1" style="font-size: 1rem" title="Apply these settings" type="checkbox" @bind="Preset.applyOther" id="applyOther">
|
||||
}
|
||||
<button class="accordion-button collapsed py-2 @(Preset.applyOther ? "text-warning fw-bold" : "")" type="button" data-bs-toggle="collapse" data-bs-target="#otherSettings">
|
||||
Other settings
|
||||
</button>
|
||||
</span>
|
||||
</h2>
|
||||
<div id="otherSettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
|
||||
<div class="accordion-body">
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="colorProfile">Color profile</label>
|
||||
<select class="form-select" id="colorProfile" @bind="Preset.ColorProfileSettings.ProfileName">
|
||||
<option value="">Not selected</option>
|
||||
@foreach (var colorProfile in ColorProfileNames)
|
||||
{
|
||||
<option value="@colorProfile">@colorProfile</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="nvScaling">Scaling</label>
|
||||
<select class="form-select" id="nvScaling" @bind="Preset.scaling">
|
||||
<option value="">Not selected</option>
|
||||
@foreach (var scaling in Enum.GetValues<Scaling>())
|
||||
{
|
||||
<option value="@scaling">@scaling.GetDescription()</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
}
|
||||
</form>
|
||||
@@ -489,6 +532,7 @@
|
||||
private List<VirtualResolution>? resolutions;
|
||||
private List<Rational>? refreshRates;
|
||||
private List<NvPreset>? displayPresets;
|
||||
private List<string>? ColorProfileNames;
|
||||
|
||||
private string WasValidated = "";
|
||||
private bool IsVisible;
|
||||
@@ -511,10 +555,17 @@
|
||||
selectedVirtualResolution = Preset?.DisplayConfig.Resolution?.GetVirtualString();
|
||||
selectedRefreshRate = Preset?.DisplayConfig.RefreshRate?.ToString();
|
||||
|
||||
if (Preset?.IsDisplayPreset != true)
|
||||
if (Preset?.IsDisplayPreset == false)
|
||||
{
|
||||
displayPresets = await _rpcClientService.CallAsync<List<NvPreset>>("NvService", "GetDisplayPresets");
|
||||
}
|
||||
|
||||
var deviceName = Preset?.IsDisplayPreset == true ? Preset.DisplayId : Preset?.DisplayId ?? displayPresets?.FirstOrDefault()?.DisplayId;
|
||||
|
||||
if (Preset != null)
|
||||
{
|
||||
ColorProfileNames = await _rpcClientService.CallAsync<List<string>>("ColorProfileService", "GetDisplayColorProfiles", deviceName);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.NVIDIA
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
|
||||
<div class="modal" id="settingsModal" tabindex="-1" @onfocus="OnFocus">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">NVIDIA settings</h5>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.NVIDIA
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using ColorControl.Shared.Common;
|
||||
@using ColorControl.UI.Generics
|
||||
@@ -74,6 +74,21 @@ else
|
||||
<div class="card-body">
|
||||
<small class="text-body">@preset.DisplayConfig.Resolution.ToString() @@ @preset.DisplayConfig.RefreshRate.ToString()Hz</small>
|
||||
<small class="text-muted">@preset.colorData</small>
|
||||
@if (preset.DitherState != NvDitherState.Auto)
|
||||
{
|
||||
<br />
|
||||
<small class="text-body">Dithering: @preset.DitherState.GetDescription() @preset.NvDitherBits.GetDescription() @preset.NvDitherMode.GetDescription()</small>
|
||||
}
|
||||
@if (preset.HDREnabled)
|
||||
{
|
||||
<br />
|
||||
<small class="text-body">HDR enabled @(preset.HdrSettings.OutputMode.HasValue ? $"({preset.HdrSettings.DisplayOutputMode.GetDescription()})" : ""), SDR brightness: @preset.SDRBrightness%</small>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(preset.ColorProfileSettings.ProfileName))
|
||||
{
|
||||
<br />
|
||||
<small class="text-body">Color profile: @preset.ColorProfileSettings.ProfileName</small>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -103,10 +118,14 @@ else
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
@if (!Presets.Any())
|
||||
{
|
||||
<span>No presets found</span>
|
||||
}
|
||||
<div class="list-group">
|
||||
@foreach (var preset in Presets)
|
||||
{
|
||||
<a class="list-group-item list-group-item-action" @onclick="() => PresetOnClick(preset)" @onclick:preventDefault @onclick:stopPropagation>
|
||||
<span class="list-group-item list-group-item-action" @onclick="() => PresetOnClick(preset)" @onclick:preventDefault @onclick:stopPropagation>
|
||||
<div class="d-flex align-items-start justify-content-between">
|
||||
<div>
|
||||
<h5 class="mb-1">
|
||||
@@ -117,34 +136,68 @@ else
|
||||
}
|
||||
</h5>
|
||||
</div>
|
||||
<span>
|
||||
<span class="d-none d-lg-block">
|
||||
<button class="btn btn-danger btn-sm" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => PresetOnDeleteClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Delete</button>
|
||||
<button class="btn btn-secondary btn-sm" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnCloneClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Clone</button>
|
||||
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnEditClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Edit</button>
|
||||
</span>
|
||||
<div class="d-lg-none" @onclick:preventDefault @onclick:stopPropagation>
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-three-dots-vertical"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => PresetOnDeleteClick(preset)">Delete</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnCloneClick(preset)">Clone</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnEditClick(preset)">Edit</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<p class="mb-1">
|
||||
@preset.DisplayConfig.Resolution.ToString() @@ @preset.DisplayConfig.RefreshRate.ToString()Hz
|
||||
@(preset.applyColorData ? " " + preset.colorData : "")
|
||||
@if (preset.ApplyColorEnhancements)
|
||||
{
|
||||
<span>, @preset.ColorEnhancementSettings</span>
|
||||
}
|
||||
@if (preset.applyHDR)
|
||||
{
|
||||
<span>, HDR: @preset.GetHdrDescription()</span>
|
||||
}
|
||||
@if (preset.applyDriverSettings)
|
||||
{
|
||||
<span>, @preset.driverSettings.Count changed driver settings</span>
|
||||
}
|
||||
@if (preset.ApplyNovideoSettings)
|
||||
{
|
||||
<span>, @preset.NovideoSettings</span>
|
||||
}
|
||||
</p>
|
||||
<small>
|
||||
<ul class="list-group list-group-horizontal-md mb-1">
|
||||
@if (preset.DisplayConfig.ApplyResolution || preset.DisplayConfig.ApplyRefreshRate)
|
||||
{
|
||||
<li class="list-group-item p-1">@preset.DisplayConfig.Resolution.ToString() @@ @preset.DisplayConfig.RefreshRate.ToString()Hz</li>
|
||||
}
|
||||
@if (preset.applyColorData)
|
||||
{
|
||||
<li class="list-group-item p-1">Color settings: @preset.colorData</li>
|
||||
}
|
||||
@if (preset.applyDithering)
|
||||
{
|
||||
<li class="list-group-item p-1">Dithering: @preset.GetDitheringDescription()</li>
|
||||
}
|
||||
@if (preset.ApplyColorEnhancements)
|
||||
{
|
||||
<li class="list-group-item p-1">@preset.ColorEnhancementSettings</li>
|
||||
}
|
||||
@if (preset.applyHDR)
|
||||
{
|
||||
<li class="list-group-item p-1">HDR: @preset.GetHdrDescription()</li>
|
||||
}
|
||||
@if (preset.applyDriverSettings)
|
||||
{
|
||||
<li class="list-group-item p-1">@preset.driverSettings.Count changed driver settings</li>
|
||||
}
|
||||
@if (preset.ApplyNovideoSettings)
|
||||
{
|
||||
<li class="list-group-item p-1">@preset.NovideoSettings</li>
|
||||
}
|
||||
@if (preset.applyHdmiSettings)
|
||||
{
|
||||
<li class="list-group-item p-1">@preset.GetHdmiSettingsDescription()</li>
|
||||
}
|
||||
@if (preset.applyOverclocking)
|
||||
{
|
||||
<li class="list-group-item p-1">@preset.GetOverclockingSettingsDescription()</li>
|
||||
}
|
||||
@if (preset.applyOther && !string.IsNullOrEmpty(preset.ColorProfileSettings.ProfileName))
|
||||
{
|
||||
<li class="list-group-item p-1">Color profile: @preset.ColorProfileSettings.ProfileName</li>
|
||||
}
|
||||
</ul>
|
||||
</small>
|
||||
<small class="text-muted">@preset.shortcut</small>
|
||||
</a>
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@page "/options"
|
||||
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject AppState AppState
|
||||
@inject NavigationManager _navigationManager;
|
||||
@inject NavigationManager _navigationManager
|
||||
@inject NotificationService _notificationService
|
||||
|
||||
<PageTitle>Options</PageTitle>
|
||||
|
||||
@@ -250,6 +251,11 @@ else
|
||||
{
|
||||
_navigationManager.NavigateTo("/options", true);
|
||||
}
|
||||
else if (module.IsActive)
|
||||
{
|
||||
_notificationService.SendNotification(new NotificationDto("Cannot activate the module: check the log for more info", Constants.Danger) { InternalUrl = "/logging" });
|
||||
module.IsActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task PollingIntervalOnInput()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.Samsung
|
||||
@using ColorControl.Shared.Services
|
||||
@using ColorControl.UI.Services
|
||||
@using NvAPIWrapper.Native.Display
|
||||
@using ColorControl.Shared.Common
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
@inject NavigationManager _navigationManager;
|
||||
|
||||
<div class="modal" id="deviceModal" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalOnFocus">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@(Device?.Name == null ? $"Add new device" : $"Settings of device {Device.Name}")</h5>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.Samsung
|
||||
@using ColorControl.Shared.Services
|
||||
@using ColorControl.UI.Services
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using NvAPIWrapper.Native.Display
|
||||
@using ColorControl.Shared.Common
|
||||
@@ -13,7 +13,7 @@
|
||||
@inject NavigationManager _navigationManager;
|
||||
|
||||
<div class="modal" id="presetModal" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalOnFocus">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@(Preset?.id == 0 ? $"Settings for new preset" : $"Settings of preset {Preset?.IdOrName}")</h5>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.Samsung
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.Samsung
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.NVIDIA
|
||||
@using ColorControl.Shared.Contracts.Samsung
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using ColorControl.UI.Generics
|
||||
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
|
||||
@@ -153,6 +153,10 @@ else
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
@if (!Presets.Any())
|
||||
{
|
||||
<span>No presets found</span>
|
||||
}
|
||||
<div class="list-group">
|
||||
@foreach (var preset in Presets)
|
||||
{
|
||||
@@ -166,11 +170,21 @@ else
|
||||
}
|
||||
<div><small class="text-muted">@preset.shortcut</small></div>
|
||||
</div>
|
||||
<span>
|
||||
<span class="d-none d-lg-block">
|
||||
<button class="btn btn-danger btn-sm" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => PresetOnDeleteClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Delete</button>
|
||||
<button class="btn btn-secondary btn-sm" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnCloneClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Clone</button>
|
||||
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnEditClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Edit</button>
|
||||
</span>
|
||||
<div class="d-lg-none" @onclick:preventDefault @onclick:stopPropagation>
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-three-dots-vertical"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => PresetOnDeleteClick(preset)">Delete</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnCloneClick(preset)">Clone</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnEditClick(preset)">Edit</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user