| File: System\Windows\Forms\Controls\Menus\MenuTimer.cs | Web Access |
| Project: src\winforms\src\System.Windows.Forms\System.Windows.Forms.csproj (System.Windows.Forms) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. namespace System.Windows.Forms; internal class MenuTimer { private readonly Timer _autoMenuExpandTimer = new(); private ToolStripMenuItem? _fromItem; private bool _inTransition; private readonly int _quickShow = 1; private readonly int _slowShow; public MenuTimer() { // MenuShowDelay can be set to 0. In this case, set to something low so it's imperceptible. _autoMenuExpandTimer.Tick += OnTick; // since MenuShowDelay is registry tweakable we've gotta make sure we've got some sort // of interval _slowShow = Math.Max(_quickShow, SystemInformation.MenuShowDelay); } // The current item to autoexpand. private ToolStripMenuItem? CurrentItem { get; set; } public bool InTransition { get { return _inTransition; } set { _inTransition = value; } } public void Start(ToolStripMenuItem item) { if (InTransition) { return; } StartCore(item); } private void StartCore(ToolStripMenuItem? item) { if (item != CurrentItem) { Cancel(CurrentItem); } CurrentItem = item; if (item is not null) { CurrentItem = item; _autoMenuExpandTimer.Interval = item.IsOnDropDown ? _slowShow : _quickShow; _autoMenuExpandTimer.Enabled = true; } } public void Transition(ToolStripMenuItem fromItem, ToolStripMenuItem? toItem) { if (toItem is null && InTransition) { Cancel(); // In this case we're likely to have hit an item that's not a menu item or nothing is selected. EndTransition(forceClose: true); return; } if (_fromItem != fromItem) { _fromItem = fromItem; CancelCore(); StartCore(toItem); } // Set up the current item to be the toItem so it will be auto expanded when complete. CurrentItem = toItem; InTransition = true; } public void Cancel() { if (InTransition) { return; } CancelCore(); } /// <summary> cancels if and only if this item was the one that /// requested the timer /// </summary> public void Cancel(ToolStripMenuItem? item) { if (InTransition) { return; } if (item == CurrentItem) { CancelCore(); } } private void CancelCore() { _autoMenuExpandTimer.Enabled = false; CurrentItem = null; } private void EndTransition(bool forceClose) { ToolStripMenuItem? lastSelected = _fromItem; _fromItem = null; // immediately clear BEFORE we call user code. if (InTransition) { InTransition = false; // we should rollup if the current item has changed and is selected. bool rollup = forceClose || (CurrentItem is not null && CurrentItem != lastSelected && CurrentItem.Selected); if (rollup && lastSelected is not null && lastSelected.HasDropDownItems) { lastSelected.HideDropDown(); } } } internal void HandleToolStripMouseLeave(ToolStrip toolStrip) { if (InTransition && toolStrip == _fromItem?.ParentInternal) { // restore the selection back to CurrentItem. // we're about to fall off the edge of the toolstrip, something should be selected // at all times while we're InTransition mode - otherwise it looks really funny // to have an auto expanded item CurrentItem?.Select(); } else { // because we've split up selected/pressed, we need to make sure // that onmouseleave we make sure there's a selected menu item. if (toolStrip.IsDropDown && toolStrip.ActiveDropDowns.Count > 0) { ToolStripMenuItem? menuItem = toolStrip.ActiveDropDowns[0].OwnerItem as ToolStripMenuItem; if (menuItem is not null && menuItem.Pressed) { menuItem.Select(); } } } } private void OnTick(object? sender, EventArgs e) { _autoMenuExpandTimer.Enabled = false; if (CurrentItem is null) { return; } EndTransition(forceClose: false); if (CurrentItem is not null && !CurrentItem.IsDisposed && CurrentItem.Selected && CurrentItem.Enabled && ToolStripManager.ModalMenuFilter.InMenuMode) { CurrentItem.OnMenuAutoExpand(); } } }