Skip to content

Commit

Permalink
304 port UI to blazor proof of concept (#308)
Browse files Browse the repository at this point in the history
* Add a Razor class library but for some reason the Razor component can't be referenced from WinForms

* Add HTML boilerplate

* Rename FrEee.WinForms to FrEee.UI.WinForms

* Update namespaces

* Update project/solutoin

* Undo some accidentally committed changes

* Blazorified the GameProgressBar

* Adjust styling

* Don't allow progress bar to overflow

* Set up the new font thingy, not using it yet because I haven't found a good place to put it in the progress bar

* Fix right text of progress bar not getting set

* Fix crashing when refreshing progress bar

* comments for things to clean up

* Wrap view model properties without having useless local fields

* Move some code from WinForms to the Blazor view model

* Fix crash when calling a WinForms modal dialog from Blazor while debugging

* Remove references to nonexitent FrEee.UI project

* Add comment, that's weird that it's inconsistent

* Fix progress bar not refreshing again

* Add resource display but it's broken. Also added Blazor test form.

* Actually render the Blazor view in the resource display

* Try to fix the resource displays' colors and icons but not quite. Icons are not appearing, not sure how to make that work in Blazor outside of wwwroot. Seems like we need middleware but how to install it in a WinForms app? Also trying to fix the margins on the GameProgressBar but no luck there iether...

* Fixed resource displays (hopefully the symlinks will persist in the builds or we'll need to find another way to display images in Blazor)

* Fix Blazor image path issue by moving index.html to the root. This feels so dastardly...

* Delete empty wwwroot folder

* Install a couple NuGet packages to fix inability to put Blazor-based controls on form

* Combine multile ResourceDisplays into a ResourceQuantityDisplay

* Refactor some code into a base BlazorControl

* Remove unneeded analytics packages and a using statement

* Adjust a few things in the BlazorControl

* Get rid of a generic type parameter, somehow partially fix resource displays for empire inventory

* Get rid of the other generic type parameter for BlazorControl

* Fix BlazorWebView based controls in WinForms designer by using some sort of abtsractoin

* Fix resource inventory display

* Delerte an unused method

* Move controls that have been converted to Blazor to their own namespace, for organization

* Adjust namespaces of moved controls

* Moved the wrong control oops

* GamePictureBox is now Blazor; for some reason there's a scvroll bar

* Actually show text on full size GamePictureBox forms

* Display name of Blazor control in WinForms designer

* Add a pie chart Blazor component, the HTML checks out in a browser bu tfor some reason it's not rendering in the test form.

* Fix pie chart rendering

* Blazor galaxy map WIP

* Somewhat fix layout of galaxy map, somewhat break that of resource display

* Fix image alignment/sizing

* Implement presence map mode

* Set up MEF and dropdown for galaxy map modes

* Trying to draw warp lines

* Draw warp lines, but they're scaled wrong

* Delete unused function

* Cleaning up the map, add green grid because green grids are cool

* Don't place star systems off the lower or right edges of the map

* Fixed map rendering

* Render the selected star system with a white border

* Clicking a system actually selects it

* Get rid of background click handler for galaxy map because it's causing weird errors when clicked repeatedly

* Fix numbers being hidden on the resourve display

* Fix vertical alignment of resource display text
  • Loading branch information
ekolis authored Apr 21, 2024
1 parent b5c561b commit 05733a0
Show file tree
Hide file tree
Showing 323 changed files with 8,563 additions and 6,705 deletions.
File renamed without changes.
File renamed without changes.
8 changes: 8 additions & 0 deletions FrEee.Assets/FrEee.Assets.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@
</ItemGroup>
<ItemGroup>
<None Include="..\.editorconfig" Link=".editorconfig" />
</ItemGroup>
<ItemGroup>
<None Update="Fonts\kwajong-readme.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Fonts\Kwajong.otf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<PropertyGroup>
<EnableSourceControlManagerQueries>false</EnableSourceControlManagerQueries>
Expand Down
3 changes: 3 additions & 0 deletions FrEee.UI.Blazor/Component1.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<div class="my-component">
This component is defined in the <strong>FrEee.UI.Blazor</strong> library.
</div>
6 changes: 6 additions & 0 deletions FrEee.UI.Blazor/Component1.razor.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.my-component {
border: 2px dashed red;
padding: 1em;
margin: 1em 0;
background-image: url('background.png');
}
37 changes: 37 additions & 0 deletions FrEee.UI.Blazor/ExampleJsInterop.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using Microsoft.JSInterop;

namespace FrEee.UI.Blazor
{
// This class provides an example of how JavaScript functionality can be wrapped
// in a .NET class for easy consumption. The associated JavaScript module is
// loaded on demand when first needed.
//
// This class can be registered as scoped DI service and then injected into Blazor
// components for use.

public class ExampleJsInterop : IAsyncDisposable
{
private readonly Lazy<Task<IJSObjectReference>> moduleTask;

public ExampleJsInterop(IJSRuntime jsRuntime)
{
moduleTask = new(() => jsRuntime.InvokeAsync<IJSObjectReference>(
"import", "./_content/FrEee.UI.Blazor/exampleJsInterop.js").AsTask());
}

public async ValueTask<string> Prompt(string message)
{
var module = await moduleTask.Value;
return await module.InvokeAsync<string>("showPrompt", message);
}

public async ValueTask DisposeAsync()
{
if (moduleTask.IsValueCreated)
{
var module = await moduleTask.Value;
await module.DisposeAsync();
}
}
}
}
6 changes: 6 additions & 0 deletions FrEee.UI.Blazor/FodyWeavers.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Installs weavers in the Fody plugin-->
<Weavers xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<!-- Automatically implements INotifyPropertyChanged change notifications for properties in classes that implement the interface -->
<PropertyChanged />
</Weavers>
74 changes: 74 additions & 0 deletions FrEee.UI.Blazor/FodyWeavers.xsd
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="https://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="PropertyChanged" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="InjectOnPropertyNameChanged" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to control if the On_PropertyName_Changed feature is enabled.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="TriggerDependentProperties" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to control if the Dependent properties feature is enabled.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="EnableIsChangedProperty" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to control if the IsChanged property feature is enabled.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="EventInvokerNames" type="xs:string">
<xs:annotation>
<xs:documentation>Used to change the name of the method that fires the notify event. This is a string that accepts multiple values in a comma separated form.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="CheckForEquality" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to control if equality checks should be inserted. If false, equality checking will be disabled for the project.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="CheckForEqualityUsingBaseEquals" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to control if equality checks should use the Equals method resolved from the base class.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="UseStaticEqualsFromBase" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to control if equality checks should use the static Equals method resolved from the base class.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="SuppressWarnings" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to turn off build warnings from this weaver.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="SuppressOnPropertyNameChangedWarning" type="xs:boolean">
<xs:annotation>
<xs:documentation>Used to turn off build warnings about mismatched On_PropertyName_Changed methods.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>
35 changes: 35 additions & 0 deletions FrEee.UI.Blazor/FrEee.UI.Blazor.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">

<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>


<ItemGroup>
<SupportedPlatform Include="browser" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Excubo.Blazor.Canvas" Version="3.2.46" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.3" />
<PackageReference Include="PropertyChanged.Fody" Version="4.1.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\FrEee.Assets\FrEee.Assets.csproj" />
<ProjectReference Include="..\FrEee\FrEee.csproj" />
</ItemGroup>

<ItemGroup>
<Content Update="Views\ImageDisplay.razor">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
</ItemGroup>

<PropertyGroup>
<EnableSourceControlManagerQueries>false</EnableSourceControlManagerQueries>
</PropertyGroup>

</Project>
100 changes: 100 additions & 0 deletions FrEee.UI.Blazor/Views/GalaxyMap.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
@using System.Drawing
@using System.ComponentModel
@using System.Numerics
@using FrEee.Extensions
@using Excubo.Blazor.Canvas

@code {
[Parameter]
public GalaxyMapViewModel VM { get; set; } = new();

/// <summary>
/// When the view model's properties change, update the UI.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ViewModelPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
StateHasChanged();
}

protected override void OnInitialized()
{
VM.PropertyChanged += ViewModelPropertyChanged;
}

public void Dispose()
{
VM.PropertyChanged -= ViewModelPropertyChanged;
}

private Canvas helper_canvas;
private ElementReference normal_canvas;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
var size = VM.Scale + 1;
var xoffset = VM.Width / 2d;
var yoffset = VM.Height / 2d;

await using (var ctx = await helper_canvas.GetContext2DAsync())
{
await ctx.ClearRectAsync(0, 0, VM.Width, VM.Height);
await ctx.SetTransformAsync(size, 0, 0, size, (xoffset + 0.5) * size, (yoffset + 0.5) * size);
await ctx.RestoreAsync();
await ctx.SaveAsync();
await ctx.StrokeStyleAsync("white");
await ctx.LineWidthAsync(0.1);
foreach (var connections in VM.WarpGraph.Connections)
{
var src = connections.Key;
foreach (var dest in connections.Value)
{
// TODO: display one way warps differently (arrows, incomplete lines, gradients?)
await ctx.MoveToAsync(src.Location.X, src.Location.Y);
await ctx.LineToAsync(dest.Location.X, dest.Location.Y);
await ctx.StrokeAsync();
}
}
}

await base.OnAfterRenderAsync(firstRender);
}
}

<div>
<div class="fill">
<ImageDisplay VM="@VM.BackgroundImageVM" />
</div>
<div class="zoom overlay" style="aspect-ratio: @(VM.AspectRatio)">
<Canvas @ref="helper_canvas" class="zoom" width="@VM.ScaledWidth" height="@VM.ScaledHeight"/>
</div>
<div class="zoom overlay2" style="aspect-ratio: @(VM.AspectRatio); display: grid">
@for (var x = VM.MinX; x <= VM.MaxX; x++)
{
for (var y = VM.MinY; y <= VM.MaxY; y++)
{
var sysloc = VM.StarSystemLocations.SingleOrDefault(q => q.Location.X == x && q.Location.Y == y);
var row = y - VM.MinY + 1;
var col = x - VM.MinX + 1;
if (sysloc is not null)
{
// system pie
string border = "1px solid black";
if (sysloc.Item == VM.SelectedStarSystem)
{
// making the border thicker breaks the layout for some reason
border = "1px solid white";
}
<div style="grid-row: @row; grid-column: @col; width: 1fr; height: 1fr; aspect-ratio: 1; border: @border">
<PieChart T="int" VM="@(VM.Mode.GetStarSystemViewModel(sysloc.Item, VM.StarSystemClicked))" />
</div>
}
else
{
// placeholder
<div style="grid-row: @row; grid-column: @col; width: 1fr; height: 1fr; aspect-ratio: 1; border: 1px dotted #00ff0044" />
}
}
}
</div>
</div>
36 changes: 36 additions & 0 deletions FrEee.UI.Blazor/Views/GalaxyMapModes/GalaxyMapModeLibrary.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;

namespace FrEee.UI.Blazor.Views.GalaxyMapModes;

public class GalaxyMapModeLibrary
{
private CompositionContainer container;

private static GalaxyMapModeLibrary Instance { get; set; } = new GalaxyMapModeLibrary();

private GalaxyMapModeLibrary()
{
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(typeof(IGalaxyMapMode).Assembly));
container = new CompositionContainer(catalog);
container.ComposeParts(this);
}

[ImportMany(typeof(IGalaxyMapMode))]
public IEnumerable<IGalaxyMapMode> AllOfThem { get; private set; }

public static IEnumerable<IGalaxyMapMode> All => Instance.AllOfThem;

public static IGalaxyMapMode? Find<T>(string name = "")
where T : IGalaxyMapMode
{
var results = All.OfType<T>();
if (!string.IsNullOrWhiteSpace(name))
{
results = results.Where(q => q.Name.Contains(name));
}
return results.FirstOrDefault();
}
}
27 changes: 27 additions & 0 deletions FrEee.UI.Blazor/Views/GalaxyMapModes/IGalaxyMapMode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FrEee.Objects.Space;

namespace FrEee.UI.Blazor.Views.GalaxyMapModes;

/// <summary>
/// Rendering modes for the galaxy map.
/// </summary>
public interface IGalaxyMapMode
{
/// <summary>
/// The name of this mode.
/// </summary>
string Name { get; }

/// <summary>
/// Gets a <see cref="PieChartViewModel{T}"/> to represent a star system.
/// </summary>
/// <param name="starSystem">The star system to represent.</param>
/// <param name="starSystemClicked">Action to take when the star system is clicked.</param>
/// <returns>The view model.</returns>
PieChartViewModel<int> GetStarSystemViewModel(StarSystem starSystem, Action<StarSystem> starSystemClicked);
}
Loading

0 comments on commit 05733a0

Please sign in to comment.