Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/AdaptiveRemote.App/AdaptiveRemote.App.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@
<PackageReference Include="StreamJsonRpc" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="Services\Layout\layout-grid.css" />
</ItemGroup>

<ItemGroup>
<Content Update="wwwroot\css\app.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
Expand Down
5 changes: 5 additions & 0 deletions src/AdaptiveRemote.App/Components/Remote.razor
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
@inject Services.IRemoteDefinitionService RemoteDefinitions
@inject Services.Layout.IDynamicStylesheetProvider Stylesheet

@if (Stylesheet.GetCss() is { } css)
{
<style>@((MarkupString)css)</style>
}
<ModalMessageUI />
<RemoteLayout LayoutElement="@RemoteDefinitions.RemoteRoot" ProgramMode="@ProgramMode" />

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ internal static IServiceCollection AddRemoteServices(this IServiceCollection ser
.AddCloudAssetServices()
.AddScopedLifecycleService<LifecycleCommandService>()
.AddScoped<IRemoteDefinitionService, RemoteLayoutDefinitionService>()
.AddScoped<IDynamicStylesheetProvider, LayoutStylesheetProvider>()
.AddSingleton<IPersistSettings, PersistSettings>()
.Configure<ProgrammaticSettings>(configuration.GetSection(SettingsKeys.ProgrammaticSettings))
.Configure<CloudSettings>(configuration.GetSection("CloudSettings"));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace AdaptiveRemote.Services.Layout;

/// <summary>
/// Scoped. Returns the CSS for the active layout in this scope.
/// </summary>
public interface IDynamicStylesheetProvider
{
string? GetCss();
}
19 changes: 19 additions & 0 deletions src/AdaptiveRemote.App/Services/Layout/LayoutStylesheetProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.Reflection;

namespace AdaptiveRemote.Services.Layout;

internal sealed class LayoutStylesheetProvider : IDynamicStylesheetProvider
{
private static readonly string _css = LoadCss();

public string? GetCss() => _css;

private static string LoadCss()
{
Assembly assembly = typeof(LayoutStylesheetProvider).Assembly;
using Stream stream = assembly.GetManifestResourceStream(
"AdaptiveRemote.Services.Layout.layout-grid.css")!;
using StreamReader reader = new(stream);
return reader.ReadToEnd();
}
}
190 changes: 190 additions & 0 deletions src/AdaptiveRemote.App/Services/Layout/layout-grid.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
#ROOT {
display: grid;
grid-template-rows: 6fr 3fr 1fr;
grid-template-columns: 3fr 2fr;
grid-gap: 20px;
width: 98vw;
height: 96vh;
padding: 2vh 1vw;
}
#ROOT #DPAD {
grid-row: 1;
grid-column: 1;
display: grid;
grid-template-rows: repeat(3, 1fr);
grid-template-columns: repeat(3, 1fr);
grid-gap: 10px;
}
#ROOT #DPAD #UP {
grid-row: 1;
grid-column: 2;
}
#ROOT #DPAD #DOWN {
grid-row: 3;
grid-column: 2;
}
#ROOT #DPAD #LEFT {
grid-row: 2;
grid-column: 1;
}
#ROOT #DPAD #RIGHT {
grid-row: 2;
grid-column: 3;
}
#ROOT #DPAD #SELECT {
grid-row: 2;
grid-column: 2;
}
#ROOT #DPAD #POWER {
grid-row: 1;
grid-column: 1;
margin: 20px;
background-color: #aa2525;
border-color: #561313;
border-width: 2px;
color: black;
}
#ROOT #DPAD #POWER:hover {
background-color: #bf2a2a;
}
#ROOT #DPAD #POWER:active,
#ROOT #DPAD #POWER.btn-active {
background-color: #d74545;
}
#ROOT #DPAD #POWER.btn-not-programmed {
color: #aa2525;
border-color: #aa2525;
background-color: #222;
border-width: 5px;
}
#ROOT #DPAD #POWER.btn-not-programmed:hover {
background-color: #2f2f2f;
}
#ROOT #DPAD #POWER.btn-not-programmed:active,
#ROOT #DPAD #POWER.btn-not-programmed.btn-active {
background-color: #484848;
}
#ROOT #DPAD #POWER.btn-disabled {
background-color: #888888;
color: #3c3c3c;
border-color: #555555;
}
#ROOT #DPAD #POWERON {
display: none;
}
#ROOT #DPAD #POWEROFF {
display: none;
}
#ROOT #DPAD #BACK {
grid-row: 3;
grid-column: 1;
margin: 20px;
}
#ROOT #WELL {
grid-row: 1;
grid-column: 2;
margin: -0.5%;
}
#ROOT #WELL > button {
width: 49%;
height: 19%;
margin: 0.5%;
}
#ROOT #PLAYBACK {
grid-row: 2;
grid-column: 1;
display: grid;
grid-template-rows: repeat(3, 1fr);
grid-template-columns: repeat(5, 1fr);
grid-gap: 10px;
}
#ROOT #PLAYBACK #REPLAY {
grid-row: 1;
grid-column: 1;
}
#ROOT #PLAYBACK #PLAY {
grid-row: 1;
grid-column: 2;
}
#ROOT #PLAYBACK #PAUSE {
grid-row: 1;
grid-column: 3;
}
#ROOT #PLAYBACK #RECORD {
grid-row: 1;
grid-column: 4;
}
#ROOT #PLAYBACK #SKIP {
grid-row: 1;
grid-column: 5;
}
#ROOT #CHANNELANDVOLUME {
grid-row: 2;
grid-column: 2;
display: grid;
grid-template-rows: 36pt 1fr 1fr;
grid-template-columns: 2fr 1fr 2fr;
grid-gap: 10px;
}
#ROOT #CHANNELANDVOLUME:before {
grid-row: 1;
grid-column: 1;
font-size: 36pt;
font-weight: bold;
color: #62b0ff;
content: "Channel";
text-align: center;
}
#ROOT #CHANNELANDVOLUME:after {
grid-row: 1;
grid-column: 3;
font-size: 36pt;
font-weight: bold;
color: #62b0ff;
content: "Volume";
text-align: center;
}
#ROOT #CHANNELANDVOLUME #CHANNELUP {
grid-row: 2;
grid-column: 1;
}
#ROOT #CHANNELANDVOLUME #CHANNELDOWN {
grid-row: 3;
grid-column: 1;
}
#ROOT #CHANNELANDVOLUME #VOLUMEUP {
grid-row: 2;
grid-column: 3;
}
#ROOT #CHANNELANDVOLUME #VOLUMEDOWN {
grid-row: 3;
grid-column: 3;
}
#ROOT #CHANNELANDVOLUME #MUTE {
grid-row: 2;
grid-column: 2;
grid-row-end: span 2;
}
#ROOT #GUTTER {
grid-row: 3;
grid-column-start: 1;
grid-column-end: span 2;
grid-gap: 20px;
display: grid;
grid-template-rows: 1fr;
grid-template-columns: 6fr 1fr 1fr;
grid-gap: 10px;
}
#ROOT #GUTTER #EXIT {
grid-row: 1;
grid-column: 3;
}
#ROOT #GUTTER #LEARN {
grid-row: 1;
grid-column: 2;
margin: 10px;
}
#ROOT #GUTTER #LISTENING {
grid-row: 1;
grid-column: 1;
}
9 changes: 9 additions & 0 deletions src/AdaptiveRemote.App/Services/Testing/IUITestService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,13 @@ public partial interface IUITestService : IDisposable
/// <param name="cancellationToken">Cancellation token for the operation.</param>
/// <returns>The inner HTML of the first matching element, or null if not found or not visible.</returns>
Task<string?> GetInnerHtmlFromElementWithCssClassAsync(string cssClass, CancellationToken cancellationToken);

/// <summary>
/// Gets a CSS property value from the first stylesheet rule that matches the provided selector.
/// </summary>
/// <param name="selector">The exact CSS selector text to match.</param>
/// <param name="propertyName">The CSS property name to read (for example, <c>display</c>).</param>
/// <param name="cancellationToken">Cancellation token for the operation.</param>
/// <returns>The matching property value, or null if no matching rule/property exists.</returns>
Task<string?> GetStylesheetRulePropertyValueAsync(string selector, string propertyName, CancellationToken cancellationToken);
}
Loading