1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.vaadin.client.ui;
18
19 import com.google.gwt.core.client.Scheduler;
20 import com.google.gwt.core.client.Scheduler.ScheduledCommand;
21 import com.google.gwt.dom.client.Element;
22 import com.google.gwt.dom.client.Style.Display;
23 import com.google.gwt.dom.client.Style.Overflow;
24 import com.google.gwt.dom.client.Style.Unit;
25 import com.google.gwt.event.dom.client.KeyCodes;
26 import com.google.gwt.event.logical.shared.ValueChangeEvent;
27 import com.google.gwt.event.logical.shared.ValueChangeHandler;
28 import com.google.gwt.event.shared.HandlerRegistration;
29 import com.google.gwt.user.client.Command;
30 import com.google.gwt.user.client.DOM;
31 import com.google.gwt.user.client.Event;
32 import com.google.gwt.user.client.Window;
33 import com.google.gwt.user.client.ui.HasValue;
34 import com.vaadin.client.ApplicationConnection;
35 import com.vaadin.client.BrowserInfo;
36 import com.vaadin.client.WidgetUtil;
37 import com.vaadin.shared.ui.slider.SliderOrientation;
38
39 public class VSliderPatched extends SimpleFocusablePanel implements Field,
40 HasValue<Double>, SubPartAware {
41
42 public static final String CLASSNAME = "v-slider";
43
44
45
46
47
48 private static final int MIN_SIZE = 50;
49
50 protected ApplicationConnection client;
51
52 protected String id;
53
54 protected boolean immediate;
55 protected boolean disabled;
56 protected boolean readonly;
57
58 private int acceleration = 1;
59 protected double min;
60 protected double max;
61 protected int resolution;
62 protected Double value;
63 protected SliderOrientation orientation = SliderOrientation.HORIZONTAL;
64
65
66 private final Element base;
67 private final int BASE_BORDER_WIDTH = 1;
68
69
70 private final Element handle;
71
72
73 private final Element smaller;
74
75
76 private final Element bigger;
77
78
79 private boolean dragging = false;
80
81 private VLazyExecutor delayedValueUpdater = new VLazyExecutor(100,
82 new ScheduledCommand() {
83
84 @Override
85 public void execute() {
86 fireValueChanged();
87 acceleration = 1;
88 }
89 });
90
91 public VSliderPatched() {
92 super();
93
94 base = DOM.createDiv();
95 handle = DOM.createDiv();
96 smaller = DOM.createDiv();
97 bigger = DOM.createDiv();
98
99 setStyleName(CLASSNAME);
100
101 getElement().appendChild(bigger);
102 getElement().appendChild(smaller);
103 getElement().appendChild(base);
104 base.appendChild(handle);
105
106
107 smaller.getStyle().setDisplay(Display.NONE);
108 bigger.getStyle().setDisplay(Display.NONE);
109
110 sinkEvents(Event.MOUSEEVENTS | Event.ONMOUSEWHEEL | Event.KEYEVENTS
111 | Event.FOCUSEVENTS | Event.TOUCHEVENTS);
112
113 }
114
115 @Override
116 public void setStyleName(String style) {
117 updateStyleNames(style, false);
118 }
119
120 @Override
121 public void setStylePrimaryName(String style) {
122 updateStyleNames(style, true);
123 }
124
125 protected void updateStyleNames(String styleName, boolean isPrimaryStyleName) {
126
127 removeStyleName(getStylePrimaryName() + "-vertical");
128
129 if (isPrimaryStyleName) {
130 super.setStylePrimaryName(styleName);
131 } else {
132 super.setStyleName(styleName);
133 }
134
135 base.setClassName(getStylePrimaryName() + "-base");
136 handle.setClassName(getStylePrimaryName() + "-handle");
137 smaller.setClassName(getStylePrimaryName() + "-smaller");
138 bigger.setClassName(getStylePrimaryName() + "-bigger");
139
140 if (isVertical()) {
141 addStyleName(getStylePrimaryName() + "-vertical");
142 }
143 }
144
145
146 public void buildBase() {
147 final String styleAttribute = isVertical() ? "height" : "width";
148 final String oppositeStyleAttribute = isVertical() ? "width" : "height";
149 final String domProperty = isVertical() ? "offsetHeight"
150 : "offsetWidth";
151
152
153 base.getStyle().clearProperty(oppositeStyleAttribute);
154
155
156
157
158
159
160
161
162
163
164 if (getElement().hasParentElement()) {
165 final Element p = getElement();
166 if (p.getPropertyInt(domProperty) > MIN_SIZE) {
167 if (isVertical()) {
168 setHeight();
169 } else {
170 base.getStyle().clearProperty(styleAttribute);
171 }
172 } else {
173
174
175 base.getStyle().setPropertyPx(styleAttribute, MIN_SIZE);
176 Scheduler.get().scheduleDeferred(new Command() {
177
178 @Override
179 public void execute() {
180 final Element p = getElement();
181 if (p.getPropertyInt(domProperty) > (MIN_SIZE + 5)
182 || propertyNotNullOrEmpty(styleAttribute, p)) {
183 if (isVertical()) {
184 setHeight();
185 } else {
186 base.getStyle().clearProperty(styleAttribute);
187 }
188
189 setValue(value, false);
190 }
191 }
192
193
194 private boolean propertyNotNullOrEmpty(
195 final String styleAttribute, final Element p) {
196 return p.getStyle().getProperty(styleAttribute) != null
197 && !p.getStyle().getProperty(styleAttribute)
198 .isEmpty();
199 }
200 });
201 }
202 }
203
204 if (!isVertical()) {
205
206 Scheduler.get().scheduleDeferred(new Command() {
207 @Override
208 public void execute() {
209 buildHandle();
210 setValue(value, false);
211 }
212 });
213 } else {
214 buildHandle();
215 setValue(value, false);
216 }
217
218
219 }
220
221 void buildHandle() {
222 final String handleAttribute = isVertical() ? "marginTop"
223 : "marginLeft";
224 final String oppositeHandleAttribute = isVertical() ? "marginLeft"
225 : "marginTop";
226
227 handle.getStyle().setProperty(handleAttribute, "0");
228
229
230 handle.getStyle().clearProperty(oppositeHandleAttribute);
231 }
232
233 @Override
234 public void onBrowserEvent(Event event) {
235 if (disabled || readonly) {
236 return;
237 }
238 final Element targ = DOM.eventGetTarget(event);
239 final Element slider = getElement();
240
241 if (DOM.eventGetType(event) == Event.ONMOUSEWHEEL) {
242 processMouseWheelEvent(event);
243 } else if (dragging || targ == handle || targ == base || targ == slider) {
244 processHandleEvent(event);
245 } else if (targ == smaller) {
246 decreaseValue(true);
247 } else if (targ == bigger) {
248 increaseValue(true);
249 } else if (DOM.eventGetType(event) == Event.MOUSEEVENTS) {
250 processBaseEvent(event);
251 } else if ((BrowserInfo.get().isGecko() && DOM.eventGetType(event) == Event.ONKEYPRESS)
252 || (!BrowserInfo.get().isGecko() && DOM.eventGetType(event) == Event.ONKEYDOWN)) {
253
254 if (handleNavigation(event.getKeyCode(), event.getCtrlKey(),
255 event.getShiftKey())) {
256
257 delayedValueUpdater.trigger();
258
259 DOM.eventPreventDefault(event);
260 DOM.eventCancelBubble(event, true);
261 }
262 } else if (targ.equals(getElement())
263 && DOM.eventGetType(event) == Event.ONFOCUS) {
264 } else if (targ.equals(getElement())
265 && DOM.eventGetType(event) == Event.ONBLUR) {
266 } else if (DOM.eventGetType(event) == Event.ONMOUSEDOWN) {
267 }
268 if (WidgetUtil.isTouchEvent(event)) {
269 event.preventDefault();
270 event.stopPropagation();
271 }
272 }
273
274 private void processMouseWheelEvent(final Event event) {
275 final int dir = DOM.eventGetMouseWheelVelocityY(event);
276
277 if (dir < 0) {
278 increaseValue(false);
279 } else {
280 decreaseValue(false);
281 }
282
283 delayedValueUpdater.trigger();
284
285 DOM.eventPreventDefault(event);
286 DOM.eventCancelBubble(event, true);
287 }
288
289 private void processHandleEvent(Event event) {
290 switch (DOM.eventGetType(event)) {
291 case Event.ONMOUSEDOWN:
292 case Event.ONTOUCHSTART:
293 if (!disabled && !readonly) {
294 focus();
295 dragging = true;
296 handle.setClassName(getStylePrimaryName() + "-handle");
297 handle.addClassName(getStylePrimaryName() + "-handle-active");
298
299 DOM.setCapture(getElement());
300 DOM.eventPreventDefault(event);
301 DOM.eventCancelBubble(event, true);
302 setValueByEvent(event, true);
303 event.stopPropagation();
304 }
305 break;
306 case Event.ONMOUSEMOVE:
307 case Event.ONTOUCHMOVE:
308 if (dragging) {
309 setValueByEvent(event, true);
310 event.stopPropagation();
311 }
312 break;
313 case Event.ONTOUCHEND:
314 case Event.ONMOUSEUP:
315 dragging = false;
316 handle.setClassName(getStylePrimaryName() + "-handle");
317 DOM.releaseCapture(getElement());
318 setValueByEvent(event, true);
319 event.stopPropagation();
320 break;
321 default:
322 break;
323 }
324 }
325
326 private void processBaseEvent(Event event) {
327 if (DOM.eventGetType(event) == Event.ONMOUSEDOWN) {
328 if (!disabled && !readonly && !dragging) {
329 setValueByEvent(event, true);
330 DOM.eventCancelBubble(event, true);
331 }
332 }
333 }
334
335 private void decreaseValue(boolean updateToServer) {
336 setValue(new Double(value.doubleValue() - Math.pow(10, -resolution)),
337 updateToServer);
338 }
339
340 private void increaseValue(boolean updateToServer) {
341 setValue(new Double(value.doubleValue() + Math.pow(10, -resolution)),
342 updateToServer);
343 }
344
345 private void setValueByEvent(Event event, boolean updateToServer) {
346 double v = min;
347
348 final int coord = getEventPosition(event);
349
350 final int handleSize, baseSize, baseOffset;
351 if (isVertical()) {
352 handleSize = handle.getOffsetHeight();
353 baseSize = base.getOffsetHeight();
354 baseOffset = base.getAbsoluteTop() - Window.getScrollTop()
355 - handleSize / 2;
356 } else {
357 handleSize = handle.getOffsetWidth();
358 baseSize = base.getOffsetWidth();
359 baseOffset = base.getAbsoluteLeft() - Window.getScrollLeft()
360 + handleSize / 2;
361 }
362
363 if (isVertical()) {
364 v = ((baseSize - (coord - baseOffset)) / (double) (baseSize - handleSize))
365 * (max - min) + min;
366 } else {
367 v = ((coord - baseOffset) / (double) (baseSize - handleSize))
368 * (max - min) + min;
369 }
370
371 if (v < min) {
372 v = min;
373 } else if (v > max) {
374 v = max;
375 }
376
377 setValue(v, updateToServer);
378 }
379
380
381
382
383
384
385
386
387 protected int getEventPosition(Event event) {
388 if (isVertical()) {
389 return WidgetUtil.getTouchOrMouseClientY(event);
390 } else {
391 return WidgetUtil.getTouchOrMouseClientX(event);
392 }
393 }
394
395 public void iLayout() {
396 if (isVertical()) {
397 setHeight();
398 }
399
400 setValue(value, false);
401 }
402
403 private void setHeight() {
404
405 base.getStyle().setHeight(0, Unit.PX);
406 base.getStyle().setOverflow(Overflow.HIDDEN);
407 int h = getElement().getOffsetHeight();
408 if (h < MIN_SIZE) {
409 h = MIN_SIZE;
410 }
411 base.getStyle().setHeight(h, Unit.PX);
412 base.getStyle().clearOverflow();
413 }
414
415 private void fireValueChanged() {
416 ValueChangeEvent.fire(VSliderPatched.this, value);
417 }
418
419
420
421
422
423
424
425
426 public boolean handleNavigation(int keycode, boolean ctrl, boolean shift) {
427
428
429 if (ctrl) {
430 return false;
431 }
432
433 if ((keycode == getNavigationUpKey() && isVertical())
434 || (keycode == getNavigationRightKey() && !isVertical())) {
435 if (shift) {
436 for (int a = 0; a < acceleration; a++) {
437 increaseValue(false);
438 }
439 acceleration++;
440 } else {
441 increaseValue(false);
442 }
443 return true;
444 } else if (keycode == getNavigationDownKey() && isVertical()
445 || (keycode == getNavigationLeftKey() && !isVertical())) {
446 if (shift) {
447 for (int a = 0; a < acceleration; a++) {
448 decreaseValue(false);
449 }
450 acceleration++;
451 } else {
452 decreaseValue(false);
453 }
454 return true;
455 }
456
457 return false;
458 }
459
460
461
462
463
464
465
466
467 protected int getNavigationUpKey() {
468 return KeyCodes.KEY_UP;
469 }
470
471
472
473
474
475
476
477
478 protected int getNavigationDownKey() {
479 return KeyCodes.KEY_DOWN;
480 }
481
482
483
484
485
486
487
488
489 protected int getNavigationLeftKey() {
490 return KeyCodes.KEY_LEFT;
491 }
492
493
494
495
496
497
498
499
500 protected int getNavigationRightKey() {
501 return KeyCodes.KEY_RIGHT;
502 }
503
504 public void setConnection(ApplicationConnection client) {
505 this.client = client;
506 }
507
508 public void setId(String id) {
509 this.id = id;
510 }
511
512 public void setImmediate(boolean immediate) {
513 this.immediate = immediate;
514 }
515
516 public void setDisabled(boolean disabled) {
517 this.disabled = disabled;
518 }
519
520 public void setReadOnly(boolean readonly) {
521 this.readonly = readonly;
522 }
523
524 private boolean isVertical() {
525 return orientation == SliderOrientation.VERTICAL;
526 }
527
528 public void setOrientation(SliderOrientation orientation) {
529 if (this.orientation != orientation) {
530 this.orientation = orientation;
531 updateStyleNames(getStylePrimaryName(), true);
532 }
533 }
534
535 public void setMinValue(double value) {
536 min = value;
537 }
538
539 public void setMaxValue(double value) {
540 max = value;
541 }
542
543 public void setResolution(int resolution) {
544 this.resolution = resolution;
545 }
546
547 @Override
548 public HandlerRegistration addValueChangeHandler(
549 ValueChangeHandler<Double> handler) {
550 return addHandler(handler, ValueChangeEvent.getType());
551 }
552
553 @Override
554 public Double getValue() {
555 return value;
556 }
557
558 @Override
559 public void setValue(Double value) {
560 if (value < min) {
561 value = min;
562 } else if (value > max) {
563 value = max;
564 }
565
566
567 final String styleAttribute = isVertical() ? "marginTop" : "marginLeft";
568 final String domProperty = isVertical() ? "offsetHeight"
569 : "offsetWidth";
570 final int handleSize = handle.getPropertyInt(domProperty);
571 final int baseSize = base.getPropertyInt(domProperty)
572 - (2 * BASE_BORDER_WIDTH);
573
574 final int range = baseSize - handleSize;
575 double v = value.doubleValue();
576
577
578 if (resolution > 0) {
579 v = Math.round(v * Math.pow(10, resolution));
580 v = v / Math.pow(10, resolution);
581 } else {
582 v = Math.round(v);
583 }
584 final double valueRange = max - min;
585 double p = 0;
586 if (valueRange > 0) {
587 p = range * ((v - min) / valueRange);
588 }
589 if (p < 0) {
590 p = 0;
591 }
592 if (isVertical()) {
593 p = range - p;
594 }
595 final double pos = p;
596
597 handle.getStyle().setPropertyPx(styleAttribute, (int) Math.round(pos));
598
599
600 this.value = new Double(v);
601 }
602
603 @Override
604 public void setValue(Double value, boolean fireEvents) {
605 if (value == null) {
606 return;
607 }
608
609 setValue(value);
610
611 if (fireEvents) {
612 fireValueChanged();
613 }
614 }
615
616 @Override
617 public com.google.gwt.user.client.Element getSubPartElement(String subPart) {
618 return null;
619 }
620
621 @Override
622 public String getSubPartName(com.google.gwt.user.client.Element subElement) {
623 return null;
624 }
625 }