Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
2816cf4
initial work in running game project from editor
ourabigdev May 23, 2025
2946b8b
Merge branch 'stride3d:xplat-editor' into xplat-editor
ourabigdev May 23, 2025
c334afa
Update Directory.Packages.props
ourabigdev May 23, 2025
594cdd2
Merge branch 'xplat-editor' of https://github.com/ourabigdev/stride i…
ourabigdev May 23, 2025
ef5a203
some fixes includin going from async void to async Task
ourabigdev May 24, 2025
3c10136
Update Stride.GameStudio.Avalonia.csproj
ourabigdev May 24, 2025
db37d54
Merge branch 'xplat-editor' of https://github.com/ourabigdev/stride i…
ourabigdev May 26, 2025
6b5a2c1
Update Directory.Packages.props
ourabigdev May 26, 2025
67283fc
Update ImageResources.axaml
ourabigdev May 26, 2025
6c4fe7f
Merge branch 'stride3d:xplat-editor' into xplat-editor
ourabigdev May 28, 2025
9972240
Wip: base changes to add start button
ourabigdev May 28, 2025
0dcf077
Update MainViewModel.cs
ourabigdev May 28, 2025
ba2c592
Merge branch 'stride3d:xplat-editor' into xplat-editor
ourabigdev May 28, 2025
dc3255c
Merge branch 'xplat_editor' into ourabigdev_xplat-editor
Kryptos-FR Jun 23, 2025
0a56ef0
Cleanup
Kryptos-FR Jun 23, 2025
c39f273
Merge branch 'stride3d:xplat-editor' into xplat-editor
ourabigdev Jun 28, 2025
4be3a55
change run button placement
ourabigdev Oct 29, 2025
344bfe3
Update MainView.axaml
ourabigdev Oct 29, 2025
10ad7d7
Update MainView.axaml
ourabigdev Nov 2, 2025
926691b
Update .editorconfig
Kryptos-FR Nov 2, 2025
9788a1f
Reformat MainView.axaml
Kryptos-FR Nov 2, 2025
58587d8
Reformat ImageResources.axaml
Kryptos-FR Nov 2, 2025
e97b863
Merge branch 'origin/xplat-editor' into ourabigdev/xplat-editor
Kryptos-FR Nov 2, 2025
e62cebe
Merge branch 'stride3d:xplat-editor' into xplat-editor
ourabigdev Dec 28, 2025
f061cac
Merge branch 'stride3d:xplat-editor' into xplat-editor
ourabigdev Jan 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ insert_final_newline = false
# XML Code Style Rules #
##################################

[*.{xaml,props,targets,proj,csproj,shproj,ttproj,vcxproj}]
[*.{axaml,xaml,props,targets,proj,csproj,shproj,ttproj,vcxproj}]
# enable this once we have merged most of our branches into master
indent_style = space
indent_size = 2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>

<DrawingImage x:Key="ImageRemove">
<DrawingImage.Drawing>
<DrawingGroup>
Expand All @@ -151,4 +151,14 @@
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>

<DrawingImage x:Key="ImageStart">
<DrawingImage.Drawing>
<GeometryDrawing Brush="#4CAF50">
<GeometryDrawing.Geometry>
<Geometry>F1 M 2,2 L 14,8 L 2,14 Z</Geometry>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingImage.Drawing>
</DrawingImage>
</ResourceDictionary>
1 change: 1 addition & 0 deletions sources/editor/Stride.GameStudio.Avalonia/App.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Data.Core.Plugins;
using Avalonia.Input;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using Avalonia.Styling;
Expand Down
130 changes: 130 additions & 0 deletions sources/editor/Stride.GameStudio.Avalonia/ViewModels/MainViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public MainViewModel(IViewModelServiceProvider serviceProvider)
OpenDebugWindowCommand = new AnonymousTaskCommand(serviceProvider, OnOpenDebugWindow, () => DialogService.HasMainWindow);
OpenSettingsWindowCommand = new AnonymousTaskCommand(serviceProvider, OnOpenSettingsWindow, () => DialogService.HasMainWindow);
OpenWebPageCommand = new AnonymousTaskCommand<string>(serviceProvider, OnOpenWebPage);
RunCurrentProjectCommand = new AnonymousTaskCommand(serviceProvider, RunCurrentProject);

Status = new StatusViewModel(ServiceProvider);
Status.PushStatus("Ready");
Expand Down Expand Up @@ -80,6 +81,8 @@ public string Title

public ICommandBase OpenWebPageCommand { get; }

public ICommandBase RunCurrentProjectCommand { get; }

private EditorDialogService DialogService => ServiceProvider.Get<EditorDialogService>();

public async Task<bool?> OpenSession(UFile? filePath, CancellationToken token = default)
Expand Down Expand Up @@ -141,6 +144,128 @@ private void OnExit()
DialogService.Exit();
}

private static async Task<bool> BuildProject(string projectPath, string framework, string workingDirectory)
{
using var process = new Process();
process.StartInfo = new ProcessStartInfo
{
FileName = "dotnet",
Arguments = $"build \"{projectPath}\" --framework {framework}",
WorkingDirectory = workingDirectory,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
};

process.OutputDataReceived += (_, e) => { if (e.Data != null) Console.Out.WriteLine("[build] " + e.Data); };
process.ErrorDataReceived += (_, e) => { if (e.Data != null) Console.Error.WriteLine("[build-err] " + e.Data); };

try
{
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
await process.WaitForExitAsync();
return process.ExitCode == 0;
}
catch (Exception ex)
{
await Console.Error.WriteLineAsync("Build process failed: " + ex);
return false;
}
}

private async Task RunCurrentProject()
{
var mainProjectPath = Session?.CurrentProject?.RootDirectory;
if (mainProjectPath == null) return;

var projectDir = Path.GetDirectoryName(mainProjectPath.FullPath);
var projectBaseName = Path.GetFileNameWithoutExtension(mainProjectPath.FullPath);

string platformSuffix, framework, platformRuntime;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
platformSuffix = "Windows";
platformRuntime = "win-x64";
framework = "net8.0-windows";
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
platformSuffix = "Linux";
platformRuntime = "linux-x64";
framework = "net8.0-linux";
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
platformSuffix = "macOS";
platformRuntime = "osx-x64";
framework = "net8.0-macos";
}
else
{
await ShowError("Unsupported OS platform");
return;
}

var platformProjectName = $"{projectBaseName}.{platformSuffix}.csproj";
var platformProjectPath = Path.Combine(projectDir, $"{projectBaseName}.{platformSuffix}", platformProjectName);
var execPath = Path.Combine(projectDir, "Bin", platformSuffix, "Debug", platformRuntime);
var dllPath = Path.Combine(execPath, $"{projectBaseName}.{platformSuffix}.dll");


if (!File.Exists(platformProjectPath))
{
await ShowError($"Platform-specific project not found: {platformProjectPath}");
return;
}

Status.PushStatus("Building project...");
await Console.Out.WriteLineAsync("Building project...");
bool buildSuccess = await Task.Run(() => BuildProject(platformProjectPath, framework, projectDir));
if (!buildSuccess)
{
Status.PushStatus("Build failed.");
await Console.Out.WriteLineAsync("Build failed.");
await ShowError("Build failed. See output for details.");
return;
}

Status.PushStatus("Running project...");
await Console.Out.WriteLineAsync("Running project...");
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "dotnet",
Arguments = $"\"{dllPath}\"",
WorkingDirectory = projectDir,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = false,
}
};

process.OutputDataReceived += (_, e) => { if (e.Data != null) Console.Out.WriteLine("[run] " + e.Data); };
process.ErrorDataReceived += (_, e) => { if (e.Data != null) Console.Error.WriteLine("[run-err] " + e.Data); };

try
{
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
}
catch (Exception ex)
{
await Console.Error.WriteLineAsync("Run process failed: " + ex);
await ShowError("Failed to start the game process. See output for details.");
}

// FIXME: should we wait for process end?
}

private async Task OnOpen(UFile? initialPath)
{
await OpenSession(initialPath);
Expand Down Expand Up @@ -169,4 +294,9 @@ private async Task OnOpenWebPage(string url)
await ServiceProvider.Get<IDialogService>().MessageBoxAsync(message, MessageBoxButton.OK, MessageBoxImage.Error);
}
}

private async Task ShowError(string message)
{
await ServiceProvider.Get<IDialogService>().MessageBoxAsync(message, MessageBoxButton.OK, MessageBoxImage.Error);
}
}
61 changes: 38 additions & 23 deletions sources/editor/Stride.GameStudio.Avalonia/Views/MainView.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
x:DataType="gsvm:MainViewModel">
<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) -->
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
<gsvm:MainViewModel/>
</Design.DataContext>
<UserControl.Resources>
Expand All @@ -27,7 +27,7 @@
</ResourceDictionary>
</UserControl.Resources>

<Grid RowDefinitions="Auto, *, 25"
<Grid RowDefinitions="Auto, Auto, *, 25"
Background="Transparent">
<!-- Menus -->
<Menu Grid.Row="0" Grid.Column="0"
Expand Down Expand Up @@ -61,8 +61,8 @@
<!-- Help menu -->
<MenuItem Header="{sd:LocalizeString _Help, Context=Menu}">
<MenuItem Header="{sd:LocalizeString Online documentation, Context=Menu}"
Command="{Binding OpenWebPageCommand}"
CommandParameter="{x:Static gsh:StrideGameStudio.DocumentationUrl}"/>
Command="{Binding OpenWebPageCommand}"
CommandParameter="{x:Static gsh:StrideGameStudio.DocumentationUrl}"/>
<Separator/>
<MenuItem Header="{sd:LocalizeString Questions and answers, Context=Menu}"
Command="{Binding OpenWebPageCommand}"
Expand All @@ -88,17 +88,32 @@
</MenuItem>
</Menu>

<!-- Tool Bar -->
<StackPanel Grid.Row="1"
Orientation="Horizontal"
Margin="4,2"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Spacing="4">
<Button Width="32" Height="32"
Command="{Binding RunCurrentProjectCommand}"
HotKey="F5"
ToolTip.Tip="{sd:LocalizeString Start (F5)}">
<Image Source="{StaticResource ImageStart}" Width="16" Height="16"/>
</Button>
</StackPanel>

<!-- Session area -->
<Grid Grid.Row="1"
<Grid Grid.Row="2"
RowDefinitions="2*, 2, *">
<Grid Grid.Row="0"
ColumnDefinitions="2*, 2, *">
<!-- Editors -->
<gsvw:EditorCollectionView Grid.Column="0"
BorderThickness="2"
BorderBrush="Red"
Padding="2"
DataContext="{Binding Session?.EditorCollection}"/>
BorderThickness="2"
BorderBrush="Red"
Padding="2"
DataContext="{Binding Session?.EditorCollection}"/>
<GridSplitter Grid.Column="1" ResizeDirection="Auto"/>
<Grid Grid.Column="2"
RowDefinitions="Auto, *">
Expand All @@ -113,29 +128,29 @@
</StackPanel>
<!-- Property Grid -->
<gsvw:PropertyGridView Grid.Row="1"
BorderThickness="2"
BorderBrush="Orange"
Padding="2"
DataContext="{Binding Session?.ActiveProperties}"/>
BorderThickness="2"
BorderBrush="Orange"
Padding="2"
DataContext="{Binding Session?.ActiveProperties}"/>
</Grid>
</Grid>
<GridSplitter Grid.Row="1" ResizeDirection="Auto"/>
<Grid Grid.Row="2"
ColumnDefinitions="*, 2, 2*, 2, *">
<!-- Solution Explorer -->
<gsvw:SolutionExplorerView Grid.Column="0"
BorderThickness="2"
BorderBrush="Black"
Padding="2"
DataContext="{Binding Session}"/>
BorderThickness="2"
BorderBrush="Black"
Padding="2"
DataContext="{Binding Session}"/>
<GridSplitter Grid.Column="1" ResizeDirection="Auto"/>
<!-- Asset view and logs -->
<TabControl Grid.Column="2">
<TabItem Header="Asset view">
<gsvw:AssetExplorerView BorderThickness="2"
BorderBrush="Blue"
Padding="2"
DataContext="{Binding Session?.AssetCollection}"/>
BorderBrush="Blue"
Padding="2"
DataContext="{Binding Session?.AssetCollection}"/>
</TabItem>
<TabItem Header="Asset errors">
<sd:TextLogViewer LogMessages="{Binding Session?.AssetLog.FilteredMessages}"
Expand All @@ -150,8 +165,8 @@
<TabControl Grid.Column="4">
<TabItem Header="Asset preview">
<gsvw:AssetPreviewView BorderThickness="2"
BorderBrush="Cyan"
Padding="2"/>
BorderBrush="Cyan"
Padding="2"/>
</TabItem>
<TabItem Header="Edit history">
<ListBox ItemsSource="{Binding Session?.ActionHistory?.Transactions}"
Expand Down Expand Up @@ -219,7 +234,7 @@
</Grid>

<!-- Status bar -->
<Grid Grid.Row="2"
<Grid Grid.Row="3"
IsVisible="{Binding Session, Converter={sd:ObjectToBool}}"
ColumnDefinitions="2*, Auto, *, Auto, *, 100">
<TextBlock Grid.Column="0"
Expand Down
Loading