diff --git a/src/BizHawk.Client.Common/config/Binding.cs b/src/BizHawk.Client.Common/config/Binding.cs index 5f3e1894654..867e25442b0 100644 --- a/src/BizHawk.Client.Common/config/Binding.cs +++ b/src/BizHawk.Client.Common/config/Binding.cs @@ -19,11 +19,13 @@ static HotkeyInfo() var dict = new Dictionary(); var i = 0; #if true - void Bind(string tabGroup, string displayName, string defaultBinding = "", string toolTip = "") - => dict.Add(displayName, new(tabGroup: tabGroup, i++, displayName: displayName, toolTip: toolTip, defaultBinding: defaultBinding)); + // Note (2025-10-23): Having bindings init with an intended scope would be better than merely marking overlaps with defaultBindingOverlaps + // e.g., for Ctrl+C overlaps (like MainForm vs. TAStudio), there's other code that checks which UI is focused, but consolidating such scope here on init would be better. --RetroEdit + void Bind(string tabGroup, string displayName, string defaultBinding = "", string toolTip = "", bool defaultBindingOverlaps = false) + => dict.Add(displayName, new(tabGroup: tabGroup, i++, displayName: displayName, toolTip: toolTip, defaultBinding: defaultBinding, defaultBindingOverlaps: defaultBindingOverlaps)); #else //TODO switch to a sort key more resilient than the DisplayName, like with this example (need to update `Config.HotkeyBindings["A Hotkey"]` usages across codebase; please switch it to a `Config.GetHotkeyBindings` method so it can return "") void Bind(string tabGroup, string displayName, string defaultBinding = "", string toolTip = "") - => dict.Add($"{tabGroup}__{displayName}".Replace(" ", ""), new(tabGroup: tabGroup, i++, displayName: displayName, toolTip: toolTip, defaultBinding: defaultBinding)); + => dict.Add($"{tabGroup}__{displayName}".Replace(" ", ""), new(tabGroup: tabGroup, i++, displayName: displayName, toolTip: toolTip, defaultBinding: defaultBinding, defaultBindingOverlaps: defaultBindingOverlaps)); #endif Bind("General", "Frame Advance", "F"); @@ -61,7 +63,7 @@ void Bind(string tabGroup, string displayName, string defaultBinding = "", strin Bind("General", "Reboot Core", "Ctrl+R"); Bind("General", "Toggle Sound"); Bind("General", "Exit Program"); - Bind("General", "Screen Raw to Clipboard", "Ctrl+C"); + Bind("General", "Screen Raw to Clipboard", "Ctrl+C", defaultBindingOverlaps: true); Bind("General", "Screen Client to Clipboard", "Ctrl+Shift+C"); Bind("General", "Toggle Skip Lag Frame"); Bind("General", "Toggle Key Priority"); @@ -151,12 +153,17 @@ void Bind(string tabGroup, string displayName, string defaultBinding = "", strin Bind("TAStudio", "Sel. bet. Markers", "Ctrl+A"); Bind("TAStudio", "Select All", "Ctrl+Shift+A"); Bind("TAStudio", "Reselect Clip.", "Ctrl+B"); + Bind("TAStudio", "Copy Frames", "Ctrl+C", defaultBindingOverlaps: true); + Bind("TAStudio", "Paste Frames", "Ctrl+V"); + Bind("TAStudio", "Paste Insert Frames", "Ctrl+Shift+V"); + Bind("TAStudio", "Cut Frames", "Ctrl+X"); Bind("TAStudio", "Clear Frames", "Delete"); Bind("TAStudio", "Delete Frames", "Ctrl+Delete"); Bind("TAStudio", "Insert Frame", "Insert"); Bind("TAStudio", "Insert # Frames", "Shift+Insert"); Bind("TAStudio", "Clone Frames", "Ctrl+Insert"); Bind("TAStudio", "Clone # Times", "Ctrl+Shift+Insert"); + Bind("TAStudio", "State Hist. Integrity Check", "Ctrl+Shift+I"); Bind("TAStudio", "Analog Increment", "Up"); Bind("TAStudio", "Analog Decrement", "Down"); Bind("TAStudio", "Analog Incr. by 10", "Shift+Up"); @@ -195,20 +202,20 @@ void Bind(string tabGroup, string displayName, string defaultBinding = "", strin Bind("NDS", "Swap Screens"); Bind("RAIntegration", "Open RA Overlay", "Escape"); - Bind("RAIntegration", "RA Up", "Up"); - Bind("RAIntegration", "RA Down", "Down"); - Bind("RAIntegration", "RA Left", "Left"); - Bind("RAIntegration", "RA Right", "Right"); - Bind("RAIntegration", "RA Confirm", "X"); - Bind("RAIntegration", "RA Cancel", "Z"); - Bind("RAIntegration", "RA Quit", "Backspace"); + Bind("RAIntegration", "RA Up", "Up", defaultBindingOverlaps: true); + Bind("RAIntegration", "RA Down", "Down", defaultBindingOverlaps: true); + Bind("RAIntegration", "RA Left", "Left", defaultBindingOverlaps: true); + Bind("RAIntegration", "RA Right", "Right", defaultBindingOverlaps: true); + Bind("RAIntegration", "RA Confirm", "X", defaultBindingOverlaps: true); + Bind("RAIntegration", "RA Cancel", "Z", defaultBindingOverlaps: true); + Bind("RAIntegration", "RA Quit", "Backspace", defaultBindingOverlaps: true); AllHotkeys = dict; Groupings = dict.Values.Select(static info => info.TabGroup).Distinct().ToList(); #if DEBUG var bindings = dict.Values - .Where(static info => !info.DisplayName.StartsWith("RA ") && !string.IsNullOrEmpty(info.DefaultBinding)) + .Where(static info => !info.DefaultBindingOverlapCheckOverride && !string.IsNullOrEmpty(info.DefaultBinding)) .Select(static info => info.DefaultBinding) .ToArray(); Debug.Assert(bindings.Distinct().CountIsExactly(bindings.Length), "Do not default bind multiple hotkeys to the same button combination."); @@ -223,6 +230,8 @@ public static void ResolveWithDefaults(IDictionary dict) public readonly string DefaultBinding; + public readonly bool DefaultBindingOverlapCheckOverride; + public readonly string DisplayName; public readonly int Ordinal; @@ -231,13 +240,14 @@ public static void ResolveWithDefaults(IDictionary dict) public readonly string ToolTip; - private HotkeyInfo(string tabGroup, int ordinal, string displayName, string toolTip, string defaultBinding) + private HotkeyInfo(string tabGroup, int ordinal, string displayName, string toolTip, string defaultBinding, bool defaultBindingOverlaps) { DefaultBinding = defaultBinding; DisplayName = displayName; Ordinal = ordinal; TabGroup = tabGroup; ToolTip = toolTip; + DefaultBindingOverlapCheckOverride = defaultBindingOverlaps; } } } diff --git a/src/BizHawk.Client.EmuHawk/MainForm.Hotkey.cs b/src/BizHawk.Client.EmuHawk/MainForm.Hotkey.cs index 403efee375c..b584df5158f 100644 --- a/src/BizHawk.Client.EmuHawk/MainForm.Hotkey.cs +++ b/src/BizHawk.Client.EmuHawk/MainForm.Hotkey.cs @@ -6,6 +6,11 @@ namespace BizHawk.Client.EmuHawk { public partial class MainForm { + private bool TAStudioFocused() + { + return Tools.IsLoaded() && Tools.Get().ContainsFocus; + } + private bool CheckHotkey(string trigger) { void SelectAndSaveToSlot(int slot) @@ -77,7 +82,7 @@ void SelectAndLoadFromSlot(int slot) case "Screen Raw to Clipboard": // Ctrl+C clash. any tool that has such acc must check this. // maybe check if mainform has focus instead? - if (!(Tools.IsLoaded() && Tools.Get().ContainsFocus)) TakeScreenshotToClipboard(); + if (!TAStudioFocused()) TakeScreenshotToClipboard(); break; case "Screen Client to Clipboard": TakeScreenshotClientToClipboard(); @@ -450,6 +455,24 @@ void SelectAndLoadFromSlot(int slot) if (!Tools.IsLoaded()) return false; Tools.TAStudio.ReselectClipboardExternal(); break; + case "Copy Frames": + // This one at least *needs* to check for TAStudio focus, + // specifically to avoid Ctrl+C conflicts + if (!TAStudioFocused()) return false; + Tools.TAStudio.CopyFramesExternal(); + break; + case "Paste Frames": + if (!TAStudioFocused()) return false; + Tools.TAStudio.PasteFramesExternal(); + break; + case "Paste Insert Frames": + if (!TAStudioFocused()) return false; + Tools.TAStudio.PasteInsertFramesExternal(); + break; + case "Cut Frames": + if (!TAStudioFocused()) return false; + Tools.TAStudio.CutFramesExternal(); + break; case "Clear Frames": if (!Tools.IsLoaded()) return false; Tools.TAStudio.ClearFramesExternal(); @@ -474,6 +497,10 @@ void SelectAndLoadFromSlot(int slot) if (!Tools.IsLoaded()) return false; Tools.TAStudio.CloneFramesXTimesExternal(); break; + case "State Hist. Integrity Check": + if (!TAStudioFocused()) return false; + Tools.TAStudio.StateHistoryIntegrityCheckExternal(); + break; case "Analog Increment": if (!Tools.IsLoaded()) return false; Tools.TAStudio.AnalogIncrementByOne(); diff --git a/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.Designer.cs b/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.Designer.cs index 14575f8f6cc..70dd64157f1 100644 --- a/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.Designer.cs +++ b/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.Designer.cs @@ -405,26 +405,21 @@ private void InitializeComponent() // // CopyMenuItem // - this.CopyMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.C))); this.CopyMenuItem.Text = "Copy"; this.CopyMenuItem.Click += new System.EventHandler(this.CopyMenuItem_Click); // // PasteMenuItem // - this.PasteMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.V))); this.PasteMenuItem.Text = "&Paste"; this.PasteMenuItem.Click += new System.EventHandler(this.PasteMenuItem_Click); // // PasteInsertMenuItem // - this.PasteInsertMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)(((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Shift) - | System.Windows.Forms.Keys.V))); this.PasteInsertMenuItem.Text = "&Paste Insert"; this.PasteInsertMenuItem.Click += new System.EventHandler(this.PasteInsertMenuItem_Click); // // CutMenuItem // - this.CutMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.X))); this.CutMenuItem.Text = "&Cut"; this.CutMenuItem.Click += new System.EventHandler(this.CutMenuItem_Click); // @@ -472,8 +467,6 @@ private void InitializeComponent() // // StateHistoryIntegrityCheckMenuItem // - this.StateHistoryIntegrityCheckMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)(((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Shift) - | System.Windows.Forms.Keys.I))); this.StateHistoryIntegrityCheckMenuItem.Text = "State History Integrity Check"; this.StateHistoryIntegrityCheckMenuItem.Click += new System.EventHandler(this.StateHistoryIntegrityCheckMenuItem_Click); // @@ -1056,25 +1049,21 @@ private void InitializeComponent() // // copyToolStripMenuItem // - this.copyToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+C"; this.copyToolStripMenuItem.Text = "Copy"; this.copyToolStripMenuItem.Click += new System.EventHandler(this.CopyMenuItem_Click); // // pasteToolStripMenuItem // - this.pasteToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+V"; this.pasteToolStripMenuItem.Text = "Paste"; this.pasteToolStripMenuItem.Click += new System.EventHandler(this.PasteMenuItem_Click); // // pasteInsertToolStripMenuItem // - this.pasteInsertToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+Shift+V"; this.pasteInsertToolStripMenuItem.Text = "Paste Insert"; this.pasteInsertToolStripMenuItem.Click += new System.EventHandler(this.PasteInsertMenuItem_Click); // // cutToolStripMenuItem // - this.cutToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+X"; this.cutToolStripMenuItem.Text = "Cut"; this.cutToolStripMenuItem.Click += new System.EventHandler(this.CutMenuItem_Click); // diff --git a/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.cs b/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.cs index b3904e8ef77..722be975ec8 100644 --- a/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.cs +++ b/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.cs @@ -226,14 +226,23 @@ public override void HandleHotkeyUpdate() SelectBetweenMarkersMenuItem.ShortcutKeyDisplayString = Config.HotkeyBindings["Sel. bet. Markers"]; SelectAllMenuItem.ShortcutKeyDisplayString = Config.HotkeyBindings["Select All"]; ReselectClipboardMenuItem.ShortcutKeyDisplayString = Config.HotkeyBindings["Reselect Clip."]; + CopyMenuItem.ShortcutKeyDisplayString = Config.HotkeyBindings["Copy Frames"]; + PasteMenuItem.ShortcutKeyDisplayString = Config.HotkeyBindings["Paste Frames"]; + PasteInsertMenuItem.ShortcutKeyDisplayString = Config.HotkeyBindings["Paste Insert Frames"]; + CutMenuItem.ShortcutKeyDisplayString = Config.HotkeyBindings["Cut Frames"]; ClearFramesMenuItem.ShortcutKeyDisplayString = Config.HotkeyBindings["Clear Frames"]; DeleteFramesMenuItem.ShortcutKeyDisplayString = Config.HotkeyBindings["Delete Frames"]; InsertFrameMenuItem.ShortcutKeyDisplayString = Config.HotkeyBindings["Insert Frame"]; InsertNumFramesMenuItem.ShortcutKeyDisplayString = Config.HotkeyBindings["Insert # Frames"]; CloneFramesMenuItem.ShortcutKeyDisplayString = Config.HotkeyBindings["Clone Frames"]; CloneFramesXTimesMenuItem.ShortcutKeyDisplayString = Config.HotkeyBindings["Clone # Times"]; + StateHistoryIntegrityCheckMenuItem.ShortcutKeyDisplayString = Config.HotkeyBindings["State Hist. Integrity Check"]; SelectBetweenMarkersContextMenuItem.ShortcutKeyDisplayString = Config.HotkeyBindings["Sel. bet. Markers"]; + copyToolStripMenuItem.ShortcutKeyDisplayString = Config.HotkeyBindings["Copy Frames"]; + pasteToolStripMenuItem.ShortcutKeyDisplayString = Config.HotkeyBindings["Paste Frames"]; + pasteInsertToolStripMenuItem.ShortcutKeyDisplayString = Config.HotkeyBindings["Paste Insert Frames"]; + cutToolStripMenuItem.ShortcutKeyDisplayString = Config.HotkeyBindings["Cut Frames"]; ClearContextMenuItem.ShortcutKeyDisplayString = Config.HotkeyBindings["Clear Frames"]; DeleteFramesContextMenuItem.ShortcutKeyDisplayString = Config.HotkeyBindings["Delete Frames"]; InsertFrameContextMenuItem.ShortcutKeyDisplayString = Config.HotkeyBindings["Insert Frame"]; @@ -485,6 +494,21 @@ public void AddColumn(string name, string text, int widthUnscaled) public void LoadBranchByIndex(int index) => BookMarkControl.LoadBranchExternal(index); + public void CopyFramesExternal() + => CopyMenuItem_Click(null, EventArgs.Empty); + + public void PasteFramesExternal() + => PasteMenuItem_Click(null, EventArgs.Empty); + + public void PasteInsertFramesExternal() + => PasteInsertMenuItem_Click(null, EventArgs.Empty); + + public void CutFramesExternal() + => CutMenuItem_Click(null, EventArgs.Empty); + + public void StateHistoryIntegrityCheckExternal() + => StateHistoryIntegrityCheckMenuItem_Click(null, EventArgs.Empty); + public void ClearFramesExternal() => ClearFramesMenuItem_Click(null, EventArgs.Empty);