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 JTextComponent; text component to receive ghost text.
43       * @param ghostText String; 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 Color; ghost color.
89       */
90      public Color getGhostColor()
91      {
92          return this.ghostColor;
93      }
94  
95      /**
96       * Set ghost color.
97       * @param ghostColor Color; 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     /** {@inheritDoc} */
114     @Override
115     public void focusGained(final FocusEvent e)
116     {
117         if (this.isEmpty)
118         {
119             unregisterListeners();
120             try
121             {
122                 this.textComp.setText("");
123                 this.textComp.setForeground(this.foregroundColor);
124             }
125             finally
126             {
127                 registerListeners();
128             }
129         }
130 
131     }
132 
133     /** {@inheritDoc} */
134     @Override
135     public void focusLost(final FocusEvent e)
136     {
137         if (this.isEmpty)
138         {
139             unregisterListeners();
140             try
141             {
142                 this.textComp.setText(this.ghostText);
143                 this.textComp.setForeground(this.ghostColor);
144             }
145             finally
146             {
147                 registerListeners();
148             }
149         }
150     }
151 
152     /** {@inheritDoc} */
153     @Override
154     public void propertyChange(final PropertyChangeEvent evt)
155     {
156         updateState();
157     }
158 
159     /** {@inheritDoc} */
160     @Override
161     public void changedUpdate(final DocumentEvent e)
162     {
163         updateState();
164     }
165 
166     /** {@inheritDoc} */
167     @Override
168     public void insertUpdate(final DocumentEvent e)
169     {
170         updateState();
171     }
172 
173     /** {@inheritDoc} */
174     @Override
175     public void removeUpdate(final DocumentEvent e)
176     {
177         updateState();
178     }
179 
180 }