mirror of
https://onedev.site.tesses.net/crosslang/crosslangdevstudio
synced 2026-02-08 09:15:45 +00:00
First Commit
This commit is contained in:
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
.vscode
|
||||||
|
bin
|
||||||
|
obj
|
||||||
|
out
|
||||||
|
build
|
||||||
24
App.axaml
Normal file
24
App.axaml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<Application xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
x:Class="CrossLangDevStudio.App"
|
||||||
|
xmlns:local="using:CrossLangDevStudio"
|
||||||
|
RequestedThemeVariant="Default"
|
||||||
|
xmlns:themes="clr-namespace:Tabalonia.Themes.Custom;assembly=Tabalonia">
|
||||||
|
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
|
||||||
|
|
||||||
|
<Application.DataTemplates>
|
||||||
|
<local:ViewLocator/>
|
||||||
|
</Application.DataTemplates>
|
||||||
|
|
||||||
|
<Application.Styles>
|
||||||
|
<FluentTheme DensityStyle="Normal" />
|
||||||
|
|
||||||
|
<!-- <fluent:FluentTheme /> -->
|
||||||
|
|
||||||
|
<themes:CustomTheme />
|
||||||
|
|
||||||
|
</Application.Styles>
|
||||||
|
<Application.Resources>
|
||||||
|
<FontFamily x:Key="AardvarkFont">avares://CrossLangDevStudio/Assets/Fonts#Aardvark Fixed Regular</FontFamily>
|
||||||
|
</Application.Resources>
|
||||||
|
</Application>
|
||||||
47
App.axaml.cs
Normal file
47
App.axaml.cs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
|
using Avalonia.Data.Core;
|
||||||
|
using Avalonia.Data.Core.Plugins;
|
||||||
|
using System.Linq;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using CrossLangDevStudio.ViewModels;
|
||||||
|
using CrossLangDevStudio.Views;
|
||||||
|
|
||||||
|
namespace CrossLangDevStudio;
|
||||||
|
|
||||||
|
public partial class App : Application
|
||||||
|
{
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnFrameworkInitializationCompleted()
|
||||||
|
{
|
||||||
|
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
|
{
|
||||||
|
// Avoid duplicate validations from both Avalonia and the CommunityToolkit.
|
||||||
|
// More info: https://docs.avaloniaui.net/docs/guides/development-guides/data-validation#manage-validationplugins
|
||||||
|
DisableAvaloniaDataAnnotationValidation();
|
||||||
|
desktop.MainWindow = new MainWindow
|
||||||
|
{
|
||||||
|
DataContext = new MainWindowViewModel(desktop.Args ?? []),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
base.OnFrameworkInitializationCompleted();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DisableAvaloniaDataAnnotationValidation()
|
||||||
|
{
|
||||||
|
// Get an array of plugins to remove
|
||||||
|
var dataValidationPluginsToRemove =
|
||||||
|
BindingPlugins.DataValidators.OfType<DataAnnotationsValidationPlugin>().ToArray();
|
||||||
|
|
||||||
|
// remove each entry found
|
||||||
|
foreach (var plugin in dataValidationPluginsToRemove)
|
||||||
|
{
|
||||||
|
BindingPlugins.DataValidators.Remove(plugin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
Assets/Fonts/aardvark-fixed-regular.ttf
Normal file
BIN
Assets/Fonts/aardvark-fixed-regular.ttf
Normal file
Binary file not shown.
BIN
Assets/crosslang.ico
Normal file
BIN
Assets/crosslang.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 45 KiB |
31
CrossLangDevStudio.csproj
Normal file
31
CrossLangDevStudio.csproj
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
||||||
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
|
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Models\" />
|
||||||
|
<AvaloniaResource Include="Assets\**" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Avalonia" Version="11.3.3" />
|
||||||
|
|
||||||
|
<PackageReference Include="Avalonia.Desktop" Version="11.3.3" />
|
||||||
|
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.3" />
|
||||||
|
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.3" />
|
||||||
|
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||||
|
<PackageReference Include="Avalonia.Diagnostics" Version="11.3.3">
|
||||||
|
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
|
||||||
|
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
|
<PackageReference Include="Tabalonia" Version="0.10.7" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
407
CrossLangFile.cs
Normal file
407
CrossLangFile.cs
Normal file
@@ -0,0 +1,407 @@
|
|||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Media;
|
||||||
|
using Avalonia.Media.Imaging;
|
||||||
|
using Avalonia.Platform;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace CrossLangDevStudio;
|
||||||
|
|
||||||
|
|
||||||
|
public class CrossLangFile
|
||||||
|
{
|
||||||
|
const string CROSSVM = "TCROSSVM";
|
||||||
|
public void Load(string path)
|
||||||
|
{
|
||||||
|
if (File.Exists(path))
|
||||||
|
using (var strm = File.OpenRead(path))
|
||||||
|
Load(strm);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void Load(Stream strm)
|
||||||
|
{
|
||||||
|
byte[] header = new byte[18];
|
||||||
|
strm.ReadExactly(header);
|
||||||
|
for (int i = 0; i < CROSSVM.Length; i++)
|
||||||
|
{
|
||||||
|
if (header[i] != (byte)CROSSVM[i]) throw new Exception("Invalid signature");
|
||||||
|
}
|
||||||
|
|
||||||
|
Version = new CrossLangVersion(header, 13);
|
||||||
|
|
||||||
|
uint ReadInt()
|
||||||
|
{
|
||||||
|
strm.ReadExactly(header, 0, 4);
|
||||||
|
|
||||||
|
if (BitConverter.IsLittleEndian)
|
||||||
|
Array.Reverse(header, 0, 4);
|
||||||
|
return BitConverter.ToUInt32(header, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadString()
|
||||||
|
{
|
||||||
|
uint length = ReadInt();
|
||||||
|
byte[] data = new byte[length];
|
||||||
|
strm.ReadExactly(data, 0, data.Length);
|
||||||
|
Strings.Add(Encoding.UTF8.GetString(data));
|
||||||
|
}
|
||||||
|
string ReadSectionName()
|
||||||
|
{
|
||||||
|
strm.ReadExactly(header, 0, 4);
|
||||||
|
return Encoding.UTF8.GetString(header, 0, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
string GetString()
|
||||||
|
{
|
||||||
|
uint index = ReadInt();
|
||||||
|
return Strings[(int)index];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uint count = ReadInt();
|
||||||
|
for (uint i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
string section = ReadSectionName();
|
||||||
|
uint sectionSize = ReadInt();
|
||||||
|
switch (section)
|
||||||
|
{
|
||||||
|
case "STRS":
|
||||||
|
{
|
||||||
|
uint theCount = ReadInt();
|
||||||
|
for (uint j = 0; j < theCount; j++)
|
||||||
|
{
|
||||||
|
ReadString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "NAME":
|
||||||
|
Name = GetString();
|
||||||
|
break;
|
||||||
|
case "INFO":
|
||||||
|
Info = GetString();
|
||||||
|
break;
|
||||||
|
case "ICON":
|
||||||
|
Icon = (int)ReadInt();
|
||||||
|
break;
|
||||||
|
case "RESO":
|
||||||
|
byte[] data = new byte[sectionSize];
|
||||||
|
strm.ReadExactly(data, 0, data.Length);
|
||||||
|
Resources.Add(data);
|
||||||
|
break;
|
||||||
|
case "DEPS":
|
||||||
|
if (sectionSize == 9)
|
||||||
|
{
|
||||||
|
string name = GetString();
|
||||||
|
strm.ReadExactly(header, 0, 5);
|
||||||
|
var version = new CrossLangVersion(header);
|
||||||
|
Dependencies.Add(new CrossLangDependency() { Name = name, Version = version });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
strm.Position += sectionSize;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
public string Name { get; private set; } = "";
|
||||||
|
public CrossLangVersion Version { get; private set; }
|
||||||
|
|
||||||
|
public string Info { get; private set; } = "";
|
||||||
|
|
||||||
|
public int Icon { get; private set; } = -1;
|
||||||
|
|
||||||
|
public CrossLangInfo InfoObject => JsonConvert.DeserializeObject<CrossLangInfo>(Info) ?? new CrossLangInfo();
|
||||||
|
|
||||||
|
public Bitmap GetIcon(int height=128)
|
||||||
|
{
|
||||||
|
using (Stream strm = (Icon > -1 && Icon < Resources.Count) ? new MemoryStream(Resources[Icon], false) : AssetLoader.Open(new Uri("avares://CrossLangDevStudio/Assets/crosslang.ico")))
|
||||||
|
{
|
||||||
|
return Bitmap.DecodeToHeight(strm, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal string GetTemplatePrettyName()
|
||||||
|
{
|
||||||
|
var info = InfoObject;
|
||||||
|
if (info is not null)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(info.TemplateNamePretty)) return info.TemplateNamePretty;
|
||||||
|
if (!string.IsNullOrWhiteSpace(info.TemplateName)) return info.TemplateName;
|
||||||
|
}
|
||||||
|
return this.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<string> Strings { get; } = new List<string>();
|
||||||
|
|
||||||
|
public List<byte[]> Resources { get; } = new List<byte[]>();
|
||||||
|
|
||||||
|
public List<CrossLangDependency> Dependencies { get; } = new List<CrossLangDependency>();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class CrossLangConfig
|
||||||
|
{
|
||||||
|
[JsonProperty("name")]
|
||||||
|
public string? Name { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("version")]
|
||||||
|
public CrossLangVersion Version { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("dependencies")]
|
||||||
|
public List<CrossLangDependency> Dependencies { get; set; } = new List<CrossLangDependency>();
|
||||||
|
|
||||||
|
[JsonProperty("info")]
|
||||||
|
public CrossLangInfo Info { get; set; } = new CrossLangInfo();
|
||||||
|
|
||||||
|
[JsonProperty("project_dependencies")]
|
||||||
|
public List<string> ProjectDependencies { get; set; } = new List<string>();
|
||||||
|
|
||||||
|
[JsonProperty("icon")]
|
||||||
|
public string? Icon { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class CrossLangInfo
|
||||||
|
{
|
||||||
|
[JsonProperty("maintainer")]
|
||||||
|
public string? Maintainer { get; set; } //Not checked when building project
|
||||||
|
[JsonProperty("type")]
|
||||||
|
public string? Type { get; set; } //gets checked whether it is compile_tool by build tool, possible values are in file: project_types.txt
|
||||||
|
[JsonProperty("repo")]
|
||||||
|
public string? Repoository { get; set; } //optional, is the place where the code is stored
|
||||||
|
[JsonProperty("homepage")]
|
||||||
|
public string? HomePage { get; set; } //optional, is the website for the project
|
||||||
|
[JsonProperty("license")]
|
||||||
|
public string? License { get; set; } //optional, but recommended to tell people what the license is
|
||||||
|
[JsonProperty("description")]
|
||||||
|
public string? Description { get; set; } //optional but tells people about the package
|
||||||
|
|
||||||
|
[JsonProperty("template_name")]
|
||||||
|
public string? TemplateName { get; set; }
|
||||||
|
[JsonProperty("template_name_pretty")]
|
||||||
|
public string? TemplateNamePretty { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("template_project_dependencies")]
|
||||||
|
public JToken? TemplateDependencies { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("template_info")]
|
||||||
|
public JToken? TemplateInfo { get; set; }
|
||||||
|
[JsonProperty("template_extra_text_ftles")]
|
||||||
|
public JToken? TemplateExtraTextFiles { get; set; }
|
||||||
|
[JsonProperty("template_ignored_files")]
|
||||||
|
public JToken? TemplateIgnoredFiles { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CrossLangDependency
|
||||||
|
{
|
||||||
|
[JsonProperty("name")]
|
||||||
|
public string Name { get; set; } = "";
|
||||||
|
[JsonProperty("verson")]
|
||||||
|
public CrossLangVersion Version { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum CrossLangVersionStage
|
||||||
|
{
|
||||||
|
Development = 0,
|
||||||
|
Alpha = 1,
|
||||||
|
Beta = 2,
|
||||||
|
Production = 3
|
||||||
|
}
|
||||||
|
public class CrossLangVersionConverter : Newtonsoft.Json.JsonConverter<CrossLangVersion>
|
||||||
|
{
|
||||||
|
public override CrossLangVersion ReadJson(JsonReader reader, Type objectType, CrossLangVersion existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
string? s = reader.Value as string;
|
||||||
|
if (!string.IsNullOrWhiteSpace(s))
|
||||||
|
{
|
||||||
|
|
||||||
|
if (CrossLangVersion.TryParse(s, out var version)) return version;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasExistingValue)
|
||||||
|
return existingValue;
|
||||||
|
return new CrossLangVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteJson(JsonWriter writer, CrossLangVersion value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
writer.WriteValue(value.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[Newtonsoft.Json.JsonConverter( typeof(CrossLangVersionConverter))]
|
||||||
|
public struct CrossLangVersion : IComparable<CrossLangVersion>, IComparable
|
||||||
|
{
|
||||||
|
public void CopyTo(byte[] data, int offset = 0)
|
||||||
|
{
|
||||||
|
if (offset + 5 > data.Length) throw new IndexOutOfRangeException("Cannot write outside of array");
|
||||||
|
data[offset + 0] = Major;
|
||||||
|
data[offset + 1] = Minor;
|
||||||
|
data[offset + 2] = Patch;
|
||||||
|
data[offset + 3] = (byte)(BuildAndStage >> 8);
|
||||||
|
data[offset + 4] = (byte)(BuildAndStage & 0xFF);
|
||||||
|
}
|
||||||
|
public byte[] ToArray()
|
||||||
|
{
|
||||||
|
byte[] data = new byte[5];
|
||||||
|
CopyTo(data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
public static bool TryParse(string str, out CrossLangVersion version)
|
||||||
|
{
|
||||||
|
var dashPart = str.Split(new char[] { '-' }, 2, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
byte major = 1;
|
||||||
|
byte minor = 0;
|
||||||
|
byte patch = 0;
|
||||||
|
ushort build = 0;
|
||||||
|
CrossLangVersionStage stage = CrossLangVersionStage.Development;
|
||||||
|
if (dashPart.Length >= 1)
|
||||||
|
{
|
||||||
|
|
||||||
|
var dotPart = dashPart[0].Split(new char[] { '.' }, 4, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
if (dotPart.Length > 0)
|
||||||
|
{
|
||||||
|
if (!byte.TryParse(dotPart[0], out major))
|
||||||
|
{
|
||||||
|
version = new CrossLangVersion();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (dotPart.Length > 1 && !byte.TryParse(dotPart[1], out minor))
|
||||||
|
{
|
||||||
|
version = new CrossLangVersion();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (dotPart.Length > 2 && !byte.TryParse(dotPart[2], out patch))
|
||||||
|
{
|
||||||
|
version = new CrossLangVersion();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (dotPart.Length > 3 && !ushort.TryParse(dotPart[3], out build))
|
||||||
|
{
|
||||||
|
version = new CrossLangVersion();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (dashPart.Length == 2)
|
||||||
|
{
|
||||||
|
switch (dashPart[1])
|
||||||
|
{
|
||||||
|
case "dev":
|
||||||
|
stage = CrossLangVersionStage.Development;
|
||||||
|
break;
|
||||||
|
case "alpha":
|
||||||
|
stage = CrossLangVersionStage.Alpha;
|
||||||
|
break;
|
||||||
|
case "beta":
|
||||||
|
stage = CrossLangVersionStage.Beta;
|
||||||
|
break;
|
||||||
|
case "prod":
|
||||||
|
stage = CrossLangVersionStage.Production;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
version = new CrossLangVersion();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
version = new CrossLangVersion(major, minor, patch, build, stage);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
version = new CrossLangVersion();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
public CrossLangVersion()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
public CrossLangVersion(byte major, byte minor, byte patch, ushort build, CrossLangVersionStage stage)
|
||||||
|
{
|
||||||
|
Major = major;
|
||||||
|
Minor = minor;
|
||||||
|
Patch = patch;
|
||||||
|
Build = build;
|
||||||
|
Stage = stage;
|
||||||
|
}
|
||||||
|
public CrossLangVersion(byte[] data, int offset = 0)
|
||||||
|
{
|
||||||
|
if (offset + 5 > data.Length) return;
|
||||||
|
|
||||||
|
Major = data[offset+0];
|
||||||
|
Minor = data[offset+1];
|
||||||
|
Patch = data[offset+2];
|
||||||
|
ushort build = (ushort)(data[offset+3] << 8 | data[offset+4]);
|
||||||
|
Build = (ushort)(build >> 2);
|
||||||
|
Stage = (CrossLangVersionStage)(build & 3);
|
||||||
|
}
|
||||||
|
public byte Major { get; set; } = 1;
|
||||||
|
public byte Minor { get; set; } = 0;
|
||||||
|
public byte Patch { get; set; } = 0;
|
||||||
|
|
||||||
|
public ushort Build { get; set; } = 0;
|
||||||
|
|
||||||
|
public static CrossLangVersion OnePointZeroProd => new CrossLangVersion(){Major=1,Minor=0,Patch=0,Build=0,Stage= CrossLangVersionStage.Production};
|
||||||
|
|
||||||
|
public CrossLangVersionStage Stage { get; set; } = CrossLangVersionStage.Development;
|
||||||
|
|
||||||
|
public ushort BuildAndStage => (ushort)(Build << 2 | (ushort)Stage);
|
||||||
|
|
||||||
|
public ulong AsInteger => (ulong)((ulong)Major << 32 | (ulong)Minor << 24 | (ulong)Patch << 16 | (ulong)BuildAndStage);
|
||||||
|
|
||||||
|
public int CompareTo(CrossLangVersion other)
|
||||||
|
{
|
||||||
|
return AsInteger.CompareTo(other.AsInteger);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int CompareTo(object? obj)
|
||||||
|
{
|
||||||
|
if (obj is CrossLangVersion oth)
|
||||||
|
return CompareTo(oth);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
string stage="dev";
|
||||||
|
switch (Stage)
|
||||||
|
{
|
||||||
|
case CrossLangVersionStage.Development:
|
||||||
|
stage = "dev";
|
||||||
|
break;
|
||||||
|
case CrossLangVersionStage.Alpha:
|
||||||
|
stage = "alpha";
|
||||||
|
break;
|
||||||
|
case CrossLangVersionStage.Beta:
|
||||||
|
stage = "beta";
|
||||||
|
break;
|
||||||
|
case CrossLangVersionStage.Production:
|
||||||
|
stage = "prod";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return $"{Major}.{Minor}.{Patch}.{Build}-{stage}";
|
||||||
|
}
|
||||||
|
}
|
||||||
270
CrossLangShell.cs
Normal file
270
CrossLangShell.cs
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace CrossLangDevStudio;
|
||||||
|
|
||||||
|
|
||||||
|
class CrossLangShell
|
||||||
|
{
|
||||||
|
private static CrossLangSettings? settings = null;
|
||||||
|
public static CrossLangSettings Settings
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (settings != null) return settings;
|
||||||
|
var config = CrossLangConfigDirectory;
|
||||||
|
if (!string.IsNullOrWhiteSpace(config))
|
||||||
|
{
|
||||||
|
var devStudioDir = Path.Combine(config, "DevStudio");
|
||||||
|
Directory.CreateDirectory(devStudioDir);
|
||||||
|
var settingsFile = Path.Combine(devStudioDir, "Settings.json");
|
||||||
|
if (File.Exists(settingsFile))
|
||||||
|
{
|
||||||
|
settings = JsonConvert.DeserializeObject<CrossLangSettings>(File.ReadAllText(settingsFile));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return settings is null ? settings = new CrossLangSettings() : settings;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static string _crosslang_config_dir = "";
|
||||||
|
|
||||||
|
public static string CrossLangConfigDirectory => string.IsNullOrWhiteSpace(_crosslang_config_dir) ? _crosslang_config_dir = GetCrossLangDirectory() : _crosslang_config_dir;
|
||||||
|
|
||||||
|
public static List<string> GetRecentProjects()
|
||||||
|
{
|
||||||
|
var config = CrossLangConfigDirectory;
|
||||||
|
if (string.IsNullOrWhiteSpace(config)) return new List<string>();
|
||||||
|
|
||||||
|
var devStudioDir = Path.Combine(config, "DevStudio");
|
||||||
|
Directory.CreateDirectory(devStudioDir);
|
||||||
|
var configPath = Path.Combine(devStudioDir, "Recents.json");
|
||||||
|
if (File.Exists(configPath))
|
||||||
|
{
|
||||||
|
var list = JsonConvert.DeserializeObject<List<string>>(File.ReadAllText(configPath));
|
||||||
|
if (list is not null) return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new List<string>();
|
||||||
|
}
|
||||||
|
public static void OpenFolderInFileManager(string path)
|
||||||
|
{
|
||||||
|
using (Process p = new Process())
|
||||||
|
{
|
||||||
|
p.StartInfo.FileName = path;
|
||||||
|
p.StartInfo.UseShellExecute = true;
|
||||||
|
p.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static void EnsureRecent(string projectPath)
|
||||||
|
{
|
||||||
|
projectPath = Path.GetFullPath(projectPath).TrimEnd(Path.DirectorySeparatorChar);
|
||||||
|
var recents = GetRecentProjects();
|
||||||
|
if (recents.Contains(projectPath))
|
||||||
|
{
|
||||||
|
recents.Remove(projectPath);
|
||||||
|
}
|
||||||
|
recents.Insert(0, projectPath);
|
||||||
|
if (recents.Count > 5)
|
||||||
|
{
|
||||||
|
recents.RemoveRange(5, recents.Count - 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = CrossLangConfigDirectory;
|
||||||
|
if (string.IsNullOrWhiteSpace(config)) return;
|
||||||
|
|
||||||
|
var devStudioDir = Path.Combine(config, "DevStudio");
|
||||||
|
Directory.CreateDirectory(devStudioDir);
|
||||||
|
var configPath = Path.Combine(devStudioDir, "Recents.json");
|
||||||
|
File.WriteAllText(configPath, JsonConvert.SerializeObject(recents, Formatting.Indented));
|
||||||
|
}
|
||||||
|
public static string GetRealPath(string exec)
|
||||||
|
{
|
||||||
|
foreach (var item in (Environment.GetEnvironmentVariable("PATH") ?? "").Split(Path.PathSeparator))
|
||||||
|
{
|
||||||
|
var path = Path.Combine(item, exec);
|
||||||
|
if (File.Exists(path)) return path;
|
||||||
|
foreach (var ext in (Environment.GetEnvironmentVariable("PATHEXT") ?? "").Split(Path.PathSeparator))
|
||||||
|
{
|
||||||
|
if (File.Exists(path + ext)) return path + ext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string CrossLangPath
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var path = GetRealPath("crosslang");
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static bool HaveCrossLang
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return !string.IsNullOrWhiteSpace(CrossLangPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static void BuildProjectInFolder(string folder)
|
||||||
|
{
|
||||||
|
OpenTerminal(false, folder, CrossLangPath, "build");
|
||||||
|
}
|
||||||
|
public static void RunProjectInFolder(string folder)
|
||||||
|
{
|
||||||
|
OpenTerminal(true, folder, CrossLangPath, "run");
|
||||||
|
}
|
||||||
|
public static void OpenTerminalInFolder(string folder)
|
||||||
|
{
|
||||||
|
if (OperatingSystem.IsLinux())
|
||||||
|
{
|
||||||
|
OpenTerminal(false, folder, "", []);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void OpenTerminal(bool keepOpen, string workingDirectory, string commandName, params string[] args)
|
||||||
|
{
|
||||||
|
var preferedTerminalCommand = Settings.PreferedTerminalCommand;
|
||||||
|
if (!string.IsNullOrWhiteSpace(preferedTerminalCommand))
|
||||||
|
{
|
||||||
|
var firstArgs = preferedTerminalCommand.Split(" ");
|
||||||
|
using Process process = new();
|
||||||
|
process.StartInfo.UseShellExecute = false;
|
||||||
|
process.StartInfo.WorkingDirectory = workingDirectory;
|
||||||
|
process.StartInfo.FileName = firstArgs[0];
|
||||||
|
for (int i = 1; i < firstArgs.Length; i++)
|
||||||
|
{
|
||||||
|
process.StartInfo.ArgumentList.Add(firstArgs[i]);
|
||||||
|
}
|
||||||
|
foreach (var arg in args)
|
||||||
|
process.StartInfo.ArgumentList.Add(arg);
|
||||||
|
process.Start();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (OperatingSystem.IsLinux() || OperatingSystem.IsFreeBSD())
|
||||||
|
{
|
||||||
|
string konsole = GetRealPath("konsole");
|
||||||
|
string gnome_terminal = GetRealPath("gnome-terminal");
|
||||||
|
|
||||||
|
string xterm = GetRealPath("xterm");
|
||||||
|
if (File.Exists(konsole))
|
||||||
|
{
|
||||||
|
using Process process = new Process();
|
||||||
|
process.StartInfo.WorkingDirectory = workingDirectory;
|
||||||
|
process.StartInfo.FileName = konsole;
|
||||||
|
process.StartInfo.UseShellExecute = false;
|
||||||
|
if (commandName.Length != 0)
|
||||||
|
{
|
||||||
|
if (keepOpen)
|
||||||
|
process.StartInfo.ArgumentList.Add("--hold");
|
||||||
|
process.StartInfo.ArgumentList.Add("-e");
|
||||||
|
process.StartInfo.ArgumentList.Add(commandName);
|
||||||
|
foreach (var arg in args)
|
||||||
|
{
|
||||||
|
process.StartInfo.ArgumentList.Add(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
process.Start();
|
||||||
|
}
|
||||||
|
else if (File.Exists(gnome_terminal))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (File.Exists(xterm))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (OperatingSystem.IsWindows())
|
||||||
|
{
|
||||||
|
using Process process = new Process();
|
||||||
|
process.StartInfo.FileName = GetRealPath("cmd.exe");
|
||||||
|
if(commandName.Length > 0)
|
||||||
|
process.StartInfo.ArgumentList.Add(keepOpen ? "/K" : "/C");
|
||||||
|
process.StartInfo.CreateNoWindow = false;
|
||||||
|
process.StartInfo.UseShellExecute = false;
|
||||||
|
process.StartInfo.WorkingDirectory = workingDirectory;
|
||||||
|
if(commandName.Length > 0)
|
||||||
|
process.StartInfo.ArgumentList.Add(commandName);
|
||||||
|
foreach (var item in args)
|
||||||
|
process.StartInfo.ArgumentList.Add(item);
|
||||||
|
process.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static string GetCrossLangDirectory()
|
||||||
|
{
|
||||||
|
string path = CrossLangPath;
|
||||||
|
if (string.IsNullOrWhiteSpace(path)) return "";
|
||||||
|
using Process process = new Process();
|
||||||
|
process.StartInfo.ArgumentList.Add("configdir");
|
||||||
|
process.StartInfo.FileName = path;
|
||||||
|
process.StartInfo.CreateNoWindow = true;
|
||||||
|
process.StartInfo.RedirectStandardOutput = true;
|
||||||
|
process.StartInfo.UseShellExecute = false;
|
||||||
|
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
|
||||||
|
if (process.Start())
|
||||||
|
{
|
||||||
|
|
||||||
|
process.WaitForExit();
|
||||||
|
var text = process.StandardOutput.ReadToEnd();
|
||||||
|
return (text ?? "").Replace("\n", "").Replace("\r", "");
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool CreateProject(string templateName, string path)
|
||||||
|
{
|
||||||
|
string cpath = CrossLangPath;
|
||||||
|
if (string.IsNullOrWhiteSpace(cpath)) return false;
|
||||||
|
using Process process = new Process();
|
||||||
|
process.StartInfo.ArgumentList.Add("new");
|
||||||
|
process.StartInfo.ArgumentList.Add(templateName);
|
||||||
|
process.StartInfo.WorkingDirectory = path;
|
||||||
|
process.StartInfo.FileName = cpath;
|
||||||
|
process.StartInfo.CreateNoWindow = true;
|
||||||
|
|
||||||
|
process.StartInfo.UseShellExecute = false;
|
||||||
|
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
|
||||||
|
if (process.Start())
|
||||||
|
{
|
||||||
|
|
||||||
|
process.WaitForExit();
|
||||||
|
return process.ExitCode == 0;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CrossLangSettings
|
||||||
|
{
|
||||||
|
[JsonProperty("prefered_terminal_command")]
|
||||||
|
public string PreferedTerminalCommand { get; set; } = "";
|
||||||
|
[JsonProperty("prefered_project_directory")]
|
||||||
|
public string PreferedProjectDirectory { get; set; } = "";
|
||||||
|
[Newtonsoft.Json.JsonIgnore]
|
||||||
|
public string ProjectDirectory
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(PreferedProjectDirectory))
|
||||||
|
return PreferedProjectDirectory;
|
||||||
|
|
||||||
|
string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "CrossLangProjects");
|
||||||
|
Directory.CreateDirectory(path);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
7
Messages/GetWindowMessage.cs
Normal file
7
Messages/GetWindowMessage.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
using CrossLangDevStudio.ViewModels;
|
||||||
|
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
|
||||||
|
namespace CrossLangDevStudio.Messages;
|
||||||
|
|
||||||
|
public class GetWindowMessage : AsyncRequestMessage<Window>;
|
||||||
9
Messages/NewProjectCloseMessage.cs
Normal file
9
Messages/NewProjectCloseMessage.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using CrossLangDevStudio.ViewModels;
|
||||||
|
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||||
|
|
||||||
|
namespace CrossLangDevStudio.Messages;
|
||||||
|
|
||||||
|
public class NewProjectCloseMessage(string? path) : AsyncRequestMessage<string?>
|
||||||
|
{
|
||||||
|
public string? Path => path;
|
||||||
|
}
|
||||||
6
Messages/NewProjectMessage.cs
Normal file
6
Messages/NewProjectMessage.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
using CrossLangDevStudio.ViewModels;
|
||||||
|
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||||
|
|
||||||
|
namespace CrossLangDevStudio.Messages;
|
||||||
|
|
||||||
|
public class NewProjectMessage : AsyncRequestMessage<string?>;
|
||||||
25
Models/ProjectFileNode.cs
Normal file
25
Models/ProjectFileNode.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
|
||||||
|
namespace CrossLangDevStudio.Models;
|
||||||
|
|
||||||
|
public class ProjectFileNode
|
||||||
|
{
|
||||||
|
public ObservableCollection<ProjectFileNode>? SubNodes { get; }
|
||||||
|
public string Title { get; }
|
||||||
|
|
||||||
|
public Action? Click { get; }
|
||||||
|
|
||||||
|
public ProjectFileNode(string title, Action? click = null)
|
||||||
|
{
|
||||||
|
Title = title;
|
||||||
|
Click = click;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProjectFileNode(string title, ObservableCollection<ProjectFileNode> subNodes)
|
||||||
|
{
|
||||||
|
Title = title;
|
||||||
|
SubNodes = subNodes;
|
||||||
|
}
|
||||||
|
}
|
||||||
0
Packaging/Linux/build.sh
Normal file
0
Packaging/Linux/build.sh
Normal file
12
Packaging/Windows/Toolchain.cmake
Normal file
12
Packaging/Windows/Toolchain.cmake
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
set(CMAKE_C_COMPILER "/usr/bin/i686-w64-mingw32-gcc")
|
||||||
|
set(CMAKE_CXX_COMPILER "/usr/bin/i686-w64-mingw32-g++")
|
||||||
|
set(CMAKE_C_FLAGS "-static-libgcc -static-libstdc++ -static")
|
||||||
|
set(CMAKE_CXX_FLAGS "-static-libgcc -static-libstdc++ -static")
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++ -static")
|
||||||
|
|
||||||
|
set(CMAKE_SYSROOT "/home/mike/dvd/Working/Sysroot/windows")
|
||||||
|
|
||||||
|
set(CMAKE_SYSTEM_NAME Windows)
|
||||||
|
set(CMAKE_SYSTEM_PROCESSOR i686)
|
||||||
|
|
||||||
19
Packaging/Windows/build.sh
Normal file
19
Packaging/Windows/build.sh
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#!/bin/bash\
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
git clone https://onedev.site.tesses.net/crosslang
|
||||||
|
cd crosslang
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake -S .. -B . -DCMAKE_TOOLCHAIN_FILE=../../../Toolchain.cmake -DTESSESFRAMEWORK_ENABLE_SHARED=OFF -DTESSESFRAMEWORK_FETCHCONTENT=ON
|
||||||
|
make -j`nproc`
|
||||||
|
mkdir -p ../../package/bin
|
||||||
|
mkdir -p ../../package/share/Tesses/CrossLang
|
||||||
|
cp crosslang.exe ../../package/bin
|
||||||
|
cp ../winicon.ico ../../package/crosslang.ico
|
||||||
|
cd ../..
|
||||||
|
wget -O package/share/Tesses/CrossLang/Tesses.CrossLang.ShellPackage-1.0.0.0-prod.crvm https://downloads.tesses.net/ShellPackage.crvm
|
||||||
|
cd package
|
||||||
|
dotnet publish -c Release -r win-x64 -o bin -p:PublishReadyToRun=true -p:PublishSingleFile=true --self-contained ../../../../CrossLangDevStudio.csproj
|
||||||
|
cp ../../script.nsh .
|
||||||
|
makensis script.nsh
|
||||||
0
Packaging/Windows/license.txt
Normal file
0
Packaging/Windows/license.txt
Normal file
147
Packaging/Windows/script.nsh
Normal file
147
Packaging/Windows/script.nsh
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
# This installs two files, app.exe and logo.ico, creates a start menu shortcut, builds an uninstaller, and
|
||||||
|
# adds uninstall information to the registry for Add/Remove Programs
|
||||||
|
|
||||||
|
# To get started, put this script into a folder with the two files (app.exe, logo.ico, and license.rtf -
|
||||||
|
# You'll have to create these yourself) and run makensis on it
|
||||||
|
|
||||||
|
# If you change the names "app.exe", "logo.ico", or "license.rtf" you should do a search and replace - they
|
||||||
|
# show up in a few places.
|
||||||
|
# All the other settings can be tweaked by editing the !defines at the top of this script
|
||||||
|
!define APPNAME "CrossLang DevStudio"
|
||||||
|
!define COMPANYNAME "Tesses"
|
||||||
|
!define DESCRIPTION "IDE for CrossLang"
|
||||||
|
# These three must be integers
|
||||||
|
!define VERSIONMAJOR 1
|
||||||
|
!define VERSIONMINOR 0
|
||||||
|
!define VERSIONBUILD 0
|
||||||
|
# These will be displayed by the "Click here for support information" link in "Add/Remove Programs"
|
||||||
|
# It is possible to use "mailto:" links in here to open the email client
|
||||||
|
!define ABOUTURL "https://crosslang.tesseslanguage.com/" # "Publisher" link
|
||||||
|
# This is the size (in kB) of all the files copied into "Program Files"
|
||||||
|
|
||||||
|
|
||||||
|
RequestExecutionLevel admin ;Require admin rights on NT6+ (When UAC is turned on)
|
||||||
|
|
||||||
|
InstallDir "$PROGRAMFILES\${COMPANYNAME}\${APPNAME}"
|
||||||
|
|
||||||
|
# rtf or txt file - remember if it is txt, it must be in the DOS text format (\r\n)
|
||||||
|
LicenseData "license.txt"
|
||||||
|
# This will be in the installer/uninstaller's title bar
|
||||||
|
Name "${COMPANYNAME} - ${APPNAME}"
|
||||||
|
Icon "crosslang.ico"
|
||||||
|
outFile "CrossLangDevStudio-Installer.exe"
|
||||||
|
|
||||||
|
!include LogicLib.nsh
|
||||||
|
|
||||||
|
# Just three pages - license agreement, install location, and installation
|
||||||
|
page license
|
||||||
|
page directory
|
||||||
|
Page instfiles
|
||||||
|
|
||||||
|
!macro VerifyUserIsAdmin
|
||||||
|
UserInfo::GetAccountType
|
||||||
|
pop $0
|
||||||
|
${If} $0 != "admin" ;Require admin rights on NT4+
|
||||||
|
messageBox mb_iconstop "Administrator rights required!"
|
||||||
|
setErrorLevel 740 ;ERROR_ELEVATION_REQUIRED
|
||||||
|
quit
|
||||||
|
${EndIf}
|
||||||
|
!macroend
|
||||||
|
|
||||||
|
function .onInit
|
||||||
|
setShellVarContext all
|
||||||
|
!insertmacro VerifyUserIsAdmin
|
||||||
|
functionEnd
|
||||||
|
|
||||||
|
section "install"
|
||||||
|
createDirectory "$INSTDIR\bin"
|
||||||
|
createDirectory "$INSTDIR\share"
|
||||||
|
createDirectory "$INSTDIR\share\Tesses"
|
||||||
|
createDirectory "$INSTDIR\share\Tesses\CrossLang"
|
||||||
|
# Files for the install directory - to build the installer, these should be in the same directory as the install script (this file)
|
||||||
|
setOutPath $INSTDIR
|
||||||
|
|
||||||
|
# Files added here should be removed by the uninstaller (see section "uninstall")
|
||||||
|
|
||||||
|
file "crosslang.ico"
|
||||||
|
setOutPath $INSTDIR\bin
|
||||||
|
|
||||||
|
file "bin\CrossLangDevStudio.exe"
|
||||||
|
file "bin\av_libglesv2.dll"
|
||||||
|
file "bin\libHarfBuzzSharp.dll"
|
||||||
|
file "bin\libSkiaSharp.dll"
|
||||||
|
file "bin\crosslang.exe"
|
||||||
|
setOutPath $INSTDIR\share\Tesses\CrossLang
|
||||||
|
|
||||||
|
file "share\Tesses\CrossLang\Tesses.CrossLang.ShellPackage-1.0.0.0-prod.crvm"
|
||||||
|
|
||||||
|
# Add any other files for the install directory (license files, app data, etc) here
|
||||||
|
|
||||||
|
# Uninstaller - See function un.onInit and section "uninstall" for configuration
|
||||||
|
writeUninstaller "$INSTDIR\uninstall.exe"
|
||||||
|
|
||||||
|
# Start Menu
|
||||||
|
createDirectory "$SMPROGRAMS\${COMPANYNAME}"
|
||||||
|
createShortCut "$SMPROGRAMS\${COMPANYNAME}\${APPNAME}.lnk" "$INSTDIR\bin\CrossLangDevStudio.exe" "" "$INSTDIR\crosslang.ico"
|
||||||
|
|
||||||
|
# Registry information for add/remove programs
|
||||||
|
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "DisplayName" "${COMPANYNAME} - ${APPNAME} - ${DESCRIPTION}"
|
||||||
|
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\""
|
||||||
|
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S"
|
||||||
|
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "InstallLocation" "$\"$INSTDIR$\""
|
||||||
|
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "DisplayIcon" "$\"$INSTDIR\logo.ico$\""
|
||||||
|
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "Publisher" "$\"${COMPANYNAME}$\""
|
||||||
|
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "URLInfoAbout" "$\"${ABOUTURL}$\""
|
||||||
|
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "DisplayVersion" "$\"${VERSIONMAJOR}.${VERSIONMINOR}.${VERSIONBUILD}$\""
|
||||||
|
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "VersionMajor" ${VERSIONMAJOR}
|
||||||
|
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "VersionMinor" ${VERSIONMINOR}
|
||||||
|
# There is no option for modifying or repairing the install
|
||||||
|
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "NoModify" 1
|
||||||
|
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "NoRepair" 1
|
||||||
|
WriteRegStr HKCR "Directory\Background\shell\${APPNAME}\command" "" "$\"$INSTDIR\bin\CrossLangDevStudio.exe$\" $\"%V$\""
|
||||||
|
sectionEnd
|
||||||
|
|
||||||
|
# Uninstaller
|
||||||
|
|
||||||
|
function un.onInit
|
||||||
|
SetShellVarContext all
|
||||||
|
|
||||||
|
#Verify the uninstaller - last chance to back out
|
||||||
|
MessageBox MB_OKCANCEL "Permanantly remove ${APPNAME}?" IDOK next
|
||||||
|
Abort
|
||||||
|
next:
|
||||||
|
!insertmacro VerifyUserIsAdmin
|
||||||
|
functionEnd
|
||||||
|
|
||||||
|
section "uninstall"
|
||||||
|
|
||||||
|
# Remove Start Menu launcher
|
||||||
|
delete "$SMPROGRAMS\${COMPANYNAME}\${APPNAME}.lnk"
|
||||||
|
# Try to remove the Start Menu folder - this will only happen if it is empty
|
||||||
|
rmDir "$SMPROGRAMS\${COMPANYNAME}"
|
||||||
|
|
||||||
|
# Remove files
|
||||||
|
delete $INSTDIR\bin\CrossLangDevStudio.exe
|
||||||
|
delete $INSTDIR\crosslang.ico
|
||||||
|
|
||||||
|
delete $INSTDIR\bin\av_libglesv2.dll
|
||||||
|
delete $INSTDIR\bin\libHarfBuzzSharp.dll
|
||||||
|
delete $INSTDIR\bin\libSkiaSharp.dll
|
||||||
|
delete $INSTDIR\bin\crosslang.exe
|
||||||
|
|
||||||
|
# Always delete uninstaller as the last action
|
||||||
|
delete $INSTDIR\uninstall.exe
|
||||||
|
delete $INSTDIR\share\Tesses\CrossLang\Tesses.CrossLang.ShellPackage-1.0.0.0-prod.crvm
|
||||||
|
rmDir "$INSTDIR\share\Tesses\CrossLang"
|
||||||
|
rmDir "$INSTDIR\share\Tesses"
|
||||||
|
rmDir "$INSTDIR\bin"
|
||||||
|
rmDir "$INSTDIR\share"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Try to remove the install directory - this will only happen if it is empty
|
||||||
|
rmDir $INSTDIR
|
||||||
|
|
||||||
|
# Remove uninstaller information from the registry
|
||||||
|
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}"
|
||||||
|
sectionEnd
|
||||||
21
Program.cs
Normal file
21
Program.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace CrossLangDevStudio;
|
||||||
|
|
||||||
|
sealed class Program
|
||||||
|
{
|
||||||
|
// Initialization code. Don't use any Avalonia, third-party APIs or any
|
||||||
|
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
|
||||||
|
// yet and stuff might break.
|
||||||
|
[STAThread]
|
||||||
|
public static void Main(string[] args) => BuildAvaloniaApp()
|
||||||
|
.StartWithClassicDesktopLifetime(args);
|
||||||
|
|
||||||
|
// Avalonia configuration, don't remove; also used by visual designer.
|
||||||
|
public static AppBuilder BuildAvaloniaApp()
|
||||||
|
=> AppBuilder.Configure<App>()
|
||||||
|
.UsePlatformDetect()
|
||||||
|
.WithInterFont()
|
||||||
|
.LogToTrace();
|
||||||
|
}
|
||||||
31
ViewLocator.cs
Normal file
31
ViewLocator.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Templates;
|
||||||
|
using CrossLangDevStudio.ViewModels;
|
||||||
|
|
||||||
|
namespace CrossLangDevStudio;
|
||||||
|
|
||||||
|
public class ViewLocator : IDataTemplate
|
||||||
|
{
|
||||||
|
|
||||||
|
public Control? Build(object? param)
|
||||||
|
{
|
||||||
|
if (param is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var name = param.GetType().FullName!.Replace("ViewModel", "View", StringComparison.Ordinal);
|
||||||
|
var type = Type.GetType(name);
|
||||||
|
|
||||||
|
if (type != null)
|
||||||
|
{
|
||||||
|
return (Control)Activator.CreateInstance(type)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TextBlock { Text = "Not Found: " + name };
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Match(object? data)
|
||||||
|
{
|
||||||
|
return data is ViewModelBase;
|
||||||
|
}
|
||||||
|
}
|
||||||
65
ViewModels/FileEditorViewModel.cs
Normal file
65
ViewModels/FileEditorViewModel.cs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Platform.Storage;
|
||||||
|
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
|
using CrossLangDevStudio.Messages;
|
||||||
|
using CrossLangDevStudio.Views;
|
||||||
|
|
||||||
|
namespace CrossLangDevStudio.ViewModels;
|
||||||
|
|
||||||
|
public partial class FileEditorViewModel : ViewModelBase, ISavable
|
||||||
|
{
|
||||||
|
TabItemViewModel tab;
|
||||||
|
public string FilePath { get; }
|
||||||
|
bool _modified = false;
|
||||||
|
private bool Modified
|
||||||
|
{
|
||||||
|
get => _modified;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_modified = value;
|
||||||
|
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
tab.Header = $"{Path.GetFileName(FilePath)}*";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tab.Header = Path.GetFileName(FilePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string _text = "";
|
||||||
|
|
||||||
|
public string Text
|
||||||
|
{
|
||||||
|
get => _text;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
|
||||||
|
Modified = true;
|
||||||
|
this.SetProperty(ref _text, value, nameof(Text));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileEditorViewModel(string path,TabItemViewModel tab)
|
||||||
|
{
|
||||||
|
this.tab = tab;
|
||||||
|
FilePath = path;
|
||||||
|
_text = File.ReadAllText(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Save()
|
||||||
|
{
|
||||||
|
File.WriteAllText(FilePath, Text);
|
||||||
|
Modified = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
5
ViewModels/ISavable.cs
Normal file
5
ViewModels/ISavable.cs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
namespace CrossLangDevStudio.ViewModels;
|
||||||
|
interface ISavable
|
||||||
|
{
|
||||||
|
void Save();
|
||||||
|
}
|
||||||
409
ViewModels/MainWindowViewModel.cs
Normal file
409
ViewModels/MainWindowViewModel.cs
Normal file
@@ -0,0 +1,409 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Platform.Storage;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
|
using CrossLangDevStudio.Messages;
|
||||||
|
using CrossLangDevStudio.Models;
|
||||||
|
using CrossLangDevStudio.Views;
|
||||||
|
|
||||||
|
namespace CrossLangDevStudio.ViewModels;
|
||||||
|
|
||||||
|
public partial class MainWindowViewModel : ViewModelBase
|
||||||
|
{
|
||||||
|
|
||||||
|
public HttpClient Client { get; set; } = new();
|
||||||
|
public string CurrentProject { get; set; } = "";
|
||||||
|
public Func<object> NewItemFactory => AddItem;
|
||||||
|
|
||||||
|
|
||||||
|
public ObservableCollection<TabItemViewModel> TabItems { get; } = new();
|
||||||
|
public ObservableCollection<ProjectFileNode> ProjectFiles { get; } = new ObservableCollection<ProjectFileNode>();
|
||||||
|
|
||||||
|
public EventHandler<Tabalonia.Events.CloseLastTabEventArgs>? Closed { get; } = null;
|
||||||
|
|
||||||
|
public TabItemViewModel WelcomePage
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return new TabItemViewModel()
|
||||||
|
{
|
||||||
|
Header = "Welcome",
|
||||||
|
Body = new WelcomeViewModel(this)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private ProjectFileNode? _selectedProjectFile = null;
|
||||||
|
|
||||||
|
public ProjectFileNode? SelectedProjectFile
|
||||||
|
{
|
||||||
|
get => _selectedProjectFile;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_selectedProjectFile = value;
|
||||||
|
value?.Click?.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public MainWindowViewModel(string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length == 1)
|
||||||
|
{
|
||||||
|
LoadProject(args[0]);
|
||||||
|
}
|
||||||
|
if (string.IsNullOrWhiteSpace(CurrentProject))
|
||||||
|
this.TabItems.Add(WelcomePage);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private object AddItem()
|
||||||
|
{
|
||||||
|
var tab = new TabItemViewModel
|
||||||
|
{
|
||||||
|
Header = "New Tab",
|
||||||
|
|
||||||
|
};
|
||||||
|
if (Directory.Exists(CurrentProject))
|
||||||
|
{
|
||||||
|
var newTabItem = new NewTabViewModel(this, tab);
|
||||||
|
tab.Body = newTabItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return tab;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void LoadProject(string obj)
|
||||||
|
{
|
||||||
|
if (!File.Exists(Path.Combine(obj, "cross.json")))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CrossLangShell.EnsureRecent(obj);
|
||||||
|
CurrentProject = obj.TrimEnd(Path.DirectorySeparatorChar);
|
||||||
|
Refresh(CurrentProject);
|
||||||
|
}
|
||||||
|
[RelayCommand]
|
||||||
|
public void RefreshListing()
|
||||||
|
{
|
||||||
|
if (!File.Exists(Path.Combine(CurrentProject, "cross.json")))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Refresh(CurrentProject);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Refresh(string obj)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 0; i < TabItems.Count; i++)
|
||||||
|
{
|
||||||
|
if (TabItems[i].Body is WelcomeViewModel)
|
||||||
|
{
|
||||||
|
TabItems.RemoveAt(i);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ObservableCollection<ProjectFileNode> entries = new ObservableCollection<ProjectFileNode>
|
||||||
|
{
|
||||||
|
new ProjectFileNode("Project",[
|
||||||
|
new ProjectFileNode("Configuration",()=>{
|
||||||
|
|
||||||
|
OpenProjectConfig();
|
||||||
|
}),
|
||||||
|
new ProjectFileNode("Packages")
|
||||||
|
])
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
void Itterate(ObservableCollection<ProjectFileNode> nodes, string dir)
|
||||||
|
{
|
||||||
|
|
||||||
|
foreach (var item in Directory.EnumerateDirectories(dir))
|
||||||
|
{
|
||||||
|
ObservableCollection<ProjectFileNode> files = new();
|
||||||
|
Itterate(files, item);
|
||||||
|
var pfn = new ProjectFileNode(Path.GetFileName(item), files);
|
||||||
|
nodes.Add(pfn);
|
||||||
|
}
|
||||||
|
foreach (var item in Directory.EnumerateFiles(dir))
|
||||||
|
{
|
||||||
|
nodes.Add(new ProjectFileNode(Path.GetFileName(item), () =>
|
||||||
|
{
|
||||||
|
OpenFile(item);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
Itterate(entries, obj);
|
||||||
|
ProjectFiles.Clear();
|
||||||
|
ProjectFiles.Add(new ProjectFileNode(Path.GetFileName(obj), entries));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OpenProjectConfig()
|
||||||
|
{
|
||||||
|
|
||||||
|
if (Directory.Exists(CurrentProject))
|
||||||
|
{
|
||||||
|
var config = Path.Combine(CurrentProject, "cross.json");
|
||||||
|
foreach (var item in TabItems)
|
||||||
|
{
|
||||||
|
if (item.Body is ProjectConfigurationViewModel model && model.FilePath == config)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TabItemViewModel vm = new TabItemViewModel();
|
||||||
|
vm.Header = "Project Configuration";
|
||||||
|
var pcm = new ProjectConfigurationViewModel(config, vm);
|
||||||
|
|
||||||
|
vm.Body = pcm;
|
||||||
|
|
||||||
|
AddTab(vm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static string[] FILE_EXTS = new string[]{
|
||||||
|
".tcross",
|
||||||
|
".json",
|
||||||
|
".txt",
|
||||||
|
".cpp",
|
||||||
|
".hpp",
|
||||||
|
".html",
|
||||||
|
".css",
|
||||||
|
".webmanifest",
|
||||||
|
".cs",
|
||||||
|
".c",
|
||||||
|
".h",
|
||||||
|
".xml",
|
||||||
|
".xaml",
|
||||||
|
".js",
|
||||||
|
".jsx",
|
||||||
|
".ts",
|
||||||
|
".tsx",
|
||||||
|
".gitignore",
|
||||||
|
".svg"
|
||||||
|
};
|
||||||
|
|
||||||
|
public void OpenFile(string path)
|
||||||
|
{
|
||||||
|
bool isValid = false;
|
||||||
|
foreach (var item in FILE_EXTS)
|
||||||
|
{
|
||||||
|
if (Path.GetExtension(path).ToLower() == item)
|
||||||
|
isValid = true;
|
||||||
|
}
|
||||||
|
if (!isValid) return;
|
||||||
|
|
||||||
|
foreach (var item in TabItems)
|
||||||
|
{
|
||||||
|
if (item.Body is FileEditorViewModel model)
|
||||||
|
{
|
||||||
|
if (model.FilePath == path)
|
||||||
|
{
|
||||||
|
SelectedTab = item;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var tab = new TabItemViewModel
|
||||||
|
{
|
||||||
|
Header = Path.GetFileName(path),
|
||||||
|
|
||||||
|
};
|
||||||
|
tab.Body = new FileEditorViewModel(path,tab);
|
||||||
|
AddTab(tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private TabItemViewModel? _selectedTab = null;
|
||||||
|
private void AddTab(TabItemViewModel item)
|
||||||
|
{
|
||||||
|
TabItems.Add(item);
|
||||||
|
SelectedTab = item;
|
||||||
|
|
||||||
|
}
|
||||||
|
[RelayCommand]
|
||||||
|
public async Task NewProjectAsync()
|
||||||
|
{
|
||||||
|
var res = await WeakReferenceMessenger.Default.Send(new NewProjectMessage());
|
||||||
|
if (!string.IsNullOrWhiteSpace(res))
|
||||||
|
{
|
||||||
|
LoadProject(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[RelayCommand]
|
||||||
|
public async Task OpenProjectAsync()
|
||||||
|
{
|
||||||
|
var opts = new FolderPickerOpenOptions();
|
||||||
|
opts.AllowMultiple = false;
|
||||||
|
opts.Title = "Open Project";
|
||||||
|
|
||||||
|
var window = await WeakReferenceMessenger.Default.Send(new GetWindowMessage());
|
||||||
|
opts.SuggestedStartLocation = await window.StorageProvider.TryGetFolderFromPathAsync(CrossLangShell.Settings.ProjectDirectory) ?? null;
|
||||||
|
var res = await window.StorageProvider.OpenFolderPickerAsync(opts);
|
||||||
|
if (res.Count == 1)
|
||||||
|
{
|
||||||
|
var url = res[0].TryGetLocalPath();
|
||||||
|
if (!string.IsNullOrWhiteSpace(url))
|
||||||
|
{
|
||||||
|
LoadProject(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void OpenProjectInFileManager()
|
||||||
|
{
|
||||||
|
if (Directory.Exists(CurrentProject))
|
||||||
|
{
|
||||||
|
CrossLangShell.OpenFolderInFileManager(CurrentProject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[RelayCommand]
|
||||||
|
private void OpenProjectInTerminal()
|
||||||
|
{
|
||||||
|
if (Directory.Exists(CurrentProject))
|
||||||
|
{
|
||||||
|
CrossLangShell.OpenTerminalInFolder(CurrentProject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[RelayCommand]
|
||||||
|
private void BuildAndRun()
|
||||||
|
{
|
||||||
|
SaveAll();
|
||||||
|
if (Directory.Exists(CurrentProject))
|
||||||
|
{
|
||||||
|
CrossLangShell.RunProjectInFolder(CurrentProject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[RelayCommand]
|
||||||
|
private void Build()
|
||||||
|
{
|
||||||
|
SaveAll();
|
||||||
|
if (Directory.Exists(CurrentProject))
|
||||||
|
{
|
||||||
|
CrossLangShell.BuildProjectInFolder(CurrentProject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void Save()
|
||||||
|
{
|
||||||
|
SelectedTab?.Save();
|
||||||
|
}
|
||||||
|
[RelayCommand]
|
||||||
|
private void SaveAll()
|
||||||
|
{
|
||||||
|
foreach (var tab in TabItems)
|
||||||
|
{
|
||||||
|
tab.Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class NewTabViewModel : ViewModelBase
|
||||||
|
{
|
||||||
|
private MainWindowViewModel mainWindowViewModel;
|
||||||
|
private TabItemViewModel tab;
|
||||||
|
|
||||||
|
public NewTabViewModel(MainWindowViewModel mainWindowViewModel, TabItemViewModel tab)
|
||||||
|
{
|
||||||
|
this.mainWindowViewModel = mainWindowViewModel;
|
||||||
|
this.tab = tab;
|
||||||
|
}
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _filePath = "src/";
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private decimal _progress = 0;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _url = "";
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void CreateDirectory()
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(FilePath))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(Path.Combine(mainWindowViewModel.CurrentProject, FilePath));
|
||||||
|
mainWindowViewModel.RefreshListing();
|
||||||
|
mainWindowViewModel.TabItems.Remove(tab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[RelayCommand]
|
||||||
|
private void CreateFile()
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(FilePath))
|
||||||
|
{
|
||||||
|
var path = Path.Combine(mainWindowViewModel.CurrentProject, FilePath);
|
||||||
|
File.WriteAllText(path, "");
|
||||||
|
mainWindowViewModel.RefreshListing();
|
||||||
|
mainWindowViewModel.TabItems.Remove(tab);
|
||||||
|
mainWindowViewModel.OpenFile(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private async Task DownloadFileAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var destStrm = File.Create(Path.Combine(mainWindowViewModel.CurrentProject, FilePath));
|
||||||
|
var res = await mainWindowViewModel.Client.GetAsync(Url);
|
||||||
|
long length = 0;
|
||||||
|
long offset = 0;
|
||||||
|
Progress = 0;
|
||||||
|
if (res.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
if (res.Content.Headers.ContentLength.HasValue)
|
||||||
|
{
|
||||||
|
length = res.Content.Headers.ContentLength.Value;
|
||||||
|
byte[] array = new byte[1024];
|
||||||
|
int read = 0;
|
||||||
|
using var src = await res.Content.ReadAsStreamAsync();
|
||||||
|
do
|
||||||
|
{
|
||||||
|
read = await src.ReadAsync(array);
|
||||||
|
await destStrm.WriteAsync(array, 0, read);
|
||||||
|
offset += read;
|
||||||
|
if (length > 0)
|
||||||
|
{
|
||||||
|
Progress = (decimal)offset / (decimal)length;
|
||||||
|
}
|
||||||
|
|
||||||
|
} while (read != 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await res.Content.CopyToAsync(destStrm);
|
||||||
|
}
|
||||||
|
Progress = 1;
|
||||||
|
mainWindowViewModel.RefreshListing();
|
||||||
|
mainWindowViewModel.TabItems.Remove(tab);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
File.Delete(FilePath);
|
||||||
|
this.Url = $"FAILED: {this.Url}";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
120
ViewModels/NewProjectDialogViewModel.cs
Normal file
120
ViewModels/NewProjectDialogViewModel.cs
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.IO;
|
||||||
|
using Avalonia.Markup.Xaml.Templates;
|
||||||
|
using Avalonia.Media;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
|
using CrossLangDevStudio.Messages;
|
||||||
|
using CrossLangDevStudio.Views;
|
||||||
|
|
||||||
|
namespace CrossLangDevStudio.ViewModels;
|
||||||
|
|
||||||
|
public partial class NewProjectDialogViewModel : ViewModelBase
|
||||||
|
{
|
||||||
|
public ObservableCollection<CrossLangTemplate> CrossLangTemplates { get; set; } = new ObservableCollection<CrossLangTemplate>();
|
||||||
|
bool updated = false;
|
||||||
|
string _name = "";
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _name;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
updated = true;
|
||||||
|
SetProperty(ref _name, value, nameof(Name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SelectProjectType(string name)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
if (!updated)
|
||||||
|
{
|
||||||
|
var newName = name.Replace(" ", "");
|
||||||
|
int i;
|
||||||
|
for (i = 1; i < int.MaxValue; i++)
|
||||||
|
{
|
||||||
|
string name2 = $"{name}{i}";
|
||||||
|
if (!Directory.Exists(Path.Combine(ParentDirectory, name2)))
|
||||||
|
{
|
||||||
|
Name = name2;
|
||||||
|
updated = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _parentDirectory = CrossLangShell.Settings.ProjectDirectory;
|
||||||
|
CrossLangTemplate? _template = null;
|
||||||
|
public CrossLangTemplate? SelectedTemplate
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _template;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
SetProperty(ref _template, value, nameof(SelectedTemplate));
|
||||||
|
if (value is not null)
|
||||||
|
{
|
||||||
|
SelectProjectType(value.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public NewProjectDialogViewModel()
|
||||||
|
{
|
||||||
|
foreach (var item in Directory.EnumerateFiles(Path.Combine(CrossLangShell.CrossLangConfigDirectory, "Templates"), "*.crvm"))
|
||||||
|
{
|
||||||
|
CrossLangFile file = new CrossLangFile();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
file.Load(item);
|
||||||
|
CrossLangTemplates.Add(new CrossLangTemplate(Path.GetFileNameWithoutExtension(item), file));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void OK()
|
||||||
|
{
|
||||||
|
if (_template is not null && !string.IsNullOrWhiteSpace(Name) && !string.IsNullOrWhiteSpace(ParentDirectory))
|
||||||
|
{
|
||||||
|
var path = Path.Combine(ParentDirectory, Name);
|
||||||
|
Directory.CreateDirectory(path);
|
||||||
|
if (CrossLangShell.CreateProject(_template.TemplateName, path))
|
||||||
|
{
|
||||||
|
WeakReferenceMessenger.Default.Send(new NewProjectCloseMessage(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[RelayCommand]
|
||||||
|
private void Cancel()
|
||||||
|
{
|
||||||
|
WeakReferenceMessenger.Default.Send(new NewProjectCloseMessage(null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class CrossLangTemplate(string name,CrossLangFile file) : ObservableObject
|
||||||
|
{
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _name = file.GetTemplatePrettyName();
|
||||||
|
[ObservableProperty]
|
||||||
|
private IImage _icon = file.GetIcon(64);
|
||||||
|
|
||||||
|
public string TemplateName = name;
|
||||||
|
|
||||||
|
}
|
||||||
220
ViewModels/ProjectConfigurationViewModel.cs
Normal file
220
ViewModels/ProjectConfigurationViewModel.cs
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Platform.Storage;
|
||||||
|
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
|
using CrossLangDevStudio.Messages;
|
||||||
|
using CrossLangDevStudio.Views;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace CrossLangDevStudio.ViewModels;
|
||||||
|
|
||||||
|
public partial class ProjectConfigurationViewModel : ViewModelBase, ISavable
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
<ComboBoxItem>Console Application</ComboBoxItem>
|
||||||
|
<ComboBoxItem>Library</ComboBoxItem>
|
||||||
|
<ComboBoxItem>Web Application</ComboBoxItem>
|
||||||
|
<ComboBoxItem>Template</ComboBoxItem>
|
||||||
|
<ComboBoxItem>Compile Tool</ComboBoxItem>
|
||||||
|
<ComboBoxItem>Tool</ComboBoxItem>
|
||||||
|
<ComboBoxItem>Archive</ComboBoxItem>
|
||||||
|
*/
|
||||||
|
static readonly string[] types = ["console","lib","app","template","compile_tool","tool","archive"];
|
||||||
|
TabItemViewModel tab;
|
||||||
|
public string FilePath { get; }
|
||||||
|
bool _modified = false;
|
||||||
|
private bool Modified
|
||||||
|
{
|
||||||
|
get => _modified;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_modified = value;
|
||||||
|
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
tab.Header = $"Project Configuration*";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tab.Header = "Project Configuration";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string _name;
|
||||||
|
string _version;
|
||||||
|
string _icon;
|
||||||
|
string _maintainer;
|
||||||
|
string _repository;
|
||||||
|
string _homepage;
|
||||||
|
|
||||||
|
string _license;
|
||||||
|
|
||||||
|
string _templatename;
|
||||||
|
string _templatenamepretty;
|
||||||
|
|
||||||
|
string _description;
|
||||||
|
|
||||||
|
int _type;
|
||||||
|
|
||||||
|
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get => _name;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Modified = true;
|
||||||
|
SetProperty(ref _name, value, nameof(Name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public string Version
|
||||||
|
{
|
||||||
|
get => _version;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Modified = true;
|
||||||
|
SetProperty(ref _version, value, nameof(Version));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Icon
|
||||||
|
{
|
||||||
|
get => _icon;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Modified = true;
|
||||||
|
SetProperty(ref _icon, value, nameof(Icon));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public string Maintainer
|
||||||
|
{
|
||||||
|
get => _maintainer;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Modified = true;
|
||||||
|
SetProperty(ref _maintainer, value, nameof(Maintainer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public string Homepage
|
||||||
|
{
|
||||||
|
get => _homepage;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Modified = true;
|
||||||
|
SetProperty(ref _homepage, value, nameof(Homepage));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public string Repository
|
||||||
|
{
|
||||||
|
get => _repository;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Modified = true;
|
||||||
|
SetProperty(ref _repository, value, nameof(Repository));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string License
|
||||||
|
{
|
||||||
|
get => _license;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Modified = true;
|
||||||
|
SetProperty(ref _license, value, nameof(License));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string TemplateName
|
||||||
|
{
|
||||||
|
get => _templatename;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Modified = true;
|
||||||
|
SetProperty(ref _templatename, value, nameof(TemplateName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public string TemplateNamePretty
|
||||||
|
{
|
||||||
|
get => _templatenamepretty;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Modified = true;
|
||||||
|
SetProperty(ref _templatenamepretty, value, nameof(TemplateNamePretty));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public string Description
|
||||||
|
{
|
||||||
|
get => _description;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Modified = true;
|
||||||
|
SetProperty(ref _description, value, nameof(Description));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Type
|
||||||
|
{
|
||||||
|
get => _type;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Modified = true;
|
||||||
|
SetProperty(ref _type, value, nameof(Type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProjectConfigurationViewModel(string configPath, TabItemViewModel tab)
|
||||||
|
{
|
||||||
|
FilePath = configPath;
|
||||||
|
this.tab = tab;
|
||||||
|
|
||||||
|
|
||||||
|
var config = JsonConvert.DeserializeObject<CrossLangConfig>(File.ReadAllText(configPath));
|
||||||
|
|
||||||
|
_name = config?.Name ?? "";
|
||||||
|
_version = config?.Version.ToString() ?? "1.0.0.0-dev";
|
||||||
|
_icon = config?.Icon ?? "";
|
||||||
|
_repository = config?.Info?.Repoository ?? "";
|
||||||
|
_homepage = config?.Info?.HomePage ?? "";
|
||||||
|
_maintainer = config?.Info?.Maintainer ?? "";
|
||||||
|
_license = config?.Info?.License ?? "";
|
||||||
|
_templatename = config?.Info?.TemplateName ?? "";
|
||||||
|
_templatenamepretty = config?.Info?.TemplateNamePretty ?? "";
|
||||||
|
_type = Array.IndexOf(types, config?.Info?.Type ?? "console");
|
||||||
|
_type = _type == -1 ? 0 : _type;
|
||||||
|
_description = config?.Info?.Description ?? "";
|
||||||
|
|
||||||
|
}
|
||||||
|
[RelayCommand]
|
||||||
|
|
||||||
|
public void Save()
|
||||||
|
{
|
||||||
|
var config = JsonConvert.DeserializeObject<CrossLangConfig>(File.ReadAllText(FilePath)) ?? new CrossLangConfig();
|
||||||
|
config.Name = Name;
|
||||||
|
if (CrossLangVersion.TryParse(Version, out var vers))
|
||||||
|
{
|
||||||
|
config.Version = vers;
|
||||||
|
}
|
||||||
|
config.Icon = string.IsNullOrWhiteSpace(Icon) ? null : Icon;
|
||||||
|
|
||||||
|
config.Info.Description = string.IsNullOrWhiteSpace(Description) ? null : Description;
|
||||||
|
config.Info.HomePage = string.IsNullOrWhiteSpace(Homepage) ? null : Homepage;
|
||||||
|
config.Info.License = string.IsNullOrWhiteSpace(License) ? null : License;
|
||||||
|
config.Info.Maintainer = string.IsNullOrWhiteSpace(Maintainer) ? null : Maintainer;
|
||||||
|
config.Info.Repoository = string.IsNullOrWhiteSpace(Repository) ? null : Repository;
|
||||||
|
config.Info.TemplateName = string.IsNullOrWhiteSpace(TemplateName) ? null : TemplateName;
|
||||||
|
config.Info.TemplateNamePretty = string.IsNullOrWhiteSpace(TemplateNamePretty) ? null : TemplateNamePretty;
|
||||||
|
config.Info.Type = types[Type];
|
||||||
|
File.WriteAllText(FilePath, JsonConvert.SerializeObject(config, Formatting.Indented, new JsonSerializerSettings()
|
||||||
|
{
|
||||||
|
NullValueHandling = NullValueHandling.Ignore
|
||||||
|
}));
|
||||||
|
Modified = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
24
ViewModels/TabItemViewModel.cs
Normal file
24
ViewModels/TabItemViewModel.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
|
||||||
|
namespace CrossLangDevStudio.ViewModels;
|
||||||
|
|
||||||
|
public partial class TabItemViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _header = "";
|
||||||
|
|
||||||
|
public ViewModelBase? Body { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public override string ToString() => Header;
|
||||||
|
|
||||||
|
public void Save()
|
||||||
|
{
|
||||||
|
if (Body is ISavable savable)
|
||||||
|
{
|
||||||
|
savable.Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
ViewModels/ViewModelBase.cs
Normal file
7
ViewModels/ViewModelBase.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
|
||||||
|
namespace CrossLangDevStudio.ViewModels;
|
||||||
|
|
||||||
|
public class ViewModelBase : ObservableObject
|
||||||
|
{
|
||||||
|
}
|
||||||
66
ViewModels/WelcomeViewModel.cs
Normal file
66
ViewModels/WelcomeViewModel.cs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Platform.Storage;
|
||||||
|
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
|
using CrossLangDevStudio.Messages;
|
||||||
|
using CrossLangDevStudio.Views;
|
||||||
|
|
||||||
|
namespace CrossLangDevStudio.ViewModels;
|
||||||
|
|
||||||
|
public partial class WelcomeViewModel : ViewModelBase
|
||||||
|
{
|
||||||
|
public MainWindowViewModel Main { get; }
|
||||||
|
public WelcomeViewModel(MainWindowViewModel main)
|
||||||
|
{
|
||||||
|
Main = main;
|
||||||
|
foreach (var proj in CrossLangShell.GetRecentProjects())
|
||||||
|
{
|
||||||
|
RecentProjects.Add(new RecentProject(proj,Main.LoadProject));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public ObservableCollection<RecentProject> RecentProjects { get; set; } = new ObservableCollection<RecentProject>();
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private async Task NewProjectAsync()
|
||||||
|
{
|
||||||
|
await Main.NewProjectAsync();
|
||||||
|
}
|
||||||
|
[RelayCommand]
|
||||||
|
private async Task OpenProjectAsync()
|
||||||
|
{
|
||||||
|
await Main.OpenProjectAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class RecentProject(string path, Action<string> onProjectClicked) : ObservableObject
|
||||||
|
{
|
||||||
|
public string ProjectPath { get; set; } = path;
|
||||||
|
|
||||||
|
private string GetDir()
|
||||||
|
{
|
||||||
|
var projPath = ProjectPath;
|
||||||
|
var path = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
||||||
|
if (projPath.StartsWith(path))
|
||||||
|
{
|
||||||
|
projPath= projPath.Replace(path, "~");
|
||||||
|
}
|
||||||
|
return projPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public string Name => $"{Path.GetFileName(ProjectPath)} ({GetDir()})";
|
||||||
|
[RelayCommand]
|
||||||
|
private void Click()
|
||||||
|
{
|
||||||
|
onProjectClicked(ProjectPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
17
Views/FileEditorView.axaml
Normal file
17
Views/FileEditorView.axaml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:vm="clr-namespace:CrossLangDevStudio.ViewModels"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="CrossLangDevStudio.Views.FileEditorView"
|
||||||
|
x:DataType="vm:FileEditorViewModel">
|
||||||
|
<Design.DataContext>
|
||||||
|
<!-- This only sets the DataContext for the previewer in an IDE,
|
||||||
|
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
|
||||||
|
<vm:FileEditorViewModel />
|
||||||
|
</Design.DataContext>
|
||||||
|
|
||||||
|
<TextBox FontSize="24" FontFamily="{StaticResource AardvarkFont}" Text="{Binding Text}" AcceptsReturn="True" AcceptsTab="True" TextWrapping="Wrap"/>
|
||||||
|
|
||||||
|
</UserControl>
|
||||||
12
Views/FileEditorView.axaml.cs
Normal file
12
Views/FileEditorView.axaml.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
using CrossLangDevStudio.ViewModels;
|
||||||
|
|
||||||
|
namespace CrossLangDevStudio.Views;
|
||||||
|
|
||||||
|
public partial class FileEditorView : UserControl
|
||||||
|
{
|
||||||
|
public FileEditorView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
86
Views/MainWindow.axaml
Normal file
86
Views/MainWindow.axaml
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
<Window xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:vm="using:CrossLangDevStudio.ViewModels"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="CrossLangDevStudio.Views.MainWindow"
|
||||||
|
x:DataType="vm:MainWindowViewModel"
|
||||||
|
Icon="/Assets/crosslang.ico"
|
||||||
|
Title="CrossLang DevStudio"
|
||||||
|
xmlns:controls="clr-namespace:Tabalonia.Controls;assembly=Tabalonia">
|
||||||
|
|
||||||
|
<Design.DataContext>
|
||||||
|
<!-- This only sets the DataContext for the previewer in an IDE,
|
||||||
|
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
|
||||||
|
<vm:MainWindowViewModel/>
|
||||||
|
</Design.DataContext>
|
||||||
|
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Menu Grid.Row="0">
|
||||||
|
<MenuItem Header="_File">
|
||||||
|
<MenuItem Header="_New Project" HotKey="Ctrl+N" Command="{Binding NewProjectCommand}"/>
|
||||||
|
<MenuItem Header="_Open Project" HotKey="Ctrl+O" Command="{Binding OpenProjectCommand}"/>
|
||||||
|
<MenuItem Header="_Save" HotKey="Ctrl+S" Command="{Binding SaveCommand}"/>
|
||||||
|
<MenuItem Header="Save All" HotKey="Ctrl+Shift+S" Command="{Binding SaveAllCommand}"/>
|
||||||
|
<Separator/>
|
||||||
|
<MenuItem Header="_Exit" HotKey="Alt+F4"/>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem Header="_Project">
|
||||||
|
<MenuItem Header="Build" Command="{Binding BuildCommand}" />
|
||||||
|
<MenuItem Header="Build And Run" HotKey="F5" Command="{Binding BuildAndRunCommand}" />
|
||||||
|
<MenuItem Header="Open In File Manager" Command="{Binding OpenProjectInFileManagerCommand}" />
|
||||||
|
<MenuItem Header="Open In Terminal" HotKey="CTRL+T" Command="{Binding OpenProjectInTerminalCommand}" />
|
||||||
|
<MenuItem Header="Refresh" HotKey="CTRL+R" Command="{Binding RefreshListingCommand}" />
|
||||||
|
</MenuItem>
|
||||||
|
</Menu>
|
||||||
|
<Grid Grid.Row="1" ColumnDefinitions="300, 4, *">
|
||||||
|
<TreeView Grid.Column="0" ItemsSource="{Binding ProjectFiles}" SelectedItem="{Binding SelectedProjectFile}">
|
||||||
|
<!--Thanks to https://stackoverflow.com/a/77362693 -->
|
||||||
|
<TreeView.ItemContainerTheme>
|
||||||
|
<ControlTheme TargetType="TreeViewItem" BasedOn="{StaticResource {x:Type TreeViewItem}}">
|
||||||
|
<Setter Property="IsExpanded" Value="True" />
|
||||||
|
</ControlTheme>
|
||||||
|
</TreeView.ItemContainerTheme>
|
||||||
|
<TreeView.ItemTemplate>
|
||||||
|
<TreeDataTemplate ItemsSource="{Binding SubNodes}">
|
||||||
|
<TextBlock Text="{Binding Title}"/>
|
||||||
|
</TreeDataTemplate>
|
||||||
|
|
||||||
|
</TreeView.ItemTemplate>
|
||||||
|
</TreeView>
|
||||||
|
|
||||||
|
<GridSplitter Grid.Column="1" Background="Black" ResizeDirection="Columns"/>
|
||||||
|
|
||||||
|
<controls:TabsControl SelectedItem="{Binding SelectedTab}" Grid.Column="2" ItemsSource="{Binding TabItems}"
|
||||||
|
NewItemFactory="{Binding NewItemFactory}"
|
||||||
|
LastTabClosedAction="{Binding Closed}"
|
||||||
|
ShowDefaultAddButton="True"
|
||||||
|
ShowDefaultCloseButton="True"
|
||||||
|
Margin="0 8 0 0"
|
||||||
|
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||||
|
<TabControl.ContentTemplate>
|
||||||
|
<DataTemplate DataType="vm:TabItemViewModel">
|
||||||
|
|
||||||
|
<Grid Background="{DynamicResource SelectedTabItemBackgroundBrush}">
|
||||||
|
<TransitioningContentControl Content="{Binding Body}" Grid.Column="2"/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
</DataTemplate>
|
||||||
|
</TabControl.ContentTemplate>
|
||||||
|
|
||||||
|
<TabControl.ItemTemplate>
|
||||||
|
<DataTemplate DataType="vm:TabItemViewModel">
|
||||||
|
<TextBlock Text="{Binding Header}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</TabControl.ItemTemplate>
|
||||||
|
|
||||||
|
</controls:TabsControl>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
38
Views/MainWindow.axaml.cs
Normal file
38
Views/MainWindow.axaml.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Platform.Storage;
|
||||||
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
|
using CrossLangDevStudio.Messages;
|
||||||
|
using CrossLangDevStudio.ViewModels;
|
||||||
|
|
||||||
|
namespace CrossLangDevStudio.Views;
|
||||||
|
|
||||||
|
public partial class MainWindow : Window
|
||||||
|
{
|
||||||
|
public MainWindow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
if (Design.IsDesignMode)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Whenever 'Send(new PurchaseAlbumMessage())' is called, invoke this callback on the MainWindow instance:
|
||||||
|
WeakReferenceMessenger.Default.Register<MainWindow, NewProjectMessage>(this, static (w, m) =>
|
||||||
|
{
|
||||||
|
// Create an instance of MusicStoreWindow and set MusicStoreViewModel as its DataContext.
|
||||||
|
var dialog = new NewProjectDialog
|
||||||
|
{
|
||||||
|
DataContext = new NewProjectDialogViewModel()
|
||||||
|
};
|
||||||
|
// Show dialog window and reply with returned AlbumViewModel or null when the dialog is closed.
|
||||||
|
m.Reply(dialog.ShowDialog<string?>(w));
|
||||||
|
});
|
||||||
|
WeakReferenceMessenger.Default.Register<MainWindow, GetWindowMessage>(this, static (w, m) =>
|
||||||
|
{
|
||||||
|
m.Reply(w);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
69
Views/NewProjectDialog.axaml
Normal file
69
Views/NewProjectDialog.axaml
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
<Window xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:vm="using:CrossLangDevStudio.ViewModels"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
Width="640"
|
||||||
|
Height="480"
|
||||||
|
WindowStartupLocation="CenterOwner"
|
||||||
|
x:Class="CrossLangDevStudio.Views.NewProjectDialog"
|
||||||
|
x:DataType="vm:NewProjectDialogViewModel"
|
||||||
|
Icon="/Assets/crosslang.ico"
|
||||||
|
Title="New Project">
|
||||||
|
<Design.DataContext>
|
||||||
|
<!-- This only sets the DataContext for the previewer in an IDE,
|
||||||
|
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
|
||||||
|
<vm:NewProjectDialogViewModel/>
|
||||||
|
</Design.DataContext>
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<ListBox Grid.Row="0" ItemsSource="{Binding CrossLangTemplates}" SelectedItem="{Binding SelectedTemplate}">
|
||||||
|
<ListBox.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="64" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Image Grid.Column="0" Source="{Binding Icon}" />
|
||||||
|
<TextBlock Grid.Column="1" Text="{Binding Name}"/>
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</ListBox.ItemTemplate>
|
||||||
|
</ListBox>
|
||||||
|
|
||||||
|
<Grid Grid.Row="1">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Label Grid.Column="0" Content="Name: " />
|
||||||
|
<TextBox Grid.Column="1" Text="{Binding Name}" />
|
||||||
|
</Grid>
|
||||||
|
<Grid Grid.Row="2">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Label Grid.Column="0" Content="Parent Directory: " />
|
||||||
|
<TextBox Grid.Column="1" Text="{Binding ParentDirectory}"/>
|
||||||
|
<Button Grid.Column="2" Content="Browse..." />
|
||||||
|
</Grid>
|
||||||
|
<Grid Grid.Row="3">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Button Grid.Column="1" Content="OK" Command="{Binding OKCommand}" />
|
||||||
|
<Button Grid.Column="2" Content="Cancel" Command="{Binding CancelCommand}" />
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
21
Views/NewProjectDialog.axaml.cs
Normal file
21
Views/NewProjectDialog.axaml.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using CommunityToolkit.Mvvm.Messaging;
|
||||||
|
using CrossLangDevStudio.Messages;
|
||||||
|
|
||||||
|
namespace CrossLangDevStudio.Views;
|
||||||
|
|
||||||
|
public partial class NewProjectDialog : Window
|
||||||
|
{
|
||||||
|
public NewProjectDialog()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
if (Design.IsDesignMode) return;
|
||||||
|
|
||||||
|
WeakReferenceMessenger.Default.Register<NewProjectDialog, NewProjectCloseMessage>(this, static (w, m) =>
|
||||||
|
{
|
||||||
|
w.Close(m.Path);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
27
Views/NewTabView.axaml
Normal file
27
Views/NewTabView.axaml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:vm="clr-namespace:CrossLangDevStudio.ViewModels"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="CrossLangDevStudio.Views.NewTabView"
|
||||||
|
x:DataType="vm:NewTabViewModel">
|
||||||
|
<Design.DataContext>
|
||||||
|
<!-- This only sets the DataContext for the previewer in an IDE,
|
||||||
|
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
|
||||||
|
<vm:NewTabViewModel />
|
||||||
|
</Design.DataContext>
|
||||||
|
<StackPanel Margin="20">
|
||||||
|
<TextBlock Margin="0 5" >Path:</TextBlock>
|
||||||
|
<TextBox Watermark="Path" Text="{Binding FilePath}"/>
|
||||||
|
|
||||||
|
<Button Content="New File" Command="{Binding CreateFileCommand}" />
|
||||||
|
<Button Content="New Folder" Command="{Binding CreateDirectoryCommand}" />
|
||||||
|
<TextBlock Margin="0 5" >Url:</TextBlock>
|
||||||
|
<TextBox Watermark="Url" Text="{Binding Url}"/>
|
||||||
|
<Button Content="Download File" Command="{Binding DownloadFileCommand}" />
|
||||||
|
<ProgressBar Margin="0 10" Height="20"
|
||||||
|
Minimum="0.0" Maximum="1.0" Value="{Binding Progress}"
|
||||||
|
ShowProgressText="True"/>
|
||||||
|
</StackPanel>
|
||||||
|
</UserControl>
|
||||||
12
Views/NewTabView.axaml.cs
Normal file
12
Views/NewTabView.axaml.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
using CrossLangDevStudio.ViewModels;
|
||||||
|
|
||||||
|
namespace CrossLangDevStudio.Views;
|
||||||
|
|
||||||
|
public partial class NewTabView : UserControl
|
||||||
|
{
|
||||||
|
public NewTabView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
56
Views/ProjectConfigurationView.axaml
Normal file
56
Views/ProjectConfigurationView.axaml
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:vm="clr-namespace:CrossLangDevStudio.ViewModels"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="CrossLangDevStudio.Views.ProjectConfigurationView"
|
||||||
|
x:DataType="vm:ProjectConfigurationViewModel">
|
||||||
|
<Design.DataContext>
|
||||||
|
<!-- This only sets the DataContext for the previewer in an IDE,
|
||||||
|
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
|
||||||
|
<vm:ProjectConfigurationViewModel />
|
||||||
|
</Design.DataContext>
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<ScrollViewer>
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="0" Margin="20">
|
||||||
|
<TextBlock Margin="0 5" >Name:</TextBlock>
|
||||||
|
<TextBox Watermark="Project Name" Text="{Binding Name}"/>
|
||||||
|
<TextBlock Margin="0 5" >Version:</TextBlock>
|
||||||
|
<TextBox Watermark="Project Version" Text="{Binding Version}"/>
|
||||||
|
<TextBlock Margin="0 5" >Icon:</TextBlock>
|
||||||
|
<TextBox Watermark="Project Icon" Text="{Binding Icon}"/>
|
||||||
|
<TextBlock Margin="0 5" >Type:</TextBlock>
|
||||||
|
<ComboBox SelectedIndex="{Binding Type}" MaxDropDownHeight="100" HorizontalAlignment="Stretch">
|
||||||
|
<ComboBoxItem>Console Application</ComboBoxItem>
|
||||||
|
<ComboBoxItem>Library</ComboBoxItem>
|
||||||
|
<ComboBoxItem>Web Application</ComboBoxItem>
|
||||||
|
<ComboBoxItem>Template</ComboBoxItem>
|
||||||
|
<ComboBoxItem>Compile Tool</ComboBoxItem>
|
||||||
|
<ComboBoxItem>Tool</ComboBoxItem>
|
||||||
|
<ComboBoxItem>Archive</ComboBoxItem>
|
||||||
|
</ComboBox>
|
||||||
|
<TextBlock Margin="0 5" >Maintainer:</TextBlock>
|
||||||
|
<TextBox Watermark="Project Maintainer" Text="{Binding Maintainer}"/>
|
||||||
|
<TextBlock Margin="0 5" >Repository:</TextBlock>
|
||||||
|
<TextBox Watermark="Project Repository" Text="{Binding Repository}"/>
|
||||||
|
<TextBlock Margin="0 5" >Homepage:</TextBlock>
|
||||||
|
<TextBox Watermark="Project Homepage" Text="{Binding Homepage}"/>
|
||||||
|
<TextBlock Margin="0 5" >License:</TextBlock>
|
||||||
|
<TextBox Watermark="Project License" Text="{Binding License}"/>
|
||||||
|
<TextBlock Margin="0 5" >Template Name (Shown in crosslang new --list):</TextBlock>
|
||||||
|
<TextBox Watermark="Project Template Name" Text="{Binding TemplateName}"/>
|
||||||
|
<TextBlock Margin="0 5" >Template Pretty Name (Shown in new project):</TextBlock>
|
||||||
|
<TextBox Watermark="Project Template Pretty Name" Text="{Binding TemplateNamePretty}"/>
|
||||||
|
<TextBlock Margin="0 15 0 5">Description:</TextBlock>
|
||||||
|
<TextBox Height="100" Text="{Binding Description}" AcceptsReturn="True" AcceptsTab="True" TextWrapping="Wrap"/>
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
<Button Margin="20" Grid.Row="1" Content="Save" Command="{Binding SaveCommand}" HorizontalAlignment="Right" />
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
11
Views/ProjectConfigurationView.axaml.cs
Normal file
11
Views/ProjectConfigurationView.axaml.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
|
||||||
|
namespace CrossLangDevStudio.Views;
|
||||||
|
|
||||||
|
public partial class ProjectConfigurationView : UserControl
|
||||||
|
{
|
||||||
|
public ProjectConfigurationView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
46
Views/WelcomeView.axaml
Normal file
46
Views/WelcomeView.axaml
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:vm="clr-namespace:CrossLangDevStudio.ViewModels"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="CrossLangDevStudio.Views.WelcomeView"
|
||||||
|
x:DataType="vm:WelcomeViewModel">
|
||||||
|
<Design.DataContext>
|
||||||
|
<!-- This only sets the DataContext for the previewer in an IDE,
|
||||||
|
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
|
||||||
|
<vm:WelcomeViewModel />
|
||||||
|
</Design.DataContext>
|
||||||
|
<Border
|
||||||
|
Padding="100">
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="0">
|
||||||
|
<TextBlock FontSize="32" FontWeight="Bold" Text="CrossLang DevStudio" />
|
||||||
|
|
||||||
|
<TextBlock FontSize="16" FontWeight="Bold" Text="Start" />
|
||||||
|
|
||||||
|
|
||||||
|
<HyperlinkButton Content="New Project" Command="{Binding NewProjectCommand}"/>
|
||||||
|
<HyperlinkButton Content="Open Project" Command="{Binding OpenProjectCommand}"/>
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Grid.Row="1">
|
||||||
|
<TextBlock FontSize="16" FontWeight="Bold" Text="Recent" />
|
||||||
|
<ItemsControl ItemsSource="{Binding RecentProjects}">
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<HyperlinkButton Content="{Binding Name}" Command="{Binding ClickCommand}"/>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</UserControl>
|
||||||
11
Views/WelcomeView.axaml.cs
Normal file
11
Views/WelcomeView.axaml.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
|
||||||
|
namespace CrossLangDevStudio.Views;
|
||||||
|
|
||||||
|
public partial class WelcomeView : UserControl
|
||||||
|
{
|
||||||
|
public WelcomeView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
18
app.manifest
Normal file
18
app.manifest
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
<!-- This manifest is used on Windows only.
|
||||||
|
Don't remove it as it might cause problems with window transparency and embedded controls.
|
||||||
|
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
|
||||||
|
<assemblyIdentity version="1.0.0.0" name="CrossLangDevStudio.Desktop"/>
|
||||||
|
|
||||||
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
|
<application>
|
||||||
|
<!-- A list of the Windows versions that this application has been tested on
|
||||||
|
and is designed to work with. Uncomment the appropriate elements
|
||||||
|
and Windows will automatically select the most compatible environment. -->
|
||||||
|
|
||||||
|
<!-- Windows 10 -->
|
||||||
|
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||||
|
</application>
|
||||||
|
</compatibility>
|
||||||
|
</assembly>
|
||||||
Reference in New Issue
Block a user