IDEA-205679 Added cellEditor tooltips and document listener to ComponentValidator.

This commit is contained in:
Kirill Kirichenko
2019-02-04 15:13:11 +03:00
parent a8a9ca709c
commit 2e80fceaa9
4 changed files with 74 additions and 65 deletions
@@ -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);
}
});
}
@@ -27,11 +27,15 @@ public abstract class CellComponentProvider<C extends JComponent> {
@NotNull
abstract public Rectangle getCellRect(@NotNull MouseEvent e);
public static CellComponentProvider<JTable> forTable(JTable table) {
return new TableProvider(table);
}
/**
* Convenient classes with standard implementations.
*/
public static class TableProvider extends CellComponentProvider<JTable> {
public TableProvider(@NotNull JTable owner) {
private static class TableProvider extends CellComponentProvider<JTable> {
private TableProvider(@NotNull JTable owner) {
super(owner);
}
@@ -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);
}
}
}
@@ -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.<br/>Should contain at least 5 symbols.<br/>Please <a href=\"#check.rules\">check rules.</a>", 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<String, JComponent, ValidationInfo> validationInfoGenerator = (text, component) ->
new ValidationInfo("Illegal value: " + text + "<br/>Please <a href=\"#check.cell.rules\">check rules.</a>", 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() + "<br/>Please <a href=\"#check.cell.rules\">check rules.</a>")));
validationInfoGenerator.apply(value.toString(), null)));
// Configure right column
JComboBox<Integer> 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 {