CVR Manager 1.0.0
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Input.Platform;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.Platform.Storage;
|
||||
using Avalonia.VisualTree;
|
||||
using CVRManager.ViewModels;
|
||||
|
||||
namespace CVRManager.Views;
|
||||
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
var vm = new MainViewModel
|
||||
{
|
||||
PickFolder = async () =>
|
||||
{
|
||||
var r = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions { AllowMultiple = false });
|
||||
return r.Count > 0 ? r[0].TryGetLocalPath() : null;
|
||||
},
|
||||
PickFiles = async () =>
|
||||
{
|
||||
var r = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
||||
{
|
||||
AllowMultiple = true,
|
||||
FileTypeFilter = new[] { new FilePickerFileType("Mod files") { Patterns = new[] { "*.dll", "*.zip" } } },
|
||||
});
|
||||
return (IReadOnlyList<string>)r.Select(f => f.TryGetLocalPath()).Where(p => p != null).Select(p => p!).ToList();
|
||||
},
|
||||
Confirm = ConfirmAsync,
|
||||
CopyImage = async path =>
|
||||
{
|
||||
if (Clipboard == null) return;
|
||||
using var bmp = new Avalonia.Media.Imaging.Bitmap(path);
|
||||
await Clipboard.SetBitmapAsync(bmp);
|
||||
},
|
||||
};
|
||||
DataContext = vm;
|
||||
Opened += (_, _) => WindowIcons.Apply(this);
|
||||
HookInstallDrag(vm);
|
||||
}
|
||||
|
||||
InstallVM? dragItem;
|
||||
Point dragStart;
|
||||
bool dragging;
|
||||
|
||||
void HookInstallDrag(MainViewModel vm)
|
||||
{
|
||||
var list = InstallList;
|
||||
list.AddHandler(PointerPressedEvent, (_, e) =>
|
||||
{
|
||||
if (!e.GetCurrentPoint(list).Properties.IsLeftButtonPressed) return;
|
||||
if ((e.Source as Visual)?.FindAncestorOfType<Button>(true) != null) return;
|
||||
if ((e.Source as StyledElement)?.DataContext is not InstallVM item) return;
|
||||
dragItem = item;
|
||||
dragStart = e.GetPosition(list);
|
||||
dragging = false;
|
||||
}, RoutingStrategies.Bubble, true);
|
||||
|
||||
list.AddHandler(PointerMovedEvent, (_, e) =>
|
||||
{
|
||||
if (dragItem == null) return;
|
||||
var p = e.GetPosition(list);
|
||||
if (!dragging)
|
||||
{
|
||||
if (Math.Abs(p.Y - dragStart.Y) < 6) return;
|
||||
dragging = true;
|
||||
e.Pointer.Capture(list);
|
||||
list.Cursor = new Cursor(StandardCursorType.SizeNorthSouth);
|
||||
}
|
||||
var from = vm.Installs.IndexOf(dragItem);
|
||||
for (var i = 0; i < list.ItemCount; i++)
|
||||
{
|
||||
if (i == from || list.ContainerFromIndex(i) is not Control c || c.TranslatePoint(default, list) is not { } top) continue;
|
||||
var mid = top.Y + c.Bounds.Height / 2;
|
||||
if ((i > from && p.Y > mid && p.Y < top.Y + c.Bounds.Height) || (i < from && p.Y < mid && p.Y > top.Y))
|
||||
{
|
||||
vm.MoveInstall(dragItem, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
MarkDragged(list);
|
||||
}, RoutingStrategies.Bubble, true);
|
||||
|
||||
void End()
|
||||
{
|
||||
if (dragItem == null) return;
|
||||
var moved = dragging;
|
||||
dragItem = null;
|
||||
dragging = false;
|
||||
list.Cursor = null;
|
||||
MarkDragged(list);
|
||||
if (moved) vm.Save();
|
||||
}
|
||||
list.AddHandler(PointerReleasedEvent, (_, e) => { if (dragging) e.Pointer.Capture(null); End(); }, RoutingStrategies.Bubble, true);
|
||||
list.AddHandler(PointerCaptureLostEvent, (_, _) => End(), RoutingStrategies.Bubble, true);
|
||||
}
|
||||
|
||||
void MarkDragged(ListBox list)
|
||||
{
|
||||
for (var i = 0; i < list.ItemCount; i++)
|
||||
if (list.ContainerFromIndex(i) is Control c)
|
||||
c.Opacity = dragging && c.DataContext == dragItem ? 0.6 : 1;
|
||||
}
|
||||
|
||||
void OnTableSelection(object? sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (e.AddedItems.Count > 0 && e.AddedItems[0] is GroupHeader header)
|
||||
header.ToggleCommand.Execute(null);
|
||||
}
|
||||
|
||||
async Task<bool> ConfirmAsync(string message)
|
||||
{
|
||||
var dlg = new Window
|
||||
{
|
||||
Title = "Confirm",
|
||||
Width = 460,
|
||||
SizeToContent = SizeToContent.Height,
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||||
CanResize = false,
|
||||
};
|
||||
var ok = new Button { Content = "OK", Padding = new Thickness(18, 6) };
|
||||
var cancel = new Button { Content = "Cancel", Padding = new Thickness(18, 6) };
|
||||
ok.Click += (_, _) => dlg.Close(true);
|
||||
cancel.Click += (_, _) => dlg.Close(false);
|
||||
dlg.Content = new StackPanel
|
||||
{
|
||||
Margin = new Thickness(20),
|
||||
Spacing = 16,
|
||||
Children =
|
||||
{
|
||||
new TextBlock { Text = message, TextWrapping = Avalonia.Media.TextWrapping.Wrap },
|
||||
new StackPanel
|
||||
{
|
||||
Orientation = Orientation.Horizontal,
|
||||
Spacing = 8,
|
||||
HorizontalAlignment = HorizontalAlignment.Right,
|
||||
Children = { ok, cancel },
|
||||
},
|
||||
},
|
||||
};
|
||||
return await dlg.ShowDialog<bool>(this);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user