mirror of
https://github.com/Maassoft/ColorControl.git
synced 2026-10-22 00:22:18 -05:00
fix several issues, add remote controls to new UI
This commit is contained in:
1 parent
c6c7e1ccc0
commit
827157f30f
91 files changed
+4140
-2530
No files matched your search
@@ -48,7 +48,7 @@
|
||||
{
|
||||
<input class="form-check-input me-1" style="font-size: 1rem; visibility: hidden" type="checkbox">
|
||||
}
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#displaySettings">
|
||||
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#displaySettings">
|
||||
Display
|
||||
</button>
|
||||
</span>
|
||||
@@ -264,10 +264,7 @@
|
||||
</label>
|
||||
</div>
|
||||
}
|
||||
<div class="mb-2">
|
||||
<label class="form-range" for="sdrBrightness">SDR brightness (@Preset.SDRBrightness%)</label>
|
||||
<input class="form-range" type="range" min="0" max="100" @bind="Preset.SDRBrightness" id="sdrBrightness">
|
||||
</div>
|
||||
<NullableRangeInput TValue="int" @bind-Value="Preset.SDRBrightness" Id="sdrBrightness" Label="SDR brightness" Unit="%" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="row mb-3">
|
||||
<div class="row mb-2">
|
||||
@if (!Displays.Any())
|
||||
{
|
||||
<span>No displays found</span>
|
||||
@@ -54,6 +54,13 @@ else
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="row mb-3">
|
||||
<div class="form-check form-switch ms-3">
|
||||
<input class="form-check-input" type="checkbox" role="switch" id="usePerUserSettings" @bind="SelectedDisplay.UsePerUserSettings" @bind:after="AfterUsePerUseSettings">
|
||||
<label class="form-check-label" for="usePerUserSettings">Use my settings for this device</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-2 d-flex align-items-center">
|
||||
<h2>Display Associated Color profiles</h2>
|
||||
<div class="d-flex ms-auto">
|
||||
@@ -320,4 +327,19 @@ else
|
||||
|
||||
await SelectedDisplayChanged();
|
||||
}
|
||||
|
||||
private async Task AfterUsePerUseSettings()
|
||||
{
|
||||
if (SelectedDisplay == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var result = await _rpcClientService.CallAsync<bool>("ColorProfileService", "SetUsePerUserDisplayProfiles", SelectedDisplay.DeviceName, SelectedDisplay.UsePerUserSettings);
|
||||
|
||||
if (result)
|
||||
{
|
||||
await RefreshDisplays();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,272 +9,235 @@
|
||||
@rendermode Constants.RenderMode
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject AppState AppState
|
||||
@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-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>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @onclick="() => IsVisible = false"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="row g-1 needs-validation @WasValidated" novalidate>
|
||||
@if (Preset == null)
|
||||
{
|
||||
<div>Loading...</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
if (RecentApps != null)
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="recentApp">Recent app</label>
|
||||
<select class="form-select" id="recentApp" @bind="RecentAppFilename" @bind:after="RecentAppSelected">
|
||||
@foreach (var app in RecentApps)
|
||||
{
|
||||
<option value="@app.Filename" title="@app.Path">@app.FriendlyName</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
}
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="name">Name</label>
|
||||
<input class="form-control" id="name" type="text" @bind="Preset.name" required />
|
||||
</div>
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox" @bind="Preset.ShowInQuickAccess" id="showInQuickAccess">
|
||||
<label class="form-check-label" for="showInQuickAccess">
|
||||
Show in Quick Access
|
||||
</label>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="path">File/URI</label>
|
||||
<div class="input-group mb-2">
|
||||
<input class="form-control" id="path" type="text" @bind="Preset.Path" required />
|
||||
<button type="button" class="btn btn-primary" @onclick="SelectFileOnClick" disabled="@SelectingFile">
|
||||
<span class="@(SelectingFile ? "spinner-border spinner-border-sm" : "")" aria-hidden="true"></span>
|
||||
<span role="status">Select...</span>
|
||||
</button>
|
||||
</div>
|
||||
<div id="selectFileHelp" class="form-text">
|
||||
Selecting a file opens a file dialog outside of the browser on the system where the ColorControl application is running.
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="parameters">Parameters</label>
|
||||
<input class="form-control" id="parameters" type="text" @bind="Preset.Parameters" />
|
||||
</div>
|
||||
<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>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @onclick="() => IsVisible = false"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="row g-1 needs-validation @WasValidated" novalidate>
|
||||
@if (Preset == null)
|
||||
{
|
||||
<div>Loading...</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
if (RecentApps != null)
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="recentApp">Recent app</label>
|
||||
<select class="form-select" id="recentApp" @bind="RecentAppFilename" @bind:after="RecentAppSelected">
|
||||
@foreach (var app in RecentApps)
|
||||
{
|
||||
<option value="@app.Filename" title="@app.Path">@app.FriendlyName</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
}
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="name">Name</label>
|
||||
<input class="form-control" id="name" type="text" @bind="Preset.name" required />
|
||||
</div>
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox" @bind="Preset.ShowInQuickAccess" id="showInQuickAccess">
|
||||
<label class="form-check-label" for="showInQuickAccess">
|
||||
Show in Quick Access
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="accordion accordion-flush" id="presetAccordion">
|
||||
<StartProgramInput @bind-Path="Preset.Path" @bind-Parameters="Preset.Parameters" @bind-Path:after="PathChanged"></StartProgramInput>
|
||||
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#advancedSettings">
|
||||
Advanced settings
|
||||
</button>
|
||||
</h2>
|
||||
<div id="advancedSettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
|
||||
<div class="accordion-body">
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox" @bind="Preset.RunAsAdministrator" id="runAsAdministrator">
|
||||
<label class="form-check-label" for="runAsAdministrator">
|
||||
Run as administrator
|
||||
</label>
|
||||
</div>
|
||||
<FlagsInput Label="Process affinity" Labels="CpuAffinityLabels" @bind-Value="Preset.ProcessAffinityMask"></FlagsInput>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="priority">Process priority</label>
|
||||
<select class="form-select" id="priority" @bind="Preset.GameProcessPriorityClass">
|
||||
@foreach (var priority in Enum.GetValues<GamePriorityClass>())
|
||||
{
|
||||
<option value="@priority.ToString()">@priority.GetDescription()</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#autoApplySettings">
|
||||
Auto-apply settings
|
||||
</button>
|
||||
</h2>
|
||||
<div id="autoApplySettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
|
||||
<div class="accordion-body">
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox" @bind="Preset.AutoSettings.AllowAutoApply" id="autoApply">
|
||||
<label class="form-check-label" for="autoApply">
|
||||
Automatically execute preset when process starts
|
||||
</label>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="autoApplyAction">Auto-apply action</label>
|
||||
<select class="form-select" id="priority" @bind="Preset.AutoSettings.ProcessAutoAction">
|
||||
@foreach (var action in Enum.GetValues<ProcessAutoAction>())
|
||||
{
|
||||
<option value="@action.ToString()">@action.GetDescription()</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#steps">
|
||||
Steps
|
||||
</button>
|
||||
</h2>
|
||||
<div id="steps" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
|
||||
<div class="accordion-body">
|
||||
<StepsInput Label="Pre-launch" @bind-Steps="Preset.PreLaunchSteps" TPreset="GamePreset"></StepsInput>
|
||||
<StepsInput Label="Post-launch" @bind-Steps="Preset.PostLaunchSteps" TPreset="GamePreset"></StepsInput>
|
||||
<StepsInput Label="Finalize" @bind-Steps="Preset.FinalizeSteps" TPreset="GamePreset"></StepsInput>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion accordion-flush" id="presetAccordion">
|
||||
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#advancedSettings">
|
||||
Advanced settings
|
||||
</button>
|
||||
</h2>
|
||||
<div id="advancedSettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
|
||||
<div class="accordion-body">
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox" @bind="Preset.RunAsAdministrator" id="runAsAdministrator">
|
||||
<label class="form-check-label" for="runAsAdministrator">
|
||||
Run as administrator
|
||||
</label>
|
||||
</div>
|
||||
<FlagsInput Label="Process affinity" Labels="CpuAffinityLabels" @bind-Value="Preset.ProcessAffinityMask"></FlagsInput>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="priority">Process priority</label>
|
||||
<select class="form-select" id="priority" @bind="Preset.GameProcessPriorityClass">
|
||||
@foreach (var priority in Enum.GetValues<GamePriorityClass>())
|
||||
{
|
||||
<option value="@priority.ToString()">@priority.GetDescription()</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#autoApplySettings">
|
||||
Auto-apply settings
|
||||
</button>
|
||||
</h2>
|
||||
<div id="autoApplySettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
|
||||
<div class="accordion-body">
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox" @bind="Preset.AutoSettings.AllowAutoApply" id="autoApply">
|
||||
<label class="form-check-label" for="autoApply">
|
||||
Automatically execute preset when process starts
|
||||
</label>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="autoApplyAction">Auto-apply action</label>
|
||||
<select class="form-select" id="priority" @bind="Preset.AutoSettings.ProcessAutoAction">
|
||||
@foreach (var action in Enum.GetValues<ProcessAutoAction>())
|
||||
{
|
||||
<option value="@action.ToString()">@action.GetDescription()</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#steps">
|
||||
Steps
|
||||
</button>
|
||||
</h2>
|
||||
<div id="steps" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
|
||||
<div class="accordion-body">
|
||||
<StepsInput Label="Pre-launch" @bind-Steps="Preset.PreLaunchSteps" TPreset="GamePreset"></StepsInput>
|
||||
<StepsInput Label="Post-launch" @bind-Steps="Preset.PostLaunchSteps" TPreset="GamePreset"></StepsInput>
|
||||
<StepsInput Label="Finalize" @bind-Steps="Preset.FinalizeSteps" TPreset="GamePreset"></StepsInput>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
}
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @onclick="() => IsVisible = false">Close</button>
|
||||
<button type="submit" class="btn btn-primary" data-bs-dismiss="@(IsValid() ? "modal" : "")" @onclick="ApplyClick">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
}
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @onclick="() => IsVisible = false">Close</button>
|
||||
<button type="submit" class="btn btn-primary" data-bs-dismiss="@(IsValid() ? "modal" : "")" @onclick="ApplyClick">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private GamePreset? Preset;
|
||||
private GamePreset? Preset;
|
||||
|
||||
[Parameter]
|
||||
public GamePreset? PresetParam { get; set; }
|
||||
[Parameter]
|
||||
public GamePreset? PresetParam { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<GamePreset, Task>? AfterApply { get; set; }
|
||||
[Parameter]
|
||||
public Func<GamePreset, Task>? AfterApply { get; set; }
|
||||
|
||||
public List<string> CpuAffinityLabels = Enumerable.Range(0, Environment.ProcessorCount).Select(i => $"CPU #{i}").ToList();
|
||||
private List<GameApp>? RecentApps;
|
||||
private string RecentAppFilename = "";
|
||||
public List<string> CpuAffinityLabels = Enumerable.Range(0, Environment.ProcessorCount).Select(i => $"CPU #{i}").ToList();
|
||||
private List<GameApp>? RecentApps;
|
||||
private string RecentAppFilename = "";
|
||||
|
||||
public bool SelectingFile { get; set; }
|
||||
public bool SelectingFile { get; set; }
|
||||
|
||||
private string WasValidated = "";
|
||||
private bool IsVisible;
|
||||
private string WasValidated = "";
|
||||
private bool IsVisible;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
if (PresetParam == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
if (PresetParam == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Preset = PresetParam.CloneWithId();
|
||||
Preset = PresetParam.CloneWithId();
|
||||
|
||||
if (Preset.id == 0)
|
||||
{
|
||||
RecentApps = await _rpcClientService.CallAsync<List<GameApp>>("GameService", "GetMruApps", true);
|
||||
RecentAppFilename = "";
|
||||
if (Preset.id == 0)
|
||||
{
|
||||
RecentApps = await _rpcClientService.CallAsync<List<GameApp>>("GameService", "GetMruApps", true);
|
||||
RecentAppFilename = "";
|
||||
|
||||
if (RecentApps != null)
|
||||
{
|
||||
RecentApps.Insert(0, new GameApp { Filename = "", FriendlyName = "Choose a recent app to fill values" });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RecentApps = null;
|
||||
}
|
||||
}
|
||||
if (RecentApps != null)
|
||||
{
|
||||
RecentApps.Insert(0, new GameApp { Filename = "", FriendlyName = "Choose a recent app to fill values" });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RecentApps = null;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ApplyClick(MouseEventArgs e)
|
||||
{
|
||||
if (Preset == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
private async Task ApplyClick(MouseEventArgs e)
|
||||
{
|
||||
if (Preset == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsValid())
|
||||
{
|
||||
WasValidated = "was-validated";
|
||||
return;
|
||||
}
|
||||
if (!IsValid())
|
||||
{
|
||||
WasValidated = "was-validated";
|
||||
return;
|
||||
}
|
||||
|
||||
var result = await _rpcClientService.CallAsync<bool>("GameService", "UpdatePreset", Preset);
|
||||
var result = await _rpcClientService.CallAsync<bool>("GameService", "UpdatePreset", Preset);
|
||||
|
||||
if (AfterApply != null)
|
||||
{
|
||||
await AfterApply.Invoke(Preset);
|
||||
}
|
||||
if (AfterApply != null)
|
||||
{
|
||||
await AfterApply.Invoke(Preset);
|
||||
}
|
||||
|
||||
IsVisible = false;
|
||||
}
|
||||
IsVisible = false;
|
||||
}
|
||||
|
||||
private bool IsValid()
|
||||
{
|
||||
return !string.IsNullOrEmpty(Preset?.name) && !string.IsNullOrEmpty(Preset?.Path);
|
||||
}
|
||||
private bool IsValid()
|
||||
{
|
||||
return !string.IsNullOrEmpty(Preset?.name) && !string.IsNullOrEmpty(Preset?.Path);
|
||||
}
|
||||
|
||||
private async Task SelectFileOnClick(MouseEventArgs e)
|
||||
{
|
||||
if (Preset == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
private void RecentAppSelected()
|
||||
{
|
||||
if (RecentApps == null || Preset == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SelectingFile = true;
|
||||
try
|
||||
{
|
||||
var options = new RpcUiClientOptions
|
||||
{
|
||||
Timeout = 60000
|
||||
};
|
||||
var app = RecentApps.FirstOrDefault(a => a.Filename == RecentAppFilename);
|
||||
|
||||
var result = await _rpcClientService.CallWithOptionsAsync<string>("GameService", "SelectPath", options);
|
||||
if (app != null)
|
||||
{
|
||||
Preset.name = app.FriendlyName;
|
||||
Preset.Path = app.Path;
|
||||
}
|
||||
}
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
Preset.Path = result;
|
||||
if (string.IsNullOrEmpty(Preset.name))
|
||||
{
|
||||
Preset.name = Path.GetFileNameWithoutExtension(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
SelectingFile = false;
|
||||
}
|
||||
}
|
||||
private void ModalOnFocus(FocusEventArgs e)
|
||||
{
|
||||
if (!IsVisible)
|
||||
{
|
||||
WasValidated = "";
|
||||
IsVisible = true;
|
||||
}
|
||||
}
|
||||
private void PathChanged()
|
||||
{
|
||||
if (Preset == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
private void RecentAppSelected()
|
||||
{
|
||||
if (RecentApps == null || Preset == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var app = RecentApps.FirstOrDefault(a => a.Filename == RecentAppFilename);
|
||||
|
||||
if (app != null)
|
||||
{
|
||||
Preset.name = app.FriendlyName;
|
||||
Preset.Path = app.Path;
|
||||
}
|
||||
}
|
||||
|
||||
private void ModalOnFocus(FocusEventArgs e)
|
||||
{
|
||||
if (!IsVisible)
|
||||
{
|
||||
WasValidated = "";
|
||||
IsVisible = true;
|
||||
}
|
||||
}
|
||||
if (Preset.Path != null && string.IsNullOrEmpty(Preset.name))
|
||||
{
|
||||
Preset.name = Path.GetFileNameWithoutExtension(Preset.Path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
@rendermode Constants.RenderMode
|
||||
|
||||
<FieldsModal ModalId="@ModalId" Fields="Fields" Title="@Title" OnConfirm="ConfirmClick"></FieldsModal>
|
||||
<FieldsModal ModalId="@ModalId" Fields="Fields" Title="@Title" OnConfirm="ConfirmClick" NextModalId="@NextModalId"></FieldsModal>
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
@@ -19,6 +19,9 @@
|
||||
[Parameter]
|
||||
public string ModalId { get; set; } = "confirmModal";
|
||||
|
||||
[Parameter]
|
||||
public string? NextModalId { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public List<FieldDefinition>? ExtraFields { get; set; }
|
||||
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
|
||||
@rendermode Constants.RenderMode
|
||||
|
||||
@inject NavigationManager _navigationManager;
|
||||
|
||||
@if (Fields == null)
|
||||
{
|
||||
return;
|
||||
@@ -106,16 +104,7 @@
|
||||
{
|
||||
var fieldId = $"fld_{Random.Shared.Next()}";
|
||||
|
||||
<div class="mb-2">
|
||||
<label class="form-range" for="@fieldId">@field.Label (Current value: @field.IntValue)</label>
|
||||
<input class="form-range" type="range" min="@((int)field.MinValue)" max="@((int)field.MaxValue)" @bind="field.IntValue" id="@fieldId">
|
||||
@if (field.SubLabel != null)
|
||||
{
|
||||
<div id="@(fieldId)_help" class="form-text">
|
||||
@field.SubLabel
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<RangeInput TValue="int" @bind-Value="field.IntValue" Id="@fieldId" Label="@field.Label" Min="@((int)field.MinValue)" Max="@((int)field.MaxValue)" SubLabel="@field.SubLabel" />
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
@using ColorControl.Shared.Common;
|
||||
|
||||
@rendermode Constants.RenderMode
|
||||
|
||||
@inject NavigationManager _navigationManager;
|
||||
@inject JSHelper jsHelper;
|
||||
|
||||
<div class="modal" id="@ModalId" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalFocus">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
@@ -41,6 +40,9 @@
|
||||
[Parameter]
|
||||
public string? ModalId { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? NextModalId { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? ConfirmCaption { get; set; } = "Yes";
|
||||
|
||||
@@ -120,8 +122,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
private void OnClose(MouseEventArgs e)
|
||||
private async Task OnClose(MouseEventArgs e)
|
||||
{
|
||||
IsVisible = false;
|
||||
|
||||
if (!string.IsNullOrEmpty(NextModalId))
|
||||
{
|
||||
await jsHelper.OpenModal(NextModalId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.EventDispatcher
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
|
||||
@* @rendermode Constants.RenderMode *@
|
||||
|
||||
@typeparam TValue where TValue : struct, IComparable, IFormattable, IConvertible, IComparable<TValue>, IEquatable<TValue>
|
||||
|
||||
@if (Id != null)
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label @(ExtraLabelStyling ?? "")" for="@Id">@Label (@Value@Unit)</label>
|
||||
<input class="form-range" type="range" min="@(Values?.Min() ?? Min)" max="@(Values?.Max() ?? Max)" @bind="Value" id="@Id" @oninput="e => RangeOnChange(e)">
|
||||
</div>
|
||||
}
|
||||
|
||||
@code {
|
||||
|
||||
[Parameter]
|
||||
public TValue? Value { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? Label { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? Unit { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? Id { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public TValue? Min { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public TValue? Max { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public List<TValue>? Values { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<TValue?> ValueChanged { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<TValue?, string>? LabelStylingFunc { get; set; }
|
||||
|
||||
private string? ExtraLabelStyling;
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
ExtraLabelStyling = LabelStylingFunc?.Invoke(Value);
|
||||
Min ??= (TValue)Convert.ChangeType(0, typeof(TValue));
|
||||
Max ??= (TValue)Convert.ChangeType(100, typeof(TValue));
|
||||
}
|
||||
|
||||
private async Task RangeOnChange(ChangeEventArgs eventArgs)
|
||||
{
|
||||
if (typeof(TValue) == typeof(int))
|
||||
{
|
||||
Value = (TValue)Convert.ChangeType(int.Parse(eventArgs.Value?.ToString() ?? "0"), typeof(TValue));
|
||||
|
||||
}
|
||||
else if (typeof(TValue) == typeof(uint))
|
||||
{
|
||||
Value = (TValue)Convert.ChangeType(uint.Parse(eventArgs.Value?.ToString() ?? "0"), typeof(TValue));
|
||||
}
|
||||
|
||||
await ValueChanged.InvokeAsync(Value);
|
||||
|
||||
ExtraLabelStyling = LabelStylingFunc?.Invoke(Value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.EventDispatcher
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
|
||||
@* @rendermode Constants.RenderMode *@
|
||||
|
||||
@typeparam TValue where TValue : struct, IComparable, IFormattable, IConvertible, IComparable<TValue>, IEquatable<TValue>
|
||||
|
||||
@if (Id != null)
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label @(ExtraLabelStyling ?? "")" for="@Id">@Label: @GetValueDisplay()@Unit</label>
|
||||
<input class="form-range" type="range" step="@Step" min="@(Values?.Min() ?? Min)" max="@(Values?.Max() ?? Max)" @bind="Value" id="@Id" @oninput="e => RangeOnChange(e)">
|
||||
@if (SubLabel != null)
|
||||
{
|
||||
<div id="@(Id)_help" class="form-text">
|
||||
@SubLabel
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@code {
|
||||
|
||||
[Parameter]
|
||||
public TValue Value { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? Label { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? SubLabel { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? Unit { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? UnitString { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? Id { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public TValue? Min { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public TValue? Max { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public TValue? Step { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public List<TValue>? Values { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<TValue> ValueChanged { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<TValue, string>? LabelStylingFunc { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<TValue, string>? CustomLabelFunc { get; set; }
|
||||
|
||||
private string? ExtraLabelStyling;
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
ExtraLabelStyling = LabelStylingFunc?.Invoke(Value);
|
||||
Min ??= (TValue)Convert.ChangeType(0, typeof(TValue));
|
||||
Max ??= (TValue)Convert.ChangeType(100, typeof(TValue));
|
||||
Step ??= (TValue)Convert.ChangeType(1, typeof(TValue));
|
||||
}
|
||||
|
||||
private string? GetValueDisplay()
|
||||
{
|
||||
if (CustomLabelFunc != null)
|
||||
{
|
||||
return CustomLabelFunc(Value);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(UnitString))
|
||||
{
|
||||
return Value.ToString();
|
||||
}
|
||||
|
||||
if (Value is uint uintValue)
|
||||
{
|
||||
return uintValue.ToUnitString(UnitString);
|
||||
}
|
||||
if (Value is int intValue)
|
||||
{
|
||||
return intValue.ToSignedUnitString(UnitString);
|
||||
}
|
||||
|
||||
return Value.ToString();
|
||||
}
|
||||
|
||||
private async Task RangeOnChange(ChangeEventArgs eventArgs)
|
||||
{
|
||||
if (typeof(TValue) == typeof(int))
|
||||
{
|
||||
Value = (TValue)Convert.ChangeType(int.Parse(eventArgs.Value?.ToString() ?? "0"), typeof(TValue));
|
||||
|
||||
}
|
||||
else if (typeof(TValue) == typeof(uint))
|
||||
{
|
||||
Value = (TValue)Convert.ChangeType(uint.Parse(eventArgs.Value?.ToString() ?? "0"), typeof(TValue));
|
||||
}
|
||||
|
||||
await ValueChanged.InvokeAsync(Value);
|
||||
|
||||
ExtraLabelStyling = LabelStylingFunc?.Invoke(Value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,7 +19,8 @@
|
||||
<button class="btn btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
@foreach (var modifier in Modifiers) {
|
||||
@foreach (var modifier in Modifiers)
|
||||
{
|
||||
<li>
|
||||
<a class="dropdown-item" @onclick="() => ModifierOnClick(modifier.Key)">
|
||||
<input class="form-check-input me-2" type="checkbox" id="flexCheckChecked" checked="@modifier.Value" @onchange="(e) => ModifierKeyChanged(e, modifier.Key)">@modifier.Key
|
||||
@@ -44,9 +45,9 @@
|
||||
private Dictionary<string, bool> Modifiers = new() {
|
||||
{ "Shift", false },
|
||||
{ "Control", false },
|
||||
{ "Alt", false },
|
||||
{ "Win", false }
|
||||
};
|
||||
{ "Alt", false },
|
||||
{ "Win", false }
|
||||
};
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.EventDispatcher
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="path">File/URI</label>
|
||||
<div class="input-group mb-2">
|
||||
<input class="form-control" id="path" type="text" @bind="Path" required @onblur="() => PathChanged.InvokeAsync(Path)" />
|
||||
<button type="button" class="btn btn-primary" @onclick="SelectFileOnClick" disabled="@SelectingFile">
|
||||
<span class="@(SelectingFile ? "spinner-border spinner-border-sm" : "")" aria-hidden="true"></span>
|
||||
<span role="status">Select...</span>
|
||||
</button>
|
||||
</div>
|
||||
<div id="selectFileHelp" class="form-text">
|
||||
Selecting a file opens a file dialog outside of the browser on the system where the ColorControl application is running.
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="parameters">Parameters</label>
|
||||
<input class="form-control" id="parameters" type="text" @bind="Parameters" @bind:after="() => ParametersChanged.InvokeAsync(Parameters)" />
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
public string? Path { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? Parameters { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<string?> PathChanged { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<string?> ParametersChanged { get; set; }
|
||||
|
||||
private bool SelectingFile;
|
||||
|
||||
private async Task SelectFileOnClick(MouseEventArgs e)
|
||||
{
|
||||
SelectingFile = true;
|
||||
try
|
||||
{
|
||||
var options = new RpcUiClientOptions
|
||||
{
|
||||
Timeout = 60000
|
||||
};
|
||||
|
||||
var result = await _rpcClientService.CallWithOptionsAsync<string>("GameService", "SelectPath", options);
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
Path = result;
|
||||
await PathChanged.InvokeAsync(Path);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
SelectingFile = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,22 +12,21 @@
|
||||
@if (Label != null && Steps != null)
|
||||
{
|
||||
var index = 0;
|
||||
|
||||
|
||||
<div class="mb-2">
|
||||
<div class="d-flex align-items-center">
|
||||
<label class="form-label" for="name">@Label</label>
|
||||
<div class="d-flex ms-auto">
|
||||
@if (NvPresets != null || SamsungPresets != null || CustomItems != null)
|
||||
@if (NvPresets != null || SamsungPresets != null || LgPresets != null || CustomItems != null)
|
||||
{
|
||||
<div class="dropdown dropend mb-1">
|
||||
<button type="button" class="btn btn-primary btn-sm dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false" data-bs-auto-close="outside">Add</button>
|
||||
<form class="dropdown-menu p-4 dropdown-menu-end" style="width: 200px">
|
||||
<form class="dropdown-menu p-4 dropdown-menu-end" style="width: 360px">
|
||||
@if (NvPresets != null && NvPresetsEnabled)
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="nvPreset">NVIDIA preset</label>
|
||||
<select class="form-select" id="nvPreset" @bind="NvPresetToAddId">
|
||||
<option value="">Select preset</option>
|
||||
<select class="form-select" multiple id="nvPreset" @onchange="NvPresetChange">
|
||||
@foreach (var preset in NvPresets)
|
||||
{
|
||||
<option value="@preset.id">@preset.IdOrName</option>
|
||||
@@ -77,6 +76,9 @@
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
<StartProgramInput @bind-Path="StartProgramPath" @bind-Parameters="StartProgramParameters"></StartProgramInput>
|
||||
|
||||
@if (ActionItems != null)
|
||||
{
|
||||
foreach (var customItem in ActionItems)
|
||||
@@ -85,15 +87,15 @@
|
||||
<label class="form-label" for="advancedCustomItem">@customItem.Name</label>
|
||||
<select class="form-select" id="advancedCustomItem" @bind="customItem.SelectedActionName" @bind:after="() => AdvancedItemChanged(customItem)">
|
||||
<option value="">Select action</option>
|
||||
@foreach (var keyValue in customItem.GroupedActions)
|
||||
{
|
||||
<optgroup label="@keyValue.Key">
|
||||
@foreach (var item in keyValue.Value)
|
||||
{
|
||||
<option value="@item.Name">@item.Title</option>
|
||||
}
|
||||
</optgroup>
|
||||
}
|
||||
@foreach (var keyValue in customItem.GroupedActions)
|
||||
{
|
||||
<optgroup label="@keyValue.Key">
|
||||
@foreach (var item in keyValue.Value)
|
||||
{
|
||||
<option value="@item.Name">@item.Title</option>
|
||||
}
|
||||
</optgroup>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<FieldDefs Fields="ActionFields"></FieldDefs>
|
||||
@@ -126,10 +128,7 @@
|
||||
<button type="button" class="btn btn-primary btn-sm" data-bs-dismiss="dropdown" title="Move right" disabled="@(stepIndex == PresetSteps.Count - 1)" @onclick="() => MoveRightOnClick(stepIndex)">></button>
|
||||
<button type="button" class="btn btn-primary btn-sm" data-bs-dismiss="dropdown" @onclick="() => RemoveOnClick(stepIndex)">Remove</button>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="delay">Delay (@(step.Delay)ms)</label>
|
||||
<input class="form-range" id="delay" type="range" min="0" max="10000" step="50" @bind="step.Delay" @bind:after="() => AfterDelayChanged()">
|
||||
</div>
|
||||
<RangeInput TValue="int" @bind-Value="step.Delay" Id="delay" Label="Delay" Unit="ms" Max="10000" @bind-Value:after="() => AfterDelayChanged()" />
|
||||
</form>
|
||||
|
||||
index++;
|
||||
@@ -212,7 +211,6 @@
|
||||
private List<SamsungPreset>? SamsungPresets { get; set; }
|
||||
private List<LgPreset>? LgPresets { get; set; }
|
||||
|
||||
private int? NvPresetToAddId { get; set; }
|
||||
private int? SamsungPresetToAddId { get; set; }
|
||||
private int? LgPresetToAddId { get; set; }
|
||||
|
||||
@@ -222,6 +220,11 @@
|
||||
|
||||
private List<FieldDefinition>? ActionFields;
|
||||
|
||||
private string? StartProgramPath;
|
||||
private string? StartProgramParameters;
|
||||
|
||||
private List<int> NvPresetsToAdd = [];
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
if (Steps != null)
|
||||
@@ -295,7 +298,7 @@
|
||||
|
||||
private async Task AddPresetOnClick()
|
||||
{
|
||||
AddPreset(NvPresets, "NvPreset", NvPresetToAddId);
|
||||
NvPresetsToAdd.ForEach(id => AddPreset(NvPresets, "NvPreset", id));
|
||||
AddPreset(SamsungPresets, "SamsungPreset", SamsungPresetToAddId);
|
||||
AddPreset(LgPresets, "LgPreset", LgPresetToAddId);
|
||||
|
||||
@@ -328,6 +331,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
AddStartProgram();
|
||||
|
||||
await EmitSteps();
|
||||
}
|
||||
|
||||
@@ -343,6 +348,22 @@
|
||||
PresetSteps.Add(PresetStep.Parse($"{type}({preset.name})"));
|
||||
}
|
||||
|
||||
private void AddStartProgram()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(StartProgramPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var path = StartProgramPath;
|
||||
if (!string.IsNullOrWhiteSpace(StartProgramParameters))
|
||||
{
|
||||
path += $"; {string.Join("; ", StartProgramParameters.Split(' '))}";
|
||||
}
|
||||
|
||||
PresetSteps.Add(PresetStep.Parse($"StartProgram({path})"));
|
||||
}
|
||||
|
||||
private async Task EmitSteps()
|
||||
{
|
||||
if (Steps != null)
|
||||
@@ -427,4 +448,13 @@
|
||||
|
||||
return field;
|
||||
}
|
||||
|
||||
private void NvPresetChange(ChangeEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.Value is IEnumerable<string> items)
|
||||
{
|
||||
NvPresetsToAdd.Clear();
|
||||
NvPresetsToAdd.AddRange(items.Select(i => int.Parse(i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,14 @@
|
||||
|
||||
@if (Triggers != null)
|
||||
{
|
||||
if (!Triggers.Any())
|
||||
{
|
||||
<div>
|
||||
<label class="form-label">No triggers found. Click to add:</label>
|
||||
<button class="btn btn-primary btn-sm" type="button" @onclick="() => Triggers.Add(new PresetTrigger())">Add</button>
|
||||
</div>
|
||||
}
|
||||
|
||||
foreach (var trigger in Triggers)
|
||||
{
|
||||
<div class="mb-2">
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
@rendermode Constants.RenderMode
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject AppState AppState
|
||||
@inject NavigationManager _navigationManager;
|
||||
@inject NotificationService _notificationService
|
||||
@inject ProtectedSessionStorage _sessionStorage
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
@rendermode Constants.RenderMode
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject AppState AppState
|
||||
@inject NavigationManager _navigationManager;
|
||||
@inject IHttpContextAccessor _httpContextAccessor
|
||||
|
||||
|
||||
@@ -3,14 +3,13 @@
|
||||
@using ColorControl.UI.Services
|
||||
@using NvAPIWrapper.Native.Display
|
||||
@using ColorControl.Shared.Common
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
|
||||
@rendermode Constants.RenderMode
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject AppState AppState
|
||||
@inject NavigationManager _navigationManager;
|
||||
|
||||
<div class="modal" id="deviceModal" tabindex="-1">
|
||||
<div class="modal" id="lgDeviceModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
@@ -73,7 +72,7 @@
|
||||
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#deviceOptions">
|
||||
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#deviceOptions">
|
||||
Device options
|
||||
</button>
|
||||
</h2>
|
||||
@@ -165,6 +164,10 @@
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
@if (Device?.IsCustom == true)
|
||||
{
|
||||
<button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#removeDeviceModal">Remove</button>
|
||||
}
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" @onclick="ApplyClick">Save</button>
|
||||
</div>
|
||||
@@ -172,6 +175,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ConfirmModal ModalId="removeDeviceModal" Title="Remove device" Message="Are you sure you want to remove this device?" OnConfirm="DeviceOnRemoveConfirmed" NextModalId="lgDeviceModal" />
|
||||
|
||||
@code {
|
||||
private LgDeviceDto? _device;
|
||||
|
||||
@@ -208,6 +213,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DeviceOnRemoveConfirmed()
|
||||
{
|
||||
if (Device == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var result = await _rpcClientService.CallAsync<bool>("LgService", "RemoveDevice", Device);
|
||||
|
||||
if (AfterApply != null)
|
||||
{
|
||||
await AfterApply.Invoke(Device);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task TestConnectionClick(MouseEventArgs e)
|
||||
{
|
||||
IsTestingConnection = true;
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
@rendermode Constants.RenderMode
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject AppState AppState
|
||||
@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-scrollable modal-dialog-centered">
|
||||
@@ -74,7 +72,7 @@
|
||||
</h2>
|
||||
<div id="steps" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
|
||||
<div class="accordion-body">
|
||||
<StepsInput Label="Steps" @bind-Steps="Preset.steps" CustomItems="CustomItems" ActionItems="CustomActions" LgPresetsEnabled="false" TPreset="LgPreset"></StepsInput>
|
||||
<StepsInput Label="Steps" @bind-Steps="Preset.Steps" CustomItems="CustomItems" ActionItems="CustomActions" LgPresetsEnabled="false" TPreset="LgPreset"></StepsInput>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="description">Description</label>
|
||||
<textarea class="form-control" id="description" type="text" @bind="Preset.Description" />
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.NVIDIA
|
||||
@using ColorControl.Shared.Contracts.LG
|
||||
@using ColorControl.UI.Components.Pages.RemoteControl
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using ColorControl.UI.Generics
|
||||
@@ -22,6 +23,9 @@
|
||||
<div class="d-flex align-items-center">
|
||||
<h1>LG controller</h1>
|
||||
<div class="d-flex ms-auto">
|
||||
<button type="button" class="btn btn-sm btn-primary me-2" data-bs-toggle="offcanvas" data-bs-target="#rcCanvasRight" aria-controls="rcCanvasRight">
|
||||
Remote control
|
||||
</button>
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-sm btn-primary dropdown-toggle me-2" data-bs-toggle="dropdown" aria-expanded="false" data-bs-auto-close="outside">
|
||||
Buttons
|
||||
@@ -64,7 +68,18 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<LgDevicePage Device="CurrentDevice" />
|
||||
<div class="offcanvas offcanvas-end" style="width: 280px" data-bs-scroll="true" tabindex="-1" id="rcCanvasRight" aria-labelledby="rcCanvasRightRightLabel">
|
||||
<div class="offcanvas-header">
|
||||
<h5 class="offcanvas-title" id="rcCanvasRightRightLabel">Remote control</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="offcanvas-body">
|
||||
<GenericRcPage ServiceName="LgService" Device="CurrentDevice" TDevice="LgDeviceDto" TPreset="LgPreset" TButtonEnum="ButtonType" Presets="RcPresets"></GenericRcPage>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<LgDevicePage Device="CurrentDevice" AfterApply="AfterDeviceApplied" />
|
||||
<LgPresetPage PresetParam="CurrentPreset" Devices="Devices" AfterApply="AfterPresetApplied" />
|
||||
<ConfirmModal Title="Delete preset" Message="Are you sure you want to delete the preset?" OnConfirm="PresetOnDeleteConfirmed" />
|
||||
|
||||
@@ -84,7 +99,7 @@ else
|
||||
}
|
||||
<span role="status">Refresh</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#deviceModal" @onclick="() => AddDeviceOnClick()">
|
||||
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#lgDeviceModal" @onclick="() => AddDeviceOnClick()">
|
||||
Add
|
||||
</button>
|
||||
</div>
|
||||
@@ -119,7 +134,7 @@ else
|
||||
{
|
||||
<button class="btn btn-sm btn-secondary ms-2" @onclick="() => SetAsPrimaryOnClick(device)">Set as primary</button>
|
||||
}
|
||||
<button type="button" class="btn btn-sm btn-primary ms-2" data-bs-toggle="modal" data-bs-target="#deviceModal" @onclick="() => DeviceOnClick(device)">
|
||||
<button type="button" class="btn btn-sm btn-primary ms-2" data-bs-toggle="modal" data-bs-target="#lgDeviceModal" @onclick="() => DeviceOnClick(device)">
|
||||
Settings
|
||||
</button>
|
||||
</div>
|
||||
@@ -221,6 +236,7 @@ else
|
||||
private InvokableActionDto<LgPreset>? CurrentInvokableAction;
|
||||
|
||||
private PresetOrder PresetOrder;
|
||||
private List<LgPreset> RcPresets = [LgPresets.InStartPreset, LgPresets.EzAdjustPreset];
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
@@ -297,7 +313,13 @@ else
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task RefreshDevicesOnClick(MouseEventArgs e)
|
||||
private async Task AfterDeviceApplied(LgDeviceDto device)
|
||||
{
|
||||
Devices = await _rpcClientService.CallAsync<List<LgDeviceDto>>("LgService", "GetDevices");
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task RefreshDevicesOnClick()
|
||||
{
|
||||
IsRefreshDisabled = true;
|
||||
try
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
@rendermode Constants.RenderMode
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject AppState AppState
|
||||
@inject NavigationManager _navigationManager;
|
||||
|
||||
<PageTitle>Logging</PageTitle>
|
||||
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
@rendermode Constants.RenderMode
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject AppState AppState
|
||||
@inject NavigationManager _navigationManager;
|
||||
|
||||
<div class="modal modal-lg" id="ditheringModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.NVIDIA
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using ColorControl.UI.Services;
|
||||
@using NvAPIWrapper.Native.Display
|
||||
@using ColorControl.Shared.Common;
|
||||
@@ -26,14 +27,14 @@
|
||||
<div class="modal-body">
|
||||
<form class="row g-1">
|
||||
|
||||
@if (ProfileNames != null)
|
||||
@if (Profiles != null)
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="profileName">Profile</label>
|
||||
<select class="form-select" id="profileName" @bind="CurrentProfileName" @bind:after="ProfileOnChange">
|
||||
@foreach (var profileName in ProfileNames)
|
||||
@foreach (var profile in Profiles)
|
||||
{
|
||||
<option value="@profileName">@profileName</option>
|
||||
<option value="@profile.Name" title="@string.Join(", ", profile.Apps)">@profile.Name</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
@@ -64,10 +65,8 @@
|
||||
{
|
||||
if (NvSettingConstants.RangeDriverSettings.Contains(setting.SettingId) && setting.Values != null)
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label @(setting.Value != setting.DefaultValue && setting.Value != NvSettingConstants.UnsetDwordValue ? "text-warning fw-bold" : "")" for="@setting.SettingId">@setting.SettingText (@setting.Value)</label>
|
||||
<input class="form-range" type="range" min="@setting.Values.Min(v => v.Value)" max="@setting.Values.Max(v => v.Value)" @bind="setting.Value" id="@setting.SettingId">
|
||||
</div>
|
||||
<RangeInput TValue="uint" @bind-Value="setting.Value" Id="@setting.SettingId.ToString()" Label="@setting.SettingText" Values="setting.Values.Select(v => v.Value).ToList()"
|
||||
LabelStylingFunc="value => RangeLabelStyleFunc(value, setting)" />
|
||||
}
|
||||
else if (setting.Values != null)
|
||||
{
|
||||
@@ -137,7 +136,7 @@
|
||||
|
||||
private List<GroupedSettings>? Groups { get; set; }
|
||||
private List<NvSettingItemDto>? DriverSettings { get; set; }
|
||||
private List<string>? ProfileNames { get; set; }
|
||||
private List<NvProfile>? Profiles { get; set; }
|
||||
private string? CurrentProfileName { get; set; }
|
||||
|
||||
private bool IsVisible { get; set; }
|
||||
@@ -244,7 +243,7 @@
|
||||
|
||||
private async Task OnlyFoundProgramsOnChange()
|
||||
{
|
||||
ProfileNames = await _rpcClientService.CallAsync<List<string>>("NvService", "GetDriverProfileNames", OnlyFoundPrograms);
|
||||
Profiles = await _rpcClientService.CallAsync<List<NvProfile>>("NvService", "GetDriverProfileNames", OnlyFoundPrograms);
|
||||
}
|
||||
|
||||
private async Task OnFocus(FocusEventArgs e)
|
||||
@@ -252,8 +251,18 @@
|
||||
if (!IsVisible)
|
||||
{
|
||||
IsVisible = true;
|
||||
ProfileNames ??= await _rpcClientService.CallAsync<List<string>>("NvService", "GetDriverProfileNames", OnlyFoundPrograms);
|
||||
Profiles ??= await _rpcClientService.CallAsync<List<NvProfile>>("NvService", "GetDriverProfileNames", OnlyFoundPrograms);
|
||||
await LoadDriverSettings();
|
||||
}
|
||||
}
|
||||
|
||||
private void RangeOnChange(ChangeEventArgs eventArgs, NvSettingItemDto s)
|
||||
{
|
||||
s.Value = uint.Parse(eventArgs.Value?.ToString() ?? "0");
|
||||
}
|
||||
|
||||
private string RangeLabelStyleFunc(uint value, NvSettingItemDto setting)
|
||||
{
|
||||
return value != setting.DefaultValue && value != NvSettingConstants.UnsetDwordValue ? "text-warning fw-bold" : "";
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.NVIDIA
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using ColorControl.UI.Services;
|
||||
@using NvAPIWrapper.Native.Display
|
||||
@using ColorControl.Shared.Common;
|
||||
@@ -53,47 +54,33 @@
|
||||
|
||||
@if (OverclockSettings.Type is NvGpuOcType.Curve or NvGpuOcType.Offset)
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="coreOffset">Core offset in MHz: @OverclockSettings.GraphicsOffsetMHz.ToSignedUnitString() (Min: @GpuInfo.MinCoreDeltaInMHz.ToSignedUnitString(), Max: @GpuInfo.MaxCoreDeltaInMHz.ToSignedUnitString())</label>
|
||||
<input class="form-range" type="range" step="@GpuInfo.MinCoreStepInMHz" min="@GpuInfo.CorrectedMinCoreDeltaInMHz" max="@GpuInfo.MaxCoreDeltaInMHz" @bind="OverclockSettings.GraphicsOffsetMHz" id="coreOffset">
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="memoryOffset">Memory offset in MHz: @OverclockSettings.MemoryOffsetMHz.ToSignedUnitString() (Min: @GpuInfo.MinMemoryDeltaInMHz.ToSignedUnitString(), Max: @GpuInfo.MaxMemoryDeltaInMHz.ToSignedUnitString())</label>
|
||||
<input class="form-range" type="range" step="1" min="@GpuInfo.MinMemoryDeltaInMHz" max="@GpuInfo.MaxMemoryDeltaInMHz" @bind="OverclockSettings.MemoryOffsetMHz" id="memoryOffset">
|
||||
</div>
|
||||
<RangeInput TValue="int" Min="@GpuInfo.CorrectedMinCoreDeltaInMHz" Max="@GpuInfo.MaxCoreDeltaInMHz" Step="@GpuInfo.MinCoreStepInMHz" @bind-Value="OverclockSettings.GraphicsOffsetMHz" Id="coreOffset" Label="Core offset" UnitString="MHz"
|
||||
SubLabel="@($"Min: {GpuInfo.MinCoreDeltaInMHz.ToSignedUnitString()}, Max: {GpuInfo.MaxCoreDeltaInMHz.ToSignedUnitString()}")" />
|
||||
<RangeInput TValue="int" Min="@GpuInfo.MinMemoryDeltaInMHz" Max="@GpuInfo.MaxMemoryDeltaInMHz" @bind-Value="OverclockSettings.MemoryOffsetMHz" Id="memoryOffset" Label="Memory offset" UnitString="MHz"
|
||||
SubLabel="@($"Min: {GpuInfo.MinMemoryDeltaInMHz.ToSignedUnitString()}, Max: {GpuInfo.MaxMemoryDeltaInMHz.ToSignedUnitString()}")" />
|
||||
}
|
||||
@if (OverclockSettings.Type is NvGpuOcType.Curve or NvGpuOcType.BoostLock)
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="maxCoreFrequency">Maximum core frequency MHz: @OverclockSettings.MaximumFrequencyMHz.ToUnitString() (Min: @GpuInfo.MinCurveFrequencyInMHz.ToUnitString(), Max: @GpuInfo.MaxCurveFrequencyInMHz.ToUnitString())</label>
|
||||
<input class="form-range" type="range" step="@GpuInfo.MinCoreStepInMHz" min="@GpuInfo.MinCurveFrequencyInMHz" max="@GpuInfo.MaxCurveFrequencyInMHz" @bind="OverclockSettings.MaximumFrequencyMHz" id="maxCoreFrequency">
|
||||
<div id="maxCoreFrequencyHelp" class="form-text">Setting this value to anything else than '0' will limit the maximum frequency of the curve.</div>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="maxCoreVoltage">Maximum core voltage in millivolt: @OverclockSettings.MaximumVoltageMv.ToUnitString("mv") (Min: @GpuInfo.MinCurveVoltageInMv.ToUnitString("mv"), Max: @GpuInfo.MaxCurveVoltageInMv.ToUnitString("mv"))</label>
|
||||
<input class="form-range" type="range" step="5" min="@GpuInfo.MinCurveVoltageInMv" max="@GpuInfo.MaxCurveVoltageInMv" @bind="OverclockSettings.MaximumVoltageMv" id="maxCoreVoltage">
|
||||
<div id="maxCoreFrequencyHelp" class="form-text">Setting this value to anything else than '0' will limit the maximum voltage (and also the frequency) of the curve.</div>
|
||||
</div>
|
||||
<RangeInput TValue="uint" Min="@GpuInfo.MinCurveFrequencyInMHz" Max="@GpuInfo.MaxCurveFrequencyInMHz" Step="@((uint)GpuInfo.MinCoreStepInMHz)" @bind-Value="OverclockSettings.MaximumFrequencyMHz" Id="maxCoreFrequency" Label="Maximum core frequency" UnitString="MHz"
|
||||
SubLabel="@($"Setting this value to anything else than '0' will limit the maximum frequency of the curve. Min: {GpuInfo.MinCurveFrequencyInMHz.ToSignedUnitString()}, Max: {GpuInfo.MaxCurveFrequencyInMHz.ToSignedUnitString()}")" />
|
||||
<RangeInput TValue="uint" Min="@GpuInfo.MinCurveVoltageInMv" Max="@GpuInfo.MaxCurveVoltageInMv" Step="5" @bind-Value="OverclockSettings.MaximumVoltageMv" Id="maxCoreVoltage" Label="Maximum core voltage" UnitString="mv"
|
||||
SubLabel="@($"Setting this value to anything else than '0' will limit the maximum voltage (and also the frequency) of the curve. Min: {GpuInfo.MinCurveVoltageInMv.ToUnitString("mv")}, Max: {GpuInfo.MaxCurveVoltageInMv.ToUnitString("mv")}")" />
|
||||
}
|
||||
@if (OverclockSettings.Type != NvGpuOcType.None)
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="voltageBoost">Voltage boost in %: @OverclockSettings.VoltageBoostPercent.ToUnitString("%")</label>
|
||||
<input class="form-range" type="range" step="1" min="0" max="100" @bind="OverclockSettings.VoltageBoostPercent" id="voltageBoost">
|
||||
</div>
|
||||
<RangeInput TValue="uint" @bind-Value="OverclockSettings.VoltageBoostPercent" Id="voltageBoost" Label="Voltage boost" UnitString="%" />
|
||||
|
||||
@if (GpuInfo.DefaultPowerInMilliWatts > 0 && GpuInfo.MaxPowerInMilliWatts > 0 && GpuInfo.MinPowerInMilliWatts > 0 && GpuInfo.MaxPowerInMilliWatts > GpuInfo.MinPowerInMilliWatts)
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="powerLimitWatts">Power limit in watts: @(((uint)(OverclockSettings.PowerPCM / 100000f * GpuInfo.DefaultPowerInMilliWatts)).ToKiloUnitString(units: "W")) (@OverclockSettings.PowerPCM.ToKiloUnitString(units: "%")) (Min: @GpuInfo.MinPowerInMilliWatts.ToKiloUnitString(units: "W"), Max: @GpuInfo.MaxPowerInMilliWatts.ToKiloUnitString(units: "W"))</label>
|
||||
<input class="form-range" type="range" step="1" min="@GpuInfo.MinimumPowerInPCM" max="@GpuInfo.MaximumPowerInPCM" @bind="OverclockSettings.PowerPCM" id="powerLimitWatts">
|
||||
</div>
|
||||
<RangeInput TValue="uint" Min="@GpuInfo.MinimumPowerInPCM" Max="@GpuInfo.MaximumPowerInPCM" @bind-Value="OverclockSettings.PowerPCM" Id="powerLimitWatts" Label="Power limit"
|
||||
CustomLabelFunc='value => $"{((uint)(OverclockSettings.PowerPCM / 100000f * GpuInfo.DefaultPowerInMilliWatts)).ToKiloUnitString(units: "W")} ({OverclockSettings.PowerPCM.ToKiloUnitString(units: "%")})"'
|
||||
SubLabel="@($"Min: {GpuInfo.MinPowerInMilliWatts.ToKiloUnitString(units: "W")}, Max: {GpuInfo.MaxPowerInMilliWatts.ToKiloUnitString(units: "W")}")" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="powerLimitPercentage">Power limit in percentage: @OverclockSettings.PowerPCM.ToKiloUnitString(units: "%") (Min: @GpuInfo.MinimumPowerInPCM.ToKiloUnitString(units: "%"), Max: @GpuInfo.MaximumPowerInPCM.ToKiloUnitString(units: "%")</label>
|
||||
<input class="form-range" type="range" step="1" min="@GpuInfo.MinimumPowerInPCM" max="@GpuInfo.MaximumPowerInPCM" @bind="OverclockSettings.PowerPCM" id="powerLimitPercentage">
|
||||
</div>
|
||||
<RangeInput TValue="uint" Min="@GpuInfo.MinimumPowerInPCM" Max="@GpuInfo.MaximumPowerInPCM" @bind-Value="OverclockSettings.PowerPCM" Id="powerLimitPercentage" Label="Power limit"
|
||||
CustomLabelFunc='value => $"{OverclockSettings.PowerPCM.ToKiloUnitString(units: "%")}"'
|
||||
SubLabel="@($"Min: {GpuInfo.MinimumPowerInPCM.ToKiloUnitString(units: "%")}, Max: {GpuInfo.MaximumPowerInPCM.ToKiloUnitString(units: "%")}")" />
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -138,6 +125,11 @@
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
if (!IsVisible)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_preset != Preset)
|
||||
{
|
||||
_preset = Preset;
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.NVIDIA
|
||||
@using ColorControl.UI.Services;
|
||||
@using NvAPIWrapper.Native.Display
|
||||
@using ColorControl.Shared.Common;
|
||||
@using static ColorControl.Shared.Common.NestedItemsBuilder
|
||||
|
||||
@rendermode Constants.RenderMode
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject JSHelper jsHelper
|
||||
|
||||
<div class="modal modal-lg" id="infoModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Info</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="row g-1">
|
||||
<div class="row">
|
||||
@if (InfoItems != null)
|
||||
{
|
||||
@RenderItems(InfoItems)
|
||||
}
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private bool _visible;
|
||||
|
||||
[Parameter]
|
||||
public bool Visible { get; set; }
|
||||
|
||||
private string? SelectedDisplayId { get; set; }
|
||||
private List<NestedItem>? InfoItems;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
if (_visible != Visible)
|
||||
{
|
||||
_visible = Visible;
|
||||
|
||||
if (Visible)
|
||||
{
|
||||
await GetSettingsAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected async Task GetSettingsAsync()
|
||||
{
|
||||
InfoItems = await _rpcClientService.CallAsync<List<NestedItem>>("NvService", "GetInfoItems");
|
||||
if (InfoItems != null)
|
||||
{
|
||||
InfoItems.ForEach(i => i.Expanded = true);
|
||||
}
|
||||
}
|
||||
|
||||
RenderFragment<List<NestedItem>> RenderItems => items =>
|
||||
@<div class="accordion accordion-flush" id="@items.GetHashCode()">
|
||||
@foreach(var item in items)
|
||||
{
|
||||
@if (item.NestedItems.Count > 0)
|
||||
{
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button py-2 @(item.Expanded ? "show" : "collapsed")" type="button" data-bs-toggle="collapse" data-bs-target="#@item.GetHashCode()" aria-expanded="false" aria-controls="flush-collapseOne" @onclick="() => OnItemClick(item)">
|
||||
@item.Value
|
||||
</button>
|
||||
</h2>
|
||||
<div id="@item.GetHashCode()" class="accordion-collapse collapse @(item.Expanded ? "show" : "")" data-bs-parent="#@items.GetHashCode()">
|
||||
<div class="accordion-body">
|
||||
@if (item.Expanded)
|
||||
{
|
||||
@RenderItems(item.NestedItems)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="mt-1" style="margin-left: 1.25rem">
|
||||
@item.Value
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
;
|
||||
|
||||
private async Task OnItemClick(NestedItem item)
|
||||
{
|
||||
await Task.Delay(400);
|
||||
|
||||
var classes = await jsHelper.GetElementClassName(item.GetHashCode().ToString());
|
||||
|
||||
item.Expanded = classes?.Contains(" show") == true;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,18 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.DisplayInfo
|
||||
@using ColorControl.Shared.Contracts.NVIDIA
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.UI.Services
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
|
||||
@using NvAPIWrapper.Native.Display
|
||||
@using ColorControl.Shared.Common;
|
||||
@using ColorControl.Shared.Common
|
||||
@using ColorControl.UI.Generics
|
||||
|
||||
@rendermode Constants.RenderMode
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject AppState AppState
|
||||
@inject NavigationManager _navigationManager;
|
||||
@inject JSHelper jsHelper;
|
||||
@inject JSHelper jsHelper
|
||||
@inject ProtectedLocalStorage _localStorage
|
||||
|
||||
<div class="modal" id="presetModal" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalOnFocus">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
@@ -109,7 +110,7 @@
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="activeResolution">Active resolution</label>
|
||||
<select class="form-select" id="activeResolution" @bind="selectedActiveResolution">
|
||||
<select class="form-select" id="activeResolution" @bind="selectedActiveResolution" @bind:after="() => AfterResolutionChange()">
|
||||
<option value="0x0">Not set</option>
|
||||
@foreach (var resolution in resolutions)
|
||||
{
|
||||
@@ -119,7 +120,7 @@
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="virtualResolution">Virtual resolution</label>
|
||||
<select class="form-select" id="virtualResolution" @bind="selectedVirtualResolution">
|
||||
<select class="form-select" id="virtualResolution" @bind="selectedVirtualResolution" @bind:after="() => AfterResolutionChange()">
|
||||
<option value="0x0">Not set</option>
|
||||
@foreach (var resolution in resolutions)
|
||||
{
|
||||
@@ -170,7 +171,7 @@
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="refreshRate">Refresh Rate</label>
|
||||
<select class="form-select" id="refreshRate" @bind="selectedRefreshRate">
|
||||
<select class="form-select" id="refreshRate" @bind="selectedRefreshRate" @bind:after="() => AfterRefreshRateChange()">
|
||||
@foreach (var refreshRate in refreshRates)
|
||||
{
|
||||
<option value="@refreshRate.ToString()">@refreshRate.ToString() Hz</option>
|
||||
@@ -182,6 +183,34 @@
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<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.DpiScaling.ApplyScaling" id="applyDpiScaling">
|
||||
}
|
||||
<button class="accordion-button collapsed py-2 @(Preset.DpiScaling.ApplyScaling ? "text-warning fw-bold" : "")" type="button" data-bs-toggle="collapse" data-bs-target="#dpiScalingSettings">
|
||||
Dpi Scaling
|
||||
</button>
|
||||
</span>
|
||||
</h2>
|
||||
<div id="dpiScalingSettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
|
||||
<div class="accordion-body">
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="dpiScaling">Percentage</label>
|
||||
<select class="form-select" id="dpiScaling" @bind="Preset.DpiScaling.Percentage">
|
||||
@foreach (var dpiScale in Shared.Native.CCD.DpiValues)
|
||||
{
|
||||
<option value="@dpiScale.ToString()">@dpiScale.ToString()</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<span class="d-flex align-items-baseline">
|
||||
@@ -256,14 +285,8 @@
|
||||
</h2>
|
||||
<div id="colorEnhancements" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
|
||||
<div class="accordion-body">
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="digitalVibrance">Digital vibrance: @Preset.ColorEnhancementSettings.DigitalVibranceLevel</label>
|
||||
<input class="form-range" type="range" min="0" max="100" @bind="Preset.ColorEnhancementSettings.DigitalVibranceLevel" id="digitalVibrance">
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="hue">Hue: @Preset.ColorEnhancementSettings.HueAngle°</label>
|
||||
<input class="form-range" type="range" min="0" max="359" @bind="Preset.ColorEnhancementSettings.HueAngle" id="hue">
|
||||
</div>
|
||||
<RangeInput TValue="int" @bind-Value="Preset.ColorEnhancementSettings.DigitalVibranceLevel" Id="digitalVibrance" Label="Digital vibrance" />
|
||||
<RangeInput TValue="int" @bind-Value="Preset.ColorEnhancementSettings.HueAngle" Id="hue" Label="Hue" Unit="°" Max="359" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -405,10 +428,8 @@
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-range" for="sdrBrightness">SDR brightness (@Preset.SDRBrightness%)</label>
|
||||
<input class="form-range" type="range" min="0" max="100" @bind="Preset.SDRBrightness" id="sdrBrightness">
|
||||
</div>
|
||||
|
||||
<NullableRangeInput TValue="int" @bind-Value="Preset.SDRBrightness" Id="sdrBrightness" Label="SDR brightness" Unit="%" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -539,11 +560,52 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="accordion accordion-flush" id="presetAccordion">
|
||||
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#advancedSettings">
|
||||
Triggers
|
||||
</button>
|
||||
</h2>
|
||||
<div id="advancedSettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
|
||||
<div class="accordion-body">
|
||||
<TriggerInput Triggers="Preset.Triggers"></TriggerInput>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#steps">
|
||||
Steps
|
||||
</button>
|
||||
</h2>
|
||||
<div id="steps" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
|
||||
<div class="accordion-body">
|
||||
<StepsInput Label="Steps" @bind-Steps="Preset.Steps" NvPresetsEnabled="false" TPreset="NvPreset"></StepsInput>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="description">Description</label>
|
||||
<textarea class="form-control" id="description" type="text" @bind="Preset.Description" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
}
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
@if (Preset?.IsDisplayPreset == true)
|
||||
{
|
||||
<input class="form-check-input" type="checkbox" @bind="ApplyImmediately" id="directApply" @bind:after="ApplyImmediatelyAfter">
|
||||
<label class="form-check-label" for="directApply">
|
||||
Apply changes immediately
|
||||
</label>
|
||||
}
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @onclick="() => IsVisible = false">Close</button>
|
||||
<button type="submit" class="btn btn-primary" disabled="@(!IsValid())" @onclick="ApplyClick">@(Preset?.IsDisplayPreset == true ? "Apply" : "Save")</button>
|
||||
</div>
|
||||
@@ -581,14 +643,33 @@
|
||||
private bool IsGpuSettingsOpened;
|
||||
private bool ShowOverclocking;
|
||||
|
||||
private bool ApplyImmediately;
|
||||
private NvPreset? OriginalPreset;
|
||||
private bool Applying;
|
||||
private bool Queued;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
if (!IsVisible)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((IsDriverSettingsOpened || IsGpuSettingsOpened) && Preset != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ApplyImmediately = false;
|
||||
|
||||
Preset = PresetParam == null ? null : PresetParam.CloneWithId();
|
||||
|
||||
displayPresets = await _rpcClientService.CallAsync<List<NvPreset>>("NvService", "GetDisplayPresets");
|
||||
if (Preset?.IsDisplayPreset == true)
|
||||
{
|
||||
Preset = displayPresets.FirstOrDefault(p => p.DisplayId == Preset.DisplayId) ?? Preset;
|
||||
}
|
||||
|
||||
if (Preset != null && PresetParam != null)
|
||||
{
|
||||
resolutions = await _rpcClientService.CallAsync<List<VirtualResolution>>("NvService", "GetAvailableResolutionsV2", Preset);
|
||||
@@ -598,27 +679,51 @@
|
||||
selectedVirtualResolution = Preset?.DisplayConfig.Resolution?.GetVirtualString();
|
||||
selectedRefreshRate = Preset?.DisplayConfig.RefreshRate?.ToString();
|
||||
|
||||
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)
|
||||
{
|
||||
ColorProfiles = await _rpcClientService.CallAsync<List<ColorProfileDto>>("ColorProfileService", "GetAllDisplayColorProfiles", deviceName);
|
||||
OriginalPreset = Preset.Clone();
|
||||
}
|
||||
|
||||
ApplyImmediately = await _localStorage.TryGet<bool>($"{nameof(NvPreset)}_{nameof(ApplyImmediately)}");
|
||||
ShowOverclocking = true;
|
||||
}
|
||||
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
// if (IsVisible && Preset?.IsDisplayPreset == false)
|
||||
// {
|
||||
// Preset.UpdateAutoApplySettings(keepChanges: true);
|
||||
// }
|
||||
await DoApplyImmediately();
|
||||
}
|
||||
|
||||
private async Task DoApplyImmediately()
|
||||
{
|
||||
if (IsVisible && OriginalPreset != null && Preset?.IsDisplayPreset == true)
|
||||
{
|
||||
if (Applying)
|
||||
{
|
||||
Queued = true;
|
||||
return;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
Applying = true;
|
||||
Queued = false;
|
||||
|
||||
Preset.UpdateAutoApplySettings(OriginalPreset, true);
|
||||
|
||||
if (ApplyImmediately && Preset.HasApplicableSettings)
|
||||
{
|
||||
await ApplyDisplayPreset();
|
||||
}
|
||||
|
||||
OriginalPreset = Preset.Clone();
|
||||
|
||||
Applying = false;
|
||||
}
|
||||
while (Queued);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ApplyClick(MouseEventArgs e)
|
||||
@@ -634,29 +739,6 @@
|
||||
return;
|
||||
}
|
||||
|
||||
var newResolution = resolutions?.FirstOrDefault(r => r.GetActiveString() == selectedActiveResolution);
|
||||
if (newResolution != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(selectedVirtualResolution) && selectedActiveResolution != selectedVirtualResolution)
|
||||
{
|
||||
newResolution = new VirtualResolution(newResolution.ActiveWidth, newResolution.ActiveHeight);
|
||||
var parts = selectedVirtualResolution.Split('x');
|
||||
newResolution.VirtualWidth = uint.Parse(parts[0]);
|
||||
newResolution.VirtualHeight = uint.Parse(parts[1]);
|
||||
}
|
||||
Preset.DisplayConfig.Resolution = newResolution;
|
||||
}
|
||||
else
|
||||
{
|
||||
Preset.DisplayConfig.Resolution = new VirtualResolution(0, 0);
|
||||
}
|
||||
|
||||
var newRefreshRate = refreshRates?.FirstOrDefault(r => r.ToString() == selectedRefreshRate);
|
||||
if (newRefreshRate != null)
|
||||
{
|
||||
Preset.DisplayConfig.RefreshRate = newRefreshRate;
|
||||
}
|
||||
|
||||
Preset.ApplyNovideoSettings = Preset.NovideoSettings.ApplyClamp;
|
||||
|
||||
if (Preset.IsDisplayPreset)
|
||||
@@ -734,4 +816,52 @@
|
||||
{
|
||||
return Preset?.IsDisplayPreset == true || !string.IsNullOrEmpty(Preset?.name);
|
||||
}
|
||||
|
||||
private async Task AfterResolutionChange()
|
||||
{
|
||||
if (Preset == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var newResolution = resolutions?.FirstOrDefault(r => r.GetActiveString() == selectedActiveResolution);
|
||||
if (newResolution != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(selectedVirtualResolution) && selectedActiveResolution != selectedVirtualResolution)
|
||||
{
|
||||
newResolution = new VirtualResolution(newResolution.ActiveWidth, newResolution.ActiveHeight);
|
||||
var parts = selectedVirtualResolution.Split('x');
|
||||
newResolution.VirtualWidth = uint.Parse(parts[0]);
|
||||
newResolution.VirtualHeight = uint.Parse(parts[1]);
|
||||
}
|
||||
Preset.DisplayConfig.Resolution = newResolution;
|
||||
}
|
||||
else
|
||||
{
|
||||
Preset.DisplayConfig.Resolution = new VirtualResolution(0, 0);
|
||||
}
|
||||
|
||||
await DoApplyImmediately();
|
||||
}
|
||||
|
||||
private async Task AfterRefreshRateChange()
|
||||
{
|
||||
if (Preset == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var newRefreshRate = refreshRates?.FirstOrDefault(r => r.ToString() == selectedRefreshRate);
|
||||
if (newRefreshRate != null)
|
||||
{
|
||||
Preset.DisplayConfig.RefreshRate = newRefreshRate;
|
||||
}
|
||||
|
||||
await DoApplyImmediately();
|
||||
}
|
||||
|
||||
private async Task ApplyImmediatelyAfter()
|
||||
{
|
||||
await _localStorage.SetAsync($"{nameof(NvPreset)}_{nameof(ApplyImmediately)}", ApplyImmediately);
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
<NvidiaDithering Visible="DitheringVisible" />
|
||||
<NvidiaDriverSettings Preset="CurrentDriverSettingsPreset" />
|
||||
<NvidiaGpuSettings Preset="CurrentGpuSettingsPreset" GpuInfo="CurrentGpuInfo" />
|
||||
<NvidiaInfo Visible="InfoVisible" />
|
||||
|
||||
<div class="d-flex align-items-center">
|
||||
<h1>NVIDIA controller</h1>
|
||||
@@ -30,6 +31,7 @@
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#ditheringModal" @onclick="() => DitheringVisible = true">Test dithering</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#driverSettingsModal" @onclick="() => CurrentDriverSettingsPreset = null">Driver settings</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#infoModal" @onclick="() => InfoVisible = true">Info</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#settingsModal">
|
||||
@@ -75,6 +77,7 @@ 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>
|
||||
<small class="text-muted">Scale: @preset.DpiScaling.ToString()</small>
|
||||
@if (preset.DitherState != NvDitherState.Auto)
|
||||
{
|
||||
<br />
|
||||
@@ -93,22 +96,7 @@ else
|
||||
@if (preset.driverSettings?.Any() == true)
|
||||
{
|
||||
<br />
|
||||
<div class="dropdown">
|
||||
<span role="button" class="text-decoration-underline dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false" data-bs-auto-close="outside" @onclick="() => DriverSettingsOnClick(preset)">
|
||||
<small>@preset.driverSettings.Count() driver settings changed</small>
|
||||
</span>
|
||||
<form class="dropdown-menu">
|
||||
@if (DriverSettings != null)
|
||||
{
|
||||
<ul class="list-group p-0">
|
||||
@foreach (var setting in DriverSettings)
|
||||
{
|
||||
<li class="list-group-item py-0 px-1"><small>@setting.FriendlyName</small></li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
</form>
|
||||
</div>
|
||||
@RenderPresetSettings(preset)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
@@ -239,7 +227,7 @@ else
|
||||
}
|
||||
@if (preset.applyDriverSettings)
|
||||
{
|
||||
<li class="list-group-item p-1">@preset.driverSettings.Count changed driver settings</li>
|
||||
<li class="list-group-item p-1">@RenderPresetSettings(preset)</li>
|
||||
}
|
||||
@if (preset.ApplyNovideoSettings)
|
||||
{
|
||||
@@ -278,6 +266,7 @@ else
|
||||
private NvPreset? CurrentGpuSettingsPreset;
|
||||
private NvGpuInfoDto? CurrentGpuInfo;
|
||||
private bool DitheringVisible;
|
||||
private bool InfoVisible;
|
||||
private List<NvSettingItemDto>? DriverSettings;
|
||||
|
||||
private PresetOrder PresetOrder;
|
||||
@@ -301,9 +290,42 @@ else
|
||||
}
|
||||
}
|
||||
|
||||
RenderFragment<NvPreset> RenderPresetSettings => preset =>
|
||||
@<div class="dropdown">
|
||||
<span role="button" class="text-decoration-underline dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false" data-bs-auto-close="outside" @onclick="() => DriverSettingsOnClick(preset)" @onclick:preventDefault @onclick:stopPropagation>
|
||||
<small>@preset.driverSettings.Count() driver settings changed</small>
|
||||
</span>
|
||||
<form class="dropdown-menu" style="width: max-content">
|
||||
@if (DriverSettings != null)
|
||||
{
|
||||
<ul class="list-group p-0">
|
||||
@foreach (var settingId in preset.driverSettings.Keys)
|
||||
{
|
||||
var setting = DriverSettings.FirstOrDefault(s => s.SettingId == settingId);
|
||||
if (setting == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var settingValue = setting.Values.FirstOrDefault(v => v.Value == preset.driverSettings[settingId]);
|
||||
if (settingValue == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
<li class="list-group-item py-0 px-1"><small>@setting.Name: @settingValue.ValueName</small></li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
</form>
|
||||
</div>
|
||||
;
|
||||
|
||||
private async Task PresetOnClick(NvPreset preset)
|
||||
{
|
||||
await _rpcClientService.CallAsync<bool>("NvService", "ApplyPresetWithId", preset.id);
|
||||
var result = await _rpcClientService.CallAsync<bool>("NvService", "ApplyPresetWithId", preset.id);
|
||||
if (result)
|
||||
{
|
||||
DisplayPresets = await _rpcClientService.CallAsync<List<NvPreset>>("NvService", "GetDisplayPresets");
|
||||
}
|
||||
}
|
||||
|
||||
private void PresetOnEditClick(NvPreset preset)
|
||||
@@ -395,11 +417,6 @@ else
|
||||
|
||||
private async Task DriverSettingsOnClick(NvPreset preset)
|
||||
{
|
||||
var driverSettings = await _rpcClientService.CallAsync<List<NvSettingItemDto>>("NvService", "GetDriverSettings", Type.Missing, true);
|
||||
|
||||
if (driverSettings != null)
|
||||
{
|
||||
DriverSettings = driverSettings.Where(s => s.Value != s.DefaultValue && s.State == 2).ToList();
|
||||
}
|
||||
DriverSettings ??= await _rpcClientService.CallAsync<List<NvSettingItemDto>>("NvService", "GetDriverSettings", Type.Missing, true);
|
||||
}
|
||||
}
|
||||
@@ -134,13 +134,9 @@ else
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Advanced</h5>
|
||||
<form>
|
||||
<div class="mb-3 form-check">
|
||||
<label class="form-range" for="processMonitorPollingInterval">
|
||||
Polling interval of process monitor (@config.ProcessMonitorPollingInterval milliseconds)
|
||||
</label>
|
||||
<input class="form-range" type="range" min="50" max="10000" step="50" @bind="config.ProcessMonitorPollingInterval" id="processMonitorPollingInterval" aria-describedby="processMonitorPollingIntervalHelp" @bind:after="PollingIntervalOnInput">
|
||||
<div id="processMonitorPollingIntervalHelp" class="form-text">Decreasing this delay may execute triggered presets sooner but can cause a higher CPU load</div>
|
||||
</div>
|
||||
<RangeInput TValue="int" Min="50" Max="10000" Step="50" @bind-Value="config.ProcessMonitorPollingInterval" Id="processMonitorPollingInterval" Label="Polling interval of process monitor" Unit=" milliseconds"
|
||||
SubLabel="Decreasing this delay may execute triggered presets sooner but can cause a higher CPU load"
|
||||
/>
|
||||
<div class="mb-3 form-check">
|
||||
<input class="form-check-input" type="checkbox" @bind="config.UseRawInput" id="useRawInput" aria-describedby="useRawInputHelp" @oninput="UseRawInputInput">
|
||||
<label class="form-check-label" for="useRawInput">
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.AMD
|
||||
@using ColorControl.Shared.Contracts.RemoteControl
|
||||
@using ColorControl.UI.Services
|
||||
@using ColorControl.Shared.Common
|
||||
|
||||
@* @rendermode Constants.RenderMode *@
|
||||
|
||||
@typeparam TPreset where TPreset : PresetBase
|
||||
@typeparam TButtonEnum where TButtonEnum : struct, Enum
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
|
||||
<div class="modal" id="rcButtonModal" data-bs-backdrop="false" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">RC button</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @onclick="() => Button = null"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="row g-1">
|
||||
@if (Button == null)
|
||||
{
|
||||
<div>Loading...</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="name">Name</label>
|
||||
<input class="form-control" id="name" type="text" @bind="Button.Name" required />
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="button">Button</label>
|
||||
<select class="form-select" id="button" @bind="Button.Button">
|
||||
@foreach (var buttonType in Enum.GetValues<TButtonEnum>())
|
||||
{
|
||||
<option value="@buttonType.ToString()">@buttonType.GetDescription()</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
@if (Presets?.Any() == true)
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="preset">Preset</label>
|
||||
<select class="form-select" id="preset" @bind="PresetName">
|
||||
<option value="">Select preset</option>
|
||||
@foreach (var preset in Presets)
|
||||
{
|
||||
<option value="@preset.name">@preset.name</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-danger" data-bs-dismiss="modal" @onclick="RemoveClick">Remove</button>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @onclick="() => Button = null">Close</button>
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" @onclick="ApplyClick">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
public BaseRcButtonDto<TPreset, TButtonEnum>? ButtonParam { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<BaseRcButtonDto<TPreset, TButtonEnum>, Task>? AfterApply { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public List<TPreset>? Presets { get; set; }
|
||||
|
||||
private BaseRcButtonDto<TPreset, TButtonEnum>? Button { get; set; }
|
||||
private string? PresetName;
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
if (ButtonParam == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Button = new BaseRcButtonDto<TPreset, TButtonEnum>(ButtonParam);
|
||||
PresetName = Button.Preset?.name;
|
||||
}
|
||||
|
||||
private async Task ApplyClick(MouseEventArgs e)
|
||||
{
|
||||
if (Button == null || ButtonParam == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Button.Preset = (Presets?.FirstOrDefault(p => p.name == PresetName))!;
|
||||
|
||||
ButtonParam.Update(Button);
|
||||
|
||||
if (AfterApply != null)
|
||||
{
|
||||
await AfterApply.Invoke(ButtonParam);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RemoveClick(MouseEventArgs e)
|
||||
{
|
||||
if (Button == null || ButtonParam == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (AfterApply != null)
|
||||
{
|
||||
await AfterApply.Invoke(null!);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.Base
|
||||
@using ColorControl.Shared.Contracts.RemoteControl
|
||||
@using ColorControl.UI.Services
|
||||
@using NvAPIWrapper.Native.Display
|
||||
@using ColorControl.Shared.Common
|
||||
@using System.Globalization
|
||||
|
||||
@* @rendermode Constants.RenderMode *@
|
||||
|
||||
@typeparam TDevice where TDevice : BaseDeviceDto
|
||||
@typeparam TPreset where TPreset : PresetBase, new()
|
||||
@typeparam TButtonEnum where TButtonEnum : struct, Enum
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject JSHelper jsHelper
|
||||
|
||||
<style>
|
||||
.myhover {
|
||||
border-radius: 25px;
|
||||
width: @(ButtonSize)%;
|
||||
aspect-ratio: 1;
|
||||
opacity: 0.2;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
.myhover:hover {
|
||||
opacity: 0.4;
|
||||
background-color: white;
|
||||
}
|
||||
</style>
|
||||
|
||||
<GenericRcButtonPage ButtonParam="CurrentButton" AfterApply="AfterApplyButton" TPreset="TPreset" TButtonEnum="TButtonEnum" Presets="Presets"></GenericRcButtonPage>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<h5>@(Device?.Name == null ? $"No device selected" : Device.Name)</h5>
|
||||
</div>
|
||||
<div>
|
||||
<form class="row g-1 needs-validation @WasValidated" novalidate>
|
||||
@if (Remotes == null)
|
||||
{
|
||||
<div>Loading...</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<select class="form-select" id="remoteControl" @bind="SelectedRemoteName" @bind:after="RemoteChanged">
|
||||
@foreach (var remote in Remotes)
|
||||
{
|
||||
<option id="@remote.Name">@remote.Name</option>
|
||||
}
|
||||
</select>
|
||||
if (RemoteController == null)
|
||||
{
|
||||
<div>Select a remote</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@if (AllowEditing)
|
||||
{
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="@(IsValid() ? "modal" : "")" @onclick="ApplyClick">Save</button>
|
||||
}
|
||||
@* <p>Left: @CurrentX, Top: @CurrentY</p> *@
|
||||
<div style="position: relative" @onclick="Button_Clicked" @oncontextmenu:preventDefault="true" @oncontextmenu="e => NewRcButtonRightClick(e)">
|
||||
<img src="remotes/@RemoteController.ImageFileName" id="rcImage" class="w-100" />
|
||||
@foreach (var button in RemoteController.Buttons)
|
||||
{
|
||||
<button class="myhover" type="button"
|
||||
style="position: absolute; top: @(button.Top.ToString(CultureInfo.InvariantCulture))%; left: @(button.Left.ToString(CultureInfo.InvariantCulture))%"
|
||||
@onclick="() => RcButtonOnClick(button)" @oncontextmenu="e => RcButtonRightClick(e, button)" @oncontextmenu:preventDefault="true" @oncontextmenu:stopPropagation>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private TDevice? _device;
|
||||
|
||||
[Parameter]
|
||||
public TDevice? Device { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<TDevice, Task>? AfterApply { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? ServiceName { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public List<TPreset>? Presets { get; set; }
|
||||
|
||||
private string WasValidated = "";
|
||||
private bool IsVisible;
|
||||
|
||||
private string? SelectedRemoteName;
|
||||
private BaseRcDto<TPreset, TButtonEnum>? RemoteController;
|
||||
private List<BaseRcDto<TPreset, TButtonEnum>>? Remotes;
|
||||
|
||||
private double CurrentX, CurrentY;
|
||||
|
||||
private RectDimension? ImageDimension;
|
||||
|
||||
private BaseRcButtonDto<TPreset, TButtonEnum>? CurrentButton;
|
||||
|
||||
private double ButtonSize = 13;
|
||||
private bool AllowEditing = false;
|
||||
|
||||
protected override async void OnParametersSet()
|
||||
{
|
||||
if (ServiceName != null && (_device != Device || Device == null))
|
||||
{
|
||||
_device = Device;
|
||||
|
||||
Remotes = await _rpcClientService.CallAsync<List<BaseRcDto<TPreset, TButtonEnum>>>(ServiceName, "GetRemotes");
|
||||
|
||||
if (Remotes?.Any() == true)
|
||||
{
|
||||
RemoteController = Remotes.First();
|
||||
SelectedRemoteName = RemoteController.Name;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ApplyClick(MouseEventArgs e)
|
||||
{
|
||||
if (RemoteController == null || ServiceName == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsValid())
|
||||
{
|
||||
WasValidated = "was-validated";
|
||||
return;
|
||||
}
|
||||
|
||||
var result = await _rpcClientService.CallAsync<bool>(ServiceName, "UpdateRemote", RemoteController);
|
||||
|
||||
// if (AfterApply != null)
|
||||
// {
|
||||
// await AfterApply.Invoke(Device);
|
||||
// }
|
||||
}
|
||||
|
||||
private void ModalOnFocus(FocusEventArgs e)
|
||||
{
|
||||
if (!IsVisible)
|
||||
{
|
||||
WasValidated = "";
|
||||
IsVisible = true;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsValid()
|
||||
{
|
||||
return true; // !string.IsNullOrEmpty(Device?.Name) && (Device?.IsCustom != true || !string.IsNullOrEmpty(Device?.IpAddress));
|
||||
}
|
||||
|
||||
private async Task RcButtonOnClick(BaseRcButtonDto<TPreset, TButtonEnum> button)
|
||||
{
|
||||
if (ServiceName == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var rcButton = button.Button;
|
||||
|
||||
var preset = button.Preset;
|
||||
if (preset == null)
|
||||
{
|
||||
preset = new TPreset();
|
||||
preset.name = rcButton.ToString();
|
||||
preset.SetSteps([rcButton.ToString()]);
|
||||
|
||||
}
|
||||
|
||||
await _rpcClientService.CallAsync<bool>(ServiceName, "ApplyPreset", preset);
|
||||
}
|
||||
|
||||
public async Task Button_Clicked(MouseEventArgs e)
|
||||
{
|
||||
if (ImageDimension == null)
|
||||
{
|
||||
ImageDimension = await jsHelper.GetElementDimensions("rcImage");
|
||||
}
|
||||
|
||||
if (ImageDimension != null)
|
||||
{
|
||||
CurrentX = Math.Round(((double)e.OffsetX / ImageDimension.Width) * 100, 4);
|
||||
CurrentY = Math.Round(((double)e.OffsetY / ImageDimension.Height) * 100, 4);
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentX = e.OffsetX;
|
||||
CurrentY = e.OffsetY;
|
||||
}
|
||||
|
||||
private async Task RcButtonRightClick(MouseEventArgs e, BaseRcButtonDto<TPreset, TButtonEnum> button)
|
||||
{
|
||||
if (!AllowEditing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentButton = button;
|
||||
|
||||
if (e.ShiftKey)
|
||||
{
|
||||
AlignItemsHorizontally(button);
|
||||
return;
|
||||
}
|
||||
if (e.CtrlKey)
|
||||
{
|
||||
AlignItemsVertically(button);
|
||||
return;
|
||||
}
|
||||
|
||||
await jsHelper.OpenModal("rcButtonModal");
|
||||
}
|
||||
|
||||
private void AlignItemsHorizontally(BaseRcButtonDto<TPreset, TButtonEnum> button)
|
||||
{
|
||||
if (RemoteController?.Buttons == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var alignedButtons = RemoteController.Buttons.Where(b => b.Top - 2 <= button.Top && b.Top + 2 >= button.Top);
|
||||
|
||||
foreach (var b in alignedButtons)
|
||||
{
|
||||
b.Top = button.Top;
|
||||
}
|
||||
}
|
||||
|
||||
private void AlignItemsVertically(BaseRcButtonDto<TPreset, TButtonEnum> button)
|
||||
{
|
||||
if (RemoteController?.Buttons == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var alignedButtons = RemoteController.Buttons.Where(b => b.Top - 15 <= button.Top && b.Top + 15 >= button.Top && b.Left - 8 <= button.Left && b.Left + 8 >= button.Left);
|
||||
|
||||
foreach (var b in alignedButtons)
|
||||
{
|
||||
b.Left = button.Left;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task NewRcButtonRightClick(MouseEventArgs e)
|
||||
{
|
||||
if (!AllowEditing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ImageDimension == null)
|
||||
{
|
||||
ImageDimension = await jsHelper.GetElementDimensions("rcImage");
|
||||
}
|
||||
|
||||
if (ImageDimension != null)
|
||||
{
|
||||
var posX = Math.Round(((double)e.OffsetX / ImageDimension.Width) * 100, 4);
|
||||
var posY = Math.Round(((double)e.OffsetY / ImageDimension.Height) * 100, 4);
|
||||
|
||||
posX -= (ButtonSize / 2);
|
||||
posY -= ((double)ImageDimension.Width / ImageDimension.Height) * ((ButtonSize / 2) - 0.5);
|
||||
|
||||
CurrentButton = new BaseRcButtonDto<TPreset, TButtonEnum>
|
||||
{
|
||||
Top = posY,
|
||||
Left = posX
|
||||
};
|
||||
await jsHelper.OpenModal("rcButtonModal");
|
||||
}
|
||||
}
|
||||
|
||||
private Task AfterApplyButton(BaseRcButtonDto<TPreset, TButtonEnum>? button)
|
||||
{
|
||||
if (RemoteController == null)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
if (button == null)
|
||||
{
|
||||
RemoteController.Buttons.Remove(CurrentButton);
|
||||
}
|
||||
else if (!RemoteController.Buttons.Contains(button))
|
||||
{
|
||||
RemoteController.Buttons.Add(button);
|
||||
}
|
||||
|
||||
StateHasChanged();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void KeyDown(KeyboardEventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void KeyUp(KeyboardEventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
private void RemoteChanged()
|
||||
{
|
||||
RemoteController = Remotes?.FirstOrDefault(r => r.Name == SelectedRemoteName);
|
||||
ImageDimension = null;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.Samsung
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using ColorControl.UI.Services
|
||||
@using NvAPIWrapper.Native.Display
|
||||
@using ColorControl.Shared.Common
|
||||
@@ -7,10 +8,8 @@
|
||||
@rendermode Constants.RenderMode
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject AppState AppState
|
||||
@inject NavigationManager _navigationManager;
|
||||
|
||||
<div class="modal" id="deviceModal" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalOnFocus">
|
||||
<div class="modal" id="samsungDeviceModal" 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">
|
||||
@@ -73,7 +72,7 @@
|
||||
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#deviceOptions">
|
||||
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#deviceOptions">
|
||||
Device options
|
||||
</button>
|
||||
</h2>
|
||||
@@ -165,6 +164,10 @@
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
@if (Device?.IsCustom == true)
|
||||
{
|
||||
<button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#removeDeviceModal">Remove</button>
|
||||
}
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @onclick="() => IsVisible = false">Close</button>
|
||||
<button type="button" class="btn btn-primary" data-bs-dismiss="@(IsValid() ? "modal" : "")" @onclick="ApplyClick">Save</button>
|
||||
</div>
|
||||
@@ -172,6 +175,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ConfirmModal ModalId="removeDeviceModal" Title="Remove device" Message="Are you sure you want to remove this device?" OnConfirm="DeviceOnRemoveConfirmed" NextModalId="samsungDeviceModal" />
|
||||
|
||||
@code {
|
||||
private SamsungDeviceDto? _device;
|
||||
|
||||
@@ -216,6 +221,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DeviceOnRemoveConfirmed()
|
||||
{
|
||||
if (Device == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var result = await _rpcClientService.CallAsync<bool>("SamsungService", "RemoveDevice", Device);
|
||||
|
||||
if (AfterApply != null)
|
||||
{
|
||||
await AfterApply.Invoke(Device);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task TestConnectionClick(MouseEventArgs e)
|
||||
{
|
||||
IsTestingConnection = true;
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
@rendermode Constants.RenderMode
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject AppState AppState
|
||||
@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-scrollable modal-dialog-centered">
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.NVIDIA
|
||||
@using ColorControl.Shared.Contracts.Samsung
|
||||
@using ColorControl.UI.Components.Pages.RemoteControl
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using ColorControl.UI.Generics
|
||||
@@ -22,6 +23,9 @@
|
||||
<div class="d-flex align-items-center">
|
||||
<h1>Samsung controller</h1>
|
||||
<div class="d-flex ms-auto">
|
||||
<button type="button" class="btn btn-sm btn-primary me-2" data-bs-toggle="offcanvas" data-bs-target="#rcCanvasRight" aria-controls="rcCanvasRight">
|
||||
Remote control
|
||||
</button>
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-sm btn-primary dropdown-toggle me-2" data-bs-toggle="dropdown" aria-expanded="false" data-bs-auto-close="outside">
|
||||
Buttons
|
||||
@@ -62,6 +66,16 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="offcanvas offcanvas-end" style="width: 280px" data-bs-scroll="true" tabindex="-1" id="rcCanvasRight" aria-labelledby="rcCanvasRightRightLabel">
|
||||
<div class="offcanvas-header">
|
||||
<h5 class="offcanvas-title" id="rcCanvasRightRightLabel">Remote control</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="offcanvas-body">
|
||||
<GenericRcPage ServiceName="SamsungService" Device="CurrentDevice" TDevice="SamsungDeviceDto" TPreset="SamsungPreset" TButtonEnum="ButtonType"></GenericRcPage>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<SamsungDevicePage Device="CurrentDevice" AfterApply="AfterDeviceApplied" />
|
||||
<SamsungPresetPage PresetParam="CurrentPreset" Devices="Devices" AfterApply="AfterPresetApplied" />
|
||||
<ConfirmModal Title="Delete preset" Message="Are you sure you want to delete the preset?" OnConfirm="PresetOnDeleteConfirmed" />
|
||||
@@ -82,7 +96,7 @@ else
|
||||
}
|
||||
<span role="status">Refresh</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#deviceModal" @onclick="() => AddDeviceOnClick()">
|
||||
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#samsungDeviceModal" @onclick="() => AddDeviceOnClick()">
|
||||
Add
|
||||
</button>
|
||||
</div>
|
||||
@@ -117,7 +131,7 @@ else
|
||||
{
|
||||
<button class="btn btn-sm btn-secondary ms-2" @onclick="() => SetAsPrimaryOnClick(device)">Set as primary</button>
|
||||
}
|
||||
<button type="button" class="btn btn-sm btn-primary ms-2" data-bs-toggle="modal" data-bs-target="#deviceModal" @onclick="() => DeviceOnClick(device)">
|
||||
<button type="button" class="btn btn-sm btn-primary ms-2" data-bs-toggle="modal" data-bs-target="#samsungDeviceModal" @onclick="() => DeviceOnClick(device)">
|
||||
Settings
|
||||
</button>
|
||||
</div>
|
||||
@@ -210,6 +224,7 @@ else
|
||||
Config = await _rpcClientService.CallAsync<SamsungServiceConfigDto>("SamsungService", "GetConfig");
|
||||
await RefreshPresets();
|
||||
Devices = await _rpcClientService.CallAsync<List<SamsungDeviceDto>>("SamsungService", "GetDevices");
|
||||
CurrentDevice = Devices?.FirstOrDefault(d => d.MacAddress == Config?.PreferredMacAddress) ?? Devices?.FirstOrDefault();
|
||||
InvokableActions = await _rpcClientService.CallAsync<List<InvokableActionDto<SamsungPreset>>>("SamsungService", "GetInvokableActions");
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user