diff --git a/TAS.Avalonia/App.axaml.cs b/TAS.Avalonia/App.axaml.cs index 682b2654..e8f10963 100644 --- a/TAS.Avalonia/App.axaml.cs +++ b/TAS.Avalonia/App.axaml.cs @@ -21,7 +21,8 @@ public override void Initialize() { public override void OnFrameworkInitializationCompleted() { if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { - desktop.MainWindow = new MainWindow { DataContext = new MainWindowViewModel() }; + desktop.MainWindow = new MainWindow(); + desktop.MainWindow.DataContext = new MainWindowViewModel((MainWindow)desktop.MainWindow); } base.OnFrameworkInitializationCompleted(); diff --git a/TAS.Avalonia/Communication/StudioCommunicationServer.cs b/TAS.Avalonia/Communication/StudioCommunicationServer.cs index 0dc0d13e..63e34b2c 100644 --- a/TAS.Avalonia/Communication/StudioCommunicationServer.cs +++ b/TAS.Avalonia/Communication/StudioCommunicationServer.cs @@ -7,9 +7,11 @@ namespace TAS.Avalonia.Communication; public class StudioCommunicationServer : StudioCommunicationBase { public event Action StateUpdated; public event Action>> BindingsUpdated; + public event Action> LinesUpdated; protected virtual void OnStateUpdated(StudioInfo obj) => StateUpdated?.Invoke(obj); protected virtual void OnBindingsUpdated(Dictionary> obj) => BindingsUpdated?.Invoke(obj); + protected virtual void OnLinesUpdated(Dictionary lines) => LinesUpdated?.Invoke(lines); private string _returnData; @@ -99,7 +101,8 @@ private void ProcessVersionInfo(byte[] data) { } private void ProcessUpdateLines(byte[] data) { - // Dictionary updateLines = BinaryFormatterHelper.FromByteArray>(data); + Dictionary updateLines = BinaryFormatterHelper.FromByteArray>(data); + OnLinesUpdated(updateLines); // CommunicationWrapper.UpdateLines(updateLines); } diff --git a/TAS.Avalonia/Controls/EditorControl.axaml.cs b/TAS.Avalonia/Controls/EditorControl.axaml.cs index 7e6b37b1..e10a8b54 100644 --- a/TAS.Avalonia/Controls/EditorControl.axaml.cs +++ b/TAS.Avalonia/Controls/EditorControl.axaml.cs @@ -67,8 +67,10 @@ public EditorControl() { }; int prevLine = 0; - (Application.Current as App).CelesteService.Server.StateUpdated += state => { - if (state.CurrentLine == prevLine) return; + (Application.Current as App)!.CelesteService.Server.StateUpdated += state => { + if (state.CurrentLine == -1 || state.CurrentLine == prevLine) return; + prevLine = state.CurrentLine; + Dispatcher.UIThread.InvokeAsync(() => { const int LinesBelow = 3; @@ -99,7 +101,6 @@ public EditorControl() { view.MakeVisible(new Rect(0, lineTop, 0, lineBottom - lineTop)); } }); - prevLine = state.CurrentLine; }; } diff --git a/TAS.Avalonia/Editing/TASCaretNavigationCommandHandler.cs b/TAS.Avalonia/Editing/TASCaretNavigationCommandHandler.cs index 16ab7e73..21fb07fe 100644 --- a/TAS.Avalonia/Editing/TASCaretNavigationCommandHandler.cs +++ b/TAS.Avalonia/Editing/TASCaretNavigationCommandHandler.cs @@ -20,6 +20,8 @@ internal static class TASCaretNavigationCommandHandler { private static readonly List CommandBindings = new List(); private static readonly List KeyBindings = new List(); + internal static RoutedCommand SelectBlock { get; } = new(nameof(SelectBlock), new KeyGesture(Key.W, TASInputHandler.PlatformCommandKey)); + public static TextAreaInputHandler Create(TextArea textArea) { var areaInputHandler = new TextAreaInputHandler(textArea); areaInputHandler.CommandBindings.AddRange(CommandBindings); @@ -73,6 +75,7 @@ static TASCaretNavigationCommandHandler() { AddBinding(RectangleSelection.BoxSelectToLineEnd, KeyModifiers.Alt | keymap.SelectionModifiers, Key.End, OnMoveCaretBoxSelection(CaretMovementType.LineEnd)); AddBinding(ApplicationCommands.SelectAll, OnSelectAll); + AddBinding(SelectBlock, OnSelectBlock); foreach (KeyGesture gesture in keymap.MoveCursorToTheStartOfLine) { AddBinding(EditingCommands.MoveToLineStart, gesture, OnMoveCaret(CaretMovementType.LineStart)); @@ -115,6 +118,29 @@ private static void OnSelectAll(object target, ExecutedRoutedEventArgs args) { textArea.Selection = Selection.Create(textArea, 0, textArea.Document.TextLength); } + private static void OnSelectBlock(object target, ExecutedRoutedEventArgs args) { + TextArea textArea = GetTextArea(target); + if (textArea?.Document == null) return; + args.Handled = true; + + // Search first empty line above/below caret + int above = textArea.Caret.Line; + DocumentLine aboveLine = null; + while (above >= 1 && !string.IsNullOrWhiteSpace(textArea.Document.GetText(aboveLine = textArea.Document.GetLineByNumber(above)))) { + above--; + } + int below = textArea.Caret.Line; + DocumentLine belowLine = null; + while (below <= textArea.Document.LineCount && !string.IsNullOrWhiteSpace(textArea.Document.GetText(belowLine = textArea.Document.GetLineByNumber(below)))) { + below++; + } + + if (aboveLine == null || belowLine == null) return; + + textArea.Caret.Offset = aboveLine.Offset + 1; + textArea.Selection = Selection.Create(textArea, aboveLine.Offset + 1, belowLine.EndOffset - 1); + } + private static TextArea GetTextArea(object target) => target as TextArea; private static EventHandler OnMoveCaret(CaretMovementType direction) { diff --git a/TAS.Avalonia/Editing/TASEditingCommandHandler.cs b/TAS.Avalonia/Editing/TASEditingCommandHandler.cs index 2282720e..cc837ab7 100644 --- a/TAS.Avalonia/Editing/TASEditingCommandHandler.cs +++ b/TAS.Avalonia/Editing/TASEditingCommandHandler.cs @@ -16,6 +16,15 @@ internal class TASEditingCommandHandler { private static readonly List CommandBindings = new List(); private static readonly List KeyBindings = new List(); + internal static RoutedCommand DeleteLine { get; } = new(nameof(DeleteLine), new KeyGesture(Key.Y, TASInputHandler.PlatformCommandKey)); + + internal static RoutedCommand ToggleCommentInputs { get; } = new(nameof(ToggleCommentInputs), new KeyGesture(Key.K, TASInputHandler.PlatformCommandKey)); + internal static RoutedCommand ToggleCommentText { get; } = new(nameof(ToggleCommentText), new KeyGesture(Key.K, TASInputHandler.PlatformCommandKey | KeyModifiers.Shift)); + + internal static RoutedCommand InsertRoomName { get; } = new(nameof(InsertRoomName), new KeyGesture(Key.R, TASInputHandler.PlatformCommandKey)); + internal static RoutedCommand InsertTime { get; } = new(nameof(InsertRoomName), new KeyGesture(Key.T, TASInputHandler.PlatformCommandKey)); + internal static RoutedCommand InsertCommand { get; } = new(nameof(InsertCommand)); + public static TextAreaInputHandler Create(TextArea textArea) { var areaInputHandler = new TextAreaInputHandler(textArea); areaInputHandler.CommandBindings.AddRange(CommandBindings); @@ -33,6 +42,7 @@ private static void AddBinding(RoutedCommand command, EventHandler $"#lvl_{(Application.Current as App)!.CelesteService.LevelName}")); + AddBinding(InsertTime, OnInsertTextAbove(static _ => $"#{(Application.Current as App)!.CelesteService.ChapterTime}")); + AddBinding(InsertCommand, OnInsertTextAbove(static command => (string)command)); + } + + // TAS specific commands + + private static void OnToggleCommentInputs(object target, ExecutedRoutedEventArgs args) { + TextArea textArea = GetTextArea(target); + if (textArea?.Document == null) return; + + using (textArea.Document.RunUpdate()) { + int startLine = textArea.Selection.StartPosition.Line; + int endLine = textArea.Selection.EndPosition.Line; + if (textArea.Selection.IsEmpty) { + startLine = endLine = textArea.Caret.Line; + } + + // Make sure that start <= end + int tmp = startLine; + startLine = Math.Min(startLine, endLine); + endLine = Math.Max(tmp, endLine); + + for (int i = startLine; i <= endLine; i++) { + var line = textArea.Document.GetLineByNumber(i); + string lineText = textArea.Document.GetText(line); + + if (lineText.TrimStart().StartsWith('#')) { + int hashIdx = lineText.IndexOf('#'); + textArea.Document.Replace(line, lineText.Remove(hashIdx, 1)); + } else { + textArea.Document.Replace(line, $"#{lineText}"); + } + } + } + + textArea.Caret.BringCaretToView(); + args.Handled = true; + } + + private static void OnToggleCommentText(object target, ExecutedRoutedEventArgs args) { + TextArea textArea = GetTextArea(target); + if (textArea?.Document == null) return; + + using (textArea.Document.RunUpdate()) { + int startLine = textArea.Selection.StartPosition.Line; + int endLine = textArea.Selection.EndPosition.Line; + if (textArea.Selection.IsEmpty) { + startLine = endLine = textArea.Caret.Line; + } + + // Make sure that start <= end + int tmp = startLine; + startLine = Math.Min(startLine, endLine); + endLine = Math.Max(tmp, endLine); + + // Only remove # when all lines start with it. Otherwise add another + bool allCommented = true; + for (int i = startLine; i <= endLine; i++) { + var line = textArea.Document.GetLineByNumber(i); + string lineText = textArea.Document.GetText(line); + + if (!lineText.TrimStart().StartsWith('#')) { + allCommented = false; + break; + } + } + for (int i = startLine; i <= endLine; i++) { + var line = textArea.Document.GetLineByNumber(i); + string lineText = textArea.Document.GetText(line); + + if (allCommented) { + int hashIdx = lineText.IndexOf('#'); + textArea.Document.Replace(line, lineText.Remove(hashIdx, 1)); + } else { + textArea.Document.Replace(line, $"#{lineText}"); + } + } + } + + textArea.Caret.BringCaretToView(); + args.Handled = true; } + private static EventHandler OnInsertTextAbove(Func getText) => (target, args) => { + TextArea textArea = GetTextArea(target); + if (textArea?.Document == null) return; + + using (textArea.Document.RunUpdate()) { + var line = textArea.Document.GetLineByNumber(textArea.Caret.Line); + textArea.Document.Insert(line.Offset, $"{getText(args.Parameter)}{Environment.NewLine}"); + } + + textArea.Caret.BringCaretToView(); + args.Handled = true; + }; + + // Editing commands + private static TextArea GetTextArea(object target) => target as TextArea; internal static void AutoFormatActionLines(TextArea textArea, int lineStart, int lineEnd) { diff --git a/TAS.Avalonia/Editing/TASInputHandler.cs b/TAS.Avalonia/Editing/TASInputHandler.cs index 424500fa..1d43da95 100644 --- a/TAS.Avalonia/Editing/TASInputHandler.cs +++ b/TAS.Avalonia/Editing/TASInputHandler.cs @@ -3,16 +3,19 @@ using AvaloniaEdit; using AvaloniaEdit.Document; using AvaloniaEdit.Editing; +using System.Runtime.InteropServices; namespace TAS.Avalonia.Editing; #nullable disable public class TASInputHandler : TextAreaInputHandler { - public TextAreaInputHandler CaretNavigation { get; } + internal static readonly KeyModifiers PlatformCommandKey = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? KeyModifiers.Meta : KeyModifiers.Control; - public TextAreaInputHandler Editing { get; } + internal static RoutedCommand Redo { get; } = new(nameof(Redo), new KeyGesture(Key.Z, PlatformCommandKey | KeyModifiers.Shift)); + public TextAreaInputHandler CaretNavigation { get; } + public TextAreaInputHandler Editing { get; } public ITextAreaInputHandler MouseSelection { get; } private TASInputHandler InputHandler => TextArea.ActiveInputHandler as TASInputHandler; @@ -27,7 +30,7 @@ public TASInputHandler(TextArea textArea) : base(textArea) { NestedInputHandlers.Add(Editing = TASEditingCommandHandler.Create(textArea)); NestedInputHandlers.Add(MouseSelection = new TASSelectionMouseHandler(textArea)); AddBinding(ApplicationCommands.Undo, ExecuteUndo, CanExecuteUndo); - AddBinding(ApplicationCommands.Redo, ExecuteRedo, CanExecuteRedo); + AddBinding(Redo, ExecuteRedo, CanExecuteRedo); } private void AddBinding(RoutedCommand command, EventHandler handler, EventHandler canExecuteHandler = null) { diff --git a/TAS.Avalonia/Models/MenuModel.cs b/TAS.Avalonia/Models/MenuModel.cs index 07a1f292..58628d8e 100644 --- a/TAS.Avalonia/Models/MenuModel.cs +++ b/TAS.Avalonia/Models/MenuModel.cs @@ -2,7 +2,10 @@ using System.Windows.Input; using Avalonia.Controls; using Avalonia.Input; +using AvaloniaEdit; +using AvaloniaEdit.Editing; using DynamicData; +using ReactiveUI; #pragma warning disable CS8601 @@ -20,6 +23,16 @@ public class MenuModel : IEnumerable { public bool IsChecked { get; init; } public bool IsVisible { get; init; } = true; + public MenuModel(string header, RoutedCommand routedCommand, TextArea textArea, object commandParameter = null, bool? isEnabled = null, bool isChecked = false, bool isVisible = true) { + Header = header; + Command = ReactiveCommand.Create(param => routedCommand.Execute(param, textArea)); + CommandParameter = commandParameter; + Gesture = routedCommand.Gesture; + IsEnabled = isEnabled; + IsChecked = isChecked; + IsVisible = isVisible; + } + public MenuModel(string header, ICommand command = null, object commandParameter = null, KeyGesture gesture = null, bool? isEnabled = null, bool isChecked = false, bool isVisible = true) { Header = header; Command = command; diff --git a/TAS.Avalonia/Models/TASDocument.cs b/TAS.Avalonia/Models/TASDocument.cs index cef851bb..09d868c0 100644 --- a/TAS.Avalonia/Models/TASDocument.cs +++ b/TAS.Avalonia/Models/TASDocument.cs @@ -1,3 +1,5 @@ +using Avalonia; +using Avalonia.Threading; using AvaloniaEdit.Document; using ReactiveUI; @@ -30,6 +32,12 @@ private set { private TASDocument(string contents) { Document = new TextDocument(contents); Document.TextChanged += Document_TextChanged; + + (Application.Current as App)!.CelesteService.Server.LinesUpdated += OnLinesUpdated; + } + + ~TASDocument() { + (Application.Current as App)!.CelesteService.Server.LinesUpdated -= OnLinesUpdated; } public static TASDocument CreateBlank() => new TASDocument(EmptyDocument); @@ -61,4 +69,13 @@ public void Save() { private void Document_TextChanged(object sender, EventArgs eventArgs) { Dirty = true; } + + private void OnLinesUpdated(Dictionary lines) { + Dispatcher.UIThread.Post(() => { + foreach ((int lineNum, string newText) in lines) { + var line = Document.GetLineByNumber(lineNum + 1); // 0-indexed + Document.Replace(line, newText); + } + }); + } } diff --git a/TAS.Avalonia/ViewModels/MainWindowViewModel.cs b/TAS.Avalonia/ViewModels/MainWindowViewModel.cs index c2c0de99..4df992a2 100644 --- a/TAS.Avalonia/ViewModels/MainWindowViewModel.cs +++ b/TAS.Avalonia/ViewModels/MainWindowViewModel.cs @@ -2,12 +2,16 @@ using System.Reactive.Linq; using System.Runtime.InteropServices; using Avalonia; +using Avalonia.Controls; using Avalonia.Input; using Avalonia.Platform.Storage; using AvaloniaEdit; using ReactiveUI; +using TAS.Avalonia.Controls; +using TAS.Avalonia.Editing; using TAS.Avalonia.Models; using TAS.Avalonia.Services; +using TAS.Avalonia.Views; namespace TAS.Avalonia.ViewModels; @@ -52,8 +56,8 @@ public class MainWindowViewModel : ViewModelBase { public ReactiveCommand SetFastForwardSpeedCommand { get; } public ReactiveCommand SetSlowForwardSpeedCommand { get; } - // Context - public ReactiveCommand ToggleCommentsCommand { get; } + // Other + public ReactiveCommand ReloadFileCommand { get; } private readonly ObservableAsPropertyHelper _windowTitle; public string WindowTitle => _windowTitle.Value; @@ -86,6 +90,8 @@ public bool GameInfoVisible { public bool MenuVisible => true; //!RuntimeInformation.IsOSPlatform(OSPlatform.OSX); + private readonly EditorControl _editor; + private readonly CelesteService _celesteService; private readonly DialogService _dialogService; private readonly SettingsService _settingsService; @@ -99,7 +105,9 @@ public bool GameInfoVisible { AppleUniformTypeIdentifiers = new[] { "public.item" }, // TODO: replace this with custom }; - public MainWindowViewModel() { + public MainWindowViewModel(MainWindow window) { + _editor = window.FindControl("editor"); + _celesteService = (Application.Current as App)!.CelesteService; _dialogService = (Application.Current as App)!.DialogService; _settingsService = (Application.Current as App)!.SettingsService; @@ -151,8 +159,11 @@ public MainWindowViewModel() { SetFastForwardSpeedCommand = ReactiveCommand.CreateFromTask(SetFastForwardSpeed); SetSlowForwardSpeedCommand = ReactiveCommand.CreateFromTask(SetSlowForwardSpeed); - // Context - ToggleCommentsCommand = ReactiveCommand.Create(ToggleComments); + // Other + ReloadFileCommand = ReactiveCommand.Create(() => { + Document = TASDocument.Load(Document.FilePath); + Console.WriteLine("haii :3"); + }); var lastOpenFilePath = _settingsService.LastOpenFilePath; @@ -188,6 +199,7 @@ private MenuModel[] CreateMenu(bool includeExit) { MenuModel.Separator, new MenuModel("Save", SaveFileCommand, gesture: new KeyGesture(Key.S, commandModifier)), new MenuModel("Save As...", SaveFileAsCommand, gesture: new KeyGesture(Key.S, commandModifier | KeyModifiers.Shift)), + new MenuModel("Reload", ReloadFileCommand, gesture: new KeyGesture(Key.R, KeyModifiers.Alt)), new MenuModel("Convert to LibTAS Inputs..."), new MenuModel(string.Empty, isVisible: includeExit), new MenuModel("Exit", ExitCommand, isVisible: includeExit), @@ -242,57 +254,82 @@ private MenuModel[] CreateMenu(bool includeExit) { } private MenuModel[] CreateContextMenu() => new[] { - new MenuModel("Cut"), - new MenuModel("Copy"), - new MenuModel("Paste"), + new MenuModel("Cut", routedCommand: ApplicationCommands.Cut, textArea: _editor.editor.TextArea), + new MenuModel("Copy", routedCommand: ApplicationCommands.Copy, textArea: _editor.editor.TextArea), + new MenuModel("Paste", routedCommand: ApplicationCommands.Paste, textArea: _editor.editor.TextArea), + MenuModel.Separator, + new MenuModel("Undo", routedCommand: ApplicationCommands.Undo, textArea: _editor.editor.TextArea), + new MenuModel("Redo", routedCommand: TASInputHandler.Redo, textArea: _editor.editor.TextArea), MenuModel.Separator, - new MenuModel("Undo"), - new MenuModel("Redo"), + new MenuModel("Select All", routedCommand: ApplicationCommands.SelectAll, textArea: _editor.editor.TextArea), + new MenuModel("Select Block", routedCommand: TASCaretNavigationCommandHandler.SelectBlock, textArea: _editor.editor.TextArea), MenuModel.Separator, new MenuModel("Insert/Remove Breakpoint"), new MenuModel("Insert/Remove Savestate Breakpoint"), new MenuModel("Remove All Uncommented Breakpoints"), new MenuModel("Remove All Breakpoints"), new MenuModel("Comment/Uncomment All Breakpoints"), + new MenuModel("Comment/Uncomment Inputs", routedCommand: TASEditingCommandHandler.ToggleCommentInputs, textArea: _editor.editor.TextArea), + new MenuModel("Comment/Uncomment Text", routedCommand: TASEditingCommandHandler.ToggleCommentText, textArea: _editor.editor.TextArea), MenuModel.Separator, - new MenuModel("Comment/Uncomment Text", command: ToggleCommentsCommand), - new MenuModel("Insert Room Name"), - new MenuModel("Insert Current In-Game Time"), + new MenuModel("Insert Room Name", routedCommand: TASEditingCommandHandler.InsertRoomName, textArea: _editor.editor.TextArea), + new MenuModel("Insert Current In-Game Time", routedCommand: TASEditingCommandHandler.InsertTime, textArea: _editor.editor.TextArea), new MenuModel("Insert Mod Info"), new MenuModel("Insert Console Load Command"), new MenuModel("Insert Simple Console Load Command"), new MenuModel("Insert Other Command") { - new MenuModel("EnforceLegal"), - new MenuModel("Unsafe"), - new MenuModel("Safe"), + new MenuModel("EnforceLegal", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "EnforceLegal", textArea: _editor.editor.TextArea), + new MenuModel("Unsafe", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "Unsafe", textArea: _editor.editor.TextArea), + new MenuModel("Safe", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "Safe", textArea: _editor.editor.TextArea), + MenuModel.Separator, + new MenuModel("Read", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "Read, File Name, Starting Line, (Ending Line)", textArea: _editor.editor.TextArea), + new MenuModel("Play", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "Play, Starting Line", textArea: _editor.editor.TextArea), + MenuModel.Separator, + new MenuModel("Repeat", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "Repeat 2\n\nEndRepeat", textArea: _editor.editor.TextArea), + new MenuModel("EndRepeat", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "EndRepeat", textArea: _editor.editor.TextArea), MenuModel.Separator, - new MenuModel("Read"), - new MenuModel("Play"), + new MenuModel("Set", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "Set, (Mod).Setting, Value", textArea: _editor.editor.TextArea), + new MenuModel("Invoke", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "Invoke, Entity.Method, Parameter", textArea: _editor.editor.TextArea), + new MenuModel("EvalLua", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "EvalLua, Code", textArea: _editor.editor.TextArea), MenuModel.Separator, - new MenuModel("Repeat"), - new MenuModel("EndRepeat"), + new MenuModel("Press", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "Press, Key1, Key2...", textArea: _editor.editor.TextArea), MenuModel.Separator, - new MenuModel("Set"), + new MenuModel("AnalogMode", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "AnalogMode, Ignore/Circle/Square/Precise", textArea: _editor.editor.TextArea), MenuModel.Separator, - new MenuModel("AnalogMode"), + new MenuModel("StunPause", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "StunPause\n\nEndStunPause", textArea: _editor.editor.TextArea), + new MenuModel("EndStunPause", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "EndStunPause", textArea: _editor.editor.TextArea), + new MenuModel("StunPauseMode", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "StunPauseMode, Simulate/Input", textArea: _editor.editor.TextArea), MenuModel.Separator, - new MenuModel("StartExportGameInfo"), - new MenuModel("FinishExportGameInfo"), + new MenuModel("AutoInput", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "AutoInput, 2\n 1,S,N\n 10,O\nStartAutoInput\n\nEndAutoInput", textArea: _editor.editor.TextArea), + new MenuModel("StartAutoInput", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "StartAutoInput", textArea: _editor.editor.TextArea), + new MenuModel("EndAutoInput", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "EndAutoInput", textArea: _editor.editor.TextArea), + new MenuModel("SkipInput", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "SkipInput", textArea: _editor.editor.TextArea), MenuModel.Separator, - new MenuModel("StartExportRoomInfo"), - new MenuModel("FinishExportRoomInfo"), + new MenuModel("SaveAndQuitReenter", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "SaveAndQuitReenter", textArea: _editor.editor.TextArea), MenuModel.Separator, - new MenuModel("Add"), - new MenuModel("Skip"), - new MenuModel("StartExportLibTAS"), - new MenuModel("FinishExportLibTAS"), + new MenuModel("ExportGameInfo", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "ExportGameInfo dump.txt", textArea: _editor.editor.TextArea), + new MenuModel("EndExportGameInfo", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "EndExportGameInfo", textArea: _editor.editor.TextArea), MenuModel.Separator, - new MenuModel("CompleteInfo"), - new MenuModel("Record Count"), - new MenuModel("File Time"), - new MenuModel("Chapter Time"), + new MenuModel("ExportRoomInfo", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "ExportRoomInfo dump_room_info.txt", textArea: _editor.editor.TextArea), + new MenuModel("EndExportRoomInfo", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "EndExportRoomInfo", textArea: _editor.editor.TextArea), MenuModel.Separator, - new MenuModel("Exit Game"), + new MenuModel("StartRecording", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "StartRecording", textArea: _editor.editor.TextArea), + new MenuModel("StopRecording", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "StopRecording", textArea: _editor.editor.TextArea), + MenuModel.Separator, + new MenuModel("Add", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "Add, (input line)", textArea: _editor.editor.TextArea), + new MenuModel("Skip", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "Skip", textArea: _editor.editor.TextArea), + new MenuModel("Marker", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "Marker", textArea: _editor.editor.TextArea), + new MenuModel("ExportLibTAS", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "ExportLibTAS Celeste.ltm", textArea: _editor.editor.TextArea), + new MenuModel("EndExportLibTAS", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "EndExportLibTAS", textArea: _editor.editor.TextArea), + MenuModel.Separator, + new MenuModel("CompleteInfo", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "CompleteInfo A 1", textArea: _editor.editor.TextArea), + new MenuModel("RecordCount", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "RecordCount: 1", textArea: _editor.editor.TextArea), + new MenuModel("FileTime", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "FileTime:", textArea: _editor.editor.TextArea), + new MenuModel("ChapterTime", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "ChapterTime:", textArea: _editor.editor.TextArea), + new MenuModel("MidwayFileTime", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "MidwayFileTime:", textArea: _editor.editor.TextArea), + new MenuModel("MidwayChapterTime", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "MidwayChapterTime:", textArea: _editor.editor.TextArea), + MenuModel.Separator, + new MenuModel("Exit Game", routedCommand: TASEditingCommandHandler.InsertCommand, commandParameter: "EnforceLegal", textArea: _editor.editor.TextArea), }, MenuModel.Separator, new MenuModel("Swap Selected C and X"), @@ -423,7 +460,4 @@ private async Task SaveFileAsAsync(bool force) { } private void Exit() => Application.Current?.DesktopLifetime().Shutdown(); - - private void ToggleComments() { - } }