Files
crosslangdevstudio/ViewModels/MainWindowViewModel.cs
2025-09-06 11:14:02 -05:00

442 lines
12 KiB
C#

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", ()=>{
OpenProjectPackages();
}),
new ProjectFileNode("Project Dependencies",()=>{
})
])
};
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 OpenProjectPackages()
{
if (Directory.Exists(CurrentProject))
{
var config = Path.Combine(CurrentProject, "cross.json");
if (File.Exists(config))
{
foreach (var item in TabItems)
{
if (item.Body is PackageManagerViewModel vm)
{
if (vm.Packages is ProjectPackageManager)
{
SelectedTab = item;
return;
}
}
}
AddTab(new TabItemViewModel()
{
Header = "Project Packages",
Body = new PackageManagerViewModel(this, new ProjectPackageManager(config))
});
}
}
}
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)
{
SelectedTab = item;
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}";
}
}
}