1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
using System.Windows;
using System.Windows.Input;
using AdamsToolkit.Core;
using AdamsToolkit.Views;
namespace AdamsToolkit;
/// <summary>
/// Janela do Editor de Clips (v1.71, ex-Adams Clips). Vive à parte da janela principal:
/// abre sozinha (ecrã de escolha / ficheiro de vídeo por argumento) ou a partir da sidebar.
/// FFmpeg é descarregado na 1ª abertura; até lá o editor não é sequer construído.
/// </summary>
public partial class EditorWindow : Window
{
private static EditorWindow? _instance;
private EditorView? _editor;
private string? _pendingFile;
private CancellationTokenSource? _setupCts;
/// <summary>Uma janela só: abre (ou traz para a frente) e opcionalmente carrega um ficheiro.</summary>
public static EditorWindow Open(string? file = null)
{
if (_instance == null)
{
_instance = new EditorWindow();
_instance.Closed += (_, _) => _instance = null;
_instance._pendingFile = file;
_instance.Show();
}
else
{
if (_instance.WindowState == WindowState.Minimized) _instance.WindowState = WindowState.Normal;
_instance.Activate();
if (file != null) _ = _instance.LoadAsync(file);
}
return _instance;
}
public static bool IsOpen => _instance != null;
private EditorWindow()
{
InitializeComponent();
VersionText.Text = $"v{SelfUpdater.CurrentVersion}";
Loaded += async (_, _) => await EnsureReadyAsync();
Closing += (_, _) => { _setupCts?.Cancel(); _editor?.Shutdown(); };
// cantos: clip geométrico na raiz (Border.CornerRadius não corta os filhos)
void Clip() { var r = WindowState == WindowState.Maximized ? 0 : 14; RootBorder.Clip = new System.Windows.Media.RectangleGeometry(new Rect(0, 0, RootBorder.ActualWidth, RootBorder.ActualHeight), r, r); }
RootBorder.SizeChanged += (_, _) => Clip();
StateChanged += (_, _) => Clip();
StateChanged += (_, _) => { var max = WindowState == WindowState.Maximized; RootBorder.CornerRadius = new CornerRadius(max ? 0 : 14); TitleBar.CornerRadius = new CornerRadius(max ? 0 : 13, max ? 0 : 13, 0, 0); };
}
private async Task EnsureReadyAsync()
{
if (FfmpegManager.IsReady) { Mount(); return; }
SetupPanel.Visibility = Visibility.Visible;
RetryBtn.Visibility = Visibility.Collapsed;
_setupCts = new CancellationTokenSource();
try
{
var prog = new Progress<(double pct, string msg)>(p => { SetupBar.Value = p.pct; SetupText.Text = p.msg; });
await FfmpegManager.EnsureAsync(prog, _setupCts.Token);
SetupPanel.Visibility = Visibility.Collapsed;
Mount();
}
catch (OperationCanceledException) { }
catch (Exception ex)
{
SetupText.Text = "Não deu: " + ex.Message;
RetryBtn.Visibility = Visibility.Visible;
}
}
private void Mount()
{
if (_editor != null) return;
Task.Run(VideoEditor.CleanupProxies);
_editor = new EditorView();
Host.Content = _editor;
_editor.Focus();
if (_pendingFile != null) { var f = _pendingFile; _pendingFile = null; _ = LoadAsync(f); }
}
private async Task LoadAsync(string file)
{
if (_editor == null) { _pendingFile = file; return; }
try { await _editor.OpenAsync(file); } catch (Exception ex) { EditorPaths.LogCrash(ex); }
}
private async void Retry_Click(object s, RoutedEventArgs e) => await EnsureReadyAsync();
// Do editor para o Toolkit: se a janela principal existir, mostra-a; senão cria-a (passa pelo login normal).
private void Toolkit_Click(object s, RoutedEventArgs e) => App.ShowToolkit();
// ---- chrome ----
private void TitleBar_Drag(object s, MouseButtonEventArgs e)
{
if (e.ClickCount == 2) { Maximize_Click(s, e); return; }
if (e.ButtonState == MouseButtonState.Pressed && WindowState == WindowState.Normal) DragMove();
}
private void Minimize_Click(object s, RoutedEventArgs e) => WindowState = WindowState.Minimized;
private void Maximize_Click(object s, RoutedEventArgs e) => WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
private void Close_Click(object s, RoutedEventArgs e) => Close();
}