View Javadoc
1   package org.opentrafficsim.swing.gui;
2   
3   import java.awt.Color;
4   import java.awt.event.FocusEvent;
5   import java.awt.event.FocusListener;
6   import java.beans.PropertyChangeEvent;
7   import java.beans.PropertyChangeListener;
8   
9   import javax.swing.event.DocumentEvent;
10  import javax.swing.event.DocumentListener;
11  import javax.swing.text.JTextComponent;
12  
13  /**
14   * Code taken from stack overflow.
15   * <p>
16   * Copyright (c) 2024-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
17   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
18   * </p>
19   * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
20   * @see <a href=
21   *      "https://stackoverflow.com/questions/10506789/how-to-display-faint-gray-ghost-text-in-a-jtextfield">stackoverflow</a>
22   */
23  public class GhostText implements FocusListener, DocumentListener, PropertyChangeListener
24  {
25      /** Text component. */
26      private final JTextComponent textComp;
27  
28      /** Whether its empty. */
29      private boolean isEmpty;
30  
31      /** Ghost color. */
32      private Color ghostColor;
33  
34      /** Regular color. */
35      private Color foregroundColor;
36  
37      /** Ghost text. */
38      private final String ghostText;
39  
40      /**
41       * Constructor.
42       * @param textComp text component to receive ghost text.
43       * @param ghostText ghost text.
44       */
45      public GhostText(final JTextComponent textComp, final String ghostText)
46      {
47          this.textComp = textComp;
48          this.ghostText = ghostText;
49          this.ghostColor = Color.LIGHT_GRAY;
50          textComp.addFocusListener(this);
51          registerListeners();
52          updateState();
53          if (!this.textComp.hasFocus())
54          {
55              focusLost(null);
56          }
57      }
58  
59      /**
60       * Delete ghost text.
61       */
62      public void delete()
63      {
64          unregisterListeners();
65          this.textComp.removeFocusListener(this);
66      }
67  
68      /**
69       * Register listeners.
70       */
71      private void registerListeners()
72      {
73          this.textComp.getDocument().addDocumentListener(this);
74          this.textComp.addPropertyChangeListener("foreground", this);
75      }
76  
77      /**
78       * Unregister listeners.
79       */
80      private void unregisterListeners()
81      {
82          this.textComp.getDocument().removeDocumentListener(this);
83          this.textComp.removePropertyChangeListener("foreground", this);
84      }
85  
86      /**
87       * Get ghost color.
88       * @return ghost color.
89       */
90      public Color getGhostColor()
91      {
92          return this.ghostColor;
93      }
94  
95      /**
96       * Set ghost color.
97       * @param ghostColor ghost color.
98       */
99      public void setGhostColor(final Color ghostColor)
100     {
101         this.ghostColor = ghostColor;
102     }
103 
104     /**
105      * Update state.
106      */
107     private void updateState()
108     {
109         this.isEmpty = this.textComp.getText().length() == 0;
110         this.foregroundColor = this.textComp.getForeground();
111     }
112 
113     @Override
114     public void focusGained(final FocusEvent e)
115     {
116         if (this.isEmpty)
117         {
118             unregisterListeners();
119             try
120             {
121                 this.textComp.setText("");
122                 this.textComp.setForeground(this.foregroundColor);
123             }
124             finally
125             {
126                 registerListeners();
127             }
128         }
129 
130     }
131 
132     @Override
133     public void focusLost(final FocusEvent e)
134     {
135         if (this.isEmpty)
136         {
137             unregisterListeners();
138             try
139             {
140                 this.textComp.setText(this.ghostText);
141                 this.textComp.setForeground(this.ghostColor);
142             }
143             finally
144             {
145                 registerListeners();
146             }
147         }
148     }
149 
150     @Override
151     public void propertyChange(final PropertyChangeEvent evt)
152     {
153         updateState();
154     }
155 
156     @Override
157     public void changedUpdate(final DocumentEvent e)
158     {
159         updateState();
160     }
161 
162     @Override
163     public void insertUpdate(final DocumentEvent e)
164     {
165         updateState();
166     }
167 
168     @Override
169     public void removeUpdate(final DocumentEvent e)
170     {
171         updateState();
172     }
173 
174 }