18a2d7154f
- Drag+Drop now shows a black line where the item is going to be dropped as an additional visual cue to make it more intuitive - Avalonia's dark default style is lacking two things: -- Highlights on ListBoxItem are too bright (easy fix) -- The window decoration remains white (could be disabled and replaced with custom title bar)
171 lines
7.1 KiB
C#
171 lines
7.1 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using System.Collections.Generic;
|
|
using System.Text.RegularExpressions;
|
|
|
|
using Avalonia;
|
|
using Avalonia.Input;
|
|
using Avalonia.Controls;
|
|
using Avalonia.VisualTree;
|
|
using Avalonia.Markup.Xaml;
|
|
using Avalonia.LogicalTree;
|
|
|
|
using BFR.Helpers;
|
|
using BFR.DataModels;
|
|
using BFR.Operations;
|
|
using Avalonia.Markup.Xaml.Styling;
|
|
|
|
namespace BFR
|
|
{
|
|
public partial class MainWindow : Window
|
|
{
|
|
public readonly AvaloniaProperty[] HandledProperties = new AvaloniaProperty[] { TextBox.TextProperty, ComboBox.SelectedItemProperty, NumericUpDown.ValueProperty, CheckBox.IsCheckedProperty };
|
|
private bool HandleEvents = false;
|
|
|
|
public async Task OpenDirectoryButtonClick() => OpenDirectory(await new OpenFolderDialog() { Directory = WorkingDirectory }.ShowAsync(this));
|
|
public void OpenDirectory(string directory)
|
|
{
|
|
WorkingDirectory = directory.Replace('\\', '/');
|
|
AllFiles.ReplaceAll(Directory.GetFiles(WorkingDirectory).Select(x => new FileModel(x.Replace('\\', '/'))));
|
|
new Sort() { Mode = SortMode.Natural }.ApplyTo(AllFiles);
|
|
Filter();
|
|
}
|
|
|
|
public void FilterChanged(object sender, AvaloniaPropertyChangedEventArgs e)
|
|
{
|
|
if(HandledProperties.Contains(e.Property) && HandleEvents)
|
|
Filter(); // Bind to PropertyChanged
|
|
}
|
|
public void Filter()
|
|
{
|
|
// Filter all files in the directory for those satisfying the given filters
|
|
Files.ReplaceAll(AllFiles.Where(x =>
|
|
(FilterExtension == "" || x.OldExtension == FilterExtension)
|
|
&& (FilterFullName ? x.OldFullName : x.OldName).RegexContains(FilterRegex ? FilterPattern : Regex.Escape(FilterPattern))));
|
|
|
|
Preview();
|
|
}
|
|
|
|
public void PreviewChanged(object sender, AvaloniaPropertyChangedEventArgs e)
|
|
{
|
|
if (HandledProperties.Contains(e.Property) && HandleEvents)
|
|
Preview(); // Bind to PropertyChanged
|
|
}
|
|
public void Preview()
|
|
{
|
|
// Reset all files to how they currently exist
|
|
foreach (var file in Files)
|
|
file.Path = file.OldPath;
|
|
|
|
// Apply operations
|
|
foreach (var operation in Operations)
|
|
operation.ApplyTo(Files);
|
|
|
|
// Validate that there are any changes, and that the new file names are all unique.
|
|
IsCommitButtonEnabled =
|
|
Files.Any(x => x.Path != x.OldPath) // Check for changes
|
|
&& Files.GroupBy(x => x.Path).All(g => g.Count() == 1) // Check for duplicates
|
|
&& !Files.Any(x => x.FullName == "") // Check for empty file names
|
|
&& !Files.Any(x => "?%*:<>|/\\\"".Any(y => x.FullName.Contains(y))); // Check for invalid characters
|
|
|
|
// Refresh the file list to guarantee that changes are displayed. TODO: Find a better way to do this.
|
|
Files.ReplaceAll(new List<FileModel>(Files));
|
|
}
|
|
|
|
public void AddOperation()
|
|
{
|
|
HandleEvents = false;
|
|
if (Operations.Count >= 1)
|
|
Operations.Insert(
|
|
SelectedOperation >= 0 ? SelectedOperation + 1 : Operations.Count,
|
|
OperationTypes[SelectedOperationType].Create());
|
|
else Operations.Add(OperationTypes[SelectedOperationType].Create());
|
|
HandleEvents = true;
|
|
Preview();
|
|
}
|
|
|
|
private ListBox OperationsListBox;
|
|
private ListBoxItem DragItem;
|
|
|
|
public void DeleteOperation(object sender, PointerReleasedEventArgs e)
|
|
{
|
|
var hoveredItem = (ListBoxItem)OperationsListBox.GetLogicalChildren().FirstOrDefault(x => this.GetVisualsAt(e.GetPosition(this)).Contains(((IVisual)x).GetVisualChildren().First()));
|
|
if(hoveredItem != null) Operations.RemoveAt(OperationsListBox.GetLogicalChildren().ToList().IndexOf(hoveredItem));
|
|
Preview();
|
|
}
|
|
|
|
private void ClearDropStyling()
|
|
{
|
|
foreach (ListBoxItem item in OperationsListBox.GetLogicalChildren())
|
|
item.Classes.RemoveAll(new[] { "BlackTop", "BlackBottom" });
|
|
}
|
|
|
|
public void StartMoveOperation(object sender, PointerPressedEventArgs e) =>
|
|
DragItem = OperationsListBox.GetLogicalChildren().Cast<ListBoxItem>().Single(x => x.IsPointerOver);
|
|
|
|
public void MoveOperation(object sender, PointerEventArgs e)
|
|
{
|
|
if (DragItem == null) return;
|
|
|
|
var hoveredItem = (ListBoxItem)OperationsListBox.GetLogicalChildren().FirstOrDefault(x => this.GetVisualsAt(e.GetPosition(this)).Contains(((IVisual)x).GetVisualChildren().First()));
|
|
var dragItemIndex = OperationsListBox.GetLogicalChildren().ToList().IndexOf(DragItem);
|
|
var hoveredItemIndex = OperationsListBox.GetLogicalChildren().ToList().IndexOf(hoveredItem);
|
|
|
|
ClearDropStyling();
|
|
if (hoveredItem != DragItem) hoveredItem?.Classes.Add(dragItemIndex > hoveredItemIndex ? "BlackTop" : "BlackBottom");
|
|
}
|
|
|
|
public void EndMoveOperation(object sender, PointerReleasedEventArgs e)
|
|
{
|
|
var hoveredItem = (ListBoxItem)OperationsListBox.GetLogicalChildren().FirstOrDefault(x => this.GetVisualsAt(e.GetPosition(this)).Contains(((IVisual)x).GetVisualChildren().First()));
|
|
if (DragItem != null && hoveredItem != null && DragItem != hoveredItem)
|
|
{
|
|
Operations.Move(
|
|
OperationsListBox.GetLogicalChildren().ToList().IndexOf(DragItem),
|
|
OperationsListBox.GetLogicalChildren().ToList().IndexOf(hoveredItem));
|
|
Preview();
|
|
}
|
|
ClearDropStyling();
|
|
DragItem = null;
|
|
}
|
|
|
|
public void Commit()
|
|
{
|
|
foreach (var file in Files)
|
|
if (file.OldPath != file.Path)
|
|
File.Move(file.OldPath, file.Path);
|
|
|
|
UndoStack.Push(new List<FileModel>(Files));
|
|
UndoCount = UndoStack.Count;
|
|
|
|
OpenDirectory(WorkingDirectory);
|
|
}
|
|
|
|
public void Undo()
|
|
{
|
|
foreach (var file in UndoStack.Pop())
|
|
File.Move(file.Path, file.OldPath);
|
|
|
|
UndoCount = UndoStack.Count;
|
|
OpenDirectory(WorkingDirectory);
|
|
}
|
|
|
|
public MainWindow() => InitializeComponent();
|
|
private void InitializeComponent()
|
|
{
|
|
AvaloniaXamlLoader.Load(this);
|
|
DataContext = this;
|
|
OpenDirectory(Environment.OSVersion.Platform == PlatformID.Win32NT ? @"C:/Users/" : @"/home/");
|
|
HandleEvents = true;
|
|
OperationsListBox = this.Find<ListBox>("OperationsListBox");
|
|
|
|
/*var dark = new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
|
|
{
|
|
Source = new Uri("resm:Avalonia.Themes.Default.Accents.BaseDark.xaml?assembly=Avalonia.Themes.Default")
|
|
};
|
|
Styles[0] = dark;/**/
|
|
}
|
|
}
|
|
} |