// Source: https://gist.githubusercontent.com/s-ilent/067d4e477041d52d2f8eabd78927d1a4/raw/568c222044a8ce3c33426c495cccbfe7c3a0c733/LinuxTabFix.cs #if UNITY_EDITOR using UnityEditor; using UnityEngine; using UnityEngine.UIElements; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; /// /// Linux Editor Tab Navigation Patch for Unity (2022.3+, Unity 6). /// Restores missing ASCII '\t' character codes stripped by Linux window backend /// and manages focus handshakes between IMGUIContainers in UI Toolkit windows. /// [InitializeOnLoad] public static class LinuxTabFix { private static readonly ConditionalWeakTable patchedContainers = new ConditionalWeakTable(); static LinuxTabFix() { // Ensure idempotent event subscription across domain reloads EditorApplication.update -= ApplyTabPatchToFocusedWindow; EditorApplication.update += ApplyTabPatchToFocusedWindow; } private static void ApplyTabPatchToFocusedWindow() { EditorWindow focusedWindow = EditorWindow.focusedWindow; if (focusedWindow == null) return; VisualElement root = focusedWindow.rootVisualElement; if (root == null) return; List containers = root.Query().ToList(); int count = containers.Count; for (int i = 0; i < count; i++) { IMGUIContainer container = containers[i]; if (container == null) continue; int currentIndex = i; // Patch unhandled containers without modifying container properties if (!patchedContainers.TryGetValue(container, out _)) { patchedContainers.Add(container, null); Action originalHandler = container.onGUIHandler; container.onGUIHandler = () => { Event e = Event.current; if (e != null && e.type == EventType.KeyDown && e.keyCode == KeyCode.Tab) { // Restore character code stripped by Linux native window backend if (e.character == 0) { e.character = '\t'; } bool isShift = e.shift; // Execute original IMGUI pass originalHandler?.Invoke(); // When boundary is reached, transfer focus to adjacent container if (GUIUtility.keyboardControl == 0) { int targetIndex = isShift ? currentIndex - 1 : currentIndex + 1; if (targetIndex >= 0 && targetIndex < count && targetIndex < containers.Count) { IMGUIContainer targetContainer = containers[targetIndex]; if (targetContainer != null) { targetContainer.Focus(); focusedWindow.Repaint(); } } } return; } originalHandler?.Invoke(); }; } } } } #endif