Files
Hub/Views/MainWindow.axaml
T
2026-09-19 12:38:12 -03:00

335 lines
17 KiB
XML

<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:NexvaneHub.ViewModels"
xmlns:conv="using:NexvaneHub.Converters"
xmlns:avc="using:Avalonia.Data.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="NexvaneHub.Views.MainWindow"
x:DataType="vm:MainWindowViewModel"
Title="Nexvane Hub"
Width="1180" Height="740"
MinWidth="920" MinHeight="600"
ExtendClientAreaToDecorationsHint="True"
ExtendClientAreaChromeHints="NoChrome"
ExtendClientAreaTitleBarHeightHint="62">
<Design.DataContext>
<vm:MainWindowViewModel />
</Design.DataContext>
<Grid RowDefinitions="62,*">
<!-- ===== Title bar (fully custom - see MainWindow.axaml.cs for the
drag/minimize/maximize/close handlers this wires up to) ===== -->
<Border Grid.Row="0" Classes="topBar" PointerPressed="TitleBar_PointerPressed">
<Grid ColumnDefinitions="Auto,*">
<!-- Logo spans the full height of both rows below, flush in the
top-left corner (like the editor's), not confined to the
title row alone. Vector geometry lifted straight from the
provided nexvane-logo.svg - see Assets/nexvane-logo.svg for
the source. Coordinates have the SVG's scale/translate baked
in directly (computed offline) rather than applied via
RenderTransform, so there's no Viewbox+transform reconciliation
for anything to get wrong - what's below is the tight bounding
box of the mark itself, plus a small safety margin. Sized to
fill most of its column's height (Border.topBar's padding trims
a few px off each end) rather than floating small in the
middle, so the text rows don't visually overshoot the mark. -->
<Border Grid.Column="0" Classes="logoBadge">
<Viewbox Width="36" Height="40" Stretch="Uniform" HorizontalAlignment="Right" VerticalAlignment="Center">
<Canvas Width="835.96" Height="934.96">
<Path Fill="White" Data="M 17.980,100.680 L 17.980,183.480 L 100.480,100.980 C 145.880,55.580 182.980,18.380 182.980,18.280 C 182.980,18.080 145.880,17.980 100.480,17.980 L 17.980,17.980 L 17.980,100.680" />
<Path Fill="White" Data="M 719.680,114.780 L 622.980,211.480 L 622.980,343.780 C 622.980,476.680 623.380,503.480 626.980,584.380 C 628.080,608.580 628.780,628.580 628.580,628.780 C 627.880,629.380 648.480,664.780 369.180,182.980 C 334.280,122.780 304.180,70.880 302.180,67.580 L 298.680,61.780 L 158.380,202.080 L 17.980,342.480 L 17.980,624.480 L 17.980,906.480 L 114.980,809.480 L 211.980,712.480 L 211.980,585.980 C 211.980,516.380 211.480,447.580 210.980,432.980 C 209.780,401.480 207.380,351.080 205.580,320.480 C 204.780,308.380 204.380,298.380 204.480,298.180 C 204.880,297.680 199.980,289.280 365.980,577.980 C 452.480,728.380 524.980,854.580 527.180,858.380 L 531.180,865.280 L 674.580,721.880 L 817.980,578.480 L 817.980,298.280 C 817.980,144.080 817.680,17.980 817.180,17.980 C 816.780,17.980 772.980,61.480 719.680,114.780" />
<Path Fill="White" Data="M 727.480,826.980 L 637.480,916.980 L 727.780,916.980 L 817.980,916.980 L 817.980,826.980 C 817.980,777.480 817.880,736.980 817.680,736.980 C 817.580,736.980 776.980,777.480 727.480,826.980" />
</Canvas>
</Viewbox>
</Border>
<StackPanel Grid.Column="1" Margin="20,0,20,0">
<Grid Height="34" ColumnDefinitions="*,Auto">
<StackPanel Grid.Column="0" Orientation="Horizontal" VerticalAlignment="Center">
<TextBlock Classes="appTitle" Text="Nexvane" />
<TextBlock Classes="appTitleMuted" Text="Hub" />
</StackPanel>
<StackPanel Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Center" Spacing="0">
<Button Classes="iconButton" Content="&#9881;" ToolTip.Tip="Settings" Margin="0,0,8,0" />
<Button Classes="captionButton" Content="&#8212;" Click="MinimizeButton_Click" ToolTip.Tip="Minimize" />
<Button Classes="captionButton" Content="&#9633;" Click="MaximizeButton_Click" ToolTip.Tip="Maximize" />
<Button Classes="captionButton captionButtonClose" Content="&#10005;" Click="CloseButton_Click" ToolTip.Tip="Close" />
</StackPanel>
</Grid>
<!-- Menu row sits below the title, not above it. Fixed height
(34 + 22 = 56, plus Border.topBar's 3px top/bottom padding =
62 total) rather than auto-sized from padding/margin, so the
bar's total height is a known number instead of something
several nested layers of content have to happen to agree on.
Both rows hug their content tightly rather than centering
inside oversized fixed heights, and the shared 3px padding on
Border.topBar keeps everything off the bar's very top/bottom
edges. Each item opens a dropdown via Button.Flyout - Avalonia
handles positioning, click-away-to-close, and Escape for us;
the content inside is ours. Margin.Left is negative to cancel
out Button.menuBarItem's own 6px left padding - otherwise
"File"'s text sits right of where "Nexvane" starts, since the
title TextBlock has no padding of its own to match against. -->
<StackPanel Orientation="Horizontal" Height="22" VerticalAlignment="Top" Spacing="12" Margin="-6,0,0,0">
<Button Classes="menuBarItem" Content="File">
<Button.Flyout>
<Flyout Placement="Bottom">
<StackPanel MinWidth="190">
<Button Classes="dropdownItem" Content="New Project" />
<Button Classes="dropdownItem" Content="Open Project" />
<Border Classes="dropdownSeparator" />
<Button Classes="dropdownItem" Content="Save" />
<Button Classes="dropdownItem" Content="Save As..." />
<Border Classes="dropdownSeparator" />
<Button Classes="dropdownItem" Content="Exit" />
</StackPanel>
</Flyout>
</Button.Flyout>
</Button>
<Button Classes="menuBarItem" Content="Edit">
<Button.Flyout>
<Flyout Placement="Bottom">
<StackPanel MinWidth="190">
<Button Classes="dropdownItem" Content="Undo" />
<Button Classes="dropdownItem" Content="Redo" />
<Border Classes="dropdownSeparator" />
<Button Classes="dropdownItem" Content="Preferences" />
</StackPanel>
</Flyout>
</Button.Flyout>
</Button>
<Button Classes="menuBarItem" Content="View">
<Button.Flyout>
<Flyout Placement="Bottom">
<StackPanel MinWidth="190">
<Button Classes="dropdownItem" Content="Toggle Sidebar" />
<Button Classes="dropdownItem" Content="Toggle Detail Panel" />
</StackPanel>
</Flyout>
</Button.Flyout>
</Button>
<Button Classes="menuBarItem" Content="Tools">
<Button.Flyout>
<Flyout Placement="Bottom">
<StackPanel MinWidth="190">
<Button Classes="dropdownItem" Content="SDK Manager" />
<Button Classes="dropdownItem" Content="Package Manager" />
</StackPanel>
</Flyout>
</Button.Flyout>
</Button>
<Button Classes="menuBarItem" Content="Help">
<Button.Flyout>
<Flyout Placement="Bottom">
<StackPanel MinWidth="190">
<Button Classes="dropdownItem" Content="Documentation" />
<Border Classes="dropdownSeparator" />
<Button Classes="dropdownItem" Content="About Nexvane Hub" />
</StackPanel>
</Flyout>
</Button.Flyout>
</Button>
</StackPanel>
</StackPanel>
</Grid>
</Border>
<!-- ===== Body ===== -->
<Grid Grid.Row="1" ColumnDefinitions="210,*,360">
<!-- Sidebar navigation -->
<Border Grid.Column="0" Classes="sidebar">
<Grid RowDefinitions="*,Auto">
<StackPanel Grid.Row="0">
<Button Classes="navItem"
Classes.selected="{Binding CurrentSection, Converter={x:Static conv:EnumEqualsConverter.Instance}, ConverterParameter=Projects}"
Command="{Binding NavigateToCommand}"
CommandParameter="{x:Static vm:HubSection.Projects}"
Content="Projects" />
<Button Classes="navItem"
Classes.selected="{Binding CurrentSection, Converter={x:Static conv:EnumEqualsConverter.Instance}, ConverterParameter=Sdk}"
Command="{Binding NavigateToCommand}"
CommandParameter="{x:Static vm:HubSection.Sdk}"
Content="SDK" />
<Button Classes="navItem"
Classes.selected="{Binding CurrentSection, Converter={x:Static conv:EnumEqualsConverter.Instance}, ConverterParameter=Packages}"
Command="{Binding NavigateToCommand}"
CommandParameter="{x:Static vm:HubSection.Packages}"
Content="Packages" />
</StackPanel>
<Button Grid.Row="1" Classes="accountItem" Content="Account" />
</Grid>
</Border>
<!-- Middle column: project list (with New folded in as its footer) / other sections -->
<Grid Grid.Column="1">
<!-- Everything for the Projects section - list + New - lives inside
ONE rounded, clipped container, so New reads as part of the
card rather than a separate floating button below it. -->
<Border Classes="listContainer"
Margin="16"
IsVisible="{Binding CurrentSection, Converter={x:Static conv:EnumEqualsConverter.Instance}, ConverterParameter=Projects}">
<Grid RowDefinitions="*,Auto">
<ScrollViewer Grid.Row="0" Padding="0,0,14,0">
<ListBox Classes="projectList"
ItemsSource="{Binding Projects}"
SelectedItem="{Binding SelectedProject}">
<ListBox.ItemTemplate>
<DataTemplate x:DataType="vm:ProjectItemViewModel">
<Border Classes="listRow">
<Grid ColumnDefinitions="56,*" Margin="14,10">
<Border Grid.Column="0" Classes="thumb" />
<StackPanel Grid.Column="1" Margin="12,0,0,0" VerticalAlignment="Center">
<TextBlock Classes="cardTitle" Text="{Binding Name}" />
<TextBlock Classes="cardSubtitle" Text="{Binding LastModifiedDisplay}" />
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</ScrollViewer>
<Border Grid.Row="1" BorderBrush="{StaticResource DividerBrush}" BorderThickness="0,1,0,0">
<Button Classes="newProject"
Content="+ New"
Command="{Binding CreateNewProjectCommand}"
CornerRadius="0"
BorderThickness="0"
Margin="0" />
</Border>
</Grid>
</Border>
<Border Padding="20"
IsVisible="{Binding CurrentSection, Converter={x:Static conv:EnumEqualsConverter.Instance}, ConverterParameter=Sdk}">
<TextBlock Classes="fieldLabel" Text="SDK management goes here." />
</Border>
<Border Padding="20"
IsVisible="{Binding CurrentSection, Converter={x:Static conv:EnumEqualsConverter.Instance}, ConverterParameter=Packages}">
<TextBlock Classes="fieldLabel" Text="Installed packages go here." />
</Border>
</Grid>
<!-- Right column: selected project detail - only part of the Projects
tab, not shown while viewing SDK/Packages. -->
<Border Grid.Column="2" Classes="detailPanel"
IsVisible="{Binding CurrentSection, Converter={x:Static conv:EnumEqualsConverter.Instance}, ConverterParameter=Projects}">
<Grid IsVisible="{Binding SelectedProject, Converter={x:Static avc:ObjectConverters.IsNotNull}}"
RowDefinitions="Auto,*,Auto"
Margin="18">
<TextBox Grid.Row="0"
Classes="input titleInput"
Text="{Binding SelectedProject.Name, Mode=TwoWay}" />
<ScrollViewer Grid.Row="1" Margin="0,14,0,0" Padding="0,0,14,0">
<StackPanel>
<Border Classes="thumbLarge" />
<ToggleButton Classes="sectionHeader"
Content="&#9662; Project"
IsChecked="{Binding IsProjectSectionExpanded}"
Margin="0,16,0,0" />
<StackPanel IsVisible="{Binding IsProjectSectionExpanded}" Margin="0,10,0,0" Spacing="10">
<Grid ColumnDefinitions="100,*">
<TextBlock Classes="fieldLabel" Text="Directory" />
<Border Grid.Column="1" Classes="fieldBox">
<TextBlock Classes="fieldValueMuted"
Text="{Binding SelectedProject.Directory}"
TextTrimming="CharacterEllipsis" />
</Border>
</Grid>
<Grid ColumnDefinitions="100,*">
<TextBlock Classes="fieldLabel" Text="Thumbnail" />
<TextBox Grid.Column="1"
Classes="input"
Text="{Binding SelectedProject.ThumbnailPath, Mode=TwoWay}"
Watermark="No thumbnail set" />
</Grid>
<Grid ColumnDefinitions="100,*">
<TextBlock Classes="fieldLabel" Text="Version" />
<TextBox Grid.Column="1"
Classes="input"
Text="{Binding SelectedProject.ProjectVersion, Mode=TwoWay}" />
</Grid>
</StackPanel>
<ToggleButton Classes="sectionHeader"
Content="&#9662; SDK"
IsChecked="{Binding IsSdkSectionExpanded}"
Margin="0,14,0,0" />
<StackPanel IsVisible="{Binding IsSdkSectionExpanded}" Margin="0,10,0,0" Spacing="10">
<Grid ColumnDefinitions="100,*">
<TextBlock Classes="fieldLabel" Text="Version" />
<TextBox Grid.Column="1"
Classes="input"
Text="{Binding SelectedProject.SdkVersion, Mode=TwoWay}" />
</Grid>
<Grid ColumnDefinitions="100,*">
<TextBlock Classes="fieldLabel" Text="Def. Compiler" />
<TextBox Grid.Column="1"
Classes="input"
Text="{Binding SelectedProject.DefaultCompiler, Mode=TwoWay}" />
</Grid>
<Grid ColumnDefinitions="100,*">
<TextBlock Classes="fieldLabel" Text="Def. Arch" />
<TextBox Grid.Column="1"
Classes="input"
Text="{Binding SelectedProject.DefaultArchitecture, Mode=TwoWay}" />
</Grid>
</StackPanel>
</StackPanel>
</ScrollViewer>
<!-- Open/Delete as one rounded, clipped block with a hairline
divider between them - a visible seam so the pair doesn't
read as one smeared-together shape, but still no gap/margin
between the two buttons themselves. -->
<Border Grid.Row="2"
Margin="0,16,0,0"
CornerRadius="8"
ClipToBounds="True"
BorderBrush="{StaticResource CardBorderBrush}"
BorderThickness="1">
<StackPanel Spacing="0">
<Button Classes="primary"
Content="Open"
Command="{Binding OpenProjectCommand}"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center"
CornerRadius="0"
BorderThickness="0" />
<Border Height="1" Background="{StaticResource CardBorderBrush}" />
<Button Classes="danger"
Content="Delete"
Command="{Binding DeleteProjectCommand}"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center"
CornerRadius="0"
BorderThickness="0" />
</StackPanel>
</Border>
</Grid>
</Border>
</Grid>
</Grid>
</Window>