Remove legacy WPF app, flatten WinUI project to repo root, rename namespace

The repo carried two parallel UIs (WPF + WinUI) sharing Models/Services via
cross-directory Link includes. Now that WinUI is the only frontend, collapse
the structure so the WinUI project IS the repo root instead of a nested
sibling folder:

- Delete the WPF project entirely (App.xaml, MainWindow.xaml, csproj) and its
  bin/obj output
- Move AmiReel.WinUI/* up to the repo root (App, MainWindow, MainPage,
  DialogHelper, Assets, Package.appxmanifest, app.manifest, Properties,
  .github/instructions, AGENTS.md) via git mv, preserving history
- Rename AmiReel.WinUI.csproj -> AmiReel.csproj; regenerate the solution as
  AmiReel.slnx (the newer XML solution format) with a single project
- Rename namespace AmigaDB.VideoRenderer.{Models,Services} -> AmiReel.{...}
  and AmiReel_WinUI -> AmiReel across all files, including the embedded
  ffmpeg/ffprobe resource logical names in the csproj and ToolExtractor
- Models/ and Services/ no longer need the Link-based cross-directory
  <Compile Include>; they're picked up by the SDK's default globbing now
  that they live under the project directory
- Rename assets/ -> branding/ (source icon art) to avoid a case-insensitive
  collision with Assets/ (packaged tile art) once both sit at repo root
- Merge the two .gitignore files into one; track the PublishProfiles pubxml
  files instead of ignoring them (no secrets, and they keep publish
  reproducible across machines) as branding, gitignore, etc.
- Simplify publish-win-x64.ps1 (drop the -Target Wpf/WinUI switch, there's
  only one target now) and rewrite README.md to describe the single-project
  layout, build/run/publish commands, and file structure

Verified: dotnet build succeeds for both AmiReel.csproj and AmiReel.slnx, and
the built exe launches and renders identically to before the move.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-13 14:07:10 +02:00
parent 3e881b79b8
commit 603af17e56
66 changed files with 641 additions and 1905 deletions
+46 -5
View File
@@ -1,14 +1,55 @@
bin/ # Build output
obj/ [Bb]in/
[Oo]bj/
[Dd]ebug/
[Rr]elease/
publish/
# IDE / tooling
.vs/ .vs/
.vscode/ .vscode/
_ReSharper*/
*.DotSettings.user
*.dotCover
.idea/
# MSBuild / user files
*.user
*.suo
*.userosscache
*.sln.docstates
*.pdb *.pdb
*.cache *.cache
*.tmp *.tmp
*.log *.log
*.userosscache *.binlog
# Test results
TestResults/ TestResults/
[Tt]est[Rr]esult*/
*.trx
*.coverage
*.coveragexml
# NuGet
*.nupkg
*.snupkg
*.nuget.props
*.nuget.targets
project.lock.json
# MSIX packaging output
AppPackages/
BundleArtifacts/
*.msix
*.msixupload
*.appx
*.appxbundle
*.appxupload
# Local dev signing certificate
devcert.pfx
# Embedded third-party binaries (fetched separately, see ThirdParty/README.md)
ThirdParty/ffmpeg.exe ThirdParty/ffmpeg.exe
ThirdParty/ffprobe.exe ThirdParty/ffprobe.exe
*.user
*.suo
-54
View File
@@ -1,54 +0,0 @@
## .NET / Visual Studio
[Bb]in/
[Oo]bj/
[Dd]ebug/
[Rr]elease/
.vs/
*.user
*.suo
*.userosscache
*.sln.docstates
artifacts/
# Build logs
[Ll]og/
[Ll]ogs/
*.log
*.binlog
# Test results
[Tt]est[Rr]esult*/
*.trx
*.coverage
*.coveragexml
# NuGet
*.nupkg
*.snupkg
*.nuget.props
*.nuget.targets
project.lock.json
# MSIX packaging output
AppPackages/
BundleArtifacts/
*.msix
*.msixupload
*.appx
*.appxbundle
*.appxupload
# Publish output
publish/
*.pubxml
PublishScripts/
# Code analysis and tooling
_ReSharper*/
*.DotSettings.user
*.dotCover
.idea/
# Generated files
Generated\ Files/
*_wpftmp.csproj
-269
View File
@@ -1,269 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Application
x:Class="AmiReel_WinUI.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AmiReel_WinUI">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
</ResourceDictionary.MergedDictionaries>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="PageBrush" Color="#0E1525"/>
<SolidColorBrush x:Key="PanelBrush" Color="#162033"/>
<SolidColorBrush x:Key="PanelAltBrush" Color="#1A2740"/>
<SolidColorBrush x:Key="FieldBrush" Color="#1D2940"/>
<SolidColorBrush x:Key="BorderBrush" Color="#2B3A58"/>
<SolidColorBrush x:Key="AccentBrush" Color="#4AB8FF"/>
<SolidColorBrush x:Key="AccentSoftBrush" Color="#13324F"/>
<SolidColorBrush x:Key="TextBrush" Color="#F5F7FB"/>
<SolidColorBrush x:Key="MutedBrush" Color="#9FB1CC"/>
<SolidColorBrush x:Key="ButtonBrush" Color="#243554"/>
<SolidColorBrush x:Key="ButtonHoverBrush" Color="#2E446C"/>
<SolidColorBrush x:Key="PrimaryTextBrush" Color="#07111D"/>
<!-- Fluent control theme-brush overrides so built-in controls match the palette -->
<SolidColorBrush x:Key="TextControlBackground" Color="#1D2940"/>
<SolidColorBrush x:Key="TextControlBackgroundPointerOver" Color="#233252"/>
<SolidColorBrush x:Key="TextControlBackgroundFocused" Color="#1D2940"/>
<SolidColorBrush x:Key="TextControlBorderBrush" Color="#2B3A58"/>
<SolidColorBrush x:Key="TextControlBorderBrushPointerOver" Color="#4AB8FF"/>
<SolidColorBrush x:Key="TextControlBorderBrushFocused" Color="#4AB8FF"/>
<SolidColorBrush x:Key="TextControlForeground" Color="#F5F7FB"/>
<SolidColorBrush x:Key="TextControlForegroundFocused" Color="#F5F7FB"/>
<SolidColorBrush x:Key="TextControlHeaderForeground" Color="#9FB1CC"/>
<SolidColorBrush x:Key="TextControlPlaceholderForeground" Color="#7488A6"/>
<SolidColorBrush x:Key="ComboBoxBackground" Color="#1D2940"/>
<SolidColorBrush x:Key="ComboBoxBackgroundPointerOver" Color="#233252"/>
<SolidColorBrush x:Key="ComboBoxBorderBrush" Color="#2B3A58"/>
<SolidColorBrush x:Key="ComboBoxForeground" Color="#F5F7FB"/>
<SolidColorBrush x:Key="ComboBoxHeaderForeground" Color="#9FB1CC"/>
<SolidColorBrush x:Key="ComboBoxItemBackgroundSelected" Color="#13324F"/>
<SolidColorBrush x:Key="ComboBoxDropDownBackground" Color="#10192B"/>
<SolidColorBrush x:Key="ButtonBackground" Color="#243554"/>
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="#2E446C"/>
<SolidColorBrush x:Key="ButtonBackgroundPressed" Color="#1B2A44"/>
<SolidColorBrush x:Key="ButtonForeground" Color="#F5F7FB"/>
<SolidColorBrush x:Key="ButtonForegroundPointerOver" Color="#F5F7FB"/>
<SolidColorBrush x:Key="ButtonBorderBrush" Color="#2B3A58"/>
<SolidColorBrush x:Key="ContentDialogBackground" Color="#162033"/>
<SolidColorBrush x:Key="ContentDialogForeground" Color="#F5F7FB"/>
<SolidColorBrush x:Key="ContentDialogBorderBrush" Color="#35507D"/>
<SolidColorBrush x:Key="ListViewItemBackgroundSelected" Color="#13324F"/>
<SolidColorBrush x:Key="ListViewItemBackgroundPointerOver" Color="#1A2740"/>
<SolidColorBrush x:Key="ListViewItemForeground" Color="#F5F7FB"/>
<SolidColorBrush x:Key="CheckBoxCheckBackgroundFillUnchecked" Color="#1D2940"/>
<SolidColorBrush x:Key="CheckBoxCheckBackgroundStrokeUnchecked" Color="#2B3A58"/>
<SolidColorBrush x:Key="CheckBoxCheckBackgroundFillChecked" Color="#4AB8FF"/>
<SolidColorBrush x:Key="CheckBoxCheckBackgroundStrokeChecked" Color="#4AB8FF"/>
<SolidColorBrush x:Key="CheckBoxCheckGlyphForegroundChecked" Color="#07111D"/>
<SolidColorBrush x:Key="CheckBoxForegroundUnchecked" Color="#F5F7FB"/>
<SolidColorBrush x:Key="CheckBoxForegroundChecked" Color="#F5F7FB"/>
<SolidColorBrush x:Key="ProgressBarForeground" Color="#4AB8FF"/>
<SolidColorBrush x:Key="ProgressBarBackground" Color="#1D2940"/>
<SolidColorBrush x:Key="HeroBorderBrush" Color="#35507D"/>
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="PageBrush" Color="#F4F7FB"/>
<SolidColorBrush x:Key="PanelBrush" Color="#FFFFFF"/>
<SolidColorBrush x:Key="PanelAltBrush" Color="#EEF4FB"/>
<SolidColorBrush x:Key="FieldBrush" Color="#F7FAFD"/>
<SolidColorBrush x:Key="BorderBrush" Color="#C7D3E3"/>
<SolidColorBrush x:Key="AccentBrush" Color="#0E9AEF"/>
<SolidColorBrush x:Key="AccentSoftBrush" Color="#D6EEFF"/>
<SolidColorBrush x:Key="TextBrush" Color="#122033"/>
<SolidColorBrush x:Key="MutedBrush" Color="#5F7390"/>
<SolidColorBrush x:Key="ButtonBrush" Color="#E5EDF7"/>
<SolidColorBrush x:Key="ButtonHoverBrush" Color="#D7E4F4"/>
<SolidColorBrush x:Key="PrimaryTextBrush" Color="#FFFFFF"/>
<SolidColorBrush x:Key="TextControlBackground" Color="#F7FAFD"/>
<SolidColorBrush x:Key="TextControlBackgroundPointerOver" Color="#EEF4FB"/>
<SolidColorBrush x:Key="TextControlBackgroundFocused" Color="#FFFFFF"/>
<SolidColorBrush x:Key="TextControlBorderBrush" Color="#C7D3E3"/>
<SolidColorBrush x:Key="TextControlBorderBrushPointerOver" Color="#0E9AEF"/>
<SolidColorBrush x:Key="TextControlBorderBrushFocused" Color="#0E9AEF"/>
<SolidColorBrush x:Key="TextControlForeground" Color="#122033"/>
<SolidColorBrush x:Key="TextControlForegroundFocused" Color="#122033"/>
<SolidColorBrush x:Key="TextControlHeaderForeground" Color="#5F7390"/>
<SolidColorBrush x:Key="TextControlPlaceholderForeground" Color="#8194AC"/>
<SolidColorBrush x:Key="ComboBoxBackground" Color="#F7FAFD"/>
<SolidColorBrush x:Key="ComboBoxBackgroundPointerOver" Color="#EEF4FB"/>
<SolidColorBrush x:Key="ComboBoxBorderBrush" Color="#C7D3E3"/>
<SolidColorBrush x:Key="ComboBoxForeground" Color="#122033"/>
<SolidColorBrush x:Key="ComboBoxHeaderForeground" Color="#5F7390"/>
<SolidColorBrush x:Key="ComboBoxItemBackgroundSelected" Color="#D6EEFF"/>
<SolidColorBrush x:Key="ComboBoxDropDownBackground" Color="#FFFFFF"/>
<SolidColorBrush x:Key="ButtonBackground" Color="#E5EDF7"/>
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="#D7E4F4"/>
<SolidColorBrush x:Key="ButtonBackgroundPressed" Color="#C7D9EE"/>
<SolidColorBrush x:Key="ButtonForeground" Color="#122033"/>
<SolidColorBrush x:Key="ButtonForegroundPointerOver" Color="#122033"/>
<SolidColorBrush x:Key="ButtonBorderBrush" Color="#C7D3E3"/>
<SolidColorBrush x:Key="ContentDialogBackground" Color="#FFFFFF"/>
<SolidColorBrush x:Key="ContentDialogForeground" Color="#122033"/>
<SolidColorBrush x:Key="ContentDialogBorderBrush" Color="#C7D3E3"/>
<SolidColorBrush x:Key="ListViewItemBackgroundSelected" Color="#D6EEFF"/>
<SolidColorBrush x:Key="ListViewItemBackgroundPointerOver" Color="#EEF4FB"/>
<SolidColorBrush x:Key="ListViewItemForeground" Color="#122033"/>
<SolidColorBrush x:Key="CheckBoxCheckBackgroundFillUnchecked" Color="#F7FAFD"/>
<SolidColorBrush x:Key="CheckBoxCheckBackgroundStrokeUnchecked" Color="#C7D3E3"/>
<SolidColorBrush x:Key="CheckBoxCheckBackgroundFillChecked" Color="#0E9AEF"/>
<SolidColorBrush x:Key="CheckBoxCheckBackgroundStrokeChecked" Color="#0E9AEF"/>
<SolidColorBrush x:Key="CheckBoxCheckGlyphForegroundChecked" Color="#FFFFFF"/>
<SolidColorBrush x:Key="CheckBoxForegroundUnchecked" Color="#122033"/>
<SolidColorBrush x:Key="CheckBoxForegroundChecked" Color="#122033"/>
<SolidColorBrush x:Key="ProgressBarForeground" Color="#0E9AEF"/>
<SolidColorBrush x:Key="ProgressBarBackground" Color="#F7FAFD"/>
<SolidColorBrush x:Key="HeroBorderBrush" Color="#A9C4E4"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<!-- Global corner rounding so built-in Fluent controls match the WPF app's rounded look -->
<CornerRadius x:Key="ControlCornerRadius">10</CornerRadius>
<CornerRadius x:Key="OverlayCornerRadius">18</CornerRadius>
<x:Double x:Key="ContentDialogMaxWidth">460</x:Double>
<Style TargetType="Page">
<Setter Property="Background" Value="{ThemeResource PageBrush}" />
<Setter Property="FontFamily" Value="Bahnschrift" />
</Style>
<Style x:Key="CardBorderStyle" TargetType="Border">
<Setter Property="Background" Value="{ThemeResource PanelBrush}" />
<Setter Property="BorderBrush" Value="{ThemeResource BorderBrush}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="16" />
</Style>
<Style x:Key="SectionTitleStyle" TargetType="TextBlock">
<Setter Property="Foreground" Value="{ThemeResource TextBrush}" />
<Setter Property="FontSize" Value="15" />
<Setter Property="FontWeight" Value="SemiBold" />
</Style>
<Style x:Key="MutedTextStyle" TargetType="TextBlock">
<Setter Property="Foreground" Value="{ThemeResource MutedBrush}" />
<Setter Property="FontSize" Value="13" />
</Style>
<Style x:Key="FieldLabelStyle" TargetType="TextBlock">
<Setter Property="Foreground" Value="{ThemeResource MutedBrush}" />
<Setter Property="FontSize" Value="12" />
<Setter Property="Margin" Value="0,0,0,4" />
</Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{ThemeResource TextBrush}" />
<Setter Property="FontSize" Value="13" />
</Style>
<Style TargetType="Button">
<Setter Property="Padding" Value="16,9" />
<Setter Property="FontSize" Value="13" />
<Setter Property="CornerRadius" Value="10" />
</Style>
<Style x:Key="PrimaryButtonStyle" TargetType="Button">
<Setter Property="Background" Value="{ThemeResource AccentBrush}" />
<Setter Property="Foreground" Value="{ThemeResource PrimaryTextBrush}" />
<Setter Property="BorderBrush" Value="{ThemeResource AccentBrush}" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Padding" Value="22,10" />
<Setter Property="CornerRadius" Value="10" />
</Style>
<Style x:Key="IconButtonStyle" TargetType="Button">
<Setter Property="Width" Value="40" />
<Setter Property="Height" Value="40" />
<Setter Property="Padding" Value="0" />
<Setter Property="FontSize" Value="16" />
<Setter Property="CornerRadius" Value="10" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
</Style>
<!-- Explicit styles bound to ContentDialog.PrimaryButtonStyle / CloseButtonStyle so its
built-in buttons match the app's own Button look instead of the default Fluent
accent-pill / plain styles. -->
<Style x:Key="DialogPrimaryButtonStyle" TargetType="Button">
<Setter Property="Background" Value="{ThemeResource AccentBrush}" />
<Setter Property="Foreground" Value="{ThemeResource PrimaryTextBrush}" />
<Setter Property="BorderBrush" Value="{ThemeResource AccentBrush}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="FontSize" Value="13" />
<Setter Property="Padding" Value="22,10" />
<Setter Property="CornerRadius" Value="10" />
<Setter Property="MinHeight" Value="40" />
<Setter Property="MinWidth" Value="110" />
</Style>
<Style x:Key="DialogCloseButtonStyle" TargetType="Button">
<Setter Property="Background" Value="{ThemeResource ButtonBrush}" />
<Setter Property="Foreground" Value="{ThemeResource TextBrush}" />
<Setter Property="BorderBrush" Value="{ThemeResource BorderBrush}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="FontSize" Value="13" />
<Setter Property="Padding" Value="16,9" />
<Setter Property="CornerRadius" Value="10" />
<Setter Property="MinHeight" Value="40" />
<Setter Property="MinWidth" Value="110" />
</Style>
<Style TargetType="TextBox">
<Setter Property="FontSize" Value="13" />
<Setter Property="MinHeight" Value="38" />
</Style>
<Style TargetType="ComboBox">
<Setter Property="FontSize" Value="13" />
<Setter Property="MinHeight" Value="38" />
</Style>
<Style TargetType="CheckBox">
<Setter Property="FontSize" Value="13" />
</Style>
<Style TargetType="ProgressBar">
<Setter Property="MinHeight" Value="8" />
</Style>
<Style TargetType="ListView">
<Setter Property="Background" Value="{ThemeResource FieldBrush}" />
<Setter Property="BorderBrush" Value="{ThemeResource BorderBrush}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="10" />
<Setter Property="FontSize" Value="13" />
<Setter Property="Padding" Value="4" />
</Style>
<Style TargetType="ListViewItem">
<Setter Property="MinHeight" Value="32" />
<Setter Property="Padding" Value="8,6" />
<Setter Property="CornerRadius" Value="6" />
</Style>
<Style TargetType="ContentDialog">
<Setter Property="CornerRadius" Value="18" />
</Style>
</ResourceDictionary>
</Application.Resources>
</Application>
-48
View File
@@ -1,48 +0,0 @@
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using Microsoft.UI.Xaml.Shapes;
using WinRT.Interop;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace AmiReel_WinUI;
/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </summary>
public partial class App : Application
{
private Window? _window;
public static IntPtr MainWindowHandle { get; private set; }
public static MainWindow? MainWindowInstance { get; set; }
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
InitializeComponent();
}
/// <summary>
/// Invoked when the application is launched.
/// </summary>
/// <param name="args">Details about the launch request and process.</param>
protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
{
_window = new MainWindow();
MainWindowHandle = WindowNative.GetWindowHandle(_window);
_window.Activate();
}
}
-38
View File
@@ -1,38 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<Window
x:Class="AmiReel_WinUI.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:AmiReel_WinUI"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="AmiReel"
mc:Ignorable="d">
<Grid Background="{ThemeResource PageBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid x:Name="AppTitleBar" Grid.Row="0" Height="44" Background="{ThemeResource PanelBrush}">
<StackPanel Orientation="Horizontal" Spacing="10" VerticalAlignment="Center" Margin="14,0,0,0">
<Image Source="ms-appx:///Assets/Square44x44Logo.scale-200.png"
Width="20"
Height="20"
Stretch="Uniform" />
<TextBlock Text="AmiReel"
FontFamily="Bahnschrift"
FontSize="14"
FontWeight="SemiBold"
VerticalAlignment="Center" />
</StackPanel>
</Grid>
<!--
The Frame hosts pages for your application content. Add your UI to
MainPage.xaml rather than here so you can use Page features such as
navigation events and the Loaded lifecycle.
-->
<Frame x:Name="RootFrame" Grid.Row="1" />
</Grid>
</Window>
-116
View File
@@ -1,116 +0,0 @@
using Microsoft.UI;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System;
using System.IO;
using System.Runtime.InteropServices;
using Windows.Graphics;
using Windows.UI;
using WinRT.Interop;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace AmiReel_WinUI;
/// <summary>
/// The application window. This hosts a Frame that displays pages. Add your
/// UI and logic to MainPage.xaml / MainPage.xaml.cs instead of here so you
/// can use Page features such as navigation events and the Loaded lifecycle.
/// </summary>
public sealed partial class MainWindow : Window
{
private bool _exitConfirmed;
public MainWindow()
{
InitializeComponent();
App.MainWindowInstance = this;
ExtendsContentIntoTitleBar = true;
SetTitleBar(AppTitleBar);
string? executablePath = Environment.ProcessPath;
if (!string.IsNullOrWhiteSpace(executablePath) && File.Exists(executablePath))
AppWindow.SetIcon(executablePath);
SetDefaultSizeAndPosition();
// Navigate the root frame to the main page on startup.
RootFrame.Navigate(typeof(MainPage));
AppWindow.Closing += AppWindow_Closing;
}
private void SetDefaultSizeAndPosition()
{
const int DefaultWidth = 1280;
const int DefaultHeight = 860;
const int MinWidth = 1100;
const int MinHeight = 780;
IntPtr hwnd = WindowNative.GetWindowHandle(this);
double scale = GetDpiForWindow(hwnd) / 96.0;
int width = (int)(DefaultWidth * scale);
int height = (int)(DefaultHeight * scale);
AppWindow.Resize(new SizeInt32(width, height));
if (AppWindow.Presenter is OverlappedPresenter presenter)
{
presenter.PreferredMinimumWidth = (int)(MinWidth * scale);
presenter.PreferredMinimumHeight = (int)(MinHeight * scale);
}
DisplayArea? displayArea = DisplayArea.GetFromWindowId(Win32Interop.GetWindowIdFromWindow(hwnd), DisplayAreaFallback.Primary);
if (displayArea is not null)
{
int centerX = displayArea.WorkArea.X + (displayArea.WorkArea.Width - width) / 2;
int centerY = displayArea.WorkArea.Y + (displayArea.WorkArea.Height - height) / 2;
AppWindow.Move(new PointInt32(centerX, centerY));
}
}
[DllImport("user32.dll")]
private static extern int GetDpiForWindow(IntPtr hwnd);
public void ApplyTheme(bool isDark)
{
if (Content is FrameworkElement root)
root.RequestedTheme = isDark ? ElementTheme.Dark : ElementTheme.Light;
AppWindowTitleBar titleBar = AppWindow.TitleBar;
Color foreground = isDark ? Color.FromArgb(255, 0xF5, 0xF7, 0xFB) : Color.FromArgb(255, 0x12, 0x20, 0x33);
Color hoverBackground = isDark ? Color.FromArgb(255, 0x2E, 0x44, 0x6C) : Color.FromArgb(255, 0xD7, 0xE4, 0xF4);
titleBar.ButtonBackgroundColor = Colors.Transparent;
titleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
titleBar.ButtonForegroundColor = foreground;
titleBar.ButtonInactiveForegroundColor = foreground;
titleBar.ButtonHoverBackgroundColor = hoverBackground;
titleBar.ButtonHoverForegroundColor = foreground;
titleBar.ButtonPressedBackgroundColor = hoverBackground;
titleBar.ButtonPressedForegroundColor = foreground;
}
private async void AppWindow_Closing(AppWindow sender, AppWindowClosingEventArgs args)
{
if (_exitConfirmed) return;
args.Cancel = true;
FrameworkElement root = (FrameworkElement)Content;
ContentDialog dialog = DialogHelper.CreateStyled(RootFrame.XamlRoot, root.RequestedTheme);
dialog.Title = "Exit AmiReel?";
dialog.Content = "Are you sure you want to close the application?";
dialog.PrimaryButtonText = "Exit";
dialog.CloseButtonText = "Cancel";
dialog.DefaultButton = ContentDialogButton.Primary;
ContentDialogResult result = await dialog.ShowAsync();
if (result != ContentDialogResult.Primary) return;
_exitConfirmed = true;
Close();
}
}
@@ -3,9 +3,9 @@
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net10.0-windows10.0.26100.0</TargetFramework> <TargetFramework>net10.0-windows10.0.26100.0</TargetFramework>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion> <TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
<RootNamespace>AmiReel_WinUI</RootNamespace> <RootNamespace>AmiReel</RootNamespace>
<AssemblyName>AmiReel</AssemblyName> <AssemblyName>AmiReel</AssemblyName>
<ApplicationIcon>..\assets\AmiReel.ico</ApplicationIcon> <ApplicationIcon>branding\AmiReel.ico</ApplicationIcon>
<ApplicationManifest>app.manifest</ApplicationManifest> <ApplicationManifest>app.manifest</ApplicationManifest>
<Platforms>x86;x64;ARM64</Platforms> <Platforms>x86;x64;ARM64</Platforms>
<RuntimeIdentifier Condition="'$(RuntimeIdentifier)' == ''">win-$([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture.ToString().ToLowerInvariant())</RuntimeIdentifier> <RuntimeIdentifier Condition="'$(RuntimeIdentifier)' == ''">win-$([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture.ToString().ToLowerInvariant())</RuntimeIdentifier>
@@ -36,13 +36,8 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\Models\*.cs" Link="Shared\Models\%(Filename)%(Extension)" /> <EmbeddedResource Include="ThirdParty\ffmpeg.exe" Condition="Exists('ThirdParty\ffmpeg.exe')" LogicalName="AmiReel.Tools.ffmpeg.exe" />
<Compile Include="..\Services\*.cs" Link="Shared\Services\%(Filename)%(Extension)" /> <EmbeddedResource Include="ThirdParty\ffprobe.exe" Condition="Exists('ThirdParty\ffprobe.exe')" LogicalName="AmiReel.Tools.ffprobe.exe" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="..\ThirdParty\ffmpeg.exe" Condition="Exists('..\ThirdParty\ffmpeg.exe')" LogicalName="AmigaDB.VideoRenderer.Tools.ffmpeg.exe" />
<EmbeddedResource Include="..\ThirdParty\ffprobe.exe" Condition="Exists('..\ThirdParty\ffprobe.exe')" LogicalName="AmigaDB.VideoRenderer.Tools.ffprobe.exe" />
</ItemGroup> </ItemGroup>
<!-- <!--
+3
View File
@@ -0,0 +1,3 @@
<Solution>
<Project Path="AmiReel.csproj" />
</Solution>
-42
View File
@@ -1,42 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<ApplicationIcon Condition="Exists('assets\\AmiReel.ico')">assets\AmiReel.ico</ApplicationIcon>
<AssemblyName>AmiReel</AssemblyName>
<RootNamespace>AmigaDB.VideoRenderer</RootNamespace>
<Product>AmiReel</Product>
<Title>AmiReel</Title>
<Version>0.1.0</Version>
<PublishSingleFile>true</PublishSingleFile>
<SelfContained>true</SelfContained>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
<EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
</PropertyGroup>
<ItemGroup>
<Compile Remove="AmiReel.WinUI\**\*.cs" />
<EmbeddedResource Remove="AmiReel.WinUI\**\*" />
<None Remove="AmiReel.WinUI\**\*" />
<Page Remove="AmiReel.WinUI\**\*.xaml" />
<ApplicationDefinition Remove="AmiReel.WinUI\App.xaml" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="ThirdParty\ffmpeg.exe" Condition="Exists('ThirdParty\ffmpeg.exe')" LogicalName="AmigaDB.VideoRenderer.Tools.ffmpeg.exe" />
<EmbeddedResource Include="ThirdParty\ffprobe.exe" Condition="Exists('ThirdParty\ffprobe.exe')" LogicalName="AmigaDB.VideoRenderer.Tools.ffprobe.exe" />
</ItemGroup>
<ItemGroup>
<Resource Include="assets\AmiReel.ico" Condition="Exists('assets\AmiReel.ico')" />
</ItemGroup>
<Target Name="RequireFfmpegForPublish" BeforeTargets="Publish">
<Error Condition="!Exists('ThirdParty\ffmpeg.exe')" Text="ThirdParty\ffmpeg.exe is required for a portable publish." />
<Error Condition="!Exists('ThirdParty\ffprobe.exe')" Text="ThirdParty\ffprobe.exe is required for a portable publish." />
</Target>
</Project>
-48
View File
@@ -1,48 +0,0 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AmigaDB.VideoRenderer", "AmigaDB.VideoRenderer.csproj", "{7DF2BA53-4031-45EA-B4EF-27B86111FBFA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AmiReel.WinUI", "AmiReel.WinUI\AmiReel.WinUI.csproj", "{7F1B3264-98F5-4B31-812F-7A4FB2A447BB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7DF2BA53-4031-45EA-B4EF-27B86111FBFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7DF2BA53-4031-45EA-B4EF-27B86111FBFA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7DF2BA53-4031-45EA-B4EF-27B86111FBFA}.Debug|x64.ActiveCfg = Debug|Any CPU
{7DF2BA53-4031-45EA-B4EF-27B86111FBFA}.Debug|x64.Build.0 = Debug|Any CPU
{7DF2BA53-4031-45EA-B4EF-27B86111FBFA}.Debug|x86.ActiveCfg = Debug|Any CPU
{7DF2BA53-4031-45EA-B4EF-27B86111FBFA}.Debug|x86.Build.0 = Debug|Any CPU
{7DF2BA53-4031-45EA-B4EF-27B86111FBFA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7DF2BA53-4031-45EA-B4EF-27B86111FBFA}.Release|Any CPU.Build.0 = Release|Any CPU
{7DF2BA53-4031-45EA-B4EF-27B86111FBFA}.Release|x64.ActiveCfg = Release|Any CPU
{7DF2BA53-4031-45EA-B4EF-27B86111FBFA}.Release|x64.Build.0 = Release|Any CPU
{7DF2BA53-4031-45EA-B4EF-27B86111FBFA}.Release|x86.ActiveCfg = Release|Any CPU
{7DF2BA53-4031-45EA-B4EF-27B86111FBFA}.Release|x86.Build.0 = Release|Any CPU
{7F1B3264-98F5-4B31-812F-7A4FB2A447BB}.Debug|Any CPU.ActiveCfg = Debug|x86
{7F1B3264-98F5-4B31-812F-7A4FB2A447BB}.Debug|Any CPU.Build.0 = Debug|x86
{7F1B3264-98F5-4B31-812F-7A4FB2A447BB}.Debug|x64.ActiveCfg = Debug|x64
{7F1B3264-98F5-4B31-812F-7A4FB2A447BB}.Debug|x64.Build.0 = Debug|x64
{7F1B3264-98F5-4B31-812F-7A4FB2A447BB}.Debug|x86.ActiveCfg = Debug|x86
{7F1B3264-98F5-4B31-812F-7A4FB2A447BB}.Debug|x86.Build.0 = Debug|x86
{7F1B3264-98F5-4B31-812F-7A4FB2A447BB}.Release|Any CPU.ActiveCfg = Release|x86
{7F1B3264-98F5-4B31-812F-7A4FB2A447BB}.Release|Any CPU.Build.0 = Release|x86
{7F1B3264-98F5-4B31-812F-7A4FB2A447BB}.Release|x64.ActiveCfg = Release|x64
{7F1B3264-98F5-4B31-812F-7A4FB2A447BB}.Release|x64.Build.0 = Release|x64
{7F1B3264-98F5-4B31-812F-7A4FB2A447BB}.Release|x86.ActiveCfg = Release|x86
{7F1B3264-98F5-4B31-812F-7A4FB2A447BB}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
+233 -387
View File
@@ -1,8 +1,16 @@
<Application x:Class="AmigaDB.VideoRenderer.App" <?xml version="1.0" encoding="utf-8"?>
<Application
x:Class="AmiReel.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml"> xmlns:local="using:AmiReel">
<Application.Resources> <Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
</ResourceDictionary.MergedDictionaries>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="PageBrush" Color="#0E1525"/> <SolidColorBrush x:Key="PageBrush" Color="#0E1525"/>
<SolidColorBrush x:Key="PanelBrush" Color="#162033"/> <SolidColorBrush x:Key="PanelBrush" Color="#162033"/>
<SolidColorBrush x:Key="PanelAltBrush" Color="#1A2740"/> <SolidColorBrush x:Key="PanelAltBrush" Color="#1A2740"/>
@@ -14,410 +22,248 @@
<SolidColorBrush x:Key="MutedBrush" Color="#9FB1CC"/> <SolidColorBrush x:Key="MutedBrush" Color="#9FB1CC"/>
<SolidColorBrush x:Key="ButtonBrush" Color="#243554"/> <SolidColorBrush x:Key="ButtonBrush" Color="#243554"/>
<SolidColorBrush x:Key="ButtonHoverBrush" Color="#2E446C"/> <SolidColorBrush x:Key="ButtonHoverBrush" Color="#2E446C"/>
<SolidColorBrush x:Key="ButtonDisabledBrush" Color="#2A3140"/>
<SolidColorBrush x:Key="ButtonDisabledTextBrush" Color="#7E8BA1"/>
<SolidColorBrush x:Key="PrimaryTextBrush" Color="#07111D"/> <SolidColorBrush x:Key="PrimaryTextBrush" Color="#07111D"/>
<SolidColorBrush x:Key="SelectionBrush" Color="#295C87"/>
<SolidColorBrush x:Key="SelectionTextBrush" Color="#FFFFFF"/>
<SolidColorBrush x:Key="ScrollTrackBrush" Color="#11192B"/>
<SolidColorBrush x:Key="ScrollThumbBrush" Color="#31476D"/>
<SolidColorBrush x:Key="ScrollThumbHoverBrush" Color="#42608F"/>
<SolidColorBrush x:Key="PopupBrush" Color="#10192B"/>
<SolidColorBrush x:Key="HeroBorderBrush" Color="#35507D"/>
<LinearGradientBrush x:Key="HeroBrush" StartPoint="0,0" EndPoint="1,1">
<GradientStop Offset="0" Color="#122441"/>
<GradientStop Offset="0.55" Color="#0F1830"/>
<GradientStop Offset="1" Color="#1A2D4D"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="PageOverlayBrush" StartPoint="0,0" EndPoint="1,1">
<GradientStop Offset="0" Color="#12000000"/>
<GradientStop Offset="1" Color="#00000000"/>
</LinearGradientBrush>
<Style TargetType="Window"> <!-- Fluent control theme-brush overrides so built-in controls match the palette -->
<Setter Property="Background" Value="{DynamicResource PageBrush}"/> <SolidColorBrush x:Key="TextControlBackground" Color="#1D2940"/>
<Setter Property="Foreground" Value="{DynamicResource TextBrush}"/> <SolidColorBrush x:Key="TextControlBackgroundPointerOver" Color="#233252"/>
<SolidColorBrush x:Key="TextControlBackgroundFocused" Color="#1D2940"/>
<SolidColorBrush x:Key="TextControlBorderBrush" Color="#2B3A58"/>
<SolidColorBrush x:Key="TextControlBorderBrushPointerOver" Color="#4AB8FF"/>
<SolidColorBrush x:Key="TextControlBorderBrushFocused" Color="#4AB8FF"/>
<SolidColorBrush x:Key="TextControlForeground" Color="#F5F7FB"/>
<SolidColorBrush x:Key="TextControlForegroundFocused" Color="#F5F7FB"/>
<SolidColorBrush x:Key="TextControlHeaderForeground" Color="#9FB1CC"/>
<SolidColorBrush x:Key="TextControlPlaceholderForeground" Color="#7488A6"/>
<SolidColorBrush x:Key="ComboBoxBackground" Color="#1D2940"/>
<SolidColorBrush x:Key="ComboBoxBackgroundPointerOver" Color="#233252"/>
<SolidColorBrush x:Key="ComboBoxBorderBrush" Color="#2B3A58"/>
<SolidColorBrush x:Key="ComboBoxForeground" Color="#F5F7FB"/>
<SolidColorBrush x:Key="ComboBoxHeaderForeground" Color="#9FB1CC"/>
<SolidColorBrush x:Key="ComboBoxItemBackgroundSelected" Color="#13324F"/>
<SolidColorBrush x:Key="ComboBoxDropDownBackground" Color="#10192B"/>
<SolidColorBrush x:Key="ButtonBackground" Color="#243554"/>
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="#2E446C"/>
<SolidColorBrush x:Key="ButtonBackgroundPressed" Color="#1B2A44"/>
<SolidColorBrush x:Key="ButtonForeground" Color="#F5F7FB"/>
<SolidColorBrush x:Key="ButtonForegroundPointerOver" Color="#F5F7FB"/>
<SolidColorBrush x:Key="ButtonBorderBrush" Color="#2B3A58"/>
<SolidColorBrush x:Key="ContentDialogBackground" Color="#162033"/>
<SolidColorBrush x:Key="ContentDialogForeground" Color="#F5F7FB"/>
<SolidColorBrush x:Key="ContentDialogBorderBrush" Color="#35507D"/>
<SolidColorBrush x:Key="ListViewItemBackgroundSelected" Color="#13324F"/>
<SolidColorBrush x:Key="ListViewItemBackgroundPointerOver" Color="#1A2740"/>
<SolidColorBrush x:Key="ListViewItemForeground" Color="#F5F7FB"/>
<SolidColorBrush x:Key="CheckBoxCheckBackgroundFillUnchecked" Color="#1D2940"/>
<SolidColorBrush x:Key="CheckBoxCheckBackgroundStrokeUnchecked" Color="#2B3A58"/>
<SolidColorBrush x:Key="CheckBoxCheckBackgroundFillChecked" Color="#4AB8FF"/>
<SolidColorBrush x:Key="CheckBoxCheckBackgroundStrokeChecked" Color="#4AB8FF"/>
<SolidColorBrush x:Key="CheckBoxCheckGlyphForegroundChecked" Color="#07111D"/>
<SolidColorBrush x:Key="CheckBoxForegroundUnchecked" Color="#F5F7FB"/>
<SolidColorBrush x:Key="CheckBoxForegroundChecked" Color="#F5F7FB"/>
<SolidColorBrush x:Key="ProgressBarForeground" Color="#4AB8FF"/>
<SolidColorBrush x:Key="ProgressBarBackground" Color="#1D2940"/>
<SolidColorBrush x:Key="HeroBorderBrush" Color="#35507D"/>
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="PageBrush" Color="#F4F7FB"/>
<SolidColorBrush x:Key="PanelBrush" Color="#FFFFFF"/>
<SolidColorBrush x:Key="PanelAltBrush" Color="#EEF4FB"/>
<SolidColorBrush x:Key="FieldBrush" Color="#F7FAFD"/>
<SolidColorBrush x:Key="BorderBrush" Color="#C7D3E3"/>
<SolidColorBrush x:Key="AccentBrush" Color="#0E9AEF"/>
<SolidColorBrush x:Key="AccentSoftBrush" Color="#D6EEFF"/>
<SolidColorBrush x:Key="TextBrush" Color="#122033"/>
<SolidColorBrush x:Key="MutedBrush" Color="#5F7390"/>
<SolidColorBrush x:Key="ButtonBrush" Color="#E5EDF7"/>
<SolidColorBrush x:Key="ButtonHoverBrush" Color="#D7E4F4"/>
<SolidColorBrush x:Key="PrimaryTextBrush" Color="#FFFFFF"/>
<SolidColorBrush x:Key="TextControlBackground" Color="#F7FAFD"/>
<SolidColorBrush x:Key="TextControlBackgroundPointerOver" Color="#EEF4FB"/>
<SolidColorBrush x:Key="TextControlBackgroundFocused" Color="#FFFFFF"/>
<SolidColorBrush x:Key="TextControlBorderBrush" Color="#C7D3E3"/>
<SolidColorBrush x:Key="TextControlBorderBrushPointerOver" Color="#0E9AEF"/>
<SolidColorBrush x:Key="TextControlBorderBrushFocused" Color="#0E9AEF"/>
<SolidColorBrush x:Key="TextControlForeground" Color="#122033"/>
<SolidColorBrush x:Key="TextControlForegroundFocused" Color="#122033"/>
<SolidColorBrush x:Key="TextControlHeaderForeground" Color="#5F7390"/>
<SolidColorBrush x:Key="TextControlPlaceholderForeground" Color="#8194AC"/>
<SolidColorBrush x:Key="ComboBoxBackground" Color="#F7FAFD"/>
<SolidColorBrush x:Key="ComboBoxBackgroundPointerOver" Color="#EEF4FB"/>
<SolidColorBrush x:Key="ComboBoxBorderBrush" Color="#C7D3E3"/>
<SolidColorBrush x:Key="ComboBoxForeground" Color="#122033"/>
<SolidColorBrush x:Key="ComboBoxHeaderForeground" Color="#5F7390"/>
<SolidColorBrush x:Key="ComboBoxItemBackgroundSelected" Color="#D6EEFF"/>
<SolidColorBrush x:Key="ComboBoxDropDownBackground" Color="#FFFFFF"/>
<SolidColorBrush x:Key="ButtonBackground" Color="#E5EDF7"/>
<SolidColorBrush x:Key="ButtonBackgroundPointerOver" Color="#D7E4F4"/>
<SolidColorBrush x:Key="ButtonBackgroundPressed" Color="#C7D9EE"/>
<SolidColorBrush x:Key="ButtonForeground" Color="#122033"/>
<SolidColorBrush x:Key="ButtonForegroundPointerOver" Color="#122033"/>
<SolidColorBrush x:Key="ButtonBorderBrush" Color="#C7D3E3"/>
<SolidColorBrush x:Key="ContentDialogBackground" Color="#FFFFFF"/>
<SolidColorBrush x:Key="ContentDialogForeground" Color="#122033"/>
<SolidColorBrush x:Key="ContentDialogBorderBrush" Color="#C7D3E3"/>
<SolidColorBrush x:Key="ListViewItemBackgroundSelected" Color="#D6EEFF"/>
<SolidColorBrush x:Key="ListViewItemBackgroundPointerOver" Color="#EEF4FB"/>
<SolidColorBrush x:Key="ListViewItemForeground" Color="#122033"/>
<SolidColorBrush x:Key="CheckBoxCheckBackgroundFillUnchecked" Color="#F7FAFD"/>
<SolidColorBrush x:Key="CheckBoxCheckBackgroundStrokeUnchecked" Color="#C7D3E3"/>
<SolidColorBrush x:Key="CheckBoxCheckBackgroundFillChecked" Color="#0E9AEF"/>
<SolidColorBrush x:Key="CheckBoxCheckBackgroundStrokeChecked" Color="#0E9AEF"/>
<SolidColorBrush x:Key="CheckBoxCheckGlyphForegroundChecked" Color="#FFFFFF"/>
<SolidColorBrush x:Key="CheckBoxForegroundUnchecked" Color="#122033"/>
<SolidColorBrush x:Key="CheckBoxForegroundChecked" Color="#122033"/>
<SolidColorBrush x:Key="ProgressBarForeground" Color="#0E9AEF"/>
<SolidColorBrush x:Key="ProgressBarBackground" Color="#F7FAFD"/>
<SolidColorBrush x:Key="HeroBorderBrush" Color="#A9C4E4"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<!-- Global corner rounding applied to built-in Fluent controls -->
<CornerRadius x:Key="ControlCornerRadius">10</CornerRadius>
<CornerRadius x:Key="OverlayCornerRadius">18</CornerRadius>
<x:Double x:Key="ContentDialogMaxWidth">460</x:Double>
<Style TargetType="Page">
<Setter Property="Background" Value="{ThemeResource PageBrush}" />
<Setter Property="FontFamily" Value="Bahnschrift" /> <Setter Property="FontFamily" Value="Bahnschrift" />
</Style> </Style>
<Style TargetType="ScrollViewer">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ScrollViewer">
<Grid Background="{TemplateBinding Background}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ScrollContentPresenter Grid.Row="0" Grid.Column="0" Margin="{TemplateBinding Padding}"/> <Style x:Key="CardBorderStyle" TargetType="Border">
<Setter Property="Background" Value="{ThemeResource PanelBrush}" />
<ScrollBar x:Name="PART_VerticalScrollBar" <Setter Property="BorderBrush" Value="{ThemeResource BorderBrush}" />
Grid.Row="0"
Grid.Column="1"
Orientation="Vertical"
Value="{TemplateBinding VerticalOffset}"
Maximum="{TemplateBinding ScrollableHeight}"
ViewportSize="{TemplateBinding ViewportHeight}"
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>
<ScrollBar x:Name="PART_HorizontalScrollBar"
Grid.Row="1"
Grid.Column="0"
Orientation="Horizontal"
Value="{TemplateBinding HorizontalOffset}"
Maximum="{TemplateBinding ScrollableWidth}"
ViewportSize="{TemplateBinding ViewportWidth}"
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
<Border x:Name="CornerBox"
Grid.Row="1"
Grid.Column="1"
Width="14"
Height="14"
Background="{DynamicResource ScrollTrackBrush}"
Visibility="Collapsed"/>
</Grid>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="ComputedVerticalScrollBarVisibility" Value="Visible"/>
<Condition Property="ComputedHorizontalScrollBarVisibility" Value="Visible"/>
</MultiTrigger.Conditions>
<Setter TargetName="CornerBox" Property="Visibility" Value="Visible"/>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ScrollBarThumbStyle" TargetType="Thumb">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Thumb">
<Border Background="{DynamicResource ScrollThumbBrush}" CornerRadius="6" Margin="2"/>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Opacity" Value="1"/>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="RepeatButton" x:Key="ScrollBarButtonStyle">
<Setter Property="Focusable" Value="False"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RepeatButton">
<Border Background="{TemplateBinding Background}" CornerRadius="6"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="ScrollBar">
<Setter Property="Width" Value="14"/>
<Setter Property="Background" Value="{DynamicResource ScrollTrackBrush}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ScrollBar">
<Grid Background="Transparent" Width="{TemplateBinding Width}">
<Border Background="{DynamicResource ScrollTrackBrush}" CornerRadius="7" Margin="2"/>
<Track x:Name="PART_Track" IsDirectionReversed="True" Margin="1">
<Track.DecreaseRepeatButton>
<RepeatButton Style="{StaticResource ScrollBarButtonStyle}" Command="ScrollBar.PageUpCommand"/>
</Track.DecreaseRepeatButton>
<Track.Thumb>
<Thumb Style="{StaticResource ScrollBarThumbStyle}"/>
</Track.Thumb>
<Track.IncreaseRepeatButton>
<RepeatButton Style="{StaticResource ScrollBarButtonStyle}" Command="ScrollBar.PageDownCommand"/>
</Track.IncreaseRepeatButton>
</Track>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="Orientation" Value="Horizontal">
<Setter Property="Height" Value="14"/>
<Setter TargetName="PART_Track" Property="IsDirectionReversed" Value="False"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="TextBox">
<Setter Property="Background" Value="{DynamicResource FieldBrush}"/>
<Setter Property="Foreground" Value="{DynamicResource TextBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource BorderBrush}"/>
<Setter Property="CaretBrush" Value="{DynamicResource TextBrush}"/>
<Setter Property="SelectionBrush" Value="{DynamicResource SelectionBrush}"/>
<Setter Property="SelectionTextBrush" Value="{DynamicResource SelectionTextBrush}"/>
<Setter Property="BorderThickness" Value="1" /> <Setter Property="BorderThickness" Value="1" />
<Setter Property="Padding" Value="9,7"/> <Setter Property="CornerRadius" Value="16" />
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="SnapsToDevicePixels" Value="True"/>
</Style> </Style>
<Style TargetType="ComboBox">
<Setter Property="Background" Value="{DynamicResource FieldBrush}"/> <Style x:Key="SectionTitleStyle" TargetType="TextBlock">
<Setter Property="Foreground" Value="{DynamicResource TextBrush}"/> <Setter Property="Foreground" Value="{ThemeResource TextBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource BorderBrush}"/> <Setter Property="FontSize" Value="15" />
<Setter Property="BorderThickness" Value="1"/> <Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Padding" Value="8,0"/>
<Setter Property="Height" Value="42"/>
<Setter Property="MinHeight" Value="42"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBox">
<Grid>
<Border x:Name="OuterBorder"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="12"
Height="{TemplateBinding Height}"
MinHeight="{TemplateBinding MinHeight}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="42"/>
</Grid.ColumnDefinitions>
<ContentPresenter Margin="12,0,8,0"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Content="{TemplateBinding SelectionBoxItem}"
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"/>
<Border Grid.Column="1" Background="{DynamicResource AccentSoftBrush}" CornerRadius="0,12,12,0" BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1,0,0,0">
<Path HorizontalAlignment="Center"
VerticalAlignment="Center"
Fill="{DynamicResource AccentBrush}"
Data="M 0 0 L 4 4 L 8 0 Z"/>
</Border>
</Grid>
</Border>
<Popup x:Name="PART_Popup"
Placement="Bottom"
AllowsTransparency="True"
Focusable="False"
IsOpen="{TemplateBinding IsDropDownOpen}"
PopupAnimation="Fade">
<Border Margin="0,8,0,0"
Background="{DynamicResource PopupBrush}"
BorderBrush="{DynamicResource BorderBrush}"
BorderThickness="1"
CornerRadius="14"
MinWidth="{TemplateBinding ActualWidth}">
<ScrollViewer Margin="6" SnapsToDevicePixels="True" MaxHeight="280">
<ItemsPresenter KeyboardNavigation.DirectionalNavigation="Contained"/>
</ScrollViewer>
</Border>
</Popup>
<ToggleButton x:Name="HitTarget" Opacity="0" Focusable="False" IsChecked="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="OuterBorder" Property="BorderBrush" Value="{DynamicResource AccentBrush}"/>
</Trigger>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter TargetName="OuterBorder" Property="BorderBrush" Value="{DynamicResource AccentBrush}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.65"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style> </Style>
<Style TargetType="ComboBoxItem">
<Setter Property="Background" Value="{DynamicResource FieldBrush}"/> <Style x:Key="MutedTextStyle" TargetType="TextBlock">
<Setter Property="Foreground" Value="{DynamicResource TextBrush}"/> <Setter Property="Foreground" Value="{ThemeResource MutedBrush}" />
<Setter Property="HorizontalContentAlignment" Value="Stretch"/> <Setter Property="FontSize" Value="13" />
<Setter Property="Padding" Value="10,8"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBoxItem">
<Border x:Name="ItemBorder" Background="{TemplateBinding Background}" CornerRadius="10" Padding="{TemplateBinding Padding}">
<ContentPresenter/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="True">
<Setter TargetName="ItemBorder" Property="Background" Value="{DynamicResource AccentSoftBrush}"/>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="ItemBorder" Property="Background" Value="{DynamicResource AccentBrush}"/>
<Setter Property="Foreground" Value="{DynamicResource PrimaryTextBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsHighlighted" Value="True">
<Setter Property="Background" Value="{DynamicResource AccentSoftBrush}"/>
<Setter Property="Foreground" Value="{DynamicResource TextBrush}"/>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{DynamicResource AccentBrush}"/>
<Setter Property="Foreground" Value="{DynamicResource PrimaryTextBrush}"/>
</Trigger>
</Style.Triggers>
</Style> </Style>
<Style TargetType="ListBox">
<Setter Property="Background" Value="{DynamicResource FieldBrush}"/> <Style x:Key="FieldLabelStyle" TargetType="TextBlock">
<Setter Property="Foreground" Value="{DynamicResource TextBrush}"/> <Setter Property="Foreground" Value="{ThemeResource MutedBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource BorderBrush}"/> <Setter Property="FontSize" Value="12" />
<Setter Property="BorderThickness" Value="1"/> <Setter Property="Margin" Value="0,0,0,4" />
</Style> </Style>
<Style TargetType="ListBoxItem">
<Setter Property="Padding" Value="8,6"/> <Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{DynamicResource TextBrush}"/> <Setter Property="Foreground" Value="{ThemeResource TextBrush}" />
<Setter Property="Background" Value="Transparent"/> <Setter Property="FontSize" Value="13" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{DynamicResource AccentSoftBrush}"/>
<Setter Property="Foreground" Value="{DynamicResource TextBrush}"/>
</Trigger>
</Style.Triggers>
</Style> </Style>
<Style TargetType="Button"> <Style TargetType="Button">
<Setter Property="Background" Value="{DynamicResource ButtonBrush}"/> <Setter Property="Padding" Value="16,9" />
<Setter Property="Foreground" Value="{DynamicResource TextBrush}"/> <Setter Property="FontSize" Value="13" />
<Setter Property="BorderBrush" Value="{DynamicResource BorderBrush}"/> <Setter Property="CornerRadius" Value="10" />
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="14,8"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="10">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"
Margin="{TemplateBinding Padding}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{DynamicResource ButtonHoverBrush}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Background" Value="{DynamicResource ButtonDisabledBrush}"/>
<Setter Property="Foreground" Value="{DynamicResource ButtonDisabledTextBrush}"/>
</Trigger>
</Style.Triggers>
</Style> </Style>
<Style x:Key="PrimaryButton" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Background" Value="{DynamicResource AccentBrush}"/> <Style x:Key="PrimaryButtonStyle" TargetType="Button">
<Setter Property="Foreground" Value="{DynamicResource PrimaryTextBrush}"/> <Setter Property="Background" Value="{ThemeResource AccentBrush}" />
<Setter Property="Foreground" Value="{ThemeResource PrimaryTextBrush}" />
<Setter Property="BorderBrush" Value="{ThemeResource AccentBrush}" />
<Setter Property="FontWeight" Value="SemiBold" /> <Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Padding" Value="22,11"/> <Setter Property="Padding" Value="22,10" />
<Setter Property="CornerRadius" Value="10" />
</Style> </Style>
<Style x:Key="IconButton" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Width" Value="46"/> <Style x:Key="IconButtonStyle" TargetType="Button">
<Setter Property="Height" Value="46"/> <Setter Property="Width" Value="40" />
<Setter Property="Height" Value="40" />
<Setter Property="Padding" Value="0" /> <Setter Property="Padding" Value="0" />
<Setter Property="FontSize" Value="20"/> <Setter Property="FontSize" Value="16" />
<Setter Property="FontWeight" Value="SemiBold"/> <Setter Property="CornerRadius" Value="10" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
</Style> </Style>
<Style x:Key="SecondaryPanel" TargetType="Border">
<Setter Property="Background" Value="{DynamicResource PanelBrush}"/> <!-- Explicit styles bound to ContentDialog.PrimaryButtonStyle / CloseButtonStyle so its
<Setter Property="BorderBrush" Value="{DynamicResource BorderBrush}"/> built-in buttons match the app's own Button look instead of the default Fluent
accent-pill / plain styles. -->
<Style x:Key="DialogPrimaryButtonStyle" TargetType="Button">
<Setter Property="Background" Value="{ThemeResource AccentBrush}" />
<Setter Property="Foreground" Value="{ThemeResource PrimaryTextBrush}" />
<Setter Property="BorderBrush" Value="{ThemeResource AccentBrush}" />
<Setter Property="BorderThickness" Value="1" /> <Setter Property="BorderThickness" Value="1" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="FontSize" Value="13" />
<Setter Property="Padding" Value="22,10" />
<Setter Property="CornerRadius" Value="10" />
<Setter Property="MinHeight" Value="40" />
<Setter Property="MinWidth" Value="110" />
</Style>
<Style x:Key="DialogCloseButtonStyle" TargetType="Button">
<Setter Property="Background" Value="{ThemeResource ButtonBrush}" />
<Setter Property="Foreground" Value="{ThemeResource TextBrush}" />
<Setter Property="BorderBrush" Value="{ThemeResource BorderBrush}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="FontSize" Value="13" />
<Setter Property="Padding" Value="16,9" />
<Setter Property="CornerRadius" Value="10" />
<Setter Property="MinHeight" Value="40" />
<Setter Property="MinWidth" Value="110" />
</Style>
<Style TargetType="TextBox">
<Setter Property="FontSize" Value="13" />
<Setter Property="MinHeight" Value="38" />
</Style>
<Style TargetType="ComboBox">
<Setter Property="FontSize" Value="13" />
<Setter Property="MinHeight" Value="38" />
</Style>
<Style TargetType="CheckBox">
<Setter Property="FontSize" Value="13" />
</Style>
<Style TargetType="ProgressBar">
<Setter Property="MinHeight" Value="8" />
</Style>
<Style TargetType="ListView">
<Setter Property="Background" Value="{ThemeResource FieldBrush}" />
<Setter Property="BorderBrush" Value="{ThemeResource BorderBrush}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="10" />
<Setter Property="FontSize" Value="13" />
<Setter Property="Padding" Value="4" />
</Style>
<Style TargetType="ListViewItem">
<Setter Property="MinHeight" Value="32" />
<Setter Property="Padding" Value="8,6" />
<Setter Property="CornerRadius" Value="6" />
</Style>
<Style TargetType="ContentDialog">
<Setter Property="CornerRadius" Value="18" /> <Setter Property="CornerRadius" Value="18" />
</Style> </Style>
<Style TargetType="GroupBox"> </ResourceDictionary>
<Setter Property="Foreground" Value="{DynamicResource TextBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource BorderBrush}"/>
<Setter Property="Background" Value="{DynamicResource PanelBrush}"/>
<Setter Property="Padding" Value="12"/>
<Setter Property="Margin" Value="0,0,0,16"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GroupBox">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="1"
CornerRadius="18"
Padding="16">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Text="{TemplateBinding Header}"
FontSize="16"
FontWeight="SemiBold"
Foreground="{DynamicResource TextBrush}"
Margin="0,0,0,12"/>
<ContentPresenter Grid.Row="1"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="ProgressBar">
<Setter Property="Background" Value="{DynamicResource FieldBrush}"/>
<Setter Property="Foreground" Value="{DynamicResource AccentBrush}"/>
<Setter Property="Height" Value="12"/>
<Setter Property="BorderBrush" Value="{DynamicResource BorderBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
</Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{DynamicResource TextBrush}"/>
</Style>
<Style x:Key="TitleBarButton" TargetType="Button">
<Setter Property="Width" Value="46"/>
<Setter Property="Height" Value="40"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="{DynamicResource TextBrush}"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="FontFamily" Value="Segoe Fluent Icons, Segoe MDL2 Assets"/>
<Setter Property="FontSize" Value="11"/>
<Setter Property="Cursor" Value="Arrow"/>
<Setter Property="Focusable" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="Bg" Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Bg" Property="Background" Value="{DynamicResource ButtonHoverBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="TitleBarCloseButton" TargetType="Button" BasedOn="{StaticResource TitleBarButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="Bg" Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Bg" Property="Background" Value="#E81123"/>
<Setter Property="Foreground" Value="White"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Application.Resources> </Application.Resources>
</Application> </Application>
+45 -2
View File
@@ -1,5 +1,48 @@
namespace AmigaDB.VideoRenderer; using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using Microsoft.UI.Xaml.Shapes;
using WinRT.Interop;
public partial class App : System.Windows.Application // To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace AmiReel;
/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </summary>
public partial class App : Application
{ {
private Window? _window;
public static IntPtr MainWindowHandle { get; private set; }
public static MainWindow? MainWindowInstance { get; set; }
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
InitializeComponent();
}
/// <summary>
/// Invoked when the application is launched.
/// </summary>
/// <param name="args">Details about the launch request and process.</param>
protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
{
_window = new MainWindow();
MainWindowHandle = WindowNative.GetWindowHandle(_window);
_window.Activate();
}
} }

Before

Width:  |  Height:  |  Size: 200 KiB

After

Width:  |  Height:  |  Size: 200 KiB

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Before

Width:  |  Height:  |  Size: 130 KiB

After

Width:  |  Height:  |  Size: 130 KiB

Before

Width:  |  Height:  |  Size: 144 KiB

After

Width:  |  Height:  |  Size: 144 KiB

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 109 KiB

@@ -2,7 +2,7 @@ using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media; using Microsoft.UI.Xaml.Media;
namespace AmiReel_WinUI; namespace AmiReel;
/// <summary> /// <summary>
/// Builds ContentDialogs styled to match the app's own palette and button shapes, /// Builds ContentDialogs styled to match the app's own palette and button shapes,
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<Page <Page
x:Class="AmiReel_WinUI.MainPage" x:Class="AmiReel.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
@@ -2,8 +2,8 @@ using System.Collections.ObjectModel;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.Text; using System.Text;
using AmigaDB.VideoRenderer.Models; using AmiReel.Models;
using AmigaDB.VideoRenderer.Services; using AmiReel.Services;
using Microsoft.UI.Xaml; using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls;
using Microsoft.Win32; using Microsoft.Win32;
@@ -12,7 +12,7 @@ using Windows.Storage;
using Windows.Storage.Pickers; using Windows.Storage.Pickers;
using WinRT.Interop; using WinRT.Interop;
namespace AmiReel_WinUI; namespace AmiReel;
public sealed partial class MainPage : Page public sealed partial class MainPage : Page
{ {
+26 -267
View File
@@ -1,279 +1,38 @@
<Window x:Class="AmigaDB.VideoRenderer.MainWindow" <?xml version="1.0" encoding="utf-8" ?>
<Window
x:Class="AmiReel.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="AmiReel" Height="860" Width="1280" MinHeight="780" MinWidth="1100" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
WindowStartupLocation="CenterScreen" xmlns:local="using:AmiReel"
WindowStyle="None" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
ResizeMode="CanResize" Title="AmiReel"
Icon="assets/AmiReel.ico"> mc:Ignorable="d">
<WindowChrome.WindowChrome> <Grid Background="{ThemeResource PageBrush}">
<WindowChrome CaptionHeight="40"
ResizeBorderThickness="6"
GlassFrameThickness="0"
UseAeroCaptionButtons="False"/>
</WindowChrome.WindowChrome>
<Grid Background="{DynamicResource PageBrush}">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid Grid.Row="0" Background="{DynamicResource PanelBrush}" Height="40"> <Grid x:Name="AppTitleBar" Grid.Row="0" Height="44" Background="{ThemeResource PanelBrush}">
<Grid.ColumnDefinitions> <StackPanel Orientation="Horizontal" Spacing="10" VerticalAlignment="Center" Margin="14,0,0,0">
<ColumnDefinition Width="Auto"/> <Image Source="ms-appx:///Assets/Square44x44Logo.scale-200.png"
<ColumnDefinition Width="*"/> Width="20"
<ColumnDefinition Width="Auto"/> Height="20"
<ColumnDefinition Width="Auto"/> Stretch="Uniform" />
<ColumnDefinition Width="Auto"/> <TextBlock Text="AmiReel"
</Grid.ColumnDefinitions> FontFamily="Bahnschrift"
FontSize="14"
<Image Source="assets/AmiReel.ico" Width="18" Height="18" Margin="14,0,8,0" VerticalAlignment="Center" FontWeight="SemiBold"
WindowChrome.IsHitTestVisibleInChrome="False"/> VerticalAlignment="Center" />
<TextBlock Grid.Column="1" Text="AmiReel" VerticalAlignment="Center" FontWeight="SemiBold"
Foreground="{DynamicResource TextBrush}" WindowChrome.IsHitTestVisibleInChrome="False"/>
<Button x:Name="MinimizeButton" Grid.Column="2" Content="&#xE921;" Style="{StaticResource TitleBarButton}"
Click="Minimize_Click" ToolTip="Minimize" WindowChrome.IsHitTestVisibleInChrome="True"/>
<Button x:Name="MaximizeButton" Grid.Column="3" Content="&#xE922;" Style="{StaticResource TitleBarButton}"
Click="MaximizeRestore_Click" ToolTip="Maximize" WindowChrome.IsHitTestVisibleInChrome="True"/>
<Button x:Name="CloseWindowButton" Grid.Column="4" Content="&#xE8BB;" Style="{StaticResource TitleBarCloseButton}"
Click="CloseWindowButton_Click" ToolTip="Close" WindowChrome.IsHitTestVisibleInChrome="True"/>
</Grid>
<Grid Grid.Row="1">
<Grid>
<Grid.Background>
<DrawingBrush Stretch="None" Viewport="0,0,36,36" ViewportUnits="Absolute" Opacity="0.08">
<DrawingBrush.Drawing>
<GeometryDrawing Brush="{DynamicResource PageOverlayBrush}">
<GeometryDrawing.Geometry>
<GeometryGroup>
<EllipseGeometry Center="10,10" RadiusX="1.2" RadiusY="1.2"/>
<EllipseGeometry Center="28,18" RadiusX="1.2" RadiusY="1.2"/>
</GeometryGroup>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</Grid.Background>
</Grid>
<Grid Margin="18">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="14"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<GroupBox Header="Source recordings">
<Grid>
<Grid.RowDefinitions><RowDefinition Height="Auto"/><RowDefinition Height="98"/><RowDefinition Height="Auto"/></Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Margin="0,0,0,8">
<Button Content="Add video files…" Click="AddInputs_Click" Margin="0,0,8,0"/>
<Button Content="Clear" Click="ClearInputs_Click"/>
</StackPanel>
<ListBox x:Name="InputList" Grid.Row="1" SelectionChanged="InputList_SelectionChanged"
AllowDrop="True" DragEnter="InputList_DragEnter" DragOver="InputList_DragEnter" Drop="InputList_Drop"
ToolTip="Drag and drop video files here to add them"/>
<Button x:Name="PreviewSourceButton" Grid.Row="2" Content="Preview selected" Click="PreviewSource_Click"
HorizontalAlignment="Left" Margin="0,10,0,0" IsEnabled="False"/>
</Grid>
</GroupBox>
<GroupBox Grid.Column="2" Header="End card and output">
<Grid>
<Grid.ColumnDefinitions><ColumnDefinition Width="*"/><ColumnDefinition Width="Auto"/></Grid.ColumnDefinitions>
<Grid.RowDefinitions><RowDefinition Height="Auto"/><RowDefinition Height="55"/><RowDefinition Height="55"/><RowDefinition Height="55"/></Grid.RowDefinitions>
<TextBlock Text="Leave blank to use a built-in black end card." Foreground="{DynamicResource MutedBrush}" Margin="0,0,0,6"/>
<TextBox x:Name="EndCardBox" Grid.Row="1" Margin="0,0,8,8" ToolTip="Optional end-card image"/>
<Button Grid.Row="1" Grid.Column="1" Content="Browse…" Click="BrowseEndCard_Click" Margin="0,0,0,8"/>
<TextBox x:Name="OutputFolderBox" Grid.Row="2" Margin="0,0,8,8" ToolTip="Output folder"/>
<Button Grid.Row="2" Grid.Column="1" Content="Browse…" Click="BrowseOutput_Click" Margin="0,0,0,8"/>
<TextBox x:Name="OutputNameBox" Grid.Row="3" Grid.ColumnSpan="2" Text="amigadb_intro" ToolTip="Output filename prefix"/>
</Grid>
</GroupBox>
</Grid>
<GroupBox Grid.Row="1" Header="Render progress" Margin="0,14,0,0">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,8">
<TextBlock x:Name="StageText" Text="Ready" FontWeight="SemiBold"/>
<TextBlock x:Name="PercentText" Text="0%" Foreground="{DynamicResource AccentBrush}" Margin="6,0,0,0"/>
</StackPanel>
<ProgressBar x:Name="RenderProgress" Minimum="0" Maximum="100" Foreground="{DynamicResource AccentBrush}"/>
<TextBlock x:Name="StatusText" Text="Select the recordings and start the render. The end card is optional."
Foreground="{DynamicResource MutedBrush}" TextWrapping="Wrap" Margin="0,8,0,0"/>
</StackPanel>
</GroupBox>
<GroupBox Grid.Row="2" Header="FFmpeg log" Margin="0,14,0,0">
<TextBox x:Name="LogBox" IsReadOnly="True" AcceptsReturn="True"
VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto"
FontFamily="Consolas" FontSize="11" TextWrapping="NoWrap"/>
</GroupBox>
<DockPanel Grid.Row="3" Margin="0,12,0,0" LastChildFill="False">
<StackPanel Orientation="Horizontal" DockPanel.Dock="Left">
<Button x:Name="OpenOutputButton" Content="Open output folder" Click="OpenOutput_Click" IsEnabled="False" Margin="0,0,10,0"/>
<Button x:Name="PreviewRenderedButton" Content="Preview render" Click="PreviewRendered_Click" IsEnabled="False"/>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" DockPanel.Dock="Right">
<Button x:Name="OpenSettingsButton" Content="⚙" Click="OpenSettings_Click" Style="{StaticResource IconButton}" Margin="0,0,10,0" ToolTip="Settings"/>
<Button x:Name="CancelButton" Content="Cancel" Click="Cancel_Click" IsEnabled="False" Margin="0,0,10,0"/>
<Button x:Name="RenderButton" Content="Start render" Style="{StaticResource PrimaryButton}" Click="Render_Click"/>
</StackPanel>
</DockPanel>
<Border x:Name="SettingsOverlay" Grid.RowSpan="4" Background="#88060B14" Visibility="Collapsed">
<Grid Margin="40">
<Border Width="760"
MaxHeight="720"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Background="{DynamicResource PanelBrush}"
BorderBrush="{DynamicResource HeroBorderBrush}"
BorderThickness="1.5"
CornerRadius="24"
Padding="20">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="Settings" FontSize="24" FontWeight="SemiBold" Margin="0,0,0,14"/>
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
<StackPanel>
<GroupBox Header="Appearance">
<StackPanel>
<TextBlock Text="Theme" Foreground="{DynamicResource MutedBrush}" Margin="0,0,0,4"/>
<ComboBox x:Name="ThemeBox" SelectionChanged="ThemeBox_SelectionChanged">
<ComboBoxItem Content="Dark" Tag="Dark"/>
<ComboBoxItem Content="Light" Tag="Light"/>
</ComboBox>
</StackPanel>
</GroupBox>
<GroupBox Header="Timing and screenshots">
<Grid>
<Grid.ColumnDefinitions><ColumnDefinition/><ColumnDefinition/></Grid.ColumnDefinitions>
<Grid.RowDefinitions><RowDefinition/><RowDefinition/></Grid.RowDefinitions>
<StackPanel Margin="0,0,8,10">
<TextBlock Text="Trim start (seconds)" Foreground="{DynamicResource MutedBrush}"/>
<TextBox x:Name="TrimBox"/>
</StackPanel>
<StackPanel Grid.Column="1" Margin="8,0,0,10">
<TextBlock Text="Fade (seconds)" Foreground="{DynamicResource MutedBrush}"/>
<TextBox x:Name="FadeBox"/>
</StackPanel>
<StackPanel Grid.Row="1" Margin="0,0,8,0">
<TextBlock Text="End-card hold (seconds)" Foreground="{DynamicResource MutedBrush}"/>
<TextBox x:Name="HoldBox"/>
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="1" Margin="8,0,0,0">
<TextBlock Text="Thumbnail interval (seconds)" Foreground="{DynamicResource MutedBrush}"/>
<TextBox x:Name="IntervalBox"/>
</StackPanel> </StackPanel>
</Grid> </Grid>
</GroupBox>
<GroupBox Header="Source files"> <!--
<StackPanel> The Frame hosts pages for your application content. Add your UI to
<CheckBox x:Name="MoveSourcesBox" MainPage.xaml rather than here so you can use Page features such as
Content="Move source videos to destination\originals" navigation events and the Loaded lifecycle.
IsChecked="True" -->
Margin="0,0,0,6"/> <Frame x:Name="RootFrame" Grid.Row="1" />
<TextBlock Foreground="{DynamicResource MutedBrush}"
TextWrapping="Wrap"
Text="After a successful render, original recordings are moved into an originals subfolder of the output folder. Uncheck to leave them where they are."/>
</StackPanel>
</GroupBox>
<GroupBox Header="Video encoding">
<Grid>
<Grid.ColumnDefinitions><ColumnDefinition Width="*"/><ColumnDefinition Width="*"/></Grid.ColumnDefinitions>
<Grid.RowDefinitions><RowDefinition/><RowDefinition/></Grid.RowDefinitions>
<StackPanel Margin="0,0,8,8">
<TextBlock Text="Encoder" Foreground="{DynamicResource MutedBrush}" Margin="0,0,0,4"/>
<ComboBox x:Name="EncoderBox" SelectedIndex="0">
<ComboBoxItem Content="Auto (NVENC → CPU fallback)" Tag="Auto"/>
<ComboBoxItem Content="NVIDIA NVENC" Tag="NvidiaNvenc"/>
<ComboBoxItem Content="CPU libx264" Tag="CpuX264"/>
</ComboBox>
</StackPanel>
<StackPanel Grid.Column="1" Margin="8,0,0,8">
<TextBlock Text="Output" Foreground="{DynamicResource MutedBrush}" Margin="0,0,0,4"/>
<TextBox Text="3840 × 2160 · 50 FPS" IsReadOnly="True"/>
</StackPanel>
<TextBlock Grid.Row="1" Grid.ColumnSpan="2" Foreground="{DynamicResource MutedBrush}"
Text="NVENC: preset P6 / CQ 16 · CPU: slow / CRF 13"/>
</Grid>
</GroupBox>
<GroupBox Header="FFmpeg tools">
<Grid>
<Grid.ColumnDefinitions><ColumnDefinition Width="*"/><ColumnDefinition Width="Auto"/></Grid.ColumnDefinitions>
<Grid.RowDefinitions><RowDefinition Height="Auto"/><RowDefinition/><RowDefinition/><RowDefinition/></Grid.RowDefinitions>
<TextBlock Text="Optional override. Leave blank to auto-detect FFmpeg and FFprobe from PATH, then use the embedded fallback."
Foreground="{DynamicResource MutedBrush}" Margin="0,0,0,6"/>
<TextBox x:Name="FfmpegPathBox" Grid.Row="1" Margin="0,0,8,8" ToolTip="Path to ffmpeg.exe"/>
<Button Grid.Row="1" Grid.Column="1" Content="Browse…" Click="BrowseFfmpeg_Click" Margin="0,0,0,8"/>
<TextBox x:Name="FfprobePathBox" Grid.Row="2" Margin="0,0,8,8" ToolTip="Path to ffprobe.exe"/>
<Button Grid.Row="2" Grid.Column="1" Content="Browse…" Click="BrowseFfprobe_Click" Margin="0,0,0,8"/>
<TextBox x:Name="PreviewPlayerPathBox" Grid.Row="3" Margin="0,0,8,0" ToolTip="Optional path to mpv.exe or another video player"/>
<Button Grid.Row="3" Grid.Column="1" Content="Browse…" Click="BrowsePreviewPlayer_Click"/>
</Grid>
</GroupBox>
</StackPanel>
</ScrollViewer>
<Grid Grid.Row="2" Margin="0,14,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Advanced settings are saved automatically." Foreground="{DynamicResource MutedBrush}" VerticalAlignment="Center"/>
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Content="Close" Click="CloseSettings_Click" MinWidth="140" Margin="0,0,10,0"/>
<Button Content="Done" Style="{StaticResource PrimaryButton}" Click="CloseSettings_Click" MinWidth="140"/>
</StackPanel>
</Grid>
</Grid>
</Border>
</Grid>
</Border>
</Grid>
</Grid>
<Border x:Name="ExitConfirmOverlay" Grid.RowSpan="2" Background="#88060B14" Visibility="Collapsed">
<Border Width="420"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Background="{DynamicResource PanelBrush}"
BorderBrush="{DynamicResource HeroBorderBrush}"
BorderThickness="1.5"
CornerRadius="20"
Padding="24">
<StackPanel>
<TextBlock Text="Exit AmiReel?" FontSize="20" FontWeight="SemiBold" Margin="0,0,0,10"/>
<TextBlock Text="Are you sure you want to close the application?"
Foreground="{DynamicResource MutedBrush}" TextWrapping="Wrap" Margin="0,0,0,20"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button Content="Cancel" Click="CancelExit_Click" MinWidth="110" Margin="0,0,10,0"/>
<Button Content="Exit" Style="{StaticResource PrimaryButton}" Click="ConfirmExit_Click" MinWidth="110"/>
</StackPanel>
</StackPanel>
</Border>
</Border>
</Grid> </Grid>
</Window> </Window>
+93 -402
View File
@@ -1,425 +1,116 @@
using System.Collections.ObjectModel; using Microsoft.UI;
using System.Diagnostics; using Microsoft.UI.Windowing;
using System.Globalization; using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using Windows.Graphics;
using System.Windows; using Windows.UI;
using System.Windows.Controls; using WinRT.Interop;
using System.Windows.Interop;
using AmigaDB.VideoRenderer.Models;
using AmigaDB.VideoRenderer.Services;
using Microsoft.Win32;
using System.Windows.Media;
namespace AmigaDB.VideoRenderer; // To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
public partial class MainWindow : Window namespace AmiReel;
/// <summary>
/// The application window. This hosts a Frame that displays pages. Add your
/// UI and logic to MainPage.xaml / MainPage.xaml.cs instead of here so you
/// can use Page features such as navigation events and the Loaded lifecycle.
/// </summary>
public sealed partial class MainWindow : Window
{ {
private readonly ObservableCollection<string> _inputs = [];
private readonly AppSettings _loadedSettings;
private readonly StringBuilder _pendingLog = new();
private readonly object _logLock = new();
private CancellationTokenSource? _renderCancellation;
private string? _lastRenderedFile;
private bool _logFlushScheduled;
private bool _titleBarIsLight;
private bool _exitConfirmed; private bool _exitConfirmed;
public MainWindow() public MainWindow()
{ {
InitializeComponent(); InitializeComponent();
InputList.ItemsSource = _inputs; App.MainWindowInstance = this;
_loadedSettings = AppSettingsStore.Load();
OutputFolderBox.Text = _loadedSettings.OutputFolder; ExtendsContentIntoTitleBar = true;
EndCardBox.Text = _loadedSettings.EndCardPath; SetTitleBar(AppTitleBar);
FfmpegPathBox.Text = _loadedSettings.FfmpegPath;
FfprobePathBox.Text = _loadedSettings.FfprobePath; string? executablePath = Environment.ProcessPath;
PreviewPlayerPathBox.Text = _loadedSettings.PreviewPlayerPath; if (!string.IsNullOrWhiteSpace(executablePath) && File.Exists(executablePath))
TrimBox.Text = _loadedSettings.TrimStart; AppWindow.SetIcon(executablePath);
FadeBox.Text = _loadedSettings.FadeSeconds;
HoldBox.Text = _loadedSettings.EndCardHoldSeconds; SetDefaultSizeAndPosition();
IntervalBox.Text = _loadedSettings.ThumbnailInterval;
SelectEncoder(_loadedSettings.Encoder); // Navigate the root frame to the main page on startup.
SelectTheme(_loadedSettings.Theme); RootFrame.Navigate(typeof(MainPage));
ApplyTheme(_loadedSettings.Theme);
MoveSourcesBox.IsChecked = _loadedSettings.ShouldMoveSourcesToOriginals; AppWindow.Closing += AppWindow_Closing;
Closing += MainWindow_Closing;
SourceInitialized += (_, _) => UpdateTitleBarTheme(_titleBarIsLight);
StateChanged += (_, _) => MaximizeButton.Content = WindowState == WindowState.Maximized ? "\uE923" : "\uE922";
} }
private void Minimize_Click(object sender, RoutedEventArgs e) => WindowState = WindowState.Minimized; private void SetDefaultSizeAndPosition()
{
const int DefaultWidth = 1280;
const int DefaultHeight = 860;
const int MinWidth = 1100;
const int MinHeight = 780;
private void MaximizeRestore_Click(object sender, RoutedEventArgs e) IntPtr hwnd = WindowNative.GetWindowHandle(this);
=> WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized; double scale = GetDpiForWindow(hwnd) / 96.0;
int width = (int)(DefaultWidth * scale);
int height = (int)(DefaultHeight * scale);
private void CloseWindowButton_Click(object sender, RoutedEventArgs e) => Close(); AppWindow.Resize(new SizeInt32(width, height));
private void MainWindow_Closing(object? sender, System.ComponentModel.CancelEventArgs e) if (AppWindow.Presenter is OverlappedPresenter presenter)
{
presenter.PreferredMinimumWidth = (int)(MinWidth * scale);
presenter.PreferredMinimumHeight = (int)(MinHeight * scale);
}
DisplayArea? displayArea = DisplayArea.GetFromWindowId(Win32Interop.GetWindowIdFromWindow(hwnd), DisplayAreaFallback.Primary);
if (displayArea is not null)
{
int centerX = displayArea.WorkArea.X + (displayArea.WorkArea.Width - width) / 2;
int centerY = displayArea.WorkArea.Y + (displayArea.WorkArea.Height - height) / 2;
AppWindow.Move(new PointInt32(centerX, centerY));
}
}
[DllImport("user32.dll")]
private static extern int GetDpiForWindow(IntPtr hwnd);
public void ApplyTheme(bool isDark)
{
if (Content is FrameworkElement root)
root.RequestedTheme = isDark ? ElementTheme.Dark : ElementTheme.Light;
AppWindowTitleBar titleBar = AppWindow.TitleBar;
Color foreground = isDark ? Color.FromArgb(255, 0xF5, 0xF7, 0xFB) : Color.FromArgb(255, 0x12, 0x20, 0x33);
Color hoverBackground = isDark ? Color.FromArgb(255, 0x2E, 0x44, 0x6C) : Color.FromArgb(255, 0xD7, 0xE4, 0xF4);
titleBar.ButtonBackgroundColor = Colors.Transparent;
titleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
titleBar.ButtonForegroundColor = foreground;
titleBar.ButtonInactiveForegroundColor = foreground;
titleBar.ButtonHoverBackgroundColor = hoverBackground;
titleBar.ButtonHoverForegroundColor = foreground;
titleBar.ButtonPressedBackgroundColor = hoverBackground;
titleBar.ButtonPressedForegroundColor = foreground;
}
private async void AppWindow_Closing(AppWindow sender, AppWindowClosingEventArgs args)
{ {
if (_exitConfirmed) return; if (_exitConfirmed) return;
e.Cancel = true; args.Cancel = true;
ExitConfirmOverlay.Visibility = Visibility.Visible;
}
private void CancelExit_Click(object sender, RoutedEventArgs e) => ExitConfirmOverlay.Visibility = Visibility.Collapsed; FrameworkElement root = (FrameworkElement)Content;
ContentDialog dialog = DialogHelper.CreateStyled(RootFrame.XamlRoot, root.RequestedTheme);
dialog.Title = "Exit AmiReel?";
dialog.Content = "Are you sure you want to close the application?";
dialog.PrimaryButtonText = "Exit";
dialog.CloseButtonText = "Cancel";
dialog.DefaultButton = ContentDialogButton.Primary;
ContentDialogResult result = await dialog.ShowAsync();
if (result != ContentDialogResult.Primary) return;
private void ConfirmExit_Click(object sender, RoutedEventArgs e)
{
_exitConfirmed = true; _exitConfirmed = true;
SaveSettings();
Close(); Close();
} }
private void InputList_DragEnter(object sender, DragEventArgs e)
{
e.Effects = e.Data.GetDataPresent(DataFormats.FileDrop) ? DragDropEffects.Copy : DragDropEffects.None;
e.Handled = true;
}
private void InputList_Drop(object sender, DragEventArgs e)
{
if (!e.Data.GetDataPresent(DataFormats.FileDrop)) return;
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop)!;
foreach (string file in files.Where(Models.SupportedVideoFormats.IsSupported).OrderBy(NaturalKey))
if (!_inputs.Contains(file, StringComparer.OrdinalIgnoreCase)) _inputs.Add(file);
}
private void AddInputs_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog dialog = new() { Filter = $"Video files|{Models.SupportedVideoFormats.PickerFilter}", Multiselect = true };
if (dialog.ShowDialog(this) != true) return;
foreach (string file in dialog.FileNames.OrderBy(NaturalKey))
if (!_inputs.Contains(file, StringComparer.OrdinalIgnoreCase)) _inputs.Add(file);
}
private void ClearInputs_Click(object sender, RoutedEventArgs e) => _inputs.Clear();
private void BrowseEndCard_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog dialog = new() { Filter = "Images|*.png;*.jpg;*.jpeg;*.webp;*.bmp|All files|*.*" };
if (dialog.ShowDialog(this) == true) EndCardBox.Text = dialog.FileName;
}
private void BrowseOutput_Click(object sender, RoutedEventArgs e)
{
OpenFolderDialog dialog = new() { InitialDirectory = OutputFolderBox.Text };
if (dialog.ShowDialog(this) == true) OutputFolderBox.Text = dialog.FolderName;
}
private void BrowseFfmpeg_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog dialog = new() { Filter = "FFmpeg executable (ffmpeg.exe)|ffmpeg.exe|Executable files (*.exe)|*.exe|All files|*.*" };
if (dialog.ShowDialog(this) == true) FfmpegPathBox.Text = dialog.FileName;
}
private void BrowseFfprobe_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog dialog = new() { Filter = "FFprobe executable (ffprobe.exe)|ffprobe.exe|Executable files (*.exe)|*.exe|All files|*.*" };
if (dialog.ShowDialog(this) == true) FfprobePathBox.Text = dialog.FileName;
}
private void BrowsePreviewPlayer_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog dialog = new() { Filter = "Video player (*.exe)|*.exe|All files|*.*" };
if (dialog.ShowDialog(this) == true) PreviewPlayerPathBox.Text = dialog.FileName;
}
private void OpenSettings_Click(object sender, RoutedEventArgs e) => SettingsOverlay.Visibility = Visibility.Visible;
private void CloseSettings_Click(object sender, RoutedEventArgs e)
{
SettingsOverlay.Visibility = Visibility.Collapsed;
SaveSettings();
}
private async void Render_Click(object sender, RoutedEventArgs e)
{
try
{
RenderSettings settings = ReadSettings();
SaveSettings();
SetRendering(true);
LogBox.Clear();
_renderCancellation = new CancellationTokenSource();
Progress<RenderProgress> progress = new(UpdateProgress);
IReadOnlyList<string> resolvedInputs = await new RenderPipeline().RenderAsync(settings, progress, AppendLog, _renderCancellation.Token);
ReplaceInputs(resolvedInputs);
_lastRenderedFile = Path.Combine(settings.OutputDirectory, settings.OutputName + "_final.mp4");
OpenOutputButton.IsEnabled = true;
PreviewRenderedButton.IsEnabled = File.Exists(_lastRenderedFile);
MessageBox.Show(this, "The AmiReel render completed successfully.", "Render complete",
MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (OperationCanceledException)
{
UpdateProgress(new(RenderProgress.Value, "Cancelled", "The render was cancelled."));
}
catch (Exception exception)
{
AppendLog("ERROR: " + exception);
MessageBox.Show(this, UserFacingErrors.Summarize(exception), "Render failed", MessageBoxButton.OK, MessageBoxImage.Error);
StageText.Text = "Failed";
}
finally
{
_renderCancellation?.Dispose();
_renderCancellation = null;
SetRendering(false);
}
}
private RenderSettings ReadSettings()
{
static double Number(string text, string name)
{
if (!double.TryParse(text.Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out double value) || value < 0)
throw new ArgumentException($"Enter a valid non-negative value for {name}.");
return value;
}
if (!int.TryParse(IntervalBox.Text, out int interval) || interval < 1)
throw new ArgumentException("Thumbnail interval must be at least one second.");
EncoderMode encoder = Enum.Parse<EncoderMode>(((ComboBoxItem)EncoderBox.SelectedItem).Tag!.ToString()!);
return new(_inputs.ToList(), EndCardBox.Text.Trim(), OutputFolderBox.Text.Trim(), OutputNameBox.Text.Trim(),
FfmpegPathBox.Text.Trim(), FfprobePathBox.Text.Trim(),
encoder, Number(TrimBox.Text, "trim start"), Number(FadeBox.Text, "fade"), Number(HoldBox.Text, "end-card hold"), interval,
MoveSourcesBox.IsChecked == true);
}
private void ReplaceInputs(IReadOnlyList<string> paths)
{
_inputs.Clear();
foreach (string path in paths)
_inputs.Add(path);
}
private void Cancel_Click(object sender, RoutedEventArgs e)
{
CancelButton.IsEnabled = false;
StatusText.Text = "Stopping FFmpeg…";
_renderCancellation?.Cancel();
}
private void OpenOutput_Click(object sender, RoutedEventArgs e)
{
if (Directory.Exists(OutputFolderBox.Text))
Process.Start(new ProcessStartInfo("explorer.exe", OutputFolderBox.Text) { UseShellExecute = true });
}
private void PreviewSource_Click(object sender, RoutedEventArgs e)
{
if (InputList.SelectedItem is string path)
OpenPreview(path);
}
private void PreviewRendered_Click(object sender, RoutedEventArgs e)
{
if (!string.IsNullOrWhiteSpace(_lastRenderedFile) && File.Exists(_lastRenderedFile))
OpenPreview(_lastRenderedFile);
}
private void InputList_SelectionChanged(object sender, SelectionChangedEventArgs e)
=> PreviewSourceButton.IsEnabled = InputList.SelectedItem is string;
private void SetRendering(bool rendering)
{
RenderButton.IsEnabled = !rendering;
CancelButton.IsEnabled = rendering;
}
private void UpdateProgress(RenderProgress value)
{
RenderProgress.Value = value.Percent;
PercentText.Text = $"{value.Percent:0}%";
StageText.Text = value.Stage;
StatusText.Text = value.Message;
}
private void AppendLog(string line)
{
lock (_logLock)
{
_pendingLog.AppendLine(line);
if (_logFlushScheduled)
return;
_logFlushScheduled = true;
}
_ = Dispatcher.BeginInvoke(() =>
{
string chunk;
lock (_logLock)
{
chunk = _pendingLog.ToString();
_pendingLog.Clear();
_logFlushScheduled = false;
}
if (chunk.Length == 0)
return;
LogBox.AppendText(chunk);
LogBox.ScrollToEnd();
});
}
private static string NaturalKey(string path)
{
string name = Path.GetFileNameWithoutExtension(path);
int underscore = name.LastIndexOf('_');
return underscore >= 0 && int.TryParse(name[(underscore + 1)..], out int number)
? name[..underscore] + number.ToString("D10")
: name;
}
private void ThemeBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (!IsLoaded) return;
string theme = SelectedTheme();
ApplyTheme(theme);
SaveSettings();
}
private void SaveSettings()
{
AppSettingsStore.Save(new AppSettings(
OutputFolderBox.Text.Trim(),
EndCardBox.Text.Trim(),
FfmpegPathBox.Text.Trim(),
FfprobePathBox.Text.Trim(),
PreviewPlayerPathBox.Text.Trim(),
SelectedTheme(),
SelectedEncoder(),
TrimBox.Text.Trim(),
FadeBox.Text.Trim(),
HoldBox.Text.Trim(),
IntervalBox.Text.Trim(),
MoveSourcesBox.IsChecked == true));
}
private void OpenPreview(string mediaPath)
{
if (!File.Exists(mediaPath))
{
MessageBox.Show(this, "The selected media file was not found.", "Preview unavailable",
MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
string playerPath = PreviewPlayerPathBox.Text.Trim();
try
{
if (!string.IsNullOrWhiteSpace(playerPath))
{
if (!File.Exists(playerPath))
throw new FileNotFoundException("Configured preview player was not found.", playerPath);
ProcessStartInfo customPlayer = new()
{
FileName = playerPath,
UseShellExecute = false
};
customPlayer.ArgumentList.Add(mediaPath);
Process.Start(customPlayer);
return;
}
Process.Start(new ProcessStartInfo(mediaPath) { UseShellExecute = true });
}
catch (Exception exception)
{
MessageBox.Show(this, exception.Message, "Preview failed", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private string SelectedTheme() => ((ComboBoxItem)ThemeBox.SelectedItem).Tag!.ToString()!;
private string SelectedEncoder() => ((ComboBoxItem)EncoderBox.SelectedItem).Tag!.ToString()!;
private void SelectTheme(string theme)
{
foreach (ComboBoxItem item in ThemeBox.Items)
{
if (string.Equals(item.Tag?.ToString(), theme, StringComparison.OrdinalIgnoreCase))
{
ThemeBox.SelectedItem = item;
return;
}
}
ThemeBox.SelectedIndex = 0;
}
private void SelectEncoder(string encoder)
{
foreach (ComboBoxItem item in EncoderBox.Items)
{
if (string.Equals(item.Tag?.ToString(), encoder, StringComparison.OrdinalIgnoreCase))
{
EncoderBox.SelectedItem = item;
return;
}
}
EncoderBox.SelectedIndex = 0;
}
private void ApplyTheme(string theme)
{
bool light = string.Equals(theme, "Light", StringComparison.OrdinalIgnoreCase);
SetBrush("PageBrush", light ? "#F4F7FB" : "#0E1525");
SetBrush("PanelBrush", light ? "#FFFFFF" : "#162033");
SetBrush("PanelAltBrush", light ? "#EEF4FB" : "#1A2740");
SetBrush("FieldBrush", light ? "#F7FAFD" : "#1D2940");
SetBrush("BorderBrush", light ? "#C7D3E3" : "#2B3A58");
SetBrush("AccentBrush", light ? "#0E9AEF" : "#4AB8FF");
SetBrush("AccentSoftBrush", light ? "#D6EEFF" : "#13324F");
SetBrush("TextBrush", light ? "#122033" : "#F5F7FB");
SetBrush("MutedBrush", light ? "#5F7390" : "#9FB1CC");
SetBrush("ButtonBrush", light ? "#E5EDF7" : "#243554");
SetBrush("ButtonHoverBrush", light ? "#D7E4F4" : "#2E446C");
SetBrush("ButtonDisabledBrush", light ? "#EEF2F7" : "#2A3140");
SetBrush("ButtonDisabledTextBrush", light ? "#8C99AB" : "#7E8BA1");
SetBrush("PrimaryTextBrush", light ? "#FFFFFF" : "#07111D");
SetBrush("SelectionBrush", light ? "#BEE4FF" : "#295C87");
SetBrush("SelectionTextBrush", light ? "#122033" : "#FFFFFF");
_titleBarIsLight = light;
UpdateTitleBarTheme(light);
}
private void SetBrush(string key, string color)
{
Application.Current.Resources[key] = new SolidColorBrush((Color)ColorConverter.ConvertFromString(color));
}
private void UpdateTitleBarTheme(bool light)
{
IntPtr hwnd = new WindowInteropHelper(this).Handle;
if (hwnd == IntPtr.Zero) return;
int useImmersiveDarkMode = light ? 0 : 1;
DwmSetWindowAttribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, ref useImmersiveDarkMode, sizeof(int));
int captionColor = ToColorRef(light ? "#F4F7FB" : "#0E1525");
int textColor = ToColorRef(light ? "#122033" : "#F5F7FB");
DwmSetWindowAttribute(hwnd, DWMWA_CAPTION_COLOR, ref captionColor, sizeof(int));
DwmSetWindowAttribute(hwnd, DWMWA_TEXT_COLOR, ref textColor, sizeof(int));
}
private static int ToColorRef(string hexColor)
{
Color color = (Color)ColorConverter.ConvertFromString(hexColor);
return color.R | (color.G << 8) | (color.B << 16);
}
private const int DWMWA_USE_IMMERSIVE_DARK_MODE = 20;
private const int DWMWA_CAPTION_COLOR = 35;
private const int DWMWA_TEXT_COLOR = 36;
[DllImport("dwmapi.dll", PreserveSig = true)]
private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attribute, ref int value, int size);
} }
+1 -1
View File
@@ -1,4 +1,4 @@
namespace AmigaDB.VideoRenderer.Models; namespace AmiReel.Models;
public sealed record AppSettings( public sealed record AppSettings(
string OutputFolder, string OutputFolder,
+1 -1
View File
@@ -1,4 +1,4 @@
namespace AmigaDB.VideoRenderer.Models; namespace AmiReel.Models;
public enum EncoderMode public enum EncoderMode
{ {
+1 -1
View File
@@ -1,7 +1,7 @@
using System.IO; using System.IO;
using System.Linq; using System.Linq;
namespace AmigaDB.VideoRenderer.Models; namespace AmiReel.Models;
/// <summary> /// <summary>
/// Video file extensions accepted as render sources. FFmpeg can demux far more than this, /// Video file extensions accepted as render sources. FFmpeg can demux far more than this,
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<PublishProtocol>FileSystem</PublishProtocol>
<Platform>ARM64</Platform>
<RuntimeIdentifier>win-arm64</RuntimeIdentifier>
<PublishDir>bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\</PublishDir>
<SelfContained>true</SelfContained>
<PublishSingleFile>False</PublishSingleFile>
</PropertyGroup>
</Project>
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<PublishProtocol>FileSystem</PublishProtocol>
<Platform>x64</Platform>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<PublishDir>bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\</PublishDir>
<SelfContained>true</SelfContained>
<PublishSingleFile>False</PublishSingleFile>
</PropertyGroup>
</Project>
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<PublishProtocol>FileSystem</PublishProtocol>
<Platform>x86</Platform>
<RuntimeIdentifier>win-x86</RuntimeIdentifier>
<PublishDir>bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\</PublishDir>
<SelfContained>true</SelfContained>
<PublishSingleFile>False</PublishSingleFile>
</PropertyGroup>
</Project>
@@ -1,9 +1,9 @@
{ {
"profiles": { "profiles": {
"AmiReel.WinUI (Package)": { "AmiReel (Package)": {
"commandName": "MsixPackage" "commandName": "MsixPackage"
}, },
"AmiReel.WinUI (Unpackaged)": { "AmiReel (Unpackaged)": {
"commandName": "Project" "commandName": "Project"
} }
} }
+75 -121
View File
@@ -1,111 +1,90 @@
# AmiReel # AmiReel
AmiReel is a Windows video-rendering tool for turning Amiga AVI captures into: AmiReel is a Windows desktop app for turning Amiga (or any) screen recordings into:
- a 4K 50 FPS final video, - a 4K 50 FPS final video,
- PNG/JPG thumbnails, - PNG/JPG thumbnails,
- an animated WebP preview, - an animated WebP preview.
- and a publishable Windows desktop app workflow.
The repository currently contains two desktop frontends that share the same render pipeline: Built with **WinUI 3** on the Windows App SDK, driving FFmpeg under the hood.
- `AmiReel.WinUI`
The newer WinUI 3 application. This is the default publish target.
- `AmigaDB.VideoRenderer.csproj`
The existing WPF application.
## Features ## Features
- multiple ordered AVI inputs - Multiple ordered video inputs`.avi`, `.mp4`, `.m4v`, `.mov`, `.mkv`, `.webm`, `.wmv`, `.flv`, `.mpg`, `.mpeg`, `.ts`, `.3gp`
- Drag-and-drop or file-picker source selection
- 3840×2160 output at 50 FPS - 3840×2160 output at 50 FPS
- NVIDIA NVENC with automatic CPU `libx264` fallback - NVIDIA NVENC with automatic CPU `libx264` fallback
- configurable trim, fade, end-card hold, and thumbnail interval - Configurable trim, fade, end-card hold, and thumbnail interval
- optional end-card image - Optional end-card image
- live render progress with frame counters - Live render progress with frame counters and FFmpeg log output
- FFmpeg log output - Source and final-render preview (built-in or a custom player)
- source video preview and final render preview - Dark and light themes
- dark and light themes - Optional move of source recordings into `originals/` in the output folder
- optional move of source recordings into `originals` in the output folder - Optional custom `ffmpeg.exe` / `ffprobe.exe` paths, with automatic detection from `PATH`
- optional custom `ffmpeg.exe` / `ffprobe.exe` paths - Self-contained single-file publish for easy distribution
- automatic FFmpeg detection from `PATH`
- single-file WinUI publish for easier distribution ## Project Layout
```
AmiReel.csproj Project file
AmiReel.slnx Solution file
App.xaml(.cs) Application entry point
MainWindow.xaml(.cs) Window shell: custom title bar, theming, exit confirmation
MainPage.xaml(.cs) Main UI: source list, settings, render controls
DialogHelper.cs Shared ContentDialog styling (Settings, exit, error dialogs)
Package.appxmanifest MSIX packaging identity and tile/icon declarations
app.manifest Win32 manifest (DPI awareness, OS compatibility)
Assets/ Packaged app icons and tiles (all required sizes)
branding/ Source artwork the Assets/ tiles are generated from
Models/
AppSettings.cs Persisted user settings
RenderSettings.cs Render job parameters
SupportedVideoFormats.cs Accepted input file extensions
Services/
RenderPipeline.cs Main render workflow (FFmpeg orchestration)
ProcessRunner.cs FFmpeg process execution and progress parsing
ToolExtractor.cs FFmpeg resolution, extraction, and fallback logic
MediaProbe.cs Duration/audio-stream probing via ffprobe
FfmpegLibraryProbe.cs NVENC capability probing
AppSettingsStore.cs Settings load/save (JSON in %LOCALAPPDATA%)
UserFacingErrors.cs Exception-to-message translation
ThirdParty/ Embedded ffmpeg.exe / ffprobe.exe (not checked in, see below)
publish-win-x64.ps1 Publish script
```
## Requirements ## Requirements
For development: For development:
- Windows 10 or Windows 11 - Windows 10 or Windows 11
- .NET SDK - .NET 10 SDK with WinUI 3 / Windows App SDK workload
- WPF project: .NET 8 SDK - Windows x64 FFmpeg binaries in `ThirdParty/`:
- WinUI project: .NET 10 SDK and WinUI/Windows App SDK support
- Windows x64 FFmpeg binaries in `ThirdParty`
- `ThirdParty\ffmpeg.exe` - `ThirdParty\ffmpeg.exe`
- `ThirdParty\ffprobe.exe` - `ThirdParty\ffprobe.exe`
For end users: For end users:
- Windows 10 or Windows 11 x64 - Windows 10 or Windows 11 x64
- the published AmiReel executable - The published `AmiReel.exe` — FFmpeg is not required separately if you distribute AmiReel with the embedded `ThirdParty` binaries
- FFmpeg is not required separately if you distribute AmiReel with embedded `ThirdParty` FFmpeg binaries
## Project Layout
- `AmiReel.WinUI\`
WinUI 3 frontend
- `MainWindow.xaml` and `MainWindow.xaml.cs`
WPF frontend
- `Services\RenderPipeline.cs`
Main render workflow
- `Services\ProcessRunner.cs`
FFmpeg process execution and progress parsing
- `Services\ToolExtractor.cs`
FFmpeg resolution, extraction, and fallback logic
- `publish-win-x64.ps1`
Publish script
## Run in Development ## Run in Development
### WinUI
```powershell ```powershell
dotnet build .\AmiReel.WinUI\AmiReel.WinUI.csproj dotnet build .\AmiReel.csproj
dotnet run --project .\AmiReel.WinUI\AmiReel.WinUI.csproj dotnet run --project .\AmiReel.csproj
```
### WPF
```powershell
dotnet build .\AmigaDB.VideoRenderer.csproj
dotnet run --project .\AmigaDB.VideoRenderer.csproj
``` ```
## Publish ## Publish
### Default: WinUI
The publish script now defaults to the WinUI application:
```powershell ```powershell
.\publish-win-x64.ps1 .\publish-win-x64.ps1
``` ```
This publishes the WinUI app as a self-contained single-file executable. This publishes a self-contained, single-file `win-x64` executable. At the end of the script
you'll see the release folder and final executable path, for example:
At the end of the script you will see:
- the release folder path
- the final executable path
Example output path:
```text ```text
AmiReel.WinUI\bin\Release\net10.0-windows10.0.26100.0\win-x64\publish\AmiReel.exe bin\Release\net10.0-windows10.0.26100.0\win-x64\publish\AmiReel.exe
```
### Publish WPF instead
```powershell
.\publish-win-x64.ps1 -Target Wpf
``` ```
## Installation ## Installation
@@ -113,42 +92,33 @@ AmiReel.WinUI\bin\Release\net10.0-windows10.0.26100.0\win-x64\publish\AmiReel.ex
### For developers ### For developers
1. Clone the repository. 1. Clone the repository.
2. Add `ffmpeg.exe` and `ffprobe.exe` to `ThirdParty`. 2. Add `ffmpeg.exe` and `ffprobe.exe` to `ThirdParty\`.
3. Restore and build the desired app: 3. Restore and build:
```powershell ```powershell
dotnet build .\AmigaDB.VideoRenderer.sln dotnet build .\AmiReel.slnx
``` ```
4. Run either the WinUI or WPF project.
### For end users ### For end users
1. Copy the published `AmiReel.exe` from the publish folder to any location, for example: 1. Copy the published `AmiReel.exe` to any location, for example `C:\Apps\AmiReel\`.
```text
C:\Apps\AmiReel\
```
2. Double-click `AmiReel.exe`. 2. Double-click `AmiReel.exe`.
3. If FFmpeg is embedded in the build, no extra setup is required. 3. If FFmpeg is embedded in the build, no extra setup is required.
4. If you want to use your own FFmpeg build: 4. To use your own FFmpeg build instead, open **Settings** and set:
open `Settings` and select: - `ffmpeg.exe` path
- `ffmpeg.exe` - `ffprobe.exe` path
- `ffprobe.exe`
### Optional desktop shortcut ### Optional desktop shortcut
1. Right-click `AmiReel.exe`. Right-click `AmiReel.exe`**Send to****Desktop (create shortcut)**.
2. Choose `Send to` -> `Desktop (create shortcut)`.
## FFmpeg Behavior ## FFmpeg Behavior
AmiReel resolves FFmpeg in this order: AmiReel resolves FFmpeg in this order:
1. custom paths from settings 1. Custom paths from Settings
2. `ffmpeg.exe` and `ffprobe.exe` found on system `PATH` 2. `ffmpeg.exe` / `ffprobe.exe` found on the system `PATH`
3. embedded `ThirdParty` binaries 3. Embedded `ThirdParty` binaries (extracted to `%LOCALAPPDATA%\AmiReel\tools` on first use)
If NVENC cannot be initialized, AmiReel automatically falls back to CPU `libx264`. If NVENC cannot be initialized, AmiReel automatically falls back to CPU `libx264`.
@@ -160,41 +130,25 @@ User settings are stored in:
%LOCALAPPDATA%\AmiReel\settings.json %LOCALAPPDATA%\AmiReel\settings.json
``` ```
This includes values such as: This includes: output folder, end-card path, FFmpeg/preview-player paths, theme, encoder
selection, timing settings, and whether source videos are moved into `originals/` after a
- output folder successful render.
- end-card path
- FFmpeg paths
- theme
- encoder selection
- timing settings
- whether source videos are moved into `originals` after a successful render
## Notes for Distribution ## Notes for Distribution
- If you distribute FFmpeg binaries with AmiReel, you are responsible for complying with the license terms of the FFmpeg build you use. - If you distribute FFmpeg binaries with AmiReel, you are responsible for complying with the
- Keep any required notices, source offer, or attribution required by that FFmpeg distribution. license terms of the FFmpeg build you use. Keep any required notices, source offer, or
- Windows Explorer may cache executable icons. If a freshly published build still shows an old icon, rename the file or refresh the icon cache before assuming the embed failed. attribution required by that distribution.
- Windows Explorer may cache executable icons. If a freshly published build still shows an old
icon, rename the file or refresh the icon cache before assuming the embed failed.
## Troubleshooting ## Troubleshooting
### App falls back to CPU instead of NVENC **App falls back to CPU instead of NVENC** — FFmpeg could not initialize `h264_nvenc`. AmiReel
continues automatically with CPU encoding.
This usually means FFmpeg could not initialize `h264_nvenc`. AmiReel will continue with CPU encoding automatically. **FFmpeg tool error** — If the embedded FFmpeg is missing or broken, AmiReel tries configured
tool paths, then FFmpeg from `PATH`. Set the paths manually in **Settings** if needed.
### FFmpeg tool error **Render failed popup** — Shows a short human-readable summary. Full technical details remain
available in the **FFmpeg log** panel.
If embedded FFmpeg is missing or broken, AmiReel will try:
1. configured tool paths
2. FFmpeg from `PATH`
If needed, set the paths manually in `Settings`.
### Render failed popup
The popup shows a short human-readable summary. Full technical details remain available in the `FFmpeg log` panel.
## Current Default Recommendation
Use the WinUI build for publishing and distribution unless you specifically need the WPF variant.
+2 -2
View File
@@ -1,8 +1,8 @@
using System.IO; using System.IO;
using System.Text.Json; using System.Text.Json;
using AmigaDB.VideoRenderer.Models; using AmiReel.Models;
namespace AmigaDB.VideoRenderer.Services; namespace AmiReel.Services;
public static class AppSettingsStore public static class AppSettingsStore
{ {
+1 -1
View File
@@ -4,7 +4,7 @@ using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
namespace AmigaDB.VideoRenderer.Services; namespace AmiReel.Services;
internal sealed partial class FfmpegLibraryProbe : IDisposable internal sealed partial class FfmpegLibraryProbe : IDisposable
{ {
+1 -1
View File
@@ -1,7 +1,7 @@
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
namespace AmigaDB.VideoRenderer.Services; namespace AmiReel.Services;
internal sealed class MediaProbe internal sealed class MediaProbe
{ {
+1 -1
View File
@@ -4,7 +4,7 @@ using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.IO; using System.IO;
namespace AmigaDB.VideoRenderer.Services; namespace AmiReel.Services;
public sealed partial class ProcessRunner public sealed partial class ProcessRunner
{ {
+2 -2
View File
@@ -1,9 +1,9 @@
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Text; using System.Text;
using AmigaDB.VideoRenderer.Models; using AmiReel.Models;
namespace AmigaDB.VideoRenderer.Services; namespace AmiReel.Services;
public sealed class RenderPipeline public sealed class RenderPipeline
{ {
+2 -2
View File
@@ -2,7 +2,7 @@ using System.IO;
using System.Reflection; using System.Reflection;
using System.Security.Cryptography; using System.Security.Cryptography;
namespace AmigaDB.VideoRenderer.Services; namespace AmiReel.Services;
public sealed record ToolPaths(string Ffmpeg, string Ffprobe); public sealed record ToolPaths(string Ffmpeg, string Ffprobe);
@@ -52,7 +52,7 @@ public static class ToolExtractor
private static async Task<string> ExtractOneAsync(string fileName, string destination, CancellationToken token) private static async Task<string> ExtractOneAsync(string fileName, string destination, CancellationToken token)
{ {
Assembly assembly = Assembly.GetExecutingAssembly(); Assembly assembly = Assembly.GetExecutingAssembly();
string resourceName = $"AmigaDB.VideoRenderer.Tools.{fileName}"; string resourceName = $"AmiReel.Tools.{fileName}";
await using Stream source = assembly.GetManifestResourceStream(resourceName) await using Stream source = assembly.GetManifestResourceStream(resourceName)
?? throw new InvalidOperationException( ?? throw new InvalidOperationException(
$"Embedded {fileName} was not found. Add it to ThirdParty and publish the application again."); $"Embedded {fileName} was not found. Add it to ThirdParty and publish the application again.");
+1 -1
View File
@@ -1,4 +1,4 @@
namespace AmigaDB.VideoRenderer.Services; namespace AmiReel.Services;
public static class UserFacingErrors public static class UserFacingErrors
{ {
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1"> <assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="AmiReel.WinUI.app"/> <assemblyIdentity version="1.0.0.0" name="AmiReel.app"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application> <application>

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 1.7 MiB

Before

Width:  |  Height:  |  Size: 200 KiB

After

Width:  |  Height:  |  Size: 200 KiB

+2 -2
View File
@@ -14,9 +14,9 @@ Contents:
Individual transparent PNG files: Individual transparent PNG files:
16, 24, 32, 48, 64, 128, 256, 512 and 1024 px. 16, 24, 32, 48, 64, 128, 256, 512 and 1024 px.
Recommended WPF project setting: Referenced from the project file as:
<ApplicationIcon>Assets\AmiReel.ico</ApplicationIcon> <ApplicationIcon>branding\AmiReel.ico</ApplicationIcon>
Product: AmiReel — Amiga Video Renderer Product: AmiReel — Amiga Video Renderer
Brand: AmigaDB Brand: AmigaDB

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 100 KiB

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Before

Width:  |  Height:  |  Size: 330 KiB

After

Width:  |  Height:  |  Size: 330 KiB

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

+5 -28
View File
@@ -1,8 +1,5 @@
[CmdletBinding()] [CmdletBinding()]
param( param()
[ValidateSet('WinUI', 'Wpf')]
[string]$Target = 'WinUI'
)
$ErrorActionPreference = 'Stop' $ErrorActionPreference = 'Stop'
@@ -14,10 +11,8 @@ if (-not (Test-Path $ffmpeg) -or -not (Test-Path $ffprobe)) {
throw 'Add ffmpeg.exe and ffprobe.exe to ThirdParty before publishing.' throw 'Add ffmpeg.exe and ffprobe.exe to ThirdParty before publishing.'
} }
switch ($Target) { $projectPath = Join-Path $projectDir 'AmiReel.csproj'
'WinUI' { $releaseDir = Join-Path $projectDir 'bin\Release\net10.0-windows10.0.26100.0\win-x64'
$projectPath = Join-Path $projectDir 'AmiReel.WinUI\AmiReel.WinUI.csproj'
$releaseDir = Join-Path $projectDir 'AmiReel.WinUI\bin\Release\net10.0-windows10.0.26100.0\win-x64'
$releaseExe = Join-Path $releaseDir 'AmiReel.exe' $releaseExe = Join-Path $releaseDir 'AmiReel.exe'
$publishDir = Join-Path $releaseDir 'publish' $publishDir = Join-Path $releaseDir 'publish'
$publishExe = Join-Path $publishDir 'AmiReel.exe' $publishExe = Join-Path $publishDir 'AmiReel.exe'
@@ -31,24 +26,6 @@ switch ($Target) {
'-p:IncludeNativeLibrariesForSelfExtract=true', '-p:IncludeNativeLibrariesForSelfExtract=true',
'-p:EnableCompressionInSingleFile=true' '-p:EnableCompressionInSingleFile=true'
) )
}
'Wpf' {
$projectPath = Join-Path $projectDir 'AmigaDB.VideoRenderer.csproj'
$releaseDir = Join-Path $projectDir 'bin\Release\net8.0-windows\win-x64'
$releaseExe = Join-Path $releaseDir 'AmiReel.exe'
$publishDir = Join-Path $releaseDir 'publish'
$publishExe = Join-Path $publishDir 'AmiReel.exe'
$publishArguments = @(
'publish', $projectPath,
'-c', 'Release',
'-r', 'win-x64',
'--self-contained', 'true',
'-p:PublishSingleFile=true',
'-p:IncludeNativeLibrariesForSelfExtract=true',
'-p:EnableCompressionInSingleFile=true'
)
}
}
if ((Test-Path $releaseExe) -or (Test-Path $publishExe)) { if ((Test-Path $releaseExe) -or (Test-Path $publishExe)) {
$runningReleaseInstances = Get-Process | Where-Object { $runningReleaseInstances = Get-Process | Where-Object {
@@ -59,7 +36,7 @@ if ((Test-Path $releaseExe) -or (Test-Path $publishExe)) {
} }
if ($runningReleaseInstances) { if ($runningReleaseInstances) {
Write-Host "Stopping running $Target build before publish..." -ForegroundColor Yellow Write-Host 'Stopping running AmiReel build before publish...' -ForegroundColor Yellow
$runningReleaseInstances | Stop-Process -Force $runningReleaseInstances | Stop-Process -Force
Start-Sleep -Milliseconds 800 Start-Sleep -Milliseconds 800
} }
@@ -71,6 +48,6 @@ if (Test-Path $publishDir) {
& dotnet @publishArguments & dotnet @publishArguments
Write-Host "Published $Target to $publishDir" -ForegroundColor Green Write-Host "Published AmiReel to $publishDir" -ForegroundColor Green
Write-Host "Release folder: $publishDir" -ForegroundColor Cyan Write-Host "Release folder: $publishDir" -ForegroundColor Cyan
Write-Host "Executable: $publishExe" -ForegroundColor Cyan Write-Host "Executable: $publishExe" -ForegroundColor Cyan