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:
parent
3f5cfb13a6
commit
a95987b3ce
|
@ -17,12 +17,13 @@
|
|||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.3" />
|
||||
<PackageReference Include="MongoDB.Driver.GridFS" Version="2.25.0" />
|
||||
<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" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Canon.Core\Canon.Core.csproj" />
|
||||
<ProjectReference Include="..\Canon.Visualization\Canon.Visualization.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using SkiaSharp;
|
||||
|
||||
namespace Canon.Visualization.Models;
|
||||
namespace Canon.Server.Models;
|
||||
|
||||
public sealed class Brush(SKCanvas canvas) : IDisposable
|
||||
{
|
|
@ -1,7 +1,7 @@
|
|||
using Canon.Core.SyntaxNodes;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Canon.Visualization.Models;
|
||||
namespace Canon.Server.Models;
|
||||
|
||||
/// <summary>
|
||||
/// 展示树节点
|
||||
|
@ -9,7 +9,7 @@ namespace Canon.Visualization.Models;
|
|||
/// </summary>
|
||||
public class PresentableTreeNode
|
||||
{
|
||||
public float X { get; set; } = -1;
|
||||
public float X { get; set; }
|
||||
public float Y { get; set; }
|
||||
|
||||
public SKPoint Position => new(X, Y);
|
|
@ -2,7 +2,6 @@ using Canon.Core.Abstractions;
|
|||
using Canon.Core.LexicalParser;
|
||||
using Canon.Server.Extensions;
|
||||
using Canon.Server.Services;
|
||||
using Canon.Visualization.Services;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
using Canon.Core.LexicalParser;
|
||||
using Canon.Core.SyntaxNodes;
|
||||
using Canon.Server.Models;
|
||||
using Canon.Visualization.Services;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Canon.Server.Services;
|
||||
|
|
89
Canon.Server/Services/SyntaxTreePresentationService.cs
Normal file
89
Canon.Server/Services/SyntaxTreePresentationService.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@
|
|||
"@fontsource/roboto": "^5.0.12",
|
||||
"@mui/icons-material": "^5.15.14",
|
||||
"@mui/material": "^5.15.14",
|
||||
"notistack": "^3.0.1",
|
||||
"openapi-fetch": "^0.9.3",
|
||||
"openapi-typescript": "^6.7.5",
|
||||
"react": "^18.2.0",
|
||||
|
|
|
@ -4,6 +4,7 @@ import {CSSProperties, useState} from "react";
|
|||
import {OutputField} from "./OutputField.tsx";
|
||||
import createClient from "openapi-fetch";
|
||||
import * as openapi from '../openapi';
|
||||
import {enqueueSnackbar} from "notistack";
|
||||
|
||||
const client = createClient<openapi.paths>();
|
||||
|
||||
|
@ -23,6 +24,7 @@ export function Index() {
|
|||
id: "",
|
||||
imageAddress: "pic/uncompiled.png"
|
||||
});
|
||||
//const {enqueueSnackbar} = useSnackbar();
|
||||
const handleValueChange = (value: string) => {
|
||||
setInputValue(value);
|
||||
};
|
||||
|
@ -43,6 +45,10 @@ export function Index() {
|
|||
id: data.id,
|
||||
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"}
|
||||
style={contentClassCss}>
|
||||
<Grid container spacing={2} style = {{width: "100%",height: "100%"}}>
|
||||
<Grid item xs={12} sm={6} 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%"}}>
|
||||
<InputField onValueChange={handleValueChange}>
|
||||
</InputField>
|
||||
</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>
|
||||
</Grid>
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import { App } from './App.tsx'
|
||||
import {App} from './App.tsx'
|
||||
import '@fontsource/roboto/300.css';
|
||||
import '@fontsource/roboto/400.css';
|
||||
import '@fontsource/roboto/500.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(
|
||||
<React.StrictMode>
|
||||
<CssBaseline />
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
<React.StrictMode>
|
||||
<CssBaseline/>
|
||||
<SnackbarProvider/>
|
||||
<App/>
|
||||
</React.StrictMode>,
|
||||
)
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Canon.Core\Canon.Core.csproj" />
|
||||
<ProjectReference Include="..\Canon.Visualization\Canon.Visualization.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -84,8 +84,9 @@ public class PascalGrammarTests
|
|||
{
|
||||
const string program = """
|
||||
program varTest;
|
||||
var a : char;
|
||||
var a : integer;
|
||||
begin
|
||||
a := 9 div 1;
|
||||
end.
|
||||
""";
|
||||
|
||||
|
|
|
@ -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>
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -26,8 +26,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{
|
|||
.gitea\workflows\build.yaml = .gitea\workflows\build.yaml
|
||||
EndProjectSection
|
||||
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}"
|
||||
EndProject
|
||||
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}.Release|Any CPU.ActiveCfg = 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.Build.0 = Debug|Any CPU
|
||||
{401112EA-1A87-4D1C-9B6D-085309F4137E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
|
Loading…
Reference in New Issue
Block a user