Files
2026-07-13 13:13:17 +08:00

184 lines
7.7 KiB
C#

#nullable enable
using ImGuiNET;
using T3.Editor.Gui.UiHelpers;
using T3.SystemUi;
namespace T3.Editor.Gui.Interaction.Keyboard;
/// <summary>
/// A static class that helps to trigger user actions with assigned keyboard shortcuts
/// </summary>
internal static class KeyActionHandling
{
//private static readonly List<KeyBinding> _bindings = [];
private static bool _initialized;
private static bool _anyKeysPressed;
internal static void InitializeFrame()
{
if (!_initialized)
{
InitializeActionOptions();
_initialized = true;
}
// ImGui's legacy KeysDown[] array was removed in 1.90, so we use TiXL's
// own KeyHandler state (populated by WndProc) as the source of truth.
_anyKeysPressed = false;
var pressedKeys = KeyHandler.PressedKeys;
for (var index = 0; index < pressedKeys.Count; index++)
{
if (!pressedKeys[index])
continue;
_anyKeysPressed = true;
break;
}
}
internal static bool Triggered(this UserActions action)
{
if (!_anyKeysPressed || !UserSettings.Config.EnableKeyboardShortCuts)
return false;
var io = ImGui.GetIO();
// Todo: Refactor this with lookup list
foreach (var binding in KeyMapSwitching.CurrentKeymap.Bindings)
{
if (binding.Action != action)
continue;
if (!binding.IsContextValid())
continue;
if (binding.IsTriggered(io))
return true;
}
return false;
}
internal static string ListShortcuts(this UserActions action)
{
var currentKeymapShortCutsLabelsForActions = KeyMapSwitching.CurrentKeymap.ShortCutsLabelsForActions;
var index = (int)action;
if (index >= currentKeymapShortCutsLabelsForActions.Length)
{
return string.Empty;
}
return currentKeymapShortCutsLabelsForActions[index];
}
internal static string ListKeyboardShortcutsForActionWithLabel(this UserActions action)
{
var shortCuts = action.ListShortcuts();
if (string.IsNullOrEmpty(shortCuts))
return shortCuts;
var prefix = shortCuts.Contains(" and ") ? "Shortcuts: " : "Shortcut: ";
return prefix + shortCuts;
}
internal static Flags GetActionFlags(UserActions action)
{
var index = (int)action;
return index >= _flagsForActions.Length ? Flags.None : _flagsForActions[index];
}
[Flags]
public enum Flags
{
None = 0,
NeedsWindowFocus = 1 << 1,
NeedsWindowHover = 1 << 2,
RemainActiveWhenItemActive = 1<<3, // Most shortcuts should be disabled when some input item is active, which few exceptions like WASD camera short cuts
KeyPressOnly = 1 << 4,
KeyHoldOnly = 1 << 5,
}
/// <summary>
/// Register special flags for user actions
/// </summary>
private static void InitializeActionOptions()
{
RegisterActionsFlags(UserActions.Save, Flags.KeyPressOnly);
RegisterActionsFlags(UserActions.FocusSelection, Flags.NeedsWindowHover);
RegisterActionsFlags(UserActions.Duplicate, Flags.NeedsWindowFocus);
RegisterActionsFlags(UserActions.DuplicateWithConnections, Flags.NeedsWindowFocus);
RegisterActionsFlags(UserActions.DeleteSelection, Flags.NeedsWindowFocus);
RegisterActionsFlags(UserActions.DeleteSelection, Flags.NeedsWindowFocus);
RegisterActionsFlags(UserActions.CopyToClipboard, Flags.NeedsWindowFocus);
RegisterActionsFlags(UserActions.PasteFromClipboard, Flags.NeedsWindowFocus);
RegisterActionsFlags(UserActions.PasteValues, Flags.NeedsWindowFocus);
RegisterActionsFlags(UserActions.Undo, Flags.KeyPressOnly);
RegisterActionsFlags(UserActions.Redo, Flags.KeyPressOnly);
// Playback controls
RegisterActionsFlags(UserActions.PlaybackForward, Flags.KeyPressOnly);
RegisterActionsFlags(UserActions.PlaybackForwardHalfSpeed, Flags.KeyPressOnly);
RegisterActionsFlags(UserActions.PlaybackBackwards, Flags.KeyPressOnly);
RegisterActionsFlags(UserActions.PlaybackStop, Flags.KeyPressOnly);
RegisterActionsFlags(UserActions.PlaybackToggle, Flags.KeyPressOnly);
RegisterActionsFlags(UserActions.PlaybackJumpToStartTime, Flags.KeyPressOnly);
RegisterActionsFlags(UserActions.PlaybackScrubTime, Flags.KeyHoldOnly);
// Timeline actions
RegisterActionsFlags(UserActions.InsertKeyframe, Flags.NeedsWindowFocus | Flags.KeyPressOnly);
RegisterActionsFlags(UserActions.SetStartTime, Flags.KeyPressOnly);
RegisterActionsFlags(UserActions.SetEndTime, Flags.KeyPressOnly);
RegisterActionsFlags(UserActions.TapBeatSync, Flags.KeyPressOnly);
RegisterActionsFlags(UserActions.TapBeatSyncMeasure, Flags.KeyPressOnly);
// Graph window actions
RegisterActionsFlags(UserActions.ToggleDisabled, Flags.NeedsWindowFocus);
RegisterActionsFlags(UserActions.ToggleBypassed, Flags.NeedsWindowFocus);
RegisterActionsFlags(UserActions.PinToOutputWindow, Flags.KeyPressOnly);
RegisterActionsFlags(UserActions.ClearBackgroundImage, Flags.NeedsWindowFocus);
RegisterActionsFlags(UserActions.AddSection, Flags.NeedsWindowFocus | Flags.KeyPressOnly);
RegisterActionsFlags(UserActions.AddComment, Flags.NeedsWindowFocus);
RegisterActionsFlags(UserActions.OpenOperator, Flags.NeedsWindowFocus);
RegisterActionsFlags(UserActions.CloseOperator, Flags.NeedsWindowFocus);
//RegisterActionsFlags(UserActions.RenameChild, Flags.NeedsWindowFocus);
// Navigation
RegisterActionsFlags(UserActions.SelectToAbove, Flags.NeedsWindowFocus);
RegisterActionsFlags(UserActions.SelectToRight, Flags.NeedsWindowFocus);
RegisterActionsFlags(UserActions.SelectToBelow, Flags.NeedsWindowFocus);
RegisterActionsFlags(UserActions.SelectToLeft, Flags.NeedsWindowFocus);
// Camera controls
RegisterActionsFlags(UserActions.CameraLeft, Flags.NeedsWindowHover | Flags.KeyHoldOnly | Flags.RemainActiveWhenItemActive);
RegisterActionsFlags(UserActions.CameraRight, Flags.NeedsWindowHover | Flags.KeyHoldOnly | Flags.RemainActiveWhenItemActive);
RegisterActionsFlags(UserActions.CameraForward, Flags.NeedsWindowHover | Flags.KeyHoldOnly | Flags.RemainActiveWhenItemActive);
RegisterActionsFlags(UserActions.CameraBackward, Flags.NeedsWindowHover | Flags.KeyHoldOnly | Flags.RemainActiveWhenItemActive);
RegisterActionsFlags(UserActions.CameraUp, Flags.NeedsWindowHover | Flags.KeyHoldOnly | Flags.RemainActiveWhenItemActive);
RegisterActionsFlags(UserActions.CameraDown, Flags.NeedsWindowHover | Flags.KeyHoldOnly | Flags.RemainActiveWhenItemActive);
// Camera reset and focus
RegisterActionsFlags(UserActions.CameraReset, Flags.NeedsWindowHover);
RegisterActionsFlags(UserActions.CameraFocusSelection, Flags.NeedsWindowHover);
RegisterActionsFlags(UserActions.RenderAnimation, Flags.KeyPressOnly);
return;
void RegisterActionsFlags(UserActions action, Flags flags)
{
var index = (int)action;
if (index >= _flagsForActions.Length)
{
Log.Warning($"Action index {index} for {action} exceeds expected max index {_flagsForActions.Length}");
return;
}
_flagsForActions[index] = flags;
}
}
private const int UserActionsCount = (int)UserActions.__Count;
private static readonly Flags[] _flagsForActions = new Flags[UserActionsCount];
}