Files
OpenNest/OpenNest.Avalonia/NestView.axaml.cs
T
aj b329dd6c6b feat(avalonia): open several nests in tabs along the bottom
Each open nest gets a tab along the bottom of the window, above the
status bar: the shown one is white with a blue line, a dot marks unsaved
Auto Nest results, x closes it and + opens another. Inside a tab, Plates
and Drawings tabs sit at the top of the left panel (Drawings lists each
drawing's nested and required quantity) and the plate header with
first/previous/next/last sits over the plate view. Each nest keeps its
own plate and zoom when you switch.

File > Open takes several files, and a file already open switches to its
tab. File > Close and Close All, View > Next/Previous Nest and
Ctrl+Tab/Ctrl+Shift+Tab switch or close nests. This version cannot save,
so closing a nest with Auto Nest results, or exiting with any, asks first
(Cancel is the default). Other nests can be opened and viewed while Auto
Nest runs; the nest being nested cannot be closed until it finishes.
Several nest paths on the command line open in tabs.
2026-11-09 22:30:12 -05:00

96 lines
3.3 KiB
C#

using System;
using System.Linq;
using Avalonia.Controls;
using Avalonia.Interactivity;
using OpenNest.Avalonia.Controls;
using OpenNest.Desktop;
namespace OpenNest.Avalonia
{
public sealed record DrawingRow(string Name, int Nested, int Required);
/// <summary>
/// One open nest: Plates and Drawings tabs on the left, the plate header and plate view on
/// the right. Each tab keeps its own view, so switching nests keeps each one's plate and zoom.
/// </summary>
public partial class NestView : UserControl
{
public NestView()
{
InitializeComponent();
}
public NestView(NestTab tab)
: this()
{
Tab = tab;
Canvas.ViewChanged += (_, _) => ViewChanged?.Invoke(this, EventArgs.Empty);
Refresh(0);
}
/// <summary>Raised when the shown plate, zoom, hover or selection changes.</summary>
public event EventHandler ViewChanged;
public NestTab Tab { get; }
public PlateCanvas PlateView => Canvas;
public int PlateIndex => PlateList.SelectedIndex;
public int PlateCount => Tab.Document.Nest.Plates.Count;
/// <summary>Rebuilds both lists and shows plate <paramref name="selectIndex"/>.</summary>
public void Refresh(int selectIndex)
{
var nest = Tab.Document.Nest;
Canvas.ClearCache();
PlateList.ItemsSource = nest.Plates
.Select((plate, i) =>
$"Plate {i + 1} {SizeText(plate)} ({plate.Parts.Count} parts)")
.ToList();
PlateList.SelectedIndex = nest.Plates.Count > 0
? System.Math.Clamp(selectIndex, 0, nest.Plates.Count - 1)
: -1;
DrawingGrid.ItemsSource = nest.Drawings
.Where(d => !d.IsCutOff)
.Select(d => new DrawingRow(d.Name, d.Quantity.Nested, d.Quantity.Required))
.ToList();
ShowPlate();
}
public void ShowPlate(int index)
{
if (PlateCount > 0)
PlateList.SelectedIndex = System.Math.Clamp(index, 0, PlateCount - 1);
}
public void StepPlate(int step) => ShowPlate(PlateList.SelectedIndex + step);
private void OnPlateSelected(object sender, SelectionChangedEventArgs e) => ShowPlate();
private void ShowPlate()
{
var nest = Tab.Document.Nest;
var index = PlateList.SelectedIndex;
var plate = index >= 0 && index < nest.Plates.Count ? nest.Plates[index] : null;
Canvas.Plate = plate;
PlateLabel.Text = plate == null
? "No plates"
: $"Plate {index + 1} of {nest.Plates.Count} "
+ $"{SizeText(plate)} qty {plate.Quantity}";
ViewChanged?.Invoke(this, EventArgs.Empty);
}
private static string SizeText(Plate plate) =>
$"{plate.Size.Length:0.###} x {plate.Size.Width:0.###}";
private void OnFirstPlate(object sender, RoutedEventArgs e) => ShowPlate(0);
private void OnPreviousPlate(object sender, RoutedEventArgs e) => StepPlate(-1);
private void OnNextPlate(object sender, RoutedEventArgs e) => StepPlate(1);
private void OnLastPlate(object sender, RoutedEventArgs e) => ShowPlate(PlateCount - 1);
}
}