diff --git a/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java b/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java
index 9dc07440394e..0c6e623eedd0 100644
--- a/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java
+++ b/platform/lang-impl/src/com/intellij/injected/editor/EditorWindow.java
@@ -625,4 +625,9 @@ public class EditorWindow extends UserDataHolderBase implements EditorEx {
public void setSoftWrapAppliancePlace(@NotNull SoftWrapAppliancePlaces place) {
myDelegate.setSoftWrapAppliancePlace(place);
}
+
+ @Override
+ public void setPlaceholder(@Nullable CharSequence text) {
+ myDelegate.setPlaceholder(text);
+ }
}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorEx.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorEx.java
index 16a41791ad59..6a26f36dca1b 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorEx.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorEx.java
@@ -164,4 +164,15 @@ public interface EditorEx extends Editor {
* @param place soft wraps appliance appliance use-case
*/
void setSoftWrapAppliancePlace(@NotNull SoftWrapAppliancePlaces place);
+
+ /**
+ * Allows to define 'placeholder text' for the current editor, i.e. virtual text that will be represented until
+ * any user data is entered and current editor is not focused.
+ *
+ * Feel free to see the detailed feature
+ * definition here.
+ *
+ * @param text virtual text to show until user data is entered or the editor is focused
+ */
+ void setPlaceholder(@Nullable CharSequence text);
}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java
index d06e648d25de..b9d337a82627 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java
@@ -259,6 +259,10 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
private char[] myPrefixText;
private TextAttributes myPrefixAttributes;
private final IndentsModel myIndentsModel;
+
+ @Nullable
+ private CharSequence myPlaceholderText;
+ private int myLastPaintedPlaceholderWidth;
static {
ourCaretBlinkingCommand = new RepaintCursorCommand();
@@ -1549,6 +1553,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
paintLineMarkersSeparators(g, clip, docMarkup);
paintLineMarkersSeparators(g, clip, myMarkupModel);
paintText(g, clip);
+ paintPlaceholderText(g, clip);
paintSegmentHighlightersBorderAndAfterEndOfLine(g, clip);
BorderEffect borderEffect = new BorderEffect(this, g);
borderEffect.paintHighlighters(getHighlighter());
@@ -1625,6 +1630,11 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
return myTextDrawingCallback;
}
+ @Override
+ public void setPlaceholder(@Nullable CharSequence text) {
+ myPlaceholderText = text;
+ }
+
private Color getBackgroundColor(final TextAttributes attributes) {
final Color attrColor = attributes.getBackgroundColor();
return attrColor == myScheme.getDefaultBackground() ? getBackgroundColor() : attrColor;
@@ -2331,6 +2341,29 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
flushCachedChars(g);
}
+ private void paintPlaceholderText(Graphics g, Rectangle clip) {
+ CharSequence hintText = myPlaceholderText;
+ if (myDocument.getTextLength() > 0 || hintText == null || hintText.length() == 0) {
+ return;
+ }
+
+ if (KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner() == myEditorComponent) {
+ // There is a possible case that placeholder text was painted and the editor gets focus now. We want to over-paint previuosly
+ // used placeholder text then.
+ myLastBackgroundColor = getBackgroundColor();
+ myLastBackgroundPosition = new Point(0, 0);
+ myLastBackgroundWidth = myLastPaintedPlaceholderWidth;
+ flushBackground(g, clip);
+ }
+ else {
+ myLastPaintedPlaceholderWidth = drawString(
+ g, CharArrayUtil.fromSequence(hintText), 0, hintText.length(), new Point(0, 0), clip, null, null, Font.PLAIN,
+ myFoldingModel.getPlaceholderAttributes().getForegroundColor()
+ );
+ flushCachedChars(g);
+ }
+ }
+
private boolean paintSelection() {
return !isOneLineMode() || IJSwingUtilities.hasFocus(getContentComponent());
}
diff --git a/platform/platform-impl/src/com/intellij/ui/EditorTextField.java b/platform/platform-impl/src/com/intellij/ui/EditorTextField.java
index 410b47c0cf23..688c44987eb4 100644
--- a/platform/platform-impl/src/com/intellij/ui/EditorTextField.java
+++ b/platform/platform-impl/src/com/intellij/ui/EditorTextField.java
@@ -76,6 +76,7 @@ public class EditorTextField extends NonOpaquePanel implements DocumentListener,
private boolean myCenterByHeight = true;
private boolean myEnsureWillComputePreferredSize;
private Dimension myPassivePreferredSize;
+ private CharSequence myHintText;
public EditorTextField() {
this("");
@@ -250,6 +251,20 @@ public class EditorTextField extends NonOpaquePanel implements DocumentListener,
});
}
+ /**
+ * Allows to define {@link EditorEx#setPlaceholder(CharSequence) editor's placeholder}. The trick here is that the editor
+ * is instantiated lazily by the editor text field and provided placeholder text is applied to the editor during its
+ * actual construction then.
+ *
+ * @param text {@link EditorEx#setPlaceholder(CharSequence) editor's placeholder} text to use
+ */
+ public void setPlaceholder(@Nullable CharSequence text) {
+ myHintText = text;
+ if (myEditor != null) {
+ myEditor.setPlaceholder(text);
+ }
+ }
+
public void selectAll() {
if (myEditor != null) {
myEditor.getSelectionModel().setSelection(0, myDocument.getTextLength());
@@ -485,6 +500,8 @@ public class EditorTextField extends NonOpaquePanel implements DocumentListener,
editor.putUserData(SUPPLEMENTARY_KEY, myIsSupplementary);
editor.getContentComponent().setFocusCycleRoot(false);
+
+ editor.setPlaceholder(myHintText);
initOneLineMode(editor);