diff --git a/platform/lang-impl/src/com/intellij/codeInsight/AutoPopupController.java b/platform/lang-impl/src/com/intellij/codeInsight/AutoPopupController.java index a4289b1a1c0d..002bc8163353 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/AutoPopupController.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/AutoPopupController.java @@ -185,7 +185,7 @@ public class AutoPopupController implements Disposable { if (editor.isDisposed() || !editor.getComponent().isShowing()) return; int lbraceOffset = editor.getCaretModel().getOffset() - 1; try { - ShowParameterInfoHandler.invoke(myProject, editor, file1, lbraceOffset, highlightedMethod); + ShowParameterInfoHandler.invoke(myProject, editor, file1, lbraceOffset, highlightedMethod, false); } catch (IndexNotReadyException ignored) { //anything can happen on alarm } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonTooltipUtil.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonTooltipUtil.java index 49117d640672..dc7a9ed89861 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonTooltipUtil.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonTooltipUtil.java @@ -40,6 +40,10 @@ public class DaemonTooltipUtil { } public static void showInfoTooltip(@NotNull final HighlightInfo info, @NotNull Editor editor, final int defaultOffset, final int currentWidth) { + showInfoTooltip(info, editor, defaultOffset, currentWidth, false); + } + + public static void showInfoTooltip(@NotNull final HighlightInfo info, @NotNull Editor editor, final int defaultOffset, final int currentWidth, final boolean requestFocus) { if (info.getToolTip() == null) return; Rectangle visibleArea = editor.getScrollingModel().getVisibleArea(); @@ -56,7 +60,11 @@ public class DaemonTooltipUtil { editor.getComponent().getRootPane().getLayeredPane() ); - TooltipController.getInstance().showTooltip(editor, p, info.getToolTip(), currentWidth, false, DAEMON_INFO_GROUP, new HintHint(editor, bestPoint).setAwtTooltip(true).setHighlighterType(true).setCalloutShift( - editor.getLineHeight() / 2 - 1)); + HintHint hintHint = new HintHint(editor, bestPoint) + .setAwtTooltip(true) + .setHighlighterType(true) + .setRequestFocus(requestFocus) + .setCalloutShift(editor.getLineHeight() / 2 - 1); + TooltipController.getInstance().showTooltip(editor, p, info.getToolTip(), currentWidth, false, DAEMON_INFO_GROUP, hintHint); } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/ShowErrorDescriptionHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/ShowErrorDescriptionHandler.java index eac277fa02ba..0b0dcaebddc5 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/ShowErrorDescriptionHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/ShowErrorDescriptionHandler.java @@ -25,9 +25,11 @@ import org.jetbrains.annotations.NotNull; public class ShowErrorDescriptionHandler implements CodeInsightActionHandler { private final int myWidth; + private final boolean myRequestFocus; - public ShowErrorDescriptionHandler(final int width) { + public ShowErrorDescriptionHandler(final int width, final boolean requestFocus) { myWidth = width; + myRequestFocus = requestFocus; } @Override @@ -36,7 +38,7 @@ public class ShowErrorDescriptionHandler implements CodeInsightActionHandler { DaemonCodeAnalyzer codeAnalyzer = DaemonCodeAnalyzer.getInstance(project); HighlightInfo info = ((DaemonCodeAnalyzerImpl)codeAnalyzer).findHighlightByOffset(editor.getDocument(), offset, false); if (info != null) { - DaemonTooltipUtil.showInfoTooltip(info, editor, editor.getCaretModel().getOffset(), myWidth); + DaemonTooltipUtil.showInfoTooltip(info, editor, editor.getCaretModel().getOffset(), myWidth, myRequestFocus); } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/actions/ShowErrorDescriptionAction.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/actions/ShowErrorDescriptionAction.java index 35ecc135c9d6..5fadab4b0d38 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/actions/ShowErrorDescriptionAction.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/actions/ShowErrorDescriptionAction.java @@ -30,12 +30,16 @@ import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.psi.PsiFile; +import com.intellij.util.ui.accessibility.ScreenReader; import org.jetbrains.annotations.NotNull; +import java.awt.event.KeyEvent; + public class ShowErrorDescriptionAction extends BaseCodeInsightAction implements DumbAware { private static int width; private static boolean shouldShowDescription = false; private static boolean descriptionShown = true; + private boolean myRequestFocus = false; public ShowErrorDescriptionAction() { setEnabledInModalContext(true); @@ -44,7 +48,7 @@ public class ShowErrorDescriptionAction extends BaseCodeInsightAction implements @NotNull @Override protected CodeInsightActionHandler getHandler() { - return new ShowErrorDescriptionHandler(shouldShowDescription ? width : 0); + return new ShowErrorDescriptionHandler(shouldShowDescription ? width : 0, myRequestFocus); } @Override @@ -62,6 +66,8 @@ public class ShowErrorDescriptionAction extends BaseCodeInsightAction implements @Override public void beforeActionPerformedUpdate(@NotNull final AnActionEvent e) { super.beforeActionPerformedUpdate(e); + // The tooltip gets the focus if using a screen reader and invocation through a keyboard shortcut. + myRequestFocus = ScreenReader.isActive() && (e.getInputEvent() instanceof KeyEvent); changeState(); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/hint/ParameterInfoComponent.java b/platform/lang-impl/src/com/intellij/codeInsight/hint/ParameterInfoComponent.java index 0bf703ed475a..c95d26719453 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/hint/ParameterInfoComponent.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/hint/ParameterInfoComponent.java @@ -29,6 +29,7 @@ import com.intellij.ui.ScrollPaneFactory; import com.intellij.ui.SideBorder; import com.intellij.util.Function; import com.intellij.util.ui.UIUtil; +import com.intellij.util.ui.accessibility.AccessibleContextUtil; import com.intellij.xml.util.XmlStringUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -73,6 +74,7 @@ public class ParameterInfoComponent extends JPanel { return -1; } }; + private boolean myRequestFocus; @TestOnly public static ParameterInfoUIContextEx createContext(Object[] objects, Editor editor, @NotNull ParameterInfoHandler handler, int currentParameterIndex) { @@ -112,6 +114,9 @@ public class ParameterInfoComponent extends JPanel { GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); } + if (myRequestFocus) { + AccessibleContextUtil.setName(this, "Parameter Info. Press TAB to navigate through each element. Press ESC to close."); + } final JScrollPane pane = ScrollPaneFactory.createScrollPane(panel); pane.setBorder(null); @@ -137,6 +142,14 @@ public class ParameterInfoComponent extends JPanel { return myHighlighted; } + public void setRequestFocus(boolean requestFocus) { + myRequestFocus = requestFocus; + } + + public boolean isRequestFocus() { + return myRequestFocus; + } + class MyParameterContext implements ParameterInfoUIContextEx { private int i; private Function myEscapeFunction; @@ -370,6 +383,8 @@ public class ParameterInfoComponent extends JPanel { myLabel.setOpaque(true); myLabel.setFont(NORMAL_FONT); + if (myRequestFocus) + myLabel.setFocusable(true); add(myLabel, new GridBagConstraints(0, 0, 1, 1, 1, 1, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); @@ -468,4 +483,4 @@ public class ParameterInfoComponent extends JPanel { isDisabledBeforeHighlight = true; } } -} \ No newline at end of file +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/hint/ShowParameterInfoContext.java b/platform/lang-impl/src/com/intellij/codeInsight/hint/ShowParameterInfoContext.java index 265b6e436e83..9e56d3877b56 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/hint/ShowParameterInfoContext.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/hint/ShowParameterInfoContext.java @@ -48,6 +48,7 @@ public class ShowParameterInfoContext implements CreateParameterInfoContext { private final int myParameterListStart; private PsiElement myHighlightedElement; private Object[] myItems; + private boolean myRequestFocus; public ShowParameterInfoContext(final Editor editor, final Project project, final PsiFile file, int offset, int parameterListStart) { @@ -108,7 +109,7 @@ public class ShowParameterInfoContext implements CreateParameterInfoContext { public void showHint(PsiElement element, int offset, ParameterInfoHandler handler) { final Object[] itemsToShow = getItemsToShow(); if (itemsToShow == null || itemsToShow.length == 0) return; - showMethodInfo(getProject(), getEditor(), element, getHighlightedElement(), itemsToShow, offset, handler); + showMethodInfo(getProject(), getEditor(), element, getHighlightedElement(), itemsToShow, offset, handler, myRequestFocus); } private static void showParameterHint(final PsiElement element, @@ -117,12 +118,14 @@ public class ShowParameterInfoContext implements CreateParameterInfoContext { final Project project, @Nullable PsiElement highlighted, final int elementStart, - final ParameterInfoHandler handler) { + final ParameterInfoHandler handler, + final boolean requestFocus) { if (ParameterInfoController.isAlreadyShown(editor, elementStart)) return; if (editor.isDisposed() || !editor.getComponent().isVisible()) return; final ParameterInfoComponent component = new ParameterInfoComponent(descriptors, editor,handler); component.setParameterOwner(element); + component.setRequestFocus(requestFocus); if (highlighted != null) { component.setHighlightedParameter(highlighted); } @@ -143,6 +146,7 @@ public class ShowParameterInfoContext implements CreateParameterInfoContext { HintHint hintHint = HintManagerImpl.createHintHint(editor, pos.getFirst(), hint, pos.getSecond()); hintHint.setExplicitClose(true); + hintHint.setRequestFocus(requestFocus); Editor editorToShow = editor instanceof EditorWindow ? ((EditorWindow)editor).getDelegate() : editor; // is case of injection we need to calculate position for EditorWindow @@ -157,9 +161,10 @@ public class ShowParameterInfoContext implements CreateParameterInfoContext { PsiElement highlighted, Object[] candidates, int offset, - ParameterInfoHandler handler + ParameterInfoHandler handler, + boolean requestFocus ) { - showParameterHint(list, editor, candidates, project, candidates.length > 1 ? highlighted : null, offset, handler); + showParameterHint(list, editor, candidates, project, candidates.length > 1 ? highlighted : null, offset, handler, requestFocus); } /** @@ -222,6 +227,14 @@ public class ShowParameterInfoContext implements CreateParameterInfoContext { HintManager.ABOVE); } + public void setRequestFocus(boolean requestFocus) { + myRequestFocus = requestFocus; + } + + public boolean isRequestFocus() { + return myRequestFocus; + } + static class MyBestLocationPointProvider implements ShowParameterInfoHandler.BestLocationPointProvider { private final Editor myEditor; private int previousOffset = -1; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/hint/ShowParameterInfoHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/hint/ShowParameterInfoHandler.java index 1fdfb2996c94..f09ebb2e4814 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/hint/ShowParameterInfoHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/hint/ShowParameterInfoHandler.java @@ -40,9 +40,19 @@ import java.awt.*; import java.util.Set; public class ShowParameterInfoHandler implements CodeInsightActionHandler { + private boolean myRequestFocus; + + public ShowParameterInfoHandler() { + this(false); + } + + public ShowParameterInfoHandler(boolean requestFocus) { + myRequestFocus = requestFocus; + } + @Override public void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) { - invoke(project, editor, file, -1, null); + invoke(project, editor, file, -1, null, myRequestFocus); } @Override @@ -57,7 +67,7 @@ public class ShowParameterInfoHandler implements CodeInsightActionHandler { return element; } - public static void invoke(final Project project, final Editor editor, PsiFile file, int lbraceOffset, PsiElement highlightedElement) { + public static void invoke(final Project project, final Editor editor, PsiFile file, int lbraceOffset, PsiElement highlightedElement, boolean requestFocus) { ApplicationManager.getApplication().assertIsDispatchThread(); PsiDocumentManager.getInstance(project).commitAllDocuments(); @@ -74,6 +84,7 @@ public class ShowParameterInfoHandler implements CodeInsightActionHandler { ); context.setHighlightedElement(highlightedElement); + context.setRequestFocus(requestFocus); final Language language = psiElement.getLanguage(); ParameterInfoHandler[] handlers = getHandlers(project, language, file.getViewProvider().getBaseLanguage()); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/hint/actions/ShowParameterInfoAction.java b/platform/lang-impl/src/com/intellij/codeInsight/hint/actions/ShowParameterInfoAction.java index 437603ff5bdb..9b49520cf6c8 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/hint/actions/ShowParameterInfoAction.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/hint/actions/ShowParameterInfoAction.java @@ -20,22 +20,35 @@ import com.intellij.codeInsight.CodeInsightActionHandler; import com.intellij.codeInsight.actions.BaseCodeInsightAction; import com.intellij.codeInsight.hint.ShowParameterInfoHandler; import com.intellij.lang.Language; +import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiFile; import com.intellij.psi.util.PsiUtilCore; +import com.intellij.util.ui.accessibility.ScreenReader; import org.jetbrains.annotations.NotNull; +import java.awt.event.KeyEvent; + public class ShowParameterInfoAction extends BaseCodeInsightAction implements DumbAware { + private boolean myRequestFocus = false; + public ShowParameterInfoAction() { setEnabledInModalContext(true); } + @Override + public void beforeActionPerformedUpdate(@NotNull AnActionEvent e) { + super.beforeActionPerformedUpdate(e); + // The tooltip gets the focus if using a screen reader and invocation through a keyboard shortcut. + myRequestFocus = ScreenReader.isActive() && (e.getInputEvent() instanceof KeyEvent); + } + @NotNull @Override protected CodeInsightActionHandler getHandler() { - return new ShowParameterInfoHandler(); + return new ShowParameterInfoHandler(myRequestFocus); } @Override @@ -48,4 +61,4 @@ public class ShowParameterInfoAction extends BaseCodeInsightAction implements Du protected boolean isValidForLookup() { return true; } -} \ No newline at end of file +} diff --git a/platform/platform-api/src/com/intellij/openapi/ui/ComponentWithBrowseButton.java b/platform/platform-api/src/com/intellij/openapi/ui/ComponentWithBrowseButton.java index 7e7579421561..262fed0d8e61 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/ComponentWithBrowseButton.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/ComponentWithBrowseButton.java @@ -36,6 +36,7 @@ import com.intellij.ui.GuiUtils; import com.intellij.ui.UIBundle; import com.intellij.util.Consumer; import com.intellij.util.ui.UIUtil; +import com.intellij.util.ui.accessibility.ScreenReader; import com.intellij.util.ui.update.LazyUiDisposable; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; @@ -71,11 +72,14 @@ public class ComponentWithBrowseButton extends JPanel i add(wrapWithoutResize(myBrowseButton), BorderLayout.EAST); myBrowseButton.setToolTipText(UIBundle.message("component.with.browse.button.browse.button.tooltip.text")); - // FixedSizeButton isn't focusable but it should be selectable via keyboard. if (ApplicationManager.getApplication() != null) { // avoid crash at design time new MyDoClickAction(myBrowseButton).registerShortcut(myComponent); } + if (ScreenReader.isActive()) { + myBrowseButton.setFocusable(true); + myBrowseButton.getAccessibleContext().setAccessibleName("Browse"); + } } private static JPanel wrapWithoutResize(JComponent component) { diff --git a/platform/platform-api/src/com/intellij/openapi/ui/popup/BalloonBuilder.java b/platform/platform-api/src/com/intellij/openapi/ui/popup/BalloonBuilder.java index 0a6bb13d5e02..41db010dbfe6 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/popup/BalloonBuilder.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/popup/BalloonBuilder.java @@ -95,6 +95,9 @@ public interface BalloonBuilder { @NotNull BalloonBuilder setBlockClicksThroughBalloon(boolean block); + @NotNull + BalloonBuilder setRequestFocus(boolean requestFocus); + /** * Links target balloon life cycle to the given object. I.e. current balloon will be auto-hide and collected as soon * as given anchor is disposed. @@ -109,4 +112,4 @@ public interface BalloonBuilder { @NotNull Balloon createBalloon(); -} \ No newline at end of file +} diff --git a/platform/platform-api/src/com/intellij/ui/InplaceButton.java b/platform/platform-api/src/com/intellij/ui/InplaceButton.java index 89dd0dfc34e2..dfe0c21d15e2 100644 --- a/platform/platform-api/src/com/intellij/ui/InplaceButton.java +++ b/platform/platform-api/src/com/intellij/ui/InplaceButton.java @@ -17,23 +17,27 @@ package com.intellij.ui; import com.intellij.openapi.ui.popup.IconButton; import com.intellij.openapi.util.Pass; +import com.intellij.ui.awt.RelativePoint; import com.intellij.util.ui.BaseButtonBehavior; import com.intellij.util.ui.CenteredIcon; import com.intellij.util.ui.TimedDeadzone; import com.intellij.util.ui.UIUtil; +import javax.accessibility.*; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; +import java.util.ArrayList; -public class InplaceButton extends JComponent implements ActiveComponent { +public class InplaceButton extends JComponent implements ActiveComponent, Accessible { private boolean myPainting = true; private boolean myActive = true; private BaseButtonBehavior myBehavior; + private ActionListener myListener; private CenteredIcon myRegular; private CenteredIcon myHovered; @@ -62,9 +66,11 @@ public class InplaceButton extends JComponent implements ActiveComponent { } public InplaceButton(IconButton source, final ActionListener listener, final Pass me, TimedDeadzone.Length mouseDeadzone) { + myListener = listener; myBehavior = new BaseButtonBehavior(this, mouseDeadzone) { + @Override protected void execute(final MouseEvent e) { - listener.actionPerformed(new ActionEvent(e, ActionEvent.ACTION_PERFORMED, "execute", e.getModifiers())); + doClick(e); } @Override @@ -91,12 +97,22 @@ public class InplaceButton extends JComponent implements ActiveComponent { protected void doRepaintComponent(Component c) { c.repaint(); } - + + public void doClick() { + RelativePoint point = new RelativePoint(this, new Point(this.getWidth() / 2, this.getHeight() / 2)); + doClick(point.toMouseEvent()); + } + + public void doClick(final MouseEvent e) { + if (myListener != null) { + myListener.actionPerformed(new ActionEvent(e, ActionEvent.ACTION_PERFORMED, "execute", e.getModifiers())); + } + } + public void setMouseDeadzone(final TimedDeadzone.Length deadZone) { myBehavior.setMouseDeadzone(deadZone); } - public void setIcons(IconButton source) { setIcons(source.getRegular(), source.getInactive(), source.getHovered()); } @@ -132,6 +148,7 @@ public class InplaceButton extends JComponent implements ActiveComponent { repaint(); } + @Override public void setActive(final boolean active) { myActive = active; repaint(); @@ -141,10 +158,12 @@ public class InplaceButton extends JComponent implements ActiveComponent { setIcons(icon, icon, icon); } + @Override public JComponent getComponent() { return this; } + @Override protected void paintComponent(Graphics g) { super.paintComponent(g); @@ -188,8 +207,120 @@ public class InplaceButton extends JComponent implements ActiveComponent { } public boolean isActive() { - - return myActive; } + + @Override + public AccessibleContext getAccessibleContext() { + if (accessibleContext == null) { + accessibleContext = new AccessibleInplaceButton(); + } + return accessibleContext; + } + + /** + * The Accessible implementation of InplaceButton is a subset of AccessibleAbstractButton. + */ + protected class AccessibleInplaceButton extends AccessibleJComponent implements AccessibleAction, AccessibleExtendedComponent { + + @Override + public String getAccessibleName() { + String name = accessibleName; + + if (name == null) { + name = (String)getClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY); + } + if (name == null) { + name = InplaceButton.this.getToolTipText(); + } + if (name == null) { + name = super.getAccessibleName(); + } + return name; + } + + @Override + public AccessibleRole getAccessibleRole() { + return AccessibleRole.PUSH_BUTTON; + } + + @Override + public int getAccessibleActionCount() { + return 1; + } + + @Override + public String getAccessibleActionDescription(int i) { + if (i == 0) { + return "Click"; + } else { + return null; + } + } + + @Override + public boolean doAccessibleAction(int i) { + if (i == 0) { + doClick(); + return true; + } else { + return false; + } + } + + @Override + public AccessibleAction getAccessibleAction() { + return this; + } + + @Override + public AccessibleIcon[] getAccessibleIcon() { + Icon[] icons = {myRegular, myInactive, myHovered}; + ArrayList accessibleIconList = new ArrayList(); + for (Icon icon : icons) { + if (icon instanceof Accessible) { + AccessibleContext ac = ((Accessible)icon).getAccessibleContext(); + if (ac != null && ac instanceof AccessibleIcon) { + accessibleIconList.add((AccessibleIcon)ac); + } + } + } + if (accessibleIconList.size() == 0) { + return null; + } + + return accessibleIconList.toArray(new AccessibleIcon[accessibleIconList.size()]); + } + + @Override + public AccessibleStateSet getAccessibleStateSet() { + AccessibleStateSet states = super.getAccessibleStateSet(); + if (isFocusOwner()) { + states.add(AccessibleState.FOCUSED); + } + return states; + } + + // ----- AccessibleExtendedComponent + + @SuppressWarnings("unused") + AccessibleExtendedComponent getAccessibleExtendedComponent() { + return this; + } + + @Override + public String getToolTipText() { + return InplaceButton.this.getToolTipText(); + } + + @Override + public String getTitledBorderText() { + return null; + } + + @Override + public AccessibleKeyBinding getAccessibleKeyBinding() { + return null; + } + } } diff --git a/platform/platform-impl/src/com/intellij/codeInsight/hint/LineTooltipRenderer.java b/platform/platform-impl/src/com/intellij/codeInsight/hint/LineTooltipRenderer.java index bcace21c4801..d5e89658b1a0 100644 --- a/platform/platform-impl/src/com/intellij/codeInsight/hint/LineTooltipRenderer.java +++ b/platform/platform-impl/src/com/intellij/codeInsight/hint/LineTooltipRenderer.java @@ -29,6 +29,7 @@ import com.intellij.ui.LightweightHint; import com.intellij.ui.ScrollPaneFactory; import com.intellij.util.ui.Html; import com.intellij.util.ui.UIUtil; +import com.intellij.util.ui.accessibility.ScreenReader; import com.intellij.util.ui.update.ComparableObject; import com.intellij.xml.util.XmlStringUtil; import org.jetbrains.annotations.NonNls; @@ -38,6 +39,7 @@ import javax.swing.*; import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkListener; import java.awt.*; +import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.net.URL; @@ -101,6 +103,10 @@ public class LineTooltipRenderer extends ComparableObject.Impl implements Toolti scrollPane.setViewportBorder(null); + if (hintHint.isRequestFocus()) { + pane.setFocusable(true); + } + final Ref actionRef = new Ref(); final LightweightHint hint = new LightweightHint(scrollPane) { @Override @@ -123,6 +129,8 @@ public class LineTooltipRenderer extends ComparableObject.Impl implements Toolti @Override public void actionPerformed(final AnActionEvent e) { + // The tooltip gets the focus if using a screen reader and invocation through a keyboard shortcut. + hintHint.setRequestFocus(ScreenReader.isActive() && (e.getInputEvent() instanceof KeyEvent)); expand(hint, editor, p, pane, alignToRight, group, hintHint); } }); diff --git a/platform/platform-impl/src/com/intellij/ide/IdeTooltip.java b/platform/platform-impl/src/com/intellij/ide/IdeTooltip.java index 6c8a63f34414..6d775ef2f37b 100644 --- a/platform/platform-impl/src/com/intellij/ide/IdeTooltip.java +++ b/platform/platform-impl/src/com/intellij/ide/IdeTooltip.java @@ -36,6 +36,7 @@ public class IdeTooltip extends ComparableObject.Impl { private boolean myToCenter = false; private boolean myToCenterIfSmall = true; private boolean myHighlighter; + private boolean myRequestFocus; private Color myTextBackground; private Color myTextForeground; @@ -263,6 +264,15 @@ public class IdeTooltip extends ComparableObject.Impl { return myUi != null && myUi.isInside(target); } + public boolean isRequestFocus() { + return myRequestFocus; + } + + public IdeTooltip setRequestFocus(boolean requestFocus) { + myRequestFocus = requestFocus; + return this; + } + public interface Ui { boolean isInside(RelativePoint target); diff --git a/platform/platform-impl/src/com/intellij/ide/IdeTooltipManager.java b/platform/platform-impl/src/com/intellij/ide/IdeTooltipManager.java index bc0f6946fad5..54b2c1cac00d 100644 --- a/platform/platform-impl/src/com/intellij/ide/IdeTooltipManager.java +++ b/platform/platform-impl/src/com/intellij/ide/IdeTooltipManager.java @@ -311,6 +311,7 @@ public class IdeTooltipManager implements ApplicationComponent, AWTEventListener .setPositionChangeYShift(tooltip.getPositionChangeY()) .setHideOnKeyOutside(!tooltip.isExplicitClose()) .setHideOnAction(!tooltip.isExplicitClose()) + .setRequestFocus(tooltip.isRequestFocus()) .setLayer(tooltip.getLayer()); tooltip.getTipComponent().setForeground(fg); tooltip.getTipComponent().setBorder(new EmptyBorder(1, 3, 2, 3)); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFrameImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFrameImpl.java index 110cf06badac..2903ef618ec4 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFrameImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/IdeFrameImpl.java @@ -55,10 +55,12 @@ import com.intellij.ui.mac.MacMainFrameDecorator; import com.intellij.util.Alarm; import com.intellij.util.io.storage.HeavyProcessLatch; import com.intellij.util.ui.UIUtil; +import com.intellij.util.ui.accessibility.AccessibleContextAccessor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.io.PowerSupplyKit; +import javax.accessibility.AccessibleContext; import javax.swing.*; import java.awt.*; import java.awt.event.WindowAdapter; @@ -72,7 +74,7 @@ import java.io.File; * @author Anton Katilin * @author Vladimir Kondratyev */ -public class IdeFrameImpl extends JFrame implements IdeFrameEx, DataProvider { +public class IdeFrameImpl extends JFrame implements IdeFrameEx, AccessibleContextAccessor, DataProvider { public static final Key SHOULD_OPEN_IN_FULL_SCREEN = Key.create("should.open.in.full.screen"); private static final String FULL_SCREEN = "FullScreen"; @@ -354,6 +356,11 @@ public class IdeFrameImpl extends JFrame implements IdeFrameEx, DataProvider { ((IdeRootPane)getRootPane()).updateNorthComponents(); } + @Override + public AccessibleContext getCurrentAccessibleContext() { + return accessibleContext; + } + private static final class Builder { public StringBuilder sb = new StringBuilder(); @@ -601,4 +608,29 @@ public class IdeFrameImpl extends JFrame implements IdeFrameEx, DataProvider { return ActionCallback.DONE; } + + @Override + public AccessibleContext getAccessibleContext() { + if (accessibleContext == null) { + accessibleContext = new AccessibleIdeFrameImpl(); + } + return accessibleContext; + } + + protected class AccessibleIdeFrameImpl extends AccessibleJFrame { + @Override + public String getAccessibleName() { + final StringBuilder builder = new StringBuilder(); + + if (myProject != null) { + builder.append(myProject.getName()); + builder.append(" - "); + } + + final String applicationName = ((ApplicationInfoEx)ApplicationInfo.getInstance()).getFullApplicationName(); + builder.append(applicationName); + + return builder.toString(); + } + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/IdeStatusBarImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/IdeStatusBarImpl.java index 6e575bcff6cb..680fc2c9e4fc 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/IdeStatusBarImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/IdeStatusBarImpl.java @@ -42,6 +42,9 @@ import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; import javax.swing.*; import javax.swing.event.HyperlinkListener; import java.awt.*; @@ -53,7 +56,7 @@ import java.util.List; /** * User: spLeaner */ -public class IdeStatusBarImpl extends JComponent implements StatusBarEx { +public class IdeStatusBarImpl extends JComponent implements Accessible, StatusBarEx { private static final int MIN_ICON_HEIGHT = 18 + 1 + 1; private final InfoAndProgressPanel myInfoAndProgressPanel; private IdeFrame myFrame; @@ -883,4 +886,19 @@ public class IdeStatusBarImpl extends JComponent implements StatusBarEx { public IdeFrame getFrame() { return myFrame; } + + @Override + public AccessibleContext getAccessibleContext() { + if (accessibleContext == null) { + accessibleContext = new AccessibleIdeStatusBarImpl(); + } + return accessibleContext; + } + + protected class AccessibleIdeStatusBarImpl extends AccessibleJComponent { + @Override + public AccessibleRole getAccessibleRole() { + return AccessibleRole.PANEL; + } + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/StatusPanel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/StatusPanel.java index 18870e5af785..355ae2f9c733 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/StatusPanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/StatusPanel.java @@ -39,6 +39,8 @@ import com.intellij.util.ui.JBUI; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; import javax.swing.*; import java.awt.*; import java.awt.datatransfer.StringSelection; @@ -243,4 +245,18 @@ class StatusPanel extends JPanel { return myTextPanel.getText(); } + @Override + public AccessibleContext getAccessibleContext() { + if (accessibleContext == null) { + accessibleContext = new AccessibleStatusPanel(); + } + return accessibleContext; + } + + protected class AccessibleStatusPanel extends AccessibleJPanel { + @Override + public AccessibleRole getAccessibleRole() { + return AccessibleRole.STATUS_BAR; + } + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/TextPanel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/TextPanel.java index 14f3d9243db9..39f734e027a5 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/TextPanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/status/TextPanel.java @@ -24,10 +24,13 @@ import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; import javax.swing.*; import java.awt.*; -public class TextPanel extends JComponent { +public class TextPanel extends JComponent implements Accessible { @Nullable private String myText; @Nullable private Color myCustomColor; @@ -135,7 +138,20 @@ public class TextPanel extends JComponent { return; } + String oldAccessibleName = null; + if (accessibleContext != null) { + oldAccessibleName = accessibleContext.getAccessibleName(); + } + myText = text; + + if ((accessibleContext != null) && !StringUtil.equals(accessibleContext.getAccessibleName(), oldAccessibleName)) { + accessibleContext.firePropertyChange( + AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, + oldAccessibleName, + accessibleContext.getAccessibleName()); + } + setPreferredSize(getPanelDimensionFromFontMetrics(myText)); revalidate(); repaint(); @@ -227,4 +243,24 @@ public class TextPanel extends JComponent { return new Dimension(size.width + 3, size.height); } } + + @Override + public AccessibleContext getAccessibleContext() { + if (accessibleContext == null) { + accessibleContext = new AccessibleTextPanel(); + } + return accessibleContext; + } + + protected class AccessibleTextPanel extends AccessibleJComponent { + @Override + public AccessibleRole getAccessibleRole() { + return AccessibleRole.LABEL; + } + + @Override + public String getAccessibleName() { + return myText; + } + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/FlatWelcomeFrame.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/FlatWelcomeFrame.java index f4f10cae2ccd..d3697f21fd6c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/FlatWelcomeFrame.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/FlatWelcomeFrame.java @@ -53,10 +53,12 @@ import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.MouseEventAdapter; import com.intellij.util.ui.UIUtil; +import com.intellij.util.ui.accessibility.AccessibleContextAccessor; import com.intellij.util.ui.accessibility.AccessibleContextUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import javax.accessibility.AccessibleContext; import javax.swing.*; import javax.swing.event.ListDataEvent; import javax.swing.event.ListDataListener; @@ -73,7 +75,7 @@ import java.util.List; /** * @author Konstantin Bulenkov */ -public class FlatWelcomeFrame extends JFrame implements IdeFrame { +public class FlatWelcomeFrame extends JFrame implements IdeFrame, AccessibleContextAccessor { private static final String ACTION_GROUP_KEY = "ACTION_GROUP_KEY"; private static final String WELCOME_TITLE = "Welcome to " + ApplicationNamesInfo.getInstance().getFullProductName(); private final BalloonLayout myBalloonLayout; @@ -176,6 +178,11 @@ public class FlatWelcomeFrame extends JFrame implements IdeFrame { return new JBColor(Gray.xEC, new Color(72, 75, 78)); } + @Override + public AccessibleContext getCurrentAccessibleContext() { + return accessibleContext; + } + private class FlatWelcomeScreen extends JPanel implements WelcomeScreen { private JBSlidingPanel mySlidingPanel = new JBSlidingPanel(); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/WelcomeFrame.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/WelcomeFrame.java index 0c2ff8cea7fb..77bfae976eb5 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/WelcomeFrame.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/welcomeScreen/WelcomeFrame.java @@ -43,15 +43,17 @@ import com.intellij.ui.AppUIUtil; import com.intellij.ui.BalloonLayout; import com.intellij.ui.BalloonLayoutImpl; import com.intellij.util.ui.UIUtil; +import com.intellij.util.ui.accessibility.AccessibleContextAccessor; import org.jetbrains.annotations.Nullable; +import javax.accessibility.AccessibleContext; import javax.swing.*; import java.awt.*; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.File; -public class WelcomeFrame extends JFrame implements IdeFrame { +public class WelcomeFrame extends JFrame implements IdeFrame, AccessibleContextAccessor { public static final ExtensionPointName EP = ExtensionPointName.create("com.intellij.welcomeFrameProvider"); static final String DIMENSION_KEY = "WELCOME_SCREEN"; private static IdeFrame ourInstance; @@ -234,4 +236,9 @@ public class WelcomeFrame extends JFrame implements IdeFrame { public JComponent getComponent() { return getRootPane(); } + + @Override + public AccessibleContext getCurrentAccessibleContext() { + return accessibleContext; + } } diff --git a/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java b/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java index fd89ed8f8ec8..f9a3bac17717 100644 --- a/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java @@ -268,6 +268,8 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui { private boolean myHideOnMouse; private final boolean myHideOnKey; private final boolean myHideOnAction; + private final boolean myRequestFocus; + private Component myOriginalFocusOwner; private final boolean myEnableButtons; public BalloonImpl(@NotNull JComponent content, @@ -294,7 +296,8 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui { boolean shadow, boolean smallVariant, boolean blockClicks, - Layer layer) { + Layer layer, + boolean requestFocus) { myBorderColor = borderColor; myBorderInsets = borderInsets != null ? borderInsets : new Insets(3, 3, 3, 3); myFillColor = fillColor; @@ -315,6 +318,7 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui { myTitle = title; myLayer = layer != null ? layer : Layer.normal; myBlockClicks = blockClicks; + myRequestFocus = requestFocus; MnemonicHelper.init(content); if (!myDialogMode) { @@ -468,6 +472,22 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui { } }); } + if (myRequestFocus) { + myFocusManager.doWhenFocusSettlesDown(new ExpirableRunnable() { + @Override + public boolean isExpired() { + return isDisposed(); + } + + @Override + public void run() { + myOriginalFocusOwner = myFocusManager.getFocusOwner(); + + // Set the focus to "myContent" + myFocusManager.requestFocus(getContentToFocus(), true); + } + }); + } myLayeredPane.addComponentListener(myComponentListener); @@ -588,6 +608,32 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui { } } + /** + * Figure out the component to focus inside the {@link myContent} field. + */ + @NotNull + private Component getContentToFocus() { + Component focusComponent = myContent; + while (true) { + // Setting focus to a JScrollPane is not very useful. Better setting focus to the + // contained view. This is useful for Tooltip popups, for example. + if (focusComponent instanceof JScrollPane) { + JViewport viewport = ((JScrollPane)focusComponent).getViewport(); + if (viewport == null) + break; + Component child = viewport.getView(); + if (child == null) + break; + focusComponent = child; + continue; + } + + // Done if we can't find anything to dive into + break; + } + return focusComponent; + } + private Rectangle getRecForPosition(AbstractPosition position, boolean adjust) { Dimension size = getContentSizeFor(position); Rectangle rec = new Rectangle(new Point(0, 0), size); @@ -888,6 +934,11 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui { @Override public void run() { myFadedOut = true; + if (myRequestFocus) { + if (myOriginalFocusOwner != null) { + myFocusManager.requestFocus(myOriginalFocusOwner, false); + } + } for (JBPopupListener each : myListeners) { each.onClosed(new LightweightWindowEvent(BalloonImpl.this, ok)); diff --git a/platform/platform-impl/src/com/intellij/ui/HintHint.java b/platform/platform-impl/src/com/intellij/ui/HintHint.java index 8969a5bca8c5..2291d586c284 100644 --- a/platform/platform-impl/src/com/intellij/ui/HintHint.java +++ b/platform/platform-impl/src/com/intellij/ui/HintHint.java @@ -50,6 +50,7 @@ public class HintHint { private int myPositionChangeY; private boolean myShowImmediately = false; private boolean myAnimationEnabled; + private boolean myRequestFocus; public HintHint() { } @@ -282,4 +283,13 @@ public class HintHint { myAnimationEnabled = enabled; return this; } + + public boolean isRequestFocus() { + return myRequestFocus; + } + + public HintHint setRequestFocus(boolean requestFocus) { + myRequestFocus = requestFocus; + return this; + } } diff --git a/platform/platform-impl/src/com/intellij/ui/LightweightHint.java b/platform/platform-impl/src/com/intellij/ui/LightweightHint.java index c0f8e51d8eb8..49861a2b8b0b 100644 --- a/platform/platform-impl/src/com/intellij/ui/LightweightHint.java +++ b/platform/platform-impl/src/com/intellij/ui/LightweightHint.java @@ -183,6 +183,7 @@ public class LightweightHint extends UserDataHolderBase implements Hint { .setCalloutShift(hintHint.getCalloutShift()) .setPositionChangeShift(hintHint.getPositionChangeX(), hintHint.getPositionChangeY()) .setExplicitClose(hintHint.isExplicitClose()) + .setRequestFocus(hintHint.isRequestFocus()) .setHint(true); myComponent.validate(); myCurrentIdeTooltip = IdeTooltipManager.getInstance().show(tooltip, hintHint.isShowImmediately(), hintHint.isAnimationEnabled()); diff --git a/platform/platform-impl/src/com/intellij/ui/MultilineTreeCellRenderer.java b/platform/platform-impl/src/com/intellij/ui/MultilineTreeCellRenderer.java index 74ab8c508cd0..56452c2807f7 100644 --- a/platform/platform-impl/src/com/intellij/ui/MultilineTreeCellRenderer.java +++ b/platform/platform-impl/src/com/intellij/ui/MultilineTreeCellRenderer.java @@ -19,10 +19,14 @@ import com.intellij.ide.ui.UISettings; import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.components.JBScrollPane; import com.intellij.util.ArrayUtil; +import com.intellij.util.SystemProperties; import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.tree.WideSelectionTreeUI; import org.jetbrains.annotations.NonNls; +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleContext; +import javax.accessibility.AccessibleRole; import javax.swing.*; import javax.swing.plaf.TreeUI; import javax.swing.tree.DefaultMutableTreeNode; @@ -34,7 +38,7 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; -public abstract class MultilineTreeCellRenderer extends JComponent implements TreeCellRenderer { +public abstract class MultilineTreeCellRenderer extends JComponent implements Accessible, TreeCellRenderer { private boolean myWrapsCalculated = false; private boolean myTooSmall = false; @@ -473,5 +477,42 @@ public abstract class MultilineTreeCellRenderer extends JComponent implements Tr // return myDelegatee.getScrollableTracksViewportHeight(); // } // } + + @Override + public AccessibleContext getAccessibleContext() { + if (accessibleContext == null) { + accessibleContext = new AccessibleMultilineTreeCellRenderer(); + } + return accessibleContext; + } + + protected class AccessibleMultilineTreeCellRenderer extends AccessibleJComponent { + @Override + public String getAccessibleName() { + String name = accessibleName; + if (name == null) { + name = (String)getClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY); + } + + if (name == null) { + StringBuilder sb = new StringBuilder(); + for (String aLine : myLines) { + sb.append(aLine); + sb.append(SystemProperties.getLineSeparator()); + } + if (sb.length() > 0) name = sb.toString(); + } + + if (name == null) { + name = super.getAccessibleName(); + } + return name; + } + + @Override + public AccessibleRole getAccessibleRole() { + return AccessibleRole.LABEL; + } + } } diff --git a/platform/platform-impl/src/com/intellij/ui/popup/BalloonPopupBuilderImpl.java b/platform/platform-impl/src/com/intellij/ui/popup/BalloonPopupBuilderImpl.java index 76b6d198396a..2784526b71a5 100644 --- a/platform/platform-impl/src/com/intellij/ui/popup/BalloonPopupBuilderImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/popup/BalloonPopupBuilderImpl.java @@ -68,6 +68,7 @@ public class BalloonPopupBuilderImpl implements BalloonBuilder { private Balloon.Layer myLayer; private boolean myBlockClicks = false; + private boolean myRequestFocus = false; public BalloonPopupBuilderImpl(@Nullable Map> storage, @NotNull final JComponent content) { myStorage = storage; @@ -144,6 +145,13 @@ public class BalloonPopupBuilderImpl implements BalloonBuilder { return this; } + @NotNull + @Override + public BalloonBuilder setRequestFocus(boolean requestFocus) { + myRequestFocus = requestFocus; + return this; + } + @NotNull @Override public BalloonBuilder setAnimationCycle(int time) { @@ -250,7 +258,7 @@ public class BalloonPopupBuilderImpl implements BalloonBuilder { myContent, myBorder, myBorderInsets, myFill, myHideOnMouseOutside, myHideOnKeyOutside, myHideOnAction, myShowCallout, myCloseButtonEnabled, myFadeoutTime, myHideOnFrameResize, myHideOnLinkClick, myClickHandler, myCloseOnClick, myAnimationCycle, myCalloutShift, myPositionChangeXShift, myPositionChangeYShift, myDialogMode, myTitle, myContentInsets, myShadow, mySmallVariant, myBlockClicks, - myLayer); + myLayer, myRequestFocus); if (myStorage != null && myAnchor != null) { List balloons = myStorage.get(myAnchor); diff --git a/platform/platform-impl/src/com/intellij/ui/popup/PopupComponent.java b/platform/platform-impl/src/com/intellij/ui/popup/PopupComponent.java index c537a6fcf893..a5c94ae5dce1 100644 --- a/platform/platform-impl/src/com/intellij/ui/popup/PopupComponent.java +++ b/platform/platform-impl/src/com/intellij/ui/popup/PopupComponent.java @@ -134,7 +134,7 @@ public interface PopupComponent { @Override public void windowClosed(WindowEvent e) { super.windowClosed(e); - A11YFix.invokeFocusGained(myDialog); + //A11YFix.invokeFocusGained(myDialog); } }); } diff --git a/platform/platform-tests/testSrc/com/intellij/ui/BalloonTest.java b/platform/platform-tests/testSrc/com/intellij/ui/BalloonTest.java index e240bb0cbd41..5093f15107ab 100644 --- a/platform/platform-tests/testSrc/com/intellij/ui/BalloonTest.java +++ b/platform/platform-tests/testSrc/com/intellij/ui/BalloonTest.java @@ -60,7 +60,7 @@ public class BalloonTest { balloon.set(new BalloonImpl( new JLabel("Content"), Color.black, null , MessageType.ERROR.getPopupBackground(), true, true, true, true, true, 0, true, false, null, - false, 500, 25, 0, 0, false, "This is the title", new Insets(2, 2, 2, 2), true, false, false, Balloon.Layer.normal)); + false, 500, 25, 0, 0, false, "This is the title", new Insets(2, 2, 2, 2), true, false, false, Balloon.Layer.normal, false)); balloon.get().setShowPointer(true); if (e.isShiftDown()) { diff --git a/platform/util/src/com/intellij/util/ui/accessibility/AccessibleContextAccessor.java b/platform/util/src/com/intellij/util/ui/accessibility/AccessibleContextAccessor.java new file mode 100644 index 000000000000..eb5e301161d6 --- /dev/null +++ b/platform/util/src/com/intellij/util/ui/accessibility/AccessibleContextAccessor.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.util.ui.accessibility; + +import javax.accessibility.AccessibleContext; + +/** + * Interface allowing direct access to the {@link AccessibleContext} instance + * of a component. + */ +public interface AccessibleContextAccessor { + /** + * Same as {@link javax.accessibility.Accessible#getAccessibleContext}, except that the returned + * {@link AccessibleContext} is null if it has not been created yet. + */ + AccessibleContext getCurrentAccessibleContext(); +} diff --git a/platform/util/src/com/intellij/util/ui/accessibility/AccessibleContextUtil.java b/platform/util/src/com/intellij/util/ui/accessibility/AccessibleContextUtil.java index 345e5b1c7bff..29739a9bfba7 100644 --- a/platform/util/src/com/intellij/util/ui/accessibility/AccessibleContextUtil.java +++ b/platform/util/src/com/intellij/util/ui/accessibility/AccessibleContextUtil.java @@ -22,6 +22,10 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; public class AccessibleContextUtil { + public static void setName(@NotNull JComponent component, @NotNull String name) { + component.getAccessibleContext().setAccessibleName(name); + } + public static void setName(@NotNull JComponent component, @NotNull JComponent source) { String name = source.getAccessibleContext().getAccessibleName(); if (name != null) { diff --git a/platform/util/src/com/intellij/util/ui/accessibility/ScreenReader.java b/platform/util/src/com/intellij/util/ui/accessibility/ScreenReader.java new file mode 100644 index 000000000000..cda3d5fcb7c2 --- /dev/null +++ b/platform/util/src/com/intellij/util/ui/accessibility/ScreenReader.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.util.ui.accessibility; + +import com.intellij.util.SystemProperties; + +import javax.accessibility.AccessibleContext; +import java.awt.*; + +/** + * Expose system-wide screen reader status for accessibility features. + */ +public class ScreenReader { + public static String SYSTEM_PROPERTY_KEY = "screenreader"; + + /** + * Components that need to customize their behavior in the presence of a external screen reader + * application should call this method to determine the presence of such screen reader. + * + * For example, this can be used to determine if components should to be focusable via keyboard + * as opposed to accessible only via the mouse pointer. + * + * @return true if a screen reader is currently active, or has been active + * since the start of the application. + */ + public static boolean isActive() { + // Return system property value if it is set + if (SystemProperties.has(SYSTEM_PROPERTY_KEY)) { + return SystemProperties.is(SYSTEM_PROPERTY_KEY); + } + + // Auto-detect if system property not set. + for (Frame frame: Frame.getFrames()) { + if (frame instanceof AccessibleContextAccessor) { + AccessibleContext frameContext = ((AccessibleContextAccessor)frame).getCurrentAccessibleContext(); + if (frameContext != null) + return true; + } + } + return false; + } +}