Author: [email protected]
Date: Tue Mar 10 17:16:05 2009
New Revision: 4978

Modified:
    releases/1.6/user/src/com/google/gwt/user/client/ui/CheckBox.java
    releases/1.6/user/test/com/google/gwt/user/client/ui/RadioButtonTest.java

Log:
Fixes issue 3454, "RadioButtons fire ValueChangeEvents when clicked
even when value does not change"

Reviewed by jgw



Modified: releases/1.6/user/src/com/google/gwt/user/client/ui/CheckBox.java
==============================================================================
--- releases/1.6/user/src/com/google/gwt/user/client/ui/CheckBox.java    
(original)
+++ releases/1.6/user/src/com/google/gwt/user/client/ui/CheckBox.java   Tue  
Mar 10 17:16:05 2009
@@ -20,6 +20,10 @@
  import com.google.gwt.dom.client.LabelElement;
  import com.google.gwt.event.dom.client.ClickEvent;
  import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.KeyUpEvent;
+import com.google.gwt.event.dom.client.KeyUpHandler;
+import com.google.gwt.event.dom.client.MouseUpEvent;
+import com.google.gwt.event.dom.client.MouseUpHandler;
  import com.google.gwt.event.logical.shared.ValueChangeEvent;
  import com.google.gwt.event.logical.shared.ValueChangeHandler;
  import com.google.gwt.event.shared.HandlerRegistration;
@@ -55,6 +59,7 @@
    private InputElement inputElem;
    private LabelElement labelElem;
    private boolean valueChangeHandlerInitialized;
+  private boolean valueBeforeClick;

    /**
     * Creates a check box with no label.
@@ -111,17 +116,29 @@

    public HandlerRegistration addValueChangeHandler(
        ValueChangeHandler<Boolean> handler) {
-    // Is this the first value change handler? If so, time to listen to  
clicks
-    // on the checkbox
+    // Is this the first value change handler? If so, time to add handlers
      if (!valueChangeHandlerInitialized) {
-      valueChangeHandlerInitialized = true;
+
+      this.addKeyUpHandler(new KeyUpHandler() {
+        public void onKeyUp(KeyUpEvent event) {
+          valueBeforeClick = getValue();
+        }
+      });
+
+      this.addMouseUpHandler(new MouseUpHandler() {
+        public void onMouseUp(MouseUpEvent event) {
+          valueBeforeClick = getValue();
+        }
+      });
+
        this.addClickHandler(new ClickHandler() {
          public void onClick(ClickEvent event) {
-          // No need to compare old value and new value--click handler
-          // only fires on real click, and value always toggles
-          ValueChangeEvent.fire(CheckBox.this, getValue());
+          ValueChangeEvent.fireIfNotEqual(CheckBox.this, valueBeforeClick,
+              getValue());
          }
        });
+
+      valueChangeHandlerInitialized = true;
      }
      return addHandler(handler, ValueChangeEvent.getType());
    }
@@ -312,13 +329,15 @@
      }
    }

-  // Unlike other widgets the CheckBox sinks on its input element, not its
-  // wrapper element.
+  // Unlike other widgets the CheckBox sinks on its constituent elements,  
not
+  // their wrapper element.
    @Override
    public void sinkEvents(int eventBitsToAdd) {
      if (isOrWasAttached()) {
        Event.sinkEvents(inputElem,
            eventBitsToAdd | Event.getEventsSunk(inputElem));
+      Event.sinkEvents(labelElem,
+          eventBitsToAdd | Event.getEventsSunk(labelElem));
      } else {
        super.sinkEvents(eventBitsToAdd);
      }

Modified:  
releases/1.6/user/test/com/google/gwt/user/client/ui/RadioButtonTest.java
==============================================================================
---  
releases/1.6/user/test/com/google/gwt/user/client/ui/RadioButtonTest.java       
 
(original)
+++  
releases/1.6/user/test/com/google/gwt/user/client/ui/RadioButtonTest.java       
 
Tue Mar 10 17:16:05 2009
@@ -15,15 +15,28 @@
   */
  package com.google.gwt.user.client.ui;

+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.InputElement;
+import com.google.gwt.dom.client.LabelElement;
+import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.event.logical.shared.ValueChangeEvent;
+import com.google.gwt.event.logical.shared.ValueChangeHandler;
  import com.google.gwt.junit.client.GWTTestCase;
  import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;

  /**
   * Tests the RadioButton class.
   */
  public class RadioButtonTest extends GWTTestCase {

+  private static class Changeable implements ValueChangeHandler<Boolean> {
+    Boolean received;
+    public void onValueChange(ValueChangeEvent<Boolean> event) {
+      received = event.getValue();
+    }
+  }
+
    @Override
    public String getModuleName() {
      return "com.google.gwt.user.DebugTest";
@@ -33,7 +46,7 @@
      RadioButton radio = new RadioButton("myName", "myLabel");

      // We need to replace the input element so we can keep a handle to it
-    Element newInput = DOM.createInputRadio("MyName");
+    com.google.gwt.user.client.Element newInput =  
DOM.createInputRadio("MyName");
      radio.replaceInputElement(newInput);

      radio.ensureDebugId("myRadio");
@@ -43,7 +56,40 @@
      UIObjectTest.assertDebugId("myRadio-input", newInput);
      UIObjectTest.assertDebugIdContents("myRadio-label", "myLabel");
    }
+
+  /**
+   * Test the name and grouping methods.
+   */
+  public void testGrouping() {
+    // Create some radio buttons
+    RadioButton r1 = new RadioButton("group1", "Radio 1");
+    RadioButton r2 = new RadioButton("group1", "Radio 2");
+    RadioButton r3 = new RadioButton("group2", "Radio 3");
+    RootPanel.get().add(r1);
+    RootPanel.get().add(r2);
+    RootPanel.get().add(r3);
+
+    // Check one button in each group
+    r2.setValue(true);
+    r3.setValue(true);

+    // Move a button over
+    r2.setName("group2");
+
+    // Check that the correct buttons are checked
+    assertTrue(r2.getValue());
+    assertFalse(r3.getValue());
+
+    r1.setValue(true);
+    assertTrue(r1.getValue());
+    assertTrue(r2.getValue());
+
+    r3.setValue(true);
+    assertTrue(r1.getValue());
+    assertFalse(r2.getValue());
+    assertTrue(r3.getValue());
+  }
+
    /**
     * Test the name and grouping methods via deprecated calls.
     */
@@ -78,36 +124,107 @@
      assertTrue(r3.isChecked());
    }

-  /**
-   * Test the name and grouping methods.
-   */
-  public void testGrouping() {
-    // Create some radio buttons
+  public void testValueChangeViaClick() {
      RadioButton r1 = new RadioButton("group1", "Radio 1");
      RadioButton r2 = new RadioButton("group1", "Radio 2");
-    RadioButton r3 = new RadioButton("group2", "Radio 3");
      RootPanel.get().add(r1);
      RootPanel.get().add(r2);
-    RootPanel.get().add(r3);
+    r1.setValue(true);
+
+    Changeable c1 = new Changeable();
+    r1.addValueChangeHandler(c1);
+
+    Changeable c2 = new Changeable();
+    r2.addValueChangeHandler(c2);
+
+    // Brittle, but there's no public access
+    InputElement r1Radio = getRadioElement(r1);
+    InputElement r2Radio = getRadioElement(r2);
+
+    doClick(r1Radio);
+    assertEquals(null, c1.received);
+    assertEquals(null, c2.received);
+
+    doClick(r2Radio);
+    assertEquals(null, c1.received);
+    assertEquals(Boolean.TRUE, c2.received);
+    c2.received = null;
+
+    doClick(r1Radio);
+    assertEquals(Boolean.TRUE, c1.received);
+    assertEquals(null, c2.received);
+  }

-    // Check one button in each group
-    r2.setValue(true);
-    r3.setValue(true);
+  public void testValueChangeViaLabelClick() {
+    RadioButton r1 = new RadioButton("group1", "Radio 1");
+    RadioButton r2 = new RadioButton("group1", "Radio 2");
+    RootPanel.get().add(r1);
+    RootPanel.get().add(r2);
+    r1.setValue(true);
+
+    Changeable c1 = new Changeable();
+    r1.addValueChangeHandler(c1);
+
+    Changeable c2 = new Changeable();
+    r2.addValueChangeHandler(c2);
+
+    LabelElement r1Label = getLabelElement(r1);
+    LabelElement r2Label = getLabelElement(r2);
+
+    doClick(r1Label);
+    assertEquals(null, c1.received);
+    assertEquals(null, c2.received);
+
+    doClick(r2Label);
+    assertEquals(null, c1.received);
+    assertEquals(Boolean.TRUE, c2.received);
+    c2.received = null;
+
+    doClick(r1Label);
+    assertEquals(Boolean.TRUE, c1.received);
+    assertEquals(null, c2.received);
+  }

-    // Move a button over
-    r2.setName("group2");
+  private void doClick(Element elm) {
+    NativeEvent e =
+        Document.get().createMouseDownEvent(0, 25, 25, 25, 25, false,  
false,
+            false, false, NativeEvent.BUTTON_LEFT);
+    elm.dispatchEvent(e);
+
+    e = Document.get().createMouseUpEvent(0, 25, 25, 25, 25, false, false,
+            false, false, NativeEvent.BUTTON_LEFT);
+    elm.dispatchEvent(e);
+
+    e = Document.get().createClickEvent(0, 25, 25, 25, 25, false, false,  
false,
+            false);
+    elm.dispatchEvent(e);
+  }

-    // Check that the correct buttons are checked
-    assertTrue(r2.getValue());
-    assertFalse(r3.getValue());
+  private void doSpacebar(Element elm) {
+    NativeEvent e =
+        Document.get().createFocusEvent();
+    elm.dispatchEvent(e);

-    r1.setValue(true);
-    assertTrue(r1.getValue());
-    assertTrue(r2.getValue());
+    e = Document.get().createKeyDownEvent(false, false, false, false, 32,  
32);
+    elm.dispatchEvent(e);

-    r3.setValue(true);
-    assertTrue(r1.getValue());
-    assertFalse(r2.getValue());
-    assertTrue(r3.getValue());
+    e = Document.get().createKeyPressEvent(false, false, false, false, 32,  
32);
+    elm.dispatchEvent(e);
+
+    e = Document.get().createKeyUpEvent(false, false, false, false, 32,  
32);
+    elm.dispatchEvent(e);
+  }
+
+  private LabelElement getLabelElement(RadioButton radioButton) {
+    LabelElement r1Label =
+        LabelElement.as(Element.as(getRadioElement(radioButton)
+          .getNextSiblingElement()));
+    return r1Label;
+  }
+
+  private InputElement getRadioElement(RadioButton radioButton) {
+    InputElement r1Radio =
+         
InputElement.as(Element.as(radioButton.getElement().getFirstChild()));
+    return r1Radio;
    }
  }

--~--~---------~--~----~------------~-------~--~----~
http://groups.google.com/group/Google-Web-Toolkit-Contributors
-~----------~----~----~----~------~----~------~--~---

Reply via email to