feat: 前端界面优化 (#48)

Co-authored-by: jackfiled <xcrenchangjun@outlook.com>
Reviewed-on: PostGuard/Canon#48
Co-authored-by: Ichirinko <1621543655@qq.com>
Co-committed-by: Ichirinko <1621543655@qq.com>
This commit is contained in:
Ichirinko 2024-04-21 16:30:44 +08:00 committed by jackfiled
parent 3f5cfb13a6
commit a95987b3ce
14 changed files with 114 additions and 95 deletions

View File

@ -17,12 +17,13 @@
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.3" /> <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.3" />
<PackageReference Include="MongoDB.Driver.GridFS" Version="2.25.0" /> <PackageReference Include="MongoDB.Driver.GridFS" Version="2.25.0" />
<PackageReference Include="MongoDB.EntityFrameworkCore" Version="7.0.0-preview.1" /> <PackageReference Include="MongoDB.EntityFrameworkCore" Version="7.0.0-preview.1" />
<PackageReference Include="SkiaSharp" Version="2.88.8" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="2.88.8" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Canon.Core\Canon.Core.csproj" /> <ProjectReference Include="..\Canon.Core\Canon.Core.csproj" />
<ProjectReference Include="..\Canon.Visualization\Canon.Visualization.csproj" />
</ItemGroup> </ItemGroup>
<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish"> <Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">

View File

@ -1,6 +1,6 @@
using SkiaSharp; using SkiaSharp;
namespace Canon.Visualization.Models; namespace Canon.Server.Models;
public sealed class Brush(SKCanvas canvas) : IDisposable public sealed class Brush(SKCanvas canvas) : IDisposable
{ {

View File

@ -1,7 +1,7 @@
using Canon.Core.SyntaxNodes; using Canon.Core.SyntaxNodes;
using SkiaSharp; using SkiaSharp;
namespace Canon.Visualization.Models; namespace Canon.Server.Models;
/// <summary> /// <summary>
/// 展示树节点 /// 展示树节点
@ -9,7 +9,7 @@ namespace Canon.Visualization.Models;
/// </summary> /// </summary>
public class PresentableTreeNode public class PresentableTreeNode
{ {
public float X { get; set; } = -1; public float X { get; set; }
public float Y { get; set; } public float Y { get; set; }
public SKPoint Position => new(X, Y); public SKPoint Position => new(X, Y);

View File

@ -2,7 +2,6 @@ using Canon.Core.Abstractions;
using Canon.Core.LexicalParser; using Canon.Core.LexicalParser;
using Canon.Server.Extensions; using Canon.Server.Extensions;
using Canon.Server.Services; using Canon.Server.Services;
using Canon.Visualization.Services;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
WebApplicationBuilder builder = WebApplication.CreateBuilder(args); WebApplicationBuilder builder = WebApplication.CreateBuilder(args);

View File

@ -2,7 +2,6 @@
using Canon.Core.LexicalParser; using Canon.Core.LexicalParser;
using Canon.Core.SyntaxNodes; using Canon.Core.SyntaxNodes;
using Canon.Server.Models; using Canon.Server.Models;
using Canon.Visualization.Services;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace Canon.Server.Services; namespace Canon.Server.Services;

View File

@ -0,0 +1,89 @@
using Canon.Core.SyntaxNodes;
using Canon.Server.Models;
using SkiaSharp;
namespace Canon.Server.Services;
public class SyntaxTreePresentationService
{
private const float Scale = 150;
public Stream Present(ProgramStruct root)
{
PresentableTreeNode presentableTreeRoot = PresentableTreeNode.Build(root);
ScaleTree(presentableTreeRoot);
(float height, float width) = presentableTreeRoot.CalculateImageSize();
using SKSurface surface = SKSurface.Create(
new SKImageInfo((int)(width + 2 * Scale), (int)(height * Scale)));
surface.Canvas.Clear(SKColors.White);
using Brush brush = new(surface.Canvas);
DrawNode(presentableTreeRoot, brush);
using SKImage image = surface.Snapshot();
SKData data = image.Encode();
return data.AsStream();
}
private void DrawNode(PresentableTreeNode node, Brush brush)
{
foreach (PresentableTreeNode child in node.Children)
{
brush.DrawLine(node.Position, child.Position);
DrawNode(child, brush);
}
brush.DrawText(node.Position, node.DisplayText);
}
private void ScaleTree(PresentableTreeNode root)
{
Queue<PresentableTreeNode> queue = [];
queue.Enqueue(root);
float minX = float.MaxValue;
// 第一次遍历
// 放大坐标并获得最左侧的节点X坐标
while (queue.Count != 0)
{
PresentableTreeNode node = queue.Dequeue();
node.X *= Scale;
node.X += Scale;
node.Y *= Scale;
node.Y += Scale;
minX = float.Min(minX, node.X);
foreach (PresentableTreeNode child in node.Children)
{
queue.Enqueue(child);
}
}
if (minX >= Scale)
{
// 判断最左侧的节点位置是否正确
return;
}
float delta = Scale - minX;
// 第二次遍历调整位置
queue.Enqueue(root);
while (queue.Count != 0)
{
PresentableTreeNode node = queue.Dequeue();
node.X += delta;
foreach (PresentableTreeNode child in node.Children)
{
queue.Enqueue(child);
}
}
}
}

View File

@ -15,6 +15,7 @@
"@fontsource/roboto": "^5.0.12", "@fontsource/roboto": "^5.0.12",
"@mui/icons-material": "^5.15.14", "@mui/icons-material": "^5.15.14",
"@mui/material": "^5.15.14", "@mui/material": "^5.15.14",
"notistack": "^3.0.1",
"openapi-fetch": "^0.9.3", "openapi-fetch": "^0.9.3",
"openapi-typescript": "^6.7.5", "openapi-typescript": "^6.7.5",
"react": "^18.2.0", "react": "^18.2.0",

View File

@ -4,6 +4,7 @@ import {CSSProperties, useState} from "react";
import {OutputField} from "./OutputField.tsx"; import {OutputField} from "./OutputField.tsx";
import createClient from "openapi-fetch"; import createClient from "openapi-fetch";
import * as openapi from '../openapi'; import * as openapi from '../openapi';
import {enqueueSnackbar} from "notistack";
const client = createClient<openapi.paths>(); const client = createClient<openapi.paths>();
@ -23,6 +24,7 @@ export function Index() {
id: "", id: "",
imageAddress: "pic/uncompiled.png" imageAddress: "pic/uncompiled.png"
}); });
//const {enqueueSnackbar} = useSnackbar();
const handleValueChange = (value: string) => { const handleValueChange = (value: string) => {
setInputValue(value); setInputValue(value);
}; };
@ -43,6 +45,10 @@ export function Index() {
id: data.id, id: data.id,
imageAddress: data.imageAddress imageAddress: data.imageAddress
}) })
enqueueSnackbar("编译成功", {variant: "success", anchorOrigin: {vertical: 'top', horizontal: 'right'}});
} else {
// error
enqueueSnackbar("编译失败", {variant: "error", anchorOrigin: {vertical: 'top', horizontal: 'right'}});
} }
} }
@ -72,12 +78,12 @@ export function Index() {
<div className={"content"} <div className={"content"}
style={contentClassCss}> style={contentClassCss}>
<Grid container spacing={2} style = {{width: "100%",height: "100%"}}> <Grid container spacing={2} style={{width: "100%", height: "100%"}}>
<Grid item xs={12} sm={6} style = {{width: "100%",height: "100%"}}> <Grid item xs={12} sm={6} style={{width: "100%", height: "100%"}}>
<InputField onValueChange={handleValueChange}> <InputField onValueChange={handleValueChange}>
</InputField> </InputField>
</Grid> </Grid>
<Grid item xs={12} sm={6} style = {{width: "100%",height: "100%"}}> <Grid item xs={12} sm={6} style={{width: "100%", height: "100%"}}>
<OutputField imgPath={outputValue.imageAddress}> <OutputField imgPath={outputValue.imageAddress}>
</OutputField> </OutputField>
</Grid> </Grid>

View File

@ -1,15 +1,17 @@
import React from 'react' import React from 'react'
import ReactDOM from 'react-dom/client' import ReactDOM from 'react-dom/client'
import { App } from './App.tsx' import {App} from './App.tsx'
import '@fontsource/roboto/300.css'; import '@fontsource/roboto/300.css';
import '@fontsource/roboto/400.css'; import '@fontsource/roboto/400.css';
import '@fontsource/roboto/500.css'; import '@fontsource/roboto/500.css';
import '@fontsource/roboto/700.css'; import '@fontsource/roboto/700.css';
import { CssBaseline } from '@mui/material'; import {CssBaseline} from '@mui/material';
import {SnackbarProvider} from "notistack";
ReactDOM.createRoot(document.getElementById('root')!).render( ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode> <React.StrictMode>
<CssBaseline /> <CssBaseline/>
<App /> <SnackbarProvider/>
</React.StrictMode>, <App/>
</React.StrictMode>,
) )

View File

@ -24,7 +24,6 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Canon.Core\Canon.Core.csproj" /> <ProjectReference Include="..\Canon.Core\Canon.Core.csproj" />
<ProjectReference Include="..\Canon.Visualization\Canon.Visualization.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -84,8 +84,9 @@ public class PascalGrammarTests
{ {
const string program = """ const string program = """
program varTest; program varTest;
var a : char; var a : integer;
begin begin
a := 9 div 1;
end. end.
"""; """;

View File

@ -1,18 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Canon.Core\Canon.Core.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SkiaSharp" Version="2.88.7" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="2.88.7"/>
</ItemGroup>
</Project>

View File

@ -1,54 +0,0 @@
using Canon.Core.SyntaxNodes;
using Canon.Visualization.Models;
using SkiaSharp;
namespace Canon.Visualization.Services;
public class SyntaxTreePresentationService
{
private const float Scale = 150;
public Stream Present(ProgramStruct root)
{
PresentableTreeNode presentableTreeRoot = PresentableTreeNode.Build(root);
ScaleTree(presentableTreeRoot);
(float height, float width) = presentableTreeRoot.CalculateImageSize();
using SKSurface surface = SKSurface.Create(
new SKImageInfo((int)(width + 2 * Scale), (int)(height * Scale)));
surface.Canvas.Clear(SKColors.White);
using Brush brush = new(surface.Canvas);
DrawNode(presentableTreeRoot, brush);
using SKImage image = surface.Snapshot();
SKData data = image.Encode();
return data.AsStream();
}
private void DrawNode(PresentableTreeNode node, Brush brush)
{
foreach (PresentableTreeNode child in node.Children)
{
brush.DrawLine(node.Position, child.Position);
DrawNode(child, brush);
}
brush.DrawText(node.Position, node.DisplayText);
}
private void ScaleTree(PresentableTreeNode node)
{
node.X *= Scale;
node.X += Scale;
node.Y *= Scale;
node.Y += Scale;
foreach (PresentableTreeNode child in node.Children)
{
ScaleTree(child);
}
}
}

View File

@ -26,8 +26,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{
.gitea\workflows\build.yaml = .gitea\workflows\build.yaml .gitea\workflows\build.yaml = .gitea\workflows\build.yaml
EndProjectSection EndProjectSection
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Canon.Visualization", "Canon.Visualization\Canon.Visualization.csproj", "{23644467-2BCB-422D-8942-C20AF4A7F429}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Canon.Server", "Canon.Server\Canon.Server.csproj", "{401112EA-1A87-4D1C-9B6D-085309F4137E}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Canon.Server", "Canon.Server\Canon.Server.csproj", "{401112EA-1A87-4D1C-9B6D-085309F4137E}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Canon.Generator", "Canon.Generator\Canon.Generator.csproj", "{32C103C4-589C-4DC2-B173-55B1799B62CE}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Canon.Generator", "Canon.Generator\Canon.Generator.csproj", "{32C103C4-589C-4DC2-B173-55B1799B62CE}"
@ -53,10 +51,6 @@ Global
{E5F2B97B-3766-466D-9309-BA361F0CE15E}.Debug|Any CPU.Build.0 = Debug|Any CPU {E5F2B97B-3766-466D-9309-BA361F0CE15E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E5F2B97B-3766-466D-9309-BA361F0CE15E}.Release|Any CPU.ActiveCfg = Release|Any CPU {E5F2B97B-3766-466D-9309-BA361F0CE15E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E5F2B97B-3766-466D-9309-BA361F0CE15E}.Release|Any CPU.Build.0 = Release|Any CPU {E5F2B97B-3766-466D-9309-BA361F0CE15E}.Release|Any CPU.Build.0 = Release|Any CPU
{23644467-2BCB-422D-8942-C20AF4A7F429}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{23644467-2BCB-422D-8942-C20AF4A7F429}.Debug|Any CPU.Build.0 = Debug|Any CPU
{23644467-2BCB-422D-8942-C20AF4A7F429}.Release|Any CPU.ActiveCfg = Release|Any CPU
{23644467-2BCB-422D-8942-C20AF4A7F429}.Release|Any CPU.Build.0 = Release|Any CPU
{401112EA-1A87-4D1C-9B6D-085309F4137E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {401112EA-1A87-4D1C-9B6D-085309F4137E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{401112EA-1A87-4D1C-9B6D-085309F4137E}.Debug|Any CPU.Build.0 = Debug|Any CPU {401112EA-1A87-4D1C-9B6D-085309F4137E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{401112EA-1A87-4D1C-9B6D-085309F4137E}.Release|Any CPU.ActiveCfg = Release|Any CPU {401112EA-1A87-4D1C-9B6D-085309F4137E}.Release|Any CPU.ActiveCfg = Release|Any CPU