First Commit

This commit is contained in:
2025-08-31 00:25:32 -05:00
commit cddf6798a6
40 changed files with 2401 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
.vscode
bin
obj
out
build

24
App.axaml Normal file
View 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
View 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);
}
}
}

Binary file not shown.

BIN
Assets/crosslang.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

31
CrossLangDevStudio.csproj Normal file
View 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
View 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
View 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;
}
}
}

View File

@@ -0,0 +1,7 @@
using CrossLangDevStudio.ViewModels;
using CommunityToolkit.Mvvm.Messaging.Messages;
using Avalonia.Controls;
namespace CrossLangDevStudio.Messages;
public class GetWindowMessage : AsyncRequestMessage<Window>;

View 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;
}

View 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
View 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
View File

View 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)

View 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

View File

View 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
View 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
View 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;
}
}

View 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
View File

@@ -0,0 +1,5 @@
namespace CrossLangDevStudio.ViewModels;
interface ISavable
{
void Save();
}

View 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}";
}
}
}

View 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;
}

View 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;
}
}

View 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();
}
}
}

View File

@@ -0,0 +1,7 @@
using CommunityToolkit.Mvvm.ComponentModel;
namespace CrossLangDevStudio.ViewModels;
public class ViewModelBase : ObservableObject
{
}

View 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);
}
}

View 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>

View 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
View 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
View 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);
}
);
}
}

View 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>

View 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
View 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
View File

@@ -0,0 +1,12 @@
using Avalonia.Controls;
using CrossLangDevStudio.ViewModels;
namespace CrossLangDevStudio.Views;
public partial class NewTabView : UserControl
{
public NewTabView()
{
InitializeComponent();
}
}

View 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>

View 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
View 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>

View File

@@ -0,0 +1,11 @@
using Avalonia.Controls;
namespace CrossLangDevStudio.Views;
public partial class WelcomeView : UserControl
{
public WelcomeView()
{
InitializeComponent();
}
}

18
app.manifest Normal file
View 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>