Files
SharpIDE/src/SharpIDE.Photino/Layout/MainLayout.razor
Matt Parker 1e2991681c new icons v1
2025-09-13 13:22:34 +10:00

209 lines
9.4 KiB
Plaintext

@using SharpIDE.Application.Features.Analysis
@using SharpIDE.Application.Features.Build
@using SharpIDE.Application.Features.Events
@using SharpIDE.Application.Features.SolutionDiscovery
@using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence
@using SharpIDE.Photino.Models
@inherits LayoutComponentBase
@inject IDialogService DialogService
@inject BuildService BuildService
@inject AppState AppState
<MudLayout Style="height: 100%">
<MudAppBar Dense="true" Gutters="false" Class="px-2">
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1">
<MudButton Style="min-width: 20px;">
<MudIcon Icon="@Icons.Material.Filled.Menu" Color="Color.Inherit"/>
</MudButton>
<MudButton OnClick="@LoadSolutionFromInteractivePicker" Style="min-width: 20px;">
<MudIcon Icon="@Icons.Material.Filled.FolderOpen" Color="Color.Inherit"/>
</MudButton>
<MudButtonGroup OverrideStyles="false">
<MudButton Variant="Variant.Filled" Size="Size.Small" Color="Color.Primary" Disabled="@IsRunningMsBuildJob()" OnClick="@BuildSolution">Build</MudButton>
<MudButton Variant="Variant.Outlined" Size="Size.Small" Color="Color.Primary" Disabled="@IsRunningMsBuildJob()" OnClick="@RebuildSolution">Rebuild</MudButton>
<MudButton Variant="Variant.Outlined" Size="Size.Small" Color="Color.Primary" Disabled="@IsRunningMsBuildJob()" OnClick="@CleanSolution">Clean</MudButton>
<MudButton Variant="Variant.Outlined" Size="Size.Small" Color="Color.Primary" Disabled="@IsRunningMsBuildJob()" OnClick="@RestoreSolution">Restore</MudButton>
</MudButtonGroup>
<MudButton OnClick="@CancelBuild" Disabled="@(_cancellationTokenSource is null or { IsCancellationRequested: true })" Style="min-width: 20px;">
<MudIcon Disabled="@(_cancellationTokenSource is null or { IsCancellationRequested: true })" Icon="@Icons.Material.Filled.Cancel" Color="Color.Error"/>
</MudButton>
</MudStack>
<MudSpacer/>
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1">
<MudButton Style="min-width: 20px;" OnClick="@OpenSettingsDialog">
<MudIcon Icon="@Icons.Material.Filled.Settings" Size="Size.Medium" Color="Color.Default"/>
</MudButton>
@if (_solutionModel is not null)
{
<RunPopover SolutionModel="@_solutionModel" />
}
else
{
<MudButton Disabled="true" Style="min-width: 20px">
<MudIcon Icon="@Icons.Material.Filled.PlayArrow" Disabled="true" Size="Size.Medium" Color="Color.Default"/>
</MudButton>
}
</MudStack>
</MudAppBar>
<MudMainContent Style="height: 100%">
<MudStack Style="height: 100%; overflow: hidden" Row="true" Spacing="0" StretchItems="StretchItems.Middle">
<MudPaper Elevation="6" Height="100%" Style="background-color: var(--mud-palette-appbar-background); z-index: 1200;">
<MudStack AlignItems="AlignItems.Center" Spacing="1" Style="padding: 4px; height: 100%">
<SidebarIconButton Icon="@Icons.Material.Outlined.FolderOpen" Text="Explorer" OnClick="@DrawerToggle" Selected="@_drawerOpen" />
<MudSpacer />
<SidebarIconButton Icon="@Icons.Material.Outlined.ErrorOutline" Text="Problems" OnClick="@ClickProblemsPanel" Selected="@(_selectedBottomPanel is BottomPanelType.Problems)" />
<SidebarIconButton Icon="@Icons.Material.Outlined.PlayArrow" Text="Run" OnClick="@ClickRunPanel" Selected="@(_selectedBottomPanel is BottomPanelType.Run)" />
<SidebarIconButton Icon="@Icons.Material.Outlined.BugReport" Text="Debug" Selected="false"/>
<SidebarIconButton Icon="@Icons.Material.Outlined.Terminal" Text="Build" OnClick="@ClickBuildPanel" Selected="@(_selectedBottomPanel is BottomPanelType.Build)" />
</MudStack>
</MudPaper>
<div>
<MudPaper Elevation="0" Height="@MainContentHeight" Style="overflow:hidden; position:relative;">
<MudDrawerContainer Style="height: 100%" Class="mud-height-full">
<MudDrawer @bind-Open="@_drawerOpen" Style="height: 100%" Width="400px" Fixed="false" Variant="@DrawerVariant.Persistent">
@if (_solutionFilePath is not null)
{
<SolutionExplorer @bind-SelectedNode="@_selectedNode" @bind-SelectedNode:after="@AfterSelectedNodeChanged" SolutionModel="@_solutionModel"/>
}
</MudDrawer>
<div class="d-flex justify-center align-center mud-height-full" style="flex-direction: row">
<MudContainer Style="height: 100%" MaxWidth="MaxWidth.False" Class="ma-0 pa-0">
@if (_solutionFilePath is not null)
{
<CodeViewer SelectedFile="@_selectedFile"/>
}
</MudContainer>
</div>
</MudDrawerContainer>
</MudPaper>
<div class="mud-elevation-24" style="height: 30%; position:relative">
@if (_solutionFilePath is not null)
{
<DisplayNoneComponent Visible="@(_selectedBottomPanel is BottomPanelType.Problems)">
<ProblemsPanel SolutionModel="@_solutionModel" OnFileSelected="@OnProblemSelected" />
</DisplayNoneComponent>
<DisplayNoneComponent Visible="@(_selectedBottomPanel is BottomPanelType.Run)">
<RunPanel SolutionModel="@_solutionModel"/>
</DisplayNoneComponent>
<DisplayNoneComponent Visible="@(_selectedBottomPanel is BottomPanelType.Build)">
<BuildOutputDisplay/>
</DisplayNoneComponent>
}
</div>
</div>
<div>@* fake for StretchItems.Middle *@</div>
</MudStack>
</MudMainContent>
</MudLayout>
@code {
private bool _drawerOpen = true;
private bool _bottomDrawerOpen = false;
private void DrawerToggle() => _drawerOpen = !_drawerOpen;
private string? _solutionFilePath;
private SharpIdeSolutionModel? _solutionModel;
private ISharpIdeNode? _selectedNode;
private SharpIdeFile? _selectedFile;
private BottomPanelType? _selectedBottomPanel = null;
private string MainContentHeight => _bottomDrawerOpen ? "70%" : "100%";
private void ClickProblemsPanel() => SwitchOrToggleBottomPanel(BottomPanelType.Problems);
private void ClickRunPanel() => SwitchOrToggleBottomPanel(BottomPanelType.Run);
private void ClickBuildPanel() => SwitchOrToggleBottomPanel(BottomPanelType.Build);
private void SwitchOrToggleBottomPanel(BottomPanelType bottomPanelType)
{
if (_selectedBottomPanel == bottomPanelType)
{
_selectedBottomPanel = null;
_bottomDrawerOpen = false;
}
else
{
_selectedBottomPanel = bottomPanelType;
_bottomDrawerOpen = true;
}
}
private void SelectBottomPanel(BottomPanelType bottomPanelType)
{
_selectedBottomPanel = bottomPanelType;
_bottomDrawerOpen = true;
}
protected override async Task OnInitializedAsync()
{
await LoadSolutionFromInteractivePicker(AppState.IdeSettings.AutoOpenLastSolution);
GlobalEvents.StartedRunningProject += OnStartedRunningProject;
}
private void OnProblemSelected(SharpIdeFile file)
{
_selectedFile = file;
_selectedNode = file;
var parent = _selectedFile.Parent;
parent.Expanded = true;
while (parent is IChildSharpIdeNode childNode)
{
if (childNode is not IExpandableSharpIdeNode expandableParent) throw new InvalidOperationException("Parent node must implement IExpandableSharpIdeNode");
expandableParent.Expanded = true;
parent = childNode.Parent;
}
if (parent is not SharpIdeSolutionModel solutionModel) throw new InvalidOperationException("Parent node must be a SharpIdeSolutionModel");
solutionModel.Expanded = true;
StateHasChanged();
}
private void AfterSelectedNodeChanged()
{
if (_selectedNode is SharpIdeFile file)
{
_selectedFile = file;
}
}
private async Task OnStartedRunningProject()
{
SelectBottomPanel(BottomPanelType.Run);
await InvokeAsync(StateHasChanged);
}
private async Task LoadSolutionFromInteractivePicker() => await LoadSolutionFromInteractivePicker(false);
private async Task LoadSolutionFromInteractivePicker(bool autoOpenLastSolution)
{
var dialogRef = await DialogService.ShowAsync<SolutionPickerDialog>("Open Solution",new DialogParameters { [nameof(SolutionPickerDialog.AutoOpenLastSolution)] = autoOpenLastSolution }, new DialogOptions { FullWidth = true, MaxWidth = MaxWidth.Medium, BackdropClick = false });
var result = await dialogRef.Result;
if (result is null) throw new InvalidOperationException("Dialog result is null");
if (result.Canceled) throw new OperationCanceledException("Dialog was canceled");
var solutionFilePath = (string)result.Data!;
_solutionFilePath = solutionFilePath;
var solutionModel = await VsPersistenceMapper.GetSolutionModel(_solutionFilePath);
_solutionModel = solutionModel;
RoslynAnalysis.StartSolutionAnalysis(solutionModel);
}
private CancellationTokenSource? _cancellationTokenSource = null!;
private async Task BuildSolution() => await MsBuildSolution(BuildType.Build);
private async Task RebuildSolution() => await MsBuildSolution(BuildType.Rebuild);
private async Task CleanSolution() => await MsBuildSolution(BuildType.Clean);
private async Task RestoreSolution() => await MsBuildSolution(BuildType.Restore);
private async Task MsBuildSolution(BuildType buildType)
{
if (AppState.IdeSettings.OpenTerminalOnBuildRebuildRestore) SelectBottomPanel(BottomPanelType.Build);
_cancellationTokenSource = new CancellationTokenSource();
await BuildService.MsBuildSolutionAsync(_solutionFilePath!, buildType, _cancellationTokenSource.Token);
_cancellationTokenSource = null;
}
private async Task CancelBuild() => await _cancellationTokenSource!.CancelAsync();
private bool IsRunningMsBuildJob() => _cancellationTokenSource is not null;
private async Task OpenSettingsDialog()
{
var dialogRef = await DialogService.ShowAsync<IdeSettingsDialog>("SharpIDE Settings", new DialogOptions { MaxWidth = MaxWidth.Medium, CloseButton = true });
}
}