View Javadoc
1   package org.vaadin.hene.expandingtextarea.widgetset.client.ui;
2   
3   import java.util.ArrayList;
4   import java.util.List;
5   
6   import com.google.gwt.user.client.Element;
7   import com.google.gwt.user.client.Event;
8   import com.google.gwt.user.client.Timer;
9   import com.vaadin.v7.client.ui.VTextArea;
10  
11  /**
12   * Client side widget which communicates with the server. Messages from the
13   * server are shown as HTML and mouse clicks are sent to the server.
14   */
15  public class VExpandingTextArea extends VTextArea {
16  
17      /** Set the CSS class name to allow styling. */
18      public static final String CLASSNAME = "v-expandingtextarea";
19      
20      private static int REPEAT_INTERVAL = 400;
21  
22      private Integer maxRows = null;
23  
24      private final HeightObserver heightObserver;
25  
26      public VExpandingTextArea() {
27          setStyleName(CLASSNAME);
28          sinkEvents(Event.ONFOCUS | Event.ONFOCUS);
29  
30          heightObserver = new HeightObserver();
31      }
32  
33      @Override
34      public void onBrowserEvent(Event event) {
35          super.onBrowserEvent(event);
36          if (event.getTypeInt() == Event.ONFOCUS) {
37              heightObserver.scheduleRepeating(REPEAT_INTERVAL);
38              getElement().addClassName(CLASSNAME + "-focus");
39          } else if (event.getTypeInt() == Event.ONBLUR) {
40              heightObserver.cancel();
41              getElement().removeClassName(CLASSNAME + "-focus");
42          }
43      }
44  
45      public void checkHeight() {
46          int origRows = getRows(getElement());
47  
48          // Check if we have to increase textarea's height
49          int rows = origRows;
50          rows++;
51          while ((maxRows == null || rows <= maxRows)
52                  && getElement().getScrollHeight() > getOffsetHeight()) {
53              setRows(rows++);
54          }
55  
56          // Check if we can reduce textarea's height
57          rows = getRows(getElement());
58          while (rows > 1) {
59              setRows(rows - 1);
60              if (!(getElement().getScrollHeight() > getOffsetHeight())) {
61                  rows -= 1;
62                  continue;
63              } else {
64                  setRows(rows);
65                  break;
66              }
67          }
68  
69          int updatedRowCount = getRows(getElement()) + 1;
70          // Add stylename if we have reached maximum row number, so we can show a
71          // scroll bar
72          if (maxRows != null && updatedRowCount > maxRows) {
73              addStyleName("max");
74              updatedRowCount = updatedRowCount > maxRows ? maxRows
75                      : updatedRowCount;
76          } else {
77              removeStyleName("max");
78          }
79  
80          setRows(updatedRowCount);
81  
82          if (origRows != getRows(getElement())) {
83          	for (HeightChangedListener listener : heightChangedListeners) {
84          		listener.heightChanged(getRows(getElement()));
85          	}
86          }
87      }
88  
89      public void setMaxRows(Integer maxRows) {
90          this.maxRows = maxRows;
91      }
92  
93      public int getRows() {
94          return getRows(getElement());
95      }
96  
97      private native int getRows(Element e)
98      /*-{
99      	try {
100     		return e.rows;
101     	} catch (e) {
102     		return -1;
103     	}
104     }-*/;
105 
106     private class HeightObserver extends Timer {
107 
108         @Override
109         public void run() {
110             checkHeight();
111         }
112     }
113 
114     @Override
115     protected void onDetach() {
116         heightObserver.cancel();
117         super.onDetach();
118     }
119     
120     List<HeightChangedListener> heightChangedListeners = new ArrayList<VExpandingTextArea.HeightChangedListener>();
121     
122     public interface HeightChangedListener {
123     	void heightChanged(int newHeight);
124     }
125     
126     public void addHeightChangedListener(HeightChangedListener listener) {
127     	heightChangedListeners.add(listener);
128     }
129     
130     public void removeHeightChangedListener(HeightChangedListener listener) {
131     	heightChangedListeners.remove(listener);
132     }
133 }