View Javadoc
1   package org.vaadin.extension.gridscroll.client;
2   
3   import org.vaadin.extension.gridscroll.GridScrollExtension;
4   import org.vaadin.extension.gridscroll.shared.ColumnResizeCompensationMode;
5   import org.vaadin.extension.gridscroll.shared.GridScrollExtensionClientRPC;
6   import org.vaadin.extension.gridscroll.shared.GridScrollExtensionServerRPC;
7   import org.vaadin.extension.gridscroll.shared.GridScrollExtensionState;
8   
9   import com.google.gwt.animation.client.AnimationScheduler;
10  import com.google.gwt.animation.client.AnimationScheduler.AnimationCallback;
11  import com.google.gwt.core.client.Scheduler;
12  import com.google.gwt.dom.client.Element;
13  import com.google.gwt.dom.client.NodeList;
14  import com.google.gwt.user.client.Timer;
15  import com.vaadin.client.BrowserInfo;
16  import com.vaadin.client.ComponentConnector;
17  import com.vaadin.client.ServerConnector;
18  import com.vaadin.client.extensions.AbstractExtensionConnector;
19  import com.vaadin.client.widgets.Grid;
20  import com.vaadin.client.widgets.Grid.Column;
21  import com.vaadin.shared.ui.Connect;
22  
23  @SuppressWarnings("serial")
24  @Connect(GridScrollExtension.class)
25  public class GridScrollExtensionConnector extends AbstractExtensionConnector {
26  
27  	private Grid<?> grid;
28  	
29  	private double xscroll = -1;
30  	private double yscroll = -1;
31  	private int width = -1;
32  	private int heigth = -1;
33  	Timer t = null;
34  	int visibleColIndex = -1;
35  	
36  	private double[] getColumnWidths() {
37  		int columns = grid.getVisibleColumns().size();
38  		double[] widths = new double[columns]; 
39  		for (int i=0;i<columns;i++) widths[i] = grid.getVisibleColumns().get(i).getWidthActual();
40  		return widths;
41  	}
42  	
43  	private double[] getColumnWidthsCapped() {
44  		int columns = grid.getVisibleColumns().size();
45  		double[] widths = new double[columns]; 
46  		for (int i=0;i<columns;i++) {
47  			widths[i] = grid.getVisibleColumns().get(i).getWidthActual();
48  			double maxWidth = grid.getVisibleColumns().get(i).getMaximumWidth();
49  			if (widths[i] > maxWidth && maxWidth > 0) {
50  				widths[i] =  maxWidth;
51  				grid.getVisibleColumns().get(i).setWidth(maxWidth);
52  			}
53  		}
54  		return widths;
55  	}
56  	
57  	// Check if widths has been calculated
58  	private boolean hasWidths(double[] widths) {
59  		boolean hasWidths = true;
60  		for (int i=0;i<grid.getVisibleColumns().size();i++) {
61  			if (!(widths[i] > 0)) hasWidths = false;
62  		}
63  		return hasWidths;
64  	}
65  
66  	// Sets the width of Grid to be sum of widths
67  	private void adjustGridWidth(double[] widths) {
68  		Double width = 0d;
69  		for (int i=0;i<widths.length;i++) width = width + widths[i];
70  		// Add the scroll bar width if it exists 
71  		Double totalWidth = width + 0.5;
72  		Double scrollerWidth = getVerticalScrollBarWidth();
73  		if (scrollerWidth > 0.0) totalWidth = totalWidth + scrollerWidth; 
74  		grid.setWidth(totalWidth.intValue()+"px");
75  	}
76  
77  	// Sets the width of the last Column of the Grid to fit remaining space of Grid
78  	// provided that there is space left
79  	private double adjustLastColumnWidth(double[] widths) {
80  		Double width = 0d;
81  		for (int i=0;i<widths.length;i++) width = width + widths[i];
82  		// Add the scroll bar width if it exists 
83  		Double totalWidth = width + 0.5;
84  		Double scrollerWidth = getVerticalScrollBarWidth();
85  		if (scrollerWidth > 0.0) totalWidth = totalWidth + scrollerWidth; 
86  		Double gridWidth = (double) grid.getOffsetWidth();
87  		if (totalWidth < gridWidth) {
88  			Double targetWidth = gridWidth - totalWidth; 
89  			grid.getVisibleColumns().get(grid.getVisibleColumns().size()-1).setWidth(targetWidth);
90  			return targetWidth;
91  		} else {
92  			return -1.0;
93  		}
94  	}
95  	
96  	// Return -1.0 if Grid has no vertical scroll bar otherwise its width
97  	private double getVerticalScrollBarWidth() {
98  		for (Element e : getGridParts("div")) {
99  			if (e.getClassName().contains("v-grid-scroller-vertical")) {
100 				if (BrowserInfo.get().isIE11() || BrowserInfo.get().isEdge()) { 
101 					return e.getClientWidth();
102 				} else {
103 					return e.getOffsetWidth();					
104 				}
105 			}
106 		}
107 		return -1.0;
108 	}
109 	
110 	// Get elements in Grid by tag name
111 	private Element[] getGridParts(String elem) {
112 		NodeList<Element> elems = grid.getElement().getElementsByTagName(elem);
113 		Element[] ary = new Element[elems.getLength()];
114 		for (int i = 0; i < ary.length; ++i) {
115 			ary[i] = elems.getItem(i);
116 		}
117 		return ary;
118 	}
119 	
120 	@Override
121 	protected void extend(ServerConnector target) {
122 		grid = (Grid<?>)((ComponentConnector)target).getWidget();
123 
124 		grid.addAttachHandler(event -> {
125 			if (event.isAttached()) {
126 				getServerRPC().reportSize(grid.getOffsetWidth(), grid.getOffsetHeight());
127 				Scheduler.get().scheduleFixedDelay(new Scheduler.RepeatingCommand() {
128 					@Override
129 					public boolean execute() {
130 						double[] widths = getColumnWidths();
131 						if (hasWidths(widths)) {
132 							getServerRPC().reportColumns(widths,-1);							
133 							getServerRPC().gridInitialColumnWidthsCalculated();
134 							if (getState().compensationMode == ColumnResizeCompensationMode.RESIZE_GRID) {
135 								adjustGridWidth(widths);
136 							}
137 							return false;
138 						}
139 						else return true;
140 					}
141 				}, 100);
142 			}
143 		});
144 		
145 		registerRpc(GridScrollExtensionClientRPC.class,
146 				new GridScrollExtensionClientRPC() {
147 
148 					@Override
149 					public void setScrollPosition(int x, int y) {
150 						grid.setScrollLeft(x);
151 						grid.setScrollTop(y);
152 						xscroll = x;
153 						yscroll = y;
154 					}
155 
156 					@Override
157 					public void recalculateGridWidth() {
158 						AnimationCallback adjustCallback = new AnimationCallback() {
159 				            @Override
160 				            public void execute(double timestamp) {
161 				            	double[] widths = getColumnWidths();
162 								adjustGridWidth(widths);
163 								getServerRPC().reportColumns(widths,-1);
164 				            }
165 						};
166 						AnimationScheduler.get().requestAnimationFrame(adjustCallback);
167 					}
168 
169 					@Override
170 					public void adjustLastColumn() {
171 						AnimationCallback adjustCallback = new AnimationCallback() {
172 				            @Override
173 				            public void execute(double timestamp) {
174 				            	double[] widths = getColumnWidths();
175 								adjustLastColumnWidth(widths);
176 				            }
177 						};
178 						AnimationScheduler.get().requestAnimationFrame(adjustCallback);
179 					}
180 		});
181 		
182 		// For some odd reason sorting resets Grid size, which may be bug in Grid, this is workaround
183 		grid.addSortHandler(event -> {
184 			AnimationCallback adjustCallback = new AnimationCallback() {
185 	            @Override
186 	            public void execute(double timestamp) {
187 	            	double[] widths = getColumnWidths();
188 					if (getState().compensationMode == ColumnResizeCompensationMode.RESIZE_GRID) adjustGridWidth(widths);
189 					else if (getState().compensationMode == ColumnResizeCompensationMode.RESIZE_COLUMN) adjustLastColumnWidth(widths);
190 	            }
191 			};
192 			if (getState().compensationMode != ColumnResizeCompensationMode.NONE) {
193 				AnimationScheduler.get().requestAnimationFrame(adjustCallback);
194 			}
195 		});
196 		
197 		grid.addColumnVisibilityChangeHandler(event -> {
198 			Column<?, ?> column = event.getColumn();
199 			updateGridDueColumnChange(column);			
200 		});
201 		
202 		grid.addColumnResizeHandler(event -> {
203 			Column<?, ?> column = event.getColumn();
204 			updateGridDueColumnChange(column);
205 		});
206 		
207 		t = new Timer() {
208 			@Override
209 			public void run() {
210 				if(xscroll == -1 || yscroll == -1) {
211 					getServerRPC().reportPosition(-1, -1);
212 					return;
213 				}
214 				
215 				boolean send = false;
216 				double y = grid.getScrollTop();
217 				if(y != yscroll) {
218 					send = true;
219 					yscroll = y;
220 				}
221 				double x = grid.getScrollLeft();
222 				if(x != xscroll) {
223 					send = true;
224 					xscroll = x;
225 				}
226 				if(send) {
227 					getServerRPC().reportPosition((int)(xscroll + .5),(int)(yscroll + .5));
228 				}
229 				
230 				send = false;
231 				int w = grid.getOffsetWidth();
232 				if (w != width) {
233 					send = true;
234 					width = w;
235 				}
236 				int h = grid.getOffsetHeight();
237 				if (h != heigth) {
238 					send = true;
239 					heigth = h;
240 				}
241 				if(send) {
242 					getServerRPC().reportSize(width, heigth);
243 					if (getState().compensationMode == ColumnResizeCompensationMode.RESIZE_COLUMN) {
244 						Scheduler.get().scheduleFinally(new Scheduler.ScheduledCommand() {					
245 							@Override
246 							public void execute() {
247 				    			double[] widths = getColumnWidthsCapped();
248 								adjustLastColumnWidth(widths);
249 								getServerRPC().reportColumns(widths,-1);						
250 							}
251 						});
252 					}
253 				}
254 			}
255 		};
256 		t.scheduleRepeating(250);
257 	}
258 
259 	private void updateGridDueColumnChange(Column<?, ?> column) {
260 		int i = 0;
261 		visibleColIndex = -1;
262 		for (Column<?, ?> col : grid.getVisibleColumns()) {
263 			if (col == column) { 
264 				visibleColIndex = i;
265 				break;
266 			}
267 			i++;
268 		}
269 		if (getState().compensationMode == ColumnResizeCompensationMode.RESIZE_GRID) {
270 			Scheduler.get().scheduleFinally(new Scheduler.ScheduledCommand() {					
271 				@Override
272 				public void execute() {
273 					double[] widths = getColumnWidthsCapped();
274 					getServerRPC().reportSize(grid.getOffsetWidth(), grid.getOffsetHeight());
275 					adjustGridWidth(widths);
276 					getServerRPC().reportColumns(widths,visibleColIndex);
277 				}
278 			});
279 		} else if (getState().compensationMode == ColumnResizeCompensationMode.RESIZE_COLUMN) {
280 			Scheduler.get().scheduleFinally(new Scheduler.ScheduledCommand() {					
281 				@Override
282 				public void execute() {
283 					double[] widths = getColumnWidthsCapped();
284 					double adjustedWidth = adjustLastColumnWidth(widths);
285 					if (visibleColIndex == grid.getVisibleColumns().size()-1) widths[visibleColIndex] = adjustedWidth;
286 					getServerRPC().reportColumns(widths,visibleColIndex);						
287 				}
288 			});
289 		}
290 	}
291 	
292 	@Override
293 	public void onUnregister() {
294 		super.onUnregister();
295 		t.cancel();
296 	}
297 	
298 	private GridScrollExtensionServerRPC getServerRPC() {
299 		return getRpcProxy(GridScrollExtensionServerRPC.class);
300 	}
301 	
302 	@Override
303     public GridScrollExtensionState getState() {
304         return ((GridScrollExtensionState) super.getState());
305     }
306 	
307 	
308 }