mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IJPL-58454 Implement selecting the main menu bar on Windows by pressing Alt
GitOrigin-RevId: 3058dfa79b65cbd98815eddf543fb51892186668
This commit is contained in:
committed by
intellij-monorepo-bot
parent
530092ee14
commit
5cf014eaf2
@@ -70,5 +70,11 @@
|
||||
defaultValue="60"
|
||||
description="The interval between too-small window size checks on Windows and attempts to restore the size. Set to zero to disable"
|
||||
/>
|
||||
<registryKey
|
||||
key="ide.windows.main.menu.focus.on.alt"
|
||||
defaultValue="false"
|
||||
description="Enable Alt key to focus the main menu bar on Windows"
|
||||
restartRequired="true"
|
||||
/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
|
||||
@@ -1287,7 +1287,9 @@ private class WindowsAltSuppressor : IdeEventQueue.NonLockedEventDispatcher {
|
||||
if (uiSettings == null ||
|
||||
!SystemInfoRt.isWindows ||
|
||||
!Registry.`is`("actionSystem.win.suppressAlt", true) ||
|
||||
!(uiSettings.hideToolStripes || uiSettings.presentationMode)) {
|
||||
// Need to handle Alt to show hidden tool stripes by double Alt or to focus the main menu
|
||||
!(uiSettings.hideToolStripes || uiSettings.presentationMode) &&
|
||||
!Registry.`is`("ide.windows.main.menu.focus.on.alt", false)) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
+9
-4
@@ -30,6 +30,8 @@ import java.awt.event.ComponentAdapter
|
||||
import java.awt.event.ComponentEvent
|
||||
import java.awt.event.MouseAdapter
|
||||
import java.awt.event.MouseEvent
|
||||
import javax.accessibility.AccessibleContext
|
||||
import javax.accessibility.AccessibleState
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.JFrame
|
||||
import javax.swing.JLayeredPane
|
||||
@@ -102,7 +104,7 @@ internal class ExpandableMenu(
|
||||
ideMenuHelper.updateUI()
|
||||
}
|
||||
|
||||
fun switchState(actionMenuToShow: ActionMenu? = null, itemInd: Int = 0) {
|
||||
fun switchState(actionMenuToShow: ActionMenu? = null, itemInd: Int = 0, selectOnlyHeaderMenu: Boolean = false) {
|
||||
if (isShowing() && actionMenuToShow == null) {
|
||||
hideExpandedMenuBar()
|
||||
return
|
||||
@@ -130,11 +132,11 @@ internal class ExpandableMenu(
|
||||
|
||||
// The first menu usage has no selection in the menu. Fix it by invokeLater
|
||||
ApplicationManager.getApplication().invokeLater {
|
||||
selectMenu(actionMenu = actionMenuToShow, itemInd = itemInd)
|
||||
selectMenu(actionMenu = actionMenuToShow, itemInd = itemInd, selectOnlyHeaderMenu = selectOnlyHeaderMenu)
|
||||
}
|
||||
}
|
||||
|
||||
private fun selectMenu(actionMenu: ActionMenu? = null, itemInd: Int) {
|
||||
private fun selectMenu(actionMenu: ActionMenu? = null, itemInd: Int, selectOnlyHeaderMenu: Boolean = false) {
|
||||
var menu = ideMenu.getMenu(itemInd)
|
||||
if (actionMenu != null) {
|
||||
for (m in ideMenu.rootMenuItems) {
|
||||
@@ -148,8 +150,11 @@ internal class ExpandableMenu(
|
||||
menu ?: return
|
||||
|
||||
val subElements = menu.popupMenu.subElements
|
||||
if (subElements.isEmpty()) {
|
||||
if (subElements.isEmpty() || selectOnlyHeaderMenu) {
|
||||
MenuSelectionManager.defaultManager().selectedPath = arrayOf<MenuElement>(ideMenu, menu)
|
||||
ApplicationManager.getApplication().invokeLater {
|
||||
menu.accessibleContext.firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY, null, AccessibleState.SELECTED)
|
||||
}
|
||||
}
|
||||
else {
|
||||
MenuSelectionManager.defaultManager().selectedPath = arrayOf<MenuElement>(ideMenu, menu, menu.popupMenu, subElements[0])
|
||||
|
||||
+151
-1
@@ -5,13 +5,42 @@ import com.intellij.ide.ui.UISettings
|
||||
import com.intellij.openapi.actionSystem.ActionGroup
|
||||
import com.intellij.openapi.actionSystem.ActionPlaces
|
||||
import com.intellij.openapi.actionSystem.impl.ActionMenu
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.util.SystemInfoRt
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.openapi.wm.impl.IdeFrameDecorator
|
||||
import com.intellij.openapi.wm.impl.headertoolbar.MainMenuWithButton
|
||||
import com.intellij.openapi.wm.impl.headertoolbar.MergedMainMenu
|
||||
import com.intellij.ui.AppUIUtil
|
||||
import com.intellij.util.concurrency.ThreadingAssertions
|
||||
import com.intellij.util.ui.accessibility.ScreenReader
|
||||
import kotlinx.coroutines.job
|
||||
import java.awt.AWTEvent
|
||||
import java.awt.KeyEventPostProcessor
|
||||
import java.awt.KeyboardFocusManager
|
||||
import java.awt.Toolkit
|
||||
import java.awt.event.AWTEventListener
|
||||
import java.awt.event.InputEvent
|
||||
import java.awt.event.KeyEvent
|
||||
import java.awt.event.MouseEvent
|
||||
import javax.accessibility.AccessibleContext
|
||||
import javax.accessibility.AccessibleState
|
||||
import javax.swing.MenuElement
|
||||
import javax.swing.MenuSelectionManager
|
||||
import javax.swing.SwingUtilities
|
||||
|
||||
internal class JMenuBasedIdeMenuBarHelper(flavor: IdeMenuFlavor, menuBar: IdeJMenuBar.JMenuBarImpl) : IdeMenuBarHelper(flavor, menuBar) {
|
||||
init {
|
||||
if (WinAltKeyProcessor.isEnabled()) {
|
||||
WinAltKeyProcessor.register(this)
|
||||
menuBar.coroutineScope.coroutineContext.job.invokeOnCompletion {
|
||||
ApplicationManager.getApplication().invokeLater {
|
||||
WinAltKeyProcessor.unregister(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun isUpdateForbidden() = MenuSelectionManager.defaultManager().selectedPath.isNotEmpty()
|
||||
|
||||
override suspend fun doUpdateVisibleActions(newVisibleActions: List<ActionGroup>, forceRebuild: Boolean) {
|
||||
@@ -68,4 +97,125 @@ internal class JMenuBasedIdeMenuBarHelper(flavor: IdeMenuFlavor, menuBar: IdeJMe
|
||||
menuBar.frame.validate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun activateMenu() {
|
||||
val menuBarComponent = menuBar.component
|
||||
if (menuBarComponent is MergedMainMenu) {
|
||||
val mainMenuWithButton = menuBarComponent.parent as? MainMenuWithButton
|
||||
if (mainMenuWithButton?.mainMenuButton?.button?.isShowing == true) {
|
||||
mainMenuWithButton.mainMenuButton.expandableMenu?.switchState(selectOnlyHeaderMenu = true)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
val ideMenuBar = menuBarComponent as? IdeJMenuBar ?: return
|
||||
val firstMenu = ideMenuBar.rootMenuItems.firstOrNull() ?: return
|
||||
MenuSelectionManager.defaultManager().selectedPath = arrayOf<MenuElement>(menuBarComponent, firstMenu)
|
||||
ApplicationManager.getApplication().invokeLater {
|
||||
firstMenu.accessibleContext.firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY, null, AccessibleState.SELECTED)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun shouldProcessAltRelease(e: KeyEvent): Boolean {
|
||||
val menuBarComponent = menuBar.component
|
||||
val visibleComponent = if (menuBarComponent.isShowing) menuBarComponent
|
||||
else {
|
||||
val button = ((menuBarComponent as? MergedMainMenu)?.parent as? MainMenuWithButton)?.mainMenuButton?.button
|
||||
if (button?.isShowing == true) button else return false
|
||||
}
|
||||
|
||||
val window = SwingUtilities.getWindowAncestor(visibleComponent) ?: return false
|
||||
if (SwingUtilities.getWindowAncestor(e.component) != window && e.component != window) return false
|
||||
if (MenuSelectionManager.defaultManager().selectedPath.isNotEmpty()) return false
|
||||
if (ScreenReader.isActive() && AppUIUtil.isInFullScreen(window)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal object WinAltKeyProcessor : KeyEventPostProcessor, AWTEventListener {
|
||||
private val helpers = mutableListOf<JMenuBasedIdeMenuBarHelper>()
|
||||
private var registered = false
|
||||
|
||||
private var altPressed = false
|
||||
private var altPressedOnly = false
|
||||
|
||||
@JvmStatic
|
||||
fun isEnabled(): Boolean = SystemInfoRt.isWindows && Registry.`is`("ide.windows.main.menu.focus.on.alt", false)
|
||||
|
||||
fun register(helper: JMenuBasedIdeMenuBarHelper) {
|
||||
helpers.add(helper)
|
||||
if (!registered) {
|
||||
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventPostProcessor(this)
|
||||
Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.MOUSE_EVENT_MASK)
|
||||
registered = true
|
||||
}
|
||||
}
|
||||
|
||||
fun unregister(helper: JMenuBasedIdeMenuBarHelper) {
|
||||
helpers.remove(helper)
|
||||
if (helpers.isEmpty() && registered) {
|
||||
KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventPostProcessor(this)
|
||||
Toolkit.getDefaultToolkit().removeAWTEventListener(this)
|
||||
registered = false
|
||||
}
|
||||
}
|
||||
|
||||
override fun eventDispatched(event: AWTEvent) {
|
||||
if (!isEnabled()) {
|
||||
return
|
||||
}
|
||||
|
||||
// Prevent focusing the menu when hold Alt + mouse click shortcuts are used
|
||||
if (event is MouseEvent && event.id == MouseEvent.MOUSE_PRESSED && altPressed) {
|
||||
altPressedOnly = false
|
||||
}
|
||||
}
|
||||
|
||||
override fun postProcessKeyEvent(e: KeyEvent): Boolean {
|
||||
if (!isEnabled()) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (e.keyCode != KeyEvent.VK_ALT) {
|
||||
if (altPressed && e.id == KeyEvent.KEY_PRESSED) {
|
||||
altPressedOnly = false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
val settings = UISettings.instanceOrNull
|
||||
// To avoid conflicts with the double Alt shortcut to show hidden tool stripes
|
||||
if (settings == null || settings.hideToolStripes || settings.presentationMode) {
|
||||
return false
|
||||
}
|
||||
|
||||
when (e.id) {
|
||||
KeyEvent.KEY_PRESSED -> {
|
||||
if (!altPressed) {
|
||||
altPressed = true
|
||||
altPressedOnly = true
|
||||
}
|
||||
}
|
||||
KeyEvent.KEY_RELEASED -> {
|
||||
if (altPressed && altPressedOnly
|
||||
&& e.modifiersEx and (InputEvent.SHIFT_DOWN_MASK or InputEvent.CTRL_DOWN_MASK or InputEvent.META_DOWN_MASK) == 0) {
|
||||
for (helper in helpers) {
|
||||
if (helper.shouldProcessAltRelease(e)) {
|
||||
ApplicationManager.getApplication().invokeLater {
|
||||
helper.activateMenu()
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
altPressed = false
|
||||
altPressedOnly = false
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user