mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-10-01 09:03:31 +07:00
IDEA-205679 Added cellEditor tooltips and document listener to ComponentValidator.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+6
-2
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+7
-7
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user