diff --git a/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.ListView.cs b/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.ListView.cs
index 6d09877b733..e6ed61dcf94 100644
--- a/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.ListView.cs
+++ b/src/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.ListView.cs
@@ -370,6 +370,45 @@ private bool TasView_QueryFrameLag(int index, bool hideWasLag)
return (lag.Lagged.HasValue && lag.Lagged.Value) || (hideWasLag && lag.WasLagged.HasValue && lag.WasLagged.Value);
}
+ ///
+ /// list of row indices sorted ascending, as returned by TasView.SelectedRows,
+ /// and must not be empty (so there must be a selection)
+ ///
+ /// fills with (unheld) if held on all selected frames, (held) otherwise
+ private void ToggleButtonStateOnFrames(int[] selection, string buttonName)
+ {
+ // nifty taseditor logic
+ // your TAS Editor logic failed us (it didn't account for non-contiguous `SelectedRows`) --yoshi
+ var allPressed = selection.All(index => CurrentTasMovie.BoolIsPressed(index, buttonName))
+ && selection[selection.Length - 1] != CurrentTasMovie.FrameCount; // last movie frame can't have input, but can be selected
+ var iSelection = 0;
+ var lastFrameIndexSeen = selection[iSelection] - 1;
+ while (iSelection < selection.Length)
+ {
+ var index = selection[iSelection];
+ if (index - lastFrameIndexSeen is not 1) break;
+ lastFrameIndexSeen = index;
+ iSelection++;
+ }
+ // `iSelection` now points to the element after the last of the first contiguous block--if the whole selection is contiguous, it's pointing after the end of the array...
+ CurrentTasMovie.SetBoolStates(frame: selection[0], count: iSelection, buttonName, val: !allPressed);
+ var blockStart = iSelection;
+ lastFrameIndexSeen = selection[iSelection] - 1;
+ while (iSelection < selection.Length) // ...and these will be equal, so the loop will end immediately
+ {
+ var index = selection[iSelection];
+ if (index - lastFrameIndexSeen is not 1)
+ {
+ // discontinuity; split off another block, and this is now the start of the next block
+ CurrentTasMovie.SetBoolStates(frame: selection[blockStart], count: iSelection - blockStart, buttonName, val: !allPressed);
+ blockStart = iSelection;
+ }
+ lastFrameIndexSeen = index;
+ iSelection++;
+ }
+ CurrentTasMovie.SetBoolStates(frame: selection[blockStart], count: iSelection - blockStart, buttonName, val: !allPressed); // `count` arg will be 0 if the whole selection is contiguous
+ }
+
private void TasView_ColumnClick(object sender, InputRoll.ColumnClickEventArgs e)
{
if (TasView.AnyRowsSelected)
@@ -382,25 +421,13 @@ private void TasView_ColumnClick(object sender, InputRoll.ColumnClickEventArgs e
}
else if (columnName != CursorColumnName)
{
- var frame = TasView.AnyRowsSelected ? TasView.FirstSelectedRowIndex : 0;
var buttonName = TasView.CurrentCell.Column!.Name;
if (ControllerType.BoolButtons.Contains(buttonName))
{
if (ModifierKeys != Keys.Alt)
{
- // nifty taseditor logic
- bool allPressed = true;
- foreach (var index in TasView.SelectedRows)
- {
- if (index == CurrentTasMovie.FrameCount // last movie frame can't have input, but can be selected
- || !CurrentTasMovie.BoolIsPressed(index, buttonName))
- {
- allPressed = false;
- break;
- }
- }
- CurrentTasMovie.SetBoolStates(frame, TasView.SelectedRows.Count(), buttonName, !allPressed);
+ ToggleButtonStateOnFrames(TasView.SelectedRows.ToArray(), buttonName);
}
else
{