mirror of
https://github.com/unclshura/splitter.git
synced 2026-06-22 00:22:01 +00:00
Avalonia UI work started
This commit is contained in:
parent
93de483bc6
commit
1f93eba839
15
Splitter-UI/App.axaml
Normal file
15
Splitter-UI/App.axaml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<Application xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
x:Class="Splitter_UI.App"
|
||||||
|
xmlns:local="using:Splitter_UI"
|
||||||
|
RequestedThemeVariant="Default">
|
||||||
|
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
|
||||||
|
|
||||||
|
<Application.DataTemplates>
|
||||||
|
<local:ViewLocator/>
|
||||||
|
</Application.DataTemplates>
|
||||||
|
|
||||||
|
<Application.Styles>
|
||||||
|
<FluentTheme />
|
||||||
|
</Application.Styles>
|
||||||
|
</Application>
|
||||||
37
Splitter-UI/App.axaml.cs
Normal file
37
Splitter-UI/App.axaml.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Splitter_UI.Views;
|
||||||
|
|
||||||
|
namespace Splitter_UI;
|
||||||
|
|
||||||
|
public partial class App : Application
|
||||||
|
{
|
||||||
|
private readonly ServiceProvider _provider;
|
||||||
|
|
||||||
|
public App(ServiceProvider provider)
|
||||||
|
{
|
||||||
|
_provider = provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnFrameworkInitializationCompleted()
|
||||||
|
{
|
||||||
|
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
|
{
|
||||||
|
var vm = _provider.GetRequiredService<MainViewModel>();
|
||||||
|
|
||||||
|
desktop.MainWindow = new MainWindow
|
||||||
|
{
|
||||||
|
DataContext = vm
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
base.OnFrameworkInitializationCompleted();
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
Splitter-UI/Assets/avalonia-logo.ico
Normal file
BIN
Splitter-UI/Assets/avalonia-logo.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 172 KiB |
8
Splitter-UI/GlobalUsing.cs
Normal file
8
Splitter-UI/GlobalUsing.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
global using System;
|
||||||
|
global using System.Collections.Generic;
|
||||||
|
global using System.Threading.Tasks;
|
||||||
|
|
||||||
|
global using splitter;
|
||||||
|
global using Splitter_UI.Models;
|
||||||
|
global using Splitter_UI.Services;
|
||||||
|
global using Splitter_UI.ViewModels;
|
||||||
11
Splitter-UI/Models/PreviewData.cs
Normal file
11
Splitter-UI/Models/PreviewData.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using Avalonia;
|
||||||
|
|
||||||
|
namespace Splitter_UI.Models;
|
||||||
|
|
||||||
|
public sealed class PreviewData
|
||||||
|
{
|
||||||
|
public Avalonia.Media.Imaging.Bitmap? Frame { get; init; }
|
||||||
|
public IReadOnlyList<Rect> FaceBoxes { get; init; } = [];
|
||||||
|
public IReadOnlyList<Rect> BodyBoxes { get; init; } = [];
|
||||||
|
public Rect? CropRect { get; init; }
|
||||||
|
}
|
||||||
6
Splitter-UI/Models/ProgressInfo.cs
Normal file
6
Splitter-UI/Models/ProgressInfo.cs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
namespace Splitter_UI.Models;
|
||||||
|
|
||||||
|
public class ProgressInfo
|
||||||
|
{
|
||||||
|
public double Percent { get; set; }
|
||||||
|
}
|
||||||
54
Splitter-UI/Program.cs
Normal file
54
Splitter-UI/Program.cs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Splitter_UI;
|
||||||
|
|
||||||
|
internal sealed class Program
|
||||||
|
{
|
||||||
|
// Initialization code. Don't use any Avalonia, third-party APIs or any
|
||||||
|
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
|
||||||
|
// yet and stuff might break.
|
||||||
|
[STAThread]
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
var services = ConfigureServices();
|
||||||
|
var provider = services.BuildServiceProvider();
|
||||||
|
|
||||||
|
BuildAvaloniaApp(provider)
|
||||||
|
.StartWithClassicDesktopLifetime(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ServiceCollection ConfigureServices()
|
||||||
|
{
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
|
||||||
|
// ViewModels
|
||||||
|
services.AddTransient<MainViewModel>();
|
||||||
|
services.AddTransient<FileListViewModel>();
|
||||||
|
services.AddTransient<PreviewPaneViewModel>();
|
||||||
|
services.AddTransient<InspectorPaneViewModel>();
|
||||||
|
services.AddTransient<StatusBarViewModel>();
|
||||||
|
services.AddTransient<LogPaneViewModel>();
|
||||||
|
|
||||||
|
// Domain services (your pipeline)
|
||||||
|
services.AddTransient<IFileProbeService, FileProbeService>();
|
||||||
|
services.AddTransient<IThumbnailService, ThumbnailService>();
|
||||||
|
services.AddSingleton<IAutoDecisionService, AutoDecisionService>();
|
||||||
|
services.AddSingleton<IProcessingService, ProcessingService>();
|
||||||
|
services.AddSingleton<ILogService, LogService>();
|
||||||
|
|
||||||
|
services.AddSingleton<IFileJobFactory, FileJobFactory>();
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avalonia configuration, don't remove; also used by visual designer.
|
||||||
|
public static AppBuilder BuildAvaloniaApp(ServiceProvider provider)
|
||||||
|
=> AppBuilder.Configure<App>(() => new App(provider))
|
||||||
|
.UsePlatformDetect()
|
||||||
|
#if DEBUG
|
||||||
|
.WithDeveloperTools()
|
||||||
|
#endif
|
||||||
|
.WithInterFont()
|
||||||
|
.LogToTrace();
|
||||||
|
}
|
||||||
8
Splitter-UI/Services/AutoDecisionService.cs
Normal file
8
Splitter-UI/Services/AutoDecisionService.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace Splitter_UI.Services;
|
||||||
|
|
||||||
|
public sealed class AutoDecisionService : IAutoDecisionService
|
||||||
|
{
|
||||||
|
public void ApplyAutoDecisions(SingleJob job, VideoInfo probe)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
17
Splitter-UI/Services/FileJobFactory.cs
Normal file
17
Splitter-UI/Services/FileJobFactory.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
public sealed class FileJobFactory : IFileJobFactory
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider _services;
|
||||||
|
|
||||||
|
public FileJobFactory(IServiceProvider services)
|
||||||
|
{
|
||||||
|
_services = services;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileJobViewModel Create(SingleJob job)
|
||||||
|
{
|
||||||
|
// Resolve a fresh VM + fresh services
|
||||||
|
return ActivatorUtilities.CreateInstance<FileJobViewModel>(_services, job);
|
||||||
|
}
|
||||||
|
}
|
||||||
10
Splitter-UI/Services/FileProbeService.cs
Normal file
10
Splitter-UI/Services/FileProbeService.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
namespace Splitter_UI.Services;
|
||||||
|
|
||||||
|
public sealed class FileProbeService : IFileProbeService
|
||||||
|
{
|
||||||
|
public async Task<VideoInfo> ProbeAsync(SingleJob job)
|
||||||
|
{
|
||||||
|
var res = await Task.Run(() =>ProbeVideo.Probe(job));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
6
Splitter-UI/Services/IAutoDecisionService.cs
Normal file
6
Splitter-UI/Services/IAutoDecisionService.cs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
namespace Splitter_UI.Services;
|
||||||
|
|
||||||
|
public interface IAutoDecisionService
|
||||||
|
{
|
||||||
|
void ApplyAutoDecisions(SingleJob job, VideoInfo probe);
|
||||||
|
}
|
||||||
10
Splitter-UI/Services/IFileJobFactory.cs
Normal file
10
Splitter-UI/Services/IFileJobFactory.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Splitter_UI.Services;
|
||||||
|
|
||||||
|
public interface IFileJobFactory
|
||||||
|
{
|
||||||
|
FileJobViewModel Create(SingleJob job);
|
||||||
|
}
|
||||||
8
Splitter-UI/Services/IFileProbeService.cs
Normal file
8
Splitter-UI/Services/IFileProbeService.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Splitter_UI.Services;
|
||||||
|
|
||||||
|
public interface IFileProbeService
|
||||||
|
{
|
||||||
|
Task<VideoInfo> ProbeAsync(SingleJob job);
|
||||||
|
}
|
||||||
9
Splitter-UI/Services/ILogService.cs
Normal file
9
Splitter-UI/Services/ILogService.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
namespace Splitter_UI.Services;
|
||||||
|
|
||||||
|
public interface ILogService
|
||||||
|
{
|
||||||
|
event Action<string>? MessageLogged;
|
||||||
|
|
||||||
|
void Write(string message);
|
||||||
|
}
|
||||||
11
Splitter-UI/Services/IProcessingService.cs
Normal file
11
Splitter-UI/Services/IProcessingService.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Splitter_UI.Services;
|
||||||
|
|
||||||
|
public interface IProcessingService
|
||||||
|
{
|
||||||
|
event Action<string, ProgressInfo>? ProgressChanged;
|
||||||
|
|
||||||
|
Task ProcessAsync(IEnumerable<SingleJob> jobs, CancellationToken token);
|
||||||
|
}
|
||||||
9
Splitter-UI/Services/IThumbnailService.cs
Normal file
9
Splitter-UI/Services/IThumbnailService.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Avalonia.Media.Imaging;
|
||||||
|
|
||||||
|
namespace Splitter_UI.Services;
|
||||||
|
|
||||||
|
public interface IThumbnailService
|
||||||
|
{
|
||||||
|
Task<Bitmap?> CreateThumbnailAsync(string file, VideoInfo probe);
|
||||||
|
}
|
||||||
14
Splitter-UI/Services/LogService.cs
Normal file
14
Splitter-UI/Services/LogService.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Avalonia;
|
||||||
|
|
||||||
|
namespace Splitter_UI.Services;
|
||||||
|
|
||||||
|
public sealed class LogService : ILogService
|
||||||
|
{
|
||||||
|
public event Action<string>? MessageLogged;
|
||||||
|
|
||||||
|
public void Write(string message)
|
||||||
|
{
|
||||||
|
MessageLogged?.Invoke(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
29
Splitter-UI/Services/ProcessingService.cs
Normal file
29
Splitter-UI/Services/ProcessingService.cs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Splitter_UI.Services;
|
||||||
|
|
||||||
|
public sealed class ProcessingService : IProcessingService
|
||||||
|
{
|
||||||
|
public event Action<string, ProgressInfo>? ProgressChanged;
|
||||||
|
|
||||||
|
public async Task ProcessAsync(IEnumerable<SingleJob> jobs, CancellationToken token)
|
||||||
|
{
|
||||||
|
foreach (var job in jobs)
|
||||||
|
{
|
||||||
|
for (int i = 0; i <= 100; i += 20)
|
||||||
|
{
|
||||||
|
if (token.IsCancellationRequested)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var progress = new ProgressInfo { Percent = i };
|
||||||
|
|
||||||
|
// Notify UI
|
||||||
|
ProgressChanged?.Invoke(job.InputFile, progress);
|
||||||
|
|
||||||
|
await Task.Delay(100, token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
120
Splitter-UI/Services/ThumbnailService.cs
Normal file
120
Splitter-UI/Services/ThumbnailService.cs
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Media.Imaging;
|
||||||
|
using Avalonia.Platform;
|
||||||
|
|
||||||
|
namespace Splitter_UI.Services;
|
||||||
|
|
||||||
|
public sealed class ThumbnailService : IThumbnailService
|
||||||
|
{
|
||||||
|
private readonly int _thumbWidth = 160;
|
||||||
|
private readonly int _thumbHeight = 90;
|
||||||
|
|
||||||
|
// Reusable buffer for BGR24 → 3 bytes per pixel
|
||||||
|
private readonly byte[] _bgrBuffer;
|
||||||
|
private readonly byte[] _bgraBuffer;
|
||||||
|
|
||||||
|
public ThumbnailService()
|
||||||
|
{
|
||||||
|
_bgrBuffer = new byte[_thumbWidth * _thumbHeight * 3];
|
||||||
|
_bgraBuffer = new byte[_thumbWidth * _thumbHeight * 4];
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Bitmap?> CreateThumbnailAsync(string file, VideoInfo probe)
|
||||||
|
{
|
||||||
|
// Decode a single frame using ffmpeg → raw BGR24 into _bgrBuffer
|
||||||
|
bool ok = await DecodeFrameAsync(file);
|
||||||
|
if (!ok)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Convert BGR24 → BGRA32
|
||||||
|
ConvertBgrToBgra(_bgrBuffer, _bgraBuffer, _thumbWidth, _thumbHeight);
|
||||||
|
|
||||||
|
// Create Avalonia Bitmap
|
||||||
|
return CreateBitmap(_bgraBuffer, _thumbWidth, _thumbHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> DecodeFrameAsync(string file)
|
||||||
|
{
|
||||||
|
// ffmpeg command: decode one frame, resize, output raw BGR24
|
||||||
|
var args =
|
||||||
|
$"-ss 0 -t 0.1 -i \"{file}\" " +
|
||||||
|
"-an -sn " +
|
||||||
|
$"-vf \"scale={_thumbWidth}:{_thumbHeight}:force_original_aspect_ratio=decrease," +
|
||||||
|
$"pad={_thumbWidth}:{_thumbHeight}:(ow-iw)/2:(oh-ih)/2,format=bgr24\" " +
|
||||||
|
"-f rawvideo -";
|
||||||
|
|
||||||
|
var psi = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = "ffmpeg",
|
||||||
|
Arguments = args,
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
RedirectStandardError = true,
|
||||||
|
UseShellExecute = false,
|
||||||
|
CreateNoWindow = true
|
||||||
|
};
|
||||||
|
|
||||||
|
var p = new Process { StartInfo = psi };
|
||||||
|
p.Start();
|
||||||
|
|
||||||
|
int needed = _bgrBuffer.Length;
|
||||||
|
int read = 0;
|
||||||
|
|
||||||
|
using var stdout = p.StandardOutput.BaseStream;
|
||||||
|
|
||||||
|
while (read < needed)
|
||||||
|
{
|
||||||
|
int r = await stdout.ReadAsync(_bgrBuffer, read, needed - read);
|
||||||
|
if (r == 0)
|
||||||
|
{
|
||||||
|
TryKill(p);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
read += r;
|
||||||
|
}
|
||||||
|
|
||||||
|
TryKill(p);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void TryKill(Process p)
|
||||||
|
{
|
||||||
|
try { p.Kill(); } catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ConvertBgrToBgra(byte[] bgr, byte[] bgra, int width, int height)
|
||||||
|
{
|
||||||
|
int si = 0;
|
||||||
|
int di = 0;
|
||||||
|
|
||||||
|
int totalPixels = width * height;
|
||||||
|
|
||||||
|
for (int i = 0; i < totalPixels; i++)
|
||||||
|
{
|
||||||
|
bgra[di + 0] = bgr[si + 0]; // B
|
||||||
|
bgra[di + 1] = bgr[si + 1]; // G
|
||||||
|
bgra[di + 2] = bgr[si + 2]; // R
|
||||||
|
bgra[di + 3] = 255; // A
|
||||||
|
|
||||||
|
si += 3;
|
||||||
|
di += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static unsafe Bitmap CreateBitmap(byte[] bgra, int width, int height)
|
||||||
|
{
|
||||||
|
int stride = width * 4;
|
||||||
|
|
||||||
|
fixed (byte* p = bgra)
|
||||||
|
{
|
||||||
|
return new Bitmap(
|
||||||
|
PixelFormat.Bgra8888,
|
||||||
|
AlphaFormat.Premul,
|
||||||
|
(nint)p,
|
||||||
|
new PixelSize(width, height),
|
||||||
|
new Vector(96, 96),
|
||||||
|
stride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
35
Splitter-UI/Splitter-UI.csproj
Normal file
35
Splitter-UI/Splitter-UI.csproj
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
|
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<AvaloniaResource Include="Assets\**" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Avalonia" Version="12.0.3" />
|
||||||
|
<PackageReference Include="Avalonia.Desktop" Version="12.0.3" />
|
||||||
|
<PackageReference Include="Avalonia.Themes.Fluent" Version="12.0.3" />
|
||||||
|
<PackageReference Include="Avalonia.Fonts.Inter" Version="12.0.3" />
|
||||||
|
<PackageReference Include="AvaloniaUI.DiagnosticsSupport" Version="2.2.1">
|
||||||
|
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
|
||||||
|
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.2" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.8" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\splitter-cli\splitter.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
34
Splitter-UI/ViewLocator.cs
Normal file
34
Splitter-UI/ViewLocator.cs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Controls.Templates;
|
||||||
|
|
||||||
|
namespace Splitter_UI;
|
||||||
|
/// <summary>
|
||||||
|
/// Given a view model, returns the corresponding view if possible.
|
||||||
|
/// </summary>
|
||||||
|
[RequiresUnreferencedCode(
|
||||||
|
"Default implementation of ViewLocator involves reflection which may be trimmed away.",
|
||||||
|
Url = "https://docs.avaloniaui.net/docs/concepts/view-locator")]
|
||||||
|
public class ViewLocator : IDataTemplate
|
||||||
|
{
|
||||||
|
public Control? Build(object? param)
|
||||||
|
{
|
||||||
|
if (param is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var name = param.GetType().FullName!.Replace("ViewModel", "View", StringComparison.Ordinal);
|
||||||
|
var type = Type.GetType(name);
|
||||||
|
|
||||||
|
if (type != null)
|
||||||
|
{
|
||||||
|
return (Control)Activator.CreateInstance(type)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TextBlock { Text = "Not Found: " + name };
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Match(object? data)
|
||||||
|
{
|
||||||
|
return data is ViewModelBase;
|
||||||
|
}
|
||||||
|
}
|
||||||
41
Splitter-UI/ViewModels/FileJobViewModel.cs
Normal file
41
Splitter-UI/ViewModels/FileJobViewModel.cs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
using Avalonia.Media.Imaging;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
|
||||||
|
namespace Splitter_UI.ViewModels;
|
||||||
|
|
||||||
|
public partial class FileJobViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
public SingleJob Job { get; }
|
||||||
|
public VideoInfo? Probe { get; set; }
|
||||||
|
public PreviewData? Preview { get; set; }
|
||||||
|
public ProgressInfo? Progress { get; set; }
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private Bitmap? _thumbnail;
|
||||||
|
|
||||||
|
public string FileName { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _suggestedAction = "";
|
||||||
|
|
||||||
|
private readonly IThumbnailService _thumbnails;
|
||||||
|
private readonly IFileProbeService _fileProbe;
|
||||||
|
|
||||||
|
public FileJobViewModel(SingleJob job, IThumbnailService thumbnails, IFileProbeService fileProbe)
|
||||||
|
{
|
||||||
|
Job = job;
|
||||||
|
_thumbnails = thumbnails;
|
||||||
|
_fileProbe = fileProbe;
|
||||||
|
|
||||||
|
FileName = Path.GetFileName(job.InputFile);
|
||||||
|
|
||||||
|
_ = Task.Run( LoadThumbnailAsync );
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LoadThumbnailAsync()
|
||||||
|
{
|
||||||
|
Probe = await _fileProbe.ProbeAsync(Job);
|
||||||
|
Thumbnail = await _thumbnails.CreateThumbnailAsync(Job.InputFile, Probe);
|
||||||
|
}
|
||||||
|
}
|
||||||
36
Splitter-UI/ViewModels/FileListViewModel.cs
Normal file
36
Splitter-UI/ViewModels/FileListViewModel.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
|
||||||
|
namespace Splitter_UI.ViewModels;
|
||||||
|
|
||||||
|
public partial class FileListViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private readonly IFileJobFactory _factory;
|
||||||
|
public ObservableCollection<FileJobViewModel> Files { get; } = [];
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private FileJobViewModel? _selected;
|
||||||
|
|
||||||
|
public event Action<FileJobViewModel?>? SelectedFileChanged;
|
||||||
|
|
||||||
|
public FileListViewModel(IFileJobFactory factory)
|
||||||
|
{
|
||||||
|
_factory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void OnSelectedChanged(FileJobViewModel? value)
|
||||||
|
=> SelectedFileChanged?.Invoke(value);
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void AddFiles(IEnumerable<string> paths)
|
||||||
|
{
|
||||||
|
foreach (var path in paths)
|
||||||
|
{
|
||||||
|
// Probe + auto-detect + thumbnail
|
||||||
|
var job = new SingleJob { InputFile = path };
|
||||||
|
var vm = _factory.Create(job);
|
||||||
|
Files.Add(vm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
Splitter-UI/ViewModels/InspectorPaneViewModel.cs
Normal file
25
Splitter-UI/ViewModels/InspectorPaneViewModel.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
|
||||||
|
namespace Splitter_UI.ViewModels;
|
||||||
|
|
||||||
|
public partial class InspectorPaneViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
[ObservableProperty]
|
||||||
|
private FileJobViewModel? _selected;
|
||||||
|
|
||||||
|
public List<string> DetectModes =>
|
||||||
|
[
|
||||||
|
"face", "body", "none"
|
||||||
|
];
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void ApplyOverrides()
|
||||||
|
{
|
||||||
|
if (Selected is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
16
Splitter-UI/ViewModels/LogPaneViewModel.cs
Normal file
16
Splitter-UI/ViewModels/LogPaneViewModel.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
|
namespace Splitter_UI.ViewModels;
|
||||||
|
|
||||||
|
public partial class LogPaneViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
public ObservableCollection<string> Logs { get; } = [];
|
||||||
|
|
||||||
|
public void Add(string message)
|
||||||
|
{
|
||||||
|
Logs.Add(message);
|
||||||
|
if (Logs.Count > 5000)
|
||||||
|
Logs.RemoveAt(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
36
Splitter-UI/ViewModels/MainViewModel.cs
Normal file
36
Splitter-UI/ViewModels/MainViewModel.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
|
||||||
|
namespace Splitter_UI.ViewModels;
|
||||||
|
|
||||||
|
public partial class MainViewModel : ViewModelBase
|
||||||
|
{
|
||||||
|
public FileListViewModel FileList { get; }
|
||||||
|
public PreviewPaneViewModel Preview { get; } = new PreviewPaneViewModel();
|
||||||
|
public InspectorPaneViewModel Inspector { get; } = new InspectorPaneViewModel();
|
||||||
|
public StatusBarViewModel StatusBar { get; } = new StatusBarViewModel();
|
||||||
|
public LogPaneViewModel LogPane { get; } = new LogPaneViewModel();
|
||||||
|
|
||||||
|
public MainViewModel(IFileJobFactory fileJobFactory)
|
||||||
|
{
|
||||||
|
FileList = new FileListViewModel(fileJobFactory);
|
||||||
|
// Wire selection → preview + inspector
|
||||||
|
FileList.SelectedFileChanged += file =>
|
||||||
|
{
|
||||||
|
Preview.Selected = file;
|
||||||
|
Inspector.Selected = file;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
StatusBar.StatusText = "Processing…";
|
||||||
|
// call IProcessingService here
|
||||||
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void Stop()
|
||||||
|
{
|
||||||
|
StatusBar.StatusText = "Stopped";
|
||||||
|
}
|
||||||
|
}
|
||||||
13
Splitter-UI/ViewModels/PreviewPaneViewModel.cs
Normal file
13
Splitter-UI/ViewModels/PreviewPaneViewModel.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
|
||||||
|
namespace Splitter_UI.ViewModels;
|
||||||
|
|
||||||
|
public partial class PreviewPaneViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
[ObservableProperty]
|
||||||
|
private FileJobViewModel? _selected;
|
||||||
|
|
||||||
|
public PreviewPaneViewModel()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
15
Splitter-UI/ViewModels/StatusBarViewModel.cs
Normal file
15
Splitter-UI/ViewModels/StatusBarViewModel.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
|
||||||
|
namespace Splitter_UI.ViewModels;
|
||||||
|
|
||||||
|
public partial class StatusBarViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _statusText = "Ready";
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private double _percent;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _threadInfo = "Threads: 0/0";
|
||||||
|
}
|
||||||
7
Splitter-UI/ViewModels/ViewModelBase.cs
Normal file
7
Splitter-UI/ViewModels/ViewModelBase.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
|
||||||
|
namespace Splitter_UI.ViewModels;
|
||||||
|
|
||||||
|
public abstract class ViewModelBase : ObservableObject
|
||||||
|
{
|
||||||
|
}
|
||||||
73
Splitter-UI/Views/FileListView.axaml
Normal file
73
Splitter-UI/Views/FileListView.axaml
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<UserControl
|
||||||
|
xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:vm="clr-namespace:Splitter_UI.ViewModels"
|
||||||
|
xmlns:views="clr-namespace:Splitter_UI.Views"
|
||||||
|
x:Class="Splitter_UI.Views.FileListView"
|
||||||
|
x:DataType="vm:FileListViewModel">
|
||||||
|
|
||||||
|
<UserControl.Styles>
|
||||||
|
<Style Selector="views|FileListView Border#DropZone">
|
||||||
|
<Setter Property="BorderBrush" Value="Transparent"/>
|
||||||
|
<Setter Property="BorderThickness" Value="0"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style Selector="views|FileListView[IsDragActive=true] Border#DropZone">
|
||||||
|
<Setter Property="BorderBrush" Value="Red"/>
|
||||||
|
<Setter Property="BorderThickness" Value="2"/>
|
||||||
|
</Style>
|
||||||
|
</UserControl.Styles>
|
||||||
|
|
||||||
|
<Border x:Name="DropZone"
|
||||||
|
Background="#1E1E1E"
|
||||||
|
Padding="10"
|
||||||
|
DragDrop.AllowDrop="True"
|
||||||
|
DragDrop.Drop="OnDrop"
|
||||||
|
DragDrop.DragOver="OnDragOver"
|
||||||
|
DragDrop.DragEnter="OnDragEnter"
|
||||||
|
DragDrop.DragLeave="OnDragLeave">
|
||||||
|
|
||||||
|
<ScrollViewer>
|
||||||
|
<ItemsControl ItemsSource="{Binding Files}">
|
||||||
|
<ItemsControl.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<WrapPanel Orientation="Horizontal"/>
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ItemsControl.ItemsPanel>
|
||||||
|
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="vm:FileJobViewModel">
|
||||||
|
<Border Margin="6" Padding="6" Background="#2A2A2A" CornerRadius="4">
|
||||||
|
<StackPanel MinWidth="160" MaxWidth="160">
|
||||||
|
<Border Width="160" Height="90" ClipToBounds="True">
|
||||||
|
<Image Source="{Binding Thumbnail}"
|
||||||
|
Stretch="UniformToFill"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"/>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<TextBlock Text="{Binding FileName}"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Margin="0,6,0,0"
|
||||||
|
FontSize="10"/>
|
||||||
|
|
||||||
|
<TextBlock Text="{Binding SuggestedAction}"
|
||||||
|
Foreground="LightGreen"
|
||||||
|
FontSize="10"/>
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
MinWidth="160"
|
||||||
|
MaxWidth="160"
|
||||||
|
Height="10"
|
||||||
|
Margin="0,4,0,0"
|
||||||
|
Value="{Binding Progress.Percent}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
|
||||||
|
</ItemsControl>
|
||||||
|
</ScrollViewer>
|
||||||
|
</Border>
|
||||||
|
</UserControl>
|
||||||
72
Splitter-UI/Views/FileListView.axaml.cs
Normal file
72
Splitter-UI/Views/FileListView.axaml.cs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Input;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
|
using Avalonia.Platform.Storage;
|
||||||
|
using Splitter_UI.ViewModels;
|
||||||
|
|
||||||
|
namespace Splitter_UI.Views;
|
||||||
|
|
||||||
|
public partial class FileListView : UserControl
|
||||||
|
{
|
||||||
|
public static readonly StyledProperty<bool> IsDragActiveProperty =
|
||||||
|
AvaloniaProperty.Register<FileListView, bool>(nameof(IsDragActive));
|
||||||
|
|
||||||
|
public bool IsDragActive
|
||||||
|
{
|
||||||
|
get => GetValue(IsDragActiveProperty);
|
||||||
|
set => SetValue(IsDragActiveProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileListView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDragEnter(object? sender, DragEventArgs e)
|
||||||
|
{
|
||||||
|
IsDragActive = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDragLeave(object? sender, DragEventArgs e)
|
||||||
|
{
|
||||||
|
IsDragActive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDragOver(object? sender, DragEventArgs e)
|
||||||
|
{
|
||||||
|
// Avalonia 12:
|
||||||
|
// e.Data is IDataObject, but it has NO strongly typed formats.
|
||||||
|
if (e.DataTransfer.Contains(DataFormat.File))
|
||||||
|
e.DragEffects = DragDropEffects.Copy;
|
||||||
|
else
|
||||||
|
e.DragEffects = DragDropEffects.None;
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnDrop(object? sender, DragEventArgs e)
|
||||||
|
{
|
||||||
|
IsDragActive = false;
|
||||||
|
|
||||||
|
if (DataContext is not FileListViewModel vm)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!e.DataTransfer.Contains(DataFormat.File))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Avalonia 12:
|
||||||
|
// This is the ONLY correct way to get dropped files.
|
||||||
|
var items = e.DataTransfer.TryGetFiles();
|
||||||
|
if (items is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var paths = items
|
||||||
|
.OfType<IStorageFile>()
|
||||||
|
.Select(f => f.Path.LocalPath)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (paths.Count > 0)
|
||||||
|
vm.AddFilesCommand.Execute(paths);
|
||||||
|
}
|
||||||
|
}
|
||||||
42
Splitter-UI/Views/InspectorPane.axaml
Normal file
42
Splitter-UI/Views/InspectorPane.axaml
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<UserControl
|
||||||
|
xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
x:Class="Splitter_UI.Views.InspectorPane"
|
||||||
|
xmlns:vm="clr-namespace:Splitter_UI.ViewModels"
|
||||||
|
x:DataType="vm:InspectorPaneViewModel">
|
||||||
|
|
||||||
|
<Border Background="#252525" Padding="12">
|
||||||
|
<StackPanel Spacing="8">
|
||||||
|
|
||||||
|
<TextBlock Text="Parameters" FontSize="18" Margin="0,0,0,10"/>
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||||
|
<TextBlock Text="Rotate:" Width="100"/>
|
||||||
|
<NumericUpDown Value="{Binding Selected.Job.Rotate}" Width="120"/>
|
||||||
|
</StackPanel>
|
||||||
|
<!--
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||||
|
<TextBlock Text="Crop:" Width="100"/>
|
||||||
|
<TextBox Text="{Binding Selected.CropText}" Width="200"/>
|
||||||
|
</StackPanel>
|
||||||
|
-->
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||||
|
<TextBlock Text="Detect:" Width="100"/>
|
||||||
|
<ComboBox ItemsSource="{Binding DetectModes}"
|
||||||
|
SelectedItem="{Binding Selected.Job.Detect}"
|
||||||
|
Width="200"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<CheckBox Content="Force Fixed Duration"
|
||||||
|
IsChecked="{Binding Selected.Job.ForceFixed}"/>
|
||||||
|
|
||||||
|
<CheckBox Content="Debug Mode"
|
||||||
|
IsChecked="{Binding Selected.Job.Debug}"/>
|
||||||
|
|
||||||
|
<Button Content="Apply to Selected"
|
||||||
|
Command="{Binding ApplyOverridesCommand}"
|
||||||
|
Margin="0,10,0,0"/>
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</UserControl>
|
||||||
11
Splitter-UI/Views/InspectorPane.axaml.cs
Normal file
11
Splitter-UI/Views/InspectorPane.axaml.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
|
||||||
|
namespace Splitter_UI.Views;
|
||||||
|
|
||||||
|
public partial class InspectorPane : UserControl
|
||||||
|
{
|
||||||
|
public InspectorPane()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
19
Splitter-UI/Views/LogPane.axaml
Normal file
19
Splitter-UI/Views/LogPane.axaml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<UserControl
|
||||||
|
xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
x:Class="Splitter_UI.Views.LogPane"
|
||||||
|
xmlns:vm="clr-namespace:Splitter_UI.ViewModels"
|
||||||
|
x:DataType="vm:LogPaneViewModel">
|
||||||
|
|
||||||
|
<Border Background="#111" Padding="8">
|
||||||
|
<ScrollViewer>
|
||||||
|
<ItemsControl ItemsSource="{Binding Logs}">
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<TextBlock Text="{Binding}" FontFamily="Consolas" FontSize="12"/>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</ScrollViewer>
|
||||||
|
</Border>
|
||||||
|
</UserControl>
|
||||||
11
Splitter-UI/Views/LogPane.axaml.cs
Normal file
11
Splitter-UI/Views/LogPane.axaml.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
|
||||||
|
namespace Splitter_UI.Views;
|
||||||
|
|
||||||
|
public partial class LogPane : UserControl
|
||||||
|
{
|
||||||
|
public LogPane()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
41
Splitter-UI/Views/MainWindow.axaml
Normal file
41
Splitter-UI/Views/MainWindow.axaml
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<Window
|
||||||
|
xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:views="clr-namespace:Splitter_UI.Views"
|
||||||
|
xmlns:vm="clr-namespace:Splitter_UI.ViewModels"
|
||||||
|
x:Class="Splitter_UI.Views.MainWindow"
|
||||||
|
x:DataType="vm:MainViewModel"
|
||||||
|
Width="1400"
|
||||||
|
Height="900"
|
||||||
|
Title="Splitter UI">
|
||||||
|
|
||||||
|
<DockPanel>
|
||||||
|
|
||||||
|
<!-- Status Bar -->
|
||||||
|
<views:StatusBarView DockPanel.Dock="Bottom"
|
||||||
|
DataContext="{Binding StatusBar}" />
|
||||||
|
|
||||||
|
<!-- Log Pane -->
|
||||||
|
<views:LogPane DockPanel.Dock="Bottom" Height="150"
|
||||||
|
DataContext="{Binding LogPane}" />
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<Grid ColumnDefinitions="2*,3*,2*">
|
||||||
|
|
||||||
|
<!-- File List -->
|
||||||
|
<views:FileListView Grid.Column="0"
|
||||||
|
DataContext="{Binding FileList}" />
|
||||||
|
|
||||||
|
<!-- Preview -->
|
||||||
|
<views:PreviewPane Grid.Column="1"
|
||||||
|
DataContext="{Binding Preview}" />
|
||||||
|
|
||||||
|
<!-- Inspector -->
|
||||||
|
<views:InspectorPane Grid.Column="2"
|
||||||
|
DataContext="{Binding Inspector}" />
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
</DockPanel>
|
||||||
|
</Window>
|
||||||
|
|
||||||
12
Splitter-UI/Views/MainWindow.axaml.cs
Normal file
12
Splitter-UI/Views/MainWindow.axaml.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
|
||||||
|
namespace Splitter_UI.Views;
|
||||||
|
|
||||||
|
public partial class MainWindow : Window
|
||||||
|
{
|
||||||
|
public MainViewModel Data { get; } = null!; // set by DI
|
||||||
|
public MainWindow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
24
Splitter-UI/Views/PreviewPane.axaml
Normal file
24
Splitter-UI/Views/PreviewPane.axaml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<UserControl
|
||||||
|
xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
x:Class="Splitter_UI.Views.PreviewPane"
|
||||||
|
xmlns:vm="clr-namespace:Splitter_UI.ViewModels"
|
||||||
|
x:DataType="vm:PreviewPaneViewModel">
|
||||||
|
|
||||||
|
<Border Background="#202020" Padding="10">
|
||||||
|
<Grid>
|
||||||
|
<Image Source="{Binding Selected.Preview.Frame}" Stretch="Uniform"/>
|
||||||
|
|
||||||
|
<!-- Optional overlays -->
|
||||||
|
<ItemsControl ItemsSource="{Binding Selected.Preview.FaceBoxes}">
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<Border BorderBrush="Lime" BorderThickness="2"
|
||||||
|
Width="{Binding Width}" Height="{Binding Height}"
|
||||||
|
Canvas.Left="{Binding X}" Canvas.Top="{Binding Y}"/>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</UserControl>
|
||||||
11
Splitter-UI/Views/PreviewPane.axaml.cs
Normal file
11
Splitter-UI/Views/PreviewPane.axaml.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
|
||||||
|
namespace Splitter_UI.Views;
|
||||||
|
|
||||||
|
public partial class PreviewPane : UserControl
|
||||||
|
{
|
||||||
|
public PreviewPane()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
24
Splitter-UI/Views/StatusBarView.axaml
Normal file
24
Splitter-UI/Views/StatusBarView.axaml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<UserControl
|
||||||
|
xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
x:Class="Splitter_UI.Views.StatusBarView"
|
||||||
|
xmlns:vm="clr-namespace:Splitter_UI.ViewModels"
|
||||||
|
x:DataType="vm:StatusBarViewModel">
|
||||||
|
|
||||||
|
<Border Padding="4" Background="{DynamicResource ThemeBackgroundBrush}">
|
||||||
|
<Grid ColumnDefinitions="*,Auto,Auto" ColumnSpacing="8">
|
||||||
|
<TextBlock Grid.Column="0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{Binding StatusText}" />
|
||||||
|
|
||||||
|
<ProgressBar Grid.Column="1"
|
||||||
|
Width="200" Height="16"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Value="{Binding Percent}" />
|
||||||
|
|
||||||
|
<TextBlock Grid.Column="2"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{Binding ThreadInfo}" />
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</UserControl>
|
||||||
11
Splitter-UI/Views/StatusBarView.axaml.cs
Normal file
11
Splitter-UI/Views/StatusBarView.axaml.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
|
||||||
|
namespace Splitter_UI.Views;
|
||||||
|
|
||||||
|
public partial class StatusBarView : UserControl
|
||||||
|
{
|
||||||
|
public StatusBarView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
18
Splitter-UI/app.manifest
Normal file
18
Splitter-UI/app.manifest
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
<!-- This manifest is used on Windows only.
|
||||||
|
Don't remove it as it might cause problems with window transparency and embedded controls.
|
||||||
|
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
|
||||||
|
<assemblyIdentity version="1.0.0.0" name="Splitter_UI.Desktop"/>
|
||||||
|
|
||||||
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
|
<application>
|
||||||
|
<!-- A list of the Windows versions that this application has been tested on
|
||||||
|
and is designed to work with. Uncomment the appropriate elements
|
||||||
|
and Windows will automatically select the most compatible environment. -->
|
||||||
|
|
||||||
|
<!-- Windows 10 -->
|
||||||
|
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||||
|
</application>
|
||||||
|
</compatibility>
|
||||||
|
</assembly>
|
||||||
@ -6,4 +6,5 @@
|
|||||||
<File Path="README.md" />
|
<File Path="README.md" />
|
||||||
</Folder>
|
</Folder>
|
||||||
<Project Path="splitter-cli/splitter.csproj" />
|
<Project Path="splitter-cli/splitter.csproj" />
|
||||||
|
<Project Path="Splitter-UI/Splitter-UI.csproj" Id="f80bfed3-d5f0-4292-92b2-909c21625ee3" />
|
||||||
</Solution>
|
</Solution>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user