From 2e80fceaa9e13b45ced7cca0a2fd7dd01d906597 Mon Sep 17 00:00:00 2001 From: Kirill Kirichenko Date: Mon, 28 Jan 2019 13:52:31 +0300 Subject: [PATCH] IDEA-205679 Added cellEditor tooltips and document listener to ComponentValidator. --- .../openapi/ui/ComponentValidator.java | 40 +++++++--- .../cellvalidators/CellComponentProvider.java | 8 +- .../ui/cellvalidators/CellTooltipManager.java | 14 ++-- .../internal/ui/ComponentPanelTestAction.java | 77 ++++++++----------- 4 files changed, 74 insertions(+), 65 deletions(-) diff --git a/platform/platform-api/src/com/intellij/openapi/ui/ComponentValidator.java b/platform/platform-api/src/com/intellij/openapi/ui/ComponentValidator.java index 244a70247b88..d1adcf2d43f4 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/ComponentValidator.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/ComponentValidator.java @@ -9,6 +9,7 @@ import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.ColorUtil; +import com.intellij.ui.DocumentAdapter; import com.intellij.ui.awt.RelativePoint; import com.intellij.util.Alarm; import com.intellij.util.ui.JBEmptyBorder; @@ -20,6 +21,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.border.Border; +import javax.swing.event.DocumentEvent; import javax.swing.event.HyperlinkListener; import javax.swing.plaf.basic.BasicHTML; import javax.swing.text.DefaultCaret; @@ -157,6 +159,19 @@ public class ComponentValidator { return this; } + /** + * Convenient wrapper for mostly used scenario. + */ + public ComponentValidator andRegisterOnDocumentListener(@NotNull JTextComponent textComponent) { + textComponent.getDocument().addDocumentListener(new DocumentAdapter() { + @Override + protected void textChanged(@NotNull DocumentEvent e) { + getInstance(textComponent).ifPresent(ComponentValidator::revalidate); // Don't use 'this' to avoid cyclic references. + } + }); + return this; + } + public void revalidate() { if (validator != null) { updateInfo(validator.get()); @@ -175,7 +190,7 @@ public class ComponentValidator { validationInfo.component.repaint(); } - hidePopup(); + hidePopup(true); popupBuilder = null; popupLocation = null; @@ -288,15 +303,18 @@ public class ComponentValidator { } } - private void hidePopup() { + private void hidePopup(boolean now) { if (popup != null && popup.isVisible()) { - popupAlarm.cancelAllRequests(); - popupAlarm.addRequest(() -> { - if (popup != null && (!isOverPopup || hyperlinkListener == null)) { - popup.cancel(); - popup = null; - } - }, Registry.intValue("ide.tooltip.initialDelay.highlighter")); + if (now || hyperlinkListener == null) { + popup.cancel(); + popup = null; + } else { + popupAlarm.addRequest(() -> { + if (!isOverPopup || hyperlinkListener == null) { + hidePopup(true); + } + }, Registry.intValue("ide.tooltip.initialDelay.highlighter")); + } } } @@ -319,7 +337,7 @@ public class ComponentValidator { @Override public void focusLost(FocusEvent e) { - hidePopup(); + hidePopup(false); ValidationInfo info = null; if (focusValidator != null) { @@ -346,7 +364,7 @@ public class ComponentValidator { if (validationInfo != null) { getFocusable(validationInfo.component).ifPresent(fc -> { if (!fc.hasFocus()) { - hidePopup(); + hidePopup(false); } }); } diff --git a/platform/platform-api/src/com/intellij/openapi/ui/cellvalidators/CellComponentProvider.java b/platform/platform-api/src/com/intellij/openapi/ui/cellvalidators/CellComponentProvider.java index 861974b98746..a82084e85bd2 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/cellvalidators/CellComponentProvider.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/cellvalidators/CellComponentProvider.java @@ -27,11 +27,15 @@ public abstract class CellComponentProvider { @NotNull abstract public Rectangle getCellRect(@NotNull MouseEvent e); + public static CellComponentProvider forTable(JTable table) { + return new TableProvider(table); + } + /** * Convenient classes with standard implementations. */ - public static class TableProvider extends CellComponentProvider { - public TableProvider(@NotNull JTable owner) { + private static class TableProvider extends CellComponentProvider { + private TableProvider(@NotNull JTable owner) { super(owner); } diff --git a/platform/platform-api/src/com/intellij/openapi/ui/cellvalidators/CellTooltipManager.java b/platform/platform-api/src/com/intellij/openapi/ui/cellvalidators/CellTooltipManager.java index 03f8116fa1af..1018c3905c60 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/cellvalidators/CellTooltipManager.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/cellvalidators/CellTooltipManager.java @@ -61,7 +61,7 @@ public final class CellTooltipManager { component.addMouseMotionListener(mouseListener); Disposer.register(parentDisposable, () -> { - closePopup(true, null); + hidePopup(true, null); component.removeMouseListener(mouseListener); component.removeMouseMotionListener(mouseListener); @@ -90,13 +90,13 @@ public final class CellTooltipManager { popupSize = tipComponent.getPreferredSize(); }); - closePopup(false, () -> showPopup(e)); + hidePopup(false, () -> showPopup(e)); } else if (!isShowing()) { showPopup(e); } } else { validationInfo = null; - closePopup(false, null); + hidePopup(false, null); } } } @@ -113,7 +113,7 @@ public final class CellTooltipManager { cellPopup.show(new RelativePoint(cellComponentProvider.getOwner(), point)); } - private void closePopup(boolean now, @Nullable Runnable onHidden) { + private void hidePopup(boolean now, @Nullable Runnable onHidden) { if (isShowing()) { if (now || hyperlinkListener == null || !closeWithDelay) { cellPopup.cancel(); @@ -127,7 +127,7 @@ public final class CellTooltipManager { popupAlarm.addRequest(() -> { isClosing = false; if (!isOverPopup) { - closePopup(true, onHidden); + hidePopup(true, onHidden); } }, Registry.intValue("ide.tooltip.initialDelay.highlighter")); } @@ -148,7 +148,7 @@ public final class CellTooltipManager { @Override public void mouseExited(MouseEvent e) { - closePopup(false, null); + hidePopup(false, null); } @Override @@ -166,7 +166,7 @@ public final class CellTooltipManager { @Override public void mouseExited(MouseEvent e) { isOverPopup = false; - closePopup(true, null); + hidePopup(true, null); } } } diff --git a/platform/platform-impl/src/com/intellij/internal/ui/ComponentPanelTestAction.java b/platform/platform-impl/src/com/intellij/internal/ui/ComponentPanelTestAction.java index 9c18bcce0404..738bfdb2476c 100644 --- a/platform/platform-impl/src/com/intellij/internal/ui/ComponentPanelTestAction.java +++ b/platform/platform-impl/src/com/intellij/internal/ui/ComponentPanelTestAction.java @@ -13,7 +13,6 @@ import com.intellij.openapi.ui.*; import com.intellij.openapi.ui.cellvalidators.CellComponentProvider; import com.intellij.openapi.ui.cellvalidators.CellTooltipManager; import com.intellij.openapi.ui.cellvalidators.ValidatingTableCellRendererWrapper; -import com.intellij.openapi.ui.panel.ComponentPanel; import com.intellij.openapi.ui.panel.ProgressPanel; import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.*; @@ -36,6 +35,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.event.DocumentEvent; import javax.swing.event.HyperlinkEvent; +import javax.swing.event.HyperlinkListener; import javax.swing.plaf.basic.BasicComboBoxEditor; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.DefaultTableModel; @@ -45,6 +45,7 @@ import java.net.MalformedURLException; import java.net.URL; import java.util.Arrays; import java.util.HashSet; +import java.util.function.BiFunction; public class ComponentPanelTestAction extends DumbAwareAction { private enum Placement { @@ -188,7 +189,7 @@ public class ComponentPanelTestAction extends DumbAwareAction { } else { return null; } - }).installOn(text1); + }).andRegisterOnDocumentListener(text1).installOn(text1); Dimension d = text1.getPreferredSize(); text1.setPreferredSize(new Dimension(JBUI.scale(100), d.height)); @@ -208,31 +209,11 @@ public class ComponentPanelTestAction extends DumbAwareAction { String tt = text2.getText(); return StringUtil.isEmpty(tt) || tt.length() < 5 ? new ValidationInfo("Message is too short.
Should contain at least 5 symbols.
Please check rules.", text2) : null; - }).andStartOnFocusLost().installOn(text2); + }).andStartOnFocusLost().andRegisterOnDocumentListener(text2).installOn(text2); gc.gridy++; topPanel.add(UI.PanelFactory.panel(text2).withLabel("&Path:").createPanel(), gc); - ComponentPanel cp = ComponentPanel.getComponentPanel(text2); - text1.getDocument().addDocumentListener(new DocumentAdapter() { - @Override - protected void textChanged(@NotNull DocumentEvent e) { - String text = text1.getText(); - if (cp != null) { - cp.setCommentText(text); - } - - ComponentValidator.getInstance(text1).ifPresent(ComponentValidator::revalidate); - } - }); - - text2.getDocument().addDocumentListener(new DocumentAdapter() { - @Override - protected void textChanged(@NotNull DocumentEvent e) { - ComponentValidator.getInstance(text2).ifPresent(ComponentValidator::revalidate); - } - }); - JCheckBox cb1 = new JCheckBox("Scroll tab layout"); cb1.addActionListener(e -> pane.setTabLayoutPolicy(cb1.isSelected() ? JTabbedPane.SCROLL_TAB_LAYOUT : JTabbedPane.WRAP_TAB_LAYOUT)); gc.gridy++; @@ -291,14 +272,20 @@ public class ComponentPanelTestAction extends DumbAwareAction { } }); + HyperlinkListener hyperlinkListener = e -> { + if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { + System.out.println("Table cell tooltip link clicked. Desc = " + e.getDescription()); + } + }; + + BiFunction validationInfoGenerator = (text, component) -> + new ValidationInfo("Illegal value: " + text + "
Please check rules.", component); + // Install custom tooltip manager for displaying error/warning tooltips new CellTooltipManager(getDisposable()). - withCellComponentProvider(new CellComponentProvider.TableProvider(table)). - withHyperlinkListener(e -> { - if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { - System.out.println("Table cell tooltip link clicked. Desc = " + e.getDescription()); - } - }).installOn(table); + //withCellComponentProvider(CellComponentProvider.forTable(table)). + withCellComponentProvider(CellComponentProvider.forTable(table)). + withHyperlinkListener(hyperlinkListener).installOn(table); // Configure left column ExtendableTextField cellEditor = new ExtendableTextField(); @@ -306,23 +293,24 @@ public class ComponentPanelTestAction extends DumbAwareAction { ExtendableTextComponent.Extension.create(AllIcons.General.OpenDisk, AllIcons.General.OpenDiskHover, "Open file", () -> System.out.println("Table browse clicked")); ExtendableTextComponent.Extension errorExtension = - ExtendableTextComponent.Extension.create(AllIcons.General.BalloonError, "Value not allowed", () -> System.out.println("Error clicked")); + ExtendableTextComponent.Extension.create(AllIcons.General.BalloonError, null, null); cellEditor.addExtension(browseExtension); cellEditor.putClientProperty(DarculaUIUtil.COMPACT_PROPERTY, Boolean.TRUE); - cellEditor.getDocument().addDocumentListener(new DocumentAdapter() { - @Override - protected void textChanged(@NotNull DocumentEvent e) { - if (ALLOWED_VALUES.contains(cellEditor.getText())) { - cellEditor.putClientProperty("JComponent.outline", null); - cellEditor.removeExtension(errorExtension); - } - else if (cellEditor.getClientProperty("JComponent.outline") == null) { - cellEditor.putClientProperty("JComponent.outline", "error"); - cellEditor.addExtension(errorExtension); - } + + new ComponentValidator(getDisposable()).withValidator(() -> { + boolean isAllowed = ALLOWED_VALUES.contains(cellEditor.getText()); + if (isAllowed) { + cellEditor.removeExtension(errorExtension); } - }); + else if (cellEditor.getClientProperty("JComponent.outline") == null) { + cellEditor.addExtension(errorExtension); + } + + return isAllowed ? null : validationInfoGenerator.apply(cellEditor.getText(), cellEditor); + }).withHyperlinkListener(hyperlinkListener). + andRegisterOnDocumentListener(cellEditor). + installOn(cellEditor); TableColumn col = table.getColumnModel().getColumn(0); col.setCellEditor(new DefaultCellEditor(cellEditor)); @@ -331,7 +319,7 @@ public class ComponentPanelTestAction extends DumbAwareAction { withCellValidator(value -> value == null ? new ValidationInfo("Null value") : ALLOWED_VALUES.contains(value.toString()) ? null : - new ValidationInfo("Illegal value: " + value.toString() + "
Please check rules."))); + validationInfoGenerator.apply(value.toString(), null))); // Configure right column JComboBox rightEditor = new ComboBox<>(Arrays.stream(data).map(i -> Integer.valueOf(i[1])).toArray(Integer[]::new)); @@ -358,8 +346,7 @@ public class ComponentPanelTestAction extends DumbAwareAction { } } } - }). - bindToEditorSize(rightEditor::getPreferredSize). + }).bindToEditorSize(rightEditor::getPreferredSize). withCellValidator(value -> { if (value == null) return NULL_VALUE_ERROR; else {