1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package com.vaadin.client.ui;
17
18 import com.google.gwt.core.client.Scheduler;
19 import com.google.gwt.core.client.Scheduler.ScheduledCommand;
20 import com.google.gwt.dom.client.Element;
21 import com.google.gwt.dom.client.Style.Display;
22 import com.google.gwt.dom.client.Style.Overflow;
23 import com.google.gwt.dom.client.Style.Unit;
24 import com.google.gwt.event.dom.client.KeyCodes;
25 import com.google.gwt.event.logical.shared.ValueChangeEvent;
26 import com.google.gwt.event.logical.shared.ValueChangeHandler;
27 import com.google.gwt.event.shared.HandlerRegistration;
28 import com.google.gwt.user.client.Command;
29 import com.google.gwt.user.client.DOM;
30 import com.google.gwt.user.client.Event;
31 import com.google.gwt.user.client.Window;
32 import com.google.gwt.user.client.ui.HasValue;
33 import com.vaadin.client.ApplicationConnection;
34 import com.vaadin.client.BrowserInfo;
35 import com.vaadin.client.WidgetUtil;
36 import com.vaadin.shared.ui.slider.SliderOrientation;
37
38 public class VSliderPatched extends SimpleFocusablePanel
39 implements Field, HasValue<Double>, SubPartAware {
40
41 public static final String CLASSNAME = "v-slider";
42
43
44
45
46
47 private static final int MIN_SIZE = 50;
48
49 protected ApplicationConnection client;
50
51 protected String id;
52
53 protected boolean disabled;
54 protected boolean readonly;
55
56 private int acceleration = 1;
57 protected double min;
58 protected double max;
59 protected int resolution;
60 protected Double value;
61 protected SliderOrientation orientation = SliderOrientation.HORIZONTAL;
62
63
64 private final Element base;
65 private final int BASE_BORDER_WIDTH = 1;
66
67
68 private final Element handle;
69
70
71 private final Element smaller;
72
73
74 private final Element bigger;
75
76
77 private boolean dragging = false;
78
79 private VLazyExecutor delayedValueUpdater = new VLazyExecutor(100,
80 new ScheduledCommand() {
81
82 @Override
83 public void execute() {
84 fireValueChanged();
85 acceleration = 1;
86 }
87 });
88
89 public VSliderPatched() {
90 super();
91
92 base = DOM.createDiv();
93 handle = DOM.createDiv();
94 smaller = DOM.createDiv();
95 bigger = DOM.createDiv();
96
97 setStyleName(CLASSNAME);
98
99 getElement().appendChild(bigger);
100 getElement().appendChild(smaller);
101 getElement().appendChild(base);
102 base.appendChild(handle);
103
104
105 smaller.getStyle().setDisplay(Display.NONE);
106 bigger.getStyle().setDisplay(Display.NONE);
107
108 sinkEvents(Event.MOUSEEVENTS | Event.ONMOUSEWHEEL | Event.KEYEVENTS
109 | Event.FOCUSEVENTS | Event.TOUCHEVENTS);
110 }
111
112 @Override
113 public void setStyleName(String style) {
114 updateStyleNames(style, false);
115 }
116
117 @Override
118 public void setStylePrimaryName(String style) {
119 updateStyleNames(style, true);
120 }
121
122 protected void updateStyleNames(String styleName,
123 boolean isPrimaryStyleName) {
124
125 removeStyleName(getStylePrimaryName() + "-vertical");
126
127 if (isPrimaryStyleName) {
128 super.setStylePrimaryName(styleName);
129 } else {
130 super.setStyleName(styleName);
131 }
132
133 base.setClassName(getStylePrimaryName() + "-base");
134 handle.setClassName(getStylePrimaryName() + "-handle");
135 smaller.setClassName(getStylePrimaryName() + "-smaller");
136 bigger.setClassName(getStylePrimaryName() + "-bigger");
137
138 if (isVertical()) {
139 addStyleName(getStylePrimaryName() + "-vertical");
140 }
141 }
142
143
144 public void buildBase() {
145 final String styleAttribute = isVertical() ? "height" : "width";
146 final String oppositeStyleAttribute = isVertical() ? "width" : "height";
147 final String domProperty = isVertical() ? "offsetHeight"
148 : "offsetWidth";
149
150
151 base.getStyle().clearProperty(oppositeStyleAttribute);
152
153
154
155
156
157
158
159
160
161
162 if (getElement().hasParentElement()) {
163 final Element p = getElement();
164 if (p.getPropertyInt(domProperty) > MIN_SIZE) {
165 if (isVertical()) {
166 setHeight();
167 } else {
168 base.getStyle().clearProperty(styleAttribute);
169 }
170 } else {
171
172
173 base.getStyle().setPropertyPx(styleAttribute, MIN_SIZE);
174 Scheduler.get().scheduleDeferred(new Command() {
175
176 @Override
177 public void execute() {
178 final Element p = getElement();
179 if (p.getPropertyInt(domProperty) > MIN_SIZE + 5
180 || propertyNotNullOrEmpty(styleAttribute, p)) {
181 if (isVertical()) {
182 setHeight();
183 } else {
184 base.getStyle().clearProperty(styleAttribute);
185 }
186
187 setValue(value, false);
188 }
189 }
190
191
192 private boolean propertyNotNullOrEmpty(
193 final String styleAttribute, final Element p) {
194 return p.getStyle().getProperty(styleAttribute) != null
195 && !p.getStyle().getProperty(styleAttribute)
196 .isEmpty();
197 }
198 });
199 }
200 }
201
202 if (!isVertical()) {
203
204 Scheduler.get().scheduleDeferred(new Command() {
205 @Override
206 public void execute() {
207 buildHandle();
208 setValue(value, false);
209 }
210 });
211 } else {
212 buildHandle();
213 setValue(value, false);
214 }
215
216
217 }
218
219 void buildHandle() {
220 final String handleAttribute = isVertical() ? "marginTop"
221 : "marginLeft";
222 final String oppositeHandleAttribute = isVertical() ? "marginLeft"
223 : "marginTop";
224
225 handle.getStyle().setProperty(handleAttribute, "0");
226
227
228 handle.getStyle().clearProperty(oppositeHandleAttribute);
229 }
230
231 @Override
232 public void onBrowserEvent(Event event) {
233 if (disabled || readonly) {
234 return;
235 }
236 final Element targ = DOM.eventGetTarget(event);
237 final Element slider = getElement();
238
239 if (DOM.eventGetType(event) == Event.ONMOUSEWHEEL) {
240 processMouseWheelEvent(event);
241 } else if (dragging || targ == handle || targ == base || targ == slider) {
242 processHandleEvent(event);
243 } else if (targ == smaller) {
244 decreaseValue(true);
245 } else if (targ == bigger) {
246 increaseValue(true);
247 } else if (DOM.eventGetType(event) == Event.MOUSEEVENTS) {
248 processBaseEvent(event);
249 } else if (BrowserInfo.get().isGecko()
250 && DOM.eventGetType(event) == Event.ONKEYPRESS
251 || !BrowserInfo.get().isGecko()
252 && 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))
365 / (double) (baseSize - handleSize) * (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 setDisabled(boolean disabled) {
513 this.disabled = disabled;
514 }
515
516 public void setReadOnly(boolean readonly) {
517 this.readonly = readonly;
518 }
519
520 private boolean isVertical() {
521 return orientation == SliderOrientation.VERTICAL;
522 }
523
524 public void setOrientation(SliderOrientation orientation) {
525 if (this.orientation != orientation) {
526 this.orientation = orientation;
527 updateStyleNames(getStylePrimaryName(), true);
528 }
529 }
530
531 public void setMinValue(double value) {
532 min = value;
533 }
534
535 public void setMaxValue(double value) {
536 max = value;
537 }
538
539 public void setResolution(int resolution) {
540 this.resolution = resolution;
541 }
542
543 @Override
544 public HandlerRegistration addValueChangeHandler(
545 ValueChangeHandler<Double> handler) {
546 return addHandler(handler, ValueChangeEvent.getType());
547 }
548
549 @Override
550 public Double getValue() {
551 return value;
552 }
553
554 @Override
555 public void setValue(Double value) {
556 if (value < min) {
557 value = min;
558 } else if (value > max) {
559 value = max;
560 }
561
562
563 final String styleAttribute = isVertical() ? "marginTop" : "marginLeft";
564 final String domProperty = isVertical() ? "offsetHeight"
565 : "offsetWidth";
566 final int handleSize = handle.getPropertyInt(domProperty);
567 final int baseSize = base.getPropertyInt(domProperty)
568 - 2 * BASE_BORDER_WIDTH;
569
570 final int range = baseSize - handleSize;
571 double v = value.doubleValue();
572
573
574 if (resolution > 0) {
575 v = Math.round(v * Math.pow(10, resolution));
576 v = v / Math.pow(10, resolution);
577 } else {
578 v = Math.round(v);
579 }
580 final double valueRange = max - min;
581 double p = 0;
582 if (valueRange > 0) {
583 p = range * ((v - min) / valueRange);
584 }
585 if (p < 0) {
586 p = 0;
587 }
588 if (isVertical()) {
589 p = range - p;
590 }
591 final double pos = p;
592
593 handle.getStyle().setPropertyPx(styleAttribute, (int) Math.round(pos));
594
595
596 this.value = new Double(v);
597 }
598
599 @Override
600 public void setValue(Double value, boolean fireEvents) {
601 if (value == null) {
602 return;
603 }
604
605 setValue(value);
606
607 if (fireEvents) {
608 fireValueChanged();
609 }
610 }
611
612 @Override
613 public com.google.gwt.user.client.Element getSubPartElement(
614 String subPart) {
615 return null;
616 }
617
618 @Override
619 public String getSubPartName(
620 com.google.gwt.user.client.Element subElement) {
621 return null;
622 }
623 }