using System; using Codice.Utils; using PlasticGui; using Unity.PlasticSCM.Editor.UI; using Unity.PlasticSCM.Editor.UI.UIElements; using UnityEditor; using UnityEditor.UIElements; using UnityEngine; using UnityEngine.UIElements; namespace Unity.PlasticSCM.Editor.Views.BranchExplorer { internal class BrExSearchField : VisualElement { internal string Text { get { return mSearchField.value; } set { mSearchField.value = value; } } internal event Action DelayedSearchTextChanged; internal event Action NextSearchResultRequested; internal event Action PreviousSearchResultRequested; internal BrExSearchField() { CreateGUI(); mSearchRunner = new DelayedActionBySecondsRunner( OnDelayedSearchChanged, UnityConstants.SEARCH_DELAYED_INPUT_ACTION_INTERVAL); } internal void Dispose() { mSearchField.UnregisterValueChangedCallback(OnSearchFieldChanged); mSearchField.UnregisterCallback(OnSearchFieldKeyDown, TrickleDown.TrickleDown); mPreviousButton.UnregisterCallback(OnNavigationButtonMouseEnter); mPreviousButton.UnregisterCallback(OnNavigationButtonMouseLeave); mNextButton.UnregisterCallback(OnNavigationButtonMouseEnter); mNextButton.UnregisterCallback(OnNavigationButtonMouseLeave); } internal void FocusSearchField() { mSearchField.Focus(); } internal void UpdateSearchCounter(int currentIndex, int totalResults) { if (string.IsNullOrEmpty(mSearchField.value)) return; if (totalResults == 0) { mSearchCounterLabel.text = "0/0"; mPreviousButton.SetEnabled(false); mNextButton.SetEnabled(false); } else { mSearchCounterLabel.text = string.Format("{0}/{1}", currentIndex, totalResults); mPreviousButton.SetEnabled(true); mNextButton.SetEnabled(true); } } void OnDelayedSearchChanged() { DelayedSearchTextChanged?.Invoke(); } void CreateGUI() { style.flexDirection = FlexDirection.Row; style.alignItems = Align.Center; mSearchField = BuildSearchField(); Add(mSearchField); var overlayContainer = BuildOverlayContainer(); Add(overlayContainer); } ToolbarSearchField BuildSearchField() { var searchField = new ToolbarSearchField(); searchField.style.width = 220; searchField.style.flexGrow = 1; searchField.tooltip = string.Format("{0}{1}{2}", PlasticLocalization.Name.BranchExplorerSearchFieldTooltip.GetString(), Environment.NewLine, GetSearchTextBoxShortcutsTooltipString()); searchField.RegisterValueChangedCallback(OnSearchFieldChanged); searchField.RegisterCallback(OnSearchFieldKeyDown, TrickleDown.TrickleDown); var textField = searchField.Q(); if (textField != null) { textField.style.paddingRight = 55; textField.SetTextCursor(); } return searchField; } VisualElement BuildOverlayContainer() { var container = new VisualElement(); container.style.position = Position.Absolute; container.style.right = 18; container.style.top = 0; container.style.bottom = 0; container.style.flexDirection = FlexDirection.Row; container.style.alignItems = Align.Center; container.pickingMode = PickingMode.Ignore; mSearchCounterLabel = BuildSearchCounterLabel(); container.Add(mSearchCounterLabel); mPreviousButton = CreateNavigationButton( OnPreviousButtonClicked, PlasticLocalization.Name.PreviousLabel.GetString(), Images.GetArrowCaretLeftIcon(), marginRight: 2); container.Add(mPreviousButton); mNextButton = CreateNavigationButton( OnNextButtonClicked, PlasticLocalization.Name.NextLabel.GetString(), Images.GetArrowCaretRightIcon()); container.Add(mNextButton); return container; } Label BuildSearchCounterLabel() { var label = new Label(); label.style.color = UnityStyles.Colors.SecondaryLabel; label.style.fontSize = 11; label.style.unityTextAlign = TextAnchor.MiddleCenter; label.style.marginLeft = 4; label.style.marginRight = 0; label.pickingMode = PickingMode.Ignore; return label; } void OnNavigationButtonMouseEnter(MouseEnterEvent evt) { VisualElement button = (VisualElement)evt.currentTarget; button.style.backgroundColor = StyleKeyword.Null; } void OnNavigationButtonMouseLeave(MouseLeaveEvent evt) { VisualElement button = (VisualElement)evt.currentTarget; button.style.backgroundColor = Color.clear; } Button CreateNavigationButton( Action clickEvent, string tooltip, Texture2D image, float marginRight = 0) { Button result = new Button(clickEvent); result.tooltip = tooltip; result.style.backgroundImage = image; result.style.width = NAVIGATION_BUTTON_SIZE; result.style.height = NAVIGATION_BUTTON_SIZE; result.style.marginLeft = 0; result.style.marginRight = marginRight; result.style.paddingLeft = 0; result.style.paddingRight = 0; result.style.paddingTop = 0; result.style.paddingBottom = 0; result.style.borderLeftWidth = 0; result.style.borderRightWidth = 0; result.style.borderTopWidth = 0; result.style.borderBottomWidth = 0; result.visible = false; result.style.backgroundColor = Color.clear; result.SetMouseCursor(MouseCursor.Arrow); result.RegisterCallback(OnNavigationButtonMouseEnter); result.RegisterCallback(OnNavigationButtonMouseLeave); return result; } void OnSearchFieldKeyDown(KeyDownEvent evt) { KeyboardActions.SearchAction searchAction = KeyboardActions.GetSearchAction(evt); if (searchAction == KeyboardActions.SearchAction.None) return; if (searchAction == KeyboardActions.SearchAction.Clean) { evt.StopPropagation(); mSearchField.value = string.Empty; return; } if (searchAction == KeyboardActions.SearchAction.PreviousSearch) { evt.StopPropagation(); PreviousSearchResultRequested?.Invoke(); return; } if (searchAction == KeyboardActions.SearchAction.NextSearch) { evt.StopPropagation(); NextSearchResultRequested?.Invoke(); return; } } void OnSearchFieldChanged(ChangeEvent evt) { mSearchRunner.Run(); UpdateSearchControlsVisibility(); } void UpdateSearchControlsVisibility() { if (string.IsNullOrEmpty(mSearchField.value)) { mSearchCounterLabel.text = string.Empty; mSearchCounterLabel.visible = false; mPreviousButton.visible = false; mNextButton.visible = false; return; } mSearchCounterLabel.visible = true; mPreviousButton.visible = true; mNextButton.visible = true; } void OnNextButtonClicked() { NextSearchResultRequested?.Invoke(); } void OnPreviousButtonClicked() { PreviousSearchResultRequested?.Invoke(); } static string GetSearchTextBoxShortcutsTooltipString() { if (PlatformIdentifier.IsWindows()) return PlasticLocalization.Name .BranchExplorerSearchFieldShortcutsTooltipForWindows.GetString(); if (PlatformIdentifier.IsMac()) return PlasticLocalization.Name .BranchExplorerSearchFieldShortcutsTooltipForMacOS.GetString(); return PlasticLocalization.Name .BranchExplorerSearchFieldShortcutsTooltipForLinux.GetString(); } ToolbarSearchField mSearchField; Label mSearchCounterLabel; Button mPreviousButton; Button mNextButton; readonly DelayedActionBySecondsRunner mSearchRunner; const float NAVIGATION_BUTTON_SIZE = 12f; } }